aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Wallabag/CoreBundle/Command/InstallCommand.php296
-rw-r--r--src/Wallabag/CoreBundle/Controller/ConfigController.php128
-rw-r--r--src/Wallabag/CoreBundle/Controller/EntryController.php7
-rw-r--r--src/Wallabag/CoreBundle/DataFixtures/ORM/LoadConfigData.php45
-rw-r--r--src/Wallabag/CoreBundle/DataFixtures/ORM/LoadEntryData.php2
-rw-r--r--src/Wallabag/CoreBundle/DataFixtures/ORM/LoadUserData.php4
-rw-r--r--src/Wallabag/CoreBundle/Entity/Config.php113
-rw-r--r--src/Wallabag/CoreBundle/Entity/User.php19
-rw-r--r--src/Wallabag/CoreBundle/Entity/UsersConfig.php123
-rw-r--r--src/Wallabag/CoreBundle/Form/Type/ChangePasswordType.php39
-rw-r--r--src/Wallabag/CoreBundle/Form/Type/ConfigType.php41
-rw-r--r--src/Wallabag/CoreBundle/Form/Type/EntryType.php29
-rw-r--r--src/Wallabag/CoreBundle/Form/Type/NewUserType.php40
-rw-r--r--src/Wallabag/CoreBundle/Form/Type/UserType.php31
-rw-r--r--src/Wallabag/CoreBundle/Repository/ConfigRepository.php9
-rw-r--r--src/Wallabag/CoreBundle/Resources/config/routing.yml6
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/Config/index.html.twig137
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/_footer.html.twig6
-rwxr-xr-xsrc/Wallabag/CoreBundle/Resources/views/_head.html.twig60
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/_menu.html.twig28
-rw-r--r--src/Wallabag/CoreBundle/Resources/views/_search_form.html.twig6
-rwxr-xr-xsrc/Wallabag/CoreBundle/Resources/views/_top.html.twig10
-rw-r--r--src/Wallabag/CoreBundle/Security/Authentication/Encoder/WallabagPasswordEncoder.php8
-rw-r--r--src/Wallabag/CoreBundle/Security/Validator/WallabagUserPasswordValidator.php48
-rw-r--r--src/Wallabag/CoreBundle/Tests/Command/InstallCommandTest.php274
-rw-r--r--src/Wallabag/CoreBundle/Tests/Controller/ConfigControllerTest.php350
-rw-r--r--src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php2
-rw-r--r--src/Wallabag/CoreBundle/Tests/Controller/WallabagRestControllerTest.php8
-rw-r--r--src/Wallabag/CoreBundle/Tests/WallabagTestCase.php4
29 files changed, 1574 insertions, 299 deletions
diff --git a/src/Wallabag/CoreBundle/Command/InstallCommand.php b/src/Wallabag/CoreBundle/Command/InstallCommand.php
index feaaebf6..ac7583ea 100644
--- a/src/Wallabag/CoreBundle/Command/InstallCommand.php
+++ b/src/Wallabag/CoreBundle/Command/InstallCommand.php
@@ -4,162 +4,308 @@ namespace Wallabag\CoreBundle\Command;
4 4
5use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; 5use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
6use Symfony\Component\Console\Input\InputInterface; 6use Symfony\Component\Console\Input\InputInterface;
7use Symfony\Component\Console\Input\InputOption;
8use Symfony\Component\Console\Input\ArrayInput;
7use Symfony\Component\Console\Output\OutputInterface; 9use Symfony\Component\Console\Output\OutputInterface;
10use Symfony\Component\Console\Output\NullOutput;
8use Wallabag\CoreBundle\Entity\User; 11use Wallabag\CoreBundle\Entity\User;
9use Wallabag\CoreBundle\Entity\UsersConfig; 12use Wallabag\CoreBundle\Entity\Config;
10 13
11class InstallCommand extends ContainerAwareCommand 14class InstallCommand extends ContainerAwareCommand
12{ 15{
16 /**
17 * @var InputInterface
18 */
19 protected $defaultInput;
20
21 /**
22 * @var OutputInterface
23 */
24 protected $defaultOutput;
25
13 protected function configure() 26 protected function configure()
14 { 27 {
15 $this 28 $this
16 ->setName('wallabag:install') 29 ->setName('wallabag:install')
17 ->setDescription('Wallabag installer.') 30 ->setDescription('Wallabag installer.')
31 ->addOption(
32 'reset',
33 null,
34 InputOption::VALUE_NONE,
35 'Reset current database'
36 )
18 ; 37 ;
19 } 38 }
20 39
21 protected function execute(InputInterface $input, OutputInterface $output) 40 protected function execute(InputInterface $input, OutputInterface $output)
22 { 41 {
23 $output->writeln('<info>Installing Wallabag.</info>'); 42 $this->defaultInput = $input;
43 $this->defaultOutput = $output;
44
45 $output->writeln('<info>Installing Wallabag...</info>');
24 $output->writeln(''); 46 $output->writeln('');
25 47
26 $this 48 $this
27 ->checkStep($output) 49 ->checkRequirements()
28 ->setupStep($input, $output) 50 ->setupDatabase()
51 ->setupAdmin()
52 ->setupAsset()
29 ; 53 ;
30 54
31 $output->writeln('<info>Wallabag has been successfully installed.</info>'); 55 $output->writeln('<info>Wallabag has been successfully installed.</info>');
32 $output->writeln('<comment>Just execute `php app/console server:run` for using wallabag: http://localhost:8000</comment>'); 56 $output->writeln('<comment>Just execute `php app/console server:run` for using wallabag: http://localhost:8000</comment>');
33 } 57 }
34 58
35 protected function checkStep(OutputInterface $output) 59 protected function checkRequirements()
36 { 60 {
37 $output->writeln('<info>Checking system requirements.</info>'); 61 $this->defaultOutput->writeln('<info><comment>Step 1 of 4.</comment> Checking system requirements.</info>');
38 62
39 $fulfilled = true; 63 $fulfilled = true;
40 64
41 // @TODO: find a better way to check requirements 65 // @TODO: find a better way to check requirements
42 $output->writeln('<comment>Check PCRE</comment>'); 66 $label = '<comment>PCRE</comment>';
43 if (extension_loaded('pcre')) { 67 if (extension_loaded('pcre')) {
44 $output->writeln(' <info>OK</info>'); 68 $status = '<info>OK!</info>';
69 $help = '';
45 } else { 70 } else {
46 $fulfilled = false; 71 $fulfilled = false;
47 $output->writeln(' <error>ERROR</error>'); 72 $status = '<error>ERROR!</error>';
48 $output->writeln('<comment>You should enabled PCRE extension</comment>'); 73 $help = 'You should enabled PCRE extension';
49 } 74 }
75 $rows[] = array($label, $status, $help);
50 76
51 $output->writeln('<comment>Check DOM</comment>'); 77 $label = '<comment>DOM</comment>';
52 if (extension_loaded('DOM')) { 78 if (extension_loaded('DOM')) {
53 $output->writeln(' <info>OK</info>'); 79 $status = '<info>OK!</info>';
80 $help = '';
54 } else { 81 } else {
55 $fulfilled = false; 82 $fulfilled = false;
56 $output->writeln(' <error>ERROR</error>'); 83 $status = '<error>ERROR!</error>';
57 $output->writeln('<comment>You should enabled DOM extension</comment>'); 84 $help = 'You should enabled DOM extension';
58 } 85 }
86 $rows[] = array($label, $status, $help);
87
88 $this->getHelper('table')
89 ->setHeaders(array('Checked', 'Status', 'Recommendation'))
90 ->setRows($rows)
91 ->render($this->defaultOutput);
59 92
60 if (!$fulfilled) { 93 if (!$fulfilled) {
61 throw new RuntimeException('Some system requirements are not fulfilled. Please check output messages and fix them.'); 94 throw new \RuntimeException('Some system requirements are not fulfilled. Please check output messages and fix them.');
95 } else {
96 $this->defaultOutput->writeln('<info>Success! Your system can run Wallabag properly.</info>');
62 } 97 }
63 98
64 $output->writeln(''); 99 $this->defaultOutput->writeln('');
65 100
66 return $this; 101 return $this;
67 } 102 }
68 103
69 protected function setupStep(InputInterface $input, OutputInterface $output) 104 protected function setupDatabase()
70 { 105 {
71 $output->writeln('<info>Setting up database.</info>'); 106 $this->defaultOutput->writeln('<info><comment>Step 2 of 4.</comment> Setting up database.</info>');
72 107
73 $this->setupDatabase($input, $output); 108 // user want to reset everything? Don't care about what is already here
109 if (true === $this->defaultInput->getOption('reset')) {
110 $this->defaultOutput->writeln('Droping database, creating database and schema');
74 111
75 // if ($this->getHelperSet()->get('dialog')->askConfirmation($output, '<question>Load fixtures (Y/N)?</question>', false)) { 112 $this
76 // $this->setupFixtures($input, $output); 113 ->runCommand('doctrine:database:drop', array('--force' => true))
77 // } 114 ->runCommand('doctrine:database:create')
115 ->runCommand('doctrine:schema:create')
116 ;
78 117
79 $output->writeln(''); 118 return $this;
80 $output->writeln('<info>Administration setup.</info>'); 119 }
81 120
82 $this->setupAdmin($output); 121 if (!$this->isDatabasePresent()) {
122 $this->defaultOutput->writeln('Creating database and schema, clearing the cache');
83 123
84 $output->writeln(''); 124 $this
125 ->runCommand('doctrine:database:create')
126 ->runCommand('doctrine:schema:create')
127 ->runCommand('cache:clear')
128 ;
85 129
86 return $this; 130 return $this;
87 } 131 }
88 132
89 protected function setupDatabase(InputInterface $input, OutputInterface $output) 133 $dialog = $this->getHelper('dialog');
90 {
91 if ($this->getHelperSet()->get('dialog')->askConfirmation($output, '<question>Drop current database (Y/N)?</question>', true)) {
92 $connection = $this->getContainer()->get('doctrine')->getConnection();
93 $params = $connection->getParams();
94 134
95 $name = isset($params['path']) ? $params['path'] : (isset($params['dbname']) ? $params['dbname'] : false); 135 if ($dialog->askConfirmation($this->defaultOutput, '<question>It appears that your database already exists. Would you like to reset it? (y/N)</question> ', false)) {
96 unset($params['dbname']); 136 $this->defaultOutput->writeln('Droping database, creating database and schema');
97 137
98 if (!isset($params['path'])) { 138 $this
99 $name = $connection->getDatabasePlatform()->quoteSingleIdentifier($name); 139 ->runCommand('doctrine:database:drop', array('--force' => true))
100 } 140 ->runCommand('doctrine:database:create')
141 ->runCommand('doctrine:schema:create')
142 ;
143 } elseif ($this->isSchemaPresent()) {
144 if ($dialog->askConfirmation($this->defaultOutput, '<question>Seems like your database contains schema. Do you want to reset it? (y/N)</question> ', false)) {
145 $this->defaultOutput->writeln('Droping schema and creating schema');
101 146
102 $connection->getSchemaManager()->dropDatabase($name); 147 $this
148 ->runCommand('doctrine:schema:drop', array('--force' => true))
149 ->runCommand('doctrine:schema:create')
150 ;
151 }
103 } else { 152 } else {
104 throw new \Exception("Install setup stopped, database need to be dropped. Please backup your current one and re-launch the install command."); 153 $this->defaultOutput->writeln('Creating schema');
154
155 $this
156 ->runCommand('doctrine:schema:create')
157 ;
105 } 158 }
106 159
107 $this 160 $this->defaultOutput->writeln('Clearing the cache');
108 ->runCommand('doctrine:database:create', $input, $output) 161 $this->runCommand('cache:clear');
109 ->runCommand('doctrine:schema:create', $input, $output) 162
110 ->runCommand('cache:clear', $input, $output) 163 /*
111 ->runCommand('assets:install', $input, $output) 164 if ($this->getHelperSet()->get('dialog')->askConfirmation($this->defaultOutput, '<question>Load fixtures (Y/N)?</question>', false)) {
112 ->runCommand('assetic:dump', $input, $output) 165 $doctrineConfig = $this->getContainer()->get('doctrine.orm.entity_manager')->getConnection()->getConfiguration();
113 ; 166 $logger = $doctrineConfig->getSQLLogger();
114 } 167 // speed up fixture load
168 $doctrineConfig->setSQLLogger(null);
169 $this->runCommand('doctrine:fixtures:load');
170 $doctrineConfig->setSQLLogger($logger);
171 }
172 */
115 173
116 protected function setupFixtures(InputInterface $input, OutputInterface $output) 174 $this->defaultOutput->writeln('');
117 { 175
118 $doctrineConfig = $this->getContainer()->get('doctrine.orm.entity_manager')->getConnection()->getConfiguration(); 176 return $this;
119 $logger = $doctrineConfig->getSQLLogger();
120 // speed up fixture load
121 $doctrineConfig->setSQLLogger(null);
122 $this->runCommand('doctrine:fixtures:load', $input, $output);
123 $doctrineConfig->setSQLLogger($logger);
124 } 177 }
125 178
126 protected function setupAdmin(OutputInterface $output) 179 protected function setupAdmin()
127 { 180 {
181 $this->defaultOutput->writeln('<info><comment>Step 3 of 4.</comment> Administration setup.</info>');
182
128 $dialog = $this->getHelperSet()->get('dialog'); 183 $dialog = $this->getHelperSet()->get('dialog');
184
185 if (false === $dialog->askConfirmation($this->defaultOutput, '<question>Would you like to create a new user ? (y/N)</question>', true)) {
186 return $this;
187 }
188
129 $em = $this->getContainer()->get('doctrine.orm.entity_manager'); 189 $em = $this->getContainer()->get('doctrine.orm.entity_manager');
130 190
131 $user = new User(); 191 $user = new User();
132 $user->setUsername($dialog->ask($output, '<question>Username</question> <comment>(default: wallabag)</comment> :', 'wallabag')); 192 $user->setUsername($dialog->ask($this->defaultOutput, '<question>Username</question> <comment>(default: wallabag)</comment> :', 'wallabag'));
133 $user->setPassword($dialog->ask($output, '<question>Password</question> <comment>(default: wallabag)</comment> :', 'wallabag')); 193 $user->setPassword($dialog->ask($this->defaultOutput, '<question>Password</question> <comment>(default: wallabag)</comment> :', 'wallabag'));
134 $user->setEmail($dialog->ask($output, '<question>Email:</question>', '')); 194 $user->setEmail($dialog->ask($this->defaultOutput, '<question>Email:</question>', ''));
135 195
136 $em->persist($user); 196 $em->persist($user);
137 197
138 $pagerConfig = new UsersConfig(); 198 $config = new Config($user);
139 $pagerConfig->setUserId($user->getId()); 199 $config->setTheme($this->getContainer()->getParameter('theme'));
140 $pagerConfig->setName('pager'); 200 $config->setItemsPerPage($this->getContainer()->getParameter('items_on_page'));
141 $pagerConfig->setValue(10); 201 $config->setLanguage($this->getContainer()->getParameter('language'));
142 202
143 $em->persist($pagerConfig); 203 $em->persist($config);
144 204
145 // $languageConfig = new LanguageConfig(); 205 $em->flush();
146 // $languageConfig->setUserId($user->getId());
147 // $languageConfig->setName('language');
148 // $languageConfig->setValue('en_EN.UTF8');
149 206
150 // $em->persist($languageConfig); 207 $this->defaultOutput->writeln('');
151 208
152 $em->flush(); 209 return $this;
153 } 210 }
154 211
155 protected function runCommand($command, InputInterface $input, OutputInterface $output) 212 protected function setupAsset()
156 { 213 {
214 $this->defaultOutput->writeln('<info><comment>Step 4 of 4.</comment> Installing assets.</info>');
215
157 $this 216 $this
158 ->getApplication() 217 ->runCommand('assets:install')
159 ->find($command) 218 ->runCommand('assetic:dump')
160 ->run($input, $output)
161 ; 219 ;
162 220
221 $this->defaultOutput->writeln('');
222
163 return $this; 223 return $this;
164 } 224 }
225
226 /**
227 * Run a command
228 *
229 * @param string $command
230 * @param array $parameters Parameters to this command (usually 'force' => true)
231 */
232 protected function runCommand($command, $parameters = array())
233 {
234 $parameters = array_merge(
235 array('command' => $command),
236 $parameters,
237 array(
238 '--no-debug' => true,
239 '--env' => $this->defaultInput->getOption('env') ?: 'dev',
240 )
241 );
242
243 if ($this->defaultInput->getOption('no-interaction')) {
244 $parameters = array_merge($parameters, array('--no-interaction' => true));
245 }
246
247 $this->getApplication()->setAutoExit(false);
248 $exitCode = $this->getApplication()->run(new ArrayInput($parameters), new NullOutput());
249
250 if (0 !== $exitCode) {
251 $this->getApplication()->setAutoExit(true);
252
253 $errorMessage = sprintf('The command "%s" terminated with an error code: %u.', $command, $exitCode);
254 $this->defaultOutput->writeln("<error>$errorMessage</error>");
255 $exception = new \Exception($errorMessage, $exitCode);
256
257 throw $exception;
258 }
259
260 // PDO does not always close the connection after Doctrine commands.
261 // See https://github.com/symfony/symfony/issues/11750.
262 $this->getContainer()->get('doctrine')->getManager()->getConnection()->close();
263
264 return $this;
265 }
266
267 /**
268 * Check if the database already exists
269 *
270 * @return boolean
271 */
272 private function isDatabasePresent()
273 {
274 $databaseName = $this->getContainer()->getParameter('database_name');
275
276 try {
277 $schemaManager = $this->getContainer()->get('doctrine')->getManager()->getConnection()->getSchemaManager();
278 } catch (\Exception $exception) {
279 if (false !== strpos($exception->getMessage(), sprintf("Unknown database '%s'", $databaseName))) {
280 return false;
281 }
282
283 throw $exception;
284 }
285
286 // custom verification for sqlite, since `getListDatabasesSQL` doesn't work for sqlite
287 if ('sqlite' == $schemaManager->getDatabasePlatform()->getName()) {
288 $params = $this->getContainer()->get('doctrine.dbal.default_connection')->getParams();
289
290 if (isset($params['path']) && file_exists($params['path'])) {
291 return true;
292 }
293
294 return false;
295 }
296
297 return in_array($databaseName, $schemaManager->listDatabases());
298 }
299
300 /**
301 * Check if the schema is already created
302 *
303 * @return boolean
304 */
305 private function isSchemaPresent()
306 {
307 $schemaManager = $this->getContainer()->get('doctrine')->getManager()->getConnection()->getSchemaManager();
308
309 return $schemaManager->tablesExist(array('entry'));
310 }
165} 311}
diff --git a/src/Wallabag/CoreBundle/Controller/ConfigController.php b/src/Wallabag/CoreBundle/Controller/ConfigController.php
new file mode 100644
index 00000000..68e034fa
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Controller/ConfigController.php
@@ -0,0 +1,128 @@
1<?php
2
3namespace Wallabag\CoreBundle\Controller;
4
5use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
6use Symfony\Bundle\FrameworkBundle\Controller\Controller;
7use Symfony\Component\HttpFoundation\Request;
8use Wallabag\CoreBundle\Entity\Config;
9use Wallabag\CoreBundle\Entity\User;
10use Wallabag\CoreBundle\Form\Type\ConfigType;
11use Wallabag\CoreBundle\Form\Type\ChangePasswordType;
12use Wallabag\CoreBundle\Form\Type\UserType;
13use Wallabag\CoreBundle\Form\Type\NewUserType;
14
15class ConfigController extends Controller
16{
17 /**
18 * @param Request $request
19 *
20 * @Route("/config", name="config")
21 */
22 public function indexAction(Request $request)
23 {
24 $em = $this->getDoctrine()->getManager();
25 $config = $this->getConfig();
26 $user = $this->getUser();
27
28 // handle basic config detail
29 $configForm = $this->createForm(new ConfigType(), $config);
30 $configForm->handleRequest($request);
31
32 if ($configForm->isValid()) {
33 $em->persist($config);
34 $em->flush();
35
36 $this->get('session')->getFlashBag()->add(
37 'notice',
38 'Config saved'
39 );
40
41 return $this->redirect($this->generateUrl('config'));
42 }
43
44 // handle changing password
45 $pwdForm = $this->createForm(new ChangePasswordType());
46 $pwdForm->handleRequest($request);
47
48 if ($pwdForm->isValid()) {
49 $user->setPassword($pwdForm->get('new_password')->getData());
50 $em->persist($user);
51 $em->flush();
52
53 $this->get('session')->getFlashBag()->add(
54 'notice',
55 'Password updated'
56 );
57
58 return $this->redirect($this->generateUrl('config'));
59 }
60
61 // handle changing user information
62 $userForm = $this->createForm(new UserType(), $user);
63 $userForm->handleRequest($request);
64
65 if ($userForm->isValid()) {
66 $em->persist($user);
67 $em->flush();
68
69 $this->get('session')->getFlashBag()->add(
70 'notice',
71 'Information updated'
72 );
73
74 return $this->redirect($this->generateUrl('config'));
75 }
76
77 // handle adding new user
78 $newUser = new User();
79 $newUserForm = $this->createForm(new NewUserType(), $newUser);
80 $newUserForm->handleRequest($request);
81
82 if ($newUserForm->isValid()) {
83 $em->persist($newUser);
84
85 $config = new Config($newUser);
86 $config->setTheme($this->container->getParameter('theme'));
87 $config->setItemsPerPage($this->container->getParameter('items_on_page'));
88 $config->setLanguage($this->container->getParameter('language'));
89
90 $em->persist($config);
91
92 $em->flush();
93
94 $this->get('session')->getFlashBag()->add(
95 'notice',
96 sprintf('User "%s" added', $newUser->getUsername())
97 );
98
99 return $this->redirect($this->generateUrl('config'));
100 }
101
102 return $this->render('WallabagCoreBundle:Config:index.html.twig', array(
103 'configForm' => $configForm->createView(),
104 'pwdForm' => $pwdForm->createView(),
105 'userForm' => $userForm->createView(),
106 'newUserForm' => $newUserForm->createView(),
107 ));
108 }
109
110 /**
111 * Retrieve config for the current user.
112 * If no config were found, create a new one.
113 *
114 * @return Wallabag\CoreBundle\Entity\Config
115 */
116 private function getConfig()
117 {
118 $config = $this->getDoctrine()
119 ->getRepository('WallabagCoreBundle:Config')
120 ->findOneByUser($this->getUser());
121
122 if (!$config) {
123 $config = new Config($this->getUser());
124 }
125
126 return $config;
127 }
128}
diff --git a/src/Wallabag/CoreBundle/Controller/EntryController.php b/src/Wallabag/CoreBundle/Controller/EntryController.php
index 89677bef..81ab7788 100644
--- a/src/Wallabag/CoreBundle/Controller/EntryController.php
+++ b/src/Wallabag/CoreBundle/Controller/EntryController.php
@@ -7,7 +7,7 @@ use Symfony\Bundle\FrameworkBundle\Controller\Controller;
7use Symfony\Component\HttpFoundation\Request; 7use Symfony\Component\HttpFoundation\Request;
8use Wallabag\CoreBundle\Entity\Entry; 8use Wallabag\CoreBundle\Entity\Entry;
9use Wallabag\CoreBundle\Service\Extractor; 9use Wallabag\CoreBundle\Service\Extractor;
10use Wallabag\CoreBundle\Helper\Url; 10use Wallabag\CoreBundle\Form\Type\EntryType;
11 11
12class EntryController extends Controller 12class EntryController extends Controller
13{ 13{
@@ -22,10 +22,7 @@ class EntryController extends Controller
22 { 22 {
23 $entry = new Entry($this->getUser()); 23 $entry = new Entry($this->getUser());
24 24
25 $form = $this->createFormBuilder($entry) 25 $form = $this->createForm(new EntryType(), $entry);
26 ->add('url', 'url')
27 ->add('save', 'submit')
28 ->getForm();
29 26
30 $form->handleRequest($request); 27 $form->handleRequest($request);
31 28
diff --git a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadConfigData.php b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadConfigData.php
new file mode 100644
index 00000000..900e151d
--- /dev/null
+++ b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadConfigData.php
@@ -0,0 +1,45 @@
1<?php
2
3namespace Wallabag\CoreBundle\DataFixtures\ORM;
4
5use Doctrine\Common\DataFixtures\AbstractFixture;
6use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
7use Doctrine\Common\Persistence\ObjectManager;
8use Wallabag\CoreBundle\Entity\Config;
9
10class LoadConfigData extends AbstractFixture implements OrderedFixtureInterface
11{
12 /**
13 * {@inheritDoc}
14 */
15 public function load(ObjectManager $manager)
16 {
17 $adminConfig = new Config($this->getReference('admin-user'));
18 $adminConfig->setTheme('baggy');
19 $adminConfig->setItemsPerPage(30);
20 $adminConfig->setLanguage('en_US');
21
22 $manager->persist($adminConfig);
23
24 $this->addReference('admin-config', $adminConfig);
25
26 $bobConfig = new Config($this->getReference('bob-user'));
27 $bobConfig->setTheme('default');
28 $bobConfig->setItemsPerPage(10);
29 $bobConfig->setLanguage('fr_FR');
30
31 $manager->persist($bobConfig);
32
33 $this->addReference('bob-config', $bobConfig);
34
35 $manager->flush();
36 }
37
38 /**
39 * {@inheritDoc}
40 */
41 public function getOrder()
42 {
43 return 20;
44 }
45}
diff --git a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadEntryData.php b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadEntryData.php
index 520b44b8..3be323ed 100644
--- a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadEntryData.php
+++ b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadEntryData.php
@@ -49,6 +49,6 @@ class LoadEntryData extends AbstractFixture implements OrderedFixtureInterface
49 */ 49 */
50 public function getOrder() 50 public function getOrder()
51 { 51 {
52 return 20; 52 return 30;
53 } 53 }
54} 54}
diff --git a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadUserData.php b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadUserData.php
index e4751f20..d99412f4 100644
--- a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadUserData.php
+++ b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadUserData.php
@@ -18,7 +18,7 @@ class LoadUserData extends AbstractFixture implements OrderedFixtureInterface
18 $userAdmin->setName('Big boss'); 18 $userAdmin->setName('Big boss');
19 $userAdmin->setEmail('bigboss@wallabag.org'); 19 $userAdmin->setEmail('bigboss@wallabag.org');
20 $userAdmin->setUsername('admin'); 20 $userAdmin->setUsername('admin');
21 $userAdmin->setPassword('test'); 21 $userAdmin->setPassword('mypassword');
22 22
23 $manager->persist($userAdmin); 23 $manager->persist($userAdmin);
24 24
@@ -28,7 +28,7 @@ class LoadUserData extends AbstractFixture implements OrderedFixtureInterface
28 $bobUser->setName('Bobby'); 28 $bobUser->setName('Bobby');
29 $bobUser->setEmail('bobby@wallabag.org'); 29 $bobUser->setEmail('bobby@wallabag.org');
30 $bobUser->setUsername('bob'); 30 $bobUser->setUsername('bob');
31 $bobUser->setPassword('test'); 31 $bobUser->setPassword('mypassword');
32 32
33 $manager->persist($bobUser); 33 $manager->persist($bobUser);
34 34
diff --git a/src/Wallabag/CoreBundle/Entity/Config.php b/src/Wallabag/CoreBundle/Entity/Config.php
index 045ca308..7b4464a1 100644
--- a/src/Wallabag/CoreBundle/Entity/Config.php
+++ b/src/Wallabag/CoreBundle/Entity/Config.php
@@ -3,10 +3,12 @@
3namespace Wallabag\CoreBundle\Entity; 3namespace Wallabag\CoreBundle\Entity;
4 4
5use Doctrine\ORM\Mapping as ORM; 5use Doctrine\ORM\Mapping as ORM;
6use Symfony\Component\Validator\Constraints as Assert;
6 7
7/** 8/**
8 * Config 9 * Config
9 * 10 *
11 * @ORM\Entity(repositoryClass="Wallabag\CoreBundle\Repository\ConfigRepository")
10 * @ORM\Table(name="config") 12 * @ORM\Table(name="config")
11 * @ORM\Entity 13 * @ORM\Entity
12 */ 14 */
@@ -15,25 +17,50 @@ class Config
15 /** 17 /**
16 * @var integer 18 * @var integer
17 * 19 *
18 * @ORM\Column(name="id", type="integer", nullable=false) 20 * @ORM\Column(name="id", type="integer")
19 * @ORM\Id 21 * @ORM\Id
20 * @ORM\GeneratedValue(strategy="IDENTITY") 22 * @ORM\GeneratedValue(strategy="AUTO")
21 */ 23 */
22 private $id; 24 private $id;
23 25
24 /** 26 /**
25 * @var string 27 * @var string
26 * 28 *
27 * @ORM\Column(name="name", type="string", nullable=true) 29 * @Assert\NotBlank()
30 * @ORM\Column(name="theme", type="string", nullable=false)
28 */ 31 */
29 private $name; 32 private $theme;
30 33
31 /** 34 /**
32 * @var string 35 * @var string
33 * 36 *
34 * @ORM\Column(name="value", type="blob", nullable=true) 37 * @Assert\NotBlank()
38 * @ORM\Column(name="items_per_page", type="integer", nullable=false)
35 */ 39 */
36 private $value; 40 private $items_per_page;
41
42 /**
43 * @var string
44 *
45 * @Assert\NotBlank()
46 * @ORM\Column(name="language", type="string", nullable=false)
47 */
48 private $language;
49
50 /**
51 * @ORM\ManyToOne(targetEntity="User", inversedBy="config")
52 */
53 private $user;
54
55 /*
56 * @param User $user
57 */
58 public function __construct(User $user)
59 {
60 $this->user = $user;
61 $this->items_per_page = 12;
62 $this->language = 'en_US';
63 }
37 64
38 /** 65 /**
39 * Get id 66 * Get id
@@ -46,48 +73,94 @@ class Config
46 } 73 }
47 74
48 /** 75 /**
49 * Set name 76 * Set theme
50 * 77 *
51 * @param string $name 78 * @param string $theme
52 * @return Config 79 * @return Config
53 */ 80 */
54 public function setName($name) 81 public function setTheme($theme)
55 { 82 {
56 $this->name = $name; 83 $this->theme = $theme;
57 84
58 return $this; 85 return $this;
59 } 86 }
60 87
61 /** 88 /**
62 * Get name 89 * Get theme
63 * 90 *
64 * @return string 91 * @return string
65 */ 92 */
66 public function getName() 93 public function getTheme()
67 { 94 {
68 return $this->name; 95 return $this->theme;
69 } 96 }
70 97
71 /** 98 /**
72 * Set value 99 * Set items_per_page
73 * 100 *
74 * @param string $value 101 * @param integer $itemsPerPage
75 * @return Config 102 * @return Config
76 */ 103 */
77 public function setValue($value) 104 public function setItemsPerPage($itemsPerPage)
78 { 105 {
79 $this->value = $value; 106 $this->items_per_page = $itemsPerPage;
80 107
81 return $this; 108 return $this;
82 } 109 }
83 110
84 /** 111 /**
85 * Get value 112 * Get items_per_page
113 *
114 * @return integer
115 */
116 public function getItemsPerPage()
117 {
118 return $this->items_per_page;
119 }
120
121 /**
122 * Set language
123 *
124 * @param string $language
125 * @return Config
126 */
127 public function setLanguage($language)
128 {
129 $this->language = $language;
130
131 return $this;
132 }
133
134 /**
135 * Get language
86 * 136 *
87 * @return string 137 * @return string
88 */ 138 */
89 public function getValue() 139 public function getLanguage()
140 {
141 return $this->language;
142 }
143
144 /**
145 * Set user
146 *
147 * @param \Wallabag\CoreBundle\Entity\User $user
148 * @return Config
149 */
150 public function setUser(\Wallabag\CoreBundle\Entity\User $user = null)
151 {
152 $this->user = $user;
153
154 return $this;
155 }
156
157 /**
158 * Get user
159 *
160 * @return \Wallabag\CoreBundle\Entity\User
161 */
162 public function getUser()
90 { 163 {
91 return $this->value; 164 return $this->user;
92 } 165 }
93} 166}
diff --git a/src/Wallabag/CoreBundle/Entity/User.php b/src/Wallabag/CoreBundle/Entity/User.php
index c83250c3..193dfebc 100644
--- a/src/Wallabag/CoreBundle/Entity/User.php
+++ b/src/Wallabag/CoreBundle/Entity/User.php
@@ -6,6 +6,7 @@ use Doctrine\Common\Collections\ArrayCollection;
6use Doctrine\ORM\Mapping as ORM; 6use Doctrine\ORM\Mapping as ORM;
7use Symfony\Component\Security\Core\User\UserInterface; 7use Symfony\Component\Security\Core\User\UserInterface;
8use Symfony\Component\Security\Core\User\AdvancedUserInterface; 8use Symfony\Component\Security\Core\User\AdvancedUserInterface;
9use Symfony\Component\Validator\Constraints as Assert;
9 10
10/** 11/**
11 * User 12 * User
@@ -29,6 +30,11 @@ class User implements AdvancedUserInterface, \Serializable
29 * @var string 30 * @var string
30 * 31 *
31 * @ORM\Column(name="username", type="text") 32 * @ORM\Column(name="username", type="text")
33 * @Assert\NotBlank()
34 * @Assert\Length(
35 * min = "3",
36 * max = "255"
37 * )
32 */ 38 */
33 private $username; 39 private $username;
34 40
@@ -56,14 +62,16 @@ class User implements AdvancedUserInterface, \Serializable
56 /** 62 /**
57 * @var string 63 * @var string
58 * 64 *
59 * @ORM\Column(name="email", type="text", nullable=true) 65 * @ORM\Column(name="email", type="text", nullable=false)
66 * @Assert\Email()
67 * @Assert\NotBlank()
60 */ 68 */
61 private $email; 69 private $email;
62 70
63 /** 71 /**
64 * @ORM\Column(name="is_active", type="boolean") 72 * @ORM\Column(name="is_active", type="boolean", nullable=false)
65 */ 73 */
66 private $isActive; 74 private $isActive = true;
67 75
68 /** 76 /**
69 * @var date 77 * @var date
@@ -86,9 +94,8 @@ class User implements AdvancedUserInterface, \Serializable
86 94
87 public function __construct() 95 public function __construct()
88 { 96 {
89 $this->isActive = true; 97 $this->salt = md5(uniqid(null, true));
90 $this->salt = md5(uniqid(null, true)); 98 $this->entries = new ArrayCollection();
91 $this->entries = new ArrayCollection();
92 } 99 }
93 100
94 /** 101 /**
diff --git a/src/Wallabag/CoreBundle/Entity/UsersConfig.php b/src/Wallabag/CoreBundle/Entity/UsersConfig.php
deleted file mode 100644
index 0742edbc..00000000
--- a/src/Wallabag/CoreBundle/Entity/UsersConfig.php
+++ /dev/null
@@ -1,123 +0,0 @@
1<?php
2
3namespace Wallabag\CoreBundle\Entity;
4
5use Doctrine\ORM\Mapping as ORM;
6
7/**
8 * UsersConfig
9 *
10 * @ORM\Table(name="users_config")
11 * @ORM\Entity
12 */
13class UsersConfig
14{
15 /**
16 * @var integer
17 *
18 * @ORM\Column(name="id", type="integer", nullable=true)
19 * @ORM\Id
20 * @ORM\GeneratedValue(strategy="IDENTITY")
21 */
22 private $id;
23
24 /**
25 * @var string
26 *
27 * @ORM\Column(name="user_id", type="decimal", precision=10, scale=0, nullable=true)
28 */
29 private $userId;
30
31 /**
32 * @var string
33 *
34 * @ORM\Column(name="name", type="text", nullable=true)
35 */
36 private $name;
37
38 /**
39 * @var string
40 *
41 * @ORM\Column(name="value", type="text", nullable=true)
42 */
43 private $value;
44
45 /**
46 * Get id
47 *
48 * @return integer
49 */
50 public function getId()
51 {
52 return $this->id;
53 }
54
55 /**
56 * Set userId
57 *
58 * @param string $userId
59 * @return UsersConfig
60 */
61 public function setUserId($userId)
62 {
63 $this->userId = $userId;
64
65 return $this;
66 }
67
68 /**
69 * Get userId
70 *
71 * @return string
72 */
73 public function getUserId()
74 {
75 return $this->userId;
76 }
77
78 /**
79 * Set name
80 *
81 * @param string $name
82 * @return UsersConfig
83 */
84 public function setName($name)
85 {
86 $this->name = $name;
87
88 return $this;
89 }
90
91 /**
92 * Get name
93 *
94 * @return string
95 */
96 public function getName()
97 {
98 return $this->name;
99 }
100
101 /**
102 * Set value
103 *
104 * @param string $value
105 * @return UsersConfig
106 */
107 public function setValue($value)
108 {
109 $this->value = $value;
110
111 return $this;
112 }
113
114 /**
115 * Get value
116 *
117 * @return string
118 */
119 public function getValue()
120 {
121 return $this->value;
122 }
123}
diff --git a/src/Wallabag/CoreBundle/Form/Type/ChangePasswordType.php b/src/Wallabag/CoreBundle/Form/Type/ChangePasswordType.php
new file mode 100644
index 00000000..e141789f
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Form/Type/ChangePasswordType.php
@@ -0,0 +1,39 @@
1<?php
2namespace Wallabag\CoreBundle\Form\Type;
3
4use Symfony\Component\Form\AbstractType;
5use Symfony\Component\Form\FormBuilderInterface;
6use Symfony\Component\Security\Core\Validator\Constraints\UserPassword;
7use Symfony\Component\Validator\Constraints;
8
9class ChangePasswordType extends AbstractType
10{
11 public function buildForm(FormBuilderInterface $builder, array $options)
12 {
13 $builder
14 ->add('old_password', 'password', array(
15 'constraints' => new UserPassword(array('message' => 'Wrong value for your current password')),
16 ))
17 ->add('new_password', 'repeated', array(
18 'type' => 'password',
19 'invalid_message' => 'The password fields must match.',
20 'required' => true,
21 'first_options' => array('label' => 'New password'),
22 'second_options' => array('label' => 'Repeat new password'),
23 'constraints' => array(
24 new Constraints\Length(array(
25 'min' => 8,
26 'minMessage' => 'Password should by at least 8 chars long',
27 )),
28 new Constraints\NotBlank(),
29 ),
30 ))
31 ->add('save', 'submit')
32 ;
33 }
34
35 public function getName()
36 {
37 return 'change_passwd';
38 }
39}
diff --git a/src/Wallabag/CoreBundle/Form/Type/ConfigType.php b/src/Wallabag/CoreBundle/Form/Type/ConfigType.php
new file mode 100644
index 00000000..a1e0ce47
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Form/Type/ConfigType.php
@@ -0,0 +1,41 @@
1<?php
2namespace Wallabag\CoreBundle\Form\Type;
3
4use Symfony\Component\Form\AbstractType;
5use Symfony\Component\Form\FormBuilderInterface;
6use Symfony\Component\OptionsResolver\OptionsResolverInterface;
7
8class ConfigType extends AbstractType
9{
10 public function buildForm(FormBuilderInterface $builder, array $options)
11 {
12 $builder
13 ->add('theme', 'choice', array(
14 'choices' => array(
15 'baggy' => 'Baggy',
16 'courgette' => 'Courgette',
17 'dark' => 'Dark',
18 'default' => 'Default',
19 'dmagenta' => 'Dmagenta',
20 'solarized' => 'Solarized',
21 'solarized_dark' => 'Solarized Dark',
22 ),
23 ))
24 ->add('items_per_page', 'text')
25 ->add('language')
26 ->add('save', 'submit')
27 ;
28 }
29
30 public function setDefaultOptions(OptionsResolverInterface $resolver)
31 {
32 $resolver->setDefaults(array(
33 'data_class' => 'Wallabag\CoreBundle\Entity\Config',
34 ));
35 }
36
37 public function getName()
38 {
39 return 'config';
40 }
41}
diff --git a/src/Wallabag/CoreBundle/Form/Type/EntryType.php b/src/Wallabag/CoreBundle/Form/Type/EntryType.php
new file mode 100644
index 00000000..cfd64473
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Form/Type/EntryType.php
@@ -0,0 +1,29 @@
1<?php
2namespace Wallabag\CoreBundle\Form\Type;
3
4use Symfony\Component\Form\AbstractType;
5use Symfony\Component\Form\FormBuilderInterface;
6use Symfony\Component\OptionsResolver\OptionsResolverInterface;
7
8class EntryType extends AbstractType
9{
10 public function buildForm(FormBuilderInterface $builder, array $options)
11 {
12 $builder
13 ->add('url', 'url')
14 ->add('save', 'submit')
15 ;
16 }
17
18 public function setDefaultOptions(OptionsResolverInterface $resolver)
19 {
20 $resolver->setDefaults(array(
21 'data_class' => 'Wallabag\CoreBundle\Entity\Entry',
22 ));
23 }
24
25 public function getName()
26 {
27 return 'entry';
28 }
29}
diff --git a/src/Wallabag/CoreBundle/Form/Type/NewUserType.php b/src/Wallabag/CoreBundle/Form/Type/NewUserType.php
new file mode 100644
index 00000000..313a9aae
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Form/Type/NewUserType.php
@@ -0,0 +1,40 @@
1<?php
2namespace Wallabag\CoreBundle\Form\Type;
3
4use Symfony\Component\Form\AbstractType;
5use Symfony\Component\Form\FormBuilderInterface;
6use Symfony\Component\OptionsResolver\OptionsResolverInterface;
7use Symfony\Component\Validator\Constraints;
8
9class NewUserType extends AbstractType
10{
11 public function buildForm(FormBuilderInterface $builder, array $options)
12 {
13 $builder
14 ->add('username', 'text')
15 ->add('password', 'password', array(
16 'constraints' => array(
17 new Constraints\Length(array(
18 'min' => 8,
19 'minMessage' => 'Password should by at least 8 chars long',
20 )),
21 new Constraints\NotBlank(),
22 ),
23 ))
24 ->add('email', 'text')
25 ->add('save', 'submit')
26 ;
27 }
28
29 public function setDefaultOptions(OptionsResolverInterface $resolver)
30 {
31 $resolver->setDefaults(array(
32 'data_class' => 'Wallabag\CoreBundle\Entity\User',
33 ));
34 }
35
36 public function getName()
37 {
38 return 'new_user';
39 }
40}
diff --git a/src/Wallabag/CoreBundle/Form/Type/UserType.php b/src/Wallabag/CoreBundle/Form/Type/UserType.php
new file mode 100644
index 00000000..b479a0b5
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Form/Type/UserType.php
@@ -0,0 +1,31 @@
1<?php
2namespace Wallabag\CoreBundle\Form\Type;
3
4use Symfony\Component\Form\AbstractType;
5use Symfony\Component\Form\FormBuilderInterface;
6use Symfony\Component\OptionsResolver\OptionsResolverInterface;
7
8class UserType extends AbstractType
9{
10 public function buildForm(FormBuilderInterface $builder, array $options)
11 {
12 $builder
13 ->add('username', 'text')
14 ->add('name', 'text')
15 ->add('email', 'text')
16 ->add('save', 'submit')
17 ;
18 }
19
20 public function setDefaultOptions(OptionsResolverInterface $resolver)
21 {
22 $resolver->setDefaults(array(
23 'data_class' => 'Wallabag\CoreBundle\Entity\User',
24 ));
25 }
26
27 public function getName()
28 {
29 return 'user';
30 }
31}
diff --git a/src/Wallabag/CoreBundle/Repository/ConfigRepository.php b/src/Wallabag/CoreBundle/Repository/ConfigRepository.php
new file mode 100644
index 00000000..b2b1f627
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Repository/ConfigRepository.php
@@ -0,0 +1,9 @@
1<?php
2
3namespace Wallabag\CoreBundle\Repository;
4
5use Doctrine\ORM\EntityRepository;
6
7class ConfigRepository extends EntityRepository
8{
9}
diff --git a/src/Wallabag/CoreBundle/Resources/config/routing.yml b/src/Wallabag/CoreBundle/Resources/config/routing.yml
index ec1d23cc..f3502e15 100644
--- a/src/Wallabag/CoreBundle/Resources/config/routing.yml
+++ b/src/Wallabag/CoreBundle/Resources/config/routing.yml
@@ -1,3 +1,7 @@
1_wllbg: 1entry:
2 resource: "@WallabagCoreBundle/Controller/EntryController.php" 2 resource: "@WallabagCoreBundle/Controller/EntryController.php"
3 type: annotation 3 type: annotation
4
5config:
6 resource: "@WallabagCoreBundle/Controller/ConfigController.php"
7 type: annotation
diff --git a/src/Wallabag/CoreBundle/Resources/views/Config/index.html.twig b/src/Wallabag/CoreBundle/Resources/views/Config/index.html.twig
new file mode 100644
index 00000000..051dafd6
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Resources/views/Config/index.html.twig
@@ -0,0 +1,137 @@
1{% extends "WallabagCoreBundle::layout.html.twig" %}
2
3{% block title %}{% trans %}Config{% endtrans %}{% endblock %}
4
5{% block menu %}
6 {% include "WallabagCoreBundle::_menu.html.twig" %}
7{% endblock %}
8
9{% block content %}
10 <h2>{% trans %}Wallabag configuration{% endtrans %}</h2>
11
12 <form action="{{ path('config') }}" method="post" {{ form_enctype(configForm) }}>
13 {{ form_errors(configForm) }}
14
15 <fieldset class="w500p inline">
16 <div class="row">
17 {{ form_label(configForm.theme) }}
18 {{ form_errors(configForm.theme) }}
19 {{ form_widget(configForm.theme) }}
20 </div>
21 </fieldset>
22
23 <fieldset class="w500p inline">
24 <div class="row">
25 {{ form_label(configForm.items_per_page) }}
26 {{ form_errors(configForm.items_per_page) }}
27 {{ form_widget(configForm.items_per_page) }}
28 </div>
29 </fieldset>
30
31 <fieldset class="w500p inline">
32 <div class="row">
33 {{ form_label(configForm.language) }}
34 {{ form_errors(configForm.language) }}
35 {{ form_widget(configForm.language) }}
36 </div>
37 </fieldset>
38
39 {{ form_rest(configForm) }}
40 </form>
41
42 <h2>{% trans %}User information{% endtrans %}</h2>
43
44 <form action="{{ path('config') }}" method="post" {{ form_enctype(userForm) }}>
45 {{ form_errors(userForm) }}
46
47 <fieldset class="w500p inline">
48 <div class="row">
49 {{ form_label(userForm.username) }}
50 {{ form_errors(userForm.username) }}
51 {{ form_widget(userForm.username) }}
52 </div>
53 </fieldset>
54
55 <fieldset class="w500p inline">
56 <div class="row">
57 {{ form_label(userForm.name) }}
58 {{ form_errors(userForm.name) }}
59 {{ form_widget(userForm.name) }}
60 </div>
61 </fieldset>
62
63 <fieldset class="w500p inline">
64 <div class="row">
65 {{ form_label(userForm.email) }}
66 {{ form_errors(userForm.email) }}
67 {{ form_widget(userForm.email) }}
68 </div>
69 </fieldset>
70
71 {{ form_rest(userForm) }}
72 </form>
73
74 <h2>{% trans %}Change your password{% endtrans %}</h2>
75
76 <form action="{{ path('config') }}" method="post" {{ form_enctype(pwdForm) }}>
77 {{ form_errors(pwdForm) }}
78
79 <fieldset class="w500p inline">
80 <div class="row">
81 {{ form_label(pwdForm.old_password) }}
82 {{ form_errors(pwdForm.old_password) }}
83 {{ form_widget(pwdForm.old_password) }}
84 </div>
85 </fieldset>
86
87 <fieldset class="w500p inline">
88 <div class="row">
89 {{ form_label(pwdForm.new_password.first) }}
90 {{ form_errors(pwdForm.new_password.first) }}
91 {{ form_widget(pwdForm.new_password.first) }}
92 </div>
93 </fieldset>
94
95 <fieldset class="w500p inline">
96 <div class="row">
97 {{ form_label(pwdForm.new_password.second) }}
98 {{ form_errors(pwdForm.new_password.second) }}
99 {{ form_widget(pwdForm.new_password.second) }}
100 </div>
101 </fieldset>
102
103 {{ form_rest(pwdForm) }}
104 </form>
105
106 <h2>{% trans %}Add a user{% endtrans %}</h2>
107
108 <form action="{{ path('config') }}" method="post" {{ form_enctype(newUserForm) }}>
109 {{ form_errors(newUserForm) }}
110
111 <fieldset class="w500p inline">
112 <div class="row">
113 {{ form_label(newUserForm.username) }}
114 {{ form_errors(newUserForm.username) }}
115 {{ form_widget(newUserForm.username) }}
116 </div>
117 </fieldset>
118
119 <fieldset class="w500p inline">
120 <div class="row">
121 {{ form_label(newUserForm.password) }}
122 {{ form_errors(newUserForm.password) }}
123 {{ form_widget(newUserForm.password) }}
124 </div>
125 </fieldset>
126
127 <fieldset class="w500p inline">
128 <div class="row">
129 {{ form_label(newUserForm.email) }}
130 {{ form_errors(newUserForm.email) }}
131 {{ form_widget(newUserForm.email) }}
132 </div>
133 </fieldset>
134
135 {{ form_rest(newUserForm) }}
136 </form>
137{% endblock %}
diff --git a/src/Wallabag/CoreBundle/Resources/views/_footer.html.twig b/src/Wallabag/CoreBundle/Resources/views/_footer.html.twig
index 26411da9..1b5f9a0f 100644
--- a/src/Wallabag/CoreBundle/Resources/views/_footer.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/_footer.html.twig
@@ -1,3 +1,3 @@
1 <footer class="w600p center mt3 mb3 smaller txtright"> 1<footer class="w600p center mt3 mb3 smaller txtright">
2 <p>{% trans %}powered by{% endtrans %} <a href="http://wallabag.org">wallabag</a></p> 2 <p>{% trans %}powered by{% endtrans %} <a href="http://wallabag.org">wallabag</a></p>
3 </footer> 3</footer>
diff --git a/src/Wallabag/CoreBundle/Resources/views/_head.html.twig b/src/Wallabag/CoreBundle/Resources/views/_head.html.twig
index 726b4163..3bdbe812 100755
--- a/src/Wallabag/CoreBundle/Resources/views/_head.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/_head.html.twig
@@ -1,40 +1,40 @@
1 <link rel="apple-touch-icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-152.png') }}" sizes="152x152"> 1<link rel="apple-touch-icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-152.png') }}" sizes="152x152">
2 <link rel="icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-152.png') }}" sizes="152x152"> 2<link rel="icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-152.png') }}" sizes="152x152">
3 3
4 <link rel="apple-touch-icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-144.png') }}" sizes="144x144"> 4<link rel="apple-touch-icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-144.png') }}" sizes="144x144">
5 <link rel="icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-144.png') }}" sizes="144x144"> 5<link rel="icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-144.png') }}" sizes="144x144">
6 6
7 <link rel="apple-touch-icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-120.png') }}" sizes="120x120"> 7<link rel="apple-touch-icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-120.png') }}" sizes="120x120">
8 <link rel="icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-120.png') }}" sizes="120x120"> 8<link rel="icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-120.png') }}" sizes="120x120">
9 9
10 <link rel="apple-touch-icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-114.png') }}" sizes="114x114"> 10<link rel="apple-touch-icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-114.png') }}" sizes="114x114">
11 <link rel="icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-114.png') }}" sizes="114x114"> 11<link rel="icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-114.png') }}" sizes="114x114">
12 12
13 <link rel="apple-touch-icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-76.png') }}" sizes="76x76"> 13<link rel="apple-touch-icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-76.png') }}" sizes="76x76">
14 <link rel="icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-76.png') }}" sizes="76x76"> 14<link rel="icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-76.png') }}" sizes="76x76">
15 15
16 <link rel="apple-touch-icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-72.png') }}" sizes="72x72"> 16<link rel="apple-touch-icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-72.png') }}" sizes="72x72">
17 <link rel="icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-72.png') }}" sizes="72x72"> 17<link rel="icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-72.png') }}" sizes="72x72">
18 18
19 <link rel="apple-touch-icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-57.png') }}" sizes="57x57"> 19<link rel="apple-touch-icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-57.png') }}" sizes="57x57">
20 <link rel="icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-57.png') }}" sizes="57x57"> 20<link rel="icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-57.png') }}" sizes="57x57">
21 21
22 <link rel="apple-touch-icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon.png') }}"> 22<link rel="apple-touch-icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon.png') }}">
23 <link rel="icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon.png') }}"> 23<link rel="icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon.png') }}">
24 24
25 <link rel="shortcut icon" type="image/x-icon" href="{{ asset('themes/_global/img/appicon/favicon.ico') }}"> 25<link rel="shortcut icon" type="image/x-icon" href="{{ asset('themes/_global/img/appicon/favicon.ico') }}">
26 26
27 <link rel="stylesheet" href="{{ asset('themes/baggy/css/ratatouille.css') }}" media="all"> 27<link rel="stylesheet" href="{{ asset('themes/baggy/css/ratatouille.css') }}" media="all">
28 <link rel="stylesheet" href="{{ asset('themes/baggy/css/font.css') }}" media="all"> 28<link rel="stylesheet" href="{{ asset('themes/baggy/css/font.css') }}" media="all">
29 <link rel="stylesheet" href="{{ asset('themes/baggy/css/main.css') }}" media="all"> 29<link rel="stylesheet" href="{{ asset('themes/baggy/css/main.css') }}" media="all">
30 <link rel="stylesheet" href="{{ asset('themes/baggy/css/messages.css') }}" media="all"> 30<link rel="stylesheet" href="{{ asset('themes/baggy/css/messages.css') }}" media="all">
31 <link rel="stylesheet" href="{{ asset('themes/baggy/css/print.css') }}" media="print"> 31<link rel="stylesheet" href="{{ asset('themes/baggy/css/print.css') }}" media="print">
32 32
33 <script src="{{ asset('themes/_global/js/jquery-2.0.3.min.js') }}"></script> 33<script src="{{ asset('themes/_global/js/jquery-2.0.3.min.js') }}"></script>
34 <script src="{{ asset('themes/_global/js/autoClose.js') }}"></script> 34<script src="{{ asset('themes/_global/js/autoClose.js') }}"></script>
35 <script src="{{ asset('themes/baggy/js/jquery.cookie.js') }}"></script> 35<script src="{{ asset('themes/baggy/js/jquery.cookie.js') }}"></script>
36 <script src="{{ asset('themes/baggy/js/init.js') }}"></script> 36<script src="{{ asset('themes/baggy/js/init.js') }}"></script>
37 <script src="{{ asset('themes/_global/js/saveLink.js') }}"></script> 37<script src="{{ asset('themes/_global/js/saveLink.js') }}"></script>
38 <script src="{{ asset('themes/_global/js/popupForm.js') }}"></script> 38<script src="{{ asset('themes/_global/js/popupForm.js') }}"></script>
39 <script src="{{ asset('themes/baggy/js/closeMessage.js') }}"></script> 39<script src="{{ asset('themes/baggy/js/closeMessage.js') }}"></script>
40 <script src="{{ asset('bundles/wallabagcore/js/bookmarklet.js') }}"></script> 40<script src="{{ asset('bundles/wallabagcore/js/bookmarklet.js') }}"></script>
diff --git a/src/Wallabag/CoreBundle/Resources/views/_menu.html.twig b/src/Wallabag/CoreBundle/Resources/views/_menu.html.twig
index 2e3b6d08..9a3cf053 100644
--- a/src/Wallabag/CoreBundle/Resources/views/_menu.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/_menu.html.twig
@@ -1,14 +1,14 @@
1 <button id="menu" class="icon icon-menu desktopHide"><span>Menu</span></button> 1<button id="menu" class="icon icon-menu desktopHide"><span>Menu</span></button>
2 <ul id="links" class="links"> 2<ul id="links" class="links">
3 <li><a href="{{ path('unread') }}">{% trans %}unread{% endtrans %}</a></li> 3 <li><a href="{{ path('unread') }}">{% trans %}unread{% endtrans %}</a></li>
4 <li><a href="{{ path('starred') }}">{% trans %}favorites{% endtrans %}</a></li> 4 <li><a href="{{ path('starred') }}">{% trans %}favorites{% endtrans %}</a></li>
5 <li><a href="{{ path('archive') }}"}>{% trans %}archive{% endtrans %}</a></li> 5 <li><a href="{{ path('archive') }}"}>{% trans %}archive{% endtrans %}</a></li>
6 <li><a href="?view=tags">{% trans %}tags{% endtrans %}</a></li> 6 <li><a href="?view=tags">{% trans %}tags{% endtrans %}</a></li>
7 <li><a href="{{ path('new_entry') }}">{% trans %}save a link{% endtrans %}</a></li> 7 <li><a href="{{ path('new_entry') }}">{% trans %}save a link{% endtrans %}</a></li>
8 <li style="position: relative;"><a href="javascript: void(null);" id="search">{% trans %}search{% endtrans %}</a> 8 <li style="position: relative;"><a href="javascript: void(null);" id="search">{% trans %}search{% endtrans %}</a>
9 {% include "WallabagCoreBundle::_search_form.html.twig" %} 9 {% include "WallabagCoreBundle::_search_form.html.twig" %}
10 </li> 10 </li>
11 <li><a href="?view=config">{% trans %}config{% endtrans %}</a></li> 11 <li><a href="{{ path('config') }}">{% trans %}config{% endtrans %}</a></li>
12 <li><a href={{ path('about') }}>{% trans %}about{% endtrans %}</a></li> 12 <li><a href="{{ path('about') }}">{% trans %}about{% endtrans %}</a></li>
13 <li><a class="icon icon-power" href="{{ path('logout') }}" title="{% trans %}logout{% endtrans %}">{% trans %}logout{% endtrans %}</a></li> 13 <li><a class="icon icon-power" href="{{ path('logout') }}" title="{% trans %}logout{% endtrans %}">{% trans %}logout{% endtrans %}</a></li>
14 </ul> 14</ul>
diff --git a/src/Wallabag/CoreBundle/Resources/views/_search_form.html.twig b/src/Wallabag/CoreBundle/Resources/views/_search_form.html.twig
index 7eb1b67d..1e6f327d 100644
--- a/src/Wallabag/CoreBundle/Resources/views/_search_form.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/_search_form.html.twig
@@ -1,9 +1,9 @@
1<div id="search-form" class="messages info popup-form"> 1<div id="search-form" class="messages info popup-form">
2<form method="get" action="index.php"> 2 <form method="get" action="index.php">
3 <h2>{% trans %}Search{% endtrans %}</h2> 3 <h2>{% trans %}Search{% endtrans %}</h2>
4 <a href="javascript: void(null);" id="search-form-close" class="close-button--popup close-button">&times;</a> 4 <a href="javascript: void(null);" id="search-form-close" class="close-button--popup close-button">&times;</a>
5 <input type="hidden" name="view" value="search"></input> 5 <input type="hidden" name="view" value="search"></input>
6 <input required placeholder="{% trans %}Enter your search here{% endtrans %}" type="text" name="search" id="searchfield"><br> 6 <input required placeholder="{% trans %}Enter your search here{% endtrans %}" type="text" name="search" id="searchfield"><br>
7 <input id="submit-search" type="submit" value="{% trans %}Search{% endtrans %}"></input> 7 <input id="submit-search" type="submit" value="{% trans %}Search{% endtrans %}"></input>
8</form> 8 </form>
9</div> 9</div>
diff --git a/src/Wallabag/CoreBundle/Resources/views/_top.html.twig b/src/Wallabag/CoreBundle/Resources/views/_top.html.twig
index 34d925df..9313071d 100755
--- a/src/Wallabag/CoreBundle/Resources/views/_top.html.twig
+++ b/src/Wallabag/CoreBundle/Resources/views/_top.html.twig
@@ -1,5 +1,5 @@
1 <header class="w600p center mbm"> 1<header class="w600p center mbm">
2 <h1> 2 <h1>
3 {% block logo %}<img width="100" height="100" src="{{ asset('themes/baggy/img/logo-w.png') }}" alt="wallabag logo" />{% endblock %} 3 {% block logo %}<img width="100" height="100" src="{{ asset('themes/baggy/img/logo-w.png') }}" alt="wallabag logo" />{% endblock %}
4 </h1> 4 </h1>
5 </header> 5</header>
diff --git a/src/Wallabag/CoreBundle/Security/Authentication/Encoder/WallabagPasswordEncoder.php b/src/Wallabag/CoreBundle/Security/Authentication/Encoder/WallabagPasswordEncoder.php
index 56f1affe..fcfe418b 100644
--- a/src/Wallabag/CoreBundle/Security/Authentication/Encoder/WallabagPasswordEncoder.php
+++ b/src/Wallabag/CoreBundle/Security/Authentication/Encoder/WallabagPasswordEncoder.php
@@ -41,10 +41,6 @@ class WallabagPasswordEncoder extends BasePasswordEncoder
41 */ 41 */
42 public function encodePassword($raw, $salt) 42 public function encodePassword($raw, $salt)
43 { 43 {
44 if (null === $this->username) {
45 throw new \LogicException('We can not check the password without a username.');
46 }
47
48 if ($this->isPasswordTooLong($raw)) { 44 if ($this->isPasswordTooLong($raw)) {
49 throw new BadCredentialsException('Invalid password.'); 45 throw new BadCredentialsException('Invalid password.');
50 } 46 }
@@ -71,6 +67,10 @@ class WallabagPasswordEncoder extends BasePasswordEncoder
71 */ 67 */
72 protected function mergePasswordAndSalt($password, $salt) 68 protected function mergePasswordAndSalt($password, $salt)
73 { 69 {
70 if (null === $this->username) {
71 throw new \LogicException('We can not check the password without a username.');
72 }
73
74 if (empty($salt)) { 74 if (empty($salt)) {
75 return $password; 75 return $password;
76 } 76 }
diff --git a/src/Wallabag/CoreBundle/Security/Validator/WallabagUserPasswordValidator.php b/src/Wallabag/CoreBundle/Security/Validator/WallabagUserPasswordValidator.php
new file mode 100644
index 00000000..5586f976
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Security/Validator/WallabagUserPasswordValidator.php
@@ -0,0 +1,48 @@
1<?php
2
3namespace Wallabag\CoreBundle\Security\Validator;
4
5use Symfony\Component\Security\Core\User\UserInterface;
6use Symfony\Component\Security\Core\SecurityContextInterface;
7use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
8use Symfony\Component\Validator\Constraint;
9use Symfony\Component\Validator\ConstraintValidator;
10use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
11use Symfony\Component\Validator\Exception\UnexpectedTypeException;
12use Symfony\Component\Security\Core\Validator\Constraints\UserPassword;
13
14class WallabagUserPasswordValidator extends ConstraintValidator
15{
16 private $securityContext;
17 private $encoderFactory;
18
19 public function __construct(SecurityContextInterface $securityContext, EncoderFactoryInterface $encoderFactory)
20 {
21 $this->securityContext = $securityContext;
22 $this->encoderFactory = $encoderFactory;
23 }
24
25 /**
26 * {@inheritdoc}
27 */
28 public function validate($password, Constraint $constraint)
29 {
30 if (!$constraint instanceof UserPassword) {
31 throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\UserPassword');
32 }
33
34 $user = $this->securityContext->getToken()->getUser();
35
36 if (!$user instanceof UserInterface) {
37 throw new ConstraintDefinitionException('The User object must implement the UserInterface interface.');
38 }
39
40 // give username, it's used to hash the password
41 $encoder = $this->encoderFactory->getEncoder($user);
42 $encoder->setUsername($user->getUsername());
43
44 if (!$encoder->isPasswordValid($user->getPassword(), $password, $user->getSalt())) {
45 $this->context->addViolation($constraint->message);
46 }
47 }
48}
diff --git a/src/Wallabag/CoreBundle/Tests/Command/InstallCommandTest.php b/src/Wallabag/CoreBundle/Tests/Command/InstallCommandTest.php
new file mode 100644
index 00000000..64f6c329
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Tests/Command/InstallCommandTest.php
@@ -0,0 +1,274 @@
1<?php
2
3namespace Wallabag\CoreBundle\Tests\Command;
4
5use Wallabag\CoreBundle\Tests\WallabagTestCase;
6use Wallabag\CoreBundle\Command\InstallCommand;
7use Symfony\Bundle\FrameworkBundle\Console\Application;
8use Symfony\Component\Console\Tester\CommandTester;
9use Symfony\Component\Console\Input\ArrayInput;
10use Symfony\Component\Console\Output\NullOutput;
11use Doctrine\Bundle\DoctrineBundle\Command\DropDatabaseDoctrineCommand;
12use Doctrine\Bundle\DoctrineBundle\Command\CreateDatabaseDoctrineCommand;
13
14class InstallCommandTest extends WallabagTestCase
15{
16 public static function tearDownAfterClass()
17 {
18 $application = new Application(static::$kernel);
19 $application->setAutoExit(false);
20
21 $code = $application->run(new ArrayInput(array(
22 'command' => 'doctrine:fixtures:load',
23 '--no-interaction' => true,
24 '--env' => 'test',
25 )), new NullOutput());
26 }
27
28 public function testRunInstallCommand()
29 {
30 $this->container = static::$kernel->getContainer();
31
32 $application = new Application(static::$kernel);
33 $application->add(new InstallCommand());
34
35 $command = $application->find('wallabag:install');
36
37 // We mock the DialogHelper
38 $dialog = $this->getMockBuilder('Symfony\Component\Console\Helper\DialogHelper')
39 ->disableOriginalConstructor()
40 ->getMock();
41 $dialog->expects($this->any())
42 ->method('ask')
43 ->will($this->returnValue('test'));
44 $dialog->expects($this->any())
45 ->method('askConfirmation')
46 ->will($this->returnValue(true));
47
48 // We override the standard helper with our mock
49 $command->getHelperSet()->set($dialog, 'dialog');
50
51 $tester = new CommandTester($command);
52 $tester->execute(array(
53 'command' => $command->getName(),
54 ));
55
56 $this->assertContains('Step 1 of 4. Checking system requirements.', $tester->getDisplay());
57 $this->assertContains('Step 2 of 4. Setting up database.', $tester->getDisplay());
58 $this->assertContains('Step 3 of 4. Administration setup.', $tester->getDisplay());
59 $this->assertContains('Step 4 of 4. Installing assets.', $tester->getDisplay());
60 }
61
62 public function testRunInstallCommandWithReset()
63 {
64 $this->container = static::$kernel->getContainer();
65
66 $application = new Application(static::$kernel);
67 $application->add(new InstallCommand());
68
69 $command = $application->find('wallabag:install');
70
71 // We mock the DialogHelper
72 $dialog = $this->getMockBuilder('Symfony\Component\Console\Helper\DialogHelper')
73 ->disableOriginalConstructor()
74 ->getMock();
75 $dialog->expects($this->any())
76 ->method('ask')
77 ->will($this->returnValue('test'));
78 $dialog->expects($this->any())
79 ->method('askConfirmation')
80 ->will($this->returnValue(true));
81
82 // We override the standard helper with our mock
83 $command->getHelperSet()->set($dialog, 'dialog');
84
85 $tester = new CommandTester($command);
86 $tester->execute(array(
87 'command' => $command->getName(),
88 '--reset' => true,
89 ));
90
91 $this->assertContains('Step 1 of 4. Checking system requirements.', $tester->getDisplay());
92 $this->assertContains('Step 2 of 4. Setting up database.', $tester->getDisplay());
93 $this->assertContains('Step 3 of 4. Administration setup.', $tester->getDisplay());
94 $this->assertContains('Step 4 of 4. Installing assets.', $tester->getDisplay());
95
96 // we force to reset everything
97 $this->assertContains('Droping database, creating database and schema', $tester->getDisplay());
98 }
99
100 public function testRunInstallCommandWithDatabaseRemoved()
101 {
102 $this->container = static::$kernel->getContainer();
103
104 $application = new Application(static::$kernel);
105 $application->add(new InstallCommand());
106 $application->add(new DropDatabaseDoctrineCommand());
107
108 // drop database first, so the install command won't ask to reset things
109 $command = new DropDatabaseDoctrineCommand();
110 $command->setApplication($application);
111 $command->run(new ArrayInput(array(
112 'command' => 'doctrine:database:drop',
113 '--force' => true,
114 )), new NullOutput());
115
116 $command = $application->find('wallabag:install');
117
118 // We mock the DialogHelper
119 $dialog = $this->getMockBuilder('Symfony\Component\Console\Helper\DialogHelper')
120 ->disableOriginalConstructor()
121 ->getMock();
122 $dialog->expects($this->any())
123 ->method('ask')
124 ->will($this->returnValue('test'));
125 $dialog->expects($this->any())
126 ->method('askConfirmation')
127 ->will($this->returnValue(true));
128
129 // We override the standard helper with our mock
130 $command->getHelperSet()->set($dialog, 'dialog');
131
132 $tester = new CommandTester($command);
133 $tester->execute(array(
134 'command' => $command->getName(),
135 ));
136
137 $this->assertContains('Step 1 of 4. Checking system requirements.', $tester->getDisplay());
138 $this->assertContains('Step 2 of 4. Setting up database.', $tester->getDisplay());
139 $this->assertContains('Step 3 of 4. Administration setup.', $tester->getDisplay());
140 $this->assertContains('Step 4 of 4. Installing assets.', $tester->getDisplay());
141
142 // the current database doesn't already exist
143 $this->assertContains('Creating database and schema, clearing the cache', $tester->getDisplay());
144 }
145
146 public function testRunInstallCommandChooseResetSchema()
147 {
148 $this->container = static::$kernel->getContainer();
149
150 $application = new Application(static::$kernel);
151 $application->add(new InstallCommand());
152
153 $command = $application->find('wallabag:install');
154
155 // We mock the DialogHelper
156 $dialog = $this->getMockBuilder('Symfony\Component\Console\Helper\DialogHelper')
157 ->disableOriginalConstructor()
158 ->getMock();
159
160 $dialog->expects($this->exactly(3))
161 ->method('askConfirmation')
162 ->will($this->onConsecutiveCalls(
163 false, // don't want to reset the entire database
164 true, // do want to reset the schema
165 false // don't want to create a new user
166 ));
167
168 // We override the standard helper with our mock
169 $command->getHelperSet()->set($dialog, 'dialog');
170
171 $tester = new CommandTester($command);
172 $tester->execute(array(
173 'command' => $command->getName(),
174 ));
175
176 $this->assertContains('Step 1 of 4. Checking system requirements.', $tester->getDisplay());
177 $this->assertContains('Step 2 of 4. Setting up database.', $tester->getDisplay());
178 $this->assertContains('Step 3 of 4. Administration setup.', $tester->getDisplay());
179 $this->assertContains('Step 4 of 4. Installing assets.', $tester->getDisplay());
180
181 $this->assertContains('Droping schema and creating schema', $tester->getDisplay());
182 }
183
184 public function testRunInstallCommandChooseNothing()
185 {
186 $this->container = static::$kernel->getContainer();
187
188 $application = new Application(static::$kernel);
189 $application->add(new InstallCommand());
190 $application->add(new DropDatabaseDoctrineCommand());
191 $application->add(new CreateDatabaseDoctrineCommand());
192
193 // drop database first, so the install command won't ask to reset things
194 $command = new DropDatabaseDoctrineCommand();
195 $command->setApplication($application);
196 $command->run(new ArrayInput(array(
197 'command' => 'doctrine:database:drop',
198 '--force' => true,
199 )), new NullOutput());
200
201 $this->container->get('doctrine')->getManager()->getConnection()->close();
202
203 $command = new CreateDatabaseDoctrineCommand();
204 $command->setApplication($application);
205 $command->run(new ArrayInput(array(
206 'command' => 'doctrine:database:create',
207 '--env' => 'test',
208 )), new NullOutput());
209
210 $command = $application->find('wallabag:install');
211
212 // We mock the DialogHelper
213 $dialog = $this->getMockBuilder('Symfony\Component\Console\Helper\DialogHelper')
214 ->disableOriginalConstructor()
215 ->getMock();
216
217 $dialog->expects($this->exactly(2))
218 ->method('askConfirmation')
219 ->will($this->onConsecutiveCalls(
220 false, // don't want to reset the entire database
221 false // don't want to create a new user
222 ));
223
224 // We override the standard helper with our mock
225 $command->getHelperSet()->set($dialog, 'dialog');
226
227 $tester = new CommandTester($command);
228 $tester->execute(array(
229 'command' => $command->getName(),
230 ));
231
232 $this->assertContains('Step 1 of 4. Checking system requirements.', $tester->getDisplay());
233 $this->assertContains('Step 2 of 4. Setting up database.', $tester->getDisplay());
234 $this->assertContains('Step 3 of 4. Administration setup.', $tester->getDisplay());
235 $this->assertContains('Step 4 of 4. Installing assets.', $tester->getDisplay());
236
237 $this->assertContains('Creating schema', $tester->getDisplay());
238 }
239
240 public function testRunInstallCommandNoInteraction()
241 {
242 $this->container = static::$kernel->getContainer();
243
244 $application = new Application(static::$kernel);
245 $application->add(new InstallCommand());
246
247 $command = $application->find('wallabag:install');
248
249 // We mock the DialogHelper
250 $dialog = $this->getMockBuilder('Symfony\Component\Console\Helper\DialogHelper')
251 ->disableOriginalConstructor()
252 ->getMock();
253 $dialog->expects($this->any())
254 ->method('ask')
255 ->will($this->returnValue('test'));
256 $dialog->expects($this->any())
257 ->method('askConfirmation')
258 ->will($this->returnValue(true));
259
260 // We override the standard helper with our mock
261 $command->getHelperSet()->set($dialog, 'dialog');
262
263 $tester = new CommandTester($command);
264 $tester->execute(array(
265 'command' => $command->getName(),
266 '--no-interaction' => true,
267 ));
268
269 $this->assertContains('Step 1 of 4. Checking system requirements.', $tester->getDisplay());
270 $this->assertContains('Step 2 of 4. Setting up database.', $tester->getDisplay());
271 $this->assertContains('Step 3 of 4. Administration setup.', $tester->getDisplay());
272 $this->assertContains('Step 4 of 4. Installing assets.', $tester->getDisplay());
273 }
274}
diff --git a/src/Wallabag/CoreBundle/Tests/Controller/ConfigControllerTest.php b/src/Wallabag/CoreBundle/Tests/Controller/ConfigControllerTest.php
new file mode 100644
index 00000000..9b1a0986
--- /dev/null
+++ b/src/Wallabag/CoreBundle/Tests/Controller/ConfigControllerTest.php
@@ -0,0 +1,350 @@
1<?php
2
3namespace Wallabag\CoreBundle\Tests\Controller;
4
5use Wallabag\CoreBundle\Tests\WallabagTestCase;
6
7class ConfigControllerTest extends WallabagTestCase
8{
9 public function testLogin()
10 {
11 $client = $this->getClient();
12
13 $client->request('GET', '/new');
14
15 $this->assertEquals(302, $client->getResponse()->getStatusCode());
16 $this->assertContains('login', $client->getResponse()->headers->get('location'));
17 }
18
19 public function testIndex()
20 {
21 $this->logInAs('admin');
22 $client = $this->getClient();
23
24 $crawler = $client->request('GET', '/config');
25
26 $this->assertEquals(200, $client->getResponse()->getStatusCode());
27
28 $this->assertCount(1, $crawler->filter('button[id=config_save]'));
29 $this->assertCount(1, $crawler->filter('button[id=change_passwd_save]'));
30 $this->assertCount(1, $crawler->filter('button[id=user_save]'));
31 }
32
33 public function testUpdate()
34 {
35 $this->logInAs('admin');
36 $client = $this->getClient();
37
38 $crawler = $client->request('GET', '/config');
39
40 $this->assertEquals(200, $client->getResponse()->getStatusCode());
41
42 $form = $crawler->filter('button[id=config_save]')->form();
43
44 $data = array(
45 'config[theme]' => 'baggy',
46 'config[items_per_page]' => '30',
47 'config[language]' => 'fr_FR',
48 );
49
50 $client->submit($form, $data);
51
52 $this->assertEquals(302, $client->getResponse()->getStatusCode());
53
54 $crawler = $client->followRedirect();
55
56 $this->assertGreaterThan(1, $alert = $crawler->filter('div.flash-notice')->extract(array('_text')));
57 $this->assertContains('Config saved', $alert[0]);
58 }
59
60 public function dataForUpdateFailed()
61 {
62 return array(
63 array(array(
64 'config[theme]' => 'baggy',
65 'config[items_per_page]' => '',
66 'config[language]' => 'fr_FR',
67 )),
68 array(array(
69 'config[theme]' => 'baggy',
70 'config[items_per_page]' => '12',
71 'config[language]' => '',
72 )),
73 );
74 }
75
76 /**
77 * @dataProvider dataForUpdateFailed
78 */
79 public function testUpdateFailed($data)
80 {
81 $this->logInAs('admin');
82 $client = $this->getClient();
83
84 $crawler = $client->request('GET', '/config');
85
86 $this->assertEquals(200, $client->getResponse()->getStatusCode());
87
88 $form = $crawler->filter('button[id=config_save]')->form();
89
90 $crawler = $client->submit($form, $data);
91
92 $this->assertEquals(200, $client->getResponse()->getStatusCode());
93
94 $this->assertGreaterThan(1, $alert = $crawler->filter('body')->extract(array('_text')));
95 $this->assertContains('This value should not be blank', $alert[0]);
96 }
97
98 public function dataForChangePasswordFailed()
99 {
100 return array(
101 array(
102 array(
103 'change_passwd[old_password]' => 'baggy',
104 'change_passwd[new_password][first]' => '',
105 'change_passwd[new_password][second]' => '',
106 ),
107 'Wrong value for your current password',
108 ),
109 array(
110 array(
111 'change_passwd[old_password]' => 'mypassword',
112 'change_passwd[new_password][first]' => '',
113 'change_passwd[new_password][second]' => '',
114 ),
115 'This value should not be blank',
116 ),
117 array(
118 array(
119 'change_passwd[old_password]' => 'mypassword',
120 'change_passwd[new_password][first]' => 'hop',
121 'change_passwd[new_password][second]' => '',
122 ),
123 'The password fields must match',
124 ),
125 array(
126 array(
127 'change_passwd[old_password]' => 'mypassword',
128 'change_passwd[new_password][first]' => 'hop',
129 'change_passwd[new_password][second]' => 'hop',
130 ),
131 'Password should by at least',
132 ),
133 );
134 }
135
136 /**
137 * @dataProvider dataForChangePasswordFailed
138 */
139 public function testChangePasswordFailed($data, $expectedMessage)
140 {
141 $this->logInAs('admin');
142 $client = $this->getClient();
143
144 $crawler = $client->request('GET', '/config');
145
146 $this->assertEquals(200, $client->getResponse()->getStatusCode());
147
148 $form = $crawler->filter('button[id=change_passwd_save]')->form();
149
150 $crawler = $client->submit($form, $data);
151
152 $this->assertEquals(200, $client->getResponse()->getStatusCode());
153
154 $this->assertGreaterThan(1, $alert = $crawler->filter('body')->extract(array('_text')));
155 $this->assertContains($expectedMessage, $alert[0]);
156 }
157
158 public function testChangePassword()
159 {
160 $this->logInAs('admin');
161 $client = $this->getClient();
162
163 $crawler = $client->request('GET', '/config');
164
165 $this->assertEquals(200, $client->getResponse()->getStatusCode());
166
167 $form = $crawler->filter('button[id=change_passwd_save]')->form();
168
169 $data = array(
170 'change_passwd[old_password]' => 'mypassword',
171 'change_passwd[new_password][first]' => 'mypassword',
172 'change_passwd[new_password][second]' => 'mypassword',
173 );
174
175 $client->submit($form, $data);
176
177 $this->assertEquals(302, $client->getResponse()->getStatusCode());
178
179 $crawler = $client->followRedirect();
180
181 $this->assertGreaterThan(1, $alert = $crawler->filter('div.flash-notice')->extract(array('_text')));
182 $this->assertContains('Password updated', $alert[0]);
183 }
184
185 public function dataForUserFailed()
186 {
187 return array(
188 array(
189 array(
190 'user[username]' => '',
191 'user[name]' => '',
192 'user[email]' => '',
193 ),
194 'This value should not be blank.',
195 ),
196 array(
197 array(
198 'user[username]' => 'ad',
199 'user[name]' => '',
200 'user[email]' => '',
201 ),
202 'This value is too short.',
203 ),
204 array(
205 array(
206 'user[username]' => 'admin',
207 'user[name]' => '',
208 'user[email]' => 'test',
209 ),
210 'This value is not a valid email address.',
211 ),
212 );
213 }
214
215 /**
216 * @dataProvider dataForUserFailed
217 */
218 public function testUserFailed($data, $expectedMessage)
219 {
220 $this->logInAs('admin');
221 $client = $this->getClient();
222
223 $crawler = $client->request('GET', '/config');
224
225 $this->assertEquals(200, $client->getResponse()->getStatusCode());
226
227 $form = $crawler->filter('button[id=user_save]')->form();
228
229 $crawler = $client->submit($form, $data);
230
231 $this->assertEquals(200, $client->getResponse()->getStatusCode());
232
233 $this->assertGreaterThan(1, $alert = $crawler->filter('body')->extract(array('_text')));
234 $this->assertContains($expectedMessage, $alert[0]);
235 }
236
237 public function testUserUpdate()
238 {
239 $this->logInAs('admin');
240 $client = $this->getClient();
241
242 $crawler = $client->request('GET', '/config');
243
244 $this->assertEquals(200, $client->getResponse()->getStatusCode());
245
246 $form = $crawler->filter('button[id=user_save]')->form();
247
248 $data = array(
249 'user[username]' => 'admin',
250 'user[name]' => 'new name',
251 'user[email]' => 'admin@wallabag.io',
252 );
253
254 $client->submit($form, $data);
255
256 $this->assertEquals(302, $client->getResponse()->getStatusCode());
257
258 $crawler = $client->followRedirect();
259
260 $this->assertGreaterThan(1, $alert = $crawler->filter('div.flash-notice')->extract(array('_text')));
261 $this->assertContains('Information updated', $alert[0]);
262 }
263
264 public function dataForNewUserFailed()
265 {
266 return array(
267 array(
268 array(
269 'new_user[username]' => '',
270 'new_user[password]' => '',
271 'new_user[email]' => '',
272 ),
273 'This value should not be blank.',
274 ),
275 array(
276 array(
277 'new_user[username]' => 'ad',
278 'new_user[password]' => '',
279 'new_user[email]' => '',
280 ),
281 'This value is too short.',
282 ),
283 array(
284 array(
285 'new_user[username]' => 'wallace',
286 'new_user[password]' => '',
287 'new_user[email]' => 'test',
288 ),
289 'This value is not a valid email address.',
290 ),
291 array(
292 array(
293 'new_user[username]' => 'wallace',
294 'new_user[password]' => 'admin',
295 'new_user[email]' => 'wallace@wallace.me',
296 ),
297 'Password should by at least',
298 ),
299 );
300 }
301
302 /**
303 * @dataProvider dataForNewUserFailed
304 */
305 public function testNewUserFailed($data, $expectedMessage)
306 {
307 $this->logInAs('admin');
308 $client = $this->getClient();
309
310 $crawler = $client->request('GET', '/config');
311
312 $this->assertEquals(200, $client->getResponse()->getStatusCode());
313
314 $form = $crawler->filter('button[id=new_user_save]')->form();
315
316 $crawler = $client->submit($form, $data);
317
318 $this->assertEquals(200, $client->getResponse()->getStatusCode());
319
320 $this->assertGreaterThan(1, $alert = $crawler->filter('body')->extract(array('_text')));
321 $this->assertContains($expectedMessage, $alert[0]);
322 }
323
324 public function testNewUserCreated()
325 {
326 $this->logInAs('admin');
327 $client = $this->getClient();
328
329 $crawler = $client->request('GET', '/config');
330
331 $this->assertEquals(200, $client->getResponse()->getStatusCode());
332
333 $form = $crawler->filter('button[id=new_user_save]')->form();
334
335 $data = array(
336 'new_user[username]' => 'wallace',
337 'new_user[password]' => 'wallace1',
338 'new_user[email]' => 'wallace@wallace.me',
339 );
340
341 $client->submit($form, $data);
342
343 $this->assertEquals(302, $client->getResponse()->getStatusCode());
344
345 $crawler = $client->followRedirect();
346
347 $this->assertGreaterThan(1, $alert = $crawler->filter('div.flash-notice')->extract(array('_text')));
348 $this->assertContains('User "wallace" added', $alert[0]);
349 }
350}
diff --git a/src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php b/src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php
index 7276f8e4..2634141e 100644
--- a/src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php
+++ b/src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php
@@ -60,7 +60,7 @@ class EntryControllerTest extends WallabagTestCase
60 $form = $crawler->filter('button[type=submit]')->form(); 60 $form = $crawler->filter('button[type=submit]')->form();
61 61
62 $data = array( 62 $data = array(
63 'form[url]' => 'https://www.mailjet.com/blog/mailjet-zapier-integrations-made-easy/', 63 'entry[url]' => 'https://www.mailjet.com/blog/mailjet-zapier-integrations-made-easy/',
64 ); 64 );
65 65
66 $client->submit($form, $data); 66 $client->submit($form, $data);
diff --git a/src/Wallabag/CoreBundle/Tests/Controller/WallabagRestControllerTest.php b/src/Wallabag/CoreBundle/Tests/Controller/WallabagRestControllerTest.php
index d77e2303..fcfa8ccf 100644
--- a/src/Wallabag/CoreBundle/Tests/Controller/WallabagRestControllerTest.php
+++ b/src/Wallabag/CoreBundle/Tests/Controller/WallabagRestControllerTest.php
@@ -47,7 +47,7 @@ class WallabagRestControllerTest extends WallabagTestCase
47 $client->request('GET', '/api/salts/admin.json'); 47 $client->request('GET', '/api/salts/admin.json');
48 $salt = json_decode($client->getResponse()->getContent()); 48 $salt = json_decode($client->getResponse()->getContent());
49 49
50 $headers = $this->generateHeaders('admin', 'test', $salt[0]); 50 $headers = $this->generateHeaders('admin', 'mypassword', $salt[0]);
51 51
52 $entry = $client->getContainer() 52 $entry = $client->getContainer()
53 ->get('doctrine.orm.entity_manager') 53 ->get('doctrine.orm.entity_manager')
@@ -73,7 +73,7 @@ class WallabagRestControllerTest extends WallabagTestCase
73 $client->request('GET', '/api/salts/admin.json'); 73 $client->request('GET', '/api/salts/admin.json');
74 $salt = json_decode($client->getResponse()->getContent()); 74 $salt = json_decode($client->getResponse()->getContent());
75 75
76 $headers = $this->generateHeaders('admin', 'test', $salt[0]); 76 $headers = $this->generateHeaders('admin', 'mypassword', $salt[0]);
77 77
78 $entry = $client->getContainer() 78 $entry = $client->getContainer()
79 ->get('doctrine.orm.entity_manager') 79 ->get('doctrine.orm.entity_manager')
@@ -101,7 +101,7 @@ class WallabagRestControllerTest extends WallabagTestCase
101 $client->request('GET', '/api/salts/admin.json'); 101 $client->request('GET', '/api/salts/admin.json');
102 $salt = json_decode($client->getResponse()->getContent()); 102 $salt = json_decode($client->getResponse()->getContent());
103 103
104 $headers = $this->generateHeaders('admin', 'test', $salt[0]); 104 $headers = $this->generateHeaders('admin', 'mypassword', $salt[0]);
105 105
106 $client->request('GET', '/api/entries', array(), array(), $headers); 106 $client->request('GET', '/api/entries', array(), array(), $headers);
107 107
@@ -125,7 +125,7 @@ class WallabagRestControllerTest extends WallabagTestCase
125 $client->request('GET', '/api/salts/admin.json'); 125 $client->request('GET', '/api/salts/admin.json');
126 $salt = json_decode($client->getResponse()->getContent()); 126 $salt = json_decode($client->getResponse()->getContent());
127 127
128 $headers = $this->generateHeaders('admin', 'test', $salt[0]); 128 $headers = $this->generateHeaders('admin', 'mypassword', $salt[0]);
129 129
130 $entry = $client->getContainer() 130 $entry = $client->getContainer()
131 ->get('doctrine.orm.entity_manager') 131 ->get('doctrine.orm.entity_manager')
diff --git a/src/Wallabag/CoreBundle/Tests/WallabagTestCase.php b/src/Wallabag/CoreBundle/Tests/WallabagTestCase.php
index a80b8bac..22016d8e 100644
--- a/src/Wallabag/CoreBundle/Tests/WallabagTestCase.php
+++ b/src/Wallabag/CoreBundle/Tests/WallabagTestCase.php
@@ -4,7 +4,7 @@ namespace Wallabag\CoreBundle\Tests;
4 4
5use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; 5use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
6 6
7class WallabagTestCase extends WebTestCase 7abstract class WallabagTestCase extends WebTestCase
8{ 8{
9 private $client = null; 9 private $client = null;
10 10
@@ -24,7 +24,7 @@ class WallabagTestCase extends WebTestCase
24 $form = $crawler->filter('button[type=submit]')->form(); 24 $form = $crawler->filter('button[type=submit]')->form();
25 $data = array( 25 $data = array(
26 '_username' => $username, 26 '_username' => $username,
27 '_password' => 'test', 27 '_password' => 'mypassword',
28 ); 28 );
29 29
30 $this->client->submit($form, $data); 30 $this->client->submit($form, $data);