WIP: Saving Local Branch

This commit is contained in:
R. Eric Wheeler 2017-02-10 21:34:31 -08:00
parent c840954ad1
commit e46f6e0810
36 changed files with 2631 additions and 21 deletions

13
.gitignore vendored
View File

@ -3,4 +3,15 @@ vendor/
*~ *~
lib/mysql/ lib/mysql/
composer.lock composer.lock
.idea/ .idea/
.php_cs.cache
app/logs/
build/dist/
cache/
html/css/
html/images/
html/js/
lib/
t.php
test.php
variables.less

View File

@ -33,9 +33,9 @@ return PhpCsFixer\Config::create()
'single_import_per_statement' => false, 'single_import_per_statement' => false,
'phpdoc_order' => true, 'phpdoc_order' => true,
'array_syntax' => ['syntax' => 'short'], 'array_syntax' => ['syntax' => 'short'],
'short_echo_tag' => false,
'phpdoc_add_missing_param_annotation' => true, 'phpdoc_add_missing_param_annotation' => true,
'psr4' => true, 'psr4' => true,
'phpdoc_var_without_name' => false,
'no_extra_consecutive_blank_lines' => [ 'no_extra_consecutive_blank_lines' => [
'break', 'break',
'continue', 'continue',

142
Gruntfile.js Normal file
View File

@ -0,0 +1,142 @@
module.exports = function (grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
uglify: {
options: {
banner: '/*! <%= pkg.name %> javascript <%= grunt.template.today("yyyy-mm-dd") %> */\n',
mangle:false
},
build: {
src: 'build/dist/js/<%= pkg.name %>.js',
dest: 'build/dist/js/<%= pkg.name %>.min.js'
},
vendor: {
src: [
'./vendor/bower/jquery/dist/jquery.js',
'./vendor/bower/uikit/dist/js/uikit.js'
],
dest: 'build/dist/js/vendor.min.js'
}
},
less: {
production: {
options: {
compress: false,
syncImport: true,
plugins: [
new (require('less-plugin-autoprefix'))({browsers: ["last 2 versions"]})
],
paths: [
'vendor/bower/uikit/src/less',
'vendor/bower/uikit/src/less/theme',
'vendor/bower/uikit/src/less/components'
]
},
files: {
'build/dist/css/<%= pkg.name %>.css': [
'./build/less/doughnut.less',
]
}
}
},
cssmin: {
production: {
files: [{
expand: true,
cwd: 'build/dist/css',
src: ['*.css', '!*.min.css'],
dest: 'build/dist/css',
ext: '.min.css'
}]
}
},
jshint: {
dev: ['Gruntfile.js', 'build/js/doughnut.js'],
options: {
// options here to override JSHint defaults
reporter: require('jshint-stylish'),
globals: {
jQuery: true,
console: true,
module: true,
document: true
}
}
},
concat: {
dist: {
src: [
'build/js/doughnut.js'
],
dest: 'build/dist/js/<%= pkg.name %>.js'
}
},
copy: {
main: {
files: [
{
expand: true,
cwd: 'build/dist/js',
src: ['*.min.js'],
dest: 'html/js',
filter: 'isFile'
},
{
expand: true,
cwd: 'build/dist/css',
src: ['*.min.css'],
dest: 'html/css',
filter: 'isFile'
},
{
expand: true,
cwd: './vendor/bower/uikit/dist/images',
src: ['**'],
dest: 'html/images'
}
]
}
},
watch: {
configFiles: {
files: ['Gruntfile.js', 'config/*.js'],
options: {
reload: true
}
},
less: {
files: 'build/less/*.less',
tasks: ['less', 'cssmin', 'copy']
},
js: {
files: 'build/js/doughnut.js',
tasks: ['concat:dist', 'jshint', 'uglify:build', 'copy']
}
},
clean: [
'build/dist',
'html/js/*.js',
'html/css/*.css',
'html/images/*.svg'
]
});
// Load the plugin that provides the "uglify" task.
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-cssmin');
// Default task(s).
grunt.registerTask('build', ['clean', 'concat', 'jshint', 'uglify', 'less', 'cssmin']);
grunt.registerTask('install', ['copy']);
grunt.registerTask('default', ['build', 'copy']);
};

354
app/Kernel.php Normal file
View File

