From 7b8e53dd1496da1b9c2c5384c2649d7edf5a22e3 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Mon, 12 Mar 2018 14:29:49 +0100 Subject: [PATCH] Bug 602612 - Add 'Alternative Reply' menu option --- data/org.gnome.evolution.mail.gschema.xml.in | 38 +- data/ui/evolution-mail-reader.ui | 2 + po/POTFILES.in | 2 +- src/composer/mail-composer.error.xml | 5 + src/mail/CMakeLists.txt | 4 + src/mail/e-mail-enums.h | 30 + src/mail/e-mail-reader-utils.c | 5 +- src/mail/e-mail-reader.c | 91 +++ .../e-mail-templates-store.c} | 340 ++++++-- src/mail/e-mail-templates-store.h | 110 +++ src/mail/e-mail-templates.c | 497 ++++++++++++ src/mail/e-mail-templates.h | 49 ++ src/mail/em-composer-utils.c | 864 +++++++++++++++++++-- src/mail/em-composer-utils.h | 11 +- src/modules/mail/e-mail-attachment-handler.c | 2 +- src/modules/webkit-editor/e-webkit-editor.c | 1 + src/plugins/templates/CMakeLists.txt | 2 - src/plugins/templates/e-templates-store.h | 95 --- src/plugins/templates/templates.c | 517 ++---------- 19 files changed, 1963 insertions(+), 702 deletions(-) rename src/{plugins/templates/e-templates-store.c => mail/e-mail-templates-store.c} (85%) create mode 100644 src/mail/e-mail-templates-store.h create mode 100644 src/mail/e-mail-templates.c create mode 100644 src/mail/e-mail-templates.h delete mode 100644 src/plugins/templates/e-templates-store.h diff --git a/data/org.gnome.evolution.mail.gschema.xml.in b/data/org.gnome.evolution.mail.gschema.xml.in index 7d4d02d4c5..4712824dd7 100644 --- a/data/org.gnome.evolution.mail.gschema.xml.in +++ b/data/org.gnome.evolution.mail.gschema.xml.in @@ -16,6 +16,7 @@ + @@ -29,6 +30,13 @@ + + + + + + + true @@ -733,7 +741,35 @@ <_summary>Visually wrap long lines in composer <_description>Whether to visually wrap long lines of text to avoid horizontal scrolling - + + 'quoted' + <_summary>Alternative reply style + + + 'inconsistent' + <_summary>Format message in HTML + + + 'inconsistent' + <_summary>Put the cursor at the bottom of alternative replies + <_description>This determines whether the cursor is placed at the top of the message or the bottom when using Alternative Reply. + + + false + <_summary>Apply chosen template when using Alternative Reply + + + '' + <_summary>Last chosen template's folder URI for Alternative Reply + + + '' + <_summary>Last chosen template's message UID for Alternative Reply + + + false + <_summary>Whether preserve original message subject when applying template for Alternative Reply + diff --git a/data/ui/evolution-mail-reader.ui b/data/ui/evolution-mail-reader.ui index d005a1efd1..1174fc4219 100644 --- a/data/ui/evolution-mail-reader.ui +++ b/data/ui/evolution-mail-reader.ui @@ -61,6 +61,7 @@ + @@ -126,6 +127,7 @@ + diff --git a/po/POTFILES.in b/po/POTFILES.in index 6ac8212507..1f3e304b97 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -352,6 +352,7 @@ src/mail/e-mail-reader.c src/mail/e-mail-reader-utils.c src/mail/e-mail-request.c src/mail/e-mail-tag-editor.c +src/mail/e-mail-templates-store.c src/mail/e-mail-ui-session.c src/mail/em-composer-utils.c src/mail/em-filter-editor.c @@ -521,7 +522,6 @@ src/plugins/save-calendar/ical-format.c src/plugins/save-calendar/org-gnome-save-calendar.eplug.xml src/plugins/save-calendar/rdf-format.c src/plugins/save-calendar/save-calendar.c -src/plugins/templates/e-templates-store.c src/plugins/templates/org-gnome-templates.eplug.xml src/plugins/templates/templates.c src/shell/e-shell-backend.c diff --git a/src/composer/mail-composer.error.xml b/src/composer/mail-composer.error.xml index c6a7dd2f9f..d7982c300f 100644 --- a/src/composer/mail-composer.error.xml +++ b/src/composer/mail-composer.error.xml @@ -148,4 +148,9 @@ Detailed error: {0} <_primary>Something has gone wrong when editing the message <_secondary>A WebKitWebProcess crashed when editing the message. You can try again by closing the composer window and opening a new one. If the issue persists, please file a bug report in the GNOME bugzilla. + + + <_primary>An error occurred while creating message composer. + {0} + diff --git a/src/mail/CMakeLists.txt b/src/mail/CMakeLists.txt index 279099fd7d..e9abcc75df 100644 --- a/src/mail/CMakeLists.txt +++ b/src/mail/CMakeLists.txt @@ -87,6 +87,8 @@ set(SOURCES e-mail-send-account-override.c e-mail-sidebar.c e-mail-tag-editor.c + e-mail-templates.c + e-mail-templates-store.c e-mail-ui-session.c e-mail-view.c em-composer-utils.c @@ -173,6 +175,8 @@ set(HEADERS e-mail-send-account-override.h e-mail-sidebar.h e-mail-tag-editor.h + e-mail-templates.h + e-mail-templates-store.h e-mail-ui-session.h e-mail-view.h em-composer-utils.h diff --git a/src/mail/e-mail-enums.h b/src/mail/e-mail-enums.h index 9665967e12..0f94c6da06 100644 --- a/src/mail/e-mail-enums.h +++ b/src/mail/e-mail-enums.h @@ -29,6 +29,7 @@ typedef enum { } EMailForwardStyle; typedef enum { + E_MAIL_REPLY_STYLE_UNKNOWN = -1, E_MAIL_REPLY_STYLE_QUOTED, E_MAIL_REPLY_STYLE_DO_NOT_QUOTE, E_MAIL_REPLY_STYLE_ATTACH, @@ -43,6 +44,35 @@ typedef enum { E_MAIL_REPLY_TO_LIST } EMailReplyType; +/** + * EMailReplyFlags: + * @E_MAIL_REPLY_FLAG_NONE: no flags used + * @E_MAIL_REPLY_FLAG_FORCE_STYLE: Force use of the passed-in reply style; if not set, + * then also checks reply style setting for the used mail account. + * @E_MAIL_REPLY_FLAG_FORMAT_PLAIN: Force compose in Plain Text format; cannot be used together + * with @E_MAIL_REPLY_FLAG_FORMAT_HTML. If none of these is set, then uses + * global setting. + * @E_MAIL_REPLY_FLAG_FORMAT_HTML: Force compose in HTML format; cannot be used together + * with @E_MAIL_REPLY_FLAG_FORMAT_PLAIN. If none of these is set, then uses + * global setting. + * @E_MAIL_REPLY_FLAG_TOP_POSTING: Force top posting; cannot be used together + * with @E_MAIL_REPLY_FLAG_BOTTOM_POSTING. If none it set, then uses global settings. + * @E_MAIL_REPLY_FLAG_BOTTOM_POSTING: Force bottom posting; cannot be used together + * with @E_MAIL_REPLY_FLAG_BOTTOM_POSTING. If none it set, then uses global settings. + * + * Flags influencing behavior of em_utils_reply_to_message(). + * + * Since: 3.30 + **/ +typedef enum { /*< flags >*/ + E_MAIL_REPLY_FLAG_NONE = 0, + E_MAIL_REPLY_FLAG_FORCE_STYLE = 1 << 0, + E_MAIL_REPLY_FLAG_FORMAT_PLAIN = 1 << 1, + E_MAIL_REPLY_FLAG_FORMAT_HTML = 1 << 2, + E_MAIL_REPLY_FLAG_TOP_POSTING = 1 << 3, + E_MAIL_REPLY_FLAG_BOTTOM_POSTING = 1 << 4 +} EMailReplyFlags; + G_END_DECLS #endif /* E_MAIL_ENUMS_H */ diff --git a/src/mail/e-mail-reader-utils.c b/src/mail/e-mail-reader-utils.c index 7e2f4d73c3..9ac8c9e091 100644 --- a/src/mail/e-mail-reader-utils.c +++ b/src/mail/e-mail-reader-utils.c @@ -2410,7 +2410,8 @@ mail_reader_reply_composer_created_cb (GObject *object, async_context->reply_type, async_context->reply_style, async_context->part_list, - async_context->address); + async_context->address, + E_MAIL_REPLY_FLAG_NONE); e_mail_reader_composer_created (async_context->reader, composer, message); } @@ -2518,7 +2519,7 @@ mail_reader_reply_to_message_composer_created_cb (GObject *source_object, } else { em_utils_reply_to_message ( composer, ccd->message, ccd->folder, ccd->message_uid, - ccd->reply_type, ccd->reply_style, ccd->part_list, ccd->address); + ccd->reply_type, ccd->reply_style, ccd->part_list, ccd->address, E_MAIL_REPLY_FLAG_NONE); if (ccd->validity_pgp_sum != 0 || ccd->validity_smime_sum != 0) { GtkToggleAction *action; diff --git a/src/mail/e-mail-reader.c b/src/mail/e-mail-reader.c index 2c84230c02..75d1de343d 100644 --- a/src/mail/e-mail-reader.c +++ b/src/mail/e-mail-reader.c @@ -1477,6 +1477,85 @@ action_mail_reply_all_cb (GtkAction *action, } static void +action_mail_reply_alternative_got_message (CamelFolder *folder, + GAsyncResult *result, + EMailReaderClosure *closure) +{ + EAlertSink *alert_sink; + EMailDisplay *mail_display; + CamelMimeMessage *message; + GError *error = NULL; + + alert_sink = e_activity_get_alert_sink (closure->activity); + + message = camel_folder_get_message_finish (folder, result, &error); + + if (e_activity_handle_cancellation (closure->activity, error)) { + g_warn_if_fail (message == NULL); + mail_reader_closure_free (closure); + g_error_free (error); + return; + + } else if (error != NULL) { + g_warn_if_fail (message == NULL); + e_alert_submit ( + alert_sink, "mail:no-retrieve-message", + error->message, NULL); + mail_reader_closure_free (closure); + g_error_free (error); + return; + } + + g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message)); + + g_clear_object (&closure->activity); + + mail_display = e_mail_reader_get_mail_display (closure->reader); + + em_utils_reply_alternative (e_mail_reader_get_window (closure->reader), + e_shell_backend_get_shell (E_SHELL_BACKEND (e_mail_reader_get_backend (closure->reader))), + alert_sink, message, folder, closure->message_uid, + e_mail_reader_get_reply_style (closure->reader), + mail_display ? e_mail_display_get_part_list (mail_display) : NULL); + + g_object_unref (message); + mail_reader_closure_free (closure); +} + +static void +action_mail_reply_alternative_cb (GtkAction *action, + EMailReader *reader) +{ + EActivity *activity; + GCancellable *cancellable; + EMailReaderClosure *closure; + CamelFolder *folder; + GtkWidget *message_list; + const gchar *message_uid; + + message_list = e_mail_reader_get_message_list (reader); + message_uid = MESSAGE_LIST (message_list)->cursor_uid; + g_return_if_fail (message_uid != NULL); + + activity = e_mail_reader_new_activity (reader); + cancellable = e_activity_get_cancellable (activity); + + closure = g_slice_new0 (EMailReaderClosure); + closure->activity = activity; + closure->reader = g_object_ref (reader); + closure->message_uid = g_strdup (message_uid); + + folder = e_mail_reader_ref_folder (reader); + + camel_folder_get_message ( + folder, message_uid, G_PRIORITY_DEFAULT, + cancellable, (GAsyncReadyCallback) + action_mail_reply_alternative_got_message, closure); + + g_clear_object (&folder); +} + +static void action_mail_reply_group_cb (GtkAction *action, EMailReader *reader) { @@ -2439,6 +2518,13 @@ static GtkActionEntry mail_reader_entries[] = { N_("Compose a reply to all the recipients of the selected message"), G_CALLBACK (action_mail_reply_all_cb) }, + { "mail-reply-alternative", + NULL, + N_("Al_ternative Reply…"), + "r", + N_("Choose reply options for the selected message"), + G_CALLBACK (action_mail_reply_alternative_cb) }, + { "mail-reply-list", NULL, N_("Reply to _List"), @@ -4070,6 +4156,11 @@ mail_reader_update_actions (EMailReader *reader, action = e_mail_reader_get_action (reader, action_name); gtk_action_set_sensitive (action, sensitive); + action_name = "mail-reply-alternative"; + sensitive = have_enabled_account && single_message_selected; + action = e_mail_reader_get_action (reader, action_name); + gtk_action_set_sensitive (action, sensitive); + action_name = "mail-reply-group"; sensitive = have_enabled_account && single_message_selected; action = e_mail_reader_get_action (reader, action_name); diff --git a/src/plugins/templates/e-templates-store.c b/src/mail/e-mail-templates-store.c similarity index 85% rename from src/plugins/templates/e-templates-store.c rename to src/mail/e-mail-templates-store.c index 1f4017b575..9d41e41879 100644 --- a/src/plugins/templates/e-templates-store.c +++ b/src/mail/e-mail-templates-store.c @@ -25,9 +25,9 @@ #include "shell/e-shell-view.h" -#include "e-templates-store.h" +#include "e-mail-templates-store.h" -struct _ETemplatesStorePrivate { +struct _EMailTemplatesStorePrivate { GWeakRef *account_store_weakref; /* EMailAccountStore * */ gulong service_enabled_handler_id; @@ -41,7 +41,7 @@ struct _ETemplatesStorePrivate { guint menu_refresh_idle_id; }; -G_DEFINE_TYPE (ETemplatesStore, e_templates_store, G_TYPE_OBJECT); +G_DEFINE_TYPE (EMailTemplatesStore, e_mail_templates_store, G_TYPE_OBJECT); enum { PROP_0, @@ -56,25 +56,25 @@ enum { static guint signals[LAST_SIGNAL]; static void -templates_store_lock (ETemplatesStore *templates_store) +templates_store_lock (EMailTemplatesStore *templates_store) { - g_return_if_fail (E_IS_TEMPLATES_STORE (templates_store)); + g_return_if_fail (E_IS_MAIL_TEMPLATES_STORE (templates_store)); g_mutex_lock (&templates_store->priv->busy_lock); } static void -templates_store_unlock (ETemplatesStore *templates_store) +templates_store_unlock (EMailTemplatesStore *templates_store) { - g_return_if_fail (E_IS_TEMPLATES_STORE (templates_store)); + g_return_if_fail (E_IS_MAIL_TEMPLATES_STORE (templates_store)); g_mutex_unlock (&templates_store->priv->busy_lock); } static void -templates_store_emit_changed (ETemplatesStore *templates_store) +templates_store_emit_changed (EMailTemplatesStore *templates_store) { - g_return_if_fail (E_IS_TEMPLATES_STORE (templates_store)); + g_return_if_fail (E_IS_MAIL_TEMPLATES_STORE (templates_store)); g_signal_emit (templates_store, signals[CHANGED], 0, NULL); } @@ -174,7 +174,7 @@ tmpl_message_data_compare (gconstpointer ptr1, typedef struct _TmplFolderData { volatile gint ref_count; - GWeakRef *templates_store_weakref; /* ETemplatesStore * */ + GWeakRef *templates_store_weakref; /* EMailTemplatesStore * */ CamelFolder *folder; gulong changed_handler_id; @@ -187,12 +187,12 @@ typedef struct _TmplFolderData { } TmplFolderData; static TmplFolderData * -tmpl_folder_data_new (ETemplatesStore *templates_store, +tmpl_folder_data_new (EMailTemplatesStore *templates_store, CamelFolder *folder) { TmplFolderData *tfd; - g_return_val_if_fail (E_IS_TEMPLATES_STORE (templates_store), NULL); + g_return_val_if_fail (E_IS_MAIL_TEMPLATES_STORE (templates_store), NULL); g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL); tfd = g_new0 (TmplFolderData, 1); @@ -397,7 +397,7 @@ tmpl_folder_data_update_done_cb (GObject *source, if (g_task_propagate_boolean (G_TASK (result), &local_error)) { /* Content changed, rebuild menu when needed */ - ETemplatesStore *templates_store; + EMailTemplatesStore *templates_store; templates_store = g_weak_ref_get (tfd->templates_store_weakref); if (templates_store) { @@ -515,7 +515,7 @@ static void tmpl_folder_data_schedule_update (TmplFolderData *tfd, CamelFolderChangeInfo *change_info) { - ETemplatesStore *templates_store; + EMailTemplatesStore *templates_store; TfdUpdateData *tud; GTask *task; guint ii; @@ -574,7 +574,7 @@ tmpl_folder_data_folder_changed_cb (CamelFolder *folder, (change_info->uid_changed && change_info->uid_changed->len)) { tmpl_folder_data_schedule_update (tfd, change_info); } else if (change_info->uid_removed && change_info->uid_removed->len) { - ETemplatesStore *templates_store; + EMailTemplatesStore *templates_store; templates_store = g_weak_ref_get (tfd->templates_store_weakref); if (templates_store) { @@ -614,7 +614,7 @@ tmpl_store_data_traverse_to_free_cb (GNode *node, typedef struct _TmplStoreData { volatile gint ref_count; - GWeakRef *templates_store_weakref; /* ETemplatesStore * */ + GWeakRef *templates_store_weakref; /* EMailTemplatesStore * */ GWeakRef *store_weakref; /* CamelStore * */ gulong folder_created_handler_id; @@ -634,7 +634,7 @@ tmpl_store_data_set_root_folder_path (TmplStoreData *tsd, const gchar *root_folder_path); static TmplStoreData * -tmpl_store_data_new (ETemplatesStore *templates_store, +tmpl_store_data_new (EMailTemplatesStore *templates_store, CamelStore *store, const gchar *root_folder_path, const gchar *templates_folder_uri, @@ -642,7 +642,7 @@ tmpl_store_data_new (ETemplatesStore *templates_store, { TmplStoreData *tsd; - g_return_val_if_fail (E_IS_TEMPLATES_STORE (templates_store), NULL); + g_return_val_if_fail (E_IS_MAIL_TEMPLATES_STORE (templates_store), NULL); g_return_val_if_fail (CAMEL_IS_STORE (store), NULL); g_return_val_if_fail (root_folder_path && *root_folder_path, NULL); g_return_val_if_fail (templates_folder_uri && *templates_folder_uri, NULL); @@ -960,7 +960,7 @@ tmpl_store_data_update_done_cb (GObject *source, if (g_task_propagate_boolean (G_TASK (result), &local_error)) { /* Content changed, rebuild menu when needed */ - ETemplatesStore *templates_store; + EMailTemplatesStore *templates_store; templates_store = g_weak_ref_get (tsd->templates_store_weakref); if (templates_store) { @@ -980,7 +980,7 @@ tmpl_store_data_initial_setup_thread (GTask *task, gpointer task_data, GCancellable *cancellable) { - ETemplatesStore *templates_store; + EMailTemplatesStore *templates_store; TmplStoreData *tsd = task_data; CamelStore *store; gboolean changed = FALSE; @@ -1079,7 +1079,7 @@ tmpl_store_data_initial_setup_thread (GTask *task, static void tmpl_store_data_schedule_initial_setup (TmplStoreData *tsd) { - ETemplatesStore *templates_store; + EMailTemplatesStore *templates_store; GTask *task; g_return_if_fail (tsd != NULL); @@ -1123,7 +1123,7 @@ tmpl_store_data_folder_thread (GTask *task, gpointer task_data, GCancellable *cancellable) { - ETemplatesStore *templates_store; + EMailTemplatesStore *templates_store; TsdFolderData *fd = task_data; CamelStore *store; gboolean changed = FALSE; @@ -1230,7 +1230,7 @@ tmpl_store_data_folder_created_cb (CamelStore *store, gpointer user_data) { TmplStoreData *tsd = user_data; - ETemplatesStore *templates_store; + EMailTemplatesStore *templates_store; g_return_if_fail (CAMEL_IS_STORE (store)); g_return_if_fail (folder_info != NULL); @@ -1271,7 +1271,7 @@ tmpl_store_data_folder_deleted_cb (CamelStore *store, gpointer user_data) { TmplStoreData *tsd = user_data; - ETemplatesStore *templates_store; + EMailTemplatesStore *templates_store; gboolean changed = FALSE; g_return_if_fail (CAMEL_IS_STORE (store)); @@ -1309,7 +1309,7 @@ tmpl_store_data_folder_renamed_cb (CamelStore *store, gpointer user_data) { TmplStoreData *tsd = user_data; - ETemplatesStore *templates_store; + EMailTemplatesStore *templates_store; gboolean changed = FALSE; g_return_if_fail (CAMEL_IS_STORE (store)); @@ -1375,7 +1375,7 @@ tmpl_store_data_notify_display_name_cb (CamelService *service, gpointer user_data) { TmplStoreData *tsd = user_data; - ETemplatesStore *templates_store; + EMailTemplatesStore *templates_store; g_return_if_fail (CAMEL_IS_SERVICE (service)); g_return_if_fail (tsd != NULL); @@ -1385,7 +1385,7 @@ tmpl_store_data_notify_display_name_cb (CamelService *service, EMailAccountStore *account_store; gboolean changed = FALSE; - account_store = e_templates_store_ref_account_store (templates_store); + account_store = e_mail_templates_store_ref_account_store (templates_store); templates_store_lock (templates_store); @@ -1404,7 +1404,7 @@ tmpl_store_data_notify_display_name_cb (CamelService *service, } static gchar * -templates_store_find_custom_templates_root_folder_path (ETemplatesStore *templates_store, +templates_store_find_custom_templates_root_folder_path (EMailTemplatesStore *templates_store, CamelStore *store, EMailSession *mail_session, ESource **out_identity_source, @@ -1414,7 +1414,7 @@ templates_store_find_custom_templates_root_folder_path (ETemplatesStore *templat ESource *identity_source; gchar *root_folder_path = NULL; - g_return_val_if_fail (E_IS_TEMPLATES_STORE (templates_store), NULL); + g_return_val_if_fail (E_IS_MAIL_TEMPLATES_STORE (templates_store), NULL); g_return_val_if_fail (CAMEL_IS_STORE (store), NULL); g_return_val_if_fail (out_identity_source != NULL, NULL); g_return_val_if_fail (out_use_store != NULL, NULL); @@ -1471,7 +1471,7 @@ templates_store_find_custom_templates_root_folder_path (ETemplatesStore *templat } static void -templates_store_maybe_add_store (ETemplatesStore *templates_store, +templates_store_maybe_add_store (EMailTemplatesStore *templates_store, CamelStore *store) { ESource *identity_source = NULL; @@ -1481,10 +1481,10 @@ templates_store_maybe_add_store (ETemplatesStore *templates_store, gchar *root_folder_path, *templates_folder_uri = NULL; gboolean changed = FALSE; - g_return_if_fail (E_IS_TEMPLATES_STORE (templates_store)); + g_return_if_fail (E_IS_MAIL_TEMPLATES_STORE (templates_store)); g_return_if_fail (CAMEL_IS_STORE (store)); - account_store = e_templates_store_ref_account_store (templates_store); + account_store = e_mail_templates_store_ref_account_store (templates_store); if (!account_store) return; @@ -1544,13 +1544,13 @@ templates_store_maybe_add_store (ETemplatesStore *templates_store, } static void -templates_store_maybe_remove_store (ETemplatesStore *templates_store, +templates_store_maybe_remove_store (EMailTemplatesStore *templates_store, CamelStore *store) { GSList *link; gboolean changed = FALSE; - g_return_if_fail (E_IS_TEMPLATES_STORE (templates_store)); + g_return_if_fail (E_IS_MAIL_TEMPLATES_STORE (templates_store)); g_return_if_fail (CAMEL_IS_STORE (store)); templates_store_lock (templates_store); @@ -1582,15 +1582,15 @@ templates_store_maybe_remove_store (ETemplatesStore *templates_store, } static void -templates_store_maybe_add_enabled_services (ETemplatesStore *templates_store) +templates_store_maybe_add_enabled_services (EMailTemplatesStore *templates_store) { EMailAccountStore *account_store; GQueue queue = G_QUEUE_INIT; - g_return_if_fail (E_IS_TEMPLATES_STORE (templates_store)); + g_return_if_fail (E_IS_MAIL_TEMPLATES_STORE (templates_store)); g_return_if_fail (templates_store->priv->stores == NULL); - account_store = e_templates_store_ref_account_store (templates_store); + account_store = e_mail_templates_store_ref_account_store (templates_store); g_return_if_fail (account_store != NULL); e_mail_account_store_queue_enabled_services (account_store, &queue); @@ -1612,7 +1612,7 @@ templates_store_service_enabled_cb (EMailAccountStore *account_store, CamelService *service, GWeakRef *weak_ref) { - ETemplatesStore *templates_store; + EMailTemplatesStore *templates_store; if (!CAMEL_IS_STORE (service)) return; @@ -1630,7 +1630,7 @@ templates_store_service_disabled_cb (EMailAccountStore *account_store, CamelService *service, GWeakRef *weak_ref) { - ETemplatesStore *templates_store; + EMailTemplatesStore *templates_store; if (!CAMEL_IS_STORE (service)) return; @@ -1648,7 +1648,7 @@ templates_store_service_removed_cb (EMailAccountStore *account_store, CamelService *service, GWeakRef *weak_ref) { - ETemplatesStore *templates_store; + EMailTemplatesStore *templates_store; if (!CAMEL_IS_STORE (service)) return; @@ -1666,7 +1666,7 @@ templates_store_source_changed_cb (ESourceRegistry *registry, ESource *source, GWeakRef *weak_ref) { - ETemplatesStore *templates_store; + EMailTemplatesStore *templates_store; g_return_if_fail (E_IS_SOURCE (source)); @@ -1754,7 +1754,7 @@ templates_store_source_changed_cb (ESourceRegistry *registry, } static void -templates_store_set_account_store (ETemplatesStore *templates_store, +templates_store_set_account_store (EMailTemplatesStore *templates_store, EMailAccountStore *account_store) { g_return_if_fail (E_IS_MAIL_ACCOUNT_STORE (account_store)); @@ -1771,7 +1771,7 @@ templates_store_set_property (GObject *object, switch (property_id) { case PROP_ACCOUNT_STORE: templates_store_set_account_store ( - E_TEMPLATES_STORE (object), + E_MAIL_TEMPLATES_STORE (object), g_value_get_object (value)); return; } @@ -1789,8 +1789,8 @@ templates_store_get_property (GObject *object, case PROP_ACCOUNT_STORE: g_value_take_object ( value, - e_templates_store_ref_account_store ( - E_TEMPLATES_STORE (object))); + e_mail_templates_store_ref_account_store ( + E_MAIL_TEMPLATES_STORE (object))); return; } @@ -1800,12 +1800,12 @@ templates_store_get_property (GObject *object, static void templates_store_dispose (GObject *object) { - ETemplatesStore *templates_store; + EMailTemplatesStore *templates_store; EMailAccountStore *account_store; - templates_store = E_TEMPLATES_STORE (object); + templates_store = E_MAIL_TEMPLATES_STORE (object); - account_store = e_templates_store_ref_account_store (templates_store); + account_store = e_mail_templates_store_ref_account_store (templates_store); if (account_store) { if (templates_store->priv->service_enabled_handler_id) { @@ -1843,15 +1843,15 @@ templates_store_dispose (GObject *object) g_clear_object (&account_store); /* Chain up to parent's method. */ - G_OBJECT_CLASS (e_templates_store_parent_class)->dispose (object); + G_OBJECT_CLASS (e_mail_templates_store_parent_class)->dispose (object); } static void templates_store_finalize (GObject *object) { - ETemplatesStore *templates_store; + EMailTemplatesStore *templates_store; - templates_store = E_TEMPLATES_STORE (object); + templates_store = E_MAIL_TEMPLATES_STORE (object); g_slist_free_full (templates_store->priv->stores, tmpl_store_data_unref); templates_store->priv->stores = NULL; @@ -1862,25 +1862,25 @@ templates_store_finalize (GObject *object) g_mutex_clear (&templates_store->priv->busy_lock); /* Chain up to parent's method. */ - G_OBJECT_CLASS (e_templates_store_parent_class)->finalize (object); + G_OBJECT_CLASS (e_mail_templates_store_parent_class)->finalize (object); } static void templates_store_constructed (GObject *object) { - ETemplatesStore *templates_store; + EMailTemplatesStore *templates_store; ESourceRegistry *registry; EMailAccountStore *account_store; EMailSession *session; - templates_store = E_TEMPLATES_STORE (object); + templates_store = E_MAIL_TEMPLATES_STORE (object); /* Chain up to parent's method. */ - G_OBJECT_CLASS (e_templates_store_parent_class)->constructed (object); + G_OBJECT_CLASS (e_mail_templates_store_parent_class)->constructed (object); templates_store->priv->cancellable = g_cancellable_new (); - account_store = e_templates_store_ref_account_store (templates_store); + account_store = e_mail_templates_store_ref_account_store (templates_store); g_return_if_fail (account_store != NULL); session = e_mail_account_store_get_session (account_store); @@ -1916,11 +1916,11 @@ templates_store_constructed (GObject *object) } static void -e_templates_store_class_init (ETemplatesStoreClass *class) +e_mail_templates_store_class_init (EMailTemplatesStoreClass *class) { GObjectClass *object_class; - g_type_class_add_private (class, sizeof (ETemplatesStorePrivate)); + g_type_class_add_private (class, sizeof (EMailTemplatesStorePrivate)); object_class = G_OBJECT_CLASS (class); object_class->set_property = templates_store_set_property; @@ -1945,22 +1945,22 @@ e_templates_store_class_init (ETemplatesStoreClass *class) "changed", G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ETemplatesStoreClass, changed), + G_STRUCT_OFFSET (EMailTemplatesStoreClass, changed), NULL, NULL, NULL, G_TYPE_NONE, 0, G_TYPE_NONE); } static void -e_templates_store_init (ETemplatesStore *templates_store) +e_mail_templates_store_init (EMailTemplatesStore *templates_store) { - templates_store->priv = G_TYPE_INSTANCE_GET_PRIVATE (templates_store, E_TYPE_TEMPLATES_STORE, ETemplatesStorePrivate); + templates_store->priv = G_TYPE_INSTANCE_GET_PRIVATE (templates_store, E_TYPE_MAIL_TEMPLATES_STORE, EMailTemplatesStorePrivate); g_mutex_init (&templates_store->priv->busy_lock); templates_store->priv->account_store_weakref = e_weak_ref_new (NULL); } -ETemplatesStore * -e_templates_store_ref_default (EMailAccountStore *account_store) +EMailTemplatesStore * +e_mail_templates_store_ref_default (EMailAccountStore *account_store) { static gpointer def_templates_store = NULL; @@ -1969,7 +1969,7 @@ e_templates_store_ref_default (EMailAccountStore *account_store) if (def_templates_store) { g_object_ref (def_templates_store); } else { - def_templates_store = g_object_new (E_TYPE_TEMPLATES_STORE, + def_templates_store = g_object_new (E_TYPE_MAIL_TEMPLATES_STORE, "account-store", account_store, NULL); @@ -1980,9 +1980,9 @@ e_templates_store_ref_default (EMailAccountStore *account_store) } EMailAccountStore * -e_templates_store_ref_account_store (ETemplatesStore *templates_store) +e_mail_templates_store_ref_account_store (EMailTemplatesStore *templates_store) { - g_return_val_if_fail (E_IS_TEMPLATES_STORE (templates_store), NULL); + g_return_val_if_fail (E_IS_MAIL_TEMPLATES_STORE (templates_store), NULL); return g_weak_ref_get (templates_store->priv->account_store_weakref); } @@ -2011,18 +2011,18 @@ tmpl_store_data_folder_has_messages_cb (GNode *node, } typedef struct _TmplActionData { - ETemplatesStore *templates_store; /* not referenced */ + EMailTemplatesStore *templates_store; /* not referenced */ CamelFolder *folder; const gchar *uid; /* from camel_pstring */ - ETemplatesStoreActionFunc action_cb; + EMailTemplatesStoreActionFunc action_cb; gpointer action_cb_user_data; } TmplActionData; static TmplActionData * -tmpl_action_data_new (ETemplatesStore *templates_store, +tmpl_action_data_new (EMailTemplatesStore *templates_store, CamelFolder *folder, const gchar *uid, - ETemplatesStoreActionFunc action_cb, + EMailTemplatesStoreActionFunc action_cb, gpointer action_cb_user_data) { TmplActionData *tad; @@ -2063,20 +2063,19 @@ templates_store_action_activated_cb (GtkAction *action, } static void -templates_store_add_to_menu_recurse (ETemplatesStore *templates_store, +templates_store_add_to_menu_recurse (EMailTemplatesStore *templates_store, GNode *node, GtkUIManager *ui_manager, GtkActionGroup *action_group, const gchar *base_menu_path, guint merge_id, - ETemplatesStoreActionFunc action_cb, + EMailTemplatesStoreActionFunc action_cb, gpointer action_cb_user_data, gboolean with_folder_menu, guint *action_count) { TmplFolderData *tfd; - g_return_if_fail (E_IS_TEMPLATES_STORE (templates_store)); g_return_if_fail (node != NULL); while (node) { @@ -2152,14 +2151,14 @@ templates_store_add_to_menu_recurse (ETemplatesStore *templates_store, } void -e_templates_store_build_menu (ETemplatesStore *templates_store, - EShellView *shell_view, - GtkUIManager *ui_manager, - GtkActionGroup *action_group, - const gchar *base_menu_path, - guint merge_id, - ETemplatesStoreActionFunc action_cb, - gpointer action_cb_user_data) +e_mail_templates_store_build_menu (EMailTemplatesStore *templates_store, + EShellView *shell_view, + GtkUIManager *ui_manager, + GtkActionGroup *action_group, + const gchar *base_menu_path, + guint merge_id, + EMailTemplatesStoreActionFunc action_cb, + gpointer action_cb_user_data) { GSList *link; GtkAction *action; @@ -2169,7 +2168,7 @@ e_templates_store_build_menu (ETemplatesStore *templates_store, gchar *tmp_menu_path = NULL; gchar *action_name; - g_return_if_fail (E_IS_TEMPLATES_STORE (templates_store)); + g_return_if_fail (E_IS_MAIL_TEMPLATES_STORE (templates_store)); g_return_if_fail (E_IS_SHELL_VIEW (shell_view)); g_return_if_fail (GTK_IS_UI_MANAGER (ui_manager)); g_return_if_fail (GTK_IS_ACTION_GROUP (action_group)); @@ -2278,3 +2277,180 @@ e_templates_store_build_menu (ETemplatesStore *templates_store, g_free (tmp_menu_path); } + +static void +templates_store_add_to_tree_store_recurse (EMailTemplatesStore *templates_store, + GNode *node, + GtkTreeStore *tree_store, + GtkTreeIter *parent, + gboolean with_folder_name, + const gchar *find_folder_uri, + const gchar *find_message_uid, + gboolean *out_found_message, + GtkTreeIter *out_found_iter, + gboolean *out_found_first_message, + GtkTreeIter *out_found_first_iter) +{ + TmplFolderData *tfd; + + g_return_if_fail (node != NULL); + g_return_if_fail (tree_store != NULL); + + while (node) { + tfd = node->data; + if (tfd) { + tmpl_folder_data_lock (tfd); + + if (tfd->folder) { + GtkTreeIter *pparent = parent, iparent, iter; + GSList *link; + gboolean is_the_folder = FALSE; + + if (out_found_message && !*out_found_message && out_found_iter && find_folder_uri && *find_folder_uri) { + gchar *folder_uri; + + folder_uri = e_mail_folder_uri_from_folder (tfd->folder); + is_the_folder = g_strcmp0 (folder_uri, find_folder_uri) == 0; + g_free (folder_uri); + } + + if (with_folder_name) { + gtk_tree_store_append (tree_store, &iparent, pparent); + gtk_tree_store_set (tree_store, &iparent, + E_MAIL_TEMPLATES_STORE_COLUMN_DISPLAY_NAME, camel_folder_get_display_name (tfd->folder), + -1); + + pparent = &iparent; + } + + if (node->children) { + templates_store_add_to_tree_store_recurse (templates_store, node->children, tree_store, pparent, + TRUE, find_folder_uri, find_message_uid, out_found_message, out_found_iter, + out_found_first_message, out_found_first_iter); + } + + for (link = tfd->messages; link; link = g_slist_next (link)) { + TmplMessageData *tmd = link->data; + + if (tmd && tmd->uid && tmd->subject) { + gtk_tree_store_append (tree_store, &iter, pparent); + gtk_tree_store_set (tree_store, &iter, + E_MAIL_TEMPLATES_STORE_COLUMN_DISPLAY_NAME, tmd->subject, + E_MAIL_TEMPLATES_STORE_COLUMN_FOLDER, tfd->folder, + E_MAIL_TEMPLATES_STORE_COLUMN_MESSAGE_UID, tmd->uid, + -1); + + if (!*out_found_first_message) { + *out_found_first_message = TRUE; + *out_found_first_iter = iter; + } + + if (is_the_folder && out_found_message && !*out_found_message) { + *out_found_message = g_strcmp0 (tmd->uid, find_message_uid) == 0; + + if (*out_found_message && out_found_iter) + *out_found_iter = iter; + } + } + } + } + + tmpl_folder_data_unlock (tfd); + } + + node = node->next; + } +} + +GtkTreeStore * +e_mail_templates_store_build_model (EMailTemplatesStore *templates_store, + const gchar *find_folder_uri, + const gchar *find_message_uid, + gboolean *out_found_message, + GtkTreeIter *out_found_iter) +{ + GtkTreeStore *tree_store; + GSList *link; + gint multiple_accounts = 0; + gboolean found_first_message = FALSE; + GtkTreeIter found_first_iter; + + g_return_val_if_fail (E_IS_MAIL_TEMPLATES_STORE (templates_store), NULL); + + if (out_found_message) + *out_found_message = FALSE; + + tree_store = gtk_tree_store_new (E_MAIL_TEMPLATES_STORE_N_COLUMNS, + G_TYPE_STRING, /* E_MAIL_TEMPLATES_STORE_COLUMN_DISPLAY_NAME */ + CAMEL_TYPE_FOLDER, /* E_MAIL_TEMPLATES_STORE_COLUMN_FOLDER */ + G_TYPE_STRING); /* E_MAIL_TEMPLATES_STORE_COLUMN_MESSAGE_UID */ + + templates_store_lock (templates_store); + + for (link = templates_store->priv->stores; link && multiple_accounts <= 1; link = g_slist_next (link)) { + TmplStoreData *tsd = link->data; + + if (!tsd) + continue; + + tmpl_store_data_lock (tsd); + + if (tsd->folders && tsd->folders->children) { + CamelStore *store; + + store = g_weak_ref_get (tsd->store_weakref); + if (store) { + g_node_traverse (tsd->folders, G_PRE_ORDER, G_TRAVERSE_ALL, -1, + tmpl_store_data_folder_has_messages_cb, &multiple_accounts); + } + + g_clear_object (&store); + } + + tmpl_store_data_unlock (tsd); + } + + for (link = templates_store->priv->stores; link && multiple_accounts > 0; link = g_slist_next (link)) { + TmplStoreData *tsd = link->data; + + if (!tsd) + continue; + + tmpl_store_data_lock (tsd); + + if (tsd->folders && tsd->folders->children) { + CamelStore *store; + + store = g_weak_ref_get (tsd->store_weakref); + if (store) { + GtkTreeIter *pparent = NULL, parent; + + if (multiple_accounts > 1) { + gtk_tree_store_append (tree_store, &parent, NULL); + gtk_tree_store_set (tree_store, &parent, + E_MAIL_TEMPLATES_STORE_COLUMN_DISPLAY_NAME, camel_service_get_display_name (CAMEL_SERVICE (store)), + -1); + + pparent = &parent; + } + + templates_store_add_to_tree_store_recurse (templates_store, tsd->folders->children, tree_store, pparent, FALSE, + find_folder_uri, find_message_uid, out_found_message, out_found_iter, + &found_first_message, &found_first_iter); + } + + g_clear_object (&store); + } + + tmpl_store_data_unlock (tsd); + } + + templates_store_unlock (templates_store); + + if (out_found_message && !*out_found_message && out_found_iter) { + *out_found_message = found_first_message; + *out_found_iter = found_first_iter; + } + + return tree_store; +} diff --git a/src/mail/e-mail-templates-store.h b/src/mail/e-mail-templates-store.h new file mode 100644 index 0000000000..f9542574bc --- /dev/null +++ b/src/mail/e-mail-templates-store.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com) + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef E_MAIL_TEMPLATES_STORE_H +#define E_MAIL_TEMPLATES_STORE_H + +#include +#include + +#include +#include +#include +#include +#include + +/* Standard GObject macros */ +#define E_TYPE_MAIL_TEMPLATES_STORE \ + (e_mail_templates_store_get_type ()) +#define E_MAIL_TEMPLATES_STORE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_MAIL_TEMPLATES_STORE, EMailTemplatesStore)) +#define E_MAIL_TEMPLATES_STORE_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_MAIL_TEMPLATES_STORE, EMailTemplatesStoreClass)) +#define E_IS_MAIL_TEMPLATES_STORE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_MAIL_TEMPLATES_STORE)) +#define E_IS_MAIL_TEMPLATES_STORE_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_MAIL_TEMPLATES_STORE)) +#define E_MAIL_TEMPLATES_STORE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_MAIL_TEMPLATES_STORE, EMailTemplatesStoreClass)) + +G_BEGIN_DECLS + +enum { + E_MAIL_TEMPLATES_STORE_COLUMN_DISPLAY_NAME = 0, /* gchar * */ + E_MAIL_TEMPLATES_STORE_COLUMN_FOLDER, /* CamelFolder * */ + E_MAIL_TEMPLATES_STORE_COLUMN_MESSAGE_UID, /* gchar * */ + E_MAIL_TEMPLATES_STORE_N_COLUMNS +}; + +typedef struct _EMailTemplatesStore EMailTemplatesStore; +typedef struct _EMailTemplatesStoreClass EMailTemplatesStoreClass; +typedef struct _EMailTemplatesStorePrivate EMailTemplatesStorePrivate; + +/** + * EMailTemplatesStore: + * + * Contains only private data that should be read and manipulated using + * the functions below. + **/ +struct _EMailTemplatesStore { + GObject parent; + EMailTemplatesStorePrivate *priv; +}; + +struct _EMailTemplatesStoreClass { + GObjectClass parent_class; + + /* Signals */ + void (*changed) (EMailTemplatesStore *templates_store); +}; + +typedef void (* EMailTemplatesStoreActionFunc) (EMailTemplatesStore *templates_store, + CamelFolder *folder, + const gchar *message_uid, + gpointer user_data); + +GType e_mail_templates_store_get_type (void) G_GNUC_CONST; +EMailTemplatesStore * + e_mail_templates_store_ref_default + (EMailAccountStore *account_store); +EMailAccountStore * + e_mail_templates_store_ref_account_store + (EMailTemplatesStore *templates_store); +void e_mail_templates_store_build_menu + (EMailTemplatesStore *templates_store, + EShellView *shell_view, + GtkUIManager *ui_manager, + GtkActionGroup *action_group, + const gchar *base_menu_path, + guint merge_id, + EMailTemplatesStoreActionFunc action_cb, + gpointer action_cb_user_data); +GtkTreeStore * e_mail_templates_store_build_model + (EMailTemplatesStore *templates_store, + const gchar *find_folder_uri, + const gchar *find_message_uid, + gboolean *out_found_message, + GtkTreeIter *out_found_iter); + +G_END_DECLS + +#endif /* E_MAIL_TEMPLATES_STORE_H */ diff --git a/src/mail/e-mail-templates.c b/src/mail/e-mail-templates.c new file mode 100644 index 0000000000..8d3bb7a5bb --- /dev/null +++ b/src/mail/e-mail-templates.c @@ -0,0 +1,497 @@ +/* + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * Copyright (C) 2008 - Diego Escalante Urrelo + * Copyright (C) 2018 Red Hat, Inc. (www.redhat.com) + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authors: + * Diego Escalante Urrelo + * Bharath Acharya + */ + +#include "evolution-config.h" + +#include + +#include "e-util/e-util.h" + +#include "e-mail-templates.h" + +/* Replaces $ORIG[variable] in given template by given replacement from the original message */ +static void +replace_template_variable (GString *text, + const gchar *variable, + const gchar *replacement) +{ + const gchar *p, *next; + GString *str; + gint find_len; + gchar *find; + + g_return_if_fail (text != NULL); + g_return_if_fail (variable != NULL); + g_return_if_fail (*variable); + + find = g_strconcat ("$ORIG[", variable, "]", NULL); + + find_len = strlen (find); + str = g_string_new (""); + p = text->str; + while (next = e_util_strstrcase (p, find), next) { + if (p < next) + g_string_append_len (str, p, next - p); + if (replacement && *replacement) + g_string_append (str, replacement); + p = next + find_len; + } + g_string_append (str, p); + + g_string_assign (text, str->str); + + g_string_free (str, TRUE); + g_free (find); +} + +static void +replace_email_addresses (GString *template, + CamelInternetAddress *internet_address, + const gchar *variable) +{ + gint address_index = 0; + GString *emails = g_string_new (""); + const gchar *address_name, *address_email; + + g_return_if_fail (template); + g_return_if_fail (internet_address); + g_return_if_fail (variable); + + while (camel_internet_address_get (internet_address, address_index, &address_name, &address_email)) { + gchar *address = camel_internet_address_format_address (address_name, address_email); + + if (address_index > 0) + g_string_append_printf (emails, ", %s", address); + else + g_string_append_printf (emails, "%s", address); + + address_index++; + g_free (address); + } + replace_template_variable (template, variable, emails->str); + g_string_free (emails, TRUE); +} + +static CamelMimePart * +fill_template (CamelMimeMessage *message, + CamelMimePart *template) +{ + const CamelNameValueArray *headers; + CamelContentType *ct; + CamelStream *stream; + CamelMimePart *return_part; + CamelMimePart *message_part = NULL; + CamelDataWrapper *dw; + CamelInternetAddress *internet_address; + GString *template_body; + GByteArray *byte_array; + gint i; + guint jj, len; + gboolean message_html, template_html; + + ct = camel_mime_part_get_content_type (template); + template_html = ct && camel_content_type_is (ct, "text", "html"); + + message_html = FALSE; + /* When template is html, then prefer HTML part of the original message. Otherwise go for plaintext */ + dw = camel_medium_get_content (CAMEL_MEDIUM (message)); + if (CAMEL_IS_MULTIPART (dw)) { + CamelMultipart *multipart = CAMEL_MULTIPART (dw); + + for (i = 0; i < camel_multipart_get_number (multipart); i++) { + CamelMimePart *part = camel_multipart_get_part (multipart, i); + CamelContentType *ct = camel_mime_part_get_content_type (part); + + if (!ct) + continue; + + if (camel_content_type_is (ct, "text", "html") && template_html) { + message_part = camel_multipart_get_part (multipart, i); + message_html = TRUE; + break; + } else if (camel_content_type_is (ct, "text", "plain") && message_html == FALSE) { + message_part = camel_multipart_get_part (multipart, i); + } + } + } else + message_part = CAMEL_MIME_PART (message); + + /* Get content of the template */ + stream = camel_stream_mem_new (); + camel_data_wrapper_decode_to_stream_sync (camel_medium_get_content (CAMEL_MEDIUM (template)), stream, NULL, NULL); + camel_stream_flush (stream, NULL, NULL); + byte_array = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (stream)); + template_body = g_string_new_len ((gchar *) byte_array->data, byte_array->len); + g_object_unref (stream); + + /* Replace all $ORIG[header_name] by respective values */ + headers = camel_medium_get_headers (CAMEL_MEDIUM (message)); + len = camel_name_value_array_get_length (headers); + for (jj = 0; jj < len; jj++) { + const gchar *header_name = NULL, *header_value = NULL; + + if (!camel_name_value_array_get (headers, jj, &header_name, &header_value) || + !header_name) + continue; + + if (g_ascii_strncasecmp (header_name, "content-", 8) != 0 && + g_ascii_strcasecmp (header_name, "to") != 0 && + g_ascii_strcasecmp (header_name, "cc") != 0 && + g_ascii_strcasecmp (header_name, "bcc") != 0 && + g_ascii_strcasecmp (header_name, "from") != 0 && + g_ascii_strcasecmp (header_name, "subject") != 0) + replace_template_variable (template_body, header_name, header_value); + } + + /* Now manually replace the *subject* header. The header->value for subject header could be + * base64 encoded, so let camel_mime_message to decode it for us if needed */ + replace_template_variable (template_body, "subject", camel_mime_message_get_subject (message)); + + /* Replace TO and FROM modifiers. */ + internet_address = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_TO); + replace_email_addresses (template_body, internet_address, "to"); + + internet_address = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_CC); + replace_email_addresses (template_body, internet_address, "cc"); + + internet_address = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_BCC); + replace_email_addresses (template_body, internet_address, "bcc"); + + internet_address = camel_mime_message_get_from (message); + replace_email_addresses (template_body, internet_address, "from"); + + /* Now extract body of the original message and replace the $ORIG[body] modifier in template */ + if (message_part && e_util_strstrcase (template_body->str, "$ORIG[body]")) { + GString *message_body; + CamelStream *mem_stream; + + stream = camel_stream_mem_new (); + mem_stream = stream; + + ct = camel_mime_part_get_content_type (message_part); + if (ct) { + const gchar *charset = camel_content_type_param (ct, "charset"); + if (charset && *charset) { + CamelMimeFilter *filter = camel_mime_filter_charset_new (charset, "UTF-8"); + if (filter) { + CamelStream *filtered = camel_stream_filter_new (stream); + + if (filtered) { + camel_stream_filter_add (CAMEL_STREAM_FILTER (filtered), filter); + g_object_unref (stream); + stream = filtered; + } + + g_object_unref (filter); + } + } + } + + camel_data_wrapper_decode_to_stream_sync (camel_medium_get_content (CAMEL_MEDIUM (message_part)), stream, NULL, NULL); + camel_stream_flush (stream, NULL, NULL); + byte_array = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (mem_stream)); + message_body = g_string_new_len ((gchar *) byte_array->data, byte_array->len); + g_object_unref (stream); + + if (template_html && !message_html) { + gchar *html = camel_text_to_html ( + message_body->str, + CAMEL_MIME_FILTER_TOHTML_CONVERT_NL | + CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES | + CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS | + CAMEL_MIME_FILTER_TOHTML_MARK_CITATION | + CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES, 0); + g_string_assign (message_body, html); + g_free (html); + } else if (!template_html && message_html) { + g_string_prepend (message_body, "
");
+			g_string_append (message_body, "
"); + } /* Other cases should not occur. And even if they happen to do, there's nothing we can really do about it */ + + replace_template_variable (template_body, "body", message_body->str); + g_string_free (message_body, TRUE); + } else { + replace_template_variable (template_body, "body", ""); + } + + return_part = camel_mime_part_new (); + + if (template_html) + camel_mime_part_set_content (return_part, template_body->str, template_body->len, "text/html"); + else + camel_mime_part_set_content (return_part, template_body->str, template_body->len, "text/plain"); + + g_string_free (template_body, TRUE); + + return return_part; +} + +static CamelMimePart * +find_template_part_in_multipart (CamelMultipart *multipart, + CamelMultipart *new_multipart) +{ + CamelMimePart *template_part = NULL; + gint ii; + + for (ii = 0; ii < camel_multipart_get_number (multipart); ii++) { + CamelMimePart *part = camel_multipart_get_part (multipart, ii); + CamelContentType *ct = camel_mime_part_get_content_type (part); + + if (!template_part && ct && camel_content_type_is (ct, "multipart", "*")) { + CamelDataWrapper *dw; + + dw = camel_medium_get_content (CAMEL_MEDIUM (part)); + template_part = (dw && CAMEL_IS_MULTIPART (dw)) ? + find_template_part_in_multipart (CAMEL_MULTIPART (dw), new_multipart) : NULL; + + if (!template_part) { + /* Copy any other parts (attachments...) to the output message */ + camel_mime_part_set_disposition (part, "attachment"); + camel_multipart_add_part (new_multipart, part); + } + } else if (ct && camel_content_type_is (ct, "text", "html")) { + template_part = part; + } else if (ct && camel_content_type_is (ct, "text", "plain") && !template_part) { + template_part = part; + } else { + /* Copy any other parts (attachments...) to the output message */ + camel_mime_part_set_disposition (part, "attachment"); + camel_multipart_add_part (new_multipart, part); + } + } + + return template_part; +} + +CamelMimeMessage * +e_mail_templates_apply_sync (CamelMimeMessage *source_message, + CamelFolder *templates_folder, + const gchar *templates_message_uid, + GCancellable *cancellable, + GError **error) +{ + CamelMimeMessage *template_message, *result_message = NULL; + CamelMultipart *new_multipart; + CamelDataWrapper *dw; + const CamelNameValueArray *headers; + CamelMimePart *template_part = NULL; + guint ii, len; + + g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (source_message), NULL); + g_return_val_if_fail (CAMEL_IS_FOLDER (templates_folder), NULL); + g_return_val_if_fail (templates_message_uid != NULL, NULL); + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return NULL; + + template_message = camel_folder_get_message_sync (templates_folder, templates_message_uid, cancellable, error); + if (!template_message) + return NULL; + + result_message = camel_mime_message_new (); + new_multipart = camel_multipart_new (); + camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (new_multipart), "multipart/alternative"); + camel_multipart_set_boundary (new_multipart, NULL); + + dw = camel_medium_get_content (CAMEL_MEDIUM (template_message)); + /* If template is a multipart, then try to use HTML. When no HTML part is available, use plaintext. Every other + * add as an attachment */ + if (CAMEL_IS_MULTIPART (dw)) { + template_part = find_template_part_in_multipart (CAMEL_MULTIPART (dw), new_multipart); + } else { + CamelContentType *ct = camel_mime_part_get_content_type (CAMEL_MIME_PART (template_message)); + + if (ct && (camel_content_type_is (ct, "text", "html") || + camel_content_type_is (ct, "text", "plain"))) { + template_part = CAMEL_MIME_PART (template_message); + } + } + + g_warn_if_fail (template_part != NULL); + + if (template_part) { + CamelMimePart *out_part = NULL; + + /* Here replace all the modifiers in template body by values + from message and return the newly created part */ + out_part = fill_template (source_message, template_part); + + /* Assigning part directly to mime_message causes problem with + "Content-type" header displaying in the HTML message (camel parsing bug?) */ + camel_multipart_add_part (new_multipart, out_part); + g_object_unref (out_part); + } + + camel_medium_set_content (CAMEL_MEDIUM (result_message), CAMEL_DATA_WRAPPER (new_multipart)); + + /* Add the headers from the message we are replying to, so CC and that + * stuff is preserved. Also replace any $ORIG[header-name] modifiers ignoring + * 'content-*' headers */ + headers = camel_medium_dup_headers (CAMEL_MEDIUM (source_message)); + len = camel_name_value_array_get_length (headers); + for (ii = 0; ii < len; ii++) { + const gchar *header_name = NULL, *header_value = NULL; + + if (!camel_name_value_array_get (headers, ii, &header_name, &header_value) || + !header_name) + continue; + + if (g_ascii_strncasecmp (header_name, "content-", 8) != 0 && + g_ascii_strcasecmp (header_name, "from") != 0) { + gchar *new_header_value = NULL; + + /* Some special handling of the 'subject' header */ + if (g_ascii_strncasecmp (header_name, "subject", 7) == 0) { + GString *subject = g_string_new (camel_mime_message_get_subject (template_message)); + guint jj; + + /* Now replace all possible $ORIG[]s in the subject line by values from original message */ + for (jj = 0; jj < len; jj++) { + const gchar *m_header_name = NULL, *m_header_value = NULL; + + if (camel_name_value_array_get (headers, jj, &m_header_name, &m_header_value) && + m_header_name && + g_ascii_strncasecmp (m_header_name, "content-", 8) != 0 && + g_ascii_strcasecmp (m_header_name, "subject") != 0) + replace_template_variable (subject, m_header_name, m_header_value); + } + /* Now replace $ORIG[subject] variable, handling possible base64 encryption */ + replace_template_variable ( + subject, "subject", + camel_mime_message_get_subject (source_message)); + new_header_value = g_string_free (subject, FALSE); + } + + camel_medium_add_header (CAMEL_MEDIUM (result_message), header_name, new_header_value ? new_header_value : header_value); + + g_free (new_header_value); + } + } + + /* Set the To: field to the same To: field of the message we are replying to. */ + camel_mime_message_set_recipients ( + result_message, CAMEL_RECIPIENT_TYPE_TO, + camel_mime_message_get_reply_to (source_message) ? camel_mime_message_get_reply_to (source_message) : + camel_mime_message_get_from (source_message)); + + /* Copy the CC and BCC from the template.*/ + camel_mime_message_set_recipients (result_message, CAMEL_RECIPIENT_TYPE_CC, + camel_mime_message_get_recipients (template_message, CAMEL_RECIPIENT_TYPE_CC)); + + camel_mime_message_set_recipients (result_message, CAMEL_RECIPIENT_TYPE_BCC, + camel_mime_message_get_recipients (template_message, CAMEL_RECIPIENT_TYPE_BCC)); + + g_clear_object (&template_message); + g_clear_object (&new_multipart); + + return result_message; +} + +typedef struct _AsyncContext { + CamelMimeMessage *source_message; + CamelFolder *templates_folder; + gchar *templates_message_uid; + CamelMimeMessage *result_message; +} AsyncContext; + +static void +async_context_free (gpointer ptr) +{ + AsyncContext *context = ptr; + + if (context) { + g_clear_object (&context->source_message); + g_clear_object (&context->templates_folder); + g_clear_object (&context->result_message); + g_free (context->templates_message_uid); + g_free (context); + } +} + +static void +e_mail_templates_apply_thread (ESimpleAsyncResult *simple, + gpointer source_object, + GCancellable *cancellable) +{ + AsyncContext *context; + GError *local_error = NULL; + + context = e_simple_async_result_get_op_pointer (simple); + g_return_if_fail (context != NULL); + + context->result_message = e_mail_templates_apply_sync (context->source_message, + context->templates_folder, context->templates_message_uid, cancellable, &local_error); + + if (local_error) + e_simple_async_result_take_error (simple, local_error); +} + +void +e_mail_templates_apply (CamelMimeMessage *source_message, + CamelFolder *templates_folder, + const gchar *templates_message_uid, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + ESimpleAsyncResult *simple; + AsyncContext *context; + + g_return_if_fail (CAMEL_IS_MIME_MESSAGE (source_message)); + g_return_if_fail (CAMEL_IS_FOLDER (templates_folder)); + g_return_if_fail (templates_message_uid != NULL); + g_return_if_fail (callback != NULL); + + context = g_new0 (AsyncContext, 1); + context->source_message = g_object_ref (source_message); + context->templates_folder = g_object_ref (templates_folder); + context->templates_message_uid = g_strdup (templates_message_uid); + context->result_message = NULL; + + simple = e_simple_async_result_new (G_OBJECT (source_message), callback, + user_data, e_mail_templates_apply); + + e_simple_async_result_set_op_pointer (simple, context, (GDestroyNotify) async_context_free); + + e_simple_async_result_run_in_thread (simple, G_PRIORITY_DEFAULT, e_mail_templates_apply_thread, cancellable); + + g_object_unref (simple); +} + +CamelMimeMessage * +e_mail_templates_apply_finish (GObject *source_object, + GAsyncResult *result, + GError **error) +{ + ESimpleAsyncResult *simple; + AsyncContext *context; + + g_return_val_if_fail (e_simple_async_result_is_valid (result, source_object, e_mail_templates_apply), NULL); + + simple = E_SIMPLE_ASYNC_RESULT (result); + context = e_simple_async_result_get_op_pointer (simple); + + if (e_simple_async_result_propagate_error (simple, error)) + return NULL; + + return context->result_message ? g_object_ref (context->result_message) : NULL; +} diff --git a/src/mail/e-mail-templates.h b/src/mail/e-mail-templates.h new file mode 100644 index 0000000000..348fb112c2 --- /dev/null +++ b/src/mail/e-mail-templates.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * Copyright (C) 2008 - Diego Escalante Urrelo + * Copyright (C) 2018 Red Hat, Inc. (www.redhat.com) + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authors: + * Diego Escalante Urrelo + * Bharath Acharya + */ + +#ifndef E_MAIL_TEMPLATES_H +#define E_MAIL_TEMPLATES_H + +#include + +G_BEGIN_DECLS + +CamelMimeMessage * + e_mail_templates_apply_sync (CamelMimeMessage *source_message, + CamelFolder *templates_folder, + const gchar *templates_message_uid, + GCancellable *cancellable, + GError **error); +void e_mail_templates_apply (CamelMimeMessage *source_message, + CamelFolder *templates_folder, + const gchar *templates_message_uid, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +CamelMimeMessage * + e_mail_templates_apply_finish (GObject *source_object, + GAsyncResult *result, + GError **error); + +G_END_DECLS + +#endif /* E_MAIL_TEMPLATES_H */ diff --git a/src/mail/em-composer-utils.c b/src/mail/em-composer-utils.c index c710512275..2aaa369077 100644 --- a/src/mail/em-composer-utils.c +++ b/src/mail/em-composer-utils.c @@ -43,6 +43,8 @@ #include "e-mail-printer.h" #include "e-mail-ui-session.h" +#include "e-mail-templates.h" +#include "e-mail-templates-store.h" #include "em-utils.h" #include "em-composer-utils.h" #include "em-folder-selector.h" @@ -2668,64 +2670,58 @@ em_utils_camel_address_to_destination (CamelInternetAddress *iaddr) return destv; } -static void -reply_setup_composer (EMsgComposer *composer, - CamelMimeMessage *message, - const gchar *identity_uid, - const gchar *identity_name, - const gchar *identity_address, - CamelInternetAddress *to, - CamelInternetAddress *cc, - CamelFolder *folder, - const gchar *message_uid, - CamelNNTPAddress *postto) +static gchar * +emcu_construct_reply_subject (const gchar *source_subject) { - gchar *message_id, *references; - EDestination **tov, **ccv; - EComposerHeaderTable *table; - CamelMedium *medium; - gchar *subject; - - g_return_if_fail (E_IS_MSG_COMPOSER (composer)); - g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message)); - - e_msg_composer_set_is_reply_or_forward (composer, TRUE); - - if (to != NULL) - g_return_if_fail (CAMEL_IS_INTERNET_ADDRESS (to)); + gchar *res; - if (cc != NULL) - g_return_if_fail (CAMEL_IS_INTERNET_ADDRESS (cc)); - - /* construct the tov/ccv */ - tov = em_utils_camel_address_to_destination (to); - ccv = em_utils_camel_address_to_destination (cc); - - /* Set the subject of the new message. */ - if ((subject = (gchar *) camel_mime_message_get_subject (message))) { + if (source_subject) { GSettings *settings; gboolean skip_len = -1; - if (em_utils_is_re_in_subject (subject, &skip_len, NULL, NULL) && skip_len > 0) - subject = subject + skip_len; + if (em_utils_is_re_in_subject (source_subject, &skip_len, NULL, NULL) && skip_len > 0) + source_subject = source_subject + skip_len; settings = e_util_ref_settings ("org.gnome.evolution.mail"); if (g_settings_get_boolean (settings, "composer-use-localized-fwd-re")) { /* Translators: This is a reply attribution in the message reply subject. The %s is replaced with the subject of the original message. Both 'Re'-s in the 'reply-attribution' translation context should translate into the same string, the same as the ':' separator. */ - subject = g_strdup_printf (C_("reply-attribution", "Re: %s"), subject); + res = g_strdup_printf (C_("reply-attribution", "Re: %s"), source_subject); } else { /* Do not localize this string */ - subject = g_strdup_printf ("Re: %s", subject); + res = g_strdup_printf ("Re: %s", source_subject); } g_clear_object (&settings); } else { - subject = g_strdup (""); + res = g_strdup (""); } + return res; +} + +static void +reply_setup_composer_recipients (EMsgComposer *composer, + CamelInternetAddress *to, + CamelInternetAddress *cc, + CamelFolder *folder, + const gchar *message_uid, + CamelNNTPAddress *postto) +{ + EComposerHeaderTable *table; + EDestination **tov, **ccv; + + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + if (to != NULL) + g_return_if_fail (CAMEL_IS_INTERNET_ADDRESS (to)); + + if (cc != NULL) + g_return_if_fail (CAMEL_IS_INTERNET_ADDRESS (cc)); + + /* Construct the tov/ccv */ + tov = em_utils_camel_address_to_destination (to); + ccv = em_utils_camel_address_to_destination (cc); + table = e_msg_composer_get_header_table (composer); - e_composer_header_table_set_subject (table, subject); e_composer_header_table_set_destinations_to (table, tov); - e_composer_header_table_set_identity_uid (table, identity_uid, identity_name, identity_address); /* Add destinations instead of setting, so we don't remove * automatic CC addresses that have already been added. */ @@ -2733,9 +2729,8 @@ reply_setup_composer (EMsgComposer *composer, e_destination_freev (tov); e_destination_freev (ccv); - g_free (subject); - /* add post-to, if nessecary */ + /* Add post-to, if necessary */ if (postto && camel_address_length ((CamelAddress *) postto)) { CamelFolder *use_folder = folder, *temp_folder = NULL; gchar *store_url = NULL; @@ -2773,6 +2768,46 @@ reply_setup_composer (EMsgComposer *composer, g_free (store_url); g_clear_object (&temp_folder); } +} + +static void +reply_setup_composer (EMsgComposer *composer, + CamelMimeMessage *message, + const gchar *identity_uid, + const gchar *identity_name, + const gchar *identity_address, + CamelInternetAddress *to, + CamelInternetAddress *cc, + CamelFolder *folder, + const gchar *message_uid, + CamelNNTPAddress *postto) +{ + gchar *message_id, *references; + EComposerHeaderTable *table; + CamelMedium *medium; + gchar *subject; + + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message)); + + e_msg_composer_set_is_reply_or_forward (composer, TRUE); + + if (to != NULL) + g_return_if_fail (CAMEL_IS_INTERNET_ADDRESS (to)); + + if (cc != NULL) + g_return_if_fail (CAMEL_IS_INTERNET_ADDRESS (cc)); + + reply_setup_composer_recipients (composer, to, cc, folder, message_uid, postto); + + /* Set the subject of the new message. */ + subject = emcu_construct_reply_subject (camel_mime_message_get_subject (message)); + + table = e_msg_composer_get_header_table (composer); + e_composer_header_table_set_subject (table, subject); + e_composer_header_table_set_identity_uid (table, identity_uid, identity_name, identity_address); + + g_free (subject); /* Add In-Reply-To and References. */ @@ -3566,9 +3601,722 @@ emcu_folder_is_inbox (CamelFolder *folder) return is_inbox; } +typedef struct _AltReplyContext { + EShell *shell; + EAlertSink *alert_sink; + CamelMimeMessage *source_message; + CamelFolder *folder; + gchar *message_uid; + CamelMimeMessage *new_message; /* When processed with a template */ + EMailPartList *source; + EMailReplyType type; + EMailReplyStyle style; + guint32 flags; + gboolean template_preserve_subject; +} AltReplyContext; + +static void +alt_reply_context_free (gpointer ptr) +{ + AltReplyContext *context = ptr; + + if (context) { + g_clear_object (&context->shell); + g_clear_object (&context->alert_sink); + g_clear_object (&context->source_message); + g_clear_object (&context->folder); + g_clear_object (&context->source); + g_clear_object (&context->new_message); + g_free (context->message_uid); + g_free (context); + } +} + +static void +alt_reply_composer_created_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + AltReplyContext *context = user_data; + EMsgComposer *composer; + GError *error = NULL; + + g_return_if_fail (context != NULL); + + composer = e_msg_composer_new_finish (result, &error); + + if (composer) { + EContentEditor *cnt_editor; + + cnt_editor = e_html_editor_get_content_editor (e_msg_composer_get_editor (composer)); + + if (context->new_message) { + CamelInternetAddress *to = NULL, *cc = NULL; + CamelNNTPAddress *postto = NULL; + gboolean need_reply_all = FALSE; + + if ((context->flags & (E_MAIL_REPLY_FLAG_FORMAT_PLAIN | E_MAIL_REPLY_FLAG_FORMAT_HTML)) != 0) { + e_content_editor_set_html_mode (cnt_editor, (context->flags & E_MAIL_REPLY_FLAG_FORMAT_HTML) != 0); + } + + em_utils_edit_message (composer, context->folder, context->new_message, context->message_uid, TRUE); + + if (context->type == E_MAIL_REPLY_TO_SENDER) { + /* Reply to sender */ + to = camel_internet_address_new (); + if (context->folder) + postto = camel_nntp_address_new (); + get_reply_sender (context->source_message, to, postto); + } else if (context->type == E_MAIL_REPLY_TO_LIST) { + /* Reply to list */ + to = camel_internet_address_new (); + + if (!get_reply_list (context->source_message, to)) { + need_reply_all = TRUE; + g_clear_object (&to); + } + } else if (context->type != E_MAIL_REPLY_TO_ALL) { + g_warn_if_reached (); + } + + if (context->type == E_MAIL_REPLY_TO_ALL || need_reply_all) { + /* Reply to all */ + to = camel_internet_address_new (); + cc = camel_internet_address_new (); + + if (context->folder) + postto = camel_nntp_address_new (); + + em_utils_get_reply_all (e_shell_get_registry (context->shell), context->source_message, to, cc, postto); + } + + reply_setup_composer_recipients (composer, to, cc, context->folder, context->message_uid, postto); + + composer_set_no_change (composer); + + g_clear_object (&to); + g_clear_object (&cc); + g_clear_object (&postto); + + if (context->folder && context->message_uid) { + gchar *source_folder_uri = NULL; + gchar *source_message_uid = NULL; + + em_utils_get_real_folder_uri_and_message_uid (context->folder, context->message_uid, + &source_folder_uri, &source_message_uid); + + if (!source_message_uid) + source_message_uid = g_strdup (context->message_uid); + + if (source_folder_uri) { + e_msg_composer_set_source_headers (composer, source_folder_uri, + source_message_uid, CAMEL_MESSAGE_ANSWERED | CAMEL_MESSAGE_SEEN); + } + + g_free (source_folder_uri); + g_free (source_message_uid); + } + } else { + em_utils_reply_to_message (composer, context->source_message, + context->folder, context->message_uid, context->type, context->style, + context->source, NULL, context->flags); + } + } else { + e_alert_submit (context->alert_sink, "mail-composer:failed-create-composer", + error ? error->message : _("Unknown error"), NULL); + } + + alt_reply_context_free (context); + g_clear_error (&error); +} + +static void +alt_reply_template_applied_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + AltReplyContext *context = user_data; + GError *error = NULL; + + g_return_if_fail (context != NULL); + + context->new_message = e_mail_templates_apply_finish (source_object, result, &error); + + if (context->new_message) { + if (context->template_preserve_subject) { + gchar *subject; + + subject = emcu_construct_reply_subject (camel_mime_message_get_subject (context->source_message)); + camel_mime_message_set_subject (context->new_message, subject); + g_free (subject); + } + + e_msg_composer_new (context->shell, alt_reply_composer_created_cb, context); + } else { + e_alert_submit (context->alert_sink, "mail:no-retrieve-message", + error ? error->message : _("Unknown error"), NULL); + alt_reply_context_free (context); + } + + g_clear_error (&error); +} + +static void +emcu_three_state_toggled_cb (GtkToggleButton *widget, + gpointer user_data) +{ + glong *phandlerid = user_data; + + g_return_if_fail (GTK_IS_TOGGLE_BUTTON (widget)); + g_return_if_fail (phandlerid != NULL); + + g_signal_handler_block (widget, *phandlerid); + + if (gtk_toggle_button_get_inconsistent (widget) && + gtk_toggle_button_get_active (widget)) { + gtk_toggle_button_set_active (widget, FALSE); + gtk_toggle_button_set_inconsistent (widget, FALSE); + } else if (!gtk_toggle_button_get_active (widget)) { + gtk_toggle_button_set_inconsistent (widget, TRUE); + gtk_toggle_button_set_active (widget, FALSE); + } else { + } + + g_signal_handler_unblock (widget, *phandlerid); +} + +static void +emcu_connect_three_state_changer (GtkToggleButton *toggle_button) +{ + glong *phandlerid; + + g_return_if_fail (GTK_IS_TOGGLE_BUTTON (toggle_button)); + + phandlerid = g_new0 (glong, 1); + + *phandlerid = g_signal_connect_data (toggle_button, "toggled", + G_CALLBACK (emcu_three_state_toggled_cb), + phandlerid, (GClosureNotify) g_free, 0); +} + +static void +emcu_three_state_set_value (GtkToggleButton *toggle_button, + EThreeState value) +{ + g_return_if_fail (GTK_IS_TOGGLE_BUTTON (toggle_button)); + + if (value == E_THREE_STATE_OFF) { + gtk_toggle_button_set_active (toggle_button, FALSE); + gtk_toggle_button_set_inconsistent (toggle_button, FALSE); + } else if (value == E_THREE_STATE_ON) { + gtk_toggle_button_set_active (toggle_button, TRUE); + gtk_toggle_button_set_inconsistent (toggle_button, FALSE); + } else { + gtk_toggle_button_set_active (toggle_button, FALSE); + gtk_toggle_button_set_inconsistent (toggle_button, TRUE); + } +} + +static EThreeState +emcu_three_state_get_value (GtkToggleButton *toggle_button) +{ + g_return_val_if_fail (GTK_IS_TOGGLE_BUTTON (toggle_button), E_THREE_STATE_INCONSISTENT); + + if (gtk_toggle_button_get_inconsistent (toggle_button)) + return E_THREE_STATE_INCONSISTENT; + else if (gtk_toggle_button_get_active (toggle_button)) + return E_THREE_STATE_ON; + + return E_THREE_STATE_OFF; +} + +static GtkComboBox * +emcu_create_templates_combo (EShell *shell, + const gchar *folder_uri, + const gchar *message_uid) +{ + GtkComboBox *combo; + GtkCellRenderer *renderer; + EShellBackend *shell_backend; + EMailSession *mail_session; + EMailTemplatesStore *templates_store; + GtkTreeStore *tree_store; + GtkTreeIter found_iter; + gboolean found_message = FALSE; + + shell_backend = e_shell_get_backend_by_name (shell, "mail"); + g_return_val_if_fail (E_IS_MAIL_BACKEND (shell_backend), NULL); + + mail_session = e_mail_backend_get_session (E_MAIL_BACKEND (shell_backend)); + templates_store = e_mail_templates_store_ref_default (e_mail_ui_session_get_account_store (E_MAIL_UI_SESSION (mail_session))); + + tree_store = e_mail_templates_store_build_model (templates_store, folder_uri, message_uid, &found_message, &found_iter); + + combo = GTK_COMBO_BOX (gtk_combo_box_new_with_model (GTK_TREE_MODEL (tree_store))); + + renderer = gtk_cell_renderer_text_new (); + g_object_set (G_OBJECT (renderer), + "ellipsize", PANGO_ELLIPSIZE_END, + "max-width-chars", 60, + NULL); + + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer, + "text", E_MAIL_TEMPLATES_STORE_COLUMN_DISPLAY_NAME, + NULL); + + g_clear_object (&templates_store); + g_clear_object (&tree_store); + + if (found_message) { + gtk_combo_box_set_active_iter (combo, &found_iter); + } else { + gtk_widget_set_sensitive (GTK_WIDGET (combo), FALSE); + } + + return combo; +} + +/** + * em_utils_reply_alternative: + * @parent: (nullable): a parent #GtkWindow for the question dialog + * @shell: an #EShell instance used to create #EMsgComposer + * @alert_sink: an #EAlertSink to put any errors to + * @message: a #CamelMimeMessage + * @folder: (nullable): a #CamelFolder, or %NULL + * @message_uid: (nullable): the UID of @message, or %NULL + * @style: the reply style to use + * @source: (nullable): source to inherit view settings from + * + * This is similar to em_utils_reply_to_message(), except it asks user to + * change some settings before sending. It calls em_utils_reply_to_message() + * at the end for non-templated replies. + * + * Since: 3.30 + **/ +void +em_utils_reply_alternative (GtkWindow *parent, + EShell *shell, + EAlertSink *alert_sink, + CamelMimeMessage *message, + CamelFolder *folder, + const gchar *message_uid, + EMailReplyStyle default_style, + EMailPartList *source) +{ + GtkWidget *dialog, *widget, *style_label; + GtkBox *hbox, *vbox; + GtkRadioButton *recip_sender, *recip_list, *recip_all; + GtkLabel *sender_label, *list_label, *all_label; + GtkRadioButton *style_default, *style_attach, *style_inline, *style_quote, *style_no_quote; + GtkToggleButton *html_format; + GtkToggleButton *bottom_posting; + GtkCheckButton *apply_template; + GtkComboBox *templates; + GtkCheckButton *preserve_message_subject; + PangoAttrList *attr_list; + GSettings *settings; + gchar *last_tmpl_folder_uri, *last_tmpl_message_uid, *address, *text; + gboolean can_reply_list = FALSE; + CamelInternetAddress *to, *cc; + CamelNNTPAddress *postto = NULL; + gint n_addresses; + + g_return_if_fail (E_IS_SHELL (shell)); + g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message)); + + settings = e_util_ref_settings ("org.gnome.evolution.mail"); + + dialog = gtk_dialog_new_with_buttons (_("Alternative Reply"), parent, + GTK_DIALOG_DESTROY_WITH_PARENT, + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Reply"), GTK_RESPONSE_OK, + NULL); + + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); + + vbox = GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 12); + gtk_box_set_spacing (vbox, 2); + + #define add_with_indent(x) \ + gtk_widget_set_margin_left (GTK_WIDGET (x), 12); \ + gtk_box_pack_start (vbox, GTK_WIDGET (x), FALSE, FALSE, 0); + + widget = gtk_label_new (_("Recipients:")); + g_object_set (G_OBJECT (widget), + "hexpand", FALSE, + "halign", GTK_ALIGN_START, + NULL); + gtk_box_pack_start (vbox, widget, FALSE, FALSE, 0); + + attr_list = pango_attr_list_new (); + pango_attr_list_insert (attr_list, pango_attr_style_new (PANGO_STYLE_ITALIC)); + + #define add_with_label(wgt, lbl) G_STMT_START { \ + GtkWidget *divider_label = gtk_label_new (":"); \ + gtk_label_set_attributes (GTK_LABEL (lbl), attr_list); \ + hbox = GTK_BOX (gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6)); \ + gtk_box_pack_start (hbox, GTK_WIDGET (wgt), FALSE, FALSE, 0); \ + gtk_box_pack_start (hbox, GTK_WIDGET (divider_label), FALSE, FALSE, 0); \ + gtk_box_pack_start (hbox, GTK_WIDGET (lbl), FALSE, FALSE, 0); \ + e_binding_bind_property ( \ + wgt, "sensitive", \ + divider_label, "visible", \ + G_BINDING_SYNC_CREATE); \ + e_binding_bind_property ( \ + wgt, "sensitive", \ + lbl, "visible", \ + G_BINDING_SYNC_CREATE); \ + add_with_indent (hbox); } G_STMT_END + + recip_sender = GTK_RADIO_BUTTON (gtk_radio_button_new_with_mnemonic ( + NULL, _("Reply to _Sender"))); + sender_label = GTK_LABEL (gtk_label_new ("")); + add_with_label (recip_sender, sender_label); + + recip_list = GTK_RADIO_BUTTON (gtk_radio_button_new_with_mnemonic ( + gtk_radio_button_get_group (recip_sender), _("Reply to _List"))); + list_label = GTK_LABEL (gtk_label_new ("")); + add_with_label (recip_list, list_label); + + recip_all = GTK_RADIO_BUTTON (gtk_radio_button_new_with_mnemonic ( + gtk_radio_button_get_group (recip_sender), _("Reply to _All"))); + all_label = GTK_LABEL (gtk_label_new ("")); + add_with_label (recip_all, all_label); + + #undef add_with_label + + pango_attr_list_unref (attr_list); + + /* One line gap between sections */ + widget = gtk_label_new (" "); + gtk_box_pack_start (vbox, widget, FALSE, FALSE, 0); + + style_label = gtk_label_new (_("Reply style:")); + g_object_set (G_OBJECT (style_label), + "hexpand", FALSE, + "halign", GTK_ALIGN_START, + NULL); + gtk_box_pack_start (vbox, style_label, FALSE, FALSE, 0); + + style_default = GTK_RADIO_BUTTON (gtk_radio_button_new_with_mnemonic ( + NULL, _("_Default"))); + add_with_indent (style_default); + + style_attach = GTK_RADIO_BUTTON (gtk_radio_button_new_with_mnemonic ( + gtk_radio_button_get_group (style_default), _("Attach_ment"))); + add_with_indent (style_attach); + + style_inline = GTK_RADIO_BUTTON (gtk_radio_button_new_with_mnemonic ( + gtk_radio_button_get_group (style_default), _("Inline (_Outlook style)"))); + add_with_indent (style_inline); + + style_quote = GTK_RADIO_BUTTON (gtk_radio_button_new_with_mnemonic ( + gtk_radio_button_get_group (style_default), _("_Quote"))); + add_with_indent (style_quote); + + style_no_quote = GTK_RADIO_BUTTON (gtk_radio_button_new_with_mnemonic ( + gtk_radio_button_get_group (style_default), _("Do _Not Quote"))); + add_with_indent (style_no_quote); + + /* One line gap between sections */ + widget = gtk_label_new (" "); + gtk_box_pack_start (vbox, widget, FALSE, FALSE, 0); + + html_format = GTK_TOGGLE_BUTTON (gtk_check_button_new_with_mnemonic (_("_Format message in HTML"))); + gtk_box_pack_start (vbox, GTK_WIDGET (html_format), FALSE, FALSE, 0); + + bottom_posting = GTK_TOGGLE_BUTTON (gtk_check_button_new_with_mnemonic (_("Start _typing at the bottom"))); + gtk_box_pack_start (vbox, GTK_WIDGET (bottom_posting), FALSE, FALSE, 0); + + /* One line gap between sections */ + widget = gtk_label_new (" "); + gtk_box_pack_start (vbox, widget, FALSE, FALSE, 0); + + apply_template = GTK_CHECK_BUTTON (gtk_check_button_new_with_mnemonic (_("Apply t_emplate"))); + gtk_box_pack_start (vbox, GTK_WIDGET (apply_template), FALSE, FALSE, 0); + + last_tmpl_folder_uri = g_settings_get_string (settings, "alt-reply-template-folder-uri"); + last_tmpl_message_uid = g_settings_get_string (settings, "alt-reply-template-message-uid"); + + templates = emcu_create_templates_combo (shell, last_tmpl_folder_uri, last_tmpl_message_uid); + add_with_indent (templates); + + g_free (last_tmpl_folder_uri); + g_free (last_tmpl_message_uid); + + preserve_message_subject = GTK_CHECK_BUTTON (gtk_check_button_new_with_mnemonic (_("Preserve original message S_ubject"))); + add_with_indent (preserve_message_subject); + + #undef add_with_indent + + gtk_widget_show_all (GTK_WIDGET (vbox)); + + #define populate_label_with_text(lbl, txt) \ + g_object_set (G_OBJECT (lbl), \ + "ellipsize", PANGO_ELLIPSIZE_END, \ + "max-width-chars", 50, \ + "label", txt ? txt : "", \ + "tooltip-text", txt ? txt : "", \ + NULL); + + /* Reply to sender */ + to = camel_internet_address_new (); + if (folder) + postto = camel_nntp_address_new (); + get_reply_sender (message, to, postto); + + if (postto && camel_address_length (CAMEL_ADDRESS (postto)) > 0) { + address = camel_address_format (CAMEL_ADDRESS (postto)); + } else { + address = camel_address_format (CAMEL_ADDRESS (to)); + } + + populate_label_with_text (sender_label, address); + + g_clear_object (&postto); + g_clear_object (&to); + g_free (address); + + /* Reply to list */ + to = camel_internet_address_new (); + + if (!get_reply_list (message, to)) { + gtk_widget_set_sensitive (GTK_WIDGET (recip_list), FALSE); + } else { + can_reply_list = TRUE; + + address = camel_address_format (CAMEL_ADDRESS (to)); + populate_label_with_text (list_label, address); + g_free (address); + } + + g_clear_object (&to); + + /* Reply to all */ + to = camel_internet_address_new (); + cc = camel_internet_address_new (); + + if (folder) + postto = camel_nntp_address_new (); + + em_utils_get_reply_all (e_shell_get_registry (shell), message, to, cc, postto); + + if (postto && camel_address_length (CAMEL_ADDRESS (postto)) > 0) { + n_addresses = camel_address_length (CAMEL_ADDRESS (postto)); + address = camel_address_format (CAMEL_ADDRESS (postto)); + } else { + camel_address_cat (CAMEL_ADDRESS (to), CAMEL_ADDRESS (cc)); + n_addresses = camel_address_length (CAMEL_ADDRESS (to)); + address = camel_address_format (CAMEL_ADDRESS (to)); + } + + text = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, "one recipient", "%d recipients", n_addresses), n_addresses); + populate_label_with_text (all_label, text); + gtk_widget_set_tooltip_text (GTK_WIDGET (all_label), address); + + g_clear_object (&to); + g_clear_object (&cc); + g_clear_object (&postto); + g_free (address); + g_free (text); + + #undef populate_label_with_text + + /* Prefer reply-to-list */ + if (g_settings_get_boolean (settings, "composer-group-reply-to-list")) { + if (can_reply_list) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (recip_list), TRUE); + else if (n_addresses > 1) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (recip_all), TRUE); + else + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (recip_sender), TRUE); + + /* Prefer reply-to-all */ + } else { + if (n_addresses > 1) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (recip_all), TRUE); + else if (can_reply_list) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (recip_list), TRUE); + else + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (recip_sender), TRUE); + } + + switch (g_settings_get_enum (settings, "alt-reply-style")) { + case E_MAIL_REPLY_STYLE_UNKNOWN: + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (style_default), TRUE); + break; + case E_MAIL_REPLY_STYLE_QUOTED: + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (style_quote), TRUE); + break; + case E_MAIL_REPLY_STYLE_DO_NOT_QUOTE: + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (style_no_quote), TRUE); + break; + case E_MAIL_REPLY_STYLE_ATTACH: + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (style_attach), TRUE); + break; + case E_MAIL_REPLY_STYLE_OUTLOOK: + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (style_inline), TRUE); + break; + } + + emcu_three_state_set_value (html_format, g_settings_get_enum (settings, "alt-reply-html-format")); + emcu_three_state_set_value (bottom_posting, g_settings_get_enum (settings, "alt-reply-start-bottom")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (apply_template), g_settings_get_boolean (settings, "alt-reply-template-apply")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (preserve_message_subject), g_settings_get_boolean (settings, "alt-reply-template-preserve-subject")); + + if (!gtk_widget_get_sensitive (GTK_WIDGET (templates))) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (apply_template), FALSE); + + emcu_connect_three_state_changer (html_format); + emcu_connect_three_state_changer (bottom_posting); + + e_binding_bind_property ( + apply_template, "active", + templates, "sensitive", + G_BINDING_SYNC_CREATE); + + e_binding_bind_property ( + apply_template, "active", + preserve_message_subject, "sensitive", + G_BINDING_SYNC_CREATE); + + /* Enable the 'Reply Style' section only if not using Template */ + e_binding_bind_property ( + apply_template, "active", + style_label, "sensitive", + G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN); + + e_binding_bind_property ( + apply_template, "active", + style_default, "sensitive", + G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN); + + e_binding_bind_property ( + apply_template, "active", + style_attach, "sensitive", + G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN); + + e_binding_bind_property ( + apply_template, "active", + style_inline, "sensitive", + G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN); + + e_binding_bind_property ( + apply_template, "active", + style_quote, "sensitive", + G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN); + + e_binding_bind_property ( + apply_template, "active", + style_no_quote, "sensitive", + G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN); + + /* Similarly with bottom posting, which doesn't work when using Templates */ + e_binding_bind_property ( + apply_template, "active", + bottom_posting, "sensitive", + G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN); + + if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) { + GtkTreeIter iter; + AltReplyContext *context; + EThreeState three_state; + CamelFolder *template_folder = NULL; + gchar *template_message_uid = NULL; + + context = g_new0 (AltReplyContext, 1); + context->shell = g_object_ref (shell); + context->alert_sink = g_object_ref (alert_sink); + context->source_message = g_object_ref (message); + context->folder = folder ? g_object_ref (folder) : NULL; + context->source = source ? g_object_ref (source) : NULL; + context->message_uid = g_strdup (message_uid); + context->style = E_MAIL_REPLY_STYLE_UNKNOWN; + context->flags = E_MAIL_REPLY_FLAG_FORCE_STYLE; + + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (style_quote))) + context->style = E_MAIL_REPLY_STYLE_QUOTED; + else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (style_no_quote))) + context->style = E_MAIL_REPLY_STYLE_DO_NOT_QUOTE; + else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (style_attach))) + context->style = E_MAIL_REPLY_STYLE_ATTACH; + else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (style_inline))) + context->style = E_MAIL_REPLY_STYLE_OUTLOOK; + else + context->flags = context->flags & (~E_MAIL_REPLY_FLAG_FORCE_STYLE); + + three_state = emcu_three_state_get_value (html_format); + g_settings_set_enum (settings, "alt-reply-html-format", three_state); + + if (three_state == E_THREE_STATE_ON) + context->flags |= E_MAIL_REPLY_FLAG_FORMAT_HTML; + else if (three_state == E_THREE_STATE_OFF) + context->flags |= E_MAIL_REPLY_FLAG_FORMAT_PLAIN; + + three_state = emcu_three_state_get_value (bottom_posting); + g_settings_set_enum (settings, "alt-reply-start-bottom", three_state); + + if (three_state == E_THREE_STATE_ON) + context->flags |= E_MAIL_REPLY_FLAG_BOTTOM_POSTING; + else if (three_state == E_THREE_STATE_OFF) + context->flags |= E_MAIL_REPLY_FLAG_TOP_POSTING; + + g_settings_set_enum (settings, "alt-reply-style", context->style); + g_settings_set_boolean (settings, "alt-reply-template-apply", gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (apply_template))); + g_settings_set_boolean (settings, "alt-reply-template-preserve-subject", gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (preserve_message_subject))); + + if (gtk_combo_box_get_active_iter (templates, &iter)) { + gtk_tree_model_get (gtk_combo_box_get_model (templates), &iter, + E_MAIL_TEMPLATES_STORE_COLUMN_FOLDER, &template_folder, + E_MAIL_TEMPLATES_STORE_COLUMN_MESSAGE_UID, &template_message_uid, + -1); + } + + if (template_folder) { + gchar *folder_uri; + + folder_uri = e_mail_folder_uri_from_folder (template_folder); + g_settings_set_string (settings, "alt-reply-template-folder-uri", folder_uri ? folder_uri : ""); + g_free (folder_uri); + } + + g_settings_set_string (settings, "alt-reply-template-message-uid", template_message_uid ? template_message_uid : ""); + + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (recip_sender))) + context->type = E_MAIL_REPLY_TO_SENDER; + else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (recip_list))) + context->type = E_MAIL_REPLY_TO_LIST; + else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (recip_all))) + context->type = E_MAIL_REPLY_TO_ALL; + else + g_warn_if_reached (); + + if (context->style == E_MAIL_REPLY_STYLE_UNKNOWN) + context->style = default_style; + + context->template_preserve_subject = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (preserve_message_subject)); + + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (apply_template))) { + e_mail_templates_apply (context->source_message, template_folder, template_message_uid, + NULL, alt_reply_template_applied_cb, context); + + } else { + e_msg_composer_new (context->shell, alt_reply_composer_created_cb, context); + } + + g_clear_object (&template_folder); + g_free (template_message_uid); + } + + gtk_widget_destroy (dialog); + g_clear_object (&settings); +} + /** * em_utils_reply_to_message: - * @shell: an #EShell + * @composer: an #EMsgComposer * @message: a #CamelMimeMessage * @folder: a #CamelFolder, or %NULL * @message_uid: the UID of @message, or %NULL @@ -3576,6 +4324,7 @@ emcu_folder_is_inbox (CamelFolder *folder) * @style: the reply style to use * @source: source to inherit view settings from * @address: used for E_MAIL_REPLY_TO_RECIPIENT @type + * @reply_flags: bit-or of #EMailReplyFlags * * Creates a new composer ready to reply to @message. * @@ -3590,7 +4339,8 @@ em_utils_reply_to_message (EMsgComposer *composer, EMailReplyType type, EMailReplyStyle style, EMailPartList *parts_list, - CamelInternetAddress *address) + CamelInternetAddress *address, + EMailReplyFlags reply_flags) { ESourceRegistry *registry; CamelInternetAddress *to, *cc; @@ -3598,12 +4348,25 @@ em_utils_reply_to_message (EMsgComposer *composer, EShell *shell; ESourceMailCompositionReplyStyle prefer_reply_style = E_SOURCE_MAIL_COMPOSITION_REPLY_STYLE_DEFAULT; ESource *source; + EContentEditor *cnt_editor; gchar *identity_uid = NULL, *identity_name = NULL, *identity_address = NULL; guint32 flags; g_return_if_fail (E_IS_MSG_COMPOSER (composer)); g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message)); + cnt_editor = e_html_editor_get_content_editor (e_msg_composer_get_editor (composer)); + + if ((reply_flags & (E_MAIL_REPLY_FLAG_FORMAT_PLAIN | E_MAIL_REPLY_FLAG_FORMAT_HTML)) != 0) { + e_content_editor_set_html_mode (cnt_editor, (reply_flags & E_MAIL_REPLY_FLAG_FORMAT_HTML) != 0); + } + + if ((reply_flags & (E_MAIL_REPLY_FLAG_TOP_POSTING | E_MAIL_REPLY_FLAG_BOTTOM_POSTING)) != 0) { + e_content_editor_set_start_bottom (cnt_editor, + (reply_flags & E_MAIL_REPLY_FLAG_TOP_POSTING) != 0 ? + E_THREE_STATE_OFF : E_THREE_STATE_ON); + } + to = camel_internet_address_new (); cc = camel_internet_address_new (); @@ -3617,7 +4380,8 @@ em_utils_reply_to_message (EMsgComposer *composer, registry, message, folder, message_uid, &identity_name, &identity_address, sort_sources_by_ui, shell); if (source != NULL) { identity_uid = e_source_dup_uid (source); - if (e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_COMPOSITION)) { + if (!(reply_flags & E_MAIL_REPLY_FLAG_FORCE_STYLE) && + e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_COMPOSITION)) { ESourceMailComposition *extension; extension = e_source_get_extension (source, E_SOURCE_EXTENSION_MAIL_COMPOSITION); @@ -3685,7 +4449,8 @@ em_utils_reply_to_message (EMsgComposer *composer, if (used_identity_uid) { source = e_source_registry_ref_source (e_shell_get_registry (shell), used_identity_uid); if (source) { - if (e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_COMPOSITION)) { + if (!(reply_flags & E_MAIL_REPLY_FLAG_FORCE_STYLE) && + e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_COMPOSITION)) { ESourceMailComposition *extension; extension = e_source_get_extension (source, E_SOURCE_EXTENSION_MAIL_COMPOSITION); @@ -3734,6 +4499,13 @@ em_utils_reply_to_message (EMsgComposer *composer, /* because some reply types can change recipients after the composer is populated */ em_utils_apply_send_account_override_to_composer (composer, folder); + /* This is required to be done (also) at the end */ + if ((reply_flags & (E_MAIL_REPLY_FLAG_TOP_POSTING | E_MAIL_REPLY_FLAG_BOTTOM_POSTING)) != 0) { + e_content_editor_set_start_bottom (cnt_editor, + (reply_flags & E_MAIL_REPLY_FLAG_TOP_POSTING) != 0 ? + E_THREE_STATE_OFF : E_THREE_STATE_ON); + } + composer_set_no_change (composer); gtk_widget_show (GTK_WIDGET (composer)); diff --git a/src/mail/em-composer-utils.h b/src/mail/em-composer-utils.h index 91471bcb5f..73e0760901 100644 --- a/src/mail/em-composer-utils.h +++ b/src/mail/em-composer-utils.h @@ -85,7 +85,16 @@ void em_utils_reply_to_message (EMsgComposer *composer, EMailReplyType type, EMailReplyStyle style, EMailPartList *source, - CamelInternetAddress *address); + CamelInternetAddress *address, + guint32 reply_flags); /* bit-or of EMailReplyFlags */ +void em_utils_reply_alternative (GtkWindow *parent, + EShell *shell, + EAlertSink *alert_sink, + CamelMimeMessage *message, + CamelFolder *folder, + const gchar *message_uid, + EMailReplyStyle default_style, + EMailPartList *source); EDestination ** em_utils_camel_address_to_destination (CamelInternetAddress *iaddr); void em_configure_new_composer (EMsgComposer *composer, diff --git a/src/modules/mail/e-mail-attachment-handler.c b/src/modules/mail/e-mail-attachment-handler.c index a035434d78..2ddec78f28 100644 --- a/src/modules/mail/e-mail-attachment-handler.c +++ b/src/modules/mail/e-mail-attachment-handler.c @@ -214,7 +214,7 @@ mail_attachment_handler_composer_created_cb (GObject *source_object, style = g_settings_get_enum (settings, "reply-style-name"); g_object_unref (settings); - em_utils_reply_to_message (composer, ccd->message, NULL, NULL, ccd->reply_type, style, NULL, NULL); + em_utils_reply_to_message (composer, ccd->message, NULL, NULL, ccd->reply_type, style, NULL, NULL, E_MAIL_REPLY_FLAG_NONE); } else if (ccd->is_forward) { em_utils_forward_message (composer, ccd->message, ccd->forward_style, ccd->folder, NULL); } else { diff --git a/src/modules/webkit-editor/e-webkit-editor.c b/src/modules/webkit-editor/e-webkit-editor.c index 720e7ebeaa..cd0d5f2cbe 100644 --- a/src/modules/webkit-editor/e-webkit-editor.c +++ b/src/modules/webkit-editor/e-webkit-editor.c @@ -1838,6 +1838,7 @@ webkit_editor_insert_content (EContentEditor *editor, if (!(wk_editor->priv->html_mode)) { if (strstr (content, "")) { if (!show_lose_formatting_dialog (wk_editor)) { + set_convert_in_situ (wk_editor, FALSE); wk_editor->priv->reload_in_progress = TRUE; webkit_editor_set_html_mode (wk_editor, TRUE); webkit_web_view_load_html ( diff --git a/src/plugins/templates/CMakeLists.txt b/src/plugins/templates/CMakeLists.txt index c5f90a667a..29c67c8cfc 100644 --- a/src/plugins/templates/CMakeLists.txt +++ b/src/plugins/templates/CMakeLists.txt @@ -10,8 +10,6 @@ set(DEPENDENCIES set(SOURCES templates.c - e-templates-store.h - e-templates-store.c ) add_library(org-gnome-templates MODULE diff --git a/src/plugins/templates/e-templates-store.h b/src/plugins/templates/e-templates-store.h deleted file mode 100644 index 76019c5089..0000000000 --- a/src/plugins/templates/e-templates-store.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2016 Red Hat, Inc. (www.redhat.com) - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -#ifndef E_TEMPLATES_STORE_H -#define E_TEMPLATES_STORE_H - -#include -#include - -#include -#include -#include -#include -#include - -/* Standard GObject macros */ -#define E_TYPE_TEMPLATES_STORE \ - (e_templates_store_get_type ()) -#define E_TEMPLATES_STORE(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST \ - ((obj), E_TYPE_TEMPLATES_STORE, ETemplatesStore)) -#define E_TEMPLATES_STORE_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_CAST \ - ((cls), E_TYPE_TEMPLATES_STORE, ETemplatesStoreClass)) -#define E_IS_TEMPLATES_STORE(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE \ - ((obj), E_TYPE_TEMPLATES_STORE)) -#define E_IS_TEMPLATES_STORE_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_TYPE \ - ((cls), E_TYPE_TEMPLATES_STORE)) -#define E_TEMPLATES_STORE_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS \ - ((obj), E_TYPE_TEMPLATES_STORE, ETemplatesStoreClass)) - -G_BEGIN_DECLS - -typedef struct _ETemplatesStore ETemplatesStore; -typedef struct _ETemplatesStoreClass ETemplatesStoreClass; -typedef struct _ETemplatesStorePrivate ETemplatesStorePrivate; - -/** - * ETemplatesStore: - * - * Contains only private data that should be read and manipulated using - * the functions below. - **/ -struct _ETemplatesStore { - GObject parent; - ETemplatesStorePrivate *priv; -}; - -struct _ETemplatesStoreClass { - GObjectClass parent_class; - - /* Signals */ - void (*changed) (ETemplatesStore *templates_store); -}; - -typedef void (* ETemplatesStoreActionFunc) (ETemplatesStore *templates_store, - CamelFolder *folder, - const gchar *message_uid, - gpointer user_data); - -GType e_templates_store_get_type (void) G_GNUC_CONST; -ETemplatesStore * - e_templates_store_ref_default (EMailAccountStore *account_store); -EMailAccountStore * - e_templates_store_ref_account_store - (ETemplatesStore *templates_store); -void e_templates_store_build_menu (ETemplatesStore *templates_store, - EShellView *shell_view, - GtkUIManager *ui_manager, - GtkActionGroup *action_group, - const gchar *base_menu_path, - guint merge_id, - ETemplatesStoreActionFunc action_cb, - gpointer action_cb_user_data); - -G_END_DECLS - -#endif /* E_TEMPLATES_STORE_H */ diff --git a/src/plugins/templates/templates.c b/src/plugins/templates/templates.c index 1757b9c5b2..79789dc5f0 100644 --- a/src/plugins/templates/templates.c +++ b/src/plugins/templates/templates.c @@ -1,24 +1,23 @@ /* - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License as published by + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * Copyright (C) 2008 - Diego Escalante Urrelo + * Copyright (C) 2018 Red Hat, Inc. (www.redhat.com) + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, see . - * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . * * Authors: * Diego Escalante Urrelo * Bharath Acharya - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * Copyright (C) 2008 - Diego Escalante Urrelo - * */ #include "evolution-config.h" @@ -27,19 +26,19 @@ #include #include -#include - -#include +#include "e-util/e-util.h" -#include -#include -#include -#include -#include +#include "shell/e-shell-view.h" -#include +#include "mail/e-mail-reader.h" +#include "mail/e-mail-ui-session.h" +#include "mail/e-mail-templates.h" +#include "mail/e-mail-templates-store.h" +#include "mail/em-composer-utils.h" +#include "mail/em-utils.h" +#include "mail/message-list.h" -#include "e-templates-store.h" +#include "composer/e-msg-composer.h" #define CONF_KEY_TEMPLATE_PLACEHOLDERS "template-placeholders" @@ -48,11 +47,11 @@ typedef struct _AsyncContext AsyncContext; struct _AsyncContext { EActivity *activity; EMailReader *reader; - CamelMimeMessage *message; - CamelMimeMessage *template; + CamelMimeMessage *source_message; + CamelMimeMessage *new_message; CamelFolder *template_folder; gchar *source_folder_uri; - gchar *message_uid; + gchar *source_message_uid; gchar *template_message_uid; }; @@ -83,7 +82,7 @@ gint e_plugin_lib_enable (EPlugin *plugin, #define TEMPLATES_DATA_KEY "templates::data" typedef struct _TemplatesData { - ETemplatesStore *templates_store; + EMailTemplatesStore *templates_store; gulong changed_handler_id; gboolean changed; guint merge_id; @@ -124,12 +123,12 @@ async_context_free (AsyncContext *context) { g_clear_object (&context->activity); g_clear_object (&context->reader); - g_clear_object (&context->message); - g_clear_object (&context->template); + g_clear_object (&context->source_message); + g_clear_object (&context->new_message); g_clear_object (&context->template_folder); g_free (context->source_folder_uri); - g_free (context->message_uid); + g_free (context->source_message_uid); g_free (context->template_message_uid); g_slice_free (AsyncContext, context); @@ -554,296 +553,6 @@ e_plugin_lib_get_configure_widget (EPlugin *epl) return hbox; } -/* Case insensitive version of strstr */ -static gchar * -strstr_nocase (const gchar *haystack, - const gchar *needle) -{ -/* When _GNU_SOURCE is available, use the nonstandard extension of libc */ -#ifdef _GNU_SOURCE - g_return_val_if_fail (haystack, NULL); - g_return_Val_if_fail (needle, NULL); - - return strcasestr (haystack, needle) -#else -/* Otherwise convert both, haystack and needle to lowercase and use good old strstr */ - gchar *l_haystack; - gchar *l_needle; - gchar *pos; - - g_return_val_if_fail (haystack, NULL); - g_return_val_if_fail (needle, NULL); - - l_haystack = g_ascii_strdown (haystack, -1); - l_needle = g_ascii_strdown (needle, -1); - pos = strstr (l_haystack, l_needle); - - /* Get actual position of the needle in the haystack instead of l_haystack or - * leave it NULL */ - if (pos) - pos = (gchar *)(haystack + (pos - l_haystack)); - - g_free (l_haystack); - g_free (l_needle); - - return pos; -#endif -} - -/* Replaces $ORIG[variable] in given template by given replacement from the original message */ -static void -replace_template_variable (GString *text, - const gchar *variable, - const gchar *replacement) -{ - const gchar *p, *next; - GString *str; - gint find_len; - gchar *find; - - g_return_if_fail (text != NULL); - g_return_if_fail (variable != NULL); - g_return_if_fail (*variable); - - find = g_strconcat ("$ORIG[", variable, "]", NULL); - - find_len = strlen (find); - str = g_string_new (""); - p = text->str; - while (next = strstr_nocase (p, find), next) { - if (p < next) - g_string_append_len (str, p, next - p); - if (replacement && *replacement) - g_string_append (str, replacement); - p = next + find_len; - } - g_string_append (str, p); - - g_string_assign (text, str->str); - - g_string_free (str, TRUE); - g_free (find); -} - -static void -replace_email_addresses (GString *template, - CamelInternetAddress *internet_address, - const gchar *variable) -{ - gint address_index = 0; - GString *emails = g_string_new (""); - const gchar *address_name, *address_email; - - g_return_if_fail (template); - g_return_if_fail (internet_address); - g_return_if_fail (variable); - - while (camel_internet_address_get (internet_address, address_index, &address_name, &address_email)) { - gchar *address = camel_internet_address_format_address (address_name, address_email); - - if (address_index > 0) - g_string_append_printf (emails, ", %s", address); - else - g_string_append_printf (emails, "%s", address); - - address_index++; - g_free (address); - } - replace_template_variable (template, variable, emails->str); - g_string_free (emails, TRUE); -} - -static CamelMimePart * -fill_template (CamelMimeMessage *message, - CamelMimePart *template) -{ - const CamelNameValueArray *headers; - CamelContentType *ct; - CamelStream *stream; - CamelMimePart *return_part; - CamelMimePart *message_part = NULL; - CamelDataWrapper *dw; - CamelInternetAddress *internet_address; - GString *template_body; - GByteArray *byte_array; - gint i; - guint jj, len; - gboolean message_html, template_html; - - ct = camel_mime_part_get_content_type (template); - template_html = ct && camel_content_type_is (ct, "text", "html"); - - message_html = FALSE; - /* When template is html, then prefer HTML part of the original message. Otherwise go for plaintext */ - dw = camel_medium_get_content (CAMEL_MEDIUM (message)); - if (CAMEL_IS_MULTIPART (dw)) { - CamelMultipart *multipart = CAMEL_MULTIPART (dw); - - for (i = 0; i < camel_multipart_get_number (multipart); i++) { - CamelMimePart *part = camel_multipart_get_part (multipart, i); - CamelContentType *ct = camel_mime_part_get_content_type (part); - - if (!ct) - continue; - - if (camel_content_type_is (ct, "text", "html") && template_html) { - message_part = camel_multipart_get_part (multipart, i); - message_html = TRUE; - break; - } else if (camel_content_type_is (ct, "text", "plain") && message_html == FALSE) { - message_part = camel_multipart_get_part (multipart, i); - } - } - } else - message_part = CAMEL_MIME_PART (message); - - /* Get content of the template */ - stream = camel_stream_mem_new (); - camel_data_wrapper_decode_to_stream_sync (camel_medium_get_content (CAMEL_MEDIUM (template)), stream, NULL, NULL); - camel_stream_flush (stream, NULL, NULL); - byte_array = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (stream)); - template_body = g_string_new_len ((gchar *) byte_array->data, byte_array->len); - g_object_unref (stream); - - /* Replace all $ORIG[header_name] by respective values */ - headers = camel_medium_get_headers (CAMEL_MEDIUM (message)); - len = camel_name_value_array_get_length (headers); - for (jj = 0; jj < len; jj++) { - const gchar *header_name = NULL, *header_value = NULL; - - if (!camel_name_value_array_get (headers, jj, &header_name, &header_value) || - !header_name) - continue; - - if (g_ascii_strncasecmp (header_name, "content-", 8) != 0 && - g_ascii_strcasecmp (header_name, "to") != 0 && - g_ascii_strcasecmp (header_name, "cc") != 0 && - g_ascii_strcasecmp (header_name, "bcc") != 0 && - g_ascii_strcasecmp (header_name, "from") != 0 && - g_ascii_strcasecmp (header_name, "subject") != 0) - replace_template_variable (template_body, header_name, header_value); - } - - /* Now manually replace the *subject* header. The header->value for subject header could be - * base64 encoded, so let camel_mime_message to decode it for us if needed */ - replace_template_variable (template_body, "subject", camel_mime_message_get_subject (message)); - - /* Replace TO and FROM modifiers. */ - internet_address = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_TO); - replace_email_addresses (template_body, internet_address, "to"); - - internet_address = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_CC); - replace_email_addresses (template_body, internet_address, "cc"); - - internet_address = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_BCC); - replace_email_addresses (template_body, internet_address, "bcc"); - - internet_address = camel_mime_message_get_from (message); - replace_email_addresses (template_body, internet_address, "from"); - - /* Now extract body of the original message and replace the $ORIG[body] modifier in template */ - if (message_part && strstr_nocase (template_body->str, "$ORIG[body]")) { - GString *message_body; - CamelStream *mem_stream; - - stream = camel_stream_mem_new (); - mem_stream = stream; - - ct = camel_mime_part_get_content_type (message_part); - if (ct) { - const gchar *charset = camel_content_type_param (ct, "charset"); - if (charset && *charset) { - CamelMimeFilter *filter = camel_mime_filter_charset_new (charset, "UTF-8"); - if (filter) { - CamelStream *filtered = camel_stream_filter_new (stream); - - if (filtered) { - camel_stream_filter_add (CAMEL_STREAM_FILTER (filtered), filter); - g_object_unref (stream); - stream = filtered; - } - - g_object_unref (filter); - } - } - } - - camel_data_wrapper_decode_to_stream_sync (camel_medium_get_content (CAMEL_MEDIUM (message_part)), stream, NULL, NULL); - camel_stream_flush (stream, NULL, NULL); - byte_array = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (mem_stream)); - message_body = g_string_new_len ((gchar *) byte_array->data, byte_array->len); - g_object_unref (stream); - - if (template_html && !message_html) { - gchar *html = camel_text_to_html ( - message_body->str, - CAMEL_MIME_FILTER_TOHTML_CONVERT_NL | - CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES | - CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS | - CAMEL_MIME_FILTER_TOHTML_MARK_CITATION | - CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES, 0); - g_string_assign (message_body, html); - g_free (html); - } else if (!template_html && message_html) { - g_string_prepend (message_body, "
");
-			g_string_append (message_body, "
"); - } /* Other cases should not occur. And even if they happen to do, there's nothing we can really do about it */ - - replace_template_variable (template_body, "body", message_body->str); - g_string_free (message_body, TRUE); - } else { - replace_template_variable (template_body, "body", ""); - } - - return_part = camel_mime_part_new (); - - if (template_html) - camel_mime_part_set_content (return_part, template_body->str, template_body->len, "text/html"); - else - camel_mime_part_set_content (return_part, template_body->str, template_body->len, "text/plain"); - - g_string_free (template_body, TRUE); - - return return_part; -} - -static CamelMimePart * -find_template_part_in_multipart (CamelMultipart *multipart, - CamelMultipart *new_multipart) -{ - CamelMimePart *template_part = NULL; - gint ii; - - for (ii = 0; ii < camel_multipart_get_number (multipart); ii++) { - CamelMimePart *part = camel_multipart_get_part (multipart, ii); - CamelContentType *ct = camel_mime_part_get_content_type (part); - - if (!template_part && ct && camel_content_type_is (ct, "multipart", "*")) { - CamelDataWrapper *dw; - - dw = camel_medium_get_content (CAMEL_MEDIUM (part)); - template_part = (dw && CAMEL_IS_MULTIPART (dw)) ? - find_template_part_in_multipart (CAMEL_MULTIPART (dw), new_multipart) : NULL; - - if (!template_part) { - /* Copy any other parts (attachments...) to the output message */ - camel_mime_part_set_disposition (part, "attachment"); - camel_multipart_add_part (new_multipart, part); - } - } else if (ct && camel_content_type_is (ct, "text", "html")) { - template_part = part; - } else if (ct && camel_content_type_is (ct, "text", "plain") && !template_part) { - template_part = part; - } else { - /* Copy any other parts (attachments...) to the output message */ - camel_mime_part_set_disposition (part, "attachment"); - camel_multipart_add_part (new_multipart, part); - } - } - - return template_part; -} - static void create_new_message_composer_created_cb (GObject *source_object, GAsyncResult *result, @@ -851,19 +560,7 @@ create_new_message_composer_created_cb (GObject *source_object, { AsyncContext *context = user_data; EAlertSink *alert_sink; - CamelMimeMessage *new; - CamelMimeMessage *message; - CamelMimeMessage *template; - CamelMultipart *new_multipart; - CamelDataWrapper *dw; - const CamelNameValueArray *headers; - CamelMimePart *template_part = NULL; - CamelFolder *folder; - EMailBackend *backend; - EMailSession *session; - const gchar *message_uid; EMsgComposer *composer; - guint ii, len; GError *error = NULL; g_return_if_fail (context != NULL); @@ -873,13 +570,11 @@ create_new_message_composer_created_cb (GObject *source_object, composer = e_msg_composer_new_finish (result, &error); if (e_activity_handle_cancellation (context->activity, error)) { - g_warn_if_fail (context->template == NULL); async_context_free (context); g_error_free (error); return; } else if (error != NULL) { - g_warn_if_fail (context->template == NULL); e_alert_submit ( alert_sink, "mail:no-retrieve-message", error->message, NULL); @@ -890,154 +585,41 @@ create_new_message_composer_created_cb (GObject *source_object, g_return_if_fail (E_IS_MSG_COMPOSER (composer)); - message = context->message; - message_uid = context->message_uid; - template = context->template; - - backend = e_mail_reader_get_backend (context->reader); - session = e_mail_backend_get_session (backend); - - folder = e_mail_session_get_local_folder (session, E_MAIL_LOCAL_FOLDER_TEMPLATES); - - new = camel_mime_message_new (); - new_multipart = camel_multipart_new (); - camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (new_multipart), "multipart/alternative"); - camel_multipart_set_boundary (new_multipart, NULL); - - dw = camel_medium_get_content (CAMEL_MEDIUM (template)); - /* If template is a multipart, then try to use HTML. When no HTML part is available, use plaintext. Every other - * add as an attachment */ - if (CAMEL_IS_MULTIPART (dw)) { - template_part = find_template_part_in_multipart (CAMEL_MULTIPART (dw), new_multipart); - } else { - CamelContentType *ct = camel_mime_part_get_content_type (CAMEL_MIME_PART (template)); - - if (ct && (camel_content_type_is (ct, "text", "html") || - camel_content_type_is (ct, "text", "plain"))) { - template_part = CAMEL_MIME_PART (template); - } - } - - g_warn_if_fail (template_part != NULL); - - if (template_part) { - CamelMimePart *out_part = NULL; - - /* Here replace all the modifiers in template body by values - from message and return the newly created part */ - out_part = fill_template (message, template_part); - - /* Assigning part directly to mime_message causes problem with - "Content-type" header displaying in the HTML message (camel parsing bug?) */ - camel_multipart_add_part (new_multipart, out_part); - g_object_unref (out_part); - } - - camel_medium_set_content (CAMEL_MEDIUM (new), CAMEL_DATA_WRAPPER (new_multipart)); - - /* Add the headers from the message we are replying to, so CC and that - * stuff is preserved. Also replace any $ORIG[header-name] modifiers ignoring - * 'content-*' headers */ - headers = camel_medium_dup_headers (CAMEL_MEDIUM (message)); - len = camel_name_value_array_get_length (headers); - for (ii = 0; ii < len; ii++) { - const gchar *header_name = NULL, *header_value = NULL; - - if (!camel_name_value_array_get (headers, ii, &header_name, &header_value) || - !header_name) - continue; - - if (g_ascii_strncasecmp (header_name, "content-", 8) != 0 && - g_ascii_strcasecmp (header_name, "from") != 0) { - gchar *new_header_value = NULL; - - /* Some special handling of the 'subject' header */ - if (g_ascii_strncasecmp (header_name, "subject", 7) == 0) { - GString *subject = g_string_new (camel_mime_message_get_subject (template)); - guint jj; - - /* Now replace all possible $ORIG[]s in the subject line by values from original message */ - for (jj = 0; jj < len; jj++) { - const gchar *m_header_name = NULL, *m_header_value = NULL; - - if (camel_name_value_array_get (headers, jj, &m_header_name, &m_header_value) && - m_header_name && - g_ascii_strncasecmp (m_header_name, "content-", 8) != 0 && - g_ascii_strcasecmp (m_header_name, "subject") != 0) - replace_template_variable (subject, m_header_name, m_header_value); - } - /* Now replace $ORIG[subject] variable, handling possible base64 encryption */ - replace_template_variable ( - subject, "subject", - camel_mime_message_get_subject (message)); - new_header_value = g_string_free (subject, FALSE); - } - - camel_medium_add_header (CAMEL_MEDIUM (new), header_name, new_header_value ? new_header_value : header_value); - - g_free (new_header_value); - } - } - - /* Set the To: field to the same To: field of the message we are replying to. */ - camel_mime_message_set_recipients ( - new, CAMEL_RECIPIENT_TYPE_TO, - camel_mime_message_get_reply_to (message) ? camel_mime_message_get_reply_to (message) : - camel_mime_message_get_from (message)); - - /* Copy the CC and BCC from the template.*/ - camel_mime_message_set_recipients ( - new, CAMEL_RECIPIENT_TYPE_CC, - camel_mime_message_get_recipients ( - template, CAMEL_RECIPIENT_TYPE_CC)); - - camel_mime_message_set_recipients ( - new, CAMEL_RECIPIENT_TYPE_BCC, - camel_mime_message_get_recipients ( - template, CAMEL_RECIPIENT_TYPE_BCC)); - /* Create the composer */ - em_utils_edit_message (composer, folder, new, message_uid, TRUE); - if (composer && context->source_folder_uri && context->message_uid) + em_utils_edit_message (composer, context->template_folder, context->new_message, context->source_message_uid, TRUE); + if (composer && context->source_folder_uri && context->source_message_uid) e_msg_composer_set_source_headers ( composer, context->source_folder_uri, - context->message_uid, CAMEL_MESSAGE_ANSWERED | CAMEL_MESSAGE_SEEN); - - g_object_unref (new_multipart); - g_object_unref (new); + context->source_message_uid, CAMEL_MESSAGE_ANSWERED | CAMEL_MESSAGE_SEEN); async_context_free (context); } static void -create_new_message (GObject *source_object, - GAsyncResult *result, - gpointer user_data) +templates_template_applied_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) { AsyncContext *context = user_data; EAlertSink *alert_sink; EMailBackend *backend; EShell *shell; - CamelFolder *folder; GError *error = NULL; - g_return_if_fail (CAMEL_IS_FOLDER (source_object)); g_return_if_fail (context != NULL); - folder = CAMEL_FOLDER (source_object); - alert_sink = e_activity_get_alert_sink (context->activity); - context->template = camel_folder_get_message_finish (folder, result, &error); + context->new_message = e_mail_templates_apply_finish (source_object, result, &error); if (e_activity_handle_cancellation (context->activity, error)) { - g_warn_if_fail (context->template == NULL); + g_warn_if_fail (context->new_message == NULL); async_context_free (context); g_error_free (error); return; } else if (error != NULL) { - g_warn_if_fail (context->template == NULL); + g_warn_if_fail (context->new_message == NULL); e_alert_submit ( alert_sink, "mail:no-retrieve-message", error->message, NULL); @@ -1046,7 +628,7 @@ create_new_message (GObject *source_object, return; } - g_return_if_fail (CAMEL_IS_MIME_MESSAGE (context->template)); + g_warn_if_fail (context->new_message != NULL); backend = e_mail_reader_get_backend (context->reader); shell = e_shell_backend_get_shell (E_SHELL_BACKEND (backend)); @@ -1060,12 +642,10 @@ template_got_source_message (CamelFolder *folder, AsyncContext *context) { EAlertSink *alert_sink; - GCancellable *cancellable; CamelMimeMessage *message; GError *error = NULL; alert_sink = e_activity_get_alert_sink (context->activity); - cancellable = e_activity_get_cancellable (context->activity); message = camel_folder_get_message_finish (folder, result, &error); @@ -1087,19 +667,14 @@ template_got_source_message (CamelFolder *folder, g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message)); - context->message = message; - - /* Now fetch the template message. */ + context->source_message = message; - camel_folder_get_message ( - context->template_folder, - context->template_message_uid, - G_PRIORITY_DEFAULT, cancellable, - create_new_message, context); + e_mail_templates_apply (context->source_message, context->template_folder, context->template_message_uid, + e_activity_get_cancellable (context->activity), templates_template_applied_cb, context); } static void -action_reply_with_template_cb (ETemplatesStore *templates_store, +action_reply_with_template_cb (EMailTemplatesStore *templates_store, CamelFolder *template_folder, const gchar *template_message_uid, gpointer user_data) @@ -1135,10 +710,10 @@ action_reply_with_template_cb (ETemplatesStore *templates_store, em_utils_get_real_folder_uri_and_message_uid ( folder, message_uid, &context->source_folder_uri, - &context->message_uid); + &context->source_message_uid); - if (context->message_uid == NULL) - context->message_uid = g_strdup (message_uid); + if (context->source_message_uid == NULL) + context->source_message_uid = g_strdup (message_uid); camel_folder_get_message ( folder, message_uid, G_PRIORITY_DEFAULT, @@ -1343,7 +918,7 @@ templates_update_actions_cb (EShellView *shell_view, shell_window = e_shell_view_get_shell_window (shell_view); ui_manager = e_shell_window_get_ui_manager (shell_window); - e_templates_store_build_menu (td->templates_store, shell_view, ui_manager, action_group, + e_mail_templates_store_build_menu (td->templates_store, shell_view, ui_manager, action_group, "/mail-message-popup/mail-message-templates", td->merge_id, action_reply_with_template_cb, shell_view); } @@ -1370,7 +945,7 @@ init_composer_actions (GtkUIManager *ui_manager, } static void -templates_store_changed_cb (ETemplatesStore *templates_store, +templates_store_changed_cb (EMailTemplatesStore *templates_store, gpointer user_data) { TemplatesData *td = user_data; @@ -1401,7 +976,7 @@ mail_shell_view_created_cb (EShellWindow *shell_window, session = e_mail_backend_get_session (backend); td = g_new0 (TemplatesData, 1); - td->templates_store = e_templates_store_ref_default (e_mail_ui_session_get_account_store (E_MAIL_UI_SESSION (session))); + td->templates_store = e_mail_templates_store_ref_default (e_mail_ui_session_get_account_store (E_MAIL_UI_SESSION (session))); td->changed_handler_id = g_signal_connect (td->templates_store, "changed", G_CALLBACK (templates_store_changed_cb), td); td->merge_id = gtk_ui_manager_new_merge_id (ui_manager); td->changed = TRUE; -- 2.11.4.GIT