Como criar um formulário de contato para páginas do GitHub atendidas pelo site Jekyll

Recentemente, montei um site Jekyll hospedado nas páginas do GitHub aqui: tgarcia.com.br

Como você provavelmente sabe, as páginas do GitHub servem sites apenas estáticos (que irei me referir como front-end), o que significa que não há back-end disponível para criar coisas como formulário de contato.

Em primeiro lugar, estava apenas a fornecer o meu endereço de e-mail no site, que acabou por ser BAD IDEA. Pensando melhor decidi que precisava de um formulário de contato com um desafio de Captcha – e para isso preciso de um back-end para enviar este e-mail.

Não demorou muito para eu encontrar o post Criar um formulário de contato para Jekyll , que é incrível, porém descreve uma solução para sites que atendem tanto front-end quanto back-end no mesmo nó.

O que difere do meu caso, uma vez que tenho o front-end sendo servido nas páginas do GitHub, e o back-end deve ser servido em outro lugar (no meu caso, eu uso o Heroku ).

Bom, depois de um final de semana de trabalho consegui montar uma solução para contemplar meu cenário e aqui eu o descrevo. Tive que adicionar tratamento de requisições CORS e melhorar o fluxo de trabalho fazendo toda a validação na mesma requisição, sem usar sessões para torná-la mais segura e rápida.

  • Inscreva-se para uma conta gratuita do Heroku se ainda não tiver uma.
  • Crie um aplicativo no Heroku e escolha um nome significativo para você (o meu é tgarcia-contact-form ), caso contrário, o Heroku gerará um nome aleatório.
  • Inscreva-se em um plano Sendgrid Starter em seu aplicativo.
  • Inscreva-se em uma conta reCAPTCHA e registre seu back-end do Heroku (o meu é tgarcia-contact-form.herokuapp.com ). IMPORTANTE: selecione a opção para gerá-la como uma CHAVE GLOBAL, caso contrário, o front-end de suas páginas do GitHub não funcionará com o reCAPTCHA.
  • Crie um repositório git local para um aplicativo baseado em Rack e adicione os seguintes arquivos:

Gemfile

source 'https://rubygems.org'
ruby
'2.0.0'
gem
'rack'
gem
'rack-recaptcha'
gem
'sinatra'
gem
'pony'
gem
'json'

config.ru

require 'rubygems'
require 'sinatra'
require 'json'
require 'rack/recaptcha'
require 'pony'

use Rack::Recaptcha, :public_key => 'YOUR_PUBLIC_KEY_FROM_RECAPTCHA', :private_key => YOUR_PRIVATE_KEY_FROM_RECAPTCHA'
helpers Rack::Recaptcha::Helpers


require '
./application'
run Sinatra::Application

Insira as chaves pública e privada do seu reCAPTCHA onde especificado.

application.rb

before do
content_type
:json
headers
'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Methods' => ['POST']
end

set :protection, false
set :public_dir, Proc.new { File.join(root, "_site") }

post
'/send_email' do
if recaptcha_valid?
res
= Pony.mail(
:from => params[:name] + "<" + params[:email] + ">",
:to => 'YOUR_EMAIL_ADDRESS',
:subject => "[YOUR FILTER] " + params[:subject],
:body => params[:message],
:via => :smtp,
:via_options => {
:address => 'smtp.sendgrid.net',
:port => '587',
:enable_starttls_auto => true,
:user_name => ENV['SENDGRID_USERNAME'],
:password => ENV['SENDGRID_PASSWORD'],
:authentication => :plain,
:domain => 'heroku.com'
})
content_type
:json
if res
{ :message => 'success' }.to_json
else
{ :message => 'failure_email' }.to_json
end
else
{ :message => 'failure_captcha' }.to_json
end
end

not_found
do
File.read('_site/404.html')
end

get '/*' do
file_name
= "_site#{request.path_info}/index.html".gsub(%r{/+},'/')
if File.exists?(file_name)
File.read(file_name)
else
raise Sinatra::NotFound
end
end

Insira seu endereço de e-mail e um filtro para o assunto do e-mail.

  • Agora você precisa adicionar algum código do lado do cliente que irá para o seu site Jekyll nas páginas do GitHub:
function showRecaptcha(element) {
Recaptcha.create('YOUR_PUBLIC_KEY_FROM_RECAPTCHA', element, {
theme
: 'custom', // you can pick another at https://developers.google.com/recaptcha/docs/customization
custom_theme_widget
: 'recaptcha_widget'
});
}

