Browse Source

[Administration] Utilisateurs : import CSV #1332

feature/souke
Guillaume Bourgeois 1 year ago
parent
commit
00978fe230
20 changed files with 450 additions and 42 deletions
  1. +15
    -22
      backend/controllers/UserController.php
  2. +83
    -0
      backend/controllers/UserImportController.php
  3. +33
    -0
      backend/forms/UserImportUploadForm.php
  4. +1
    -0
      backend/views/layouts/left.php
  5. +65
    -0
      backend/views/user-import/index.php
  6. +2
    -0
      backend/web/csv/users-import-sample.csv
  7. +11
    -0
      common/components/ActiveRecordCommon.php
  8. +5
    -0
      common/config/main.php
  9. +24
    -0
      common/helpers/CSV.php
  10. +1
    -1
      common/logic/AbstractService.php
  11. +22
    -0
      common/logic/Producer/Producer/Event/UserObserver.php
  12. +1
    -1
      common/logic/Producer/Producer/Service/ProducerBuilder.php
  13. +2
    -4
      common/logic/User/User/Event/TicketObserver.php
  14. +16
    -0
      common/logic/User/User/Event/UserCreateEvent.php
  15. +2
    -0
      common/logic/User/User/Model/User.php
  16. +8
    -0
      common/logic/User/User/Module/UserModule.php
  17. +98
    -13
      common/logic/User/User/Service/UserBuilder.php
  18. +49
    -0
      common/logic/User/User/Service/UserBulkImporter.php
  19. +1
    -1
      common/logic/User/User/Service/UserNotifier.php
  20. +11
    -0
      common/logic/User/User/Service/UserSolver.php

+ 15
- 22
backend/controllers/UserController.php View File

$this->setFlash('success', "L'utilisateur que vous souhaitez créer possède déjà un compte sur la plateforme. Il vient d'être lié à votre établissement."); $this->setFlash('success', "L'utilisateur que vous souhaitez créer possède déjà un compte sur la plateforme. Il vient d'être lié à votre établissement.");
} else { } else {
if ($model->load(\Yii::$app->request->post()) && $model->validate() && YII_ENV != 'demo') { if ($model->load(\Yii::$app->request->post()) && $model->validate() && YII_ENV != 'demo') {
$model->id_producer = 0;
$password = Password::generate();
$userModule->setPassword($model, $password);
$userModule->generateAuthKey($model);
$model->username = $model->email;
if (!strlen($model->email)) {
$model->username = 'inconnu@opendistrib.net';
}

$model->save();


// liaison etablissement / user
$useProducer = new UserProducer();
$useProducer->id_user = $model->id;
$useProducer->id_producer = GlobalParam::getCurrentProducerId();
$useProducer->credit = 0;
$useProducer->active = 1;
$useProducer->newsletter = $model->newsletter;
$useProducer->save();
$model = $userModule->getBuilder()->createUser(
User::TYPE_INDIVIDUAL,
$model->email,
$model->name,
$model->lastname,
$model->name_legal_person,
$model->phone,
$model->address,
$model->newsletter,
Password::generate()
);


$userModule->sendMailWelcome($model, $password);
$this->processLinkPointSale($model); $this->processLinkPointSale($model);
$this->processLinkUserGroup($model); $this->processLinkUserGroup($model);
$this->processProductPricePercent($model); $this->processProductPricePercent($model);


$this->setFlash('success', 'Utilisateur créé.'); $this->setFlash('success', 'Utilisateur créé.');
$model = $userModule->instanciateUser();

return $this->refresh();
} }
} }


$this->processProductPricePercent($model); $this->processProductPricePercent($model);


if($model->newsletter) { if($model->newsletter) {
$userModule->subscribeUserNewsletter($model);
$userModule->getNewsletterManager()->subscribeUserNewsletter($model);
} }
else { else {
$userModule->unsubscribeUserNewsletter($model);
$userModule->getNewsletterManager()->unsubscribeUserNewsletter($model);
} }


$this->setFlash('success', 'Utilisateur <strong>'.Html::encode($userModule->getUsername($model)).'</strong> modifié.'); $this->setFlash('success', 'Utilisateur <strong>'.Html::encode($userModule->getUsername($model)).'</strong> modifié.');

+ 83
- 0
backend/controllers/UserImportController.php View File

<?php

/**
* Copyright distrib (2018)
*
* contact@opendistrib.net
*
* Ce logiciel est un programme informatique servant à aider les producteurs
* à distribuer leur production en circuits courts.
*
* Ce logiciel est régi par la licence CeCILL soumise au droit français et
* respectant les principes de diffusion des logiciels libres. Vous pouvez
* utiliser, modifier et/ou redistribuer ce programme sous les conditions
* de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA
* sur le site "http://www.cecill.info".
*
* En contrepartie de l'accessibilité au code source et des droits de copie,
* de modification et de redistribution accordés par cette licence, il n'est
* offert aux utilisateurs qu'une garantie limitée. Pour les mêmes raisons,
* seule une responsabilité restreinte pèse sur l'auteur du programme, le
* titulaire des droits patrimoniaux et les concédants successifs.
*
* A cet égard l'attention de l'utilisateur est attirée sur les risques
* associés au chargement, à l'utilisation, à la modification et/ou au
* développement et à la reproduction du logiciel par l'utilisateur étant
* donné sa spécificité de logiciel libre, qui peut le rendre complexe à
* manipuler et qui le réserve donc à des développeurs et des professionnels
* avertis possédant des connaissances informatiques approfondies. Les
* utilisateurs sont donc invités à charger et tester l'adéquation du
* logiciel à leurs besoins dans des conditions permettant d'assurer la
* sécurité de leurs systèmes et ou de leurs données et, plus généralement,
* à l'utiliser et l'exploiter dans les mêmes conditions de sécurité.
*
* Le fait que vous puissiez accéder à cet en-tête signifie que vous avez
* pris connaissance de la licence CeCILL, et que vous en avez accepté les
* termes.
*/

namespace backend\controllers;

use backend\forms\UserImportUploadForm;
use common\helpers\CSV;
use yii\filters\AccessControl;
use yii\web\UploadedFile;

class UserImportController extends BackendController
{
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::class,
'rules' => [
[
'allow' => true,
'roles' => ['@'],
'matchCallback' => function ($rule, $action) {
return $this->getUserModule()
->getAuthorizationChecker()
->isGrantedAsProducer($this->getUserCurrent());
}
]
],
],
];
}

public function actionIndex()
{
$model = new UserImportUploadForm();
if (\Yii::$app->request->isPost) {
$model->file = UploadedFile::getInstance($model, 'file');
if($model->file && $model->validate()) {
$this->getUserModule()->getBulkImporter()->import($model->file->tempName);
$this->setFlash('success', "Fichier importé.");
}
}

return $this->render('index', [
'model' => $model
]);
}
}

+ 33
- 0
backend/forms/UserImportUploadForm.php View File

<?php

namespace backend\forms;

use yii\base\Model;
use yii\web\UploadedFile;

class UserImportUploadForm extends Model
{
/**
* @var UploadedFile file attribute
*/
public $file;

/**
* @return array the validation rules.
*/
public function rules()
{
return [
[['file'], 'file', 'skipOnEmpty' => false, 'mimeTypes' => 'text/csv, text/plain'],
];
}

public function attributeLabels()
{
return [
'file' => "Fichier d'import (CSV)"
];
}
}

?>

+ 1
- 0
backend/views/layouts/left.php View File

'visible' => $isUserCurrentGrantedAsProducer && $producerModule->getConfig('credit') 'visible' => $isUserCurrentGrantedAsProducer && $producerModule->getConfig('credit')
], ],
['label' => 'Groupes', 'icon' => 'users', 'url' => ['/user-group/index'], 'visible' => $isUserCurrentGrantedAsProducer], ['label' => 'Groupes', 'icon' => 'users', 'url' => ['/user-group/index'], 'visible' => $isUserCurrentGrantedAsProducer],
['label' => 'Import', 'icon' => 'upload', 'url' => ['/user-import/index'], 'visible' => $isUserCurrentGrantedAsProducer],
], ],
], ],
['label' => 'Abonnements', 'icon' => 'repeat', 'url' => ['/subscription/index'], 'visible' => $isUserCurrentGrantedAsProducer, 'active' => Yii::$app->controller->id == 'subscription'], ['label' => 'Abonnements', 'icon' => 'repeat', 'url' => ['/subscription/index'], 'visible' => $isUserCurrentGrantedAsProducer, 'active' => Yii::$app->controller->id == 'subscription'],