@ -0,0 +1,354 @@
<?php
/*
* doughnutwedding.com
* Copyright (C) 2017 http://doughnutwedding.com eric@doughnutwedding.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
use Bramus\Monolog\Formatter\{
ColorSchemes\TrafficLight,
ColoredLineFormatter
};
use Composer\Autoload\ClassLoader;
use Dflydev\Provider\DoctrineOrm\DoctrineOrmServiceProvider;
use Doctrine\Common\Annotations\AnnotationRegistry;
use Monolog\{
Handler\StreamHandler,
Logger
};
use Sikofitt\App\Traits\FlashTrait;
use Silex\Application;
use Silex\Application\{
FormTrait,
MonologTrait,
SecurityTrait,
SwiftmailerTrait,
TranslationTrait,
TwigTrait,
UrlGeneratorTrait
};
use Silex\Provider\{
AssetServiceProvider,
CsrfServiceProvider,
DoctrineServiceProvider,
ExceptionHandlerServiceProvider,
FormServiceProvider,
HttpFragmentServiceProvider,
HttpKernelServiceProvider,
MonologServiceProvider,
RoutingServiceProvider,
SecurityServiceProvider,
ServiceControllerServiceProvider,
SessionServiceProvider,
SwiftmailerServiceProvider,
TwigServiceProvider,
ValidatorServiceProvider,
VarDumperServiceProvider,
WebProfilerServiceProvider
};
use Symfony\Bridge\Twig\Extension\TranslationExtension;
use Symfony\Component\Debug\ErrorHandler;
use Symfony\Component\Debug\ExceptionHandler;
use Symfony\Component\Form\FormFactory;
use Symfony\Component\Translation\Translator;
/**
* Class Kernel.
*/
class Kernel extends Application
{
use FlashTrait;
use FormTrait;
use MonologTrait;
use SecurityTrait;
use SwiftmailerTrait;
use TranslationTrait;
use TwigTrait;
use UrlGeneratorTrait;
/**
* Kernel constructor.
*
* @param ClassLoader $loader
* @param bool $debug
* @param array $values
*/
public function __construct(ClassLoader $loader, bool $debug = false, array $values = [])
{
parent::__construct($values);
AnnotationRegistry::registerLoader([$loader, 'loadClass']);
if (true === $debug) {
$this->setDebug();
}
$this->setUpProviders();
$this->setUpDatabase();
$this->setUpView();
$this->setUpLogger();
$this->setUpMailer();
}
/**
* @param array $values
*
* @throws \InvalidArgumentException
*
* @return int
*/
public function mail(array $values)
{
if (false === isset($values['from']) || false === is_array($values['from'])) {
throw new \InvalidArgumentException('Array key "from" should be an array.');
} elseif (false === isset($values['to']) || false === is_array($values['to'])) {
throw new \InvalidArgumentException('Array key "to" should be an array.');
} elseif (false === isset($values['body']) || false === is_string($values['body'])) {
throw new \InvalidArgumentException('Array key "body" should be a string.');
} elseif (false === isset($values['subject']) || false === is_string($values['subject'])) {
throw new \InvalidArgumentException('Array key "subject" should be a string.');
}
$message = \Swift_Message::newInstance();
$message
->setSubject($values['subject'])
->setFrom($values['from'])
->setTo($values['to'])
->setBody($values['body']);
/**
* @var \Swift_Transport $mailer
*/
$mailer = $this['mailer'];
return $mailer->send($message);
}
/**
* Sets the application to debug.
*/
public function setDebug()
{
$this['debug'] = true;
ErrorHandler::register();
ExceptionHandler::register();
}
/**
* @return bool
*/
public function getDebug(): bool
{
return $this['debug'];
}
/**
* @return string
*/
public function getBaseDir(): string
{
return __DIR__.'/..';
}
/**
* @return string
*/
public function getAppDir(): string
{
return $this->getBaseDir().'/app';
}
/**
* @return string
*/
public function getConfigDir(): string
{
return $this->getAppDir().'/config';
}
/**
* @return string
*/
public function getLogDir(): string
{
return $this->getAppDir().'/logs';
}
/**
* @return string
*/
public function getCacheDir(): string
{
return $this->getBaseDir().'/cache';
}
/**
* @return FormFactory
*/
public function getFormFactory()
{
return $this['form.factory'];
}
/**
* Sets up the database environment.
*/
protected function setUpDatabase()
{
$this->register(new DoctrineServiceProvider(), [
'db.options' => [
'driver' => 'pdo_mysql',
'dbname' => 'doughnut',
'host' => 'mysql',
'user' => 'doughnut',
'password' => 'doughnut',
],
]);
$this->register(new DoctrineOrmServiceProvider(), [
'orm.proxies_dir' => $this->getCacheDir().'/doctrine/proxies',
'orm.default_cache' => 'array',
'orm.em.options' => [
'connection' => 'default',
'mappings' => [
[
'type' => 'annotation',
'path' => $this->getBaseDir().'/src/Sikofitt/App/Entity',
'namespace' => 'Sikofitt\App\Entity',
'use_simple_annotation_reader' => false,
],
],
],
]);
}
/**
* Sets up the view for the application.
*/
protected function setUpView()
{
$this
->register(new HttpKernelServiceProvider())
->register(new RoutingServiceProvider())
->register(new AssetServiceProvider())
->register(new SessionServiceProvider())
->register(new HttpFragmentServiceProvider())
->register(new ServiceControllerServiceProvider())
->register(new ValidatorServiceProvider());
$this->register(new TwigServiceProvider(), [
'twig.path' => $this->getAppDir().'/views',
]);
if (true === $this['debug']) {
$this
->register(new VarDumperServiceProvider())
->register(new WebProfilerServiceProvider(),
[
'profiler.cache_dir' => $this->getCacheDir().'/profiler',
'profiler.mount_prefix' => '/_profiler',
])
->register(new ExceptionHandlerServiceProvider())
;
}
/*
* Closure supports \Twig_Environment and Silex\Application as a second
* parameter, but we never use Silex\Application so we leave it out.
*/
$r = new \Symfony\Component\HttpFoundation\RequestStack();
$this->extend('twig', function (\Twig_Environment $twig) {
$twig->addGlobal('session', $this['session']);
$twig->addExtension(new TranslationExtension(new Translator('en')));
return $twig;
});
}
/**
* Sets up the rest of the providers for the application.
*/
protected function setUpProviders()
{
$this
->register(new CsrfServiceProvider())
->register(new FormServiceProvider())
->register(new SecurityServiceProvider(), [
'security.firewalls' => [
'admin' => [
'pattern' => '^/admin',
'http' => true,
],
],
])
;
$this->extend('form.extensions', function ($extensions) {
return $extensions;
});
}
/**
* Sets up the logger for the application.
*/
protected function setUpLogger()
{
if (true === $this->getDebug()) {
$monologLevel = Logger::DEBUG;
} else {
$monologLevel = Logger::INFO;
}
$this->register(new MonologServiceProvider(), [
'monolog.logfile' => $this->getLogDir().'/'.$this->getEnvironment().'.log',
'monolog.level' => $monologLevel,
]);
$this->extend('monolog', function (Logger $monolog, Application $app) {
$streamHandler = new StreamHandler($app['monolog.logfile']);
$streamHandler->setFormatter(new ColoredLineFormatter(new TrafficLight()));
$monolog->pushHandler($streamHandler);
return $monolog;
});
}
/**
* Sets up the mailer for the application.
*/
protected function setUpMailer()
{
$this['swiftmailer.options'] = [
'host' => 'mx.bgemi.net',
'port' => '25',
'username' => null,
'password' => null,
'encryption' => null,
'auth_mode' => null,
];
$this->register(new SwiftmailerServiceProvider());
}
/**
* @return string
*/
protected function getEnvironment(): string
{
$appEnv = getenv('APP_ENV');
if (false === $appEnv) {
return 'development';
} else {
return $appEnv;
}
}
}

49
app/config/csp.json Normal file
View File

@ -0,0 +1,49 @@
{
"report-only": false,
"report-uri": "/csp_violation_reporting_endpoint",
"base-uri": [],
"default-src": {
"self": true
},
"child-src": {
"allow": [
"https://www.youtube.com",
"https://www.youtube-nocookie.com"
],
"self": false
},
"connect-src": {
"self": true
},
"font-src": {
"self": true,
"data": true
},
"form-action": {
"allow": [
"http://localhost.doughnutwedding.com"
],
"self": true
},
"frame-ancestors": [],
"img-src": {
"self": true,
"data": true
},
"media-src": [],
"object-src": [],
"plugin-types": [],
"script-src": {
"allow": [
"https://www.google-analytics.com"
],
"self": true,
"unsafe-inline": true,
"unsafe-eval": true
},
"style-src": {
"self": true,
"unsafe-inline":true
},
"upgrade-insecure-requests": false
}

20
app/config/database.yml Normal file
View File

@ -0,0 +1,20 @@
#connections:
# default:
# connection:
# driver: pdo_mysql
# dbname: doughnut
# password: doughnut
# user: doughnut
# host: mysql
# annotation_autoloaders:
# - 'class_exists'
# metadata_mapping:
# - type: 'Jgut\Slim\Doctrine\ManagerBuilder::METADATA_MAPPING_ANNOTATION'
# path: ['__DIR__ . /../src/Sikofitt/App/Entity']
dbs.options:
default:
driver: pdo_mysql
host: mysql
dbname: doughnut
user: doughnut
password: doughnut

20
app/views/base.html.twig Normal file
View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<meta name="viewport" id="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=10.0,initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="{{ asset('css/doughnutwedding.min.css') }}" />
<script src="{{ asset('js/vendor.min.js') }}" type="text/javascript"></script>
<script src="{{ asset('js/doughnutwedding.min.js') }}" type="text/javascript"></script>
</head>
<body>
{% block debug %}{% endblock %}
<div class="uk-container-small uk-container uk-margin-large-top uk-margin-large-bottom">
{% include 'flash_messages.html.twig' %}
{% block body %}{% endblock %}
</div>
</body>
</html>

View File

@ -0,0 +1,20 @@
<div class="uk-width-1-1">
{% for messageType, messages in app.session.flashbag.all %}
{% if messageType == 'error' %}
{% set messageType = 'danger' %}
{% set displayType = 'error' %}
{% elseif messageType == 'info' %}
{% set messageType = 'primary' %}
{% set displayType = 'info' %}
{% else %}
{% set displayType = messageType %}
{% endif %}
<div class="uk-alert-{{ messageType }} uk-margin-small" uk-alert>
<a class="uk-alert-close uk-text-{{ messageType }}" uk-close></a>
<h3 class="uk-text-{{ messageType }}">{{ displayType|capitalize }}</h3>
{% for message in messages %}
<div class="uk-width-1-1">{{ message }}</div>
{% endfor %}
</div>
{% endfor %}
</div>

