Show emails attached to main message; fix #6450
Attachments to those emails are not found yet.
This commit is contained in:
parent
dbfce10d44
commit
2617f9949e
4 changed files with 227 additions and 132 deletions
|
|
@ -149,7 +149,6 @@ public class ConversationViewer : Gtk.Box {
|
|||
return;
|
||||
|
||||
string message_id = get_div_id(email.id);
|
||||
string header = "";
|
||||
|
||||
WebKit.DOM.Node insert_before = web_view.container.get_last_child();
|
||||
|
||||
|
|
@ -158,36 +157,12 @@ public class ConversationViewer : Gtk.Box {
|
|||
if (higher != null)
|
||||
insert_before = web_view.get_dom_document().get_element_by_id(get_div_id(higher.id));
|
||||
|
||||
WebKit.DOM.HTMLElement div_email_container;
|
||||
WebKit.DOM.HTMLElement div_message;
|
||||
|
||||
try {
|
||||
// The HTML is like this:
|
||||
// <div id="$MESSAGE_ID" class="email">
|
||||
// <div class="geary_spacer"></div>
|
||||
// <div class="email_container">
|
||||
// <div class="button_bar">
|
||||
// <div class="starred button"><img class="icon" /></div>
|
||||
// <div class="unstarred button"><img class="icon" /></div>
|
||||
// <div class="menu button"><img class="icon" /></div>
|
||||
// </div>
|
||||
// <table>$HEADER</table>
|
||||
// <span>
|
||||
// $EMAIL_BODY
|
||||
//
|
||||
// <div class="signature">$SIGNATURE</div>
|
||||
//
|
||||
// <div class="quote_container controllable">
|
||||
// <div class="shower">[show]</div>
|
||||
// <div class="hider">[hide]</div>
|
||||
// <div class="quote">$QUOTE</div>
|
||||
// </div>
|
||||
// </span>
|
||||
// </div>
|
||||
// </div>
|
||||
div_message = Util.DOM.clone_select(web_view.get_dom_document(), "#email_template");
|
||||
div_message = make_email_div();
|
||||
div_message.set_attribute("id", message_id);
|
||||
web_view.container.insert_before(div_message, insert_before);
|
||||
div_email_container = Util.DOM.select(div_message, "div.email_container");
|
||||
if (email.is_unread() == Geary.Trillian.FALSE) {
|
||||
div_message.get_class_list().add("hide");
|
||||
}
|
||||
|
|
@ -196,93 +171,35 @@ public class ConversationViewer : Gtk.Box {
|
|||
|
||||
return;
|
||||
}
|
||||
|
||||
email_to_element.set(email.id, div_message);
|
||||
|
||||
insert_header_address(ref header, _("From:"), email.from != null ? email.from : email.sender,
|
||||
true);
|
||||
|
||||
if (email.to != null)
|
||||
insert_header_address(ref header, _("To:"), email.to);
|
||||
|
||||
if (email.cc != null) {
|
||||
insert_header_address(ref header, _("Cc:"), email.cc);
|
||||
}
|
||||
|
||||
if (email.bcc != null) {
|
||||
insert_header_address(ref header, _("Bcc:"), email.bcc);
|
||||
}
|
||||
|
||||
if (email.subject != null)
|
||||
insert_header(ref header, _("Subject:"), email.get_subject_as_string());
|
||||
|
||||
if (email.date != null)
|
||||
insert_header_date(ref header, _("Date:"), email.date.value, true);
|
||||
|
||||
// Add the avatar.
|
||||
Geary.RFC822.MailboxAddress? primary = email.get_primary_originator();
|
||||
if (primary != null) {
|
||||
try {
|
||||
WebKit.DOM.HTMLImageElement icon = Util.DOM.select(div_message, ".avatar")
|
||||
as WebKit.DOM.HTMLImageElement;
|
||||
icon.set_attribute("src",
|
||||
Gravatar.get_image_uri(primary, Gravatar.Default.MYSTERY_MAN, 48));
|
||||
} catch (Error error) {
|
||||
debug("Failed to inject avatar URL: %s", error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Insert the preview text.
|
||||
try {
|
||||
WebKit.DOM.HTMLElement preview =
|
||||
Util.DOM.select(div_message, ".header_container .preview");
|
||||
string preview_str = email.get_preview_as_string();
|
||||
if (preview_str.length == Geary.Email.MAX_PREVIEW_BYTES) {
|
||||
preview_str += "…";
|
||||
}
|
||||
preview.set_inner_text(Geary.String.reduce_whitespace(preview_str));
|
||||
} catch (Error error) {
|
||||
debug("Failed to add preview text: %s", error.message);
|
||||
}
|
||||
|
||||
string body_text = "";
|
||||
bool remote_images = false;
|
||||
try {
|
||||
body_text = email.get_message().get_body(true);
|
||||
body_text = insert_html_markup(body_text, email, out remote_images);
|
||||
} catch (Error err) {
|
||||
debug("Could not get message text. %s", err.message);
|
||||
set_message_html(email.get_message(), div_message, out remote_images);
|
||||
} catch (Error error) {
|
||||
warning("Error getting message from email: %s", error.message);
|
||||
}
|
||||
|
||||
// Graft header and email body into the email container.
|
||||
try {
|
||||
WebKit.DOM.HTMLElement table_header =
|
||||
Util.DOM.select(div_email_container, ".header_container .header");
|
||||
table_header.set_inner_html(header);
|
||||
|
||||
WebKit.DOM.HTMLElement span_body = Util.DOM.select(div_email_container, ".body");
|
||||
span_body.set_inner_html(body_text);
|
||||
|
||||
if (remote_images) {
|
||||
WebKit.DOM.HTMLElement remote_images_bar =
|
||||
Util.DOM.select(div_email_container, ".remote_images");
|
||||
|
||||
if (remote_images) {
|
||||
WebKit.DOM.HTMLElement remote_images_bar =
|
||||
Util.DOM.select(div_message, ".remote_images");
|
||||
try {
|
||||
((WebKit.DOM.Element) remote_images_bar).get_class_list().add("show");
|
||||
remote_images_bar.set_inner_html("""%s %s
|
||||
<input type="button" value="%s" class="show_images" />""".printf(
|
||||
remote_images_bar.get_inner_html(), _("This message contains remote images."),
|
||||
_("Show Images")));
|
||||
} catch (Error error) {
|
||||
warning("Error showing remote images bar: %s", error.message);
|
||||
}
|
||||
|
||||
} catch (Error html_error) {
|
||||
warning("Error setting HTML for message: %s", html_error.message);
|
||||
}
|
||||
|
||||
|
||||
// Set attachment icon and add the attachments container if we have any attachments.
|
||||
set_attachment_icon(div_message, email.attachments.size > 0);
|
||||
if (email.attachments.size > 0) {
|
||||
insert_attachments(div_message, email.attachments);
|
||||
}
|
||||
|
||||
|
||||
// Add classes according to the state of the email.
|
||||
update_flags(email);
|
||||
|
||||
|
|
@ -304,7 +221,7 @@ public class ConversationViewer : Gtk.Box {
|
|||
bind_event(web_view, ".email_container .starred", "click", (Callback) on_unstar_clicked, this);
|
||||
bind_event(web_view, ".email_container .unstarred", "click", (Callback) on_star_clicked, this);
|
||||
bind_event(web_view, ".header .field .value", "click", (Callback) on_value_clicked, this);
|
||||
bind_event(web_view, ".email:not(:only-of-type) .header_container", "click", (Callback) on_body_toggle_clicked, this);
|
||||
bind_event(web_view, ".email:not(:only-of-type) .header_container, .email .email .header_container","click", (Callback) on_body_toggle_clicked, this);
|
||||
bind_event(web_view, ".email .compressed_note", "click", (Callback) on_body_toggle_clicked, this);
|
||||
bind_event(web_view, ".attachment_container .attachment", "click", (Callback) on_attachment_clicked, this);
|
||||
bind_event(web_view, ".attachment_container .attachment", "contextmenu", (Callback) on_attachment_menu, this);
|
||||
|
|
@ -312,6 +229,120 @@ public class ConversationViewer : Gtk.Box {
|
|||
bind_event(web_view, ".remote_images .close_show_images", "click", (Callback) on_close_show_images, this);
|
||||
}
|
||||
|
||||
private WebKit.DOM.HTMLElement make_email_div() {
|
||||
// The HTML is like this:
|
||||
// <div id="$MESSAGE_ID" class="email">
|
||||
// <div class="geary_spacer"></div>
|
||||
// <div class="email_container">
|
||||
// <div class="button_bar">
|
||||
// <div class="starred button"><img class="icon" /></div>
|
||||
// <div class="unstarred button"><img class="icon" /></div>
|
||||
// <div class="menu button"><img class="icon" /></div>
|
||||
// </div>
|
||||
// <table>$HEADER</table>
|
||||
// <span>
|
||||
// $EMAIL_BODY
|
||||
//
|
||||
// <div class="signature">$SIGNATURE</div>
|
||||
//
|
||||
// <div class="quote_container controllable">
|
||||
// <div class="shower">[show]</div>
|
||||
// <div class="hider">[hide]</div>
|
||||
// <div class="quote">$QUOTE</div>
|
||||
// </div>
|
||||
// </span>
|
||||
// </div>
|
||||
// </div>
|
||||
return Util.DOM.clone_select(web_view.get_dom_document(), "#email_template");
|
||||
}
|
||||
|
||||
private void set_message_html(Geary.RFC822.Message message, WebKit.DOM.HTMLElement div_message,
|
||||
out bool remote_images) {
|
||||
string header = "";
|
||||
WebKit.DOM.HTMLElement div_email_container = Util.DOM.select(div_message, "div.email_container");
|
||||
|
||||
insert_header_address(ref header, _("From:"), message.from, true);
|
||||
|
||||
if (message.to != null)
|
||||
insert_header_address(ref header, _("To:"), message.to);
|
||||
|
||||
if (message.cc != null)
|
||||
insert_header_address(ref header, _("Cc:"), message.cc);
|
||||
|
||||
if (message.bcc != null)
|
||||
insert_header_address(ref header, _("Bcc:"), message.bcc);
|
||||
|
||||
if (message.subject != null)
|
||||
insert_header(ref header, _("Subject:"), message.subject.value);
|
||||
|
||||
if (message.date != null)
|
||||
insert_header_date(ref header, _("Date:"), message.date.value, true);
|
||||
|
||||
// Add the avatar.
|
||||
Geary.RFC822.MailboxAddress? primary = message.sender;
|
||||
if (primary != null) {
|
||||
try {
|
||||
WebKit.DOM.HTMLImageElement icon = Util.DOM.select(div_message, ".avatar")
|
||||
as WebKit.DOM.HTMLImageElement;
|
||||
icon.set_attribute("src",
|
||||
Gravatar.get_image_uri(primary, Gravatar.Default.MYSTERY_MAN, 48));
|
||||
} catch (Error error) {
|
||||
debug("Failed to inject avatar URL: %s", error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Insert the preview text.
|
||||
try {
|
||||
WebKit.DOM.HTMLElement preview =
|
||||
Util.DOM.select(div_message, ".header_container .preview");
|
||||
string preview_str = message.get_preview();
|
||||
if (preview_str.length == Geary.Email.MAX_PREVIEW_BYTES) {
|
||||
preview_str += "…";
|
||||
}
|
||||
preview.set_inner_text(Geary.String.reduce_whitespace(preview_str));
|
||||
} catch (Error error) {
|
||||
debug("Failed to add preview text: %s", error.message);
|
||||
}
|
||||
|
||||
string body_text = "";
|
||||
remote_images = false;
|
||||
try {
|
||||
body_text = message.get_body(true);
|
||||
body_text = insert_html_markup(body_text, message, out remote_images);
|
||||
} catch (Error err) {
|
||||
debug("Could not get message text. %s", err.message);
|
||||
}
|
||||
|
||||
// Graft header and email body into the email container.
|
||||
try {
|
||||
WebKit.DOM.HTMLElement table_header =
|
||||
Util.DOM.select(div_email_container, ".header_container .header");
|
||||
table_header.set_inner_html(header);
|
||||
|
||||
WebKit.DOM.HTMLElement span_body = Util.DOM.select(div_email_container, ".body");
|
||||
span_body.set_inner_html(body_text);
|
||||
} catch (Error html_error) {
|
||||
warning("Error setting HTML for message: %s", html_error.message);
|
||||
}
|
||||
|
||||
// Look for any attached emails
|
||||
Gee.List<Geary.RFC822.Message> sub_messages = message.get_sub_messages();
|
||||
foreach (Geary.RFC822.Message sub_message in sub_messages) {
|
||||
WebKit.DOM.HTMLElement div_sub_message = make_email_div();
|
||||
bool sub_remote_images = false;
|
||||
try {
|
||||
div_sub_message.set_attribute("id", "");
|
||||
div_sub_message.get_class_list().add("read");
|
||||
div_sub_message.get_class_list().add("hide");
|
||||
div_message.append_child(div_sub_message);
|
||||
set_message_html(sub_message, div_sub_message, out sub_remote_images);
|
||||
remote_images = remote_images || sub_remote_images;
|
||||
} catch (Error error) {
|
||||
debug("Error adding message: %s", error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void unhide_last_email() {
|
||||
WebKit.DOM.HTMLElement last_email = (WebKit.DOM.HTMLElement) web_view.container.get_last_child().previous_sibling;
|
||||
if (last_email != null) {
|
||||
|
|
@ -644,18 +675,22 @@ public class ConversationViewer : Gtk.Box {
|
|||
private void show_images_email(WebKit.DOM.Element email_element) {
|
||||
// TODO: Remember that these images have been shown.
|
||||
try {
|
||||
WebKit.DOM.Element? body = email_element.query_selector(".body");
|
||||
if (body == null)
|
||||
return;
|
||||
|
||||
WebKit.DOM.NodeList nodes = body.query_selector_all("img");
|
||||
for (ulong i = 0; i < nodes.length; i++) {
|
||||
WebKit.DOM.Element? element = nodes.item(i) as WebKit.DOM.Element;
|
||||
if (element == null || !element.has_attribute("src"))
|
||||
WebKit.DOM.NodeList body_nodes = email_element.query_selector_all(".body");
|
||||
for (ulong j = 0; j < body_nodes.length; j++) {
|
||||
WebKit.DOM.Element? body = body_nodes.item(j) as WebKit.DOM.Element;
|
||||
if (body == null)
|
||||
continue;
|
||||
|
||||
string src = element.get_attribute("src");
|
||||
element.set_attribute("src", web_view.allow_prefix + src);
|
||||
WebKit.DOM.NodeList nodes = body.query_selector_all("img");
|
||||
for (ulong i = 0; i < nodes.length; i++) {
|
||||
WebKit.DOM.Element? element = nodes.item(i) as WebKit.DOM.Element;
|
||||
if (element == null || !element.has_attribute("src"))
|
||||
continue;
|
||||
|
||||
string src = element.get_attribute("src");
|
||||
if (!web_view.is_always_loaded(src))
|
||||
element.set_attribute("src", web_view.allow_prefix + src);
|
||||
}
|
||||
}
|
||||
|
||||
WebKit.DOM.Element? remote_images = email_element.query_selector(".remote_images");
|
||||
|
|
@ -892,7 +927,7 @@ public class ConversationViewer : Gtk.Box {
|
|||
}
|
||||
}
|
||||
|
||||
private string insert_html_markup(string text, Geary.Email email, out bool remote_images) {
|
||||
private string insert_html_markup(string text, Geary.RFC822.Message message, out bool remote_images) {
|
||||
remote_images = false;
|
||||
try {
|
||||
// Create a workspace for manipulating the HTML.
|
||||
|
|
@ -948,7 +983,7 @@ public class ConversationViewer : Gtk.Box {
|
|||
} else if (src.has_prefix("cid:")) {
|
||||
string mime_id = src.substring(4);
|
||||
Geary.Memory.AbstractBuffer image_content =
|
||||
email.get_message().get_content_by_mime_id(mime_id);
|
||||
message.get_content_by_mime_id(mime_id);
|
||||
uint8[] image_data = image_content.get_array();
|
||||
|
||||
// Get the content type.
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ public class ConversationWebView : WebKit.WebView {
|
|||
}
|
||||
}
|
||||
|
||||
private bool is_always_loaded(string? uri) {
|
||||
public bool is_always_loaded(string? uri) {
|
||||
if (uri == null)
|
||||
return true;
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ public class Geary.RFC822.Message : BaseObject {
|
|||
public RFC822.MessageIDList? references { get; private set; default = null; }
|
||||
public RFC822.Subject? subject { get; private set; default = null; }
|
||||
public string? mailer { get; private set; default = null; }
|
||||
public Geary.RFC822.Date? date { get; private set; default = null; }
|
||||
|
||||
private GMime.Message message;
|
||||
|
||||
|
|
@ -36,6 +37,11 @@ public class Geary.RFC822.Message : BaseObject {
|
|||
stock_from_gmime();
|
||||
}
|
||||
|
||||
public Message.from_gmime_message(GMime.Message message) {
|
||||
this.message = message;
|
||||
stock_from_gmime();
|
||||
}
|
||||
|
||||
public Message.from_string(string full_email) throws RFC822Error {
|
||||
this(new Geary.RFC822.Full(new Geary.Memory.StringBuffer(full_email)));
|
||||
}
|
||||
|
|
@ -162,7 +168,8 @@ public class Geary.RFC822.Message : BaseObject {
|
|||
sender = email.sender;
|
||||
message.set_sender(email.message.get_sender());
|
||||
|
||||
message.set_date_as_string(email.message.get_date_as_string());
|
||||
date = email.date;
|
||||
message.set_date_as_string(email.date.to_string());
|
||||
|
||||
// Optional headers.
|
||||
if (email.to != null) {
|
||||
|
|
@ -248,43 +255,43 @@ public class Geary.RFC822.Message : BaseObject {
|
|||
|
||||
email.set_message_header(new Geary.RFC822.Header(new Geary.Memory.StringBuffer(
|
||||
message.get_headers())));
|
||||
email.set_send_date(new Geary.RFC822.Date(message.get_date_as_string()));
|
||||
email.set_send_date(date);
|
||||
email.set_originators(from, new Geary.RFC822.MailboxAddresses.single(sender), null);
|
||||
email.set_receivers(to, cc, bcc);
|
||||
email.set_full_references(null, in_reply_to, references);
|
||||
email.set_message_subject(subject);
|
||||
email.set_message_body(new Geary.RFC822.Text(new Geary.Memory.StringBuffer(
|
||||
message.get_body().to_string())));
|
||||
email.set_message_preview(new Geary.RFC822.PreviewText.from_string(
|
||||
preview_from_email(email)));
|
||||
email.set_message_preview(new Geary.RFC822.PreviewText.from_string(get_preview()));
|
||||
|
||||
return email;
|
||||
}
|
||||
|
||||
// Takes an e-mail object with a body and generates a preview. If there is no body
|
||||
// or the body is the empty string, the empty string will be returned.
|
||||
//
|
||||
// Note that this is intended for outgoing messages, and as such we rely on the text
|
||||
// section existing.
|
||||
private string preview_from_email(Geary.Email email) {
|
||||
public string get_preview() {
|
||||
string? preview = null;
|
||||
try {
|
||||
return Geary.String.safe_byte_substring(email.get_message().
|
||||
get_first_mime_part_of_content_type("text/plain").to_string().
|
||||
chug(), Geary.Email.MAX_PREVIEW_BYTES);
|
||||
preview = get_text_body(false);
|
||||
} catch (Error e) {
|
||||
debug("Could not generate outbox preview: %s", e.message);
|
||||
|
||||
// fall through
|
||||
try {
|
||||
preview = Geary.HTML.remove_html_tags(get_html_body());
|
||||
} catch (Error error) {
|
||||
debug("Could not generate message preview: %s\n and: %s", e.message, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
return Geary.String.safe_byte_substring((preview ?? "").chug(),
|
||||
Geary.Email.MAX_PREVIEW_BYTES);
|
||||
}
|
||||
|
||||
private void stock_from_gmime() {
|
||||
from = new RFC822.MailboxAddresses.from_rfc822_string(message.get_sender());
|
||||
|
||||
// sender is defined as first From address, from better or worse
|
||||
sender = (from.size != 0) ? from[0] : null;
|
||||
string? message_sender = message.get_sender();
|
||||
if (message_sender != null) {
|
||||
from = new RFC822.MailboxAddresses.from_rfc822_string(message_sender);
|
||||
// sender is defined as first From address, from better or worse
|
||||
sender = (from.size != 0) ? from[0] : null;
|
||||
}
|
||||
|
||||
Gee.List<RFC822.MailboxAddress>? converted = convert_gmime_address_list(
|
||||
message.get_recipients(GMime.RecipientType.TO));
|
||||
|
|
@ -310,6 +317,14 @@ public class Geary.RFC822.Message : BaseObject {
|
|||
|
||||
if (!String.is_empty(message.get_header(HEADER_MAILER)))
|
||||
mailer = message.get_header(HEADER_MAILER);
|
||||
|
||||
if (!String.is_empty(message.get_date_as_string())) {
|
||||
try {
|
||||
date = new Geary.RFC822.Date(message.get_date_as_string());
|
||||
} catch (Error error) {
|
||||
debug("Could not get date from message: %s", error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Gee.List<RFC822.MailboxAddress>? convert_gmime_address_list(InternetAddressList? addrlist,
|
||||
|
|
@ -485,6 +500,30 @@ public class Geary.RFC822.Message : BaseObject {
|
|||
attachments.add(root as GMime.Part);
|
||||
}
|
||||
}
|
||||
|
||||
public Gee.List<Geary.RFC822.Message> get_sub_messages() {
|
||||
Gee.List<Geary.RFC822.Message> messages = new Gee.ArrayList<Geary.RFC822.Message>();
|
||||
find_sub_messages(messages, message.get_mime_part());
|
||||
return messages;
|
||||
}
|
||||
|
||||
private void find_sub_messages(Gee.List<Geary.RFC822.Message> messages, GMime.Object root) {
|
||||
// If this is a multipart container, check each of its children.
|
||||
GMime.Multipart? multipart = root as GMime.Multipart;
|
||||
if (multipart != null) {
|
||||
int count = multipart.get_count();
|
||||
for (int i = 0; i < count; ++i) {
|
||||
find_sub_messages(messages, multipart.get_part(i));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
GMime.MessagePart? messagepart = root as GMime.MessagePart;
|
||||
if (messagepart != null) {
|
||||
GMime.Message sub_message = messagepart.get_message();
|
||||
messages.add(new Geary.RFC822.Message.from_gmime_message(sub_message));
|
||||
}
|
||||
}
|
||||
|
||||
private Geary.Memory.AbstractBuffer mime_part_to_memory_buffer(GMime.Part part,
|
||||
bool to_utf8 = false, bool to_html = false) throws RFC822Error {
|
||||
|
|
|
|||
|
|
@ -200,10 +200,15 @@ hr {
|
|||
}
|
||||
.email.hide .body,
|
||||
.email.hide > .attachment_container,
|
||||
.email:not(.hide) .header_container .preview {
|
||||
.email:not(.hide) .header_container .preview,
|
||||
.email.hide .email {
|
||||
display: none;
|
||||
}
|
||||
.email:not(:only-of-type) .header_container {
|
||||
.email.hide .header_container .preview {
|
||||
display: block;
|
||||
}
|
||||
.email:not(:only-of-type) .header_container,
|
||||
.email .email .header_container {
|
||||
cursor: pointer;
|
||||
}
|
||||
.email:not(.hide) .header .field .value {
|
||||
|
|
@ -263,6 +268,18 @@ hr {
|
|||
cursor: hand;
|
||||
}
|
||||
|
||||
.email .email {
|
||||
box-shadow: none;
|
||||
margin-top: 0;
|
||||
border: none;
|
||||
border-top: 1px rgba(0,0,0,0.4) solid;
|
||||
}
|
||||
.email .email .email_container .menu,
|
||||
.email .email .email_container .starred,
|
||||
.email .email .email_container .unstarred {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.email:not(.attachment) .attachment.icon {
|
||||
display: none;
|
||||
}
|
||||
|
|
@ -280,6 +297,10 @@ hr {
|
|||
background-color: white;
|
||||
margin: 0 16px 5px;
|
||||
}
|
||||
.email > .email + .attachment_container .top_border{
|
||||
height: auto;
|
||||
margin: 0;
|
||||
}
|
||||
.email > .attachment_container > .attachment {
|
||||
margin: 10px 10px 0 10px;
|
||||
padding: 2px;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue