Finished mailer

This commit is contained in:
R. Eric Wheeler 2016-07-13 14:29:34 -07:00
parent 70c0a92595
commit da55c23343
15 changed files with 242 additions and 91 deletions

View File

@ -74,9 +74,9 @@ module.exports = function (grunt) {
mangle: true, mangle: true,
report: 'gzip', report: 'gzip',
compress: { compress: {
drop_console: true drop_console: false
}, },
banner: '/*! \n * Resume.PHP - v<%= pkg.version %> @license MIT (http://resume.reric.me)\n' + banner: '/*! \n * Resume.PHP - v<%= pkg.version %> MIT (http://resume.reric.me)\n' +
' * <%= grunt.template.today("dddd, mmmm dS, yyyy, h:MM:ss tt") %> \n */\n', ' * <%= grunt.template.today("dddd, mmmm dS, yyyy, h:MM:ss tt") %> \n */\n',
footer: '\n/*! Resume.PHP end */', footer: '\n/*! Resume.PHP end */',
nameCache: '.tmp/grunt-uglify-cache.json', nameCache: '.tmp/grunt-uglify-cache.json',

View File

@ -9,8 +9,4 @@ app:
captcha: true captcha: true
captcha_sitekey: 6LcvmSQTAAAAAMmf9w6mhCbpdLvknuD9SGVHT0q- captcha_sitekey: 6LcvmSQTAAAAAMmf9w6mhCbpdLvknuD9SGVHT0q-
captcha_secret: 6LcvmSQTAAAAAITkvYJjgLar1LqGGLz-ic0ZMiXo captcha_secret: 6LcvmSQTAAAAAITkvYJjgLar1LqGGLz-ic0ZMiXo
twig_theme: default theme: default
twig_paths:
- views
- default
twig_template: uikit.html.twig

View File

@ -8,8 +8,8 @@ app:
captcha: true captcha: true
captcha_sitekey: 6LcvmSQTAAAAAMmf9w6mhCbpdLvknuD9SGVHT0q- captcha_sitekey: 6LcvmSQTAAAAAMmf9w6mhCbpdLvknuD9SGVHT0q-
captcha_secret: 6LcvmSQTAAAAAITkvYJjgLar1LqGGLz-ic0ZMiXo captcha_secret: 6LcvmSQTAAAAAITkvYJjgLar1LqGGLz-ic0ZMiXo
twig_theme: default theme: default
twig_paths: #twig_paths:
- views # - views
twig_template: uikit.html.twig #twig_template: uikit.html.twig

View File

@ -1,14 +0,0 @@
<?php
/*
* This file is part of Resume.PHP.
*
* (copyleft) R. Eric Wheeler <sikofitt@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
return [
'app' => 'neat',
];

View File

@ -38,11 +38,10 @@ $app->register(new ConfigServiceProvider(), [
$app->setDebug(); $app->setDebug();
$app $app
->register(new TwigServiceProvider(), [ ->register(new TwigServiceProvider(), [
'twig.path' => [ 'twig.path' => $app->getAppDirectory() . '/themes/' . $app->config('app.theme'),
$app->getRootDirectory() . '/app/views',
],
]) ])
->register(new JsonServiceProvider()) ->register(new JsonServiceProvider())
->register(new AssetServiceProvider()) ->register(new AssetServiceProvider())
@ -59,7 +58,22 @@ $app
'monolog.name' => 'Resume.PHP', 'monolog.name' => 'Resume.PHP',
'monolog.level' => $app->getDebug() ? Logger::DEBUG : Logger::INFO, 'monolog.level' => $app->getDebug() ? Logger::DEBUG : Logger::INFO,
]) ])
->register(new RoutingServiceProvider()) ->register(new \Silex\Provider\SwiftmailerServiceProvider());
if(false === getenv('SPARKPOST_API_KEY')) {
$app['swiftmailer.transport'] = new Swift_SendmailTransport();
} else {
$app['swiftmailer.options'] = [
'host' => getenv('SPARKPOST_SMTP_HOST'),
'port' => getenv('SPARKPOST_SMTP_PORT'),
'username' => getenv('SPARKPOST_SMTP_USERNAME'),
'password' => getenv('SPARKPOST_SMTP_PASSWORD'),
'encryption' => 'tls',
'auth_mode' => 'plain',
];
}
$app->register(new RoutingServiceProvider())
->register(new ServiceControllerServiceProvider()) ->register(new ServiceControllerServiceProvider())
->register(new HttpFragmentServiceProvider()); ->register(new HttpFragmentServiceProvider());

View File

@ -1,4 +1,4 @@
{% extends app.config.twig.template %} {% extends 'base.html.twig' %}
{% block title %} {% block title %}
{{ app.config.app.title | default('Resume') }} {{ app.config.app.title | default('Resume') }}
@ -175,10 +175,10 @@
</div> </div>
</div> </div>
<div class="uk-form-row"> <div class="uk-form-row">
{{ form_label(contact_form.message) }} {{ form_label(contact_form.message|raw) }}
<div class="uk-form-controls"> <div class="uk-form-controls">
{{ form_widget(contact_form.message, {'attr':{'placeholder': "Please leave me a message. I will get back to you as soon as possible."}}) }} </div> {{ form_widget(contact_form.message) }}
</div>
</div> </div>
<div class="uk-form-row"> <div class="uk-form-row">
{{ form_label(contact_form.submit) }} {{ form_label(contact_form.submit) }}
@ -207,7 +207,7 @@
<h1>Verify</h1> <h1>Verify</h1>
<div> <div>
<p> Verify that you are a human please.</p> <p> Verify that you are a human please.</p>
<form class="uk-form" id="recaptcha" method="post" action="/api/v1/captcha" <form class="uk-form" id="recaptcha" method="post" action="{{ path('api_captcha') }}"
onsubmit="return false;"> onsubmit="return false;">
<div class="uk-form-row"> <div class="uk-form-row">
<div class="g-recaptcha" data-sitekey="6LcvmSQTAAAAAMmf9w6mhCbpdLvknuD9SGVHT0q-"></div> <div class="g-recaptcha" data-sitekey="6LcvmSQTAAAAAMmf9w6mhCbpdLvknuD9SGVHT0q-"></div>

2
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"hash": "58b6f8f80e5846af7b9ae23a628a0861", "hash": "e690fdd7168c515136615a9ee42053de",
"content-hash": "9d13427d982ec276ccfba5e7fdc2cd8a", "content-hash": "9d13427d982ec276ccfba5e7fdc2cd8a",
"packages": [ "packages": [
{ {

View File

@ -24,15 +24,23 @@
namespace Sikofitt\Controller; namespace Sikofitt\Controller;
use Prophecy\Doubler\ClassPatch\ReflectionClassNewInstancePatch;
use ReCaptcha\ReCaptcha; use ReCaptcha\ReCaptcha;
use Sikofitt\Form\Type\ContactType;
use Silex\Api\ControllerProviderInterface; use Silex\Api\ControllerProviderInterface;
use Silex\Application; use Silex\Application;
use Silex\Provider\SecurityServiceProvider;
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Validator\Constraints\Collection;
use Symfony\Component\Validator\Constraints\Email;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\EqualTo;
use Symfony\Component\Validator\ValidatorBuilder;
class ApiControllerProvider implements ControllerProviderInterface class ApiControllerProvider implements ControllerProviderInterface {
{
/** /**
* {@inheritdoc} * {@inheritdoc}
* *
@ -40,26 +48,130 @@ class ApiControllerProvider implements ControllerProviderInterface
* *
* @return mixed * @return mixed
*/ */
public function connect(Application $app) public function connect(Application $app) {
{
$controllers = $app['controllers_factory']; $controllers = $app['controllers_factory'];
$controllers->get('/v1/schema', function () use ($app) { $controllers->get('/v1/schema', function () use ($app) {
$response = new Response(file_get_contents($app->getDataDirectory() . '/schema/schema.v1.json'), Response::HTTP_OK); $response = new Response(file_get_contents($app->getDataDirectory() . '/schema/schema.v1.json'), Response::HTTP_OK);
$response->headers->set('Content-Type', 'application/schema+json'); $response->headers->set('Content-Type', 'application/schema+json');
return $response; return $response;
}); });
$controllers->match('/v1/message', function (Request $request) use ($app) { $controllers->match('/v1/message', function (Request $request) use ($app) {
/*$app->mail(\Swift_Message::newInstance()
->setSubject('[YourSite] Feedback') static $code = 255;
->setFrom(array('noreply@yoursite.com')) $returnData = [
->setTo(array('feedback@yoursite.com')) 'status' => 'error',
->setBody($request->get('message'))); */ 'message' => 'Unknown error.',
return new Response('This is the message.'); 'code' => $code,
})->method('GET|POST'); ];
$csrf = $request->getSession()->get('_csrf/contact') ?: false;
// Set some validation constraints
$constraints = [
'contact' => new Collection([
'name' => [
new Length([
'min' => 4,
'minMessage' => 'Name must be at least 4 characters.',
]
),
new NotBlank([
'message' => 'Name must not be blank.',
]
),
],
'email' => [
new Email([
'message' => 'Invalid email',
]),
new NotBlank([
'message' => 'Email must not be blank.',
]),
],
'message' => [
new Length([
'min' => 20,
'minMessage' => 'Message must be at least 20 characters.',
]),
new NotBlank([
'message' => 'Message must not be blank',
]),
],
'_token' => [
new EqualTo(['value' => $csrf, 'message' => 'Invalid token.']),
]
]
),
];
$contactFormData = $request->request->all();
$valid = $app['validator']->validate($contactFormData, new Collection($constraints));
if(count($valid) > 0) {
$sanitizeProperty = function() use ($valid) {
return str_replace(['][', '[', ']'], ['_','',''], $valid[0]->getPropertyPath());
};
return new JsonResponse([
'status' => 'error',
'message' => $valid[0]->getMessage(),
'id' => $sanitizeProperty(),
'const' => $valid[0]->getCode(),
'code' => 256
], 256);
} else {
$contactFormName = $contactFormData['contact']['name'];
$contactFormEmail = $contactFormData['contact']['email'];
$contactFormMessage = $contactFormData['contact']['message'];
$request->getSession()->remove('_csrf/contact');
try {
$app->mail(\Swift_Message::newInstance()
->setSubject('[Resume] Message')
->setFrom([$contactFormEmail => $contactFormName])
->setTo($app->config('app.email'))
->setBody($contactFormMessage)
);
$returnData = [
'status' => 'success',
'message' => 'Message successfully sent.',
'code' => 201,
'data' => $contactFormData,
];
} catch(\Exception $e) {
$returnData = [
'status' => 'error',
'message' => 'Could not send message.',
'code' => 256,
'data' => $e->getMessage(),
];
}
return new JsonResponse($returnData, 201);
}
})->method('GET|POST')->bind('api_message');
$controllers->get('/v1/emailTest', function(Request $request) use ($app) {
try {
$app->mail(\Swift_Message::newInstance()
->setSubject('[Resume] Message')
->setFrom(['eric@rewiv.com' => 'Eric'])
->setTo('eric@ericwheeler.net')
->setBody('Testing message.')
);
}
catch(\Exception $e) {
dump($e->getMessage());
}
return new Response('Hello');
});
$controllers->post('/v1/captcha', function (Request $request) use ($app) { $controllers->post('/v1/captcha', function (Request $request) use ($app) {
$captcha = new ReCaptcha('6LcvmSQTAAAAAITkvYJjgLar1LqGGLz-ic0ZMiXo'); $captcha = new ReCaptcha('6LcvmSQTAAAAAITkvYJjgLar1LqGGLz-ic0ZMiXo');
@ -75,12 +187,13 @@ class ApiControllerProvider implements ControllerProviderInterface
'phone' => null !== $app->config('app.phone') ? $app->config('app.phone') : 'No phone has been set in the configuration. Please let the owner know.', 'phone' => null !== $app->config('app.phone') ? $app->config('app.phone') : 'No phone has been set in the configuration. Please let the owner know.',
], ],
]; ];
} else { }
else {
$errorCodes = [ $errorCodes = [
'missing-input-secret' => 'The secret parameter is missing.', 'missing-input-secret' => 'The secret parameter is missing.',
'invalid-input-secret' => 'The secret parameter is invalid or malformed.', 'invalid-input-secret' => 'The secret parameter is invalid or malformed.',
'missing-input-response' => 'The response parameter is missing.', 'missing-input-response' => 'The response parameter is missing.',
'invalid-input-response' => 'The response parameter is invalid or malformed.' 'invalid-input-response' => 'The response parameter is invalid or malformed.',
]; ];
foreach ($valid->getErrorCodes() as $code) { foreach ($valid->getErrorCodes() as $code) {
@ -98,7 +211,7 @@ class ApiControllerProvider implements ControllerProviderInterface
} }
return new JsonResponse(json_encode($return)); return new JsonResponse(json_encode($return));
}); })->bind('api_captcha');
return $controllers; return $controllers;
} }

View File

@ -46,7 +46,7 @@ class ResumeControllerProvider implements ControllerProviderInterface
$controllers->get('/', function (Request $request) use ($app, $contactForm) { $controllers->get('/', function (Request $request) use ($app, $contactForm) {
return $app['twig']->render('resume.html.twig', [ return $app['twig']->render('index.html.twig', [
'fullData' => $this->resumeData, 'fullData' => $this->resumeData,
'basics' => $this->getResumeBasics(), 'basics' => $this->getResumeBasics(),

View File

@ -32,14 +32,15 @@ use Symfony\Component\Validator\Constraints\NotBlank;
class ContactType extends AbstractType class ContactType extends AbstractType
{ {
/** /**
* @param FormBuilderInterface $builder * @param FormBuilderInterface $builder
* @param array $options * @param array $options
*/ */
public function buildForm(FormBuilderInterface $builder, array $options) public function buildForm(FormBuilderInterface $builder, array $options)
{ {
$builder $builder
->setAction('/api/v1/message')
->add('name', TextType::class, [ ->add('name', TextType::class, [
'constraints' => [ 'constraints' => [
new Length([ new Length([
@ -53,7 +54,7 @@ class ContactType extends AbstractType
], ],
'label' => 'Name', 'label' => 'Name',
'label_attr' => [ 'label_attr' => [
'class' => 'uk-form-label', 'class' => 'uk-hidden-small uk-form-label',
], ],
]) ])
->add('email', EmailType::class, [ ->add('email', EmailType::class, [
@ -67,7 +68,7 @@ class ContactType extends AbstractType
], ],
'label' => 'Email', 'label' => 'Email',
'label_attr' => [ 'label_attr' => [
'class' => 'uk-form-label', 'class' => 'uk-hidden-small uk-form-label',
], ],
]) ])
->add('message', TextareaType::class, [ ->add('message', TextareaType::class, [
@ -75,7 +76,7 @@ class ContactType extends AbstractType
'rows' => 5, 'rows' => 5,
'cols' => 40, 'cols' => 40,
'class' => 'uk-form-large uk-width-1-1', 'class' => 'uk-form-large uk-width-1-1',
'placeholder' => 'Please leave me a message, I will get back to you as soon as possible.', 'placeholder' => 'Please leave me a message. I will get back to you as soon as possible.',
], ],
'constraints' => [ 'constraints' => [
new NotBlank(), new NotBlank(),
@ -84,8 +85,9 @@ class ContactType extends AbstractType
]), ]),
], ],
'label_attr' => [ 'label_attr' => [
'class' => 'uk-invisible uk-form-label', 'class' => 'uk-hidden-small uk-form-label',
], ],
'label' => 'If you prefer you can send me a message at eric@ericwheeler.net',
]) ])
->add('submit', SubmitType::class, [ ->add('submit', SubmitType::class, [
@ -109,6 +111,7 @@ class ContactType extends AbstractType
$resolver->setDefaults([ $resolver->setDefaults([
'attr' => [ 'attr' => [
'class' => 'uk-form uk-form-horizontal', 'class' => 'uk-form uk-form-horizontal',
'onsubmit' => 'return false;',
], ],
]); ]);
} }

View File

@ -32,5 +32,35 @@ jq(document).ready(function (jq) {
} }
}); });
}); // Phone form
jq('form[name=contact]').on('submit', function(event) {
jq.post(jq(this).attr('action'), jq(this).serialize(), function(response) {
if(response.status !== 'success') {
jq('#' + response.id).addClass('uk-form-danger');
UIkit.notify('<i class="uk-icon uk-icon-frown-o uk-icon-justify uk-margin-right"></i>' + response.message + ' ('+response.code+')</div>', {
pos: 'top-center',
status: 'danger'
});
console.log(response);
} else {
console.log(response);
UIkit.notify('<i class="uk-icon uk-icon-check uk-icon-justify uk-margin-right"></i> ' + response.message, {
pos: 'top-center',
status: 'success'
});
var $wrapper = jq('#contact-form'),
$button = jq('<button class="uk-modal-close uk-button uk-button-massive uk-button-primary uk-align-center" type="button">Close</button>'),
$thankYouText = jq('<p class="uk-text-lead uk-text-center">Thank you for your message.</p><p class="uk-text-center">I will get back to you as soon as possible.</p>');
$wrapper.empty().append($thankYouText).append($button);
jq('a[href="#contact-form-wrapper"]').replaceWith('eric@ericwheeler.net');
}
});
});
jq('form[name=contact] input, form[name=contact] textarea').on('focus', function() {
if(jq(this).hasClass('uk-form-danger')) {
jq(this).removeClass('uk-form-danger');
}
}); });
}); });

View File

@ -49,8 +49,17 @@ a.profile-link {
border-bottom: 1px solid #ccc; border-bottom: 1px solid #ccc;
padding-bottom: 12px; padding-bottom: 12px;
} }
.uk-modal-dialog-blank {
.uk-close { .uk-close {
&:after { &:after {
font-size: 30px; font-size: 30px;
} }
} }
}
.uk-button-massive {
min-height: 64px;
padding: 0 36px;
line-height: 40px;
font-size: 34px;
border-radius: 2px;
}