function setupRecaptcha() {
var contactFormHost = 'YOUR_BACKEND_ADDRESS_FROM_HEROKU',
form
= $('#contact-form'),
notice
= form.find('#notice');

if (form.length) {
showRecaptcha
('recaptcha_widget');

form
.submit(function(ev){
ev
.preventDefault();

$
.ajax({
type
: 'POST',
url
: contactFormHost + 'send_email',
data
: form.serialize(),
dataType
: 'json',
success
: function(response) {
switch (response.message) {
case 'success':
form
.fadeOut(function() {
form
.html('<h4>' + form.data('success') + '</h4>').fadeIn();
});
break;

case 'failure_captcha':
showRecaptcha
('recaptcha_widget');
notice
.text(notice.data('captcha-failed')).fadeIn();
break;

case 'failure_email':
notice
.text(notice.data('error')).fadeIn();
}
},
error
: function(xhr, ajaxOptions, thrownError) {
notice
.text(notice.data('error')).fadeIn();
}
});
});
}
}

E aqui está o meu formulário HTML:

<form id="contact-form" class="contact-form" method="post" data-success="Message successfully sent!">

<label for="name">Name</label>
<input id="name" type="text" name="name" class="field" required autofocus /><br/>

<label for="email">E-mail</label>
<input id="email" type="email" name="email" class="field" required /><br/>

<label for="subject">Subject</label>
<input id="subject" type="text" name="subject" class="field" required /><br/>

<label for="message">Message</label>
<textarea id="message" name="message" required ></textarea><br/>

<label for="recaptcha_response_field">Captcha</label>
<div id="recaptcha_widget" class="recaptcha">
<div class="image">
<div id="recaptcha_image"></div>
</div>

<div class="headline recaptcha_only_if_image">Enter the words above:</div>
<div class="headline recaptcha_only_if_audio">Enter the numbers you hear:</div>

<input type="text" id="recaptcha_response_field" name="recaptcha_response_field" required />

<span class="recaptcha_icon"><a href="javascript:Recaptcha.reload()"><i class="fa fa-refresh"></i></a></span>
<span class="recaptcha_icon recaptcha_only_if_image"><a href="javascript:Recaptcha.switch_type('audio')"><i class="fa fa-volume-up"></i></a></span>
<span class="recaptcha_icon recaptcha_only_if_audio"><a href="javascript:Recaptcha.switch_type('image')"><i class="fa fa-font"></i></a></span>
<span class="recaptcha_icon"><a href="javascript:Recaptcha.showhelp()"><i class="fa fa-question-circle"></i></a></span>
</div><br/>
<div id="notice" class="notice" data-captcha-failed="Incorrect captcha!" data-error="There was an error sending the message, please try again."></div>
<button type="submit">Send</button>
</form>

<script type="text/javascript" src="http://www.google.com/recaptcha/api/js/recaptcha_ajax.js"></script>

BTW, estou usando o Font Awesome aqui.

E se você quiser ver meus estilos (usei MENOS aqui)

.contact-form {
h4
{
color
: #668944;
margin
-left: 20px;
}
label
{
width
: 100px;
margin
: 5px 0;
display
: inline-block;
vertical
-align: top;
}
input
, textarea, .recaptcha, button {
margin
: 5px 0;
padding
: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border
-radius: 5px;
border
: 1px solid #bbb;

&:focus {
outline
-color: @accent-color;
}
}
input
.field, textarea {
min
-width: 330px;
}
input
[name=subject], textarea {
width
: 73%;
}
textarea
{
height
: 150px;
}
button
{
border
: 0;
background
-color: @accent-color;
padding
: 8px 20px;
color
: #fff;
margin
-top: 20px;

&:hover {
background
-color: darken(@accent-color, 10%);
color
: darken(#fff, 10%);
}
}
.notice {
display
: none;
background
-color: #f2dede;
border
: 1px solid #ebccd1;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border
-radius: 5px;
padding
: 10px 15px;
color
: #a94442;
margin
-top: 15px;
}
.recaptcha {
padding
: 0;
display
: inline-block;

div
.image {
-webkit-border-radius: 5px 5px 0 0;
-moz-border-radius: 5px 5px 0 0;
border
-radius: 5px 5px 0 0;
padding
: 10px;
background
-color: #fff;
width
: 320px;

br
{
display
: none;
}
embed
, span {
float: left;
clear
: both;
a
{
cursor
: pointer;
font
-size: 0.9em;
}
}
}

div
.headline {
padding
: 5px 5px 0 10px;
}

input
[type=text] {
margin
: 10px;
}
}
}

Aqui, @ accent-color é minha cor de link padrão.

Espero que te sirva bem!