View File

@ -0,0 +1,8 @@
{% extends 'base.html.twig' %}
{% block body %}
{{ 'Hello World!' }}
{% for key, request in app.request_stack %}
{{ dump(key) }}
{% endfor %}
{% endblock %}

View File

@ -0,0 +1,21 @@
{% extends 'base.html.twig' %}
{% block body %}
{{ form_start(form) }}
<fieldset class="uk-fieldset uk-margin-small-bottom">
<legend class="uk-legend">Reset Password</legend>
</fieldset>
<div class="uk-grid-small" uk-grid>
<div class="uk-form-controls uk-form-controls-text uk-width-2-3@m">
{{ form_row(form.email) }}
</div>
<div class="uk-form-controls uk-width-1-3@m">
{{ form_row(form.submit) }}
</div>
</div>
<div class="form-controls uk-form-blank">
{{ form_rest(form) }}
</div>
{{ form_end(form) }}
{% endblock %}

View File

@ -0,0 +1,5 @@
{% extends 'base.html.twig' %}
{% block body %}
{% dump(request.flash) %}
{% endblock %}

View File

@ -0,0 +1,70 @@
{% extends 'base.html.twig' %}
{% block body %}
{{ form_start(form) }}
<fieldset class="uk-fieldset uk-margin-large-bottom">
<legend class="uk-legend">RSVP!</legend>
<div class="uk-margin">
{{ form_label(form.firstname) }}
<div class="uk-form-controls uk-form-controls-text">
{{ form_errors(form.firstname) }}
{{ form_widget(form.firstname) }}
</div>
</div>
<div class="uk-margin">
{{ form_label(form.lastname) }}
<div class="uk-form-controls uk-form-controls-text">
{{ form_errors(form.lastname) }}
{{ form_widget(form.lastname) }}
</div>
</div>
<div class="uk-margin">
{{ form_label(form.email) }}
<div class="uk-form-controls uk-form-controls-text">
{{ form_errors(form.email) }}
{{ form_widget(form.email) }}
</div>
</div>
<div class="uk-margin">
{{ form_label(form.plainPassword) }}
<div class="uk-form-controls uk-form-controls-text">
{{ form_errors(form.plainPassword) }}
{{ form_widget(form.plainPassword) }}
</div>
</div>
<div class="uk-grid-auto" uk-grid>
<div class="uk-margin">
{{ form_label(form.rsvp) }}
<div class="uk-form-controls uk-form-controls-text">
{{ form_errors(form.rsvp) }}
{{ form_widget(form.rsvp) }}
</div>
</div>
<div class="uk-margin">
<div class="uk-h4">
({{ count }} spots available.)
</div>
</div>
</div>
<div class="uk-margin">
{{ form_label(form.familyside) }}
<div class="uk-form-controls">
{{ form_errors(form.familyside) }}
{{ form_widget(form.familyside) }}
</div>
</div>
<div class="uk-margin uk-align-left@m">
{{ form_errors(form.family) }}
{{ form_label(form.family) }}
{{ form_widget(form.family) }}
</div>
<div class="uk-margin uk-align-right@m">
<input type="submit" class="uk-button-primary uk-button uk-button" value="Save" />
</div>
</fieldset>
{{ form_rest(form) }}
{{ form_end(form) }}
{% endblock %}

View File

@ -0,0 +1,3 @@
jQuery.ready(function($) {
});

21
build/less/doughnut.less Normal file
View File

@ -0,0 +1,21 @@
@import "../../vendor/bower/uikit/src/less/uikit.less";
@import "../../vendor/bower/uikit/src/less/components/variables.less";
//@global-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
//@global-font-size: 16px;
@form-background: #ffffff;
.uk-input,
.uk-select,
.uk-textarea {
border:1px solid @global-muted-background;
background: #ffffff;
}
.uk-input:focus,
.uk-select:focus,
.uk-textarea:focus {
background: #f5fbfe;
border:1px solid #99baca;
color: #666;
}

21
cli-config.php Normal file
View File

@ -0,0 +1,21 @@
<?php
use Doctrine\DBAL\Tools\Console\ConsoleRunner;
use Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper;
use Symfony\Component\Console\Helper\HelperSet;
/**
* @var EntityManager $em
*/
$em = require 'doctrine.php';
$helperSet = new HelperSet([
'db' => new ConnectionHelper($em->getConnection()),
'em' => new EntityManagerHelper($em),
]);
return $helperSet;

View File

@ -3,32 +3,57 @@
"description": "doughnutwedding.com website", "description": "doughnutwedding.com website",
"type": "project", "type": "project",
"require": { "require": {
"hoa/router": "3.17.01.14", "php":">=7.0",
"twig/twig": "^2.1", "bramus/monolog-colored-line-formatter": "~2.0",
"twig/extensions": "^1.4", "container-interop/container-interop": "^1.1",
"dflydev/doctrine-orm-service-provider": "^2.0",
"doctrine/annotations": "^1.3",
"doctrine/collections": "^1.4",
"doctrine/dbal": "^2.5", "doctrine/dbal": "^2.5",
"doctrine/orm": "^2.5", "doctrine/orm": "^2.5",
"symfony/console": "^3.2", "egulias/email-validator": "^2.1",
"pimple/pimple": "^3.0", "google/recaptcha": "^1.1",
"symfony/http-foundation": "^3.2",
"symfony/config": "^3.2",
"symfony/yaml": "^3.2",
"doctrine/annotations": "^1.3",
"tedivm/stash": "^0.14.1",
"doctrine/collections": "^1.4",
"ircmaxell/security-lib": "^1.1",
"ircmaxell/random-lib": "^1.2", "ircmaxell/random-lib": "^1.2",
"sikofitt/retrorsum": "^1.0" "ircmaxell/security-lib": "^1.1",
"monolog/monolog": "^1.22",
"paragonie/cookie": "^3.1",
"paragonie/csp-builder": "^2.0",
"paragonie/sodium_compat": "^0.4.0",
"psr/http-message": "^1.0",
"psr/log": "^1.0",
"silex/silex": "^2.0",
"swiftmailer/swiftmailer": "^5.4",
"symfony/asset": "^3.2",
"symfony/config": "^3.2",
"symfony/console": "^3.2",
"symfony/form": "^3.2",
"symfony/http-foundation": "^3.2",
"symfony/monolog-bridge": "^3.2",
"symfony/security-csrf": "^3.2",
"symfony/security-guard": "^3.2",
"symfony/security-http": "^3.2",
"symfony/twig-bridge": "^3.2",
"symfony/validator": "^3.2",
"symfony/yaml": "^3.2",
"tedivm/stash": "^0.14.1",
"twig/extensions": "^1.4",
"twig/twig": "^2.1"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^6.0", "friendsofphp/php-cs-fixer": "^2.0",
"fzaninotto/faker": "^1.6", "fzaninotto/faker": "^1.6",
"friendsofphp/php-cs-fixer": "^2.0" "phpunit/phpunit": "^6.0",
"silex/providers": "^2.0",
"silex/web-profiler": "^2.0",
"symfony/debug-bundle": "^3.2",
"symfony/var-dumper": "^3.2"
}, },
"autoload": { "autoload": {
"psr-0": { "psr-4": {
"Sikofitt\\":"src/Sikofitt" "Sikofitt\\":"src/Sikofitt"
} },
"files": ["app/Kernel.php"]
}, },
"license": "GPL-3.0", "license": "GPL-3.0",
"authors": [ "authors": [
@ -37,5 +62,8 @@
"email": "sikofitt@gmail.com" "email": "sikofitt@gmail.com"
} }
], ],
"minimum-stability": "stable" "minimum-stability": "stable",
"config": {
"sort-packages": true
}
} }

