Commit d17a2a9a authored by Korko's avatar Korko
Browse files

Add feature dearsanta

parent cd05ff7d
......@@ -2,6 +2,7 @@
namespace Korko\SecretSanta\Console;
use Draw;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
......@@ -24,5 +25,8 @@ class Kernel extends ConsoleKernel
*/
protected function schedule(Schedule $schedule)
{
$schedule->call(function () {
Draw::where('expiration', '<=', date('Y-m-d'))->delete();
})->daily();
}
}
<?php
namespace Korko\SecretSanta;
use Illuminate\Database\Eloquent\Model;
class Draw extends Model
{
//
}
<?php
namespace Korko\SecretSanta\Http\Controllers;
use Illuminate\Http\Request;
use Korko\SecretSanta\Http\Requests\DearSantaRequest;
use Korko\SecretSanta\Participant;
use Mail;
use Statsd;
class DearSantaController extends Controller
{
public function view(Participant $participant)
{
// Times 2 because we are in UTF8 and the iv is in hexadecimal
$ivLength = openssl_cipher_iv_length('aes256') * 2;
return view('dearSanta', [
'challenge' => substr($participant->challenge, $ivLength),
'iv' => substr($participant->challenge, 0, $ivLength),
'santa' => $participant->id,
]);
}
public function handle(Participant $participant, DearSantaRequest $request)
{
$santa = $this->getSanta($participant, $request);
Statsd::increment('dearsanta');
$this->sendMail($santa, $request->input('title'), $request->input('content'));
$message = trans('message.sent');
return $request->ajax() ? [$message] : redirect('/dearsanta/'.$participant->id)->with('message', $message);
}
private function getSanta(Participant $participant, Request $request)
{
$key = hex2bin($request->input('key'));
// Times 2 because we are in UTF8 and the iv is in hexadecimal
$ivLength = openssl_cipher_iv_length('aes256') * 2;
$santa = substr($participant->santa, $ivLength);
$iv = hex2bin(substr($participant->santa, 0, $ivLength));
$santaR = openssl_decrypt($santa, 'aes256', $key, 0, $iv);
$santa = unserialize($santaR);
return $santa;
}
protected function sendMail(array $santa, $title, $contentMail)
{
$contentMail .= PHP_EOL.trans('form.mail.post');
Mail::raw($contentMail, function ($m) use ($santa, $title) {
$m->to($santa['email'], $santa['name'])->subject($title);
});
}
}
......@@ -3,7 +3,9 @@
namespace Korko\SecretSanta\Http\Controllers;
use Illuminate\Http\Request;
use Korko\SecretSanta\Draw;
use Korko\SecretSanta\Http\Requests\RandomFormRequest;
use Korko\SecretSanta\Participant;
use Mail;
use Sms;
use Solver;
......@@ -64,9 +66,14 @@ class RandomFormController extends Controller
protected function sendMessage(Request $request, array $santa, array $target)
{
$dearSantaLink = null;
if ($request->input('dearsanta')) {
$dearSantaLink = $this->getDearSantaLink($santa, $request->input('dearsanta-limit'));
}
if (!empty($santa['email'])) {
Statsd::increment('email');
$this->sendMail($santa, $target, $request->input('title'), $request->input('contentMail'));
$this->sendMail($santa, $target, $request->input('title'), $request->input('contentMail'), $dearSantaLink);
}
if (!empty($santa['phone'])) {
......@@ -75,10 +82,50 @@ class RandomFormController extends Controller
}
}
protected function sendMail(array $santa, array $target, $title, $content)
protected function getDearSantaLink(array $santa, $expirationDate)
{
$key = openssl_random_pseudo_bytes(32);
$participant = $this->addParticipant($santa, $key, $expirationDate);
return route('dearsanta', ['santa' => $participant->id]).'#'.bin2hex($key);
}
private function getDraw($expirationDate)
{
if (!isset($this->draw)) {
$this->draw = new Draw();
$this->draw->expiration = $expirationDate;
$this->draw->save();
}
return $this->draw;
}
private function addParticipant(array $santa, $key, $expirationDate)
{
$draw = $this->getDraw($expirationDate);
$participant = new Participant();
$participant->draw_id = $draw->id;
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes256'));
$participant->santa = bin2hex($iv).openssl_encrypt(serialize($santa), 'aes256', $key, 0, $iv);
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes256'));
$participant->challenge = bin2hex($iv).openssl_encrypt(Participant::CHALLENGE, 'aes256', $key, 0, $iv);
$participant->save();
return $participant;
}
protected function sendMail(array $santa, array $target, $title, $content, $dearSantaLink)
{
$contentMail = str_replace(['{SANTA}', '{TARGET}'], [$santa['name'], $target['name']], $content);
$contentMail .= PHP_EOL.trans('form.mail.post');
$contentMail .= !empty($dearSantaLink) ?
PHP_EOL.trans('form.mail.post2', ['link' => $dearSantaLink]) :
PHP_EOL.trans('form.mail.post');
Mail::raw($contentMail, function ($m) use ($santa, $title) {
$m->to($santa['email'], $santa['name'])->subject($title);
});
......@@ -88,6 +135,7 @@ class RandomFormController extends Controller
{
$contentSms = str_replace(['{SANTA}', '{TARGET}'], [$santa['name'], $target['name']], $content);
$contentSms .= PHP_EOL.trans('form.sms.post');
Sms::message($santa['phone'], $contentSms);
}
}
<?php
namespace Korko\SecretSanta\Http\Requests;
class DearSantaRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
$rules = [
'g-recaptcha-response' => 'required|recaptcha',
'title' => 'required|string',
'content' => 'required|string',
'key' => 'required|string',
];
return $rules;
}
}
......@@ -23,10 +23,12 @@ class RandomFormRequest extends Request
{
$rules = [
'g-recaptcha-response' => 'required|recaptcha',
'name' => 'required|array|min:2|arrayunique',
'name' => 'required|array|min:3|arrayunique',
'email' => 'array',
'phone' => 'array',
'exclusions' => 'array',
'dearsanta' => 'boolean|in:"0","1"',
'dearsanta-limit' => 'required_if:dearsanta,"1"|date|after:tomorrow|before:+1year',
];
if (!empty($this->request->get('name'))) {
......@@ -44,7 +46,7 @@ class RandomFormRequest extends Request
foreach ($this->request->get('name') as $key => $name) {
$rules += [
'email.'.$key => 'required_without:phone.'.$key.'|email',
'email.'.$key => 'required_without:phone.'.$key.'|required_if:dearsanta,1|email',
'phone.'.$key => 'required_without:email.'.$key.'|numeric|regex:#0?[67]\d{8}#',
'exclusions.'.$key => 'sometimes|array',
];
......
<?php
namespace Korko\SecretSanta;
use Illuminate\Database\Eloquent\Model;
class Participant extends Model
{
const CHALLENGE = 'Ping?';
public $timestamps = false;
}
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateDrawsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('draws', function (Blueprint $table) {
$table->increments('id');
$table->date('expiration');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('draws');
}
}
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateParticipantsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('participants', function (Blueprint $table) {
$table->increments('id');
$table->integer('draw_id');
$table->text('santa');
$table->text('challenge');
$table->foreign('draw_id')->references('id')->on('draws')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('participants');
}
}
......@@ -16,23 +16,37 @@ elixir(function(mix) {
'main.js', 'randomForm.js'
], 'public/assets/randomForm.js');
mix.browserify([
'main.js', 'dearSanta.js'
], 'public/assets/dearSanta.js');
var sassOptions = {
includePaths: [
'./node_modules/bootstrap-sass/assets/stylesheets/',
'./node_modules/font-awesome/scss/',
'./node_modules/alertify.js/src/sass/',
'./node_modules/multiple-select/',
'./node_modules/jquery-ui-browserify/themes/base/',
'./resources/assets/vendor/bootstrap-languages/'
]
};
mix.sass([
'layout.scss'
], 'public/assets/layout.css', sassOptions);
mix.sass([
'randomForm.scss'
], 'public/assets/randomForm.css', sassOptions);
mix.sass([
'dearSanta.scss'
], 'public/assets/dearSanta.css', sassOptions);
mix.copy('resources/assets/img', 'public/img');
mix.copy('resources/assets/vendor/bootstrap-languages/languages.png', 'public/assets/img');
mix.copy('node_modules/bootstrap-sass/assets/fonts', 'public/assets/fonts');
mix.copy('node_modules/font-awesome/fonts', 'public/assets/fonts');
mix.copy('node_modules/multiple-select/multiple-select.png', 'public/assets');
mix.copy('node_modules/jquery-ui-browserify/themes/base/images', 'public/assets/images');
});
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment