aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/Wallabag/CoreBundle/Command
diff options
context:
space:
mode:
Diffstat (limited to 'src/Wallabag/CoreBundle/Command')
-rw-r--r--src/Wallabag/CoreBundle/Command/CleanDuplicatesCommand.php117
-rw-r--r--src/Wallabag/CoreBundle/Command/ExportCommand.php24
-rw-r--r--src/Wallabag/CoreBundle/Command/InstallCommand.php281
-rw-r--r--src/Wallabag/CoreBundle/Command/ListUserCommand.php61
-rw-r--r--src/Wallabag/CoreBundle/Command/ReloadEntryCommand.php90
-rw-r--r--src/Wallabag/CoreBundle/Command/ShowUserCommand.php75
-rw-r--r--src/Wallabag/CoreBundle/Command/TagAllCommand.php16
7 files changed, 429 insertions, 235 deletions
diff --git a/src/Wallabag/CoreBundle/Command/CleanDuplicatesCommand.php b/src/Wallabag/CoreBundle/Command/CleanDuplicatesCommand.php
new file mode 100644
index 00000000..b58909db
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Command/CleanDuplicatesCommand.php
@@ -0,0 +1,117 @@
1<?php
2
3namespace Wallabag\CoreBundle\Command;
4
5use Doctrine\ORM\NoResultException;
6use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
7use Symfony\Component\Console\Input\InputArgument;
8use Symfony\Component\Console\Input\InputInterface;
9use Symfony\Component\Console\Output\OutputInterface;
10use Symfony\Component\Console\Style\SymfonyStyle;
11use Wallabag\CoreBundle\Entity\Entry;
12use Wallabag\UserBundle\Entity\User;
13
14class CleanDuplicatesCommand extends ContainerAwareCommand
15{
16 /** @var SymfonyStyle */
17 protected $io;
18
19 protected $duplicates = 0;
20
21 protected function configure()
22 {
23 $this
24 ->setName('wallabag:clean-duplicates')
25 ->setDescription('Cleans the database for duplicates')
26 ->setHelp('This command helps you to clean your articles list in case of duplicates')
27 ->addArgument(
28 'username',
29 InputArgument::OPTIONAL,
30 'User to clean'
31 );
32 }
33
34 protected function execute(InputInterface $input, OutputInterface $output)
35 {
36 $this->io = new SymfonyStyle($input, $output);
37
38 $username = $input->getArgument('username');
39
40 if ($username) {
41 try {
42 $user = $this->getUser($username);
43 $this->cleanDuplicates($user);
44 } catch (NoResultException $e) {
45 $this->io->error(sprintf('User "%s" not found.', $username));
46
47 return 1;
48 }
49
50 $this->io->success('Finished cleaning.');
51 } else {
52 $users = $this->getContainer()->get('wallabag_user.user_repository')->findAll();
53
54 $this->io->text(sprintf('Cleaning through <info>%d</info> user accounts', count($users)));
55
56 foreach ($users as $user) {
57 $this->io->text(sprintf('Processing user <info>%s</info>', $user->getUsername()));
58 $this->cleanDuplicates($user);
59 }
60 $this->io->success(sprintf('Finished cleaning. %d duplicates found in total', $this->duplicates));
61 }
62
63 return 0;
64 }
65
66 /**
67 * @param User $user
68 */
69 private function cleanDuplicates(User $user)
70 {
71 $em = $this->getContainer()->get('doctrine.orm.entity_manager');
72 $repo = $this->getContainer()->get('wallabag_core.entry_repository');
73
74 $entries = $repo->findAllEntriesIdAndUrlByUserId($user->getId());
75
76 $duplicatesCount = 0;
77 $urls = [];
78 foreach ($entries as $entry) {
79 $url = $this->similarUrl($entry['url']);
80
81 /* @var $entry Entry */
82 if (in_array($url, $urls, true)) {
83 ++$duplicatesCount;
84
85 $em->remove($repo->find($entry['id']));
86 $em->flush(); // Flushing at the end of the loop would require the instance not being online
87 } else {
88 $urls[] = $entry['url'];
89 }
90 }
91
92 $this->duplicates += $duplicatesCount;
93
94 $this->io->text(sprintf('Cleaned <info>%d</info> duplicates for user <info>%s</info>', $duplicatesCount, $user->getUserName()));
95 }
96
97 private function similarUrl($url)
98 {
99 if (in_array(substr($url, -1), ['/', '#'], true)) { // get rid of "/" and "#" and the end of urls
100 return substr($url, 0, strlen($url));
101 }
102
103 return $url;
104 }
105
106 /**
107 * Fetches a user from its username.
108 *
109 * @param string $username
110 *
111 * @return \Wallabag\UserBundle\Entity\User
112 */
113 private function getUser($username)
114 {
115 return $this->getContainer()->get('wallabag_user.user_repository')->findOneByUserName($username);
116 }
117}
diff --git a/src/Wallabag/CoreBundle/Command/ExportCommand.php b/src/Wallabag/CoreBundle/Command/ExportCommand.php
index e3d3b399..b07087c8 100644
--- a/src/Wallabag/CoreBundle/Command/ExportCommand.php
+++ b/src/Wallabag/CoreBundle/Command/ExportCommand.php
@@ -7,6 +7,7 @@ use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
7use Symfony\Component\Console\Input\InputArgument; 7use Symfony\Component\Console\Input\InputArgument;
8use Symfony\Component\Console\Input\InputInterface; 8use Symfony\Component\Console\Input\InputInterface;
9use Symfony\Component\Console\Output\OutputInterface; 9use Symfony\Component\Console\Output\OutputInterface;
10use Symfony\Component\Console\Style\SymfonyStyle;
10 11
11class ExportCommand extends ContainerAwareCommand 12class ExportCommand extends ContainerAwareCommand
12{ 13{
@@ -31,47 +32,44 @@ class ExportCommand extends ContainerAwareCommand
31 32
32 protected function execute(InputInterface $input, OutputInterface $output) 33 protected function execute(InputInterface $input, OutputInterface $output)
33 { 34 {
35 $io = new SymfonyStyle($input, $output);
36
34 try { 37 try {
35 $user = $this->getDoctrine()->getRepository('WallabagUserBundle:User')->findOneByUserName($input->getArgument('username')); 38 $user = $this->getContainer()->get('wallabag_user.user_repository')->findOneByUserName($input->getArgument('username'));
36 } catch (NoResultException $e) { 39 } catch (NoResultException $e) {
37 $output->writeln(sprintf('<error>User "%s" not found.</error>', $input->getArgument('username'))); 40 $io->error(sprintf('User "%s" not found.', $input->getArgument('username')));
38 41
39 return 1; 42 return 1;
40 } 43 }
41 44
42 $entries = $this->getDoctrine() 45 $entries = $this->getContainer()->get('wallabag_core.entry_repository')
43 ->getRepository('WallabagCoreBundle:Entry')
44 ->getBuilderForAllByUser($user->getId()) 46 ->getBuilderForAllByUser($user->getId())
45 ->getQuery() 47 ->getQuery()
46 ->getResult(); 48 ->getResult();
47 49
48 $output->write(sprintf('Exporting %d entrie(s) for user « <comment>%s</comment> »... ', count($entries), $user->getUserName())); 50 $io->text(sprintf('Exporting <info>%d</info> entrie(s) for user <info>%s</info>...', count($entries), $user->getUserName()));
49 51
50 $filePath = $input->getArgument('filepath'); 52 $filePath = $input->getArgument('filepath');
51 53
52 if (!$filePath) { 54 if (!$filePath) {
53 $filePath = $this->getContainer()->getParameter('kernel.root_dir').'/../'.sprintf('%s-export.json', $user->getUsername()); 55 $filePath = $this->getContainer()->getParameter('kernel.root_dir') . '/../' . sprintf('%s-export.json', $user->getUsername());
54 } 56 }
55 57
56 try { 58 try {
57 $data = $this->getContainer()->get('wallabag_core.helper.entries_export') 59 $data = $this->getContainer()->get('wallabag_core.helper.entries_export')
58 ->setEntries($entries) 60 ->setEntries($entries)
59 ->updateTitle('All') 61 ->updateTitle('All')
62 ->updateAuthor('All')
60 ->exportJsonData(); 63 ->exportJsonData();
61 file_put_contents($filePath, $data); 64 file_put_contents($filePath, $data);
62 } catch (\InvalidArgumentException $e) { 65 } catch (\InvalidArgumentException $e) {
63 $output->writeln(sprintf('<error>Error: "%s"</error>', $e->getMessage())); 66 $io->error(sprintf('Error: "%s"', $e->getMessage()));
64 67
65 return 1; 68 return 1;
66 } 69 }
67 70
68 $output->writeln('<info>Done.</info>'); 71 $io->success('Done.');
69 72
70 return 0; 73 return 0;
71 } 74 }
72
73 private function getDoctrine()
74 {
75 return $this->getContainer()->get('doctrine');
76 }
77} 75}
diff --git a/src/Wallabag/CoreBundle/Command/InstallCommand.php b/src/Wallabag/CoreBundle/Command/InstallCommand.php
index f0738b91..877dbfa2 100644
--- a/src/Wallabag/CoreBundle/Command/InstallCommand.php
+++ b/src/Wallabag/CoreBundle/Command/InstallCommand.php
@@ -2,19 +2,17 @@
2 2
3namespace Wallabag\CoreBundle\Command; 3namespace Wallabag\CoreBundle\Command;
4 4
5use Craue\ConfigBundle\Entity\Setting;
5use FOS\UserBundle\Event\UserEvent; 6use FOS\UserBundle\Event\UserEvent;
6use FOS\UserBundle\FOSUserEvents; 7use FOS\UserBundle\FOSUserEvents;
7use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; 8use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
8use Symfony\Component\Console\Helper\Table;
9use Symfony\Component\Console\Input\ArrayInput; 9use Symfony\Component\Console\Input\ArrayInput;
10use Symfony\Component\Console\Input\InputInterface; 10use Symfony\Component\Console\Input\InputInterface;
11use Symfony\Component\Console\Input\InputOption; 11use Symfony\Component\Console\Input\InputOption;
12use Symfony\Component\Console\Output\BufferedOutput; 12use Symfony\Component\Console\Output\BufferedOutput;
13use Symfony\Component\Console\Output\OutputInterface; 13use Symfony\Component\Console\Output\OutputInterface;
14use Symfony\Component\Console\Question\ConfirmationQuestion;
15use Symfony\Component\Console\Question\Question; 14use Symfony\Component\Console\Question\Question;
16use Wallabag\CoreBundle\Entity\Config; 15use Symfony\Component\Console\Style\SymfonyStyle;
17use Craue\ConfigBundle\Entity\Setting;
18 16
19class InstallCommand extends ContainerAwareCommand 17class InstallCommand extends ContainerAwareCommand
20{ 18{
@@ -24,9 +22,9 @@ class InstallCommand extends ContainerAwareCommand
24 protected $defaultInput; 22 protected $defaultInput;
25 23
26 /** 24 /**
27 * @var OutputInterface 25 * @var SymfonyStyle
28 */ 26 */
29 protected $defaultOutput; 27 protected $io;
30 28
31 /** 29 /**
32 * @var array 30 * @var array
@@ -53,25 +51,27 @@ class InstallCommand extends ContainerAwareCommand
53 protected function execute(InputInterface $input, OutputInterface $output) 51 protected function execute(InputInterface $input, OutputInterface $output)
54 { 52 {
55 $this->defaultInput = $input; 53 $this->defaultInput = $input;
56 $this->defaultOutput = $output;
57 54
58 $output->writeln('<info>Installing wallabag...</info>'); 55 $this->io = new SymfonyStyle($input, $output);
59 $output->writeln(''); 56
57 $this->io->title('Wallabag installer');
60 58
61 $this 59 $this
62 ->checkRequirements() 60 ->checkRequirements()
63 ->setupDatabase() 61 ->setupDatabase()
64 ->setupAdmin() 62 ->setupAdmin()
65 ->setupConfig() 63 ->setupConfig()
64 ->runMigrations()
66 ; 65 ;
67 66
68 $output->writeln('<info>wallabag has been successfully installed.</info>'); 67 $this->io->success('Wallabag has been successfully installed.');
69 $output->writeln('<comment>Just execute `php bin/console server:run --env=prod` for using wallabag: http://localhost:8000</comment>'); 68 $this->io->note('Just execute `php bin/console server:run --env=prod` for using wallabag: http://localhost:8000');
70 } 69 }
71 70
72 protected function checkRequirements() 71 protected function checkRequirements()
73 { 72 {
74 $this->defaultOutput->writeln('<info><comment>Step 1 of 4.</comment> Checking system requirements.</info>'); 73 $this->io->section('Step 1 of 5: Checking system requirements.');
74
75 $doctrineManager = $this->getContainer()->get('doctrine')->getManager(); 75 $doctrineManager = $this->getContainer()->get('doctrine')->getManager();
76 76
77 $rows = []; 77 $rows = [];
@@ -85,7 +85,7 @@ class InstallCommand extends ContainerAwareCommand
85 if (!extension_loaded($this->getContainer()->getParameter('database_driver'))) { 85 if (!extension_loaded($this->getContainer()->getParameter('database_driver'))) {
86 $fulfilled = false; 86 $fulfilled = false;
87 $status = '<error>ERROR!</error>'; 87 $status = '<error>ERROR!</error>';
88 $help = 'Database driver "'.$this->getContainer()->getParameter('database_driver').'" is not installed.'; 88 $help = 'Database driver "' . $this->getContainer()->getParameter('database_driver') . '" is not installed.';
89 } 89 }
90 90
91 $rows[] = [sprintf($label, $this->getContainer()->getParameter('database_driver')), $status, $help]; 91 $rows[] = [sprintf($label, $this->getContainer()->getParameter('database_driver')), $status, $help];
@@ -100,10 +100,10 @@ class InstallCommand extends ContainerAwareCommand
100 $conn->connect(); 100 $conn->connect();
101 } catch (\Exception $e) { 101 } catch (\Exception $e) {
102 if (false === strpos($e->getMessage(), 'Unknown database') 102 if (false === strpos($e->getMessage(), 'Unknown database')
103 && false === strpos($e->getMessage(), 'database "'.$this->getContainer()->getParameter('database_name').'" does not exist')) { 103 && false === strpos($e->getMessage(), 'database "' . $this->getContainer()->getParameter('database_name') . '" does not exist')) {
104 $fulfilled = false; 104 $fulfilled = false;
105 $status = '<error>ERROR!</error>'; 105 $status = '<error>ERROR!</error>';
106 $help = 'Can\'t connect to the database: '.$e->getMessage(); 106 $help = 'Can\'t connect to the database: ' . $e->getMessage();
107 } 107 }
108 } 108 }
109 109
@@ -122,7 +122,7 @@ class InstallCommand extends ContainerAwareCommand
122 if (false === version_compare($version, $minimalVersion, '>')) { 122 if (false === version_compare($version, $minimalVersion, '>')) {
123 $fulfilled = false; 123 $fulfilled = false;
124 $status = '<error>ERROR!</error>'; 124 $status = '<error>ERROR!</error>';
125 $help = 'Your MySQL version ('.$version.') is too old, consider upgrading ('.$minimalVersion.'+).'; 125 $help = 'Your MySQL version (' . $version . ') is too old, consider upgrading (' . $minimalVersion . '+).';
126 } 126 }
127 } 127 }
128 128
@@ -136,50 +136,44 @@ class InstallCommand extends ContainerAwareCommand
136 if (isset($matches[1]) & version_compare($matches[1], '9.2.0', '<')) { 136 if (isset($matches[1]) & version_compare($matches[1], '9.2.0', '<')) {
137 $fulfilled = false; 137 $fulfilled = false;
138 $status = '<error>ERROR!</error>'; 138 $status = '<error>ERROR!</error>';
139 $help = 'PostgreSQL should be greater than 9.1 (actual version: '.$matches[1].')'; 139 $help = 'PostgreSQL should be greater than 9.1 (actual version: ' . $matches[1] . ')';
140 } 140 }
141 } 141 }
142 142
143 $rows[] = [$label, $status, $help]; 143 $rows[] = [$label, $status, $help];
144 144
145 foreach ($this->functionExists as $functionRequired) { 145 foreach ($this->functionExists as $functionRequired) {
146 $label = '<comment>'.$functionRequired.'</comment>'; 146 $label = '<comment>' . $functionRequired . '</comment>';
147 $status = '<info>OK!</info>'; 147 $status = '<info>OK!</info>';
148 $help = ''; 148 $help = '';
149 149
150 if (!function_exists($functionRequired)) { 150 if (!function_exists($functionRequired)) {
151 $fulfilled = false; 151 $fulfilled = false;
152 $status = '<error>ERROR!</error>'; 152 $status = '<error>ERROR!</error>';
153 $help = 'You need the '.$functionRequired.' function activated'; 153 $help = 'You need the ' . $functionRequired . ' function activated';
154 } 154 }
155 155
156 $rows[] = [$label, $status, $help]; 156 $rows[] = [$label, $status, $help];
157 } 157 }
158 158
159 $table = new Table($this->defaultOutput); 159 $this->io->table(['Checked', 'Status', 'Recommendation'], $rows);
160 $table
161 ->setHeaders(['Checked', 'Status', 'Recommendation'])
162 ->setRows($rows)
163 ->render();
164 160
165 if (!$fulfilled) { 161 if (!$fulfilled) {
166 throw new \RuntimeException('Some system requirements are not fulfilled. Please check output messages and fix them.'); 162 throw new \RuntimeException('Some system requirements are not fulfilled. Please check output messages and fix them.');
167 } 163 }
168 164
169 $this->defaultOutput->writeln('<info>Success! Your system can run wallabag properly.</info>'); 165 $this->io->success('Success! Your system can run wallabag properly.');
170
171 $this->defaultOutput->writeln('');
172 166
173 return $this; 167 return $this;
174 } 168 }
175 169
176 protected function setupDatabase() 170 protected function setupDatabase()
177 { 171 {
178 $this->defaultOutput->writeln('<info><comment>Step 2 of 4.</comment> Setting up database.</info>'); 172 $this->io->section('Step 2 of 5: Setting up database.');
179 173
180 // user want to reset everything? Don't care about what is already here 174 // user want to reset everything? Don't care about what is already here
181 if (true === $this->defaultInput->getOption('reset')) { 175 if (true === $this->defaultInput->getOption('reset')) {
182 $this->defaultOutput->writeln('Droping database, creating database and schema, clearing the cache'); 176 $this->io->text('Dropping database, creating database and schema, clearing the cache');
183 177
184 $this 178 $this
185 ->runCommand('doctrine:database:drop', ['--force' => true]) 179 ->runCommand('doctrine:database:drop', ['--force' => true])
@@ -188,13 +182,13 @@ class InstallCommand extends ContainerAwareCommand
188 ->runCommand('cache:clear') 182 ->runCommand('cache:clear')
189 ; 183 ;
190 184
191 $this->defaultOutput->writeln(''); 185 $this->io->newLine();
192 186
193 return $this; 187 return $this;
194 } 188 }
195 189
196 if (!$this->isDatabasePresent()) { 190 if (!$this->isDatabasePresent()) {
197 $this->defaultOutput->writeln('Creating database and schema, clearing the cache'); 191 $this->io->text('Creating database and schema, clearing the cache');
198 192
199 $this 193 $this
200 ->runCommand('doctrine:database:create') 194 ->runCommand('doctrine:database:create')
@@ -202,16 +196,13 @@ class InstallCommand extends ContainerAwareCommand
202 ->runCommand('cache:clear') 196 ->runCommand('cache:clear')
203 ; 197 ;
204 198
205 $this->defaultOutput->writeln(''); 199 $this->io->newLine();
206 200
207 return $this; 201 return $this;
208 } 202 }
209 203
210 $questionHelper = $this->getHelper('question'); 204 if ($this->io->confirm('It appears that your database already exists. Would you like to reset it?', false)) {
211 $question = new ConfirmationQuestion('It appears that your database already exists. Would you like to reset it? (y/N)', false); 205 $this->io->text('Dropping database, creating database and schema...');
212
213 if ($questionHelper->ask($this->defaultInput, $this->defaultOutput, $question)) {
214 $this->defaultOutput->writeln('Droping database, creating database and schema');
215 206
216 $this 207 $this
217 ->runCommand('doctrine:database:drop', ['--force' => true]) 208 ->runCommand('doctrine:database:drop', ['--force' => true])
@@ -219,9 +210,8 @@ class InstallCommand extends ContainerAwareCommand
219 ->runCommand('doctrine:schema:create') 210 ->runCommand('doctrine:schema:create')
220 ; 211 ;
221 } elseif ($this->isSchemaPresent()) { 212 } elseif ($this->isSchemaPresent()) {
222 $question = new ConfirmationQuestion('Seems like your database contains schema. Do you want to reset it? (y/N)', false); 213 if ($this->io->confirm('Seems like your database contains schema. Do you want to reset it?', false)) {
223 if ($questionHelper->ask($this->defaultInput, $this->defaultOutput, $question)) { 214 $this->io->text('Dropping schema and creating schema...');
224 $this->defaultOutput->writeln('Droping schema and creating schema');
225 215
226 $this 216 $this
227 ->runCommand('doctrine:schema:drop', ['--force' => true]) 217 ->runCommand('doctrine:schema:drop', ['--force' => true])
@@ -229,29 +219,27 @@ class InstallCommand extends ContainerAwareCommand
229 ; 219 ;
230 } 220 }
231 } else { 221 } else {
232 $this->defaultOutput->writeln('Creating schema'); 222 $this->io->text('Creating schema...');
233 223
234 $this 224 $this
235 ->runCommand('doctrine:schema:create') 225 ->runCommand('doctrine:schema:create')
236 ; 226 ;
237 } 227 }
238 228
239 $this->defaultOutput->writeln('Clearing the cache'); 229 $this->io->text('Clearing the cache...');
240 $this->runCommand('cache:clear'); 230 $this->runCommand('cache:clear');
241 231
242 $this->defaultOutput->writeln(''); 232 $this->io->newLine();
233 $this->io->text('<info>Database successfully setup.</info>');
243 234
244 return $this; 235 return $this;
245 } 236 }
246 237
247 protected function setupAdmin() 238 protected function setupAdmin()
248 { 239 {
249 $this->defaultOutput->writeln('<info><comment>Step 3 of 4.</comment> Administration setup.</info>'); 240 $this->io->section('Step 3 of 5: Administration setup.');
250
251 $questionHelper = $this->getHelperSet()->get('question');
252 $question = new ConfirmationQuestion('Would you like to create a new admin user (recommended) ? (Y/n)', true);
253 241
254 if (!$questionHelper->ask($this->defaultInput, $this->defaultOutput, $question)) { 242 if (!$this->io->confirm('Would you like to create a new admin user (recommended)?', true)) {
255 return $this; 243 return $this;
256 } 244 }
257 245
@@ -260,14 +248,13 @@ class InstallCommand extends ContainerAwareCommand
260 $userManager = $this->getContainer()->get('fos_user.user_manager'); 248 $userManager = $this->getContainer()->get('fos_user.user_manager');
261 $user = $userManager->createUser(); 249 $user = $userManager->createUser();
262 250
263 $question = new Question('Username (default: wallabag) :', 'wallabag'); 251 $user->setUsername($this->io->ask('Username', 'wallabag'));
264 $user->setUsername($questionHelper->ask($this->defaultInput, $this->defaultOutput, $question));
265 252
266 $question = new Question('Password (default: wallabag) :', 'wallabag'); 253 $question = new Question('Password', 'wallabag');
267 $user->setPlainPassword($questionHelper->ask($this->defaultInput, $this->defaultOutput, $question)); 254 $question->setHidden(true);
255 $user->setPlainPassword($this->io->askQuestion($question));
268 256
269 $question = new Question('Email:', ''); 257 $user->setEmail($this->io->ask('Email', ''));
270 $user->setEmail($questionHelper->ask($this->defaultInput, $this->defaultOutput, $question));
271 258
272 $user->setEnabled(true); 259 $user->setEnabled(true);
273 $user->addRole('ROLE_SUPER_ADMIN'); 260 $user->addRole('ROLE_SUPER_ADMIN');
@@ -278,168 +265,20 @@ class InstallCommand extends ContainerAwareCommand
278 $event = new UserEvent($user); 265 $event = new UserEvent($user);
279 $this->getContainer()->get('event_dispatcher')->dispatch(FOSUserEvents::USER_CREATED, $event); 266 $this->getContainer()->get('event_dispatcher')->dispatch(FOSUserEvents::USER_CREATED, $event);
280 267
281 $this->defaultOutput->writeln(''); 268 $this->io->text('<info>Administration successfully setup.</info>');
282 269
283 return $this; 270 return $this;
284 } 271 }
285 272
286 protected function setupConfig() 273 protected function setupConfig()
287 { 274 {
288 $this->defaultOutput->writeln('<info><comment>Step 4 of 4.</comment> Config setup.</info>'); 275 $this->io->section('Step 4 of 5: Config setup.');
289 $em = $this->getContainer()->get('doctrine.orm.entity_manager'); 276 $em = $this->getContainer()->get('doctrine.orm.entity_manager');
290 277
291 // cleanup before insert new stuff 278 // cleanup before insert new stuff
292 $em->createQuery('DELETE FROM CraueConfigBundle:Setting')->execute(); 279 $em->createQuery('DELETE FROM CraueConfigBundle:Setting')->execute();
293 280
294 $settings = [ 281 foreach ($this->getContainer()->getParameter('wallabag_core.default_internal_settings') as $setting) {
295 [
296 'name' => 'share_public',
297 'value' => '1',
298 'section' => 'entry',
299 ],
300 [
301 'name' => 'carrot',
302 'value' => '1',
303 'section' => 'entry',
304 ],
305 [
306 'name' => 'share_diaspora',
307 'value' => '1',
308 'section' => 'entry',
309 ],
310 [
311 'name' => 'diaspora_url',
312 'value' => 'http://diasporapod.com',
313 'section' => 'entry',
314 ],
315 [
316 'name' => 'share_unmark',
317 'value' => '1',
318 'section' => 'entry',
319 ],
320 [
321 'name' => 'unmark_url',
322 'value' => 'https://unmark.it',
323 'section' => 'entry',
324 ],
325 [
326 'name' => 'share_shaarli',
327 'value' => '1',
328 'section' => 'entry',
329 ],
330 [
331 'name' => 'shaarli_url',
332 'value' => 'http://myshaarli.com',
333 'section' => 'entry',
334 ],
335 [
336 'name' => 'share_mail',
337 'value' => '1',
338 'section' => 'entry',
339 ],
340 [
341 'name' => 'share_twitter',
342 'value' => '1',
343 'section' => 'entry',
344 ],
345 [
346 'name' => 'export_epub',
347 'value' => '1',
348 'section' => 'export',
349 ],
350 [
351 'name' => 'export_mobi',
352 'value' => '1',
353 'section' => 'export',
354 ],
355 [
356 'name' => 'export_pdf',
357 'value' => '1',
358 'section' => 'export',
359 ],
360 [
361 'name' => 'export_csv',
362 'value' => '1',
363 'section' => 'export',
364 ],
365 [
366 'name' => 'export_json',
367 'value' => '1',
368 'section' => 'export',
369 ],
370 [
371 'name' => 'export_txt',
372 'value' => '1',
373 'section' => 'export',
374 ],
375 [
376 'name' => 'export_xml',
377 'value' => '1',
378 'section' => 'export',
379 ],
380 [
381 'name' => 'import_with_redis',
382 'value' => '0',
383 'section' => 'import',
384 ],
385 [
386 'name' => 'import_with_rabbitmq',
387 'value' => '0',
388 'section' => 'import',
389 ],
390 [
391 'name' => 'show_printlink',
392 'value' => '1',
393 'section' => 'entry',
394 ],
395 [
396 'name' => 'wallabag_support_url',
397 'value' => 'https://www.wallabag.org/pages/support.html',
398 'section' => 'misc',
399 ],
400 [
401 'name' => 'wallabag_url',
402 'value' => '',
403 'section' => 'misc',
404 ],
405 [
406 'name' => 'piwik_enabled',
407 'value' => '0',
408 'section' => 'analytics',
409 ],
410 [
411 'name' => 'piwik_host',
412 'value' => 'v2.wallabag.org',
413 'section' => 'analytics',
414 ],
415 [
416 'name' => 'piwik_site_id',
417 'value' => '1',
418 'section' => 'analytics',
419 ],
420 [
421 'name' => 'demo_mode_enabled',
422 'value' => '0',
423 'section' => 'misc',
424 ],
425 [
426 'name' => 'demo_mode_username',
427 'value' => 'wallabag',
428 'section' => 'misc',
429 ],
430 [
431 'name' => 'download_images_enabled',
432 'value' => '0',
433 'section' => 'misc',
434 ],
435 [
436 'name' => 'restricted_access',
437 'value' => '0',
438 'section' => 'entry',
439 ],
440 ];
441
442 foreach ($settings as $setting) {
443 $newSetting = new Setting(); 282 $newSetting = new Setting();
444 $newSetting->setName($setting['name']); 283 $newSetting->setName($setting['name']);
445 $newSetting->setValue($setting['value']); 284 $newSetting->setValue($setting['value']);
@@ -449,7 +288,19 @@ class InstallCommand extends ContainerAwareCommand
449 288
450 $em->flush(); 289 $em->flush();
451 290
452 $this->defaultOutput->writeln(''); 291 $this->io->text('<info>Config successfully setup.</info>');
292
293 return $this;
294 }
295
296 protected function runMigrations()
297 {
298 $this->io->section('Step 5 of 5: Run migrations.');
299
300 $this
301 ->runCommand('doctrine:migrations:migrate', ['--no-interaction' => true]);
302
303 $this->io->text('<info>Migrations successfully executed.</info>');
453 304
454 return $this; 305 return $this;
455 } 306 }
@@ -480,20 +331,18 @@ class InstallCommand extends ContainerAwareCommand
480 $output = new BufferedOutput(); 331 $output = new BufferedOutput();
481 $exitCode = $this->getApplication()->run(new ArrayInput($parameters), $output); 332 $exitCode = $this->getApplication()->run(new ArrayInput($parameters), $output);
482 333
334 // PDO does not always close the connection after Doctrine commands.
335 // See https://github.com/symfony/symfony/issues/11750.
336 $this->getContainer()->get('doctrine')->getManager()->getConnection()->close();
337
483 if (0 !== $exitCode) { 338 if (0 !== $exitCode) {
484 $this->getApplication()->setAutoExit(true); 339 $this->getApplication()->setAutoExit(true);
485 340
486 $this->defaultOutput->writeln(''); 341 throw new \RuntimeException(
487 $this->defaultOutput->writeln('<error>The command "'.$command.'" generates some errors: </error>'); 342 'The command "' . $command . "\" generates some errors: \n\n"
488 $this->defaultOutput->writeln($output->fetch()); 343 . $output->fetch());
489
490 die();
491 } 344 }
492 345
493 // PDO does not always close the connection after Doctrine commands.
494 // See https://github.com/symfony/symfony/issues/11750.
495 $this->getContainer()->get('doctrine')->getManager()->getConnection()->close();
496
497 return $this; 346 return $this;
498 } 347 }
499 348
@@ -535,7 +384,7 @@ class InstallCommand extends ContainerAwareCommand
535 } 384 }
536 385
537 try { 386 try {
538 return in_array($databaseName, $schemaManager->listDatabases()); 387 return in_array($databaseName, $schemaManager->listDatabases(), true);
539 } catch (\Doctrine\DBAL\Exception\DriverException $e) { 388 } catch (\Doctrine\DBAL\Exception\DriverException $e) {
540 // it means we weren't able to get database list, assume the database doesn't exist 389 // it means we weren't able to get database list, assume the database doesn't exist
541 390
diff --git a/src/Wallabag/CoreBundle/Command/ListUserCommand.php b/src/Wallabag/CoreBundle/Command/ListUserCommand.php
new file mode 100644
index 00000000..20660d18
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Command/ListUserCommand.php
@@ -0,0 +1,61 @@
1<?php
2
3namespace Wallabag\CoreBundle\Command;
4
5use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
6use Symfony\Component\Console\Input\InputArgument;
7use Symfony\Component\Console\Input\InputInterface;
8use Symfony\Component\Console\Input\InputOption;
9use Symfony\Component\Console\Output\OutputInterface;
10use Symfony\Component\Console\Style\SymfonyStyle;
11
12class ListUserCommand extends ContainerAwareCommand
13{
14 protected function configure()
15 {
16 $this
17 ->setName('wallabag:user:list')
18 ->setDescription('List all users')
19 ->setHelp('This command list all existing users')
20 ->addArgument('search', InputArgument::OPTIONAL, 'Filter list by given search term')
21 ->addOption('limit', 'l', InputOption::VALUE_REQUIRED, 'Max number of displayed users', 100)
22 ;
23 }
24
25 protected function execute(InputInterface $input, OutputInterface $output)
26 {
27 $io = new SymfonyStyle($input, $output);
28
29 $users = $this->getContainer()->get('wallabag_user.user_repository')
30 ->getQueryBuilderForSearch($input->getArgument('search'))
31 ->setMaxResults($input->getOption('limit'))
32 ->getQuery()
33 ->getResult();
34
35 $nbUsers = $this->getContainer()->get('wallabag_user.user_repository')
36 ->getSumUsers();
37
38 $rows = [];
39 foreach ($users as $user) {
40 $rows[] = [
41 $user->getUsername(),
42 $user->getEmail(),
43 $user->isEnabled() ? 'yes' : 'no',
44 $user->hasRole('ROLE_SUPER_ADMIN') || $user->hasRole('ROLE_ADMIN') ? 'yes' : 'no',
45 ];
46 }
47
48 $io->table(['username', 'email', 'is enabled?', 'is admin?'], $rows);
49
50 $io->success(
51 sprintf(
52 '%s/%s%s user(s) displayed.',
53 count($users),
54 $nbUsers,
55 $input->getArgument('search') === null ? '' : ' (filtered)'
56 )
57 );
58
59 return 0;
60 }
61}
diff --git a/src/Wallabag/CoreBundle/Command/ReloadEntryCommand.php b/src/Wallabag/CoreBundle/Command/ReloadEntryCommand.php
new file mode 100644
index 00000000..91998841
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Command/ReloadEntryCommand.php
@@ -0,0 +1,90 @@
1<?php
2
3namespace Wallabag\CoreBundle\Command;
4
5use Doctrine\ORM\NoResultException;
6use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
7use Symfony\Component\Console\Input\InputArgument;
8use Symfony\Component\Console\Input\InputInterface;
9use Symfony\Component\Console\Output\OutputInterface;
10use Symfony\Component\Console\Style\SymfonyStyle;
11use Wallabag\CoreBundle\Event\EntrySavedEvent;
12
13class ReloadEntryCommand extends ContainerAwareCommand
14{
15 protected function configure()
16 {
17 $this
18 ->setName('wallabag:entry:reload')
19 ->setDescription('Reload entries')
20 ->setHelp('This command reload entries')
21 ->addArgument('username', InputArgument::OPTIONAL, 'Reload entries only for the given user')
22 ;
23 }
24
25 protected function execute(InputInterface $input, OutputInterface $output)
26 {
27 $io = new SymfonyStyle($input, $output);
28
29 $userId = null;
30 if ($username = $input->getArgument('username')) {
31 try {
32 $userId = $this->getContainer()
33 ->get('wallabag_user.user_repository')
34 ->findOneByUserName($username)
35 ->getId();
36 } catch (NoResultException $e) {
37 $io->error(sprintf('User "%s" not found.', $username));
38
39 return 1;
40 }
41 }
42
43 $entryRepository = $this->getContainer()->get('wallabag_core.entry_repository');
44 $entryIds = $entryRepository->findAllEntriesIdByUserId($userId);
45
46 $nbEntries = count($entryIds);
47 if (!$nbEntries) {
48 $io->success('No entry to reload.');
49
50 return 0;
51 }
52
53 $io->note(
54 sprintf(
55 "You're going to reload %s entries. Depending on the number of entry to reload, this could be a very long process.",
56 $nbEntries
57 )
58 );
59
60 if (!$io->confirm('Are you sure you want to proceed?')) {
61 return 0;
62 }
63
64 $progressBar = $io->createProgressBar($nbEntries);
65
66 $contentProxy = $this->getContainer()->get('wallabag_core.content_proxy');
67 $em = $this->getContainer()->get('doctrine')->getManager();
68 $dispatcher = $this->getContainer()->get('event_dispatcher');
69
70 $progressBar->start();
71 foreach ($entryIds as $entryId) {
72 $entry = $entryRepository->find($entryId);
73
74 $contentProxy->updateEntry($entry, $entry->getUrl());
75 $em->persist($entry);
76 $em->flush();
77
78 $dispatcher->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
79 $progressBar->advance();
80
81 $em->detach($entry);
82 }
83 $progressBar->finish();
84
85 $io->newLine(2);
86 $io->success('Done.');
87
88 return 0;
89 }
90}
diff --git a/src/Wallabag/CoreBundle/Command/ShowUserCommand.php b/src/Wallabag/CoreBundle/Command/ShowUserCommand.php
new file mode 100644
index 00000000..2dca32c4
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Command/ShowUserCommand.php
@@ -0,0 +1,75 @@
1<?php
2
3namespace Wallabag\CoreBundle\Command;
4
5use Doctrine\ORM\NoResultException;
6use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
7use Symfony\Component\Console\Input\InputArgument;
8use Symfony\Component\Console\Input\InputInterface;
9use Symfony\Component\Console\Output\OutputInterface;
10use Symfony\Component\Console\Style\SymfonyStyle;
11use Wallabag\UserBundle\Entity\User;
12
13class ShowUserCommand extends ContainerAwareCommand
14{
15 /** @var SymfonyStyle */
16 protected $io;
17
18 protected function configure()
19 {
20 $this
21 ->setName('wallabag:user:show')
22 ->setDescription('Show user details')
23 ->setHelp('This command shows the details for an user')
24 ->addArgument(
25 'username',
26 InputArgument::REQUIRED,
27 'User to show details for'
28 );
29 }
30
31 protected function execute(InputInterface $input, OutputInterface $output)
32 {
33 $this->io = new SymfonyStyle($input, $output);
34
35 $username = $input->getArgument('username');
36
37 try {
38 $user = $this->getUser($username);
39 $this->showUser($user);
40 } catch (NoResultException $e) {
41 $this->io->error(sprintf('User "%s" not found.', $username));
42
43 return 1;
44 }
45
46 return 0;
47 }
48
49 /**
50 * @param User $user
51 */
52 private function showUser(User $user)
53 {
54 $this->io->listing([
55 sprintf('Username: %s', $user->getUsername()),
56 sprintf('Email: %s', $user->getEmail()),
57 sprintf('Display name: %s', $user->getName()),
58 sprintf('Creation date: %s', $user->getCreatedAt()->format('Y-m-d H:i:s')),
59 sprintf('Last login: %s', $user->getLastLogin() !== null ? $user->getLastLogin()->format('Y-m-d H:i:s') : 'never'),
60 sprintf('2FA activated: %s', $user->isTwoFactorAuthentication() ? 'yes' : 'no'),
61 ]);
62 }
63
64 /**
65 * Fetches a user from its username.
66 *
67 * @param string $username
68 *
69 * @return \Wallabag\UserBundle\Entity\User
70 */
71 private function getUser($username)
72 {
73 return $this->getContainer()->get('wallabag_user.user_repository')->findOneByUserName($username);
74 }
75}
diff --git a/src/Wallabag/CoreBundle/Command/TagAllCommand.php b/src/Wallabag/CoreBundle/Command/TagAllCommand.php
index 3f9bb04d..4afac2d4 100644
--- a/src/Wallabag/CoreBundle/Command/TagAllCommand.php
+++ b/src/Wallabag/CoreBundle/Command/TagAllCommand.php
@@ -7,6 +7,7 @@ use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
7use Symfony\Component\Console\Input\InputArgument; 7use Symfony\Component\Console\Input\InputArgument;
8use Symfony\Component\Console\Input\InputInterface; 8use Symfony\Component\Console\Input\InputInterface;
9use Symfony\Component\Console\Output\OutputInterface; 9use Symfony\Component\Console\Output\OutputInterface;
10use Symfony\Component\Console\Style\SymfonyStyle;
10 11
11class TagAllCommand extends ContainerAwareCommand 12class TagAllCommand extends ContainerAwareCommand
12{ 13{
@@ -25,21 +26,22 @@ class TagAllCommand extends ContainerAwareCommand
25 26
26 protected function execute(InputInterface $input, OutputInterface $output) 27 protected function execute(InputInterface $input, OutputInterface $output)
27 { 28 {
29 $io = new SymfonyStyle($input, $output);
30
28 try { 31 try {
29 $user = $this->getUser($input->getArgument('username')); 32 $user = $this->getUser($input->getArgument('username'));
30 } catch (NoResultException $e) { 33 } catch (NoResultException $e) {
31 $output->writeln(sprintf('<error>User "%s" not found.</error>', $input->getArgument('username'))); 34 $io->error(sprintf('User "%s" not found.', $input->getArgument('username')));
32 35
33 return 1; 36 return 1;
34 } 37 }
35 $tagger = $this->getContainer()->get('wallabag_core.rule_based_tagger'); 38 $tagger = $this->getContainer()->get('wallabag_core.rule_based_tagger');
36 39
37 $output->write(sprintf('Tagging entries for user « <comment>%s</comment> »... ', $user->getUserName())); 40 $io->text(sprintf('Tagging entries for user <info>%s</info>...', $user->getUserName()));
38 41
39 $entries = $tagger->tagAllForUser($user); 42 $entries = $tagger->tagAllForUser($user);
40 43
41 $output->writeln('<info>Done.</info>'); 44 $io->text('Persist entries... ');
42 $output->write(sprintf('Persist entries ... ', $user->getUserName()));
43 45
44 $em = $this->getDoctrine()->getManager(); 46 $em = $this->getDoctrine()->getManager();
45 foreach ($entries as $entry) { 47 foreach ($entries as $entry) {
@@ -47,7 +49,9 @@ class TagAllCommand extends ContainerAwareCommand
47 } 49 }
48 $em->flush(); 50 $em->flush();
49 51
50 $output->writeln('<info>Done.</info>'); 52 $io->success('Done.');
53
54 return 0;
51 } 55 }
52 56
53 /** 57 /**
@@ -59,7 +63,7 @@ class TagAllCommand extends ContainerAwareCommand
59 */ 63 */
60 private function getUser($username) 64 private function getUser($username)
61 { 65 {
62 return $this->getDoctrine()->getRepository('WallabagUserBundle:User')->findOneByUserName($username); 66 return $this->getContainer()->get('wallabag_user.user_repository')->findOneByUserName($username);
63 } 67 }
64 68
65 private function getDoctrine() 69 private function getDoctrine()