+ 65
- 0
backend/views/user-import/index.php View File

<?php

/**
* Copyright distrib (2018)
*
* contact@opendistrib.net
*
* Ce logiciel est un programme informatique servant à aider les producteurs
* à distribuer leur production en circuits courts.
*
* Ce logiciel est régi par la licence CeCILL soumise au droit français et
* respectant les principes de diffusion des logiciels libres. Vous pouvez
* utiliser, modifier et/ou redistribuer ce programme sous les conditions
* de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA
* sur le site "http://www.cecill.info".
*
* En contrepartie de l'accessibilité au code source et des droits de copie,
* de modification et de redistribution accordés par cette licence, il n'est
* offert aux utilisateurs qu'une garantie limitée. Pour les mêmes raisons,
* seule une responsabilité restreinte pèse sur l'auteur du programme, le
* titulaire des droits patrimoniaux et les concédants successifs.
*
* A cet égard l'attention de l'utilisateur est attirée sur les risques
* associés au chargement, à l'utilisation, à la modification et/ou au
* développement et à la reproduction du logiciel par l'utilisateur étant
* donné sa spécificité de logiciel libre, qui peut le rendre complexe à
* manipuler et qui le réserve donc à des développeurs et des professionnels
* avertis possédant des connaissances informatiques approfondies. Les
* utilisateurs sont donc invités à charger et tester l'adéquation du
* logiciel à leurs besoins dans des conditions permettant d'assurer la
* sécurité de leurs systèmes et ou de leurs données et, plus généralement,
* à l'utiliser et l'exploiter dans les mêmes conditions de sécurité.
*
* Le fait que vous puissiez accéder à cet en-tête signifie que vous avez
* pris connaissance de la licence CeCILL, et que vous en avez accepté les
* termes.
*/

use yii\bootstrap\ActiveForm;
use yii\helpers\Html;

$this->setTitle('Import utilisateurs');
$this->addBreadcrumb($this->getTitle());

?>

<div class="product-price-import">
<div class="alert alert-info">
<i class="icon fa fa-info"></i>
Fichier d'exemple : <a href="<?= Yii::$app->urlManager->baseUrl ?>/csv/users-import-sample.csv">Télécharger (CSV)</a>
</div>

<?php $form = ActiveForm::begin([
'enableClientValidation' => false,
'options' => ['enctype' => 'multipart/form-data']
]); ?>

<?= $form->field($model, 'file')->fileInput() ?>

<div class="form-group">
<?= Html::submitButton('Envoyer', ['class' => 'btn btn-primary']) ?>
</div>

<?php ActiveForm::end(); ?>
</div>

+ 2
- 0
backend/web/csv/users-import-sample.csv View File

Type;Nom;Prénom;Nom personne légale;Téléphone;Email;Inscrit au bulletin d'information;Adresse
Particulier;Durand;Paul;;600000000;durand.paul@yopmail.com;1;

+ 11
- 0
common/components/ActiveRecordCommon.php View File

$this->populateRelation($fieldObject, $object); $this->populateRelation($fieldObject, $object);
} }


public function triggerEvent(string $event, array $attributes)
{
$event = new $event();

foreach($attributes as $key => $value) {
$event->$key = $value;
}

$this->trigger($event::NAME, $event);
}

