diff --git a/app/Http/Controllers/CronController.php b/app/Http/Controllers/CronController.php new file mode 100644 index 0000000..86eb5a8 --- /dev/null +++ b/app/Http/Controllers/CronController.php @@ -0,0 +1,54 @@ +getEmployeesWithBirtheyOn($date); + + // No birthey in two weeks frmon now + if (!count($employees)) { + return; + } + + $administrators = $employeeRepository->getAdminUsers(); + + // No admin users to notify + if (!count($administrators)) { + return; + } + + $data = [ + 'employees' => $employees, + 'date' => $date, + ]; + + foreach ($administrators as $admin) { + $data['admin'] = $admin; + + Mail::send('emails.birthdays.reminder', $data, function ($message) use ($admin) { + $message->to($admin->email, $admin->email)->subject(trans('emails.birthdays.reminder.subject')); + }); + } + + var_dump('Birthey reminders sent to ' . count($administrators) . ' administrator(s).'); + } +} diff --git a/app/Providers/RepositoryServiceProvider.php b/app/Providers/RepositoryServiceProvider.php index 40551d5..96eaa10 100644 --- a/app/Providers/RepositoryServiceProvider.php +++ b/app/Providers/RepositoryServiceProvider.php @@ -44,6 +44,7 @@ public function register() Time\Repositories\ClientRepository::class => [Time\Repositories\Interfaces\ClientRepositoryInterface::class], Time\Repositories\ProjectRepository::class => [Time\Repositories\Interfaces\ProjectRepositoryInterface::class], Time\Repositories\TimeLogRepository::class => [Time\Repositories\Interfaces\TimeLogRepositoryInterface::class], + \App\Repositories\EmployeeRepository::class => [\App\Repositories\Interfaces\EmployeeRepositoryInterface::class], ]; foreach ($bindings as $concrete => $interfaces) { diff --git a/app/Providers/ViewComposerServiceProvider.php b/app/Providers/ViewComposerServiceProvider.php index 6dfd76a..385fdc5 100644 --- a/app/Providers/ViewComposerServiceProvider.php +++ b/app/Providers/ViewComposerServiceProvider.php @@ -27,5 +27,58 @@ public function register() view()->composer('includes.header', function($view) { $view->with('current', preg_replace('/\..*/', '', Route::currentRouteName())); }); + + /** + * In the lines below we define the $style and $fontFamily variable that will be accessible + * in all the emails views. + */ + view()->composer('emails.*', function($view) { + $style = [ + /* Layout ------------------------------ */ + + 'body' => 'margin: 0; padding: 0; width: 100%; background-color: #F2F4F6;', + 'email-wrapper' => 'width: 100%; margin: 0; padding: 0; background-color: #F2F4F6;', + + /* Masthead ----------------------- */ + + 'email-masthead' => 'padding: 25px 0; text-align: center;', + 'email-masthead_name' => 'font-size: 16px; font-weight: bold; color: #2F3133; text-decoration: none; text-shadow: 0 1px 0 white;', + + 'email-body' => 'width: 100%; margin: 0; padding: 0; border-top: 1px solid #EDEFF2; border-bottom: 1px solid #EDEFF2; background-color: #FFF;', + 'email-body_inner' => 'width: auto; max-width: 570px; margin: 0 auto; padding: 0;', + 'email-body_cell' => 'padding: 35px;', + + 'email-footer' => 'width: auto; max-width: 570px; margin: 0 auto; padding: 0; text-align: center;', + 'email-footer_cell' => 'color: #AEAEAE; padding: 35px; text-align: center;', + + /* Body ------------------------------ */ + + 'body_action' => 'width: 100%; margin: 30px auto; padding: 0; text-align: center;', + 'body_sub' => 'margin-top: 25px; padding-top: 25px; border-top: 1px solid #EDEFF2;', + + /* Type ------------------------------ */ + + 'anchor' => 'color: #3869D4;', + 'header-1' => 'margin-top: 0; color: #2F3133; font-size: 19px; font-weight: bold; text-align: left;', + 'paragraph' => 'margin-top: 0; color: #74787E; font-size: 16px; line-height: 1.5em;', + 'paragraph-sub' => 'margin-top: 0; color: #74787E; font-size: 12px; line-height: 1.5em;', + 'paragraph-center' => 'text-align: center;', + + /* Buttons ------------------------------ */ + + 'button' => 'display: block; display: inline-block; width: 200px; min-height: 20px; padding: 10px; + background-color: #3869D4; border-radius: 3px; color: #ffffff; font-size: 15px; line-height: 25px; + text-align: center; text-decoration: none; -webkit-text-size-adjust: none;', + + 'button--green' => 'background-color: #22BC66;', + 'button--red' => 'background-color: #dc4d2f;', + 'button--blue' => 'background-color: #3869D4;', + 'label-small' => 'color:gray; font-size:13px;', + ]; + + $fontFamily = 'font-family: Arial, \'Helvetica Neue\', Helvetica, sans-serif;'; + + $view->with(compact('style', 'fontFamily')); + }); } } diff --git a/app/Repositories/EmployeeRepository.php b/app/Repositories/EmployeeRepository.php new file mode 100644 index 0000000..e106cac --- /dev/null +++ b/app/Repositories/EmployeeRepository.php @@ -0,0 +1,39 @@ +model = $model; + } + + public function getAdminUsers() + { + return $this->model->where('role', $this->model::USER_ROLE_ADMIN)->get(); + } + + public function getEmployeeUsers() + { + return $this->model->where('role', $this->model::USER_ROLE_EMPLOYEE)->get(); + } + + public function getEmployeesWithBirtheyOn(Carbon $date) + { + $birthday_date = '%' . $date->format('-m-d'); + + return $this->model + ->where('role', $this->model::USER_ROLE_EMPLOYEE) + ->where('birth_date', 'LIKE', $birthday_date) + ->get(); + } +} diff --git a/app/Repositories/Interfaces/EmployeeRepositoryInterface.php b/app/Repositories/Interfaces/EmployeeRepositoryInterface.php new file mode 100644 index 0000000..261ca8d --- /dev/null +++ b/app/Repositories/Interfaces/EmployeeRepositoryInterface.php @@ -0,0 +1,39 @@ +attributes['email'] = null; } else { $this->attributes['email'] = $email; @@ -50,7 +50,7 @@ public function setEmailAttribute($email) public function setBirthDateAttribute($birthDate) { - if(!$birthDate) { + if (!$birthDate) { $this->attributes['birth_date'] = null; } else { $this->attributes['birth_date'] = $birthDate; @@ -59,13 +59,25 @@ public function setBirthDateAttribute($birthDate) public function setNotesAttribute($notes) { - if(!$notes) { + if (!$notes) { $this->attributes['notes'] = null; } else { $this->attributes['notes'] = $notes; } } + /** + * This mutator allow us to access the attribute 'name' + * on the instantiated objects even if we don't have a + * column in the database for it. + * + * @return String The concatenation of the first_name and last_name. + */ + public function getNameAttribute() + { + return $this->first_name . ' ' . $this->last_name; + } + public function jobs() { return $this->belongsTo(UserJobs::class); diff --git a/resources/lang/en/emails.php b/resources/lang/en/emails.php new file mode 100644 index 0000000..282996f --- /dev/null +++ b/resources/lang/en/emails.php @@ -0,0 +1,38 @@ + [ + 'action_text' => 'Click Here', + 'action_trouble' => 'If you’re having trouble clicking the ":actiontext" button, copy and paste the URL below into your web browser:', + ], + + /* + |-------------------------------------------------------------------------- + | Birthday Emails Lines + |-------------------------------------------------------------------------- + | + | The following language lines contain the messages used in the birthday + | email views and subject lines. + | + */ + 'birthdays' => [ + 'reminder' => [ + 'subject' => 'Birthday Reminder!', + 'greetings' => 'Hello :name,', + 'intro' => 'On day :date is the birthday to your employees listed below:', + 'outro' => 'Don\'t miss the chance to organize something and celebrate with them their aging!', + 'salutation' => 'Regards, ', + ], + ], + +]; diff --git a/resources/views/emails/birthdays/reminder.blade.php b/resources/views/emails/birthdays/reminder.blade.php new file mode 100644 index 0000000..29d5ca7 --- /dev/null +++ b/resources/views/emails/birthdays/reminder.blade.php @@ -0,0 +1,40 @@ + +@extends('layouts.mail') + +@section('content') + + + + + + + + +
+ +

+ {{trans('emails.birthdays.reminder.greetings', ['name' => $admin->name])}} +

+ + +

+ {{trans('emails.birthdays.reminder.intro', ['date' => $date->format('d/m/Y')])}} +

+ +

+ @foreach ($employees as $employee) + {{ $employee->name }}
+ @endforeach +

+ + +

+ {{trans('emails.birthdays.reminder.outro')}} +

+ + +

+ {{ trans('emails.birthdays.reminder.salutation') }}
{{ config('app.name') }} +

+
+@endsection diff --git a/resources/views/layouts/mail.blade.php b/resources/views/layouts/mail.blade.php new file mode 100644 index 0000000..c790c4e --- /dev/null +++ b/resources/views/layouts/mail.blade.php @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + {{ config('app.name') }} + +
+ @yield('content') +
+ + + + +
+

+ © {{ date('Y') }} + {{ config('app.name') }}. + All rights reserved. +

+
+
+
+ + diff --git a/routes/web.php b/routes/web.php index 3b79434..27fc5f1 100644 --- a/routes/web.php +++ b/routes/web.php @@ -14,6 +14,24 @@ Route::get('/', '\App\Http\Controllers\HomeController@index')->name('home')->middleware(['auth', 'admin']); +/** + * Start Cron functions routes declaration + */ +Route::group(['prefix' => 'cron', 'as' => 'cron.'], function() { + + /** + * @route cron/remind-birthdeys-to-admin + * @name cron.remind-birthdeys-to-admin + * @function notifyBirthdeysToAdmin + * @controller App\Http\Controllers\CronController + */ + Route::get('notify-birthdeys-to-admin', '\App\Http\Controllers\CronController@notifyBirthdeysToAdmin')->name('notify-birthdeys-to-admin'); +}); +/** + * End Cron functions routes declaration + */ + + Route::get('/storage/{path}', function($path) { return response()->file(storage_path().'/app/'.$path); })->name('storage')->where('path','(.*)')->middleware(['auth', 'admin']);