View File

@ -13,12 +13,16 @@ services:
- php - php
- mysql - mysql
restart: always restart: always
environment:
- "APP_ENV=development"
php: php:
build: ./docker/php build: ./docker/php
volumes: volumes:
- ./:/var/www - ./:/var/www
- ./html:/var/www/html - ./html:/var/www/html
restart: always restart: always
environment:
- "APP_ENV=development"
mysql: mysql:
image: mysql:5.7 image: mysql:5.7
environment: environment:

View File

@ -16,6 +16,8 @@ RUN apk update && apk add --no-cache --virtual .build-deps $PHPIZE_DEPS && apk a
RUN /usr/local/bin/docker-php-ext-install pdo_mysql intl RUN /usr/local/bin/docker-php-ext-install pdo_mysql intl
RUN echo "y\n"|pecl install scrypt && /usr/local/bin/docker-php-ext-enable scrypt
RUN echo "y\n"|pecl install xdebug && /usr/local/bin/docker-php-ext-enable xdebug
RUN apk del .build-deps RUN apk del .build-deps
COPY ./php.ini /usr/local/etc/php COPY ./php.ini /usr/local/etc/php

30
doctrine.php Normal file
View File

@ -0,0 +1,30 @@
<?php
use Jgut\Slim\Doctrine\ManagerBuilder;
require __DIR__ . '/vendor/autoload.php';
$settings = [
'default' => [
'annotation_autoloaders' => ['class_exists'],
'connection' => [
'driver' => 'pdo_mysql',
'user' => 'doughnut',
'password' => 'doughnut',
'dbname' => 'doughnut',
'host' => 'mysql',
],
'metadata_mapping' => [
[
'type' => ManagerBuilder::METADATA_MAPPING_ANNOTATION,
'path' => [__DIR__ . '/src/Sikofitt/App/Entity'],
],
],
],
];
$managerBuilder = new ManagerBuilder([ManagerBuilder::RELATIONAL_MANAGER_KEY => 'default']);
$managerBuilder->loadSettings($settings);
return $managerBuilder->getManager('entityManager');

View File

@ -18,4 +18,38 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
phpinfo(); use Sikofitt\App\Controller\DefaultController;
use Sikofitt\App\Controller\RsvpController;
use Sikofitt\App\Middleware\CspMiddleware;
use Sikofitt\App\Middleware\HeaderMiddleware;
$loader = require __DIR__.'/../vendor/autoload.php';
$app = new Kernel($loader, true);
// Controllers
// Default
$app->get('/', DefaultController::class.'::indexAction')
->bind('index');
//$app->match('/login', DefaultController::class.'loginAction')
// ->bind('login');
// RSVP Actions
$app->match('/rsvp', RsvpController::class.'::indexAction')
->method('GET|POST')
->bind('rsvp');
$app->match('/rsvp/reset', RsvpController::class.'::resetAction')
->method('GET|POST')
->bind('rsvp_password_reset');
$app->get('/rsvp/reset/{token}', RsvpController::class.'::tokenAction')
->bind('rsvp_token');
//->before(new MysqlAuthenticatorMiddleware());
$app->match('/rsvp/edit', RsvpController::class.'::editAction')
->method('GET|POST')
->bind('rsvp_edit');
//->before(new MysqlAuthenticatorMiddleware());
// Middleware
$app->before(new CspMiddleware(), \Kernel::EARLY_EVENT);
$app->before(new HeaderMiddleware(), \Kernel::EARLY_EVENT);
// Run the app
$app->run();

View File

@ -17,9 +17,15 @@
"license": "GPL-3.0", "license": "GPL-3.0",
"devDependencies": { "devDependencies": {
"grunt": "^1.0.1", "grunt": "^1.0.1",
"grunt-contrib-clean": "^1.0.0",
"grunt-contrib-concat": "^1.0.1",
"grunt-contrib-copy": "^1.0.0",
"grunt-contrib-cssmin": "^1.0.2", "grunt-contrib-cssmin": "^1.0.2",
"grunt-contrib-jshint": "^1.1.0",
"grunt-contrib-less": "^1.4.0", "grunt-contrib-less": "^1.4.0",
"grunt-contrib-uglify": "^2.1.0",
"grunt-contrib-watch": "^1.0.0", "grunt-contrib-watch": "^1.0.0",
"jshint-stylish": "^2.2.1",
"less-plugin-autoprefix": "^1.5.1" "less-plugin-autoprefix": "^1.5.1"
} }
} }

View File

@ -0,0 +1,96 @@
<?php
/*
* doughnutwedding.com
* Copyright (C) 2017 http://doughnutwedding.com eric@doughnutwedding.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Sikofitt\App\Configuration;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
class DatabaseConfiguration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('doughnut');
$rootNode->children()
->arrayNode('connections')
->prototype('array')
->children()
->arrayNode('connection')
->children()
->scalarNode('driver')
->isRequired()
->validate()
->ifNotInArray(['pdo_mysql', 'pdo_pgsql', 'pdo_sqlite'])
->thenInvalid('Invalid driver : %s')
->end() // ifNotInArray
->end() // driver
->scalarNode('dbname')->isRequired()->end() // database
->scalarNode('host')->defaultValue('127.0.0.1')->end()
->scalarNode('user')->isRequired()->end()
->scalarNode('password')->isRequired()->end()
->end() // connection.prototype
->end() // connection
->arrayNode('annotation_autoloaders')
->requiresAtLeastOneElement()
->prototype('scalar')
->isRequired()
->end()
->end()
->arrayNode('metadata_mapping')
->prototype('array')
->children()
->arrayNode('path')
->requiresAtLeastOneElement()
->prototype('scalar')
->isRequired()
->end()
->end()
->scalarNode('type')
->isRequired()
->beforeNormalization()
->ifString()
->then(function ($s) {
return $this->normalizeConstant($s);
})
->end()
->end()
->end()
->end()
->end()
->end();
return $treeBuilder;
}
private function normalizeConstant($const)
{
$classParts = explode('::', $const);
if (isset($classParts[1])) {
$reflected = new \ReflectionClass($classParts[0]);
$constant = $reflected->getConstant($classParts[1]);
return $constant;
} else {
return $const;
}
}
}

View File

@ -0,0 +1,75 @@
<?php
/*
* doughnutwedding.com
* Copyright (C) 2017 http://doughnutwedding.com eric@doughnutwedding.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Sikofitt\App\Controller;
use Sikofitt\App\Form\RsvpType;
use Symfony\Component\Form\Extension\Csrf\CsrfExtension;
use Symfony\Component\Form\Extension\HttpFoundation\Type\FormTypeHttpFoundationExtension;
use Symfony\Component\Form\Forms;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Csrf\CsrfTokenManager;
class DefaultController
{
public function indexAction(Request $request, \Kernel $app)
{
return $app->render('index.html.twig', ['request' => $request]);
}
public function rsvpAction()
{
/* $app = $this->app;
$rsvp = new Rsvp();
$rsvp
->setGuests(2)
->setCreated(new \DateTime('now'))
->setUpdated(new \DateTime('now'));
$user = new User();
$user->setFirstName('Eric')
->setLastName('Wheeler')
->setFamily(true)
->setEmail('sikofitt@gmail.com')
->setCreated(new \DateTime('now'))
->setUpdated(new \DateTime('now'))
->setFamilySide(User::ERIC_SIDE)
->setRsvp($rsvp);
$app['em']->persist($user);
$app['em']->flush(); */
$bytes = \ParagonIE_Sodium_Compat::randombytes_buf(22);
$password = new Password(new ScryptPassword());
// dump($password->hash('password'));
$blake = \ParagonIE_Sodium_Compat::crypto_generichash($bytes);
$blake2b = \ParagonIE_Sodium_Core_BLAKE2b::bin2hex($blake);
$formFactory = Forms::createFormFactoryBuilder()
->addTypeExtension(new FormTypeHttpFoundationExtension())
->addExtension(new CsrfExtension(new CsrfTokenManager()))
->getFormFactory();
$form = $formFactory->create(RsvpType::class);
// dump($form->createView());
return 'hello';
//return $this->container->get('view')->render('RsvpForm.html.twig', ['form' => $form->createView()]);
}
}