/** /**
* Méthode générique de recherche utilisée pour tous les modèles. Elle a * Méthode générique de recherche utilisée pour tous les modèles. Elle a
* pour but de construire la requête et de retourner le résultat. * pour but de construire la requête et de retourner le résultat.

+ 5
- 0
common/config/main.php View File

use common\logic\Order\Order\Model\Order; use common\logic\Order\Order\Model\Order;
use common\logic\Ticket\Ticket\Model\Ticket; use common\logic\Ticket\Ticket\Model\Ticket;
use common\logic\Payment\Model\Payment; use common\logic\Payment\Model\Payment;
use common\logic\User\User\Model\User;


$serverName = isset($_SERVER['SERVER_NAME']) ?? ''; $serverName = isset($_SERVER['SERVER_NAME']) ?? '';


'class' => \justcoded\yii2\eventlistener\components\EventListener::class, 'class' => \justcoded\yii2\eventlistener\components\EventListener::class,
'listeners' => [], 'listeners' => [],
'observers' => [ 'observers' => [
User::class => [
// Producer : lien avec utilisateur
common\logic\Producer\Producer\Event\UserObserver::class
],
Order::class => [ Order::class => [
// Payment : remboursement commande // Payment : remboursement commande
common\logic\Payment\Event\OrderObserver::class common\logic\Payment\Event\OrderObserver::class

+ 24
- 0
common/helpers/CSV.php View File

die(); die();
} }


public static function csv2array(string $filename = '', string $delimiter = ';')
{
if(!file_exists($filename) || !is_readable($filename)) {
return FALSE;
}

$header = null;
$data = array();
if (($handle = fopen($filename, 'r')) !== FALSE) {
while (($row = fgetcsv($handle, 1000, $delimiter)) !== FALSE) {
if(!$header) {
$header = $row;
}
else {
//$data[] = array_combine($header, $row);
$data[] = $row;
}
}
fclose($handle);
}

return $data;
}

public static function array2csv(array &$array) public static function array2csv(array &$array)
{ {
$producerModule = ProducerModule::getInstance(); $producerModule = ProducerModule::getInstance();

+ 1
- 1
common/logic/AbstractService.php View File

SolverInterface::class, SolverInterface::class,
RepositoryQueryInterface::class, RepositoryQueryInterface::class,
RepositoryInterface::class, RepositoryInterface::class,
BuilderInterface::class,
NotifierInterface::class, NotifierInterface::class,
BuilderInterface::class,
ResolverInterface::class, ResolverInterface::class,
GeneratorInterface::class, GeneratorInterface::class,
ManagerInterface::class, ManagerInterface::class,

+ 22
- 0
common/logic/Producer/Producer/Event/UserObserver.php View File

<?php
namespace common\logic\Producer\Producer\Event;

use common\logic\Producer\Producer\Module\ProducerModule;
use common\logic\User\User\Event\UserCreateEvent;
use justcoded\yii2\eventlistener\observers\Observer;

class UserObserver extends Observer
{
public function events()
{
return [
UserCreateEvent::NAME => 'onUserCreate'
];
}

public function onUserCreate(UserCreateEvent $event)
{
ProducerModule::getInstance()->getBuilder()
->addUser($event->user, $event->producer, true, $event->newsletter);
}
}

+ 1
- 1
common/logic/Producer/Producer/Service/ProducerBuilder.php View File

*/ */
public function addUser(User $user, Producer $producer, bool $bookmark = true, bool $newsletter = true): UserProducer public function addUser(User $user, Producer $producer, bool $bookmark = true, bool $newsletter = true): UserProducer
{ {
$userProducer = $this->userProducerBuilder->createUserProducerIfNotExist($user, $producer, $bookmark, $newsletter);
$userProducer = $this->userProducerBuilder->createUserProducerIfNotExist($user, $producer, $bookmark, $newsletter);


if (!$userProducer->getActive()) { if (!$userProducer->getActive()) {
$userProducer->setActive(true); $userProducer->setActive(true);

+ 2
- 4
common/logic/User/User/Event/TicketObserver.php View File

{ {
public function inserted(AfterSaveEvent $event) public function inserted(AfterSaveEvent $event)
{ {
$ticket = $event->sender;
$userModule = UserModule::getInstance();

$userModule->sendMailNewTicketAdmin($ticket);
UserModule::getInstance()->getNotifier()
->sendMailNewTicketAdmin($event->sender);
} }
} }

+ 16
- 0
common/logic/User/User/Event/UserCreateEvent.php View File

<?php

namespace common\logic\User\User\Event;

use common\logic\Producer\Producer\Model\Producer;
use common\logic\User\User\Model\User;
use yii\base\Event;

class UserCreateEvent extends Event
{
const NAME = 'user.create.event';

public User $user;
public Producer $producer;
public bool $newsletter;
}

+ 2
- 0
common/logic/User/User/Model/User.php View File

*/ */
class User extends ActiveRecordCommon implements IdentityInterface class User extends ActiveRecordCommon implements IdentityInterface
{ {
const EVENT_CREATE = 'user.event.create';

const TYPE_INDIVIDUAL = 'individual'; const TYPE_INDIVIDUAL = 'individual';
const TYPE_LEGAL_PERSON = 'legal-person'; const TYPE_LEGAL_PERSON = 'legal-person';
const TYPE_GUEST = 'guest'; const TYPE_GUEST = 'guest';

+ 8
- 0
common/logic/User/User/Module/UserModule.php View File

use common\logic\User\User\Service\AuthorizationChecker; use common\logic\User\User\Service\AuthorizationChecker;
use common\logic\User\User\Service\NewsletterManager; use common\logic\User\User\Service\NewsletterManager;
use common\logic\User\User\Service\UserBuilder; use common\logic\User\User\Service\UserBuilder;
use common\logic\User\User\Service\UserBulkImporter;
use common\logic\User\User\Service\UserDefinition; use common\logic\User\User\Service\UserDefinition;
use common\logic\User\User\Service\UserNotifier; use common\logic\User\User\Service\UserNotifier;
use common\logic\User\User\Service\UsersCreditCsvGenerator; use common\logic\User\User\Service\UsersCreditCsvGenerator;
* @mixin UsersCreditCsvGenerator * @mixin UsersCreditCsvGenerator
* @mixin NewsletterManager * @mixin NewsletterManager
* @mixin AuthorizationChecker * @mixin AuthorizationChecker
* @mixin UserBulkImporter
*/ */
class UserModule extends AbstractModule class UserModule extends AbstractModule
{ {
UsersCreditCsvGenerator::class, UsersCreditCsvGenerator::class,
NewsletterManager::class, NewsletterManager::class,
AuthorizationChecker::class, AuthorizationChecker::class,
UserBulkImporter::class
]; ];
} }


{ {
return AuthorizationChecker::getInstance(); return AuthorizationChecker::getInstance();
} }

public function getBulkImporter(): UserBulkImporter
{
return UserBulkImporter::getInstance();
}
} }

+ 98
- 13
common/logic/User/User/Service/UserBuilder.php View File



namespace common\logic\User\User\Service; namespace common\logic\User\User\Service;


use common\helpers\Password;
use common\logic\AbstractBuilder; use common\logic\AbstractBuilder;
use common\logic\Order\Order\Event\OrderDeleteEvent;
use common\logic\Order\Order\Model\Order;
use common\logic\Producer\Producer\Model\Producer; use common\logic\Producer\Producer\Model\Producer;
use common\logic\User\User\Event\UserCreateEvent;
use common\logic\User\User\Model\User; use common\logic\User\User\Model\User;
use common\logic\User\User\Repository\UserRepository; use common\logic\User\User\Repository\UserRepository;
use common\logic\User\UserProducer\Service\UserProducerBuilder;


class UserBuilder extends AbstractBuilder class UserBuilder extends AbstractBuilder
{ {
protected UserRepository $userRepository; protected UserRepository $userRepository;
protected UserNotifier $userNotifier;
protected UserProducerBuilder $userProducerBuilder;


public function loadDependencies(): void public function loadDependencies(): void
{ {
$this->userRepository = $this->loadService(UserRepository::class); $this->userRepository = $this->loadService(UserRepository::class);
$this->userNotifier = $this->loadService(UserNotifier::class);
$this->userProducerBuilder = $this->loadService(UserProducerBuilder::class);
} }


public function instanciateUser(): User
public function instanciateUser(
string $type = null,
string $email = null,
string $firstname = null,
string $lastname = null,
string $nameLegalPerson = null,
string $phone = null,
string $address = null,
string $password = null
): User
{ {
$user = new User(); $user = new User();
$user->id_producer = 0;
$user->type = $type;
$this->initUsername($user, $email);
$user->email = $email;
$user->name = $firstname;
$user->lastname = $lastname;
$user->name_legal_person = $nameLegalPerson;
$user->phone = $phone;
$user->address = $address;
$this->initPassword($user, $password);


return $user; return $user;
} }


public function initPassword(User $user, string $password)
public function createUser(
string $type = null,
string $email = null,
string $firstname = null,
string $lastname = null,
string $nameLegalPerson = null,
string $phone = null,
string $address = null,
bool $newsletter = true,
string $password = null
): User
{ {
$this->setPassword($user, $password);
$this->generateAuthKey($user);
$userExist = null;
if($email && strlen($email)) {
$userExist = $this->userRepository->findOneUserByEmail($email);
$user = $userExist;
}

if(!$userExist) {
$user = $this->instanciateUser(
$type,
$email,
$firstname,
$lastname,
$nameLegalPerson,
$phone,
$address,
$password
);
$this->create($user);

$this->userNotifier->sendMailWelcome($user, $password);
}

$user->triggerEvent(
UserCreateEvent::class,
[
'user' => $user,
'producer' => $this->getProducerContext(),
'newsletter' => $newsletter
]
);

return $user;
} }


public function initProducer(User $user, Producer $producer)
public function initUsername(User $user, string $email = null)
{ {
$user->id_producer = $producer->id;
$user->status = User::STATUS_PRODUCER;
$user->username = $email;
if (!$email) {
$user->username = 'inconnu@opendistrib.net';
}
} }


/**
* Met à jour la date de dernière connexion de l'utilisateur.
*/
public function updateUserLastConnection(User $user)
public function initPassword(User $user, string $password = null)
{ {
$user->date_last_connection = date('Y-m-d H:i:s');
$user->save();
if(!$password) {
$password = Password::generate();
}
$this->setPassword($user, $password);
$this->generateAuthKey($user);
} }


/** /**
$user->auth_key = \Yii::$app->security->generateRandomString(); $user->auth_key = \Yii::$app->security->generateRandomString();
} }


public function initProducer(User $user, Producer $producer)
{
$user->id_producer = $producer->id;
$user->status = User::STATUS_PRODUCER;
}

/**
* Met à jour la date de dernière connexion de l'utilisateur.
*/
public function updateUserLastConnection(User $user)
{
$user->date_last_connection = date('Y-m-d H:i:s');
$user->save();
}

/** /**
* Generates new password reset token * Generates new password reset token
*/ */

+ 49
- 0
common/logic/User/User/Service/UserBulkImporter.php View File

<?php

namespace common\logic\User\User\Service;

use common\helpers\CSV;
use common\helpers\Password;
use common\logic\AbstractManager;
use common\logic\User\User\Model\User;
use common\logic\User\UserProducer\Model\UserProducer;

class UserBulkImporter extends AbstractManager
{
const TYPE = 0;
const LASTNAME = 1;
const FIRSTNAME = 2;
const NAME_LEGAL_PERSON = 3;
const PHONE = 4;
const EMAIL = 5;
const NEWSLETTER = 6;
const ADDRESS = 7;

protected UserSolver $userSolver;
protected UserBuilder $userBuilder;

public function loadDependencies(): void
{
$this->userSolver = $this->loadService(UserSolver::class);
$this->userBuilder = $this->loadService(UserBuilder::class);
}

public function import(string $fileName): void
{
$usersArray = CSV::csv2array($fileName);

foreach($usersArray as $userData) {
$this->userBuilder->createUser(
$this->userSolver->getTypeIndex($userData[self::TYPE]),
$userData[self::EMAIL],
$userData[self::FIRSTNAME],
$userData[self::LASTNAME],
$userData[self::NAME_LEGAL_PERSON],
$userData[self::PHONE],
$userData[self::ADDRESS],
$userData[self::NEWSLETTER],
Password::generate()
);
}
}
}

+ 1
- 1
common/logic/User/User/Service/UserNotifier.php View File

public function sendMailWelcome(User $user, string $password): void public function sendMailWelcome(User $user, string $password): void
{ {
if (strlen($user->email)) { if (strlen($user->email)) {
$producer = Producer::findOne(GlobalParam::getCurrentProducerId());
$producer = $this->getProducerContext();
$this->mailer->sendFromProducer( $this->mailer->sendFromProducer(
'Inscription', 'Inscription',
'createUserAdmin', 'createUserAdmin',

+ 11
- 0
common/logic/User/User/Service/UserSolver.php View File

return ''; return '';
} }


public function getTypeIndex(string $typeLabel = null): ?string
{
foreach($this->getTypeChoicesArray() as $key => $label) {
if($label == $typeLabel) {
return $key;
}
}

return null;
}

public function isTypeValid(string $type = null): bool public function isTypeValid(string $type = null): bool
{ {
return in_array($type, User::$types); return in_array($type, User::$types);

Loading…
Cancel
Save