Adds a setting to disable first time wizard for new users (#5938)
This commit is contained in:
@@ -370,6 +370,11 @@ final class SystemConfiguration
|
||||
return $this->getDefaultCurrency();
|
||||
}
|
||||
|
||||
public function isUserWizardActive(): bool
|
||||
{
|
||||
return (bool) $this->find('user.wizard');
|
||||
}
|
||||
|
||||
// ========== Timesheet configurations ==========
|
||||
/*
|
||||
public function getTimesheetBreakWarningDuration(): int
|
||||
|
||||
@@ -546,6 +546,10 @@ final class SystemConfigurationController extends AbstractController
|
||||
->setLabel('theme.avatar_url')
|
||||
->setType(YesNoType::class)
|
||||
->setTranslationDomain('system-configuration'),
|
||||
(new Configuration('user.wizard'))
|
||||
->setLabel('user_auth_wizard')
|
||||
->setType(YesNoType::class)
|
||||
->setTranslationDomain('system-configuration'),
|
||||
]),
|
||||
(new SystemConfigurationModel('theme'))
|
||||
->setConfiguration([
|
||||
|
||||
@@ -568,6 +568,9 @@ final class Configuration implements ConfigurationInterface
|
||||
->booleanNode('registration')
|
||||
->defaultFalse()
|
||||
->end()
|
||||
->booleanNode('wizard')
|
||||
->defaultTrue()
|
||||
->end()
|
||||
->booleanNode('password_reset')
|
||||
->defaultTrue()
|
||||
->end()
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
namespace App\EventSubscriber;
|
||||
|
||||
use App\Configuration\SystemConfiguration;
|
||||
use App\Entity\User;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
@@ -23,7 +24,8 @@ class WizardSubscriber implements EventSubscriberInterface
|
||||
public function __construct(
|
||||
private UrlGeneratorInterface $urlGenerator,
|
||||
private AuthorizationCheckerInterface $security,
|
||||
private TokenStorageInterface $storage
|
||||
private TokenStorageInterface $storage,
|
||||
private SystemConfiguration $systemConfiguration
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -63,6 +65,15 @@ class WizardSubscriber implements EventSubscriberInterface
|
||||
return;
|
||||
}
|
||||
|
||||
if ($user->requiresPasswordReset()) {
|
||||
$response = new RedirectResponse($this->urlGenerator->generate('wizard', ['wizard' => 'password']));
|
||||
$event->setResponse($response);
|
||||
}
|
||||
|
||||
if ($user->isRegularUserOnly() && !$this->systemConfiguration->isUserWizardActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (User::WIZARDS as $wizard) {
|
||||
if (!$user->hasSeenWizard($wizard)) {
|
||||
$response = new RedirectResponse($this->urlGenerator->generate('wizard', ['wizard' => $wizard]));
|
||||
@@ -71,10 +82,5 @@ class WizardSubscriber implements EventSubscriberInterface
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ($user->requiresPasswordReset()) {
|
||||
$response = new RedirectResponse($this->urlGenerator->generate('wizard', ['wizard' => 'password']));
|
||||
$event->setResponse($response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,6 +105,9 @@ class SystemConfigurationTest extends TestCase
|
||||
'company' => 'Acme Corp.',
|
||||
],
|
||||
],
|
||||
'user' => [
|
||||
'wizard' => true,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -125,6 +128,7 @@ class SystemConfigurationTest extends TestCase
|
||||
(new Configuration())->setName('timesheet.markdown_content')->setValue('1'),
|
||||
(new Configuration())->setName('timesheet.default_begin')->setValue('07:00'),
|
||||
(new Configuration())->setName('timesheet.active_entries.hard_limit')->setValue('7'),
|
||||
(new Configuration())->setName('user.wizard')->setValue(false),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -226,6 +230,18 @@ class SystemConfigurationTest extends TestCase
|
||||
self::assertEquals('IT', $sut->getUserDefaultLanguage());
|
||||
}
|
||||
|
||||
public function testUserWizardWithoutLoader(): void
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), []);
|
||||
self::assertTrue($sut->isUserWizardActive());
|
||||
}
|
||||
|
||||
public function testUserWizardWithLoader(): void
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), $this->getDefaultLoaderSettings());
|
||||
self::assertFalse($sut->isUserWizardActive());
|
||||
}
|
||||
|
||||
public function testTimesheetWithoutLoader(): void
|
||||
{
|
||||
$sut = $this->getSut($this->getDefaultSettings(), []);
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
|
||||
namespace App\Tests\Controller;
|
||||
|
||||
use App\DataFixtures\UserFixtures;
|
||||
use App\Entity\User;
|
||||
use App\Entity\UserPreference;
|
||||
use PHPUnit\Framework\Attributes\Group;
|
||||
|
||||
#[Group('integration')]
|
||||
@@ -37,6 +39,97 @@ class WizardControllerTest extends AbstractControllerBaseTestCase
|
||||
$this->assertAccessIsGranted($client, '/wizard/profile');
|
||||
}
|
||||
|
||||
public function testWizardDoesNotAppearOnFirstLoginIfDisabled(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
|
||||
$this->setSystemConfiguration('user.wizard', false);
|
||||
|
||||
$user = $this->loadUserFromDatabase(UserFixtures::USERNAME_USER);
|
||||
$user->setPreferenceValue('__wizards__', null);
|
||||
$user->setRequiresPasswordReset(false);
|
||||
$this->getEntityManager()->persist($user);
|
||||
$this->getEntityManager()->flush();
|
||||
|
||||
$this->request($client, '/timesheet/');
|
||||
|
||||
self::assertTrue($client->getResponse()->isSuccessful());
|
||||
self::assertFalse($client->getResponse()->isRedirect());
|
||||
}
|
||||
|
||||
public function testWizardAppearsOnFirstLoginWithDefaultConfiguration(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
|
||||
$user = $this->loadUserFromDatabase(UserFixtures::USERNAME_USER);
|
||||
$user->setPreferenceValue('__wizards__', null);
|
||||
$user->setRequiresPasswordReset(false);
|
||||
$this->getEntityManager()->persist($user);
|
||||
$this->getEntityManager()->flush();
|
||||
|
||||
$this->request($client, '/timesheet/');
|
||||
|
||||
$this->assertIsRedirect($client, '/wizard/intro');
|
||||
}
|
||||
|
||||
public function testProfileWizardSubmitRedirectsToDoneAndMarksSeen(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
|
||||
$user = $this->loadUserFromDatabase(UserFixtures::USERNAME_USER);
|
||||
$user->setPreferenceValue('__wizards__', null);
|
||||
$user->setRequiresPasswordReset(false);
|
||||
$this->getEntityManager()->persist($user);
|
||||
$this->getEntityManager()->flush();
|
||||
|
||||
$crawler = $this->request($client, '/wizard/profile');
|
||||
$form = $crawler->filter('form[name=form]')->form();
|
||||
$values = $form->getPhpValues();
|
||||
$values['form']['reload'] = '0';
|
||||
$values['form'][UserPreference::LANGUAGE] = 'en';
|
||||
$values['form'][UserPreference::LOCALE] = 'en';
|
||||
$values['form'][UserPreference::TIMEZONE] = 'Europe/Berlin';
|
||||
$values['form'][UserPreference::SKIN] = 'auto';
|
||||
$client->submit($form, $values);
|
||||
|
||||
$this->assertIsRedirect($client, '/wizard/done');
|
||||
|
||||
$this->getEntityManager()->clear();
|
||||
$user = $this->loadUserFromDatabase(UserFixtures::USERNAME_USER);
|
||||
self::assertTrue($user->hasSeenWizard('profile'));
|
||||
}
|
||||
|
||||
public function testProfileWizardSubmitReloadsProfileWhenRequested(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
|
||||
$crawler = $this->request($client, '/wizard/profile');
|
||||
$form = $crawler->filter('form[name=form]')->form();
|
||||
$values = $form->getPhpValues();
|
||||
$values['form']['reload'] = '1';
|
||||
$client->submit($form, $values);
|
||||
|
||||
$this->assertIsRedirect($client, '/wizard/profile');
|
||||
}
|
||||
|
||||
public function testProfileWizardSubmitRedirectsToPasswordIfResetRequired(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
|
||||
$user = $this->loadUserFromDatabase(UserFixtures::USERNAME_USER);
|
||||
$user->setRequiresPasswordReset(true);
|
||||
$this->getEntityManager()->persist($user);
|
||||
$this->getEntityManager()->flush();
|
||||
|
||||
$crawler = $this->request($client, '/wizard/profile');
|
||||
$form = $crawler->filter('form[name=form]')->form();
|
||||
$values = $form->getPhpValues();
|
||||
$values['form']['reload'] = '0';
|
||||
$client->submit($form, $values);
|
||||
|
||||
$this->assertIsRedirect($client, '/wizard/password');
|
||||
}
|
||||
|
||||
public function testDoneWizard(): void
|
||||
{
|
||||
$client = $this->getClientForAuthenticatedUser(User::ROLE_USER);
|
||||
|
||||
@@ -412,6 +412,7 @@ class ConfigurationTest extends TestCase
|
||||
'user' => [
|
||||
'registration' => false,
|
||||
'password_reset' => true,
|
||||
'wizard' => true,
|
||||
'login' => true,
|
||||
'password_reset_retry_ttl' => 3600,
|
||||
'password_reset_token_ttl' => 86400,
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
<source>theme.avatar_url</source>
|
||||
<target state="translated">Erlaube die Nutzung von URLs für Avatarbilder</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="uf2_qIW" resname="user_auth_wizard">
|
||||
<source>user_auth_wizard</source>
|
||||
<target>Einrichtungsassistent für neue Benutzer anzeigen</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="FaGqoTT" resname="timesheet.mode" xml:space="preserve">
|
||||
<source>timesheet.mode</source>
|
||||
<target state="translated">Zeiterfassungs-Modus</target>
|
||||
|
||||
@@ -254,6 +254,10 @@
|
||||
<source>authentication</source>
|
||||
<target state="translated">Authentifizierung</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="uf2_qIW" resname="user_auth_wizard">
|
||||
<source>user_auth_wizard</source>
|
||||
<target>Einrichtungsassistent für neue Benutzer anzeigen</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="FaGqoTT" resname="timesheet.mode">
|
||||
<source>timesheet.mode</source>
|
||||
<target state="translated">Zeiterfassungsmodus</target>
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
<source>theme.avatar_url</source>
|
||||
<target>Allow the use of URLs for avatar images</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="uf2_qIW" resname="user_auth_wizard">
|
||||
<source>user_auth_wizard</source>
|
||||
<target>Show setup wizard for new users</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="FaGqoTT" resname="timesheet.mode">
|
||||
<source>timesheet.mode</source>
|
||||
<target>Timetracking mode</target>
|
||||
|
||||
Reference in New Issue
Block a user