View File

@ -0,0 +1,160 @@
<?php
/*
* doughnutwedding.com
* Copyright (C) 2017 http://doughnutwedding.com eric@doughnutwedding.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Sikofitt\App\Controller;
use Doctrine\ORM\EntityManager;
use Sikofitt\{
App\Entity\Rsvp, App\Entity\User, App\Form\ResetType, App\Form\RsvpType, App\Repository\RsvpRepository, App\Repository\UserRepository
};
use Symfony\Component\Form\FormFactory;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Validator\ConstraintViolationList;
class RsvpController
{
/**
* @param \Symfony\Component\HttpFoundation\Request $request
* @param \Kernel $app
*
* @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
*/
public function indexAction(Request $request, \Kernel $app)
{
/**
* @var EntityManager $em
* @var RsvpRepository $rsvpRepo
* @var UserRepository $userRepo
*/
$em = $app['orm.em'];
$rsvpRepo = $em->getRepository('Sikofitt\App\Entity\Rsvp');
$count = (40 - $rsvpRepo->getRsvpCount());
$userRepo = $em->getRepository('Sikofitt\App\Entity\User');
$kCount = $userRepo->getKatrinaCount();
$eCount = $userRepo->getEricCount();
/**
* @var FormFactory $formFactory
*/
$formFactory = $app['form.factory'];
$user = new User();
$rsvp = new Rsvp();
$rsvp
->setCreated(new \DateTime('now'))
->setUpdated(new \DateTime('now'));
$user
->setRsvp($rsvp)
->setCreated(new \DateTime('now'))
->setUpdated(new \DateTime('now'));
$form = $formFactory->create(RsvpType::class, $user);
if ($request->isMethod('POST')) {
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$user = $form->getData();
$user->setPassword($user->getPlainPassword());
/**
* @var EntityManager $em
*/
$em = $app['orm.em'];
$em->persist($user);
$em->flush();
return $app->redirect('/rsvp');
}
}
return $app->render(
'rsvp_form.html.twig',
[
'form' => $form->createView(),
'count' => $count,
'kCount' => $kCount,
'eCount' => $eCount,
]
);
}
/**
* @param \Symfony\Component\HttpFoundation\Request $request
* @param \Kernel $app
* @param string $token
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function resetAction(Request $request, \Kernel $app)
{
$formFactory = $app->getFormFactory();
$passwordResetForm = $formFactory
->createBuilder(ResetType::class)
->getForm();
$update = false;
if ($request->isMethod('POST')) {
$passwordResetForm->handleRequest($request);
if ($passwordResetForm->isSubmitted() && $passwordResetForm->isValid()) {
$data = $passwordResetForm->get('email')->getData();
$update = $app['orm.em']->getRepository('Sikofitt\App\Entity\User')
->setResetToken($data);
} else {
$data = null;
}
} else {
$data = null;
}
/**
* @var UserRepository $userRepo
*/
$userRepo = $app['orm.em']->getRepository('Sikofitt\App\Entity\User');
//$emailResult = $userRepo->getEmail($passwordResetForm->get('email'));
/*if(null === $emailResult) {
return $app->render('reset_password_confirm.html.twig', [
'message' => 'Email was not found in database',
]);
} elseif($emailResult instanceof ConstraintViolationList) {
return $app->render(
'reset_password.html.twig',
[
'form' => $passwordResetForm->createView(),
'data' => $data,
'email' => $emailResult,
'message' => $emailResult,
]
);
} else {
return $app->render('reset_password_confirm.html.twig', [
'message' => 'Please check your email. A reset request has been sent.',
]);
}*/
$app->addInfo('Message', 'message 2');
return $app->render(
'reset_password.html.twig',
[
'form' => $passwordResetForm->createView(),
'data' => $data,
//'email' => $emailResult,
]
);
}
public function tokenAction(Request $request, \Kernel $app, string $token = null)
{
}
}

View File

@ -0,0 +1,183 @@
<?php
/*
* doughnutwedding.com
* Copyright (C) 2017 http://doughnutwedding.com eric@doughnutwedding.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Sikofitt\App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Class Rsvp.
*
* @ORM\Table(name="rsvps")
* @ORM\Entity(repositoryClass="Sikofitt\App\Repository\RsvpRepository")
*/
class Rsvp
{
/**
* @var int $id
* @ORM\Id()
* @ORM\Column(name="id", type="integer", unique=true, nullable=false)
* @ORM\GeneratedValue(strategy="IDENTITY")
* @Assert\NotBlank()
* @Assert\Regex(pattern="'/\d+/'")
*/
private $id;
/**
* @var int
* @ORM\OneToOne(targetEntity="Sikofitt\App\Entity\User", mappedBy="rsvp", cascade={"persist"})
*/
private $user;
/**
* @var int
* @ORM\Column(name="guests", nullable=true, type="integer")
* @Assert\Regex(pattern="'/\d+/'")
* @Assert\Range(min="1", max="2")
*/
private $guests;
/**
* @var \DateTime
* @ORM\Column(name="created", type="datetime")
*/
private $created = null;
/**
* @var \DateTime
* @ORM\Column(name="updated", type="datetime")
*/
private $updated = null;
public function __construct()
{
if (null === $this->created) {
$this->created = new \DateTime('now');
}
$this->updated = new \DateTime('now');
}
/**
* Get id.
*
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* Set guests.
*
* @param int $guests
*
* @return Rsvp
*/
public function setGuests($guests)
{
$this->guests = $guests;
return $this;
}
/**
* Get guests.
*
* @return int
*/
public function getGuests()
{
return $this->guests;
}
/**
* Set created.
*
* @param \DateTime $created
*
* @return Rsvp
*/
public function setCreated($created)
{
$this->created = $created;
return $this;
}
/**
* Get created.
*
* @return \DateTime
*/
public function getCreated()
{
return $this->created;
}
/**
* Set updated.
*
* @param \DateTime $updated
*
* @return Rsvp
*/
public function setUpdated($updated)
{
$this->updated = $updated;
return $this;
}
/**
* Get updated.
*
* @return \DateTime
*/
public function getUpdated()
{
return $this->updated;
}
/**
* Set user.
*
* @param \Sikofitt\App\Entity\User $user
*
* @return Rsvp
*/
public function setUser(\Sikofitt\App\Entity\User $user = null)
{
$this->user = $user;
return $this;
}
/**
* Get user.
*
* @return \Sikofitt\App\Entity\User
*/
public function getUser()
{
return $this->user;
}
}

View File

@ -0,0 +1,390 @@
<?php
/*
* doughnutwedding.com
* Copyright (C) 2017 http://doughnutwedding.com eric@doughnutwedding.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Sikofitt\App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Class User.
*
* @ORM\Entity(repositoryClass="Sikofitt\App\Repository\UserRepository")
* @ORM\Table(name="users")
*/
class User
{
const KATRINA_SIDE = 'Katrina';
const ERIC_SIDE = 'Eric';
/**
* @ORM\Id()
* @ORM\Column(name="id", type="integer", nullable=false, unique=true)
* @ORM\GeneratedValue(strategy="IDENTITY")
*
* @var int
*/
private $id;
/**
* @ORM\Column(name="first_name", type="string", length=255, nullable=false)
* @Assert\NotBlank()
* @Assert\Regex(pattern="/\w+/")
*/
private $firstName;
/**
* @ORM\Column(name="last_name", type="string", length=255, nullable=false)
* @Assert\NotBlank()
* @Assert\Regex(pattern="/\w+/")
*/
private $lastName;
/**
* @ORM\Column(type="boolean", name="is_family", nullable=false)
* @Assert\Type(type="bool")
*/
private $family = false;
/**
* @ORM\Column(type="string", name="family_side", nullable=true)
* @Assert\Choice(choices="{self::KATRINA_SIDE, self::ERIC_SIDE}", multiple=false)
*
* @var null|string
*/
private $familySide = null;
/**
* @ORM\Column(name="email", type="string", length=255)
* @Assert\Email(strict=true, checkHost=true, checkMX=true)
*/
private $email;
/**
* @var string
* @ORM\Column(name="password", type="string", length=255))
*/
private $password;
/**
* @var string
*/
private $plainPassword;
/**
* @var string
* @ORM\Column(name="token", type="string", length=255, nullable=true)
*/
private $token;
/**
* @var int
* @ORM\OneToOne(targetEntity="Sikofitt\App\Entity\Rsvp", inversedBy="user", cascade={"persist"})
*/
private $rsvp;
/**
* @ORM\Column(type="datetime", name="created")
*/
private $created = null;
/**
* @ORM\Column(type="datetime", name="updated")
*/
private $updated = null;
public function __construct()
{
if (null === $this->created) {
$this->created = new \DateTime('now');
}
$this->updated = new \DateTime('now');
}
/**
* Get id.
*
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* Set firstName.
*
* @param string $firstName
*
* @return User
*/
public function setFirstName($firstName)
{
$this->firstName = $firstName;
return $this;
}
/**
* Get firstName.
*
* @return string
*/
public function getFirstName()
{
return $this->firstName;
}
/**
* Set lastName.
*
* @param string $lastName
*
* @return User
*/
public function setLastName($lastName)
{
$this->lastName = $lastName;
return $this;
}
/**
* Get lastName.
*
* @return string
*/
public function getLastName()
{
return $this->lastName;
}
/**
* Set family.
*
* @param bool $family
*
* @return User
*/
public function setFamily($family)
{
$this->family = $family;
return $this;
}
/**
* Get family.
*
* @return bool
*/
public function getFamily()
{
return $this->family;
}
/**
* Set familySide.
*
* @param string $familySide
*
* @return User
*/
public function setFamilySide($familySide)
{
$this->familySide = $familySide;
return $this;
}
/**
* Get familySide.
*
* @return string
*/
public function getFamilySide()
{
return $this->familySide;
}
/**
* Set email.
*
* @param string $email
*
* @return User
*/
public function setEmail($email)
{
$this->email = $email;
return $this;
}
/**
* Get email.
*
* @return string
*/
public function getEmail()
{
return $this->email;
}
/**
* @param string $password
*
* @return $this
*/
public function setPassword($password)
{
$encoder = new BCryptPasswordEncoder(14);
$salt = bin2hex(random_bytes(16));
$this->password = $encoder->encodePassword($password, $salt);
return $this;
}
/**
* @return string
*/
public function getPassword()
{
return $this->password;
}
/**
* @return string
*/
public function getPlainPassword(): string
{
if (null === $this->plainPassword) {
return '';
} else {
return $this->plainPassword;
}
}
/**
* @param string $plainPassword
*
* @return User
*/
public function setPlainPassword(string $plainPassword): User
{
$this->plainPassword = $plainPassword;
return $this;
}
/**
* @param string $token
*
* @return $this
*/
public function setToken($token)
{
$this->token = $token;
return $this;
}
/**
* @return string
*/
public function getToken()
{
return $this->token;
}
/**
* Set created.
*
* @param \DateTime $created
*
* @return User
*/
public function setCreated($created)
{
$this->created = $created;
return $this;
}
/**
* Get created.
*
* @return \DateTime
*/
public function getCreated()
{
return $this->created;
}
/**
* Set updated.
*
* @param \DateTime $updated
*
* @return User
*/
public function setUpdated($updated)
{
$this->updated = $updated;
return $this;
}
/**
* Get updated.
*
* @return \DateTime
*/
public function getUpdated()
{
return $this->updated;
}
/**
* Set rsvp.
*
* @param \Sikofitt\App\Entity\Rsvp $rsvp
*
* @return User
*/
public function setRsvp(\Sikofitt\App\Entity\Rsvp $rsvp = null)
{
$this->rsvp = $rsvp;
return $this;
}
/**
* Get rsvp.
*
* @return \Sikofitt\App\Entity\Rsvp
*/
public function getRsvp()
{
return $this->rsvp;
}
}

View File

@ -0,0 +1,78 @@
<?php
/*
* doughnutwedding.com
* Copyright (C) 2017 http://doughnutwedding.com eric@doughnutwedding.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Sikofitt\App\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\Email;
use Symfony\Component\Validator\Constraints\NotBlank;
/**
* Class ResetType.
*/
class ResetType extends AbstractType
{
/**
* @param \Symfony\Component\Form\FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('email', EmailType::class, [
'attr' => [
'class' => 'uk-input uk-form-large uk-padding-small uk-box-shadow-hover-small',
'placeholder' => 'Email address',
],
'label' => 'Email address',
'label_attr' => [
'class' => 'uk-form-label uk-text-primary uk-hidden',
],
'constraints' => [
new NotBlank(),
new Email([
'strict' => true,
'checkMX' => true,
'checkHost' => true,
]),
],
])
->add('submit', SubmitType::class, [
'attr' => [
'class' => 'uk-button uk-button-large uk-button-primary uk-width-1-1@s',
],
])
;
}
/**
* @param \Symfony\Component\OptionsResolver\OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefault('attr', [
'class' => 'uk-form uk-margin-large',
]);
}
}

View File

@ -0,0 +1,141 @@
<?php
/*
* doughnutwedding.com
* Copyright (C) 2017 http://doughnutwedding.com eric@doughnutwedding.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Sikofitt\App\Form;
use Sikofitt\App\Entity\Rsvp;
use Sikofitt\App\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\Extension\Core\Type\{
CheckboxType,
ChoiceType,
EmailType,
IntegerType,
PasswordType,
RadioType,
TextType
};
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class RsvpType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('firstname', TextType::class, [
'attr' => [
'class' => 'uk-input uk-form-large uk-padding-small uk-box-shadow-hover-small',
'placeholder' => 'First Name',
],
'label' => 'First name',
'label_attr' => [
'class' => 'uk-form-label uk-text-primary',
],
])
->add('lastname', TextType::class, [
'attr' => [
'class' => 'uk-input uk-form-large uk-padding-small uk-box-shadow-hover-small',
'placeholder' => 'Last Name',
],
'label' => 'Last name',
'label_attr' => [
'class' => 'uk-form-label uk-text-primary',
],
])
->add('email', EmailType::class, [
'attr' => [
'class' => 'uk-input uk-form-large uk-padding-small uk-box-shadow-hover-small',
'placeholder' => 'Email address',
],
'label' => 'Email address',
'label_attr' => [
'class' => 'uk-form-label uk-text-primary',
],
])
->add('plainPassword', PasswordType::class, [
'attr' => [
'class' => 'uk-input uk-form-large uk-padding-small uk-box-shadow-hover-small',
'placeholder' => 'Password',
],
'label' => 'Password',
'label_attr' => [
'class' => 'uk-form-label uk-text-primary',
],
])
->add('rsvp', IntegerType::class, [
'attr' => [
'class' => 'uk-input uk-form-large uk-padding-small uk-form-width-medium uk-box-shadow-hover-small',
'placeholder' => 'Number of Guests (including yourself)',
],
'label' => 'Number of guests? (including yourself)',
'label_attr' => [
'class' => 'uk-form-label uk-text-primary',
],
])
->add('familyside', ChoiceType::class, [
'choices' => [
User::ERIC_SIDE => User::ERIC_SIDE,
User::KATRINA_SIDE => User::KATRINA_SIDE,
],
'attr' => [
'class' => 'uk-select uk-form-large uk-box-shadow-hover-small',
'style' => 'padding-left:16px;',
],
'label' => 'Who are you coming for?',
'label_attr' => [
'class' => 'uk-form-label uk-text-primary',
],
])
->add('family', CheckboxType::class, [
'label' => 'Are you an immediate family member?',
'required' => false,
'attr' => [
'class' => 'uk-checkbox uk-box-shadow-hover-small',
],
'label_attr' => [
'class' => 'uk-margin-right',
],
]);
$builder->get('rsvp')
->addModelTransformer(new CallbackTransformer(
function ($rsvp) {
if (null === $rsvp) {
return $rsvp;
} else {
return $rsvp->getGuests();
}
},
function (Int $rsvpInt) {
$rsvp = new Rsvp();
$rsvp->setGuests($rsvpInt);
return $rsvp;
}
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefault('data_class', User::class);
$resolver->setDefault('attr', ['class' => 'uk-form-horizontal uk-margin-large']);
}
}

View File

@ -0,0 +1,56 @@
<?php
/*
* doughnutwedding.com
* Copyright (C) 2017 http://doughnutwedding.com eric@doughnutwedding.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Sikofitt\App\Middleware;
use Monolog\Logger;
use ParagonIE\CSPBuilder\CSPBuilder;
use Symfony\Component\HttpFoundation\Request;
/**
* Class CspMiddleware.
*
* Builds Content Security Policy (CSP) headers.
*/
class CspMiddleware
{
public function __invoke(Request $request, \Kernel $app)
{
$cspDir = realpath($app->getConfigDir());
if (false === file_exists($cspDir.'/csp.json')) {
$app->log(
sprintf('csp.json was not found in %s, skipping.', $cspDir),
[
'configured log dir' => realpath($cspDir),
],
Logger::NOTICE
);
return;
}
$app->log('Setting Content Security Policy (CSP) headers.',
[
'class' => get_class($this),
]
);
$csp = CSPBuilder::fromFile($app->getBaseDir().'/app/config/csp.json');
$csp->sendCSPHeader();
}
}

View File

@ -0,0 +1,44 @@
<?php
/*
* doughnutwedding.com
* Copyright (C) 2017 http://doughnutwedding.com eric@doughnutwedding.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Sikofitt\App\Middleware;
use Silex\Application;
use Symfony\Component\HttpFoundation\Request;
/**
* Class HeaderMiddleware.
*
* Injects custom headers into the application.
*/
class HeaderMiddleware
{
/**
* @param \Symfony\Component\HttpFoundation\Request $request
* @param \Silex\Application $app
*/
public function __invoke(Request $request, Application $app)
{
$poweredByLine = sprintf('Silex/%s [%s] %s/%s', Application::VERSION, php_sapi_name(), php_uname('s'), php_uname('m'));
header('X-Powered-By: '.$poweredByLine);
header('Server: Nginx/Unix ('.php_uname('m').')');
}
}

View File

@ -0,0 +1,34 @@
<?php
/*
* doughnutwedding.com
* Copyright (C) 2017 http://doughnutwedding.com eric@doughnutwedding.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Sikofitt\App\Repository;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query;
class RsvpRepository extends EntityRepository
{
public function getRsvpCount()
{
return $this->createQueryBuilder('r')
->select('sum(r.guests)')
->getQuery()->getOneOrNullResult(Query::HYDRATE_SINGLE_SCALAR);
}
}

View File

@ -0,0 +1,94 @@
<?php
/*
* doughnutwedding.com
* Copyright (C) 2017 http://doughnutwedding.com eric@doughnutwedding.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Sikofitt\App\Repository;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query;
use Sikofitt\App\Entity\User;
use Symfony\Component\Validator\Constraints\Email;
use Symfony\Component\Validator\Validation;
/**
* Class UserRepository.
*
* Doctrine repository for User entity.
*/
class UserRepository extends EntityRepository
{
public function getKatrinaCount()
{
return $this->createQueryBuilder('u')
->select('count(u.familySide)')
->distinct(true)
->where('u.familySide = :side')
->setParameter('side', User::KATRINA_SIDE)
->getQuery()
->getOneOrNullResult(Query::HYDRATE_SINGLE_SCALAR);
}
public function getEricCount()
{
return $this->createQueryBuilder('u')
->select('count(u.familySide)')
->distinct(true)
->where('u.familySide = :side')
->setParameter('side', User::ERIC_SIDE)
->getQuery()
->getOneOrNullResult(Query::HYDRATE_SINGLE_SCALAR);
}
public function getEmail(string $email)
{
$validator = Validation::createValidator();
$emailConstraint = new Email([
'checkMX' => true,
'checkHost' => true,
'strict' => true,
]);
$result = $validator->validate($email, [$emailConstraint]);
if ($result->count() > 0) {
return $result;
}
return $this->createQueryBuilder('u')
->select('u.email')
->where('u.email = :email')
->setParameter('email', $email)
->getQuery()
->getOneOrNullResult(Query::HYDRATE_SINGLE_SCALAR);
}
public function setResetToken(string $email)
{
$token = bin2hex(random_bytes(22));
return (bool) $this->createQueryBuilder('u')
->update()
->set('u.token', ':token')
->setParameter('token', $token)
->where('u.email = :email')
->setParameter('email', $email)
->getQuery()
->getOneOrNullResult(Query::HYDRATE_SINGLE_SCALAR);
}
}

View File

@ -0,0 +1,96 @@
<?php
/*
* doughnutwedding.com
* Copyright (C) 2017 http://doughnutwedding.com eric@doughnutwedding.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Sikofitt\App\Traits;
/**
* Trait FlashTrait.
*
* Adds shortcuts for adding flash messages.
*/
trait FlashTrait
{
/**
* @param \string[] ...$messages
*
* @return $this
*/
public function addInfo(string ...$messages)
{
if (false === isset($this['session'])) {
return;
}
foreach ($messages as $message) {
$this['session']->getFlashBag()->add('info', $message);
}
return $this;
}
/**
* @param \string[] ...$messages
*
* @return $this
*/
public function addError(string ...$messages)
{
if (false === isset($this['session'])) {
return;
}
foreach ($messages as $message) {
$this['session']->getFlashBag()->add('error', $message);
}
return $this;
}
/**
* @param \string[] ...$messages
*
* @return $this
*/
public function addSuccess(string ...$messages)
{
if (false === isset($this['session'])) {
return;
}
foreach ($messages as $message) {
$this['session']->getFlashBag()->add('success', $message);
}
return $this;
}
public function addWarning(string ...$messages)
{
if (false === isset($this['session'])) {
return;
}
foreach ($messages as $message) {
$this['session']->getFlashBag()->add('warning', $message);
}
return $this;
}
}

View File

@ -0,0 +1,198 @@
<?php
/*
* doughnutwedding.com
* Copyright (C) 2017 http://doughnutwedding.com eric@doughnutwedding.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Sikofitt\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
class MysqlAuthenticator extends AbstractGuardAuthenticator
{
/**
* Returns a response that directs the user to authenticate.
*
* This is called when an anonymous request accesses a resource that
* requires authentication. The job of this method is to return some
* response that "helps" the user start into the authentication process.
*
* Examples:
* A) For a form login, you might redirect to the login page
* return new RedirectResponse('/login');
* B) For an API token authentication system, you return a 401 response
* return new Response('Auth header required', 401);
*
* @param Request $request The request that resulted
* in an
* AuthenticationException
* @param AuthenticationException $authException The exception that started
* the authentication process
*
* @return Response
*/
public function start(
Request $request,
AuthenticationException $authException = null
) {
// TODO: Implement start() method.
}
/**
* Get the authentication credentials from the request and return them
* as any type (e.g. an associate array). If you return null,
* authentication
* will be skipped.
*
* Whatever value you return here will be passed to getUser() and
* checkCredentials()
*
* For example, for a form login, you might:
*
* if ($request->request->has('_username')) {
* return array(
* 'username' => $request->request->get('_username'),
* 'password' => $request->request->get('_password'),
* );
* } else {
* return;
* }
*
* Or for an API token that's on a header, you might use:
*
* return array('api_key' => $request->headers->get('X-API-TOKEN'));
*
* @param Request $request
*
* @return mixed|null
*/
public function getCredentials(Request $request)
{
// TODO: Implement getCredentials() method.
}
/**
* Return a UserInterface object based on the credentials.
*
* The *credentials* are the return value from getCredentials()
*
* You may throw an AuthenticationException if you wish. If you return
* null, then a UsernameNotFoundException is thrown for you.
*
* @param mixed $credentials
* @param UserProviderInterface $userProvider
*
* @throws AuthenticationException
*
* @return UserInterface|null
*/
public function getUser($credentials, UserProviderInterface $userProvider)
{
// TODO: Implement getUser() method.
}
/**
* Returns true if the credentials are valid.
*
* If any value other than true is returned, authentication will
* fail. You may also throw an AuthenticationException if you wish
* to cause authentication to fail.
*
* The *credentials* are the return value from getCredentials()
*
* @param mixed $credentials
* @param UserInterface $user
*
* @throws AuthenticationException
*
* @return bool
*/
public function checkCredentials($credentials, UserInterface $user)
{
// TODO: Implement checkCredentials() method.
}
/**
* Called when authentication executed, but failed (e.g. wrong username
* password).
*
* This should return the Response sent back to the user, like a
* RedirectResponse to the login page or a 403 response.
*
* If you return null, the request will continue, but the user will
* not be authenticated. This is probably not what you want to do.
*
* @param Request $request
* @param AuthenticationException $exception
*
* @return Response|null
*/
public function onAuthenticationFailure(
Request $request,
AuthenticationException $exception
) {
// TODO: Implement onAuthenticationFailure() method.
}
/**
* Called when authentication executed and was successful!
*
* This should return the Response sent back to the user, like a
* RedirectResponse to the last page they visited.
*
* If you return null, the current request will continue, and the user
* will be authenticated. This makes sense, for example, with an API.
*
* @param Request $request
* @param TokenInterface $token
* @param string $providerKey The provider (i.e. firewall) key
*
* @return Response|null
*/
public function onAuthenticationSuccess(
Request $request,
TokenInterface $token,
$providerKey
) {
// TODO: Implement onAuthenticationSuccess() method.
}
/**
* Does this method support remember me cookies?
*
* Remember me cookie will be set if *all* of the following are met:
* A) This method returns true
* B) The remember_me key under your firewall is configured
* C) The "remember me" functionality is activated. This is usually
* done by having a _remember_me checkbox in your form, but
* can be configured by the "always_remember_me" and
* "remember_me_parameter" parameters under the "remember_me" firewall
* key
*
* @return bool
*/
public function supportsRememberMe()
{
// TODO: Implement supportsRememberMe() method.
}
}

