I am working on Ruby on Rails applications. We choose the AWS SES service to send emails to our users. We are using the aws-sdk-ses gem for this purpose.
And I'm faced with a problem with previewing emails.
My user_mailer.rb has the next code:
# frozen_string_literal: true
class UserMailer < ApplicationMailer
layout 'mailer'
def invite
invitation = params[:invitation]
email = invitation.email
subject = 'Invitation email'
# The HTML body of the email
htmlbody = render_to_string(
template: 'user_mailer/invite',
layout: true,
locals: {
email: receiver_email,
}
)
send_email(
receiver: receiver_email,
sender: 'default#email.com',
subject: subject,
body: htmlbody
)
end
def send_email(receiver:, sender:, subject:, body:)
AWS_SES_CLIENT.send_email(
{
destination: { to_addresses: [receiver] },
message: {
body: {
html: { charset: 'UTF-8',
data: body }
},
subject: {
charset: 'UTF-8',
data: subject
}
},
source: sender
}
)
rescue Aws::SES::Errors::ServiceError => e
Rails.logger.info "ERROR! Email for #{receiver} with subject #{subject} wasn't sent with error message: #{e}"
end
end
And my spec/mailers/preview/user_mailer_preview.rb has the next code:
# frozen_string_literal: true
# Preview all emails at http://localhost:3000/rails/mailers/user_mailer
class UserMailerPreview < ActionMailer::Preview
def invite
UserMailer.with(invitation: UserInvitation.first).invite
end
end
But when I go to the http://localhost:3000/rails/mailers/user_mailer I see the message
You are trying to preview an email that does not have any content.
This is probably because the mail method has not been called in
user_mailer#invite.
I suggest that standard Rails previewer track the calling the mail method, but I'm used custom send_email method to send email. So it doesn't work. I'm trying to find the solution on the AWS SDK guide but I didn't. I suppose it needs to reconfigure action_mailer in some way.
I'm using this tutorial to try to facilitate image uploads using the TinyMCE WYSIWYG editor on my rails 5.2 app.
So far I have implemented all the code in the tutorial and everything works perfectly, but when I try to upload an image I get a "Got a bad response from the server" error message.
In my heroku logs I then get this:
FATAL -- : [527c4468-9ebe-475a-97a9-380bfc327aab] ActionController::RoutingError (uninitialized constant #<Class:0x000055b6d991e020>::EditController
2020-02-22T17:34:07.814727+00:00 app[web.1]: Did you mean? DeviseController):
Here is the route I'm using:
post '/tinymce_assets', to: 'article/edit#image_upload'
With these controller methods:
def image_upload
file = params[:file]
url = upload_file(file)
render json: {
image: {
url: url
}
}, content_type: "text/html"
end
private
def upload_file(file)
s3 = Aws::S3::Resource.new(region:ENV['AWS_REGION'])
obj = s3.bucket(ENV['S3_BUCKET_NAME']).object('articles/images/content/' + filename(file))
obj.upload_file(file.tempfile, {acl: 'public-read'})
obj.public_url.to_s
end
def filename(file)
file.original_filename.gsub(/[^a-zA-Z0-9_\.]/, '_')
end
And this to initialize it:
<%= f.text_area :body, class: "tinymce", rows: 20, cols: 120 %>
<%= tinymce :content_css => asset_path('application.css')%>
...
<script>
$(document).ready(function() {
tinymce.init({
selector: "textarea.tinymce", // change this value according to your HTML
});
});
</script>
Can anyone see where I'm going wrong here?
Rails is trying to find an EditController because of your route: post '/tinymce_assets', to: 'article/edit#image_upload'
This route suggests that the controller it should look in is Article::EditController. Since you are actually looking for the ArticlesController you should change your route to be:
post '/tinymce_assets', to: 'articles#image_upload'
The Rails routing documentation can be helpful, specifically section 2.2 and 2.6. https://guides.rubyonrails.org/routing.html
After already checking the Rails Gem repository for similar issues as well as Stack Overflow, I couldn't find an answer to my problem.
I'm trying to render a pdf using wicked_pdf within a Rails controller, but the header is not showing up, no matter what I do or which recommended solutions to similar issues I follow.
First and foremost, here is the development console output:
***************WICKED***************
Rendering biddings/show.pdf.html.haml within layouts/pdf
Rendered biddings/show.pdf.html.haml within layouts/pdf (0.7ms)
Rendering biddings/header_pdf.html.haml within layouts/pdf_header
Rendered biddings/header_pdf.html.haml within layouts/pdf_header (1.9ms)
"***************[\"/home/tommy/.rvm/gems/ruby-
2.5.1#igalbids/bin/wkhtmltopdf\", \"-q\", \"--encoding\", \"UTF-8\",
\"--javascript-delay\", \"500\",
\"--disable-internal-links\", \"--disable-external-links\",
\"--orientation\", \"Portrait\", \"--margin-top\",
\"50\", \"--margin-bottom\", \"25\", \"--header-html\",
\"file:////tmp/wicked_header_pdf20180801-27285-b8y5sg.html\",
\"--footer-right\", \"Página [page] de [topage]\",
\"file:////tmp/wicked_pdf20180801-27285-1jfgdd7.html\",
\"/tmp/wicked_pdf_generated_file20180801-27285-1bkrvhx.pdf\"]***************"
Rendering text template
Rendered text template (0.1ms)
Sent data Licitación_2524.pdf (0.6ms)
Completed 200 OK in 2334ms (Views: 0.5ms | ActiveRecord: 64.4ms)
As you can see, both the header layout and its contents are being rendered and processed, however they don't make the final output PDF, and I don't know why! Look:
So, here's my controller code:
class Api::V1::Biddings::PdfBiddingsController < PdfController
# JWT Authentication enforced
before_action :authenticate_user!
# GET /biddings/:id/pdf
def show
#bidding = scoped_collection.find(params[:id])
authorize [:biddings, :pdf, #bidding]
respond_to do |format|
format.pdf do
render(
pdf: "#{Bidding.model_name.human}_#{#bidding.code}",
disposition: "inline",
orientation: "Portrait",
template: 'biddings/show.pdf.html.haml',
header: {
html: {
template: "biddings/header_pdf.html.haml",
handlers: [:haml],
layout: "pdf_header",
formats: [:haml, :html]
}
},
footer: {
html: {
handlers: [:haml],
layout: "pdf",
formats: [:haml, :html],
encoding: 'UTF-8'
},
right: "#{I18n.t('pdf.page')} [page] #{I18n.t('pdf.of')} [topage]"
},
margin: { :top => 50, :bottom => 25},
handlers: [:haml],
layout: "pdf",
javascript_delay: 500,
encoding: 'UTF-8',
show_as_html: false,
disable_internal_links: true,
disable_external_links: true) and return
end
end
end
protected
def self.model
Bidding
end
private
def scoped_collection
policy_scope([:biddings, :pdf, Bidding]).includes(:bidding_type, :client, :payment_condition, :price_list, :real_payment_condition, :sales_man, :user)
end
def records_per_page
params[:per_page] || 10
end
end
Nothing fancy, there you can see all the config options, pretty standard. Needless to say, the footer with the page numbering IS working fine (screenshot too long to show, but trust me). Can't say the same about the header.
Here's the PDF header layout file:
pdf_header.html.haml:
!!! 5
%html
%head
%meta{:content => "text/html; charset=utf-8", "http-equiv" => "content-type"}/
= wicked_pdf_stylesheet_link_tag "bidding_pdf", media: :all
= csrf_meta_tags
%body.pdf
= yield
and here the contents for the header "contents" per se:
header_pdf.html.haml:
Test text
Just plain text. I have a Linux 16.04 x64 OS, wicked_pdf (1.1.0), wkhtmltopdf-binary (0.12.4). How can I debug this?
For anyone who bumps in this, since OP's answer is not too precise, what did the job for me was including a DOCTYPE HTML tag in your header/footer. Went from invisible header (with text that could be found using the search tool), to fully rendered.
For anyone else reaching here... it was a CSS issue. The header was there but "invisible" and no matter what margin I set on the render options it was a CSS issue. After starting the CSS from scratch, the header appeared! I could not debug it with the flag show_as_html: true because header and footer are not rendered in that mode, only the body.
If anyone reads this and happens to be in the same situation, use the search tool in the PDF document to find a word you know that's in the header. If it finds something but it's invisible, then you know you have a CSS problem.
Also don't forget to check if you included in the html of the header the <!DOCTYPE html>. Thanks #joaolell for this.
Another thing to check, is that you have the version with patched qt of the wkhtmltopdf library (0.12.4 and above) that supports header and footer. Previous versions won't
Upgrade wkhtmltopdf binary to at least version "0.12.4 (with patched qt)". I just spent half a day troubleshooting because my version of 0.12.1 did not support header and footer.
Ref: Wicked pdf not rendering header/footer
According to the GitHub Page for the axlsx gem I should use this syntax to render a xlsx view to a file and attach it:
xlsx = render_to_string handlers: [:axlsx], formats: [:xlsx], template: "users/export", locals: {users: users}
attachments["Users.xlsx"] = {mime_type: Mime::XLSX, content: xlsx}
Here is my mail method:
xlsx = render_to_string(handlers: [:axlsx], formats: [:xlsx], template: 'v1/reports/reportxyz', params: {start_date: '2016-09-12', period: 'weekly'})
attachments["report.xlsx"] = {content: xlsx, mime_type: Mime::XLSX}
mail(to: "my#email.address", subject: "Report", format: "text")
However I get this error when I try and call the mailer method:
ActionView::MissingTemplate: Missing template layouts/mailer with {:locale=>[:en], :formats=>[:xlsx], :variants=>[], :handlers=>[:axlsx]}. Searched in:
* "path/to/project/app/views"
Why is the render_to_string method affecting what the mailer view the mailer is trying to render? locgially I don't have a mailer.xlsx.axlsx file in my app/views/layouts folder but rather the mailer.text.erb I am trying to use as with other emails.
EDIT
I changed the render line to xlsx = render_to_string(template: 'v1/reports/azamara_social', params: {start_date: '2016-09-12', period: 'weekly'})
And now it seems to try and render the xlsx view but of course gets nil:NilClass errors when the xlsx view tries to reference instance variables defined in the reports controller.
Have you tried passing layout: false? What versions of axlsx, axlsx_rails, rails, and rubyzip are you using?
In the end it all came down to moving the controller code into a lib file. This way I call it in the controller to get the data if it needs to be rendered via web-requests as well as via the Mailer method where I recreate the #variables the view template is looking for.
Here is the finished salient parts of the report mailer method:
data = ReportUtils.get_data(args)
xlsx = render_to_string(template: 'path/to/report.xlsx', locals: {:#period => period, :#date_ranges => data[:date_ranges], :#data => data[:data]})
attachments["report.xlsx"] = {content: xlsx, mime_type: Mime::XLSX}
I'm trying to render inline images in a standard ActionMailer view using the approach outlined here Rails attachments inline are not shown correctly in gmail.
My code in my mailer:
attachments.inline["sample.png"] = {
mime_type: "image/png",
encoding: "base64", # Including this line causes byte sequence error
data: File.read('public/sample.png')
}
In mail view:
image_tag(attachments["sample.png"].url)
Gives the familiar ruby UTF-8 byte sequence error:
invalid byte sequence in UTF-8
To get around this I tried the following:
attachments.inline["logo.png"] = {
mime_type: "image/png",
data: Base64.encode64(File.read('public/logo.png'))
}
and also
attachments.inline["logo.png"] = File.read('public/logo.png')
Using the same image_tag syntax shown above.
Both of these resolve the UTF error, but I'm left with this nonsensical URL in the view:
<img src="cid:5707a64ededbc_7bd83ffd648601e029875#localhostname.mail">
The PNG image is valid and renders properly in a standard HTML view. I'm using Rails 4.2.5 with Ruby 2.2.4
EDIT
This works:
Mailer:
attachments.inline["cape.png"] = {
mime_type: "image/png",
# encoding: "base64",
content: Base64.encode64(File.read(Rails.root.join("public/", "cape.png")))
}
View:
= image_tag "data:image/png;base64,#{attachments['logo.png'].read}"
Very awkward, however, and I'm still wondering why the conventional approach doesn't work.
In my application I use only
attachments.inline["logo.png"] = File.read('public/logo.png')
It works fine for me