client: Add more options for displaying images from messages
- An application setting allowing to always trust images - An option to trust images from an email domain - Replaces buttons by a menu in infobar
This commit is contained in:
parent
bc4fe28a25
commit
9f893adc47
9 changed files with 246 additions and 47 deletions
|
|
@ -97,6 +97,12 @@
|
||||||
<description>The last recorded size of the detached composer window.</description>
|
<description>The last recorded size of the detached composer window.</description>
|
||||||
</key>
|
</key>
|
||||||
|
|
||||||
|
<key name="images-trusted-domains" type="as">
|
||||||
|
<default>[]</default>
|
||||||
|
<summary>Allow images for these domains</summary>
|
||||||
|
<description>Images from these domains will be trusted</description>
|
||||||
|
</key>
|
||||||
|
|
||||||
<key name="undo-send-delay" type="i">
|
<key name="undo-send-delay" type="i">
|
||||||
<default>5</default>
|
<default>5</default>
|
||||||
<summary>Undo sending email delay</summary>
|
<summary>Undo sending email delay</summary>
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ public class Application.Configuration : Geary.BaseObject {
|
||||||
public const string WINDOW_HEIGHT_KEY = "window-height";
|
public const string WINDOW_HEIGHT_KEY = "window-height";
|
||||||
public const string WINDOW_MAXIMIZE_KEY = "window-maximize";
|
public const string WINDOW_MAXIMIZE_KEY = "window-maximize";
|
||||||
public const string WINDOW_WIDTH_KEY = "window-width";
|
public const string WINDOW_WIDTH_KEY = "window-width";
|
||||||
|
public const string IMAGES_TRUSTED_DOMAINS = "images-trusted-domains";
|
||||||
|
|
||||||
|
|
||||||
public enum DesktopEnvironment {
|
public enum DesktopEnvironment {
|
||||||
|
|
@ -156,6 +157,16 @@ public class Application.Configuration : Geary.BaseObject {
|
||||||
settings.bind(key, object, property, flags);
|
settings.bind(key, object, property, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void bind_with_mapping(string key, Object object, string property,
|
||||||
|
SettingsBindGetMappingShared get_mapping,
|
||||||
|
SettingsBindSetMappingShared set_mapping,
|
||||||
|
SettingsBindFlags flags = GLib.SettingsBindFlags.DEFAULT) {
|
||||||
|
settings.bind_with_mapping(
|
||||||
|
key, object, property, flags,
|
||||||
|
get_mapping, set_mapping, null, null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private void set_boolean(string name, bool value) {
|
private void set_boolean(string name, bool value) {
|
||||||
if (!settings.set_boolean(name, value))
|
if (!settings.set_boolean(name, value))
|
||||||
message("Unable to set configuration value %s = %s", name, value.to_string());
|
message("Unable to set configuration value %s = %s", name, value.to_string());
|
||||||
|
|
@ -178,6 +189,34 @@ public class Application.Configuration : Geary.BaseObject {
|
||||||
this.settings.set_value(COMPOSER_WINDOW_SIZE_KEY, value);
|
this.settings.set_value(COMPOSER_WINDOW_SIZE_KEY, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns list of trusted domains for which images loading is allowed. */
|
||||||
|
public string[] get_images_trusted_domains() {
|
||||||
|
return this.settings.get_strv(IMAGES_TRUSTED_DOMAINS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sets list of trusted domains for which images loading is allowed. */
|
||||||
|
public void set_images_trusted_domains(string[] value) {
|
||||||
|
this.settings.set_strv(IMAGES_TRUSTED_DOMAINS, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Adds domain to trusted list for which images loading is allowed. */
|
||||||
|
public void add_images_trusted_domain(string domain) {
|
||||||
|
var domains = get_images_trusted_domains();
|
||||||
|
domains += domain;
|
||||||
|
set_images_trusted_domains(domains);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Removes domain from trusted for which images loading is allowed. */
|
||||||
|
public void remove_images_trusted_domain(string domain) {
|
||||||
|
var domains = get_images_trusted_domains();
|
||||||
|
string[] new_domains = {};
|
||||||
|
foreach (var _domain in domains) {
|
||||||
|
if (domain != _domain)
|
||||||
|
new_domains += _domain;
|
||||||
|
}
|
||||||
|
set_images_trusted_domains(new_domains);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns list of optional plugins to load by default
|
* Returns list of optional plugins to load by default
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -163,6 +163,16 @@ public class Components.PreferencesWindow : Hdy.PreferencesWindow {
|
||||||
startup_notifications_row.activatable_widget = startup_notifications;
|
startup_notifications_row.activatable_widget = startup_notifications;
|
||||||
startup_notifications_row.add(startup_notifications);
|
startup_notifications_row.add(startup_notifications);
|
||||||
|
|
||||||
|
var trust_images = new Gtk.Switch();
|
||||||
|
trust_images.valign = CENTER;
|
||||||
|
|
||||||
|
var trust_images_row = new Hdy.ActionRow();
|
||||||
|
/// Translators: Preferences label
|
||||||
|
trust_images_row.title = _("_Always load images");
|
||||||
|
trust_images_row.use_underline = true;
|
||||||
|
trust_images_row.activatable_widget = autoselect;
|
||||||
|
trust_images_row.add(trust_images);
|
||||||
|
|
||||||
var group = new Hdy.PreferencesGroup();
|
var group = new Hdy.PreferencesGroup();
|
||||||
/// Translators: Preferences group title
|
/// Translators: Preferences group title
|
||||||
//group.title = _("General");
|
//group.title = _("General");
|
||||||
|
|
@ -172,6 +182,7 @@ public class Components.PreferencesWindow : Hdy.PreferencesWindow {
|
||||||
group.add(display_preview_row);
|
group.add(display_preview_row);
|
||||||
group.add(single_key_shortucts_row);
|
group.add(single_key_shortucts_row);
|
||||||
group.add(startup_notifications_row);
|
group.add(startup_notifications_row);
|
||||||
|
group.add(trust_images_row);
|
||||||
|
|
||||||
var page = new Hdy.PreferencesPage();
|
var page = new Hdy.PreferencesPage();
|
||||||
/// Translators: Preferences page title
|
/// Translators: Preferences page title
|
||||||
|
|
@ -209,6 +220,13 @@ public class Components.PreferencesWindow : Hdy.PreferencesWindow {
|
||||||
startup_notifications,
|
startup_notifications,
|
||||||
"state"
|
"state"
|
||||||
);
|
);
|
||||||
|
config.bind_with_mapping(
|
||||||
|
Application.Configuration.IMAGES_TRUSTED_DOMAINS,
|
||||||
|
trust_images,
|
||||||
|
"state",
|
||||||
|
(GLib.SettingsBindGetMappingShared) settings_trust_images_getter,
|
||||||
|
(GLib.SettingsBindSetMappingShared) settings_trust_images_setter
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.delete_event.connect(on_delete);
|
this.delete_event.connect(on_delete);
|
||||||
|
|
@ -252,4 +270,17 @@ public class Components.PreferencesWindow : Hdy.PreferencesWindow {
|
||||||
return Gdk.EVENT_PROPAGATE;
|
return Gdk.EVENT_PROPAGATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool settings_trust_images_getter(GLib.Value value, GLib.Variant variant, void* user_data) {
|
||||||
|
var domains = variant.get_strv();
|
||||||
|
value.set_boolean(domains.length > 0 && domains[0] == "*");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GLib.Variant settings_trust_images_setter(GLib.Value value, GLib.VariantType expected_type, void* user_data) {
|
||||||
|
var trusted = value.get_boolean();
|
||||||
|
string[] values = {};
|
||||||
|
if (trusted)
|
||||||
|
values += "*";
|
||||||
|
return new GLib.Variant.strv(values);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,8 @@ public class Conversation.ContactPopover : Gtk.Popover {
|
||||||
|
|
||||||
private GLib.Cancellable load_cancellable = new GLib.Cancellable();
|
private GLib.Cancellable load_cancellable = new GLib.Cancellable();
|
||||||
|
|
||||||
|
private Application.Configuration config;
|
||||||
|
|
||||||
[GtkChild] private unowned Gtk.Grid contact_pane;
|
[GtkChild] private unowned Gtk.Grid contact_pane;
|
||||||
|
|
||||||
[GtkChild] private unowned Hdy.Avatar avatar;
|
[GtkChild] private unowned Hdy.Avatar avatar;
|
||||||
|
|
@ -74,11 +76,13 @@ public class Conversation.ContactPopover : Gtk.Popover {
|
||||||
|
|
||||||
public ContactPopover(Gtk.Widget relative_to,
|
public ContactPopover(Gtk.Widget relative_to,
|
||||||
Application.Contact contact,
|
Application.Contact contact,
|
||||||
Geary.RFC822.MailboxAddress mailbox) {
|
Geary.RFC822.MailboxAddress mailbox,
|
||||||
|
Application.Configuration config) {
|
||||||
|
|
||||||
this.relative_to = relative_to;
|
this.relative_to = relative_to;
|
||||||
this.contact = contact;
|
this.contact = contact;
|
||||||
this.mailbox = mailbox;
|
this.mailbox = mailbox;
|
||||||
|
this.config = config;
|
||||||
|
|
||||||
this.load_remote_button.role = CHECK;
|
this.load_remote_button.role = CHECK;
|
||||||
|
|
||||||
|
|
@ -143,7 +147,10 @@ public class Conversation.ContactPopover : Gtk.Popover {
|
||||||
actions.lookup_action(ACTION_LOAD_REMOTE);
|
actions.lookup_action(ACTION_LOAD_REMOTE);
|
||||||
load_remote.set_state(
|
load_remote.set_state(
|
||||||
new GLib.Variant.boolean(
|
new GLib.Variant.boolean(
|
||||||
is_desktop || this.contact.load_remote_resources
|
is_desktop ||
|
||||||
|
Util.Contact.should_load_images(
|
||||||
|
this.contact,
|
||||||
|
this.config)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -177,6 +184,14 @@ public class Conversation.ContactPopover : Gtk.Popover {
|
||||||
|
|
||||||
private async void set_load_remote_resources(bool enabled) {
|
private async void set_load_remote_resources(bool enabled) {
|
||||||
try {
|
try {
|
||||||
|
// Remove all contact email domains from trusted list
|
||||||
|
// Otherwise, user may not understand why images are always shown
|
||||||
|
if (!enabled) {
|
||||||
|
var email_addresses = this.contact.email_addresses;
|
||||||
|
foreach (Geary.RFC822.MailboxAddress email in email_addresses) {
|
||||||
|
this.config.remove_images_trusted_domain(email.domain);
|
||||||
|
}
|
||||||
|
}
|
||||||
yield this.contact.set_remote_resource_loading(enabled, null);
|
yield this.contact.set_remote_resource_loading(enabled, null);
|
||||||
load_remote_resources_changed(enabled);
|
load_remote_resources_changed(enabled);
|
||||||
} catch (GLib.Error err) {
|
} catch (GLib.Error err) {
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,9 @@ public class ConversationMessage : Gtk.Grid, Geary.BaseInterface {
|
||||||
private const string ACTION_OPEN_LINK = "open-link";
|
private const string ACTION_OPEN_LINK = "open-link";
|
||||||
private const string ACTION_SAVE_IMAGE = "save-image";
|
private const string ACTION_SAVE_IMAGE = "save-image";
|
||||||
private const string ACTION_SELECT_ALL = "select-all";
|
private const string ACTION_SELECT_ALL = "select-all";
|
||||||
|
private const string ACTION_SHOW_IMAGES_MESSAGE = "show-images-message";
|
||||||
|
private const string ACTION_SHOW_IMAGES_SENDER = "show-images-sender";
|
||||||
|
private const string ACTION_SHOW_IMAGES_DOMAIN = "show-images-domain";
|
||||||
|
|
||||||
|
|
||||||
// Widget used to display sender/recipient email addresses in
|
// Widget used to display sender/recipient email addresses in
|
||||||
|
|
@ -377,6 +380,9 @@ public class ConversationMessage : Gtk.Grid, Geary.BaseInterface {
|
||||||
private MenuModel context_menu_main;
|
private MenuModel context_menu_main;
|
||||||
private MenuModel? context_menu_inspector = null;
|
private MenuModel? context_menu_inspector = null;
|
||||||
|
|
||||||
|
// Menu model for creating the show images menu
|
||||||
|
private MenuModel show_images_menu;
|
||||||
|
|
||||||
// Address fields that can be search through
|
// Address fields that can be search through
|
||||||
private Gee.List<ContactFlowBoxChild> searchable_addresses =
|
private Gee.List<ContactFlowBoxChild> searchable_addresses =
|
||||||
new Gee.LinkedList<ContactFlowBoxChild>();
|
new Gee.LinkedList<ContactFlowBoxChild>();
|
||||||
|
|
@ -397,6 +403,8 @@ public class ConversationMessage : Gtk.Grid, Geary.BaseInterface {
|
||||||
|
|
||||||
private int remote_resources_loaded = 0;
|
private int remote_resources_loaded = 0;
|
||||||
|
|
||||||
|
private bool authenticated_message = false;
|
||||||
|
|
||||||
// Timeouts for showing the progress bar and hiding it when
|
// Timeouts for showing the progress bar and hiding it when
|
||||||
// complete. The former is so that when loading cached images it
|
// complete. The former is so that when loading cached images it
|
||||||
// doesn't pop up and then go away immediately afterwards.
|
// doesn't pop up and then go away immediately afterwards.
|
||||||
|
|
@ -504,6 +512,12 @@ public class ConversationMessage : Gtk.Grid, Geary.BaseInterface {
|
||||||
.activate.connect(on_link_activated);
|
.activate.connect(on_link_activated);
|
||||||
add_action(ACTION_SAVE_IMAGE, true, new VariantType("(sms)"))
|
add_action(ACTION_SAVE_IMAGE, true, new VariantType("(sms)"))
|
||||||
.activate.connect(on_save_image);
|
.activate.connect(on_save_image);
|
||||||
|
add_action(ACTION_SHOW_IMAGES_MESSAGE, true)
|
||||||
|
.activate.connect(on_show_images);
|
||||||
|
add_action(ACTION_SHOW_IMAGES_SENDER, true)
|
||||||
|
.activate.connect(on_show_images_sender);
|
||||||
|
add_action(ACTION_SHOW_IMAGES_DOMAIN, true)
|
||||||
|
.activate.connect(on_show_images_domain);
|
||||||
insert_action_group("msg", message_actions);
|
insert_action_group("msg", message_actions);
|
||||||
|
|
||||||
// Context menu
|
// Context menu
|
||||||
|
|
@ -515,6 +529,9 @@ public class ConversationMessage : Gtk.Grid, Geary.BaseInterface {
|
||||||
context_menu_email = (MenuModel) builder.get_object("context_menu_email");
|
context_menu_email = (MenuModel) builder.get_object("context_menu_email");
|
||||||
context_menu_image = (MenuModel) builder.get_object("context_menu_image");
|
context_menu_image = (MenuModel) builder.get_object("context_menu_image");
|
||||||
context_menu_main = (MenuModel) builder.get_object("context_menu_main");
|
context_menu_main = (MenuModel) builder.get_object("context_menu_main");
|
||||||
|
|
||||||
|
show_images_menu = (MenuModel) builder.get_object("show_images_menu");
|
||||||
|
|
||||||
if (config.enable_inspector) {
|
if (config.enable_inspector) {
|
||||||
context_menu_inspector =
|
context_menu_inspector =
|
||||||
(MenuModel) builder.get_object("context_menu_inspector");
|
(MenuModel) builder.get_object("context_menu_inspector");
|
||||||
|
|
@ -872,11 +889,15 @@ public class ConversationMessage : Gtk.Grid, Geary.BaseInterface {
|
||||||
initialize_web_view();
|
initialize_web_view();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool contact_load_images = (
|
bool contact_load_images = Util.Contact.should_load_images(
|
||||||
this.primary_contact != null &&
|
this.primary_contact, this.config
|
||||||
this.primary_contact.load_remote_resources
|
|
||||||
);
|
);
|
||||||
if (this.load_remote_resources || contact_load_images) {
|
this.authenticated_message = message.auth_results != null && (
|
||||||
|
message.auth_results.is_dkim_valid() ||
|
||||||
|
message.auth_results.is_dmarc_valid()
|
||||||
|
);
|
||||||
|
if (this.load_remote_resources || (
|
||||||
|
contact_load_images && this.authenticated_message)) {
|
||||||
yield this.web_view.load_remote_resources(load_cancelled);
|
yield this.web_view.load_remote_resources(load_cancelled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1244,7 +1265,8 @@ public class ConversationMessage : Gtk.Grid, Geary.BaseInterface {
|
||||||
Conversation.ContactPopover popover = new Conversation.ContactPopover(
|
Conversation.ContactPopover popover = new Conversation.ContactPopover(
|
||||||
address_child,
|
address_child,
|
||||||
address_child.contact,
|
address_child.contact,
|
||||||
address
|
address,
|
||||||
|
this.config
|
||||||
);
|
);
|
||||||
popover.set_position(Gtk.PositionType.BOTTOM);
|
popover.set_position(Gtk.PositionType.BOTTOM);
|
||||||
popover.load_remote_resources_changed.connect((enabled) => {
|
popover.load_remote_resources_changed.connect((enabled) => {
|
||||||
|
|
@ -1391,51 +1413,48 @@ public class ConversationMessage : Gtk.Grid, Geary.BaseInterface {
|
||||||
|
|
||||||
private void on_remote_resources_blocked() {
|
private void on_remote_resources_blocked() {
|
||||||
if (this.remote_images_info_bar == null) {
|
if (this.remote_images_info_bar == null) {
|
||||||
|
/* If message is authenticated, user is allowed to whitelist
|
||||||
|
* images loading for sender/domain sender.
|
||||||
|
*/
|
||||||
|
if (this.authenticated_message) {
|
||||||
this.remote_images_info_bar = new Components.InfoBar(
|
this.remote_images_info_bar = new Components.InfoBar(
|
||||||
// Translators: Info bar status message
|
// Translators: Info bar status message
|
||||||
_("Remote images not shown"),
|
_("Remote images not shown"),
|
||||||
// Translators: Info bar description
|
// Translators: Info bar description
|
||||||
_("Only show remote images from senders you trust.")
|
_("Only show remote images from senders you trust.")
|
||||||
);
|
);
|
||||||
var show = this.remote_images_info_bar.add_button(
|
|
||||||
// Translators: Info bar button label
|
var menu_image = new Gtk.Image();
|
||||||
_("Show"), 1
|
menu_image.icon_name = "view-more-symbolic";
|
||||||
|
|
||||||
|
var menu_button = new Gtk.MenuButton();
|
||||||
|
menu_button.use_popover = true;
|
||||||
|
menu_button.image = menu_image;
|
||||||
|
menu_button.menu_model = this.show_images_menu;
|
||||||
|
menu_button.halign = Gtk.Align.END;
|
||||||
|
menu_button.hexpand =true;
|
||||||
|
menu_button.show_all();
|
||||||
|
|
||||||
|
this.remote_images_info_bar.get_action_area().add(menu_button);
|
||||||
|
} else {
|
||||||
|
this.remote_images_info_bar = new Components.InfoBar(
|
||||||
|
// Translators: Info bar status message
|
||||||
|
_("Remote images not shown"),
|
||||||
|
// Translators: Info bar description
|
||||||
|
_("This message can't be trusted.")
|
||||||
);
|
);
|
||||||
this.remote_images_info_bar.add_button(
|
this.remote_images_info_bar.add_button(
|
||||||
// Translators: Info bar button label
|
// Translators: Info bar button label
|
||||||
_("Always show from sender"), 2
|
_("Show"), 1
|
||||||
);
|
);
|
||||||
this.remote_images_info_bar.response.connect(on_remote_images_response);
|
this.remote_images_info_bar.response.connect(() => {
|
||||||
var buttons = this.remote_images_info_bar.get_action_area() as Gtk.ButtonBox;
|
show_images(true);
|
||||||
if (buttons != null) {
|
});
|
||||||
buttons.set_child_non_homogeneous(show, true);
|
|
||||||
}
|
}
|
||||||
this.info_bars.add(this.remote_images_info_bar);
|
this.info_bars.add(this.remote_images_info_bar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void on_remote_images_response(Components.InfoBar info_bar, int response_id) {
|
|
||||||
switch (response_id) {
|
|
||||||
case 1:
|
|
||||||
// Show images for the message
|
|
||||||
show_images(true);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
// Show images for sender
|
|
||||||
show_images(false);
|
|
||||||
if (this.primary_contact != null) {
|
|
||||||
this.primary_contact.set_remote_resource_loading.begin(
|
|
||||||
true, null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
this.info_bars.remove(this.remote_images_info_bar);
|
|
||||||
this.remote_images_info_bar = null;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void on_copy_link(Variant? param) {
|
private void on_copy_link(Variant? param) {
|
||||||
Gtk.Clipboard clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD);
|
Gtk.Clipboard clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD);
|
||||||
clipboard.set_text(param.get_string(), -1);
|
clipboard.set_text(param.get_string(), -1);
|
||||||
|
|
@ -1484,6 +1503,30 @@ public class ConversationMessage : Gtk.Grid, Geary.BaseInterface {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void on_show_images(Variant? param) {
|
||||||
|
show_images(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void on_show_images_sender(Variant? param) {
|
||||||
|
show_images(false);
|
||||||
|
if (this.primary_contact != null) {
|
||||||
|
this.primary_contact.set_remote_resource_loading.begin(
|
||||||
|
true, null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void on_show_images_domain(Variant? param) {
|
||||||
|
show_images(false);
|
||||||
|
if (this.primary_contact != null) {
|
||||||
|
var email_addresses = this.primary_contact.email_addresses;
|
||||||
|
foreach (Geary.RFC822.MailboxAddress email in email_addresses) {
|
||||||
|
this.config.add_images_trusted_domain(email.domain);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void on_link_activated(GLib.Variant? param) {
|
private void on_link_activated(GLib.Variant? param) {
|
||||||
string link = param.get_string();
|
string link = param.get_string();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -138,6 +138,7 @@ client_vala_sources = files(
|
||||||
|
|
||||||
'util/util-avatar.vala',
|
'util/util-avatar.vala',
|
||||||
'util/util-cache.vala',
|
'util/util-cache.vala',
|
||||||
|
'util/util-contact.vala',
|
||||||
'util/util-date.vala',
|
'util/util-date.vala',
|
||||||
'util/util-email.vala',
|
'util/util-email.vala',
|
||||||
'util/util-files.vala',
|
'util/util-files.vala',
|
||||||
|
|
|
||||||
34
src/client/util/util-contact.vala
Normal file
34
src/client/util/util-contact.vala
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2022 Cédric Bellegarde <cedric.bellegarde@adishatz.org>
|
||||||
|
*
|
||||||
|
* This software is licensed under the GNU Lesser General Public License
|
||||||
|
* (version 2.1 or later). See the COPYING file in this distribution.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Util.Contact {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if loading images for contact is allowed
|
||||||
|
*/
|
||||||
|
public bool should_load_images(Application.Contact contact, Application.Configuration config) {
|
||||||
|
var email_addresses = contact.email_addresses;
|
||||||
|
var domains = config.get_images_trusted_domains();
|
||||||
|
if (contact == null) {
|
||||||
|
return false;
|
||||||
|
// Contact trusted
|
||||||
|
} else if (contact.load_remote_resources) {
|
||||||
|
return true;
|
||||||
|
// All emails are trusted
|
||||||
|
} else if (domains.length > 0 && domains[0] == "*") {
|
||||||
|
return true;
|
||||||
|
// Contact domain trusted
|
||||||
|
} else {
|
||||||
|
foreach (Geary.RFC822.MailboxAddress email in email_addresses) {
|
||||||
|
if (email.domain in domains) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -45,4 +45,21 @@
|
||||||
</item>
|
</item>
|
||||||
</section>
|
</section>
|
||||||
</menu>
|
</menu>
|
||||||
|
<menu id="show_images_menu">
|
||||||
|
<section>
|
||||||
|
<attribute name="label" translatable="yes">Show images</attribute>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">For this message</attribute>
|
||||||
|
<attribute name="action">msg.show-images-message</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">For this sender</attribute>
|
||||||
|
<attribute name="action">msg.show-images-sender</attribute>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label" translatable="yes">For this domain</attribute>
|
||||||
|
<attribute name="action">msg.show-images-domain</attribute>
|
||||||
|
</item>
|
||||||
|
</section>
|
||||||
|
</menu>
|
||||||
</interface>
|
</interface>
|
||||||
|
|
|
||||||
13
ui/geary.css
13
ui/geary.css
|
|
@ -159,6 +159,19 @@ row.geary-folder-popover-list-row > label {
|
||||||
border-width: 0;
|
border-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.geary-message infobar box button {
|
||||||
|
background: alpha(black, 0.1);
|
||||||
|
color: alpha(@theme_text_color, 0.7);
|
||||||
|
border: none;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.geary-message infobar box button:hover,
|
||||||
|
.geary-message infobar box button:checked {
|
||||||
|
background: alpha(black, 0.2);
|
||||||
|
color: @theme_text_color;
|
||||||
|
}
|
||||||
|
|
||||||
grid.geary-message-summary {
|
grid.geary-message-summary {
|
||||||
border-top: 4px solid transparent;
|
border-top: 4px solid transparent;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue