Skip to content

Commit d33a650

Browse files
committed
Added a new command to create users
1 parent 6dd29ce commit d33a650

File tree

4 files changed

+313
-5
lines changed

4 files changed

+313
-5
lines changed

app/Resources/assets/scss/main.scss

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,24 @@ body#homepage {
144144
// Page: 'login'
145145
// --------------------------------------------------
146146
body#login {
147+
#login-users-help p {
148+
font-size: $font-size-base;
149+
line-height: $line-height-base;
150+
151+
&:last-child {
152+
margin-bottom: 0;
153+
}
154+
155+
.label {
156+
margin-right: 5px;
157+
}
158+
159+
.console {
160+
display: block;
161+
margin: 5px 0;
162+
padding: 10px;
163+
}
164+
}
147165
}
148166

149167
//

app/Resources/views/security/login.html.twig

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,24 @@
5959
</tbody>
6060
</table>
6161

62-
<p>
63-
If they don't work, reload your fixtures by running this command
64-
from the terminal: <br/>
65-
<code>$ php app/console doctrine:fixtures:load</code>
66-
</p>
62+
<div id="login-users-help" class="panel panel-default">
63+
<div class="panel-body">
64+
<p>
65+
<span class="label label-success">NOTE</span>
66+
If these users don't work, reload application fixtures by
67+
running this command from the terminal: <br/>
68+
69+
<code class="console">$ php app/console doctrine:fixtures:load</code>
70+
</p>
71+
72+
<p>
73+
<span class="label label-success">TIP</span>
74+
If you want to create new users, run this other command: <br/>
75+
76+
<code class="console">$ php app/console app:add-user</code>
77+
</p>
78+
</div>
79+
</div>
6780
</div>
6881
</div>
6982
{% endblock %}
Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace AppBundle\Command;
13+
14+
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
15+
use Symfony\Component\Console\Input\InputArgument;
16+
use Symfony\Component\Console\Input\InputInterface;
17+
use Symfony\Component\Console\Input\InputOption;
18+
use Symfony\Component\Console\Output\OutputInterface;
19+
use Symfony\Component\Console\Question\Question;
20+
use Doctrine\Common\Persistence\ObjectManager;
21+
use AppBundle\Entity\User;
22+
23+
/**
24+
* A command console that creates users and stores them in the database.
25+
* To use this command, open a terminal window, enter into your project
26+
* directory and execute the following:
27+
* $ php app/console app:add-user
28+
*
29+
* To output detailed information, increase the command verbosity:
30+
* $ php app/console app:add-user -vv
31+
*
32+
* See http://symfony.com/doc/current/cookbook/console/console_command.html
33+
*
34+
* @author Javier Eguiluz <javier.eguiluz@gmail.com>
35+
*/
36+
class AddUserCommand extends ContainerAwareCommand
37+
{
38+
const MAX_ATTEMPTS = 5;
39+
40+
/** @var ObjectManager */
41+
private $em;
42+
43+
protected function configure()
44+
{
45+
$this
46+
// a good practice is to use the 'app:' prefix to group all your custom application commands
47+
->setName('app:add-user')
48+
->setDescription('Creates users and stores them in the database')
49+
->setHelp($this->getCommandHelp())
50+
// commands can optionally define arguments and/or options (mandatory and optional)
51+
// see http://symfony.com/doc/current/components/console/console_arguments.html
52+
->addArgument('username', InputArgument::OPTIONAL, 'The username of the new user')
53+
->addArgument('password', InputArgument::OPTIONAL, 'The plain password of the new user')
54+
->addArgument('email', InputArgument::OPTIONAL, 'The email of the new user')
55+
->addOption('is-admin', null, InputOption::VALUE_NONE, 'If set, the user is created as an administrator')
56+
;
57+
}
58+
59+
/**
60+
* This method is executed before initialize() and execute(). Its purpose is
61+
* to check if some of the options/arguments are missing and interactively
62+
* ask the user for those values.
63+
*
64+
* This method is completely optional. If you are developing an internal console
65+
* command, you probably should not implement this method because it requires
66+
* quite a lot of work. However, if the command is meant to be used by external
67+
* users, this method is a nice way to fall back and prevent errors.
68+
*/
69+
protected function interact(InputInterface $input, OutputInterface $output)
70+
{
71+
if (null !== $input->getArgument('username') && null !== $input->getArgument('password') && null !== $input->getArgument('email')) {
72+
return;
73+
}
74+
75+
$output->writeln('');
76+
$output->writeln('Add User Command Interactive Wizard');
77+
$output->writeln('-----------------------------------');
78+
79+
$output->writeln(array(
80+
'',
81+
'If you prefer to not use this interactive wizard, provide the',
82+
'arguments required by this command as follows:',
83+
'',
84+
' $ php app/console app:add-user username password email@example.com',
85+
'',
86+
));
87+
88+
$output->writeln(array(
89+
'',
90+
'Now we\'ll ask you for the value of all the missing command arguments.',
91+
'',
92+
));
93+
94+
// See http://symfony.com/doc/current/components/console/helpers/questionhelper.html
95+
$console = $this->getHelper('question');
96+
97+
// Ask for the username if it's not defined
98+
if (null === $username = $input->getArgument('username')) {
99+
$question = new Question(' > <info>Username</>: ');
100+
$question->setValidator(function ($answer) {
101+
if (empty($answer)) {
102+
throw new \RuntimeException('The username cannot be empty');
103+
}
104+
105+
return $answer;
106+
});
107+
$question->setMaxAttempts(self::MAX_ATTEMPTS);
108+
109+
$username = $console->ask($input, $output, $question);
110+
$input->setArgument('username', $username);
111+
} else {
112+
$output->writeln(' > <info>Username</>: '.$username);
113+
}
114+
115+
// Ask for the password if it's not defined
116+
if (null === $password = $input->getArgument('password')) {
117+
$question = new Question(' > <info>Password</> (your type will be hidden): ');
118+
$question->setValidator(array($this, 'passwordValidator'));
119+
$question->setHidden(true);
120+
$question->setMaxAttempts(self::MAX_ATTEMPTS);
121+
122+
$password = $console->ask($input, $output, $question);
123+
$input->setArgument('password', $password);
124+
} else {
125+
$output->writeln(' > <info>Password</>: '.str_repeat('*', strlen($password)));
126+
}
127+
128+
// Ask for the email if it's not defined
129+
if (null === $email = $input->getArgument('email')) {
130+
$question = new Question(' > <info>Email</>: ');
131+
$question->setValidator(array($this, 'emailValidator'));
132+
$question->setMaxAttempts(self::MAX_ATTEMPTS);
133+
134+
$email = $console->ask($input, $output, $question);
135+
$input->setArgument('email', $email);
136+
} else {
137+
$output->writeln(' > <info>Email</>: '.$email);
138+
}
139+
}
140+
141+
/**
142+
* This method is executed after the interact() and before the execute()
143+
* method. It's main purpose is to initialize the variables used in the rest
144+
* of the command methods.
145+
*/
146+
protected function initialize(InputInterface $input, OutputInterface $output)
147+
{
148+
$this->em = $this->getContainer()->get('doctrine')->getManager();
149+
}
150+
151+
/**
152+
* This method is executed after interact() and initialize(). It usually
153+
* contains the logic to execute to complete this command task.
154+
*/
155+
protected function execute(InputInterface $input, OutputInterface $output)
156+
{
157+
$startTime = microtime(true);
158+
159+
$username = $input->getArgument('username');
160+
$plainPassword = $input->getArgument('password');
161+
$email = $input->getArgument('email');
162+
$isAdmin = $input->getOption('is-admin');
163+
164+
// first check if a user with the same username already exists
165+
$existingUser = $this->em->getRepository('AppBundle:User')->findOneBy(array('username' => $username));
166+
167+
if (null !== $existingUser) {
168+
throw new \RuntimeException(sprintf('There is already a user registered with the "%s" username.', $username));
169+
}
170+
171+
// create the user and encode its password
172+
$user = new User();
173+
$user->setUsername($username);
174+
$user->setEmail($email);
175+
$user->setRoles(array($isAdmin ? 'ROLE_ADMIN' : 'ROLE_USER'));
176+
177+
// See http://symfony.com/doc/current/book/security.html#security-encoding-password
178+
$encoder = $this->getContainer()->get('security.password_encoder');
179+
$encodedPassword = $encoder->encodePassword($user, $plainPassword);
180+
$user->setPassword($encodedPassword);
181+
182+
$this->em->persist($user);
183+
$this->em->flush($user);
184+
185+
$output->writeln('');
186+
$output->writeln(sprintf('[OK] %s was successfully created: %s (%s)', $isAdmin ? 'Administrator user' : 'User', $user->getUsername(), $user->getEmail()));
187+
188+
if ($output->isVerbose()) {
189+
$finishTime = microtime(true);
190+
$elapsedTime = $finishTime - $startTime;
191+
192+
$output->writeln(sprintf('[INFO] New user database id: %d / Elapsed time: %.2f ms', $user->getId(), $elapsedTime*1000));
193+
}
194+
}
195+
196+
/**
197+
* This internal method should be private, but it's declared as public to
198+
* maintain PHP 5.3 compatibility when using it in a callback.
199+
*
200+
* @internal
201+
*/
202+
public function passwordValidator($plainPassword)
203+
{
204+
if (empty($plainPassword)) {
205+
throw new \Exception('The password can not be empty');
206+
}
207+
208+
if (strlen(trim($plainPassword)) < 6) {
209+
throw new \Exception('The password must be at least 6 characters long');
210+
}
211+
212+
return $plainPassword;
213+
}
214+
215+
/**
216+
* This internal method should be private, but it's declared as public to
217+
* maintain PHP 5.3 compatibility when using it in a callback.
218+
*
219+
* @internal
220+
*/
221+
public function emailValidator($email)
222+
{
223+
if (empty($email)) {
224+
throw new \Exception('The email can not be empty');
225+
}
226+
227+
if (false === strpos($email, '@')) {
228+
throw new \Exception('The email should look like a real email');
229+
}
230+
231+
return $email;
232+
}
233+
234+
/**
235+
* The command help is usually included in the configure() method, but when
236+
* it's too long, it's better to define a separate method to maintain the
237+
* code readability.
238+
*/
239+
private function getCommandHelp()
240+
{
241+
return <<<HELP
242+
The <info>%command.name%</info> command creates new users and saves them in the database:
243+
244+
<info>php %command.full_name%</info> <comment>username password email</comment>
245+
246+
By default the command creates regular users. To create administrator users,
247+
add the <comment>--is-admin</comment> option:
248+
249+
<info>php %command.full_name%</info> username password email <comment>--is-admin</comment>
250+
251+
If you omit any of the three required arguments, the command will ask you to
252+
provide the missing values:
253+
254+
# command will ask you for the email
255+
<info>php %command.full_name%</info> <comment>username password</comment>
256+
257+
# command will ask you for the email and password
258+
<info>php %command.full_name%</info> <comment>username</comment>
259+
260+
# command will ask you for all arguments
261+
<info>php %command.full_name%</info>
262+
263+
HELP;
264+
}
265+
}

web/css/app.css

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7046,6 +7046,18 @@ footer {
70467046
body#homepage {
70477047
text-align: center; }
70487048

7049+
body#login #login-users-help p {
7050+
font-size: 15px;
7051+
line-height: 1.42857; }
7052+
body#login #login-users-help p:last-child {
7053+
margin-bottom: 0; }
7054+
body#login #login-users-help p .label {
7055+
margin-right: 5px; }
7056+
body#login #login-users-help p .console {
7057+
display: block;
7058+
margin: 5px 0;
7059+
padding: 10px; }
7060+
70497061
body#blog_index h1 {
70507062
margin-bottom: .5em; }
70517063
body#blog_index article.post {

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy