use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use App\User;
+use \Illuminate\Database\Query\Builder;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
+use Illuminate\Validation\Rule;
class RegisterController extends Controller
{
protected function validator(array $data)
{
return Validator::make($data, [
- 'name' => ['required', 'string', 'max:255'],
- 'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
+ 'first_name' => ['required', 'string', 'max:255'],
+ 'last_name' => ['required', 'string', 'max:255'],
+ 'employer' => ['required', 'string', 'max:255'],
+ 'email' => [
+ 'required',
+ 'string',
+ 'email',
+ 'max:255',
+ Rule::unique('users')
+ ->where(fn(Builder $builder) => $builder->where('is_prospect', false))
+ ],
'password' => ['required', 'string', 'min:8', 'confirmed'],
]);
}
*/
protected function create(array $data)
{
- return User::create([
- 'name' => $data['name'],
- 'email' => $data['email'],
- 'password' => Hash::make($data['password']),
- ]);
+ /** @var User $user */
+ $user = User::query()->updateOrCreate(
+ [
+ 'email' => $data['email'],
+ ],
+ [
+ 'first_name' => $data['first_name'],
+ 'last_name' => $data['last_name'],
+ 'employer' => $data['employer'],
+ 'password' => Hash::make($data['password']),
+ 'reg_complete' => true,
+ 'is_prospect' => false,
+ 'self_registered' => true,
+ ]
+ );
+
+ $user->startTrial();
+
+ $user->sendEmailVerificationNotification();
+
+ return $user;
}
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
+use App\Notifications\EmailValidated;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\VerifiesEmails;
+use Illuminate\Http\Request;
class VerificationController extends Controller
{
$this->middleware('signed')->only('verify');
$this->middleware('throttle:6,1')->only('verify', 'resend');
}
+
+ /**
+ * @param Request $request
+ */
+ protected function verified(Request $request)
+ {
+ $request->user()->notify(new EmailValidated);
+ }
}
--- /dev/null
+<?php
+
+namespace App\Notifications;
+
+use App\User;
+use Illuminate\Bus\Queueable;
+use Illuminate\Contracts\Queue\ShouldQueue;
+use Illuminate\Notifications\Messages\MailMessage;
+use Illuminate\Notifications\Notification;
+
+class EmailValidated extends Notification
+{
+ use Queueable;
+
+ /**
+ * Create a new notification instance.
+ *
+ * @return void
+ */
+ public function __construct()
+ {
+ //
+ }
+
+ /**
+ * Get the notification's delivery channels.
+ *
+ * @param mixed $notifiable
+ * @return array
+ */
+ public function via($notifiable)
+ {
+ return ['mail'];
+ }
+
+ /**
+ * Get the mail representation of the notification.
+ *
+ * @param mixed $notifiable
+ * @return \Illuminate\Notifications\Messages\MailMessage
+ */
+ public function toMail($notifiable)
+ {
+ return (new MailMessage)
+ ->subject('Inscription réussie !')
+ ->line("Bienvenue ! Vous bénéficiez dès aujourd'hui d'une période d'essai de ".User::trialDurationDays." jours.")
+ ->line('Merci [....]');
+ }
+
+ /**
+ * Get the array representation of the notification.
+ *
+ * @param mixed $notifiable
+ * @return array
+ */
+ public function toArray($notifiable)
+ {
+ return [
+ //
+ ];
+ }
+}
public function updating(User $user)
{
if(
+ $user->self_registered === false &&
+ $user->reg_complete &&
$user->isDirty('reg_complete') &&
- $user->getOriginal()['reg_complete'] === false &&
- $user->reg_complete
+ $user->getOriginal()['reg_complete'] === false
) {
$user->notify(new RegistrationComplete);
}
use App\PdfFile;
use App\User;
use Illuminate\Database\Eloquent\Relations\Relation;
+use Illuminate\Support\Carbon;
use Illuminate\Support\ServiceProvider;
use League\HTMLToMarkdown\HtmlConverter;
use Mailgun\Mailgun;
*/
public function boot()
{
+ setlocale(LC_TIME, 'fr_FR', 'fr', 'FR', 'French', 'fr_FR.UTF-8');
+ Carbon::setLocale('fr');
Relation::morphMap([
'PdfFiles' => PdfFile::class,
]);
new FileAccess,
new MailEvents('opened', 'Mails ouverts'),
new MailEventsPartition,
-
];
}
*
* @var string
*/
- public const HOME = '/home';
+ public const HOME = '/';
/**
* Define your route model bindings, pattern filters, etc.
namespace App;
+use App\Notifications\EmailValidated;
use DemeterChain\B;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
+use Illuminate\Support\Arr;
use Illuminate\Support\Carbon;
use Laravel\Cashier\Billable;
use Laravel\Scout\Searchable;
* @property-read $name
* @property Organization $organization
* @property string $position
- * @property bool $isSubscribed
- * @property Carbon $trial_ends_at
+ * @property bool $isSubscribed (is user has org subscribed)
+ * @property Carbon $trial_ends_at (is user is on trial)
+ * @property bool self_registered (if user used /register)
+ * @property string $employer (used if self registered)
+ * @property bool $is_prospect
+ * @property bool $reg_complete
+ * @property string $status
*/
class User extends Authenticatable
{
'reg_complete' => 'bool',
'trial_ends_at' => 'datetime',
'active_until' => 'datetime',
+ 'self_registered' => 'bool',
+ 'is_prospect' => 'bool',
];
- public function toSearchableArray()
- {
- return [
- 'first_name' => $this->first_name,
- 'last_name' => $this->last_name,
- 'position' => $this->position,
- 'organization' => $this->organization->name ?? null,
- ];
- }
-
- public const trialDurationDays = 14;
-
- /**
- * @return BelongsTo
- */
- public function organization(): BelongsTo
- {
- return $this->belongsTo(Organization::class);
- }
-
/**
- * @return bool
+ * Trial duration in days
*/
- public function getIsSubscribedAttribute(): bool
- {
- return ($o = $this->organization) === null ?
- false:
- $o->isSubscribed();
- }
+ public const trialDurationDays = 14;
/**
* Possible Statuses
'label' => 'Abonnement actif (orga)'
],
'trial' => [
- 'badge' => 'warning',
+ 'badge' => 'info',
'label' => "Période d'essai"
],
+ 'prospect' => [
+ 'badge' => 'warning',
+ 'label' => 'Prospect',
+ ]
];
+
+
+
+
+
/**
- * @return string
+ * @return array
*/
- public function getStatusAttribute(): string
+ public function toSearchableArray()
{
- $id = 'inactive';
+ return [
+ 'first_name' => $this->first_name,
+ 'last_name' => $this->last_name,
+ 'position' => $this->position,
+ 'organization' => $this->organization->name ?? null,
+ ];
+ }
+
- if($this->isSubscribed)
- $id = 'subscribed';
- if($this->onTrial())
- $id = 'trial';
- return $id;
- }
/**
- * @return string
- * Returns current status
+ * RELATIONSHIPS
*/
- public function getStatusLabelAttribute(): string
+
+ /**
+ * @return BelongsTo
+ */
+ public function organization(): BelongsTo
{
- return self::statuses[$this->status]['label'];
+ return $this->belongsTo(Organization::class);
}
+
/**
* @return HasMany
*/
return $this->hasMany(LoginToken::class);
}
+
+
+
+
+ /**
+ * METHODS
+ */
+
/**
* @param $route
* @param array $params
$this->save();
}
+ /**
+ * @param $status
+ * @return bool
+ */
+ public function hasStatus($status): bool
+ {
+ $status = Arr::wrap($status);
+ return in_array($this->status, $status, true);
+ }
+ /**
+ * SCOPES
+ */
+ /**
+ * @param Builder $builder
+ */
public function scopeRecievesEmails(Builder $builder): void
{
$builder->hasActiveSubscription()->orWhere->isOnTrial();
}
+ /**
+ * @param Builder $builder
+ */
public function scopeIsOnTrial(Builder $builder): void
{
$builder->whereDate('trial_ends_at', '>', now());
}
+ /**
+ * @param Builder $builder
+ */
public function scopeHasActiveSubscription(Builder $builder): void
{
$builder->whereHas('organization', fn($builder) => $builder->subscribed());
}
+ /**
+ * @param Builder $builder
+ */
+ public function scopeProspect(Builder $builder): void
+ {
+ $builder->where('is_prospect', true);
+ }
+
+ /**
+ * @param Builder $builder
+ */
+ public function scopeRegisteredUser(Builder $builder): void
+ {
+ $builder->where('is_prospect', false);
+ }
+
+ /**
+ * ATTRIBUTES
+ */
+
+ /**
+ * @return bool
+ * Checks if affiliated organization has valid subscription
+ */
+ public function getIsSubscribedAttribute(): bool
+ {
+ return ($o = $this->organization) === null ?
+ false:
+ $o->isSubscribed();
+ }
+
+
+ /**
+ * @return string
+ * Returns status slug
+ */
+ public function getStatusAttribute(): string
+ {
+ $id = 'inactive';
+
+ if($this->isSubscribed)
+ $id = 'subscribed';
+ if($this->onTrial())
+ $id = 'trial';
+ if($this->is_prospect)
+ $id = 'prospect';
+
+ return $id;
+
+ }
+
+ /**
+ * @return string
+ * Returns current status
+ */
+ public function getStatusLabelAttribute(): string
+ {
+ return self::statuses[$this->status]['label'];
+ }
+
+
/**
* @return string|null
+ * Get full name
*/
public function getNameAttribute(): ?string
{
/**
* @return bool
+ * Checks if is affiliated to an organization
*/
public function getIsIndividualAttribute(): bool
{
--- /dev/null
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class AddEmployerToUsers extends Migration
+{
+ /**
+ * Run the migrations.
+ *
+ * @return void
+ */
+ public function up()
+ {
+ Schema::table('users', function (Blueprint $table) {
+ $table->string('employer')->nullable();
+ $table->boolean('self_registered')->default(false);
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::table('users', function (Blueprint $table) {
+ $table->dropColumn('employer');
+ $table->dropColumn('self_registered');
+ });
+ }
+}
--- /dev/null
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class AddProspectFlagToUsers extends Migration
+{
+ /**
+ * Run the migrations.
+ *
+ * @return void
+ */
+ public function up()
+ {
+ Schema::table('users', function (Blueprint $table) {
+ $table->boolean('is_prospect')->default(false);
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::table('users', function (Blueprint $table) {
+ $table->dropColumn('is_prospect');
+ });
+ }
+}
'gt' => [
'numeric' => 'La valeur de :attribute doit être supérieure à :value.',
'file' => 'La taille du fichier de :attribute doit être supérieure à :value kilo-octets.',
- 'string' => 'Le texte :attribute doit contenir plus de :value caractères.',
+ 'string' => 'Le champ :attribute doit contenir plus de :value caractères.',
'array' => 'Le tableau :attribute doit contenir plus de :value éléments.',
],
'gte' => [
'numeric' => 'La valeur de :attribute doit être supérieure ou égale à :value.',
'file' => 'La taille du fichier de :attribute doit être supérieure ou égale à :value kilo-octets.',
- 'string' => 'Le texte :attribute doit contenir au moins :value caractères.',
+ 'string' => 'Le champ :attribute doit contenir au moins :value caractères.',
'array' => 'Le tableau :attribute doit contenir au moins :value éléments.',
],
'image' => 'Le champ :attribute doit être une image.',
</div>
@else
@if($user->isSubscribed)
- <div class="alert alert-info">
+ <div class="alert alert-success">
Votre compte est actif, vous pouvez accéder aux contenus <b>Prescription Santé.</b>
</div>
+
+ @elseif($user->onTrial())
+ <div class="alert alert-info">
+ Vous bénéficiez d'une période d'évaluation jusqu'au {{$user->trial_ends_at->formatLocalized('%d %B %Y')}}
+ </div>
@endif
@endif
@if(session()->has('message'))
<label for="last_name" class="col-md-4 col-form-label text-md-right">{{ __('Nom') }}</label>
<div class="col-md-6">
- <input id="last_name" type="text" class="form-control @error('last_name') is-invalid @enderror" name="last_name" value="{{ old('last_name', $user->last_name) }}" required autocomplete="family-name" autofocus>
+ <input id="last_name" type="text" class="form-control @error('last_name') is-invalid @enderror" name="last_name" value="{{ old('last_name', $user->last_name) }}" required autocomplete="family-name">
@error('last_name')
<span class="invalid-feedback" role="alert">
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
+
+ <div class="alert-info alert">
+ <p class="font-weight-bold">Bienvenue sur {{config('app.name')}}</p>
+ <p class="mb-0">Vous bénéficierez de 2 semaines d'accès gratuit dès votre inscription !</p>
+ </div>
+
<div class="card">
+
<div class="card-header">{{ __('Register') }}</div>
<div class="card-body">
@csrf
<div class="form-group row">
- <label for="name" class="col-md-4 col-form-label text-md-right">{{ __('Name') }}</label>
+ <label for="first_name" class="col-md-4 col-form-label text-md-right">{{ __('Prénom') }}</label>
+
+ <div class="col-md-6">
+ <input id="first_name" type="text" class="form-control @error('first_name') is-invalid @enderror" name="first_name" value="{{ old('first_name') }}" required autocomplete="given-name" autofocus>
+
+ @error('first_name')
+ <span class="invalid-feedback" role="alert">
+ <strong>{{ $message }}</strong>
+ </span>
+ @enderror
+ </div>
+ </div>
+
+ <div class="form-group row">
+ <label for="last_name" class="col-md-4 col-form-label text-md-right">{{ __('Nom') }}</label>
<div class="col-md-6">
- <input id="name" type="text" class="form-control @error('name') is-invalid @enderror" name="name" value="{{ old('name') }}" required autocomplete="name" autofocus>
+ <input id="last_name" type="text" class="form-control @error('last_name') is-invalid @enderror" name="last_name" value="{{ old('last_name') }}" required autocomplete="family-name">
- @error('name')
+ @error('last_name')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
</div>
</div>
+ <div class="form-group row">
+ <label for="employer" class="col-md-4 col-form-label text-md-right">{{ __('Employeur') }}</label>
+
+ <div class="col-md-6">
+ <input id="employer" type="text" class="form-control @error('employer') is-invalid @enderror" name="employer" value="{{ old('employer') }}" required>
+
+ @error('employer')
+ <span class="invalid-feedback" role="alert">
+ <strong>{{ $message }}</strong>
+ </span>
+ @enderror
+ </div>
+ </div>
+
+
+
<div class="form-group row">
<label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>
-Auth::routes();
+Auth::routes(['verify' => true]);
+