View File

@ -0,0 +1,96 @@
<?php
/*
* doughnutwedding.com
* Copyright (C) 2017 http://doughnutwedding.com eric@doughnutwedding.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Sikofitt\Security;
use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface;
class ScryptEncoder implements PasswordEncoderInterface
{
/**
* Encodes the raw password.
*
* @param string $raw The password to encode
* @param string $salt The salt
*
* @return string The encoded password
*/
public function encodePassword($raw, $salt)
{
$salt = $this->generateSalt();
$hash = \scrypt($raw, $salt, 16384, 8, 1, 32);
return 16384 .'$'. 8 .'$'. 1 .'$'.$salt.'$'.$hash;
}
/**
* Checks a raw password against an encoded password.
*
* @param string $encoded An encoded password
* @param string $raw A raw password
* @param string $salt The salt
*
* @return bool true if the password is valid, false otherwise
*/
public function isPasswordValid($encoded, $raw, $salt)
{
$salt = null;
// Is there actually a hash?
if (!$encoded) {
return false;
}
list($N, $r, $p, $salt, $hash) = explode('$', $encoded);
// No empty fields?
if (empty($N) or empty($r) or empty($p) or empty($salt) or empty($hash)) {
return false;
}
// Are numeric values numeric?
if (!is_numeric($N) or !is_numeric($r) or !is_numeric($p)) {
return false;
}
$calculated = \scrypt($raw, $salt, $N, $r, $p, 32);
// Use compareStrings to avoid timeing attacks
return $this->compareStrings($hash, $calculated);
}
private function compareStrings($expected, $actual)
{
$expected = (string) $expected;
$actual = (string) $actual;
$lenExpected = \mb_strlen($expected);
$lenActual = \mb_strlen($actual);
$len = min($lenExpected, $lenActual);
$result = 0;
for ($i = 0; $i < $len; ++$i) {
$result |= ord($expected[$i]) ^ ord($actual[$i]);
}
$result |= $lenExpected ^ $lenActual;
return $result === 0;
}
private function generateSalt()
{
$buffer = random_bytes(8);
$salt = str_replace(['+', '$'], ['.', ''], base64_encode($buffer));
return $salt;
}
}