diff --git a/composer.json b/composer.json index f3adddb..9f4428f 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name": "2amigos/yii2-usuario", + "name": "pcrt/yii2-usuario", "description": "Highly customizable and extensible user management, authentication, and authorization Yii2 extension", "type": "yii2-extension", "license": "BSD-3-Clause", @@ -41,7 +41,7 @@ "prefer-stable": true, "require": { "php": ">=5.5", - "2amigos/yii2-selectize-widget": "^1.1", + "pcrt/yii2-select2": "^1.0.6", "yiisoft/yii2-authclient": "^2.1", "yiisoft/yii2-httpclient": "^2.0", "yiisoft/yii2-bootstrap": "^2.0", diff --git a/src/User/Controller/AdminController.php b/src/User/Controller/AdminController.php index 05b1ca2..dd56b13 100755 --- a/src/User/Controller/AdminController.php +++ b/src/User/Controller/AdminController.php @@ -144,6 +144,8 @@ class AdminController extends Controller $this->make(AjaxRequestModelValidator::class, [$user])->validate(); if ($user->load(Yii::$app->request->post()) && $user->validate()) { + $user->created_by = Yii::$app->user->getId() ?? null; + $this->trigger(UserEvent::EVENT_BEFORE_CREATE, $event); $mailService = MailFactory::makeWelcomeMailerService($user); diff --git a/src/User/Controller/ProfileController.php b/src/User/Controller/ProfileController.php index da779b8..9d5e519 100644 --- a/src/User/Controller/ProfileController.php +++ b/src/User/Controller/ProfileController.php @@ -11,18 +11,47 @@ namespace Da\User\Controller; +use Da\User\Contracts\MailChangeStrategyInterface; +use Da\User\Event\GdprEvent; +use Da\User\Event\ProfileEvent; +use Da\User\Event\SocialNetworkConnectEvent; +use Da\User\Event\UserEvent; +use Da\User\Form\GdprDeleteForm; +use Da\User\Form\SettingsForm; +use Da\User\Helper\SecurityHelper; +use Da\User\Model\Profile; +use Da\User\Model\SocialNetworkAccount; use Da\User\Model\User; +use Da\User\Module; use Da\User\Query\ProfileQuery; +use Da\User\Query\SocialNetworkAccountQuery; +use Da\User\Query\UserQuery; +use Da\User\Search\SessionHistorySearch; +use Da\User\Service\EmailChangeService; +use Da\User\Service\SessionHistory\TerminateUserSessionsService; +use Da\User\Service\TwoFactorEmailCodeGeneratorService; +use Da\User\Service\TwoFactorQrCodeUriGeneratorService; +use Da\User\Service\TwoFactorSmsCodeGeneratorService; +use Da\User\Traits\ContainerAwareTrait; use Da\User\Traits\ModuleAwareTrait; +use Da\User\Validator\AjaxRequestModelValidator; +use Da\User\Validator\TwoFactorCodeValidator; +use Da\User\Validator\TwoFactorEmailValidator; +use Da\User\Validator\TwoFactorTextMessageValidator; use Yii; -use yii\base\Module; +use yii\base\DynamicModel; +use yii\base\InvalidParamException; use yii\filters\AccessControl; +use yii\filters\VerbFilter; +use yii\helpers\ArrayHelper; use yii\web\Controller; use yii\web\ForbiddenHttpException; use yii\web\NotFoundHttpException; +use yii\web\Response; class ProfileController extends Controller { + use ContainerAwareTrait; use ModuleAwareTrait; /** @var int will allow only profile owner */ @@ -34,19 +63,36 @@ class ProfileController extends Controller /** @var int will allow anyone, including guests */ public const PROFILE_VISIBILITY_PUBLIC = 3; + /** + * {@inheritdoc} + */ + public $defaultAction = 'profile'; + protected $profileQuery; + protected $userQuery; + protected $socialNetworkAccountQuery; /** - * ProfileController constructor. + * SettingsController constructor. * - * @param string $id - * @param Module $module - * @param ProfileQuery $profileQuery - * @param array $config + * @param string $id + * @param Module $module + * @param ProfileQuery $profileQuery + * @param UserQuery $userQuery + * @param SocialNetworkAccountQuery $socialNetworkAccountQuery + * @param array $config */ - public function __construct($id, Module $module, ProfileQuery $profileQuery, array $config = []) - { + public function __construct( + $id, + Module $module, + ProfileQuery $profileQuery, + UserQuery $userQuery, + SocialNetworkAccountQuery $socialNetworkAccountQuery, + array $config = [] + ) { $this->profileQuery = $profileQuery; + $this->userQuery = $userQuery; + $this->socialNetworkAccountQuery = $socialNetworkAccountQuery; parent::__construct($id, $module, $config); } @@ -56,71 +102,559 @@ class ProfileController extends Controller public function behaviors() { return [ + 'verbs' => [ + 'class' => VerbFilter::class, + 'actions' => [ + 'disconnect' => ['post'], + 'delete' => ['post'], + 'two-factor-disable' => ['post'], + 'terminate-sessions' => ['post'], + ], + ], 'access' => [ 'class' => AccessControl::class, 'rules' => [ [ 'allow' => true, - 'actions' => ['index'], + 'actions' => [ + 'profile', + 'account', + 'export', + 'networks', + 'privacy', + 'gdpr-consent', + 'gdpr-delete', + 'disconnect', + 'delete', + 'two-factor', + 'two-factor-enable', + 'two-factor-disable', + 'two-factor-mobile-phone' + ], 'roles' => ['@'], ], [ 'allow' => true, - 'actions' => ['show'], + 'actions' => ['confirm'], 'roles' => ['?', '@'], ], + [ + 'allow' => $this->getModule()->enableSessionHistory, + 'actions' => ['session-history', 'terminate-sessions'], + 'roles' => ['@'], + ], ], ], ]; } - public function actionIndex() + /** + * @throws \yii\base\InvalidConfigException + * @return string|Response + */ + public function actionProfile() { - return $this->redirect(['show', 'id' => Yii::$app->user->getId()]); - } - - public function actionShow($id) - { - $user = Yii::$app->user; - $id = (int) $id; - - /** @var ?User $identity */ - $identity = $user->getIdentity(); - - switch($this->module->profileVisibility) { - case static::PROFILE_VISIBILITY_OWNER: - if($identity === null || $id !== $user->getId()) { - throw new ForbiddenHttpException(); - } - break; - case static::PROFILE_VISIBILITY_ADMIN: - if($id === $user->getId() || ($identity !== null && $identity->getIsAdmin())) { - break; - } - throw new ForbiddenHttpException(); - case static::PROFILE_VISIBILITY_USERS: - if((!$user->getIsGuest())) { - break; - } - throw new ForbiddenHttpException(); - case static::PROFILE_VISIBILITY_PUBLIC: - break; - default: - throw new ForbiddenHttpException(); - - } - - $profile = $this->profileQuery->whereUserId($id)->one(); + $profile = $this->profileQuery->whereUserId(Yii::$app->user->identity->getId())->one(); if ($profile === null) { - throw new NotFoundHttpException(); + $profile = $this->make(Profile::class); + $profile->link('user', Yii::$app->user->identity); } + /** + * + * + * @var ProfileEvent $event + */ + $event = $this->make(ProfileEvent::class, [$profile]); + + $this->make(AjaxRequestModelValidator::class, [$profile])->validate(); + + if ($profile->load(Yii::$app->request->post())) { + $this->trigger(UserEvent::EVENT_BEFORE_PROFILE_UPDATE, $event); + if ($profile->save()) { + Yii::$app->getSession()->setFlash('success', Yii::t('usuario', 'Your profile has been updated')); + $this->trigger(UserEvent::EVENT_AFTER_PROFILE_UPDATE, $event); + + return $this->refresh(); + } + } + + Yii::debug(get_class(Yii::$app->session)); + return $this->render( - 'show', + 'profile', [ - 'profile' => $profile, + 'model' => $profile, ] ); } -} + + /** + * @throws NotFoundHttpException + * @return string + */ + public function actionPrivacy() + { + if (!$this->module->enableGdprCompliance) { + throw new NotFoundHttpException(); + } + return $this->render( + 'privacy', + [ + 'module' => $this->module + ] + ); + } + + /** + * @throws NotFoundHttpException + * @throws \Throwable + * @throws \yii\base\Exception + * @throws \yii\base\InvalidConfigException + * @throws \yii\db\StaleObjectException + * @throws ForbiddenHttpException + * @return string|Response + */ + public function actionGdprDelete() + { + if (!$this->module->enableGdprCompliance) { + throw new NotFoundHttpException(); + } + /** + * + * + * @var GdprDeleteForm $form + */ + $form = $this->make(GdprDeleteForm::class); + + $user = $form->getUser(); + /* @var $event GdprEvent */ + $event = $this->make(GdprEvent::class, [$user]); + + if ($form->load(Yii::$app->request->post()) && $form->validate()) { + $this->trigger(GdprEvent::EVENT_BEFORE_DELETE, $event); + + if ($event->isValid) { + Yii::$app->user->logout(); + //Disconnect social networks + $networks = $this->socialNetworkAccountQuery->where(['user_id' => $user->id])->all(); + foreach ($networks as $network) { + $this->disconnectSocialNetwork($network->id); + } + + /* @var $security SecurityHelper */ + $security = $this->make(SecurityHelper::class); + $anonymReplacement = $this->module->gdprAnonymizePrefix . $user->id; + + $user->updateAttributes( + [ + 'email' => $anonymReplacement . "@example.com", + 'username' => $anonymReplacement, + 'gdpr_deleted' => 1, + 'blocked_at' => time(), + 'auth_key' => $security->generateRandomString() + ] + ); + $user->profile->updateAttributes( + [ + 'public_email' => $anonymReplacement . "@example.com", + 'name' => $anonymReplacement, + 'surname' => $anonymReplacement, + 'gravatar_email' => $anonymReplacement . "@example.com", + 'location' => $anonymReplacement, + 'website' => $anonymReplacement . ".tld", + 'bio' => Yii::t('usuario', 'Deleted by GDPR request') + ] + ); + } + $this->trigger(GdprEvent::EVENT_AFTER_DELETE, $event); + + Yii::$app->session->setFlash('info', Yii::t('usuario', 'Your personal information has been removed')); + + return $this->goHome(); + } + + return $this->render( + 'gdpr-delete', + [ + 'model' => $form, + ] + ); + } + + public function actionGdprConsent() + { + /** + * + * + * @var User $user + */ + $user = Yii::$app->user->identity; + if ($user->gdpr_consent) { + return $this->redirect(['profile']); + } + $model = new DynamicModel(['gdpr_consent']); + $model->addRule('gdpr_consent', 'boolean'); + $model->addRule('gdpr_consent', 'default', ['value' => 0, 'skipOnEmpty' => false]); + $model->addRule( + 'gdpr_consent', + 'compare', + [ + 'compareValue' => true, + 'message' => Yii::t('usuario', 'Your consent is required to work with this site'), + 'when' => function () { + return $this->module->enableGdprCompliance; + }, + ] + ); + if ($model->load(Yii::$app->request->post()) && $model->validate()) { + $user->updateAttributes( + [ + 'gdpr_consent' => 1, + 'gdpr_consent_date' => time(), + ] + ); + return $this->redirect(['profile']); + } + + return $this->render( + 'gdpr-consent', + [ + 'model' => $model, + 'gdpr_consent_hint' => $this->module->getConsentMessage(), + ] + ); + } + + /** + * Exports the data from the current user in a mechanical readable format (csv). Properties exported can be defined + * in the module configuration. + * + * @throws NotFoundHttpException if gdpr compliance is not enabled + * @throws \Exception + * @throws \Throwable + */ + public function actionExport() + { + if (!$this->module->enableGdprCompliance) { + throw new NotFoundHttpException(); + } + try { + $properties = $this->module->gdprExportProperties; + $user = Yii::$app->user->identity; + $data = [$properties, []]; + + $formatter = Yii::$app->formatter; + // override the default html-specific format for nulls + $formatter->nullDisplay = ""; + + foreach ($properties as $property) { + $data[1][] = $formatter->asText(ArrayHelper::getValue($user, $property)); + } + + array_walk($data[0], function (&$value, $key) { + $splitted = explode('.', $value); + $value = array_pop($splitted); + }); + + Yii::$app->response->headers->removeAll(); + Yii::$app->response->headers->add('Content-type', 'text/csv'); + Yii::$app->response->headers->add('Content-Disposition', 'attachment;filename=gdpr-data.csv'); + Yii::$app->response->send(); + $f = fopen('php://output', 'w'); + foreach ($data as $line) { + fputcsv($f, $line); + } + } catch (\Exception $e) { + throw $e; + } catch (\Throwable $e) { + throw $e; + } + } + + public function actionAccount() + { + /** +* + * + * @var SettingsForm $form +*/ + $form = $this->make(SettingsForm::class); + $event = $this->make(UserEvent::class, [$form->getUser()]); + + $this->make(AjaxRequestModelValidator::class, [$form])->validate(); + + if ($form->load(Yii::$app->request->post())) { + $this->trigger(UserEvent::EVENT_BEFORE_ACCOUNT_UPDATE, $event); + + if ($form->save()) { + Yii::$app->getSession()->setFlash( + 'success', + Yii::t('usuario', 'Your account details have been updated') + ); + $this->trigger(UserEvent::EVENT_AFTER_ACCOUNT_UPDATE, $event); + + return $this->refresh(); + } + } + + return $this->render( + 'account', + [ + 'model' => $form, + ] + ); + } + + public function actionConfirm($id, $code) + { + $user = $this->userQuery->whereId($id)->one(); + + if ($user === null || MailChangeStrategyInterface::TYPE_INSECURE === $this->module->emailChangeStrategy) { + throw new NotFoundHttpException(); + } + $event = $this->make(UserEvent::class, [$user]); + + $this->trigger(UserEvent::EVENT_BEFORE_CONFIRMATION, $event); + if ($this->make(EmailChangeService::class, [$code, $user])->run()) { + $this->trigger(UserEvent::EVENT_AFTER_CONFIRMATION, $event); + } + + return $this->redirect(['account']); + } + + public function actionNetworks() + { + return $this->render( + 'networks', + [ + 'user' => Yii::$app->user->identity, + ] + ); + } + + public function actionDisconnect($id) + { + $this->disconnectSocialNetwork($id); + return $this->redirect(['networks']); + } + + public function actionDelete() + { + if (!$this->module->allowAccountDelete) { + throw new NotFoundHttpException(Yii::t('usuario', 'Not found')); + } + + /** + * + * + * @var User $user + */ + $user = Yii::$app->user->identity; + $event = $this->make(UserEvent::class, [$user]); + Yii::$app->user->logout(); + + $this->trigger(UserEvent::EVENT_BEFORE_DELETE, $event); + $user->delete(); + $this->trigger(UserEvent::EVENT_AFTER_DELETE, $event); + + Yii::$app->session->setFlash('info', Yii::t('usuario', 'Your account has been completely deleted')); + + return $this->goHome(); + } + + public function actionTwoFactor($id) + { + if (!$this->module->enableTwoFactorAuthentication) { + throw new ForbiddenHttpException(Yii::t('usuario', 'Application not configured for two factor authentication.')); + } + + if ($id != Yii::$app->user->id) { + throw new ForbiddenHttpException(); + } + + $choice = Yii::$app->request->post('choice'); + /** @var User $user */ + $user = $this->userQuery->whereId($id)->one(); + + if (null === $user) { + throw new NotFoundHttpException(); + } + + switch ($choice) { + case 'google-authenticator': + $uri = $this->make(TwoFactorQrCodeUriGeneratorService::class, [$user])->run(); + return $this->renderAjax('two-factor', ['id' => $id, 'uri' => $uri, 'user' => $user]); + case 'email': + $emailCode = $this->make(TwoFactorEmailCodeGeneratorService::class, [$user])->run(); + return $this->renderAjax('two-factor-email', ['id' => $id, 'code' => $emailCode]); + case 'sms': + // get mobile phone, if exists + $mobilePhone = $user->getAuthTfMobilePhone(); + $smsCode = $this->make(TwoFactorSmsCodeGeneratorService::class, [$user])->run(); + return $this->renderAjax('two-factor-sms', ['id' => $id, 'code' => $smsCode, 'mobilePhone' => $mobilePhone]); + default: + throw new InvalidParamException("Invalid 2FA choice"); + } + } + + public function actionTwoFactorEnable($id) + { + if (!$this->module->enableTwoFactorAuthentication) { + throw new ForbiddenHttpException(Yii::t('usuario', 'Application not configured for two factor authentication.')); + } + + Yii::$app->response->format = Response::FORMAT_JSON; + + /** @var User $user */ + $user = $this->userQuery->whereId($id)->one(); + + if (null === $user) { + return [ + 'success' => false, + 'message' => Yii::t('usuario', 'User not found.') + ]; + } + $code = Yii::$app->request->get('code'); + $module = Yii::$app->getModule('user'); + $validators = $module->twoFactorAuthenticationValidators; + $choice = Yii::$app->request->get('choice'); + $codeDurationTime = ArrayHelper::getValue($validators, $choice.'.codeDurationTime', 300); + $class = ArrayHelper::getValue($validators, $choice.'.class'); + + $object = $this + ->make($class, [$user, $code, $this->module->twoFactorAuthenticationCycles]); + $success = $object->validate(); + $success = $success && $user->updateAttributes(['auth_tf_enabled' => '1','auth_tf_type' => $choice]); + $message = $success ? $object->getSuccessMessage() : $object->getUnsuccessMessage($codeDurationTime); + + return [ + 'success' => $success, + 'message' => $message + ]; + } + + public function actionTwoFactorDisable($id) + { + if (!$this->module->enableTwoFactorAuthentication) { + throw new ForbiddenHttpException(Yii::t('usuario', 'Application not configured for two factor authentication.')); + } + + if ($id != Yii::$app->user->id) { + throw new ForbiddenHttpException(); + } + + /** + * @var User $user + */ + $user = $this->userQuery->whereId($id)->one(); + + if (null === $user) { + throw new NotFoundHttpException(); + } + + if ($user->updateAttributes(['auth_tf_enabled' => '0', 'auth_tf_key' => null])) { + Yii::$app + ->getSession() + ->setFlash('success', Yii::t('usuario', 'Two factor authentication has been disabled.')); + } else { + Yii::$app + ->getSession() + ->setFlash('danger', Yii::t('usuario', 'Unable to disable Two factor authentication.')); + } + + $this->redirect(['account']); + } + + /** + * Display list session history. + */ + public function actionSessionHistory() + { + $searchModel = new SessionHistorySearch([ + 'user_id' => Yii::$app->user->id, + ]); + $dataProvider = $searchModel->search(Yii::$app->request->queryParams); + + return $this->render('session-history', [ + 'searchModel' => $searchModel, + 'dataProvider' => $dataProvider, + ]); + } + + /** + * Terminate all session user + */ + public function actionTerminateSessions() + { + $this->make(TerminateUserSessionsService::class, [Yii::$app->user->id])->run(); + + return $this->redirect(['session-history']); + } + + public function actionTwoFactorMobilePhone($id) + { + Yii::$app->response->format = Response::FORMAT_JSON; + + /** + * + * + * @var User $user + */ + $user = $this->userQuery->whereId($id)->one(); + + if (null === $user) { + return [ + 'success' => false, + 'message' => Yii::t('usuario', 'User not found.') + ]; + } + $mobilePhone = Yii::$app->request->get('mobilephone'); + $currentMobilePhone = $user->getAuthTfMobilePhone(); + $success = false; + if ($currentMobilePhone == $mobilePhone) { + $success = true; + } else { + $success = $user->updateAttributes(['auth_tf_mobile_phone' => $mobilePhone]); + $success = $success && $this->make(TwoFactorSmsCodeGeneratorService::class, [$user])->run(); + } + + return [ + 'success' => $success, + 'message' => $success + ? Yii::t('usuario', 'Mobile phone number successfully enabled.') + : Yii::t('usuario', 'Error while enabling SMS two factor authentication. Please reload the page.'), + ]; + } + + /** + * @param $id + * @throws ForbiddenHttpException + * @throws NotFoundHttpException + * @throws \Exception + * @throws \Throwable + * @throws \yii\db\StaleObjectException + */ + protected function disconnectSocialNetwork($id) + { + /** + * + * + * @var SocialNetworkAccount $account + */ + $account = $this->socialNetworkAccountQuery->whereId($id)->one(); + + if ($account === null) { + throw new NotFoundHttpException(); + } + if ($account->user_id !== Yii::$app->user->id) { + throw new ForbiddenHttpException(); + } + $event = $this->make(SocialNetworkConnectEvent::class, [Yii::$app->user->identity, $account]); + + $this->trigger(SocialNetworkConnectEvent::EVENT_BEFORE_DISCONNECT, $event); + $account->delete(); + $this->trigger(SocialNetworkConnectEvent::EVENT_AFTER_DISCONNECT, $event); + } +} \ No newline at end of file diff --git a/src/User/Controller/RecoveryController.php b/src/User/Controller/RecoveryController.php index 2367ef2..f02b217 100644 --- a/src/User/Controller/RecoveryController.php +++ b/src/User/Controller/RecoveryController.php @@ -109,13 +109,16 @@ class RecoveryController extends Controller $this->trigger(FormEvent::EVENT_AFTER_REQUEST, $event); } - return $this->render( - '/shared/message', - [ - 'title' => Yii::t('usuario', 'Recovery message sent'), - 'module' => $this->module, - ] - ); + Yii::$app->session->setFlash('info', Yii::t('usuario', 'Recovery message sent')); + return $this->redirect(['/user/login']); + + // return $this->render( + // '/shared/message', + // [ + // 'title' => Yii::t('usuario', 'Recovery message sent'), + // 'module' => $this->module, + // ] + // ); } return $this->render('request', ['model' => $form]); @@ -151,13 +154,14 @@ class RecoveryController extends Controller Yii::t('usuario', 'Recovery link is invalid or expired. Please try requesting a new one.') ); - return $this->render( - '/shared/message', - [ - 'title' => Yii::t('usuario', 'Invalid or expired link'), - 'module' => $this->module, - ] - ); + // return $this->render( + // '/shared/message', + // [ + // 'title' => Yii::t('usuario', 'Invalid or expired link'), + // 'module' => $this->module, + // ] + // ); + return $this->redirect(['/user/recovery/request']); } /** @var RecoveryForm $form */ @@ -172,13 +176,15 @@ class RecoveryController extends Controller Yii::$app->session->setFlash('success', Yii::t('usuario', 'Password has been changed')); - return $this->render( - '/shared/message', - [ - 'title' => Yii::t('usuario', 'Password has been changed'), - 'module' => $this->module, - ] - ); + // return $this->render( + // '/shared/message', + // [ + // 'title' => Yii::t('usuario', 'Password has been changed'), + // 'module' => $this->module, + // ] + // ); + + return $this->redirect(['/user/login']); } } diff --git a/src/User/Controller/RegistrationController.php b/src/User/Controller/RegistrationController.php index aa5f3b8..17bc990 100644 --- a/src/User/Controller/RegistrationController.php +++ b/src/User/Controller/RegistrationController.php @@ -135,13 +135,7 @@ class RegistrationController extends Controller Yii::$app->session->setFlash('info', Yii::t('usuario', 'Your account has been created')); } $this->trigger(FormEvent::EVENT_AFTER_REGISTER, $event); - return $this->render( - '/shared/message', - [ - 'title' => Yii::t('usuario', 'Your account has been created'), - 'module' => $this->module, - ] - ); + return $this->redirect(['/user/login']); } Yii::$app->session->setFlash('danger', Yii::t('usuario', 'User could not be registered.')); } else { @@ -234,13 +228,14 @@ class RegistrationController extends Controller ); } - return $this->render( - '/shared/message', - [ - 'title' => Yii::t('usuario', 'Account confirmation'), - 'module' => $this->module, - ] - ); + // return $this->render( + // '/shared/message', + // [ + // 'title' => Yii::t('usuario', 'Account confirmation'), + // 'module' => $this->module, + // ] + // ); + return $this->redirect(['/user/profile']); } /** @@ -285,15 +280,17 @@ class RegistrationController extends Controller ); } - return $this->render( - '/shared/message', - [ - 'title' => $success - ? Yii::t('usuario', 'A new confirmation link has been sent') - : Yii::t('usuario', 'Unable to send confirmation link'), - 'module' => $this->module, - ] - ); + return $this->redirect(['/user/login']); + + // return $this->render( + // '/shared/message', + // [ + // 'title' => $success + // ? Yii::t('usuario', 'A new confirmation link has been sent') + // : Yii::t('usuario', 'Unable to send confirmation link'), + // 'module' => $this->module, + // ] + // ); } return $this->render( diff --git a/src/User/Controller/SettingsController.php b/src/User/Controller/SettingsController.php deleted file mode 100644 index a96040b..0000000 --- a/src/User/Controller/SettingsController.php +++ /dev/null @@ -1,648 +0,0 @@ - - * - * For the full copyright and license information, please view - * the LICENSE file that was distributed with this source code. - */ - -namespace Da\User\Controller; - -use Da\User\Contracts\MailChangeStrategyInterface; -use Da\User\Event\GdprEvent; -use Da\User\Event\ProfileEvent; -use Da\User\Event\SocialNetworkConnectEvent; -use Da\User\Event\UserEvent; -use Da\User\Form\GdprDeleteForm; -use Da\User\Form\SettingsForm; -use Da\User\Helper\SecurityHelper; -use Da\User\Model\Profile; -use Da\User\Model\SocialNetworkAccount; -use Da\User\Model\User; -use Da\User\Module; -use Da\User\Query\ProfileQuery; -use Da\User\Query\SocialNetworkAccountQuery; -use Da\User\Query\UserQuery; -use Da\User\Search\SessionHistorySearch; -use Da\User\Service\EmailChangeService; -use Da\User\Service\SessionHistory\TerminateUserSessionsService; -use Da\User\Service\TwoFactorEmailCodeGeneratorService; -use Da\User\Service\TwoFactorQrCodeUriGeneratorService; -use Da\User\Service\TwoFactorSmsCodeGeneratorService; -use Da\User\Traits\ContainerAwareTrait; -use Da\User\Traits\ModuleAwareTrait; -use Da\User\Validator\AjaxRequestModelValidator; -use Da\User\Validator\TwoFactorCodeValidator; -use Da\User\Validator\TwoFactorEmailValidator; -use Da\User\Validator\TwoFactorTextMessageValidator; -use Yii; -use yii\base\DynamicModel; -use yii\base\InvalidParamException; -use yii\filters\AccessControl; -use yii\filters\VerbFilter; -use yii\helpers\ArrayHelper; -use yii\web\Controller; -use yii\web\ForbiddenHttpException; -use yii\web\NotFoundHttpException; -use yii\web\Response; - -class SettingsController extends Controller -{ - use ContainerAwareTrait; - use ModuleAwareTrait; - - /** - * {@inheritdoc} - */ - public $defaultAction = 'profile'; - - protected $profileQuery; - protected $userQuery; - protected $socialNetworkAccountQuery; - - /** - * SettingsController constructor. - * - * @param string $id - * @param Module $module - * @param ProfileQuery $profileQuery - * @param UserQuery $userQuery - * @param SocialNetworkAccountQuery $socialNetworkAccountQuery - * @param array $config - */ - public function __construct( - $id, - Module $module, - ProfileQuery $profileQuery, - UserQuery $userQuery, - SocialNetworkAccountQuery $socialNetworkAccountQuery, - array $config = [] - ) { - $this->profileQuery = $profileQuery; - $this->userQuery = $userQuery; - $this->socialNetworkAccountQuery = $socialNetworkAccountQuery; - parent::__construct($id, $module, $config); - } - - /** - * {@inheritdoc} - */ - public function behaviors() - { - return [ - 'verbs' => [ - 'class' => VerbFilter::class, - 'actions' => [ - 'disconnect' => ['post'], - 'delete' => ['post'], - 'two-factor-disable' => ['post'], - 'terminate-sessions' => ['post'], - ], - ], - 'access' => [ - 'class' => AccessControl::class, - 'rules' => [ - [ - 'allow' => true, - 'actions' => [ - 'profile', - 'account', - 'export', - 'networks', - 'privacy', - 'gdpr-consent', - 'gdpr-delete', - 'disconnect', - 'delete', - 'two-factor', - 'two-factor-enable', - 'two-factor-disable', - 'two-factor-mobile-phone' - ], - 'roles' => ['@'], - ], - [ - 'allow' => true, - 'actions' => ['confirm'], - 'roles' => ['?', '@'], - ], - [ - 'allow' => $this->getModule()->enableSessionHistory, - 'actions' => ['session-history', 'terminate-sessions'], - 'roles' => ['@'], - ], - ], - ], - ]; - } - - /** - * @throws \yii\base\InvalidConfigException - * @return string|Response - */ - public function actionProfile() - { - $profile = $this->profileQuery->whereUserId(Yii::$app->user->identity->getId())->one(); - - if ($profile === null) { - $profile = $this->make(Profile::class); - $profile->link('user', Yii::$app->user->identity); - } - - /** - * - * - * @var ProfileEvent $event - */ - $event = $this->make(ProfileEvent::class, [$profile]); - - $this->make(AjaxRequestModelValidator::class, [$profile])->validate(); - - if ($profile->load(Yii::$app->request->post())) { - $this->trigger(UserEvent::EVENT_BEFORE_PROFILE_UPDATE, $event); - if ($profile->save()) { - Yii::$app->getSession()->setFlash('success', Yii::t('usuario', 'Your profile has been updated')); - $this->trigger(UserEvent::EVENT_AFTER_PROFILE_UPDATE, $event); - - return $this->refresh(); - } - } - - return $this->render( - 'profile', - [ - 'model' => $profile, - ] - ); - } - - /** - * @throws NotFoundHttpException - * @return string - */ - public function actionPrivacy() - { - if (!$this->module->enableGdprCompliance) { - throw new NotFoundHttpException(); - } - return $this->render( - 'privacy', - [ - 'module' => $this->module - ] - ); - } - - /** - * @throws NotFoundHttpException - * @throws \Throwable - * @throws \yii\base\Exception - * @throws \yii\base\InvalidConfigException - * @throws \yii\db\StaleObjectException - * @throws ForbiddenHttpException - * @return string|Response - */ - public function actionGdprDelete() - { - if (!$this->module->enableGdprCompliance) { - throw new NotFoundHttpException(); - } - /** - * - * - * @var GdprDeleteForm $form - */ - $form = $this->make(GdprDeleteForm::class); - - $user = $form->getUser(); - /* @var $event GdprEvent */ - $event = $this->make(GdprEvent::class, [$user]); - - if ($form->load(Yii::$app->request->post()) && $form->validate()) { - $this->trigger(GdprEvent::EVENT_BEFORE_DELETE, $event); - - if ($event->isValid) { - Yii::$app->user->logout(); - //Disconnect social networks - $networks = $this->socialNetworkAccountQuery->where(['user_id' => $user->id])->all(); - foreach ($networks as $network) { - $this->disconnectSocialNetwork($network->id); - } - - /* @var $security SecurityHelper */ - $security = $this->make(SecurityHelper::class); - $anonymReplacement = $this->module->gdprAnonymizePrefix . $user->id; - - $user->updateAttributes( - [ - 'email' => $anonymReplacement . "@example.com", - 'username' => $anonymReplacement, - 'gdpr_deleted' => 1, - 'blocked_at' => time(), - 'auth_key' => $security->generateRandomString() - ] - ); - $user->profile->updateAttributes( - [ - 'public_email' => $anonymReplacement . "@example.com", - 'name' => $anonymReplacement, - 'gravatar_email' => $anonymReplacement . "@example.com", - 'location' => $anonymReplacement, - 'website' => $anonymReplacement . ".tld", - 'bio' => Yii::t('usuario', 'Deleted by GDPR request') - ] - ); - } - $this->trigger(GdprEvent::EVENT_AFTER_DELETE, $event); - - Yii::$app->session->setFlash('info', Yii::t('usuario', 'Your personal information has been removed')); - - return $this->goHome(); - } - - return $this->render( - 'gdpr-delete', - [ - 'model' => $form, - ] - ); - } - - public function actionGdprConsent() - { - /** - * - * - * @var User $user - */ - $user = Yii::$app->user->identity; - if ($user->gdpr_consent) { - return $this->redirect(['profile']); - } - $model = new DynamicModel(['gdpr_consent']); - $model->addRule('gdpr_consent', 'boolean'); - $model->addRule('gdpr_consent', 'default', ['value' => 0, 'skipOnEmpty' => false]); - $model->addRule( - 'gdpr_consent', - 'compare', - [ - 'compareValue' => true, - 'message' => Yii::t('usuario', 'Your consent is required to work with this site'), - 'when' => function () { - return $this->module->enableGdprCompliance; - }, - ] - ); - if ($model->load(Yii::$app->request->post()) && $model->validate()) { - $user->updateAttributes( - [ - 'gdpr_consent' => 1, - 'gdpr_consent_date' => time(), - ] - ); - return $this->redirect(['profile']); - } - - return $this->render( - 'gdpr-consent', - [ - 'model' => $model, - 'gdpr_consent_hint' => $this->module->getConsentMessage(), - ] - ); - } - - /** - * Exports the data from the current user in a mechanical readable format (csv). Properties exported can be defined - * in the module configuration. - * - * @throws NotFoundHttpException if gdpr compliance is not enabled - * @throws \Exception - * @throws \Throwable - */ - public function actionExport() - { - if (!$this->module->enableGdprCompliance) { - throw new NotFoundHttpException(); - } - try { - $properties = $this->module->gdprExportProperties; - $user = Yii::$app->user->identity; - $data = [$properties, []]; - - $formatter = Yii::$app->formatter; - // override the default html-specific format for nulls - $formatter->nullDisplay = ""; - - foreach ($properties as $property) { - $data[1][] = $formatter->asText(ArrayHelper::getValue($user, $property)); - } - - array_walk($data[0], function (&$value, $key) { - $splitted = explode('.', $value); - $value = array_pop($splitted); - }); - - Yii::$app->response->headers->removeAll(); - Yii::$app->response->headers->add('Content-type', 'text/csv'); - Yii::$app->response->headers->add('Content-Disposition', 'attachment;filename=gdpr-data.csv'); - Yii::$app->response->send(); - $f = fopen('php://output', 'w'); - foreach ($data as $line) { - fputcsv($f, $line); - } - } catch (\Exception $e) { - throw $e; - } catch (\Throwable $e) { - throw $e; - } - } - - public function actionAccount() - { - /** -* - * - * @var SettingsForm $form -*/ - $form = $this->make(SettingsForm::class); - $event = $this->make(UserEvent::class, [$form->getUser()]); - - $this->make(AjaxRequestModelValidator::class, [$form])->validate(); - - if ($form->load(Yii::$app->request->post())) { - $this->trigger(UserEvent::EVENT_BEFORE_ACCOUNT_UPDATE, $event); - - if ($form->save()) { - Yii::$app->getSession()->setFlash( - 'success', - Yii::t('usuario', 'Your account details have been updated') - ); - $this->trigger(UserEvent::EVENT_AFTER_ACCOUNT_UPDATE, $event); - - return $this->refresh(); - } - } - - return $this->render( - 'account', - [ - 'model' => $form, - ] - ); - } - - public function actionConfirm($id, $code) - { - $user = $this->userQuery->whereId($id)->one(); - - if ($user === null || MailChangeStrategyInterface::TYPE_INSECURE === $this->module->emailChangeStrategy) { - throw new NotFoundHttpException(); - } - $event = $this->make(UserEvent::class, [$user]); - - $this->trigger(UserEvent::EVENT_BEFORE_CONFIRMATION, $event); - if ($this->make(EmailChangeService::class, [$code, $user])->run()) { - $this->trigger(UserEvent::EVENT_AFTER_CONFIRMATION, $event); - } - - return $this->redirect(['account']); - } - - public function actionNetworks() - { - return $this->render( - 'networks', - [ - 'user' => Yii::$app->user->identity, - ] - ); - } - - public function actionDisconnect($id) - { - $this->disconnectSocialNetwork($id); - return $this->redirect(['networks']); - } - - public function actionDelete() - { - if (!$this->module->allowAccountDelete) { - throw new NotFoundHttpException(Yii::t('usuario', 'Not found')); - } - - /** - * - * - * @var User $user - */ - $user = Yii::$app->user->identity; - $event = $this->make(UserEvent::class, [$user]); - Yii::$app->user->logout(); - - $this->trigger(UserEvent::EVENT_BEFORE_DELETE, $event); - $user->delete(); - $this->trigger(UserEvent::EVENT_AFTER_DELETE, $event); - - Yii::$app->session->setFlash('info', Yii::t('usuario', 'Your account has been completely deleted')); - - return $this->goHome(); - } - - public function actionTwoFactor($id) - { - if (!$this->module->enableTwoFactorAuthentication) { - throw new ForbiddenHttpException(Yii::t('usuario', 'Application not configured for two factor authentication.')); - } - - if ($id != Yii::$app->user->id) { - throw new ForbiddenHttpException(); - } - - $choice = Yii::$app->request->post('choice'); - /** @var User $user */ - $user = $this->userQuery->whereId($id)->one(); - - if (null === $user) { - throw new NotFoundHttpException(); - } - - switch ($choice) { - case 'google-authenticator': - $uri = $this->make(TwoFactorQrCodeUriGeneratorService::class, [$user])->run(); - return $this->renderAjax('two-factor', ['id' => $id, 'uri' => $uri, 'user' => $user]); - case 'email': - $emailCode = $this->make(TwoFactorEmailCodeGeneratorService::class, [$user])->run(); - return $this->renderAjax('two-factor-email', ['id' => $id, 'code' => $emailCode]); - case 'sms': - // get mobile phone, if exists - $mobilePhone = $user->getAuthTfMobilePhone(); - $smsCode = $this->make(TwoFactorSmsCodeGeneratorService::class, [$user])->run(); - return $this->renderAjax('two-factor-sms', ['id' => $id, 'code' => $smsCode, 'mobilePhone' => $mobilePhone]); - default: - throw new InvalidParamException("Invalid 2FA choice"); - } - } - - public function actionTwoFactorEnable($id) - { - if (!$this->module->enableTwoFactorAuthentication) { - throw new ForbiddenHttpException(Yii::t('usuario', 'Application not configured for two factor authentication.')); - } - - Yii::$app->response->format = Response::FORMAT_JSON; - - /** @var User $user */ - $user = $this->userQuery->whereId($id)->one(); - - if (null === $user) { - return [ - 'success' => false, - 'message' => Yii::t('usuario', 'User not found.') - ]; - } - $code = Yii::$app->request->get('code'); - $module = Yii::$app->getModule('user'); - $validators = $module->twoFactorAuthenticationValidators; - $choice = Yii::$app->request->get('choice'); - $codeDurationTime = ArrayHelper::getValue($validators, $choice.'.codeDurationTime', 300); - $class = ArrayHelper::getValue($validators, $choice.'.class'); - - $object = $this - ->make($class, [$user, $code, $this->module->twoFactorAuthenticationCycles]); - $success = $object->validate(); - $success = $success && $user->updateAttributes(['auth_tf_enabled' => '1','auth_tf_type' => $choice]); - $message = $success ? $object->getSuccessMessage() : $object->getUnsuccessMessage($codeDurationTime); - - return [ - 'success' => $success, - 'message' => $message - ]; - } - - public function actionTwoFactorDisable($id) - { - if (!$this->module->enableTwoFactorAuthentication) { - throw new ForbiddenHttpException(Yii::t('usuario', 'Application not configured for two factor authentication.')); - } - - if ($id != Yii::$app->user->id) { - throw new ForbiddenHttpException(); - } - - /** - * @var User $user - */ - $user = $this->userQuery->whereId($id)->one(); - - if (null === $user) { - throw new NotFoundHttpException(); - } - - if ($user->updateAttributes(['auth_tf_enabled' => '0', 'auth_tf_key' => null])) { - Yii::$app - ->getSession() - ->setFlash('success', Yii::t('usuario', 'Two factor authentication has been disabled.')); - } else { - Yii::$app - ->getSession() - ->setFlash('danger', Yii::t('usuario', 'Unable to disable Two factor authentication.')); - } - - $this->redirect(['account']); - } - - /** - * Display list session history. - */ - public function actionSessionHistory() - { - $searchModel = new SessionHistorySearch([ - 'user_id' => Yii::$app->user->id, - ]); - $dataProvider = $searchModel->search(Yii::$app->request->queryParams); - - return $this->render('session-history', [ - 'searchModel' => $searchModel, - 'dataProvider' => $dataProvider, - ]); - } - - /** - * Terminate all session user - */ - public function actionTerminateSessions() - { - $this->make(TerminateUserSessionsService::class, [Yii::$app->user->id])->run(); - - return $this->redirect(['session-history']); - } - - public function actionTwoFactorMobilePhone($id) - { - Yii::$app->response->format = Response::FORMAT_JSON; - - /** - * - * - * @var User $user - */ - $user = $this->userQuery->whereId($id)->one(); - - if (null === $user) { - return [ - 'success' => false, - 'message' => Yii::t('usuario', 'User not found.') - ]; - } - $mobilePhone = Yii::$app->request->get('mobilephone'); - $currentMobilePhone = $user->getAuthTfMobilePhone(); - $success = false; - if ($currentMobilePhone == $mobilePhone) { - $success = true; - } else { - $success = $user->updateAttributes(['auth_tf_mobile_phone' => $mobilePhone]); - $success = $success && $this->make(TwoFactorSmsCodeGeneratorService::class, [$user])->run(); - } - - return [ - 'success' => $success, - 'message' => $success - ? Yii::t('usuario', 'Mobile phone number successfully enabled.') - : Yii::t('usuario', 'Error while enabling SMS two factor authentication. Please reload the page.'), - ]; - } - - /** - * @param $id - * @throws ForbiddenHttpException - * @throws NotFoundHttpException - * @throws \Exception - * @throws \Throwable - * @throws \yii\db\StaleObjectException - */ - protected function disconnectSocialNetwork($id) - { - /** - * - * - * @var SocialNetworkAccount $account - */ - $account = $this->socialNetworkAccountQuery->whereId($id)->one(); - - if ($account === null) { - throw new NotFoundHttpException(); - } - if ($account->user_id !== Yii::$app->user->id) { - throw new ForbiddenHttpException(); - } - $event = $this->make(SocialNetworkConnectEvent::class, [Yii::$app->user->identity, $account]); - - $this->trigger(SocialNetworkConnectEvent::EVENT_BEFORE_DISCONNECT, $event); - $account->delete(); - $this->trigger(SocialNetworkConnectEvent::EVENT_AFTER_DISCONNECT, $event); - } -} diff --git a/src/User/Model/Profile.php b/src/User/Model/Profile.php index 2ce9ca2..7166a5f 100644 --- a/src/User/Model/Profile.php +++ b/src/User/Model/Profile.php @@ -27,6 +27,7 @@ use yii\db\ActiveRecord; /** * @property int $user_id * @property string $name + * @property string $surname * @property string $public_email * @property string $gravatar_email * @property string $gravatar_id @@ -88,6 +89,7 @@ class Profile extends ActiveRecord 'gravatarEmailPattern' => ['gravatar_email', 'email'], 'websiteUrl' => ['website', 'url'], 'nameLength' => ['name', 'string', 'max' => 255], + 'surnameLength' => ['surname', 'string', 'max' => 255], 'publicEmailLength' => ['public_email', 'string', 'max' => 255], 'gravatarEmailLength' => ['gravatar_email', 'string', 'max' => 255], 'locationLength' => ['location', 'string', 'max' => 255], @@ -102,6 +104,7 @@ class Profile extends ActiveRecord { return [ 'name' => Yii::t('usuario', 'Name'), + 'surname' => Yii::t('usuario', 'Surname'), 'public_email' => Yii::t('usuario', 'Email (public)'), 'gravatar_email' => Yii::t('usuario', 'Gravatar email'), 'location' => Yii::t('usuario', 'Location'), diff --git a/src/User/Model/User.php b/src/User/Model/User.php index 1e60d71..2764b17 100644 --- a/src/User/Model/User.php +++ b/src/User/Model/User.php @@ -57,7 +57,14 @@ use yii\web\IdentityInterface; * @property string $last_login_ip * @property int $password_changed_at * @property int $password_age - * Defined relations: + * @property int $status + * @property string $password_reset_token + * @property string $language + * @property boolean $rememberMe + * @property int $state + * @property int $created_by + * + * Defined relations: * @property SocialNetworkAccount[] $socialNetworkAccounts * @property Profile $profile */ @@ -204,6 +211,12 @@ class User extends ActiveRecord implements IdentityInterface 'last_login_ip' => Yii::t('usuario', 'Last login IP'), 'password_changed_at' => Yii::t('usuario', 'Last password change'), 'password_age' => Yii::t('usuario', 'Password age'), + 'language' => Yii::t('usuario', 'Language'), + 'status' => Yii::t('usuario', 'Status'), + 'state' => Yii::t('usuario', 'State'), + 'created_by' => Yii::t('usuario', 'Created by'), + 'rememberMe' => Yii::t('usuario', 'Remember Me'), + 'password_reset_token' => Yii::t('usuario', 'Password Reset Token'), ]; } @@ -393,4 +406,40 @@ class User extends ActiveRecord implements IdentityInterface { return $this->getAttribute('auth_tf_mobile_phone'); } + + /** + * Returns the user who created this user + * @return \yii\db\ActiveQuery + */ + public function getCreator() + { + return $this->hasOne(self::class, ['id' => 'created_by']); + } + + /** + * Returns the user's name + * @return string|null + */ + public function getName() + { + return $this->profile ? $this->profile->name : null; + } + + /** + * Returns the user's surname + * @return string|null + */ + public function getSurname() + { + return $this->profile ? $this->profile->surname : null; + } + + /** + * Returns the user's full name + * @return string + */ + public function getFullName() + { + return $this->profile ? ($this->profile->name . ' ' . $this->profile->surname) : $this->username; + } } diff --git a/src/User/Module.php b/src/User/Module.php index 0f70d5e..ed5a639 100755 --- a/src/User/Module.php +++ b/src/User/Module.php @@ -91,7 +91,7 @@ class Module extends BaseModule * @see AccessRuleFilter */ public $gdprConsentExcludedUrls = [ - 'user/settings/*' + 'user/profile/*' ]; /** * @var bool whether to enable two factor authentication or not diff --git a/src/User/resources/i18n/it/usuario.php b/src/User/resources/i18n/it/usuario.php index 44b7636..b999d79 100644 --- a/src/User/resources/i18n/it/usuario.php +++ b/src/User/resources/i18n/it/usuario.php @@ -202,19 +202,24 @@ return [ 'Rule name' => 'Nome regola', 'Rule {0} does not exists' => 'La regola {0} non esiste', 'Rule {0} not found.' => 'Regola {0} non trovata.', - 'Rule' => 'ruolo', + 'Rule' => 'Regola', 'Rules' => 'Regole', 'Save' => 'Salva', 'Scan the QrCode with Google Authenticator App, then insert its temporary code on the box and submit.' => 'Scansiona il codice QR con l\'applicazione Google Authenticator, poi inserisci il codice temporaneo nel riquadro ed invia.', + 'Select children...' => 'Seleziona figli...', + 'Select items...' => 'Seleziona elementi...', + 'Select role...' => 'Seleziona ruolo...', 'Select rule...' => 'Seleziona una regola...', 'Send password recovery email' => 'Invia email di recupero password', 'Session history' => 'Cronologia sessioni', 'Session ID' => 'ID sessione', + 'Settings' => 'Impostazioni', 'Sign in' => 'Accedi', 'Sign up' => 'Registrati', 'Something went wrong' => 'È successo qualcosa di strano', 'Status' => 'Stato', 'Submit' => 'Invia', + 'Surname' => 'Cognome', 'Switch identities is disabled.' => 'Il cambio identità è disabilitato', 'Terminate all sessions' => 'Termina tutte le sessioni', 'Text message' => 'Messaggio di testo tramite SMS', diff --git a/src/User/resources/views/admin/_account.php b/src/User/resources/views/admin/_account.php index 7b44021..8c27f12 100644 --- a/src/User/resources/views/admin/_account.php +++ b/src/User/resources/views/admin/_account.php @@ -9,7 +9,7 @@ * the LICENSE file that was distributed with this source code. */ -use yii\bootstrap\ActiveForm; +use yii\bootstrap4\ActiveForm; use yii\helpers\Html; /** @var yii\web\View $this */ @@ -22,21 +22,15 @@ use yii\helpers\Html; 'horizontal', 'enableAjaxValidation' => true, 'enableClientValidation' => false, - 'fieldConfig' => [ - 'horizontalCssClasses' => [ - 'wrapper' => 'col-sm-9', - ], - ], ] ); ?> = $this->render('/admin/_user', ['form' => $form, 'user' => $user]) ?> -
= Yii::t('usuario', 'You are about to delete all your personal data from this site.') ?>
++ = Yii::t( + 'usuario', + 'Once you have deleted your data, you will not longer be able to sign in with this account.' + ) ?> +
+= Yii::t( + 'usuario', + 'Here you can download your personal data in a comma separated values format.' + ) ?> +
+ = Html::a( + Yii::t('usuario', 'Download my data'), + ['/user/profile/export'], + [ + 'class' => 'btn btn-info', + 'target' => '_blank' + ] + ) + ?> += Yii::t( + 'usuario', + 'This will remove your personal data from this site. You will no longer be able to sign in.' + ) ?> +
+ allowAccountDelete): ?> + = Html::a( + Yii::t('usuario', 'Delete account'), + ['delete'], + [ + 'id' => 'gdpr-del-button', + 'class' => 'btn btn-danger', + 'data-method' => 'post', + 'data-confirm' => Yii::t('usuario', 'Are you sure? There is no going back'), + ] + ) ?> + 'btn btn-danger', + 'id' => 'gdpr-del-button', + + ] + ) + ?> + += Html::encode($profile->bio) ?>
- -
+
+
+ = Html::a(Yii::t('usuario', 'Already registered? Sign in!'), ['/user/security/login']) ?>
+
+ @@ -96,4 +98,4 @@ $this->params['breadcrumbs'][] = $this->title; ] ) ?>
= Yii::t('usuario', 'You are about to delete all your personal data from this site.') ?>
-- = Yii::t( - 'usuario', - 'Once you have deleted your data, you will not longer be able to sign in with this account.' - ) ?> -
-= Yii::t( - 'usuario', - 'Here you can download your personal data in a comma separated values format.' - ) ?> -
- = Html::a(Yii::t('usuario', 'Download my data'), - ['/user/settings/export'], - [ - 'class' => 'btn btn-info', - 'target' => '_blank' - ]) - ?> -= Yii::t( - 'usuario', - 'This will remove your personal data from this site. You will no longer be able to sign in.' - ) ?> -
- allowAccountDelete): ?> - = Html::a( - Yii::t('usuario', 'Delete account'), - ['delete'], - [ - 'id' => 'gdpr-del-button', - 'class' => 'btn btn-danger', - 'data-method' => 'post', - 'data-confirm' => Yii::t('usuario', 'Are you sure? There is no going back'), - ] - ) ?> - 'btn btn-danger', - 'id' => 'gdpr-del-button', - - ]) - ?> - -