summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMilan Crha <mcrha@redhat.com>2023-05-09 15:44:22 +0200
committerMilan Crha <mcrha@redhat.com>2023-05-09 15:46:45 +0200
commit1df5266bd673453803722912a0b61603dd4b5cd4 (patch)
treee9a28e063fca177c1dd2fdda1aea1a2054fc4bf4
parent2eebe2d656a82613d547d5cb9d7b95f412107fa3 (diff)
downloadevolution-1df5266bd673453803722912a0b61603dd4b5cd4.tar.gz
I#2354 - Composer: Notify about attachment file change before send
Closes https://gitlab.gnome.org/GNOME/evolution/-/issues/2354
-rw-r--r--data/org.gnome.evolution.mail.gschema.xml.in5
-rw-r--r--src/e-util/e-attachment-view.c88
-rw-r--r--src/e-util/e-attachment.c146
-rw-r--r--src/e-util/e-attachment.h6
-rw-r--r--src/mail/em-composer-utils.c77
-rw-r--r--src/mail/mail-config.ui16
-rw-r--r--src/mail/mail.error.xml7
-rw-r--r--src/modules/mail/em-composer-prefs.c6
8 files changed, 303 insertions, 48 deletions
diff --git a/data/org.gnome.evolution.mail.gschema.xml.in b/data/org.gnome.evolution.mail.gschema.xml.in
index ce1c6414dd..441042f23c 100644
--- a/data/org.gnome.evolution.mail.gschema.xml.in
+++ b/data/org.gnome.evolution.mail.gschema.xml.in
@@ -939,6 +939,11 @@
<_summary>(Deprecated) Asks whether to close the message window when the user forwards or replies to the message shown in the window</_summary>
<_description>This key was deprecated in version 3.10 and should no longer be used. Use “browser-close-on-reply-policy” instead.</_description>
</key>
+ <key name="prompt-on-changed-attachment" type="b">
+ <default>true</default>
+ <_summary>Prompt before send when attachment changed on the disk</_summary>
+ <_description>Ask whether can send a message with attachments which changed on the disk since they had been attached to the message.</_description>
+ </key>
</schema>
diff --git a/src/e-util/e-attachment-view.c b/src/e-util/e-attachment-view.c
index 210fad648e..80a0d4c113 100644
--- a/src/e-util/e-attachment-view.c
+++ b/src/e-util/e-attachment-view.c
@@ -45,6 +45,7 @@ static const gchar *ui =
"<ui>"
" <popup name='context'>"
" <menuitem action='cancel'/>"
+" <menuitem action='reload'/>"
" <menuitem action='save-as'/>"
" <menuitem action='remove'/>"
" <menuitem action='properties'/>"
@@ -68,6 +69,21 @@ G_DEFINE_INTERFACE (
GTK_TYPE_WIDGET)
static void
+call_attachment_load_handle_error (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GtkWindow *window = user_data;
+
+ g_return_if_fail (E_IS_ATTACHMENT (source_object));
+ g_return_if_fail (!window || GTK_IS_WINDOW (window));
+
+ e_attachment_load_handle_error (E_ATTACHMENT (source_object), result, window);
+
+ g_clear_object (&window);
+}
+
+static void
action_add_cb (GtkAction *action,
EAttachmentView *view)
{
@@ -196,6 +212,37 @@ action_properties_cb (GtkAction *action,
}
static void
+action_reload_cb (GtkAction *action,
+ EAttachmentView *view)
+{
+ GList *list, *link;
+ gpointer parent;
+
+ g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
+
+ parent = gtk_widget_get_toplevel (GTK_WIDGET (view));
+ parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
+
+ list = e_attachment_view_get_selected_attachments (view);
+
+ for (link = list; link; link = g_list_next (link)) {
+ EAttachment *attachment = link->data;
+ GFile *file;
+
+ file = e_attachment_ref_file (attachment);
+ if (file) {
+ e_attachment_load_async (
+ attachment, (GAsyncReadyCallback)
+ call_attachment_load_handle_error, parent ? g_object_ref (parent) : NULL);
+
+ g_clear_object (&file);
+ }
+ }
+
+ g_list_free_full (list, g_object_unref);
+}
+
+static void
action_remove_cb (GtkAction *action,
EAttachmentView *view)
{
@@ -350,6 +397,13 @@ static GtkActionEntry editable_entries[] = {
NULL, /* XXX Add a tooltip! */
G_CALLBACK (action_properties_cb) },
+ { "reload",
+ "view-refresh",
+ N_("Re_load"),
+ NULL,
+ N_("Reload attachment content"),
+ G_CALLBACK (action_reload_cb) },
+
{ "remove",
"list-remove",
N_("_Remove"),
@@ -359,21 +413,6 @@ static GtkActionEntry editable_entries[] = {
};
static void
-call_attachment_load_handle_error (GObject *source_object,
- GAsyncResult *result,
- gpointer user_data)
-{
- GtkWindow *window = user_data;
-
- g_return_if_fail (E_IS_ATTACHMENT (source_object));
- g_return_if_fail (!window || GTK_IS_WINDOW (window));
-
- e_attachment_load_handle_error (E_ATTACHMENT (source_object), result, window);
-
- g_clear_object (&window);
-}
-
-static void
attachment_view_netscape_url (EAttachmentView *view,
GdkDragContext *drag_context,
gint x,
@@ -702,13 +741,26 @@ attachment_view_update_actions (EAttachmentView *view)
GList *list, *iter;
guint n_selected;
gboolean busy = FALSE;
+ gboolean may_reload = FALSE;
g_return_if_fail (E_IS_ATTACHMENT_VIEW (view));
priv = e_attachment_view_get_private (view);
list = e_attachment_view_get_selected_attachments (view);
- n_selected = g_list_length (list);
+ n_selected = 0;
+
+ for (iter = list; iter && (!busy || !may_reload); iter = g_list_next (iter)) {
+ EAttachment *attach = iter->data;
+
+ n_selected++;
+
+ if (e_attachment_get_may_reload (attach)) {
+ may_reload = TRUE;
+ busy |= e_attachment_get_loading (attach);
+ busy |= e_attachment_get_saving (attach);
+ }
+ }
if (n_selected == 1) {
attachment = g_object_ref (list->data);
@@ -729,6 +781,10 @@ attachment_view_update_actions (EAttachmentView *view)
action = e_attachment_view_get_action (view, "properties");
gtk_action_set_visible (action, !busy && n_selected == 1);
+ action = e_attachment_view_get_action (view, "reload");
+ gtk_action_set_visible (action, may_reload);
+ gtk_action_set_sensitive (action, !busy);
+
action = e_attachment_view_get_action (view, "remove");
gtk_action_set_visible (action, !busy && n_selected > 0);
diff --git a/src/e-util/e-attachment.c b/src/e-util/e-attachment.c
index f59787ad9c..97b7a490e0 100644
--- a/src/e-util/e-attachment.c
+++ b/src/e-util/e-attachment.c
@@ -47,6 +47,7 @@
#define EMBLEM_CANCELLED "process-stop"
#define EMBLEM_LOADING "emblem-downloads"
#define EMBLEM_SAVING "document-save"
+#define EMBLEM_MAY_RELOAD "dialog-warning"
#define EMBLEM_ENCRYPT_WEAK "security-low"
#define EMBLEM_ENCRYPT_STRONG "security-high"
#define EMBLEM_ENCRYPT_UNKNOWN "security-medium"
@@ -74,6 +75,7 @@ struct _EAttachmentPrivate {
guint loading : 1;
guint saving : 1;
guint initially_shown : 1;
+ guint may_reload : 1;
guint save_self : 1;
guint save_extracted : 1;
@@ -104,7 +106,8 @@ enum {
PROP_SAVE_EXTRACTED,
PROP_SAVING,
PROP_INITIALLY_SHOWN,
- PROP_SIGNED
+ PROP_SIGNED,
+ PROP_MAY_RELOAD
};
enum {
@@ -385,6 +388,9 @@ attachment_update_icon_column_idle_cb (gpointer weak_ref)
else if (e_attachment_get_saving (attachment))
emblem_name = EMBLEM_SAVING;
+ else if (e_attachment_get_may_reload (attachment))
+ emblem_name = EMBLEM_MAY_RELOAD;
+
else if (e_attachment_get_encrypted (attachment))
switch (e_attachment_get_encrypted (attachment)) {
case CAMEL_CIPHER_VALIDITY_ENCRYPT_WEAK:
@@ -658,6 +664,12 @@ attachment_set_property (GObject *object,
E_ATTACHMENT (object),
g_value_get_boolean (value));
return;
+
+ case PROP_MAY_RELOAD:
+ e_attachment_set_may_reload (
+ E_ATTACHMENT (object),
+ g_value_get_boolean (value));
+ return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -767,6 +779,13 @@ attachment_get_property (GObject *object,
e_attachment_get_signed (
E_ATTACHMENT (object)));
return;
+
+ case PROP_MAY_RELOAD:
+ g_value_set_boolean (
+ value,
+ e_attachment_get_may_reload (
+ E_ATTACHMENT (object)));
+ return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -986,6 +1005,17 @@ e_attachment_class_init (EAttachmentClass *class)
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
+ g_object_class_install_property (
+ object_class,
+ PROP_MAY_RELOAD,
+ g_param_spec_boolean (
+ "may-reload",
+ "May Reload",
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT));
+
signals[UPDATE_FILE_INFO] = g_signal_new (
"update-file-info",
G_TYPE_FROM_CLASS (class),
@@ -1780,6 +1810,85 @@ e_attachment_update_store_columns (EAttachment *attachment)
attachment_update_progress_columns (attachment);
}
+gboolean
+e_attachment_check_file_changed (EAttachment *attachment,
+ gboolean *out_file_exists,
+ GCancellable *cancellable)
+{
+ GFileInfo *disk_file_info;
+ GFile *file;
+ gboolean same = FALSE;
+ gboolean file_exists = FALSE;
+
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
+
+ file = e_attachment_ref_file (attachment);
+ if (!file) {
+ if (out_file_exists)
+ *out_file_exists = FALSE;
+ return FALSE;
+ }
+
+ disk_file_info = g_file_query_info (file, G_FILE_ATTRIBUTE_TIME_MODIFIED "," G_FILE_ATTRIBUTE_STANDARD_SIZE, G_FILE_QUERY_INFO_NONE, cancellable, NULL);
+ if (disk_file_info) {
+ GFileInfo *attachment_file_info;
+
+ attachment_file_info = e_attachment_ref_file_info (attachment);
+ if (attachment_file_info) {
+ guint64 a_size, f_size;
+
+ a_size = g_file_info_get_attribute_uint64 (attachment_file_info, G_FILE_ATTRIBUTE_STANDARD_SIZE);
+ f_size = g_file_info_get_attribute_uint64 (disk_file_info, G_FILE_ATTRIBUTE_STANDARD_SIZE);
+
+ same = a_size == f_size;
+ file_exists = TRUE;
+
+ if (same) {
+ guint64 a_modified, f_modified;
+
+ a_modified = g_file_info_get_attribute_uint64 (attachment_file_info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
+ f_modified = g_file_info_get_attribute_uint64 (disk_file_info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
+
+ same = a_modified == f_modified;
+ }
+ }
+
+ g_clear_object (&attachment_file_info);
+ g_clear_object (&disk_file_info);
+ }
+
+ g_object_unref (file);
+
+ if (out_file_exists)
+ *out_file_exists = file_exists;
+
+ return !same;
+}
+
+void
+e_attachment_set_may_reload (EAttachment *attachment,
+ gboolean may_reload)
+{
+ g_return_if_fail (E_IS_ATTACHMENT (attachment));
+
+ if ((!attachment->priv->may_reload) == (!may_reload))
+ return;
+
+ attachment->priv->may_reload = may_reload;
+
+ g_object_notify (G_OBJECT (attachment), "may-reload");
+
+ attachment_update_icon_column (attachment);
+}
+
+gboolean
+e_attachment_get_may_reload (EAttachment *attachment)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
+
+ return attachment->priv->may_reload;
+}
+
/************************* e_attachment_load_async() *************************/
typedef struct _LoadContext LoadContext;
@@ -2424,6 +2533,7 @@ e_attachment_load_finish (EAttachment *attachment,
attachment, load_context->file_info);
e_attachment_set_mime_part (
attachment, load_context->mime_part);
+ e_attachment_set_may_reload (attachment, FALSE);
}
attachment_set_loading (attachment, FALSE);
@@ -2694,38 +2804,10 @@ e_attachment_open_async (EAttachment *attachment,
/* open existing file only if it did not change */
if (file && mime_part) {
- GFileInfo *disk_file_info;
- gboolean same = FALSE;
-
- disk_file_info = g_file_query_info (file, G_FILE_ATTRIBUTE_TIME_MODIFIED "," G_FILE_ATTRIBUTE_STANDARD_SIZE, G_FILE_QUERY_INFO_NONE, NULL, NULL);
- if (disk_file_info) {
- GFileInfo *attachment_file_info;
-
- attachment_file_info = e_attachment_ref_file_info (attachment);
- if (attachment_file_info) {
- guint64 a_size, f_size;
-
- a_size = g_file_info_get_attribute_uint64 (attachment_file_info, G_FILE_ATTRIBUTE_STANDARD_SIZE);
- f_size = g_file_info_get_attribute_uint64 (disk_file_info, G_FILE_ATTRIBUTE_STANDARD_SIZE);
-
- same = a_size == f_size;
-
- if (same) {
- guint64 a_modified, f_modified;
-
- a_modified = g_file_info_get_attribute_uint64 (attachment_file_info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
- f_modified = g_file_info_get_attribute_uint64 (disk_file_info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
-
- same = a_modified == f_modified;
- }
- }
-
- g_clear_object (&attachment_file_info);
- g_clear_object (&disk_file_info);
- }
-
- if (!same)
+ if (e_attachment_check_file_changed (attachment, NULL, NULL)) {
+ e_attachment_set_may_reload (attachment, TRUE);
g_clear_object (&file);
+ }
}
/* If the attachment already references a GFile, we can launch
diff --git a/src/e-util/e-attachment.h b/src/e-util/e-attachment.h
index 2a060a76f6..d46752a49b 100644
--- a/src/e-util/e-attachment.h
+++ b/src/e-util/e-attachment.h
@@ -131,6 +131,12 @@ GList * e_attachment_list_apps (EAttachment *attachment);
GAppInfo * e_attachment_ref_default_app (EAttachment *attachment);
void e_attachment_update_store_columns
(EAttachment *attachment);
+gboolean e_attachment_check_file_changed (EAttachment *attachment,
+ gboolean *out_file_exists,
+ GCancellable *cancellable);
+void e_attachment_set_may_reload (EAttachment *attachment,
+ gboolean may_reload);
+gboolean e_attachment_get_may_reload (EAttachment *attachment);
/* Asynchronous Operations */
void e_attachment_load_async (EAttachment *attachment,
diff --git a/src/mail/em-composer-utils.c b/src/mail/em-composer-utils.c
index 122c6079fd..b82cff6211 100644
--- a/src/mail/em-composer-utils.c
+++ b/src/mail/em-composer-utils.c
@@ -602,6 +602,79 @@ composer_presend_check_unwanted_html (EMsgComposer *composer,
return check_passed;
}
+static gboolean
+composer_presend_check_attachments (EMsgComposer *composer,
+ EMailSession *session)
+{
+ EAttachmentView *view;
+ EAttachmentStore *store;
+ GList *attachments, *link;
+ gboolean can_send = TRUE;
+ EAttachment *first_changed = NULL;
+ guint n_changed = 0;
+
+ view = e_msg_composer_get_attachment_view (composer);
+ store = e_attachment_view_get_store (view);
+ attachments = e_attachment_store_get_attachments (store);
+
+ for (link = attachments; link; link = g_list_next (link)) {
+ EAttachment *attach = link->data;
+ gboolean file_exists = FALSE;
+
+ if (e_attachment_check_file_changed (attach, &file_exists, NULL) &&
+ file_exists) {
+ e_attachment_set_may_reload (attach, TRUE);
+ if (!first_changed)
+ first_changed = attach;
+ n_changed++;
+ } else {
+ e_attachment_set_may_reload (attach, FALSE);
+ }
+ }
+
+ if (n_changed > 0) {
+ GFileInfo *file_info = NULL;
+ const gchar *display_name = NULL;
+ gchar *title, *text;
+
+ if (n_changed == 1) {
+ file_info = e_attachment_ref_file_info (first_changed);
+ display_name = g_file_info_get_display_name (file_info);
+ if (display_name && !*display_name)
+ display_name = NULL;
+ }
+
+ title = g_strdup_printf (ngettext (
+ "Attachment changed",
+ "%d attachments changed",
+ n_changed), n_changed);
+
+ if (n_changed == 1 && display_name) {
+ text = g_strdup_printf (_("Attachment “%s” changed after being added to the message. Do you want to send the message anyway?"),
+ display_name);
+ } else {
+ text = g_strdup_printf (ngettext ("One attachment changed after being added to the message. Do you want to send the message anyway?",
+ "%d attachments changed after being added to the message. Do you want to send the message anyway?",
+ n_changed), n_changed);
+ }
+
+ can_send = e_util_prompt_user (
+ GTK_WINDOW (composer),
+ "org.gnome.evolution.mail",
+ "prompt-on-changed-attachment",
+ "mail:ask-composer-changed-attachment",
+ title, text, NULL);
+
+ g_clear_object (&file_info);
+ g_free (title);
+ g_free (text);
+ }
+
+ g_list_free_full (attachments, g_object_unref);
+
+ return can_send;
+}
+
static void
composer_send_completed (GObject *source_object,
GAsyncResult *result,
@@ -4934,6 +5007,10 @@ em_configure_new_composer (EMsgComposer *composer,
G_CALLBACK (composer_presend_check_unwanted_html), session);
g_signal_connect (
+ composer, "presend",
+ G_CALLBACK (composer_presend_check_attachments), session);
+
+ g_signal_connect (
composer, "send",
G_CALLBACK (em_utils_composer_send_cb), session);
diff --git a/src/mail/mail-config.ui b/src/mail/mail-config.ui
index 3dadcae6be..84a2fdc3ca 100644
--- a/src/mail/mail-config.ui
+++ b/src/mail/mail-config.ui
@@ -1177,6 +1177,22 @@
<property name="position">8</property>
</packing>
</child>
+ <child>
+ <object class="GtkCheckButton" id="chkPromptChangedAttachment">
+ <property name="label" translatable="yes" comments="This is in the context of: Ask for confirmation before...">Sending a message with _attachments which changed since being attached</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="xalign">0.5</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">9</property>
+ </packing>
+ </child>
</object>
</child>
</object>
diff --git a/src/mail/mail.error.xml b/src/mail/mail.error.xml
index e58559ebb5..ad1fa82d61 100644
--- a/src/mail/mail.error.xml
+++ b/src/mail/mail.error.xml
@@ -679,4 +679,11 @@ in the folder will be available in offline mode.</_secondary>
<_primary>Label named “{0}” already exists</_primary>
<_secondary>Choose a different name, please.</_secondary>
</error>
+
+ <error id="ask-composer-changed-attachment" type="warning" default="GTK_RESPONSE_CANCEL">
+ <primary>{0}</primary>
+ <secondary>{1}</secondary>
+ <button response="GTK_RESPONSE_CANCEL" _label="_Cancel"/>
+ <button response="GTK_RESPONSE_YES" _label="_Send"></button>
+ </error>
</error-list>
diff --git a/src/modules/mail/em-composer-prefs.c b/src/modules/mail/em-composer-prefs.c
index 24b6734a80..77ba8dc728 100644
--- a/src/modules/mail/em-composer-prefs.c
+++ b/src/modules/mail/em-composer-prefs.c
@@ -1223,6 +1223,12 @@ em_composer_prefs_construct (EMComposerPrefs *prefs,
widget, "active",
G_SETTINGS_BIND_DEFAULT);
+ widget = e_builder_get_widget (prefs->builder, "chkPromptChangedAttachment");
+ g_settings_bind (
+ settings, "prompt-on-changed-attachment",
+ widget, "active",
+ G_SETTINGS_BIND_DEFAULT);
+
widget = e_builder_get_widget (prefs->builder, "chkAutoSmileys");
g_settings_bind (
settings, "composer-magic-smileys",