framework:
#esi: ~
- #translator: { fallback: "%locale%" }
+ translator: { fallback: "%locale%" }
secret: "%secret%"
router:
resource: "%kernel.root_dir%/config/routing.yml"
routing_loader:
default_format: json
-nelmio_api_doc: ~
\ No newline at end of file
+nelmio_api_doc: ~
+++ /dev/null
-<?php
-/**
- * wallabag, self hostable application allowing you to not miss any content anymore
- *
- * @category wallabag
- * @author Nicolas Lœuillet <nicolas@loeuillet.org>
- * @copyright 2013
- * @license http://opensource.org/licenses/MIT see COPYING file
- */
-
-define('ROOT', dirname(__FILE__) . '/../..');
-
-require_once ROOT . '/vendor/autoload.php';
-
-# system configuration
-require_once __DIR__ . '/config.inc.php';
-require_once __DIR__ . '/config.inc.default.php';
-
-if (!ini_get('date.timezone') || !@date_default_timezone_set(ini_get('date.timezone'))) {
- date_default_timezone_set('UTC');
-}
-
-if (defined('ERROR_REPORTING')) {
- error_reporting(ERROR_REPORTING);
-}
database_name: symfony
database_user: root
database_password: ~
- database_path: "%kernel.root_dir%/../data/db/poche.sqlite"
+ database_path: "%kernel.root_dir%/../data/db/wallabag.sqlite"
mailer_transport: smtp
mailer_host: 127.0.0.1
export_mobi: true
export_pdf: true
- # List view
- items_on_page: 12
\ No newline at end of file
+ # default user config
+ items_on_page: 12
+ theme: baggy
+ language: en_US
parameters:
security.authentication.provider.dao.class: Wallabag\CoreBundle\Security\Authentication\Provider\WallabagAuthenticationProvider
security.encoder.digest.class: Wallabag\CoreBundle\Security\Authentication\Encoder\WallabagPasswordEncoder
+ security.validator.user_password.class: Wallabag\CoreBundle\Security\Validator\WallabagUserPasswordValidator
services:
# service_name:
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\Console\Output\NullOutput;
use Wallabag\CoreBundle\Entity\User;
-use Wallabag\CoreBundle\Entity\UsersConfig;
+use Wallabag\CoreBundle\Entity\Config;
class InstallCommand extends ContainerAwareCommand
{
+ /**
+ * @var InputInterface
+ */
+ protected $defaultInput;
+
+ /**
+ * @var OutputInterface
+ */
+ protected $defaultOutput;
+
protected function configure()
{
$this
->setName('wallabag:install')
->setDescription('Wallabag installer.')
+ ->addOption(
+ 'reset',
+ null,
+ InputOption::VALUE_NONE,
+ 'Reset current database'
+ )
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
- $output->writeln('<info>Installing Wallabag.</info>');
+ $this->defaultInput = $input;
+ $this->defaultOutput = $output;
+
+ $output->writeln('<info>Installing Wallabag...</info>');
$output->writeln('');
$this
- ->checkStep($output)
- ->setupStep($input, $output)
+ ->checkRequirements()
+ ->setupDatabase()
+ ->setupAdmin()
+ ->setupAsset()
;
$output->writeln('<info>Wallabag has been successfully installed.</info>');
$output->writeln('<comment>Just execute `php app/console server:run` for using wallabag: http://localhost:8000</comment>');
}
- protected function checkStep(OutputInterface $output)
+ protected function checkRequirements()
{
- $output->writeln('<info>Checking system requirements.</info>');
+ $this->defaultOutput->writeln('<info><comment>Step 1 of 4.</comment> Checking system requirements.</info>');
$fulfilled = true;
// @TODO: find a better way to check requirements
- $output->writeln('<comment>Check PCRE</comment>');
+ $label = '<comment>PCRE</comment>';
if (extension_loaded('pcre')) {
- $output->writeln(' <info>OK</info>');
+ $status = '<info>OK!</info>';
+ $help = '';
} else {
$fulfilled = false;
- $output->writeln(' <error>ERROR</error>');
- $output->writeln('<comment>You should enabled PCRE extension</comment>');
+ $status = '<error>ERROR!</error>';
+ $help = 'You should enabled PCRE extension';
}
+ $rows[] = array($label, $status, $help);
- $output->writeln('<comment>Check DOM</comment>');
+ $label = '<comment>DOM</comment>';
if (extension_loaded('DOM')) {
- $output->writeln(' <info>OK</info>');
+ $status = '<info>OK!</info>';
+ $help = '';
} else {
$fulfilled = false;
- $output->writeln(' <error>ERROR</error>');
- $output->writeln('<comment>You should enabled DOM extension</comment>');
+ $status = '<error>ERROR!</error>';
+ $help = 'You should enabled DOM extension';
}
+ $rows[] = array($label, $status, $help);
+
+ $this->getHelper('table')
+ ->setHeaders(array('Checked', 'Status', 'Recommendation'))
+ ->setRows($rows)
+ ->render($this->defaultOutput);
if (!$fulfilled) {
- throw new RuntimeException('Some system requirements are not fulfilled. Please check output messages and fix them.');
+ throw new \RuntimeException('Some system requirements are not fulfilled. Please check output messages and fix them.');
+ } else {
+ $this->defaultOutput->writeln('<info>Success! Your system can run Wallabag properly.</info>');
}
- $output->writeln('');
+ $this->defaultOutput->writeln('');
return $this;
}
- protected function setupStep(InputInterface $input, OutputInterface $output)
+ protected function setupDatabase()
{
- $output->writeln('<info>Setting up database.</info>');
+ $this->defaultOutput->writeln('<info><comment>Step 2 of 4.</comment> Setting up database.</info>');
- $this->setupDatabase($input, $output);
+ // user want to reset everything? Don't care about what is already here
+ if (true === $this->defaultInput->getOption('reset')) {
+ $this->defaultOutput->writeln('Droping database, creating database and schema');
- // if ($this->getHelperSet()->get('dialog')->askConfirmation($output, '<question>Load fixtures (Y/N)?</question>', false)) {
- // $this->setupFixtures($input, $output);
- // }
+ $this
+ ->runCommand('doctrine:database:drop', array('--force' => true))
+ ->runCommand('doctrine:database:create')
+ ->runCommand('doctrine:schema:create')
+ ;
- $output->writeln('');
- $output->writeln('<info>Administration setup.</info>');
+ return $this;
+ }
- $this->setupAdmin($output);
+ if (!$this->isDatabasePresent()) {
+ $this->defaultOutput->writeln('Creating database and schema, clearing the cache');
- $output->writeln('');
+ $this
+ ->runCommand('doctrine:database:create')
+ ->runCommand('doctrine:schema:create')
+ ->runCommand('cache:clear')
+ ;
- return $this;
- }
+ return $this;
+ }
- protected function setupDatabase(InputInterface $input, OutputInterface $output)
- {
- if ($this->getHelperSet()->get('dialog')->askConfirmation($output, '<question>Drop current database (Y/N)?</question>', true)) {
- $connection = $this->getContainer()->get('doctrine')->getConnection();
- $params = $connection->getParams();
+ $dialog = $this->getHelper('dialog');
- $name = isset($params['path']) ? $params['path'] : (isset($params['dbname']) ? $params['dbname'] : false);
- unset($params['dbname']);
+ if ($dialog->askConfirmation($this->defaultOutput, '<question>It appears that your database already exists. Would you like to reset it? (y/N)</question> ', false)) {
+ $this->defaultOutput->writeln('Droping database, creating database and schema');
- if (!isset($params['path'])) {
- $name = $connection->getDatabasePlatform()->quoteSingleIdentifier($name);
- }
+ $this
+ ->runCommand('doctrine:database:drop', array('--force' => true))
+ ->runCommand('doctrine:database:create')
+ ->runCommand('doctrine:schema:create')
+ ;
+ } elseif ($this->isSchemaPresent()) {
+ if ($dialog->askConfirmation($this->defaultOutput, '<question>Seems like your database contains schema. Do you want to reset it? (y/N)</question> ', false)) {
+ $this->defaultOutput->writeln('Droping schema and creating schema');
- $connection->getSchemaManager()->dropDatabase($name);
+ $this
+ ->runCommand('doctrine:schema:drop', array('--force' => true))
+ ->runCommand('doctrine:schema:create')
+ ;
+ }
} else {
- throw new \Exception("Install setup stopped, database need to be dropped. Please backup your current one and re-launch the install command.");
+ $this->defaultOutput->writeln('Creating schema');
+
+ $this
+ ->runCommand('doctrine:schema:create')
+ ;
}
- $this
- ->runCommand('doctrine:database:create', $input, $output)
- ->runCommand('doctrine:schema:create', $input, $output)
- ->runCommand('cache:clear', $input, $output)
- ->runCommand('assets:install', $input, $output)
- ->runCommand('assetic:dump', $input, $output)
- ;
- }
+ $this->defaultOutput->writeln('Clearing the cache');
+ $this->runCommand('cache:clear');
+
+ /*
+ if ($this->getHelperSet()->get('dialog')->askConfirmation($this->defaultOutput, '<question>Load fixtures (Y/N)?</question>', false)) {
+ $doctrineConfig = $this->getContainer()->get('doctrine.orm.entity_manager')->getConnection()->getConfiguration();
+ $logger = $doctrineConfig->getSQLLogger();
+ // speed up fixture load
+ $doctrineConfig->setSQLLogger(null);
+ $this->runCommand('doctrine:fixtures:load');
+ $doctrineConfig->setSQLLogger($logger);
+ }
+ */
- protected function setupFixtures(InputInterface $input, OutputInterface $output)
- {
- $doctrineConfig = $this->getContainer()->get('doctrine.orm.entity_manager')->getConnection()->getConfiguration();
- $logger = $doctrineConfig->getSQLLogger();
- // speed up fixture load
- $doctrineConfig->setSQLLogger(null);
- $this->runCommand('doctrine:fixtures:load', $input, $output);
- $doctrineConfig->setSQLLogger($logger);
+ $this->defaultOutput->writeln('');
+
+ return $this;
}
- protected function setupAdmin(OutputInterface $output)
+ protected function setupAdmin()
{
+ $this->defaultOutput->writeln('<info><comment>Step 3 of 4.</comment> Administration setup.</info>');
+
$dialog = $this->getHelperSet()->get('dialog');
+
+ if (false === $dialog->askConfirmation($this->defaultOutput, '<question>Would you like to create a new user ? (y/N)</question>', true)) {
+ return $this;
+ }
+
$em = $this->getContainer()->get('doctrine.orm.entity_manager');
$user = new User();
- $user->setUsername($dialog->ask($output, '<question>Username</question> <comment>(default: wallabag)</comment> :', 'wallabag'));
- $user->setPassword($dialog->ask($output, '<question>Password</question> <comment>(default: wallabag)</comment> :', 'wallabag'));
- $user->setEmail($dialog->ask($output, '<question>Email:</question>', ''));
+ $user->setUsername($dialog->ask($this->defaultOutput, '<question>Username</question> <comment>(default: wallabag)</comment> :', 'wallabag'));
+ $user->setPassword($dialog->ask($this->defaultOutput, '<question>Password</question> <comment>(default: wallabag)</comment> :', 'wallabag'));
+ $user->setEmail($dialog->ask($this->defaultOutput, '<question>Email:</question>', ''));
$em->persist($user);
- $pagerConfig = new UsersConfig();
- $pagerConfig->setUserId($user->getId());
- $pagerConfig->setName('pager');
- $pagerConfig->setValue(10);
+ $config = new Config($user);
+ $config->setTheme($this->getContainer()->getParameter('theme'));
+ $config->setItemsPerPage($this->getContainer()->getParameter('items_on_page'));
+ $config->setLanguage($this->getContainer()->getParameter('language'));
- $em->persist($pagerConfig);
+ $em->persist($config);
- // $languageConfig = new LanguageConfig();
- // $languageConfig->setUserId($user->getId());
- // $languageConfig->setName('language');
- // $languageConfig->setValue('en_EN.UTF8');
+ $em->flush();
- // $em->persist($languageConfig);
+ $this->defaultOutput->writeln('');
- $em->flush();
+ return $this;
}
- protected function runCommand($command, InputInterface $input, OutputInterface $output)
+ protected function setupAsset()
{
+ $this->defaultOutput->writeln('<info><comment>Step 4 of 4.</comment> Installing assets.</info>');
+
$this
- ->getApplication()
- ->find($command)
- ->run($input, $output)
+ ->runCommand('assets:install')
+ ->runCommand('assetic:dump')
;
+ $this->defaultOutput->writeln('');
+
return $this;
}
+
+ /**
+ * Run a command
+ *
+ * @param string $command
+ * @param array $parameters Parameters to this command (usually 'force' => true)
+ */
+ protected function runCommand($command, $parameters = array())
+ {
+ $parameters = array_merge(
+ array('command' => $command),
+ $parameters,
+ array(
+ '--no-debug' => true,
+ '--env' => $this->defaultInput->getOption('env') ?: 'dev',
+ )
+ );
+
+ if ($this->defaultInput->getOption('no-interaction')) {
+ $parameters = array_merge($parameters, array('--no-interaction' => true));
+ }
+
+ $this->getApplication()->setAutoExit(false);
+ $exitCode = $this->getApplication()->run(new ArrayInput($parameters), new NullOutput());
+
+ if (0 !== $exitCode) {
+ $this->getApplication()->setAutoExit(true);
+
+ $errorMessage = sprintf('The command "%s" terminated with an error code: %u.', $command, $exitCode);
+ $this->defaultOutput->writeln("<error>$errorMessage</error>");
+ $exception = new \Exception($errorMessage, $exitCode);
+
+ throw $exception;
+ }
+
+ // PDO does not always close the connection after Doctrine commands.
+ // See https://github.com/symfony/symfony/issues/11750.
+ $this->getContainer()->get('doctrine')->getManager()->getConnection()->close();
+
+ return $this;
+ }
+
+ /**
+ * Check if the database already exists
+ *
+ * @return boolean
+ */
+ private function isDatabasePresent()
+ {
+ $databaseName = $this->getContainer()->getParameter('database_name');
+
+ try {
+ $schemaManager = $this->getContainer()->get('doctrine')->getManager()->getConnection()->getSchemaManager();
+ } catch (\Exception $exception) {
+ if (false !== strpos($exception->getMessage(), sprintf("Unknown database '%s'", $databaseName))) {
+ return false;
+ }
+
+ throw $exception;
+ }
+
+ // custom verification for sqlite, since `getListDatabasesSQL` doesn't work for sqlite
+ if ('sqlite' == $schemaManager->getDatabasePlatform()->getName()) {
+ $params = $this->getContainer()->get('doctrine.dbal.default_connection')->getParams();
+
+ if (isset($params['path']) && file_exists($params['path'])) {
+ return true;
+ }
+
+ return false;
+ }
+
+ return in_array($databaseName, $schemaManager->listDatabases());
+ }
+
+ /**
+ * Check if the schema is already created
+ *
+ * @return boolean
+ */
+ private function isSchemaPresent()
+ {
+ $schemaManager = $this->getContainer()->get('doctrine')->getManager()->getConnection()->getSchemaManager();
+
+ return $schemaManager->tablesExist(array('entry'));
+ }
}
--- /dev/null
+<?php
+
+namespace Wallabag\CoreBundle\Controller;
+
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
+use Symfony\Bundle\FrameworkBundle\Controller\Controller;
+use Symfony\Component\HttpFoundation\Request;
+use Wallabag\CoreBundle\Entity\Config;
+use Wallabag\CoreBundle\Entity\User;
+use Wallabag\CoreBundle\Form\Type\ConfigType;
+use Wallabag\CoreBundle\Form\Type\ChangePasswordType;
+use Wallabag\CoreBundle\Form\Type\UserType;
+use Wallabag\CoreBundle\Form\Type\NewUserType;
+
+class ConfigController extends Controller
+{
+ /**
+ * @param Request $request
+ *
+ * @Route("/config", name="config")
+ */
+ public function indexAction(Request $request)
+ {
+ $em = $this->getDoctrine()->getManager();
+ $config = $this->getConfig();
+ $user = $this->getUser();
+
+ // handle basic config detail
+ $configForm = $this->createForm(new ConfigType(), $config);
+ $configForm->handleRequest($request);
+
+ if ($configForm->isValid()) {
+ $em->persist($config);
+ $em->flush();
+
+ $this->get('session')->getFlashBag()->add(
+ 'notice',
+ 'Config saved'
+ );
+
+ return $this->redirect($this->generateUrl('config'));
+ }
+
+ // handle changing password
+ $pwdForm = $this->createForm(new ChangePasswordType());
+ $pwdForm->handleRequest($request);
+
+ if ($pwdForm->isValid()) {
+ $user->setPassword($pwdForm->get('new_password')->getData());
+ $em->persist($user);
+ $em->flush();
+
+ $this->get('session')->getFlashBag()->add(
+ 'notice',
+ 'Password updated'
+ );
+
+ return $this->redirect($this->generateUrl('config'));
+ }
+
+ // handle changing user information
+ $userForm = $this->createForm(new UserType(), $user);
+ $userForm->handleRequest($request);
+
+ if ($userForm->isValid()) {
+ $em->persist($user);
+ $em->flush();
+
+ $this->get('session')->getFlashBag()->add(
+ 'notice',
+ 'Information updated'
+ );
+
+ return $this->redirect($this->generateUrl('config'));
+ }
+
+ // handle adding new user
+ $newUser = new User();
+ $newUserForm = $this->createForm(new NewUserType(), $newUser);
+ $newUserForm->handleRequest($request);
+
+ if ($newUserForm->isValid()) {
+ $em->persist($newUser);
+
+ $config = new Config($newUser);
+ $config->setTheme($this->container->getParameter('theme'));
+ $config->setItemsPerPage($this->container->getParameter('items_on_page'));
+ $config->setLanguage($this->container->getParameter('language'));
+
+ $em->persist($config);
+
+ $em->flush();
+
+ $this->get('session')->getFlashBag()->add(
+ 'notice',
+ sprintf('User "%s" added', $newUser->getUsername())
+ );
+
+ return $this->redirect($this->generateUrl('config'));
+ }
+
+ return $this->render('WallabagCoreBundle:Config:index.html.twig', array(
+ 'configForm' => $configForm->createView(),
+ 'pwdForm' => $pwdForm->createView(),
+ 'userForm' => $userForm->createView(),
+ 'newUserForm' => $newUserForm->createView(),
+ ));
+ }
+
+ /**
+ * Retrieve config for the current user.
+ * If no config were found, create a new one.
+ *
+ * @return Wallabag\CoreBundle\Entity\Config
+ */
+ private function getConfig()
+ {
+ $config = $this->getDoctrine()
+ ->getRepository('WallabagCoreBundle:Config')
+ ->findOneByUser($this->getUser());
+
+ if (!$config) {
+ $config = new Config($this->getUser());
+ }
+
+ return $config;
+ }
+}
use Symfony\Component\HttpFoundation\Request;
use Wallabag\CoreBundle\Entity\Entry;
use Wallabag\CoreBundle\Service\Extractor;
-use Wallabag\CoreBundle\Helper\Url;
+use Wallabag\CoreBundle\Form\Type\EntryType;
class EntryController extends Controller
{
{
$entry = new Entry($this->getUser());
- $form = $this->createFormBuilder($entry)
- ->add('url', 'url')
- ->add('save', 'submit')
- ->getForm();
+ $form = $this->createForm(new EntryType(), $entry);
$form->handleRequest($request);
--- /dev/null
+<?php
+
+namespace Wallabag\CoreBundle\DataFixtures\ORM;
+
+use Doctrine\Common\DataFixtures\AbstractFixture;
+use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
+use Doctrine\Common\Persistence\ObjectManager;
+use Wallabag\CoreBundle\Entity\Config;
+
+class LoadConfigData extends AbstractFixture implements OrderedFixtureInterface
+{
+ /**
+ * {@inheritDoc}
+ */
+ public function load(ObjectManager $manager)
+ {
+ $adminConfig = new Config($this->getReference('admin-user'));
+ $adminConfig->setTheme('baggy');
+ $adminConfig->setItemsPerPage(30);
+ $adminConfig->setLanguage('en_US');
+
+ $manager->persist($adminConfig);
+
+ $this->addReference('admin-config', $adminConfig);
+
+ $bobConfig = new Config($this->getReference('bob-user'));
+ $bobConfig->setTheme('default');
+ $bobConfig->setItemsPerPage(10);
+ $bobConfig->setLanguage('fr_FR');
+
+ $manager->persist($bobConfig);
+
+ $this->addReference('bob-config', $bobConfig);
+
+ $manager->flush();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function getOrder()
+ {
+ return 20;
+ }
+}
*/
public function getOrder()
{
- return 20;
+ return 30;
}
}
$userAdmin->setName('Big boss');
$userAdmin->setEmail('bigboss@wallabag.org');
$userAdmin->setUsername('admin');
- $userAdmin->setPassword('test');
+ $userAdmin->setPassword('mypassword');
$manager->persist($userAdmin);
$bobUser->setName('Bobby');
$bobUser->setEmail('bobby@wallabag.org');
$bobUser->setUsername('bob');
- $bobUser->setPassword('test');
+ $bobUser->setPassword('mypassword');
$manager->persist($bobUser);
namespace Wallabag\CoreBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
+use Symfony\Component\Validator\Constraints as Assert;
/**
* Config
*
+ * @ORM\Entity(repositoryClass="Wallabag\CoreBundle\Repository\ConfigRepository")
* @ORM\Table(name="config")
* @ORM\Entity
*/
/**
* @var integer
*
- * @ORM\Column(name="id", type="integer", nullable=false)
+ * @ORM\Column(name="id", type="integer")
* @ORM\Id
- * @ORM\GeneratedValue(strategy="IDENTITY")
+ * @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
- * @ORM\Column(name="name", type="string", nullable=true)
+ * @Assert\NotBlank()
+ * @ORM\Column(name="theme", type="string", nullable=false)
*/
- private $name;
+ private $theme;
/**
* @var string
*
- * @ORM\Column(name="value", type="blob", nullable=true)
+ * @Assert\NotBlank()
+ * @ORM\Column(name="items_per_page", type="integer", nullable=false)
*/
- private $value;
+ private $items_per_page;
+
+ /**
+ * @var string
+ *
+ * @Assert\NotBlank()
+ * @ORM\Column(name="language", type="string", nullable=false)
+ */
+ private $language;
+
+ /**
+ * @ORM\ManyToOne(targetEntity="User", inversedBy="config")
+ */
+ private $user;
+
+ /*
+ * @param User $user
+ */
+ public function __construct(User $user)
+ {
+ $this->user = $user;
+ $this->items_per_page = 12;
+ $this->language = 'en_US';
+ }
/**
* Get id
}
/**
- * Set name
+ * Set theme
*
- * @param string $name
+ * @param string $theme
* @return Config
*/
- public function setName($name)
+ public function setTheme($theme)
{
- $this->name = $name;
+ $this->theme = $theme;
return $this;
}
/**
- * Get name
+ * Get theme
*
* @return string
*/
- public function getName()
+ public function getTheme()
{
- return $this->name;
+ return $this->theme;
}
/**
- * Set value
+ * Set items_per_page
*
- * @param string $value
+ * @param integer $itemsPerPage
* @return Config
*/
- public function setValue($value)
+ public function setItemsPerPage($itemsPerPage)
{
- $this->value = $value;
+ $this->items_per_page = $itemsPerPage;
return $this;
}
/**
- * Get value
+ * Get items_per_page
+ *
+ * @return integer
+ */
+ public function getItemsPerPage()
+ {
+ return $this->items_per_page;
+ }
+
+ /**
+ * Set language
+ *
+ * @param string $language
+ * @return Config
+ */
+ public function setLanguage($language)
+ {
+ $this->language = $language;
+
+ return $this;
+ }
+
+ /**
+ * Get language
*
* @return string
*/
- public function getValue()
+ public function getLanguage()
+ {
+ return $this->language;
+ }
+
+ /**
+ * Set user
+ *
+ * @param \Wallabag\CoreBundle\Entity\User $user
+ * @return Config
+ */
+ public function setUser(\Wallabag\CoreBundle\Entity\User $user = null)
+ {
+ $this->user = $user;
+
+ return $this;
+ }
+
+ /**
+ * Get user
+ *
+ * @return \Wallabag\CoreBundle\Entity\User
+ */
+ public function getUser()
{
- return $this->value;
+ return $this->user;
}
}
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
+use Symfony\Component\Validator\Constraints as Assert;
/**
* User
* @var string
*
* @ORM\Column(name="username", type="text")
+ * @Assert\NotBlank()
+ * @Assert\Length(
+ * min = "3",
+ * max = "255"
+ * )
*/
private $username;
/**
* @var string
*
- * @ORM\Column(name="email", type="text", nullable=true)
+ * @ORM\Column(name="email", type="text", nullable=false)
+ * @Assert\Email()
+ * @Assert\NotBlank()
*/
private $email;
/**
- * @ORM\Column(name="is_active", type="boolean")
+ * @ORM\Column(name="is_active", type="boolean", nullable=false)
*/
- private $isActive;
+ private $isActive = true;
/**
* @var date
public function __construct()
{
- $this->isActive = true;
- $this->salt = md5(uniqid(null, true));
- $this->entries = new ArrayCollection();
+ $this->salt = md5(uniqid(null, true));
+ $this->entries = new ArrayCollection();
}
/**
+++ /dev/null
-<?php
-
-namespace Wallabag\CoreBundle\Entity;
-
-use Doctrine\ORM\Mapping as ORM;
-
-/**
- * UsersConfig
- *
- * @ORM\Table(name="users_config")
- * @ORM\Entity
- */
-class UsersConfig
-{
- /**
- * @var integer
- *
- * @ORM\Column(name="id", type="integer", nullable=true)
- * @ORM\Id
- * @ORM\GeneratedValue(strategy="IDENTITY")
- */
- private $id;
-
- /**
- * @var string
- *
- * @ORM\Column(name="user_id", type="decimal", precision=10, scale=0, nullable=true)
- */
- private $userId;
-
- /**
- * @var string
- *
- * @ORM\Column(name="name", type="text", nullable=true)
- */
- private $name;
-
- /**
- * @var string
- *
- * @ORM\Column(name="value", type="text", nullable=true)
- */
- private $value;
-
- /**
- * Get id
- *
- * @return integer
- */
- public function getId()
- {
- return $this->id;
- }
-
- /**
- * Set userId
- *
- * @param string $userId
- * @return UsersConfig
- */
- public function setUserId($userId)
- {
- $this->userId = $userId;
-
- return $this;
- }
-
- /**
- * Get userId
- *
- * @return string
- */
- public function getUserId()
- {
- return $this->userId;
- }
-
- /**
- * Set name
- *
- * @param string $name
- * @return UsersConfig
- */
- public function setName($name)
- {
- $this->name = $name;
-
- return $this;
- }
-
- /**
- * Get name
- *
- * @return string
- */
- public function getName()
- {
- return $this->name;
- }
-
- /**
- * Set value
- *
- * @param string $value
- * @return UsersConfig
- */
- public function setValue($value)
- {
- $this->value = $value;
-
- return $this;
- }
-
- /**
- * Get value
- *
- * @return string
- */
- public function getValue()
- {
- return $this->value;
- }
-}
--- /dev/null
+<?php
+namespace Wallabag\CoreBundle\Form\Type;
+
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\FormBuilderInterface;
+use Symfony\Component\Security\Core\Validator\Constraints\UserPassword;
+use Symfony\Component\Validator\Constraints;
+
+class ChangePasswordType extends AbstractType
+{
+ public function buildForm(FormBuilderInterface $builder, array $options)
+ {
+ $builder
+ ->add('old_password', 'password', array(
+ 'constraints' => new UserPassword(array('message' => 'Wrong value for your current password')),
+ ))
+ ->add('new_password', 'repeated', array(
+ 'type' => 'password',
+ 'invalid_message' => 'The password fields must match.',
+ 'required' => true,
+ 'first_options' => array('label' => 'New password'),
+ 'second_options' => array('label' => 'Repeat new password'),
+ 'constraints' => array(
+ new Constraints\Length(array(
+ 'min' => 8,
+ 'minMessage' => 'Password should by at least 8 chars long',
+ )),
+ new Constraints\NotBlank(),
+ ),
+ ))
+ ->add('save', 'submit')
+ ;
+ }
+
+ public function getName()
+ {
+ return 'change_passwd';
+ }
+}
--- /dev/null
+<?php
+namespace Wallabag\CoreBundle\Form\Type;
+
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\FormBuilderInterface;
+use Symfony\Component\OptionsResolver\OptionsResolverInterface;
+
+class ConfigType extends AbstractType
+{
+ public function buildForm(FormBuilderInterface $builder, array $options)
+ {
+ $builder
+ ->add('theme', 'choice', array(
+ 'choices' => array(
+ 'baggy' => 'Baggy',
+ 'courgette' => 'Courgette',
+ 'dark' => 'Dark',
+ 'default' => 'Default',
+ 'dmagenta' => 'Dmagenta',
+ 'solarized' => 'Solarized',
+ 'solarized_dark' => 'Solarized Dark',
+ ),
+ ))
+ ->add('items_per_page', 'text')
+ ->add('language')
+ ->add('save', 'submit')
+ ;
+ }
+
+ public function setDefaultOptions(OptionsResolverInterface $resolver)
+ {
+ $resolver->setDefaults(array(
+ 'data_class' => 'Wallabag\CoreBundle\Entity\Config',
+ ));
+ }
+
+ public function getName()
+ {
+ return 'config';
+ }
+}
--- /dev/null
+<?php
+namespace Wallabag\CoreBundle\Form\Type;
+
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\FormBuilderInterface;
+use Symfony\Component\OptionsResolver\OptionsResolverInterface;
+
+class EntryType extends AbstractType
+{
+ public function buildForm(FormBuilderInterface $builder, array $options)
+ {
+ $builder
+ ->add('url', 'url')
+ ->add('save', 'submit')
+ ;
+ }
+
+ public function setDefaultOptions(OptionsResolverInterface $resolver)
+ {
+ $resolver->setDefaults(array(
+ 'data_class' => 'Wallabag\CoreBundle\Entity\Entry',
+ ));
+ }
+
+ public function getName()
+ {
+ return 'entry';
+ }
+}
--- /dev/null
+<?php
+namespace Wallabag\CoreBundle\Form\Type;
+
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\FormBuilderInterface;
+use Symfony\Component\OptionsResolver\OptionsResolverInterface;
+use Symfony\Component\Validator\Constraints;
+
+class NewUserType extends AbstractType
+{
+ public function buildForm(FormBuilderInterface $builder, array $options)
+ {
+ $builder
+ ->add('username', 'text')
+ ->add('password', 'password', array(
+ 'constraints' => array(
+ new Constraints\Length(array(
+ 'min' => 8,
+ 'minMessage' => 'Password should by at least 8 chars long',
+ )),
+ new Constraints\NotBlank(),
+ ),
+ ))
+ ->add('email', 'text')
+ ->add('save', 'submit')
+ ;
+ }
+
+ public function setDefaultOptions(OptionsResolverInterface $resolver)
+ {
+ $resolver->setDefaults(array(
+ 'data_class' => 'Wallabag\CoreBundle\Entity\User',
+ ));
+ }
+
+ public function getName()
+ {
+ return 'new_user';
+ }
+}
--- /dev/null
+<?php
+namespace Wallabag\CoreBundle\Form\Type;
+
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\FormBuilderInterface;
+use Symfony\Component\OptionsResolver\OptionsResolverInterface;
+
+class UserType extends AbstractType
+{
+ public function buildForm(FormBuilderInterface $builder, array $options)
+ {
+ $builder
+ ->add('username', 'text')
+ ->add('name', 'text')
+ ->add('email', 'text')
+ ->add('save', 'submit')
+ ;
+ }
+
+ public function setDefaultOptions(OptionsResolverInterface $resolver)
+ {
+ $resolver->setDefaults(array(
+ 'data_class' => 'Wallabag\CoreBundle\Entity\User',
+ ));
+ }
+
+ public function getName()
+ {
+ return 'user';
+ }
+}
--- /dev/null
+<?php
+
+namespace Wallabag\CoreBundle\Repository;
+
+use Doctrine\ORM\EntityRepository;
+
+class ConfigRepository extends EntityRepository
+{
+}
-_wllbg:
+entry:
resource: "@WallabagCoreBundle/Controller/EntryController.php"
type: annotation
+
+config:
+ resource: "@WallabagCoreBundle/Controller/ConfigController.php"
+ type: annotation
--- /dev/null
+{% extends "WallabagCoreBundle::layout.html.twig" %}
+
+{% block title %}{% trans %}Config{% endtrans %}{% endblock %}
+
+{% block menu %}
+ {% include "WallabagCoreBundle::_menu.html.twig" %}
+{% endblock %}
+
+{% block content %}
+ <h2>{% trans %}Wallabag configuration{% endtrans %}</h2>
+
+ <form action="{{ path('config') }}" method="post" {{ form_enctype(configForm) }}>
+ {{ form_errors(configForm) }}
+
+ <fieldset class="w500p inline">
+ <div class="row">
+ {{ form_label(configForm.theme) }}
+ {{ form_errors(configForm.theme) }}
+ {{ form_widget(configForm.theme) }}
+ </div>
+ </fieldset>
+
+ <fieldset class="w500p inline">
+ <div class="row">
+ {{ form_label(configForm.items_per_page) }}
+ {{ form_errors(configForm.items_per_page) }}
+ {{ form_widget(configForm.items_per_page) }}
+ </div>
+ </fieldset>
+
+ <fieldset class="w500p inline">
+ <div class="row">
+ {{ form_label(configForm.language) }}
+ {{ form_errors(configForm.language) }}
+ {{ form_widget(configForm.language) }}
+ </div>
+ </fieldset>
+
+ {{ form_rest(configForm) }}
+ </form>
+
+ <h2>{% trans %}User information{% endtrans %}</h2>
+
+ <form action="{{ path('config') }}" method="post" {{ form_enctype(userForm) }}>
+ {{ form_errors(userForm) }}
+
+ <fieldset class="w500p inline">
+ <div class="row">
+ {{ form_label(userForm.username) }}
+ {{ form_errors(userForm.username) }}
+ {{ form_widget(userForm.username) }}
+ </div>
+ </fieldset>
+
+ <fieldset class="w500p inline">
+ <div class="row">
+ {{ form_label(userForm.name) }}
+ {{ form_errors(userForm.name) }}
+ {{ form_widget(userForm.name) }}
+ </div>
+ </fieldset>
+
+ <fieldset class="w500p inline">
+ <div class="row">
+ {{ form_label(userForm.email) }}
+ {{ form_errors(userForm.email) }}
+ {{ form_widget(userForm.email) }}
+ </div>
+ </fieldset>
+
+ {{ form_rest(userForm) }}
+ </form>
+
+ <h2>{% trans %}Change your password{% endtrans %}</h2>
+
+ <form action="{{ path('config') }}" method="post" {{ form_enctype(pwdForm) }}>
+ {{ form_errors(pwdForm) }}
+
+ <fieldset class="w500p inline">
+ <div class="row">
+ {{ form_label(pwdForm.old_password) }}
+ {{ form_errors(pwdForm.old_password) }}
+ {{ form_widget(pwdForm.old_password) }}
+ </div>
+ </fieldset>
+
+ <fieldset class="w500p inline">
+ <div class="row">
+ {{ form_label(pwdForm.new_password.first) }}
+ {{ form_errors(pwdForm.new_password.first) }}
+ {{ form_widget(pwdForm.new_password.first) }}
+ </div>
+ </fieldset>
+
+ <fieldset class="w500p inline">
+ <div class="row">
+ {{ form_label(pwdForm.new_password.second) }}
+ {{ form_errors(pwdForm.new_password.second) }}
+ {{ form_widget(pwdForm.new_password.second) }}
+ </div>
+ </fieldset>
+
+ {{ form_rest(pwdForm) }}
+ </form>
+
+ <h2>{% trans %}Add a user{% endtrans %}</h2>
+
+ <form action="{{ path('config') }}" method="post" {{ form_enctype(newUserForm) }}>
+ {{ form_errors(newUserForm) }}
+
+ <fieldset class="w500p inline">
+ <div class="row">
+ {{ form_label(newUserForm.username) }}
+ {{ form_errors(newUserForm.username) }}
+ {{ form_widget(newUserForm.username) }}
+ </div>
+ </fieldset>
+
+ <fieldset class="w500p inline">
+ <div class="row">
+ {{ form_label(newUserForm.password) }}
+ {{ form_errors(newUserForm.password) }}
+ {{ form_widget(newUserForm.password) }}
+ </div>
+ </fieldset>
+
+ <fieldset class="w500p inline">
+ <div class="row">
+ {{ form_label(newUserForm.email) }}
+ {{ form_errors(newUserForm.email) }}
+ {{ form_widget(newUserForm.email) }}
+ </div>
+ </fieldset>
+
+ {{ form_rest(newUserForm) }}
+ </form>
+{% endblock %}
- <footer class="w600p center mt3 mb3 smaller txtright">
- <p>{% trans %}powered by{% endtrans %} <a href="http://wallabag.org">wallabag</a></p>
- </footer>
+<footer class="w600p center mt3 mb3 smaller txtright">
+ <p>{% trans %}powered by{% endtrans %} <a href="http://wallabag.org">wallabag</a></p>
+</footer>
- <link rel="apple-touch-icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-152.png') }}" sizes="152x152">
- <link rel="icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-152.png') }}" sizes="152x152">
+<link rel="apple-touch-icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-152.png') }}" sizes="152x152">
+<link rel="icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-152.png') }}" sizes="152x152">
- <link rel="apple-touch-icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-144.png') }}" sizes="144x144">
- <link rel="icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-144.png') }}" sizes="144x144">
+<link rel="apple-touch-icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-144.png') }}" sizes="144x144">
+<link rel="icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-144.png') }}" sizes="144x144">
- <link rel="apple-touch-icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-120.png') }}" sizes="120x120">
- <link rel="icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-120.png') }}" sizes="120x120">
+<link rel="apple-touch-icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-120.png') }}" sizes="120x120">
+<link rel="icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-120.png') }}" sizes="120x120">
- <link rel="apple-touch-icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-114.png') }}" sizes="114x114">
- <link rel="icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-114.png') }}" sizes="114x114">
+<link rel="apple-touch-icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-114.png') }}" sizes="114x114">
+<link rel="icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-114.png') }}" sizes="114x114">
- <link rel="apple-touch-icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-76.png') }}" sizes="76x76">
- <link rel="icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-76.png') }}" sizes="76x76">
+<link rel="apple-touch-icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-76.png') }}" sizes="76x76">
+<link rel="icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-76.png') }}" sizes="76x76">
- <link rel="apple-touch-icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-72.png') }}" sizes="72x72">
- <link rel="icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-72.png') }}" sizes="72x72">
+<link rel="apple-touch-icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-72.png') }}" sizes="72x72">
+<link rel="icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-72.png') }}" sizes="72x72">
- <link rel="apple-touch-icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-57.png') }}" sizes="57x57">
- <link rel="icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-57.png') }}" sizes="57x57">
+<link rel="apple-touch-icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-57.png') }}" sizes="57x57">
+<link rel="icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon-57.png') }}" sizes="57x57">
- <link rel="apple-touch-icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon.png') }}">
- <link rel="icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon.png') }}">
+<link rel="apple-touch-icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon.png') }}">
+<link rel="icon" type="image/png" href="{{ asset('themes/_global/img/appicon/apple-touch-icon.png') }}">
- <link rel="shortcut icon" type="image/x-icon" href="{{ asset('themes/_global/img/appicon/favicon.ico') }}">
+<link rel="shortcut icon" type="image/x-icon" href="{{ asset('themes/_global/img/appicon/favicon.ico') }}">
- <link rel="stylesheet" href="{{ asset('themes/baggy/css/ratatouille.css') }}" media="all">
- <link rel="stylesheet" href="{{ asset('themes/baggy/css/font.css') }}" media="all">
- <link rel="stylesheet" href="{{ asset('themes/baggy/css/main.css') }}" media="all">
- <link rel="stylesheet" href="{{ asset('themes/baggy/css/messages.css') }}" media="all">
- <link rel="stylesheet" href="{{ asset('themes/baggy/css/print.css') }}" media="print">
+<link rel="stylesheet" href="{{ asset('themes/baggy/css/ratatouille.css') }}" media="all">
+<link rel="stylesheet" href="{{ asset('themes/baggy/css/font.css') }}" media="all">
+<link rel="stylesheet" href="{{ asset('themes/baggy/css/main.css') }}" media="all">
+<link rel="stylesheet" href="{{ asset('themes/baggy/css/messages.css') }}" media="all">
+<link rel="stylesheet" href="{{ asset('themes/baggy/css/print.css') }}" media="print">
- <script src="{{ asset('themes/_global/js/jquery-2.0.3.min.js') }}"></script>
- <script src="{{ asset('themes/_global/js/autoClose.js') }}"></script>
- <script src="{{ asset('themes/baggy/js/jquery.cookie.js') }}"></script>
- <script src="{{ asset('themes/baggy/js/init.js') }}"></script>
- <script src="{{ asset('themes/_global/js/saveLink.js') }}"></script>
- <script src="{{ asset('themes/_global/js/popupForm.js') }}"></script>
- <script src="{{ asset('themes/baggy/js/closeMessage.js') }}"></script>
- <script src="{{ asset('bundles/wallabagcore/js/bookmarklet.js') }}"></script>
+<script src="{{ asset('themes/_global/js/jquery-2.0.3.min.js') }}"></script>
+<script src="{{ asset('themes/_global/js/autoClose.js') }}"></script>
+<script src="{{ asset('themes/baggy/js/jquery.cookie.js') }}"></script>
+<script src="{{ asset('themes/baggy/js/init.js') }}"></script>
+<script src="{{ asset('themes/_global/js/saveLink.js') }}"></script>
+<script src="{{ asset('themes/_global/js/popupForm.js') }}"></script>
+<script src="{{ asset('themes/baggy/js/closeMessage.js') }}"></script>
+<script src="{{ asset('bundles/wallabagcore/js/bookmarklet.js') }}"></script>
- <button id="menu" class="icon icon-menu desktopHide"><span>Menu</span></button>
- <ul id="links" class="links">
- <li><a href="{{ path('unread') }}">{% trans %}unread{% endtrans %}</a></li>
- <li><a href="{{ path('starred') }}">{% trans %}favorites{% endtrans %}</a></li>
- <li><a href="{{ path('archive') }}"}>{% trans %}archive{% endtrans %}</a></li>
- <li><a href="?view=tags">{% trans %}tags{% endtrans %}</a></li>
- <li><a href="{{ path('new_entry') }}">{% trans %}save a link{% endtrans %}</a></li>
- <li style="position: relative;"><a href="javascript: void(null);" id="search">{% trans %}search{% endtrans %}</a>
- {% include "WallabagCoreBundle::_search_form.html.twig" %}
- </li>
- <li><a href="?view=config">{% trans %}config{% endtrans %}</a></li>
- <li><a href={{ path('about') }}>{% trans %}about{% endtrans %}</a></li>
- <li><a class="icon icon-power" href="{{ path('logout') }}" title="{% trans %}logout{% endtrans %}">{% trans %}logout{% endtrans %}</a></li>
- </ul>
+<button id="menu" class="icon icon-menu desktopHide"><span>Menu</span></button>
+<ul id="links" class="links">
+ <li><a href="{{ path('unread') }}">{% trans %}unread{% endtrans %}</a></li>
+ <li><a href="{{ path('starred') }}">{% trans %}favorites{% endtrans %}</a></li>
+ <li><a href="{{ path('archive') }}"}>{% trans %}archive{% endtrans %}</a></li>
+ <li><a href="?view=tags">{% trans %}tags{% endtrans %}</a></li>
+ <li><a href="{{ path('new_entry') }}">{% trans %}save a link{% endtrans %}</a></li>
+ <li style="position: relative;"><a href="javascript: void(null);" id="search">{% trans %}search{% endtrans %}</a>
+ {% include "WallabagCoreBundle::_search_form.html.twig" %}
+ </li>
+ <li><a href="{{ path('config') }}">{% trans %}config{% endtrans %}</a></li>
+ <li><a href="{{ path('about') }}">{% trans %}about{% endtrans %}</a></li>
+ <li><a class="icon icon-power" href="{{ path('logout') }}" title="{% trans %}logout{% endtrans %}">{% trans %}logout{% endtrans %}</a></li>
+</ul>
<div id="search-form" class="messages info popup-form">
-<form method="get" action="index.php">
- <h2>{% trans %}Search{% endtrans %}</h2>
+ <form method="get" action="index.php">
+ <h2>{% trans %}Search{% endtrans %}</h2>
<a href="javascript: void(null);" id="search-form-close" class="close-button--popup close-button">×</a>
<input type="hidden" name="view" value="search"></input>
<input required placeholder="{% trans %}Enter your search here{% endtrans %}" type="text" name="search" id="searchfield"><br>
<input id="submit-search" type="submit" value="{% trans %}Search{% endtrans %}"></input>
-</form>
+ </form>
</div>
- <header class="w600p center mbm">
- <h1>
- {% block logo %}<img width="100" height="100" src="{{ asset('themes/baggy/img/logo-w.png') }}" alt="wallabag logo" />{% endblock %}
- </h1>
- </header>
+<header class="w600p center mbm">
+ <h1>
+ {% block logo %}<img width="100" height="100" src="{{ asset('themes/baggy/img/logo-w.png') }}" alt="wallabag logo" />{% endblock %}
+ </h1>
+</header>
*/
public function encodePassword($raw, $salt)
{
- if (null === $this->username) {
- throw new \LogicException('We can not check the password without a username.');
- }
-
if ($this->isPasswordTooLong($raw)) {
throw new BadCredentialsException('Invalid password.');
}
*/
protected function mergePasswordAndSalt($password, $salt)
{
+ if (null === $this->username) {
+ throw new \LogicException('We can not check the password without a username.');
+ }
+
if (empty($salt)) {
return $password;
}
--- /dev/null
+<?php
+
+namespace Wallabag\CoreBundle\Security\Validator;
+
+use Symfony\Component\Security\Core\User\UserInterface;
+use Symfony\Component\Security\Core\SecurityContextInterface;
+use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
+use Symfony\Component\Validator\Constraint;
+use Symfony\Component\Validator\ConstraintValidator;
+use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
+use Symfony\Component\Validator\Exception\UnexpectedTypeException;
+use Symfony\Component\Security\Core\Validator\Constraints\UserPassword;
+
+class WallabagUserPasswordValidator extends ConstraintValidator
+{
+ private $securityContext;
+ private $encoderFactory;
+
+ public function __construct(SecurityContextInterface $securityContext, EncoderFactoryInterface $encoderFactory)
+ {
+ $this->securityContext = $securityContext;
+ $this->encoderFactory = $encoderFactory;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function validate($password, Constraint $constraint)
+ {
+ if (!$constraint instanceof UserPassword) {
+ throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\UserPassword');
+ }
+
+ $user = $this->securityContext->getToken()->getUser();
+
+ if (!$user instanceof UserInterface) {
+ throw new ConstraintDefinitionException('The User object must implement the UserInterface interface.');
+ }
+
+ // give username, it's used to hash the password
+ $encoder = $this->encoderFactory->getEncoder($user);
+ $encoder->setUsername($user->getUsername());
+
+ if (!$encoder->isPasswordValid($user->getPassword(), $password, $user->getSalt())) {
+ $this->context->addViolation($constraint->message);
+ }
+ }
+}
--- /dev/null
+<?php
+
+namespace Wallabag\CoreBundle\Tests\Command;
+
+use Wallabag\CoreBundle\Tests\WallabagTestCase;
+use Wallabag\CoreBundle\Command\InstallCommand;
+use Symfony\Bundle\FrameworkBundle\Console\Application;
+use Symfony\Component\Console\Tester\CommandTester;
+use Symfony\Component\Console\Input\ArrayInput;
+use Symfony\Component\Console\Output\NullOutput;
+use Doctrine\Bundle\DoctrineBundle\Command\DropDatabaseDoctrineCommand;
+use Doctrine\Bundle\DoctrineBundle\Command\CreateDatabaseDoctrineCommand;
+
+class InstallCommandTest extends WallabagTestCase
+{
+ public static function tearDownAfterClass()
+ {
+ $application = new Application(static::$kernel);
+ $application->setAutoExit(false);
+
+ $code = $application->run(new ArrayInput(array(
+ 'command' => 'doctrine:fixtures:load',
+ '--no-interaction' => true,
+ '--env' => 'test',
+ )), new NullOutput());
+ }
+
+ public function testRunInstallCommand()
+ {
+ $this->container = static::$kernel->getContainer();
+
+ $application = new Application(static::$kernel);
+ $application->add(new InstallCommand());
+
+ $command = $application->find('wallabag:install');
+
+ // We mock the DialogHelper
+ $dialog = $this->getMockBuilder('Symfony\Component\Console\Helper\DialogHelper')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $dialog->expects($this->any())
+ ->method('ask')
+ ->will($this->returnValue('test'));
+ $dialog->expects($this->any())
+ ->method('askConfirmation')
+ ->will($this->returnValue(true));
+
+ // We override the standard helper with our mock
+ $command->getHelperSet()->set($dialog, 'dialog');
+
+ $tester = new CommandTester($command);
+ $tester->execute(array(
+ 'command' => $command->getName(),
+ ));
+
+ $this->assertContains('Step 1 of 4. Checking system requirements.', $tester->getDisplay());
+ $this->assertContains('Step 2 of 4. Setting up database.', $tester->getDisplay());
+ $this->assertContains('Step 3 of 4. Administration setup.', $tester->getDisplay());
+ $this->assertContains('Step 4 of 4. Installing assets.', $tester->getDisplay());
+ }
+
+ public function testRunInstallCommandWithReset()
+ {
+ $this->container = static::$kernel->getContainer();
+
+ $application = new Application(static::$kernel);
+ $application->add(new InstallCommand());
+
+ $command = $application->find('wallabag:install');
+
+ // We mock the DialogHelper
+ $dialog = $this->getMockBuilder('Symfony\Component\Console\Helper\DialogHelper')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $dialog->expects($this->any())
+ ->method('ask')
+ ->will($this->returnValue('test'));
+ $dialog->expects($this->any())
+ ->method('askConfirmation')
+ ->will($this->returnValue(true));
+
+ // We override the standard helper with our mock
+ $command->getHelperSet()->set($dialog, 'dialog');
+
+ $tester = new CommandTester($command);
+ $tester->execute(array(
+ 'command' => $command->getName(),
+ '--reset' => true,
+ ));
+
+ $this->assertContains('Step 1 of 4. Checking system requirements.', $tester->getDisplay());
+ $this->assertContains('Step 2 of 4. Setting up database.', $tester->getDisplay());
+ $this->assertContains('Step 3 of 4. Administration setup.', $tester->getDisplay());
+ $this->assertContains('Step 4 of 4. Installing assets.', $tester->getDisplay());
+
+ // we force to reset everything
+ $this->assertContains('Droping database, creating database and schema', $tester->getDisplay());
+ }
+
+ public function testRunInstallCommandWithDatabaseRemoved()
+ {
+ $this->container = static::$kernel->getContainer();
+
+ $application = new Application(static::$kernel);
+ $application->add(new InstallCommand());
+ $application->add(new DropDatabaseDoctrineCommand());
+
+ // drop database first, so the install command won't ask to reset things
+ $command = new DropDatabaseDoctrineCommand();
+ $command->setApplication($application);
+ $command->run(new ArrayInput(array(
+ 'command' => 'doctrine:database:drop',
+ '--force' => true,
+ )), new NullOutput());
+
+ $command = $application->find('wallabag:install');
+
+ // We mock the DialogHelper
+ $dialog = $this->getMockBuilder('Symfony\Component\Console\Helper\DialogHelper')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $dialog->expects($this->any())
+ ->method('ask')
+ ->will($this->returnValue('test'));
+ $dialog->expects($this->any())
+ ->method('askConfirmation')
+ ->will($this->returnValue(true));
+
+ // We override the standard helper with our mock
+ $command->getHelperSet()->set($dialog, 'dialog');
+
+ $tester = new CommandTester($command);
+ $tester->execute(array(
+ 'command' => $command->getName(),
+ ));
+
+ $this->assertContains('Step 1 of 4. Checking system requirements.', $tester->getDisplay());
+ $this->assertContains('Step 2 of 4. Setting up database.', $tester->getDisplay());
+ $this->assertContains('Step 3 of 4. Administration setup.', $tester->getDisplay());
+ $this->assertContains('Step 4 of 4. Installing assets.', $tester->getDisplay());
+
+ // the current database doesn't already exist
+ $this->assertContains('Creating database and schema, clearing the cache', $tester->getDisplay());
+ }
+
+ public function testRunInstallCommandChooseResetSchema()
+ {
+ $this->container = static::$kernel->getContainer();
+
+ $application = new Application(static::$kernel);
+ $application->add(new InstallCommand());
+
+ $command = $application->find('wallabag:install');
+
+ // We mock the DialogHelper
+ $dialog = $this->getMockBuilder('Symfony\Component\Console\Helper\DialogHelper')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dialog->expects($this->exactly(3))
+ ->method('askConfirmation')
+ ->will($this->onConsecutiveCalls(
+ false, // don't want to reset the entire database
+ true, // do want to reset the schema
+ false // don't want to create a new user
+ ));
+
+ // We override the standard helper with our mock
+ $command->getHelperSet()->set($dialog, 'dialog');
+
+ $tester = new CommandTester($command);
+ $tester->execute(array(
+ 'command' => $command->getName(),
+ ));
+
+ $this->assertContains('Step 1 of 4. Checking system requirements.', $tester->getDisplay());
+ $this->assertContains('Step 2 of 4. Setting up database.', $tester->getDisplay());
+ $this->assertContains('Step 3 of 4. Administration setup.', $tester->getDisplay());
+ $this->assertContains('Step 4 of 4. Installing assets.', $tester->getDisplay());
+
+ $this->assertContains('Droping schema and creating schema', $tester->getDisplay());
+ }
+
+ public function testRunInstallCommandChooseNothing()
+ {
+ $this->container = static::$kernel->getContainer();
+
+ $application = new Application(static::$kernel);
+ $application->add(new InstallCommand());
+ $application->add(new DropDatabaseDoctrineCommand());
+ $application->add(new CreateDatabaseDoctrineCommand());
+
+ // drop database first, so the install command won't ask to reset things
+ $command = new DropDatabaseDoctrineCommand();
+ $command->setApplication($application);
+ $command->run(new ArrayInput(array(
+ 'command' => 'doctrine:database:drop',
+ '--force' => true,
+ )), new NullOutput());
+
+ $this->container->get('doctrine')->getManager()->getConnection()->close();
+
+ $command = new CreateDatabaseDoctrineCommand();
+ $command->setApplication($application);
+ $command->run(new ArrayInput(array(
+ 'command' => 'doctrine:database:create',
+ '--env' => 'test',
+ )), new NullOutput());
+
+ $command = $application->find('wallabag:install');
+
+ // We mock the DialogHelper
+ $dialog = $this->getMockBuilder('Symfony\Component\Console\Helper\DialogHelper')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $dialog->expects($this->exactly(2))
+ ->method('askConfirmation')
+ ->will($this->onConsecutiveCalls(
+ false, // don't want to reset the entire database
+ false // don't want to create a new user
+ ));
+
+ // We override the standard helper with our mock
+ $command->getHelperSet()->set($dialog, 'dialog');
+
+ $tester = new CommandTester($command);
+ $tester->execute(array(
+ 'command' => $command->getName(),
+ ));
+
+ $this->assertContains('Step 1 of 4. Checking system requirements.', $tester->getDisplay());
+ $this->assertContains('Step 2 of 4. Setting up database.', $tester->getDisplay());
+ $this->assertContains('Step 3 of 4. Administration setup.', $tester->getDisplay());
+ $this->assertContains('Step 4 of 4. Installing assets.', $tester->getDisplay());
+
+ $this->assertContains('Creating schema', $tester->getDisplay());
+ }
+
+ public function testRunInstallCommandNoInteraction()
+ {
+ $this->container = static::$kernel->getContainer();
+
+ $application = new Application(static::$kernel);
+ $application->add(new InstallCommand());
+
+ $command = $application->find('wallabag:install');
+
+ // We mock the DialogHelper
+ $dialog = $this->getMockBuilder('Symfony\Component\Console\Helper\DialogHelper')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $dialog->expects($this->any())
+ ->method('ask')
+ ->will($this->returnValue('test'));
+ $dialog->expects($this->any())
+ ->method('askConfirmation')
+ ->will($this->returnValue(true));
+
+ // We override the standard helper with our mock
+ $command->getHelperSet()->set($dialog, 'dialog');
+
+ $tester = new CommandTester($command);
+ $tester->execute(array(
+ 'command' => $command->getName(),
+ '--no-interaction' => true,
+ ));
+
+ $this->assertContains('Step 1 of 4. Checking system requirements.', $tester->getDisplay());
+ $this->assertContains('Step 2 of 4. Setting up database.', $tester->getDisplay());
+ $this->assertContains('Step 3 of 4. Administration setup.', $tester->getDisplay());
+ $this->assertContains('Step 4 of 4. Installing assets.', $tester->getDisplay());
+ }
+}
--- /dev/null
+<?php
+
+namespace Wallabag\CoreBundle\Tests\Controller;
+
+use Wallabag\CoreBundle\Tests\WallabagTestCase;
+
+class ConfigControllerTest extends WallabagTestCase
+{
+ public function testLogin()
+ {
+ $client = $this->getClient();
+
+ $client->request('GET', '/new');
+
+ $this->assertEquals(302, $client->getResponse()->getStatusCode());
+ $this->assertContains('login', $client->getResponse()->headers->get('location'));
+ }
+
+ public function testIndex()
+ {
+ $this->logInAs('admin');
+ $client = $this->getClient();
+
+ $crawler = $client->request('GET', '/config');
+
+ $this->assertEquals(200, $client->getResponse()->getStatusCode());
+
+ $this->assertCount(1, $crawler->filter('button[id=config_save]'));
+ $this->assertCount(1, $crawler->filter('button[id=change_passwd_save]'));
+ $this->assertCount(1, $crawler->filter('button[id=user_save]'));
+ }
+
+ public function testUpdate()
+ {
+ $this->logInAs('admin');
+ $client = $this->getClient();
+
+ $crawler = $client->request('GET', '/config');
+
+ $this->assertEquals(200, $client->getResponse()->getStatusCode());
+
+ $form = $crawler->filter('button[id=config_save]')->form();
+
+ $data = array(
+ 'config[theme]' => 'baggy',
+ 'config[items_per_page]' => '30',
+ 'config[language]' => 'fr_FR',
+ );
+
+ $client->submit($form, $data);
+
+ $this->assertEquals(302, $client->getResponse()->getStatusCode());
+
+ $crawler = $client->followRedirect();
+
+ $this->assertGreaterThan(1, $alert = $crawler->filter('div.flash-notice')->extract(array('_text')));
+ $this->assertContains('Config saved', $alert[0]);
+ }
+
+ public function dataForUpdateFailed()
+ {
+ return array(
+ array(array(
+ 'config[theme]' => 'baggy',
+ 'config[items_per_page]' => '',
+ 'config[language]' => 'fr_FR',
+ )),
+ array(array(
+ 'config[theme]' => 'baggy',
+ 'config[items_per_page]' => '12',
+ 'config[language]' => '',
+ )),
+ );
+ }
+
+ /**
+ * @dataProvider dataForUpdateFailed
+ */
+ public function testUpdateFailed($data)
+ {
+ $this->logInAs('admin');
+ $client = $this->getClient();
+
+ $crawler = $client->request('GET', '/config');
+
+ $this->assertEquals(200, $client->getResponse()->getStatusCode());
+
+ $form = $crawler->filter('button[id=config_save]')->form();
+
+ $crawler = $client->submit($form, $data);
+
+ $this->assertEquals(200, $client->getResponse()->getStatusCode());
+
+ $this->assertGreaterThan(1, $alert = $crawler->filter('body')->extract(array('_text')));
+ $this->assertContains('This value should not be blank', $alert[0]);
+ }
+
+ public function dataForChangePasswordFailed()
+ {
+ return array(
+ array(
+ array(
+ 'change_passwd[old_password]' => 'baggy',
+ 'change_passwd[new_password][first]' => '',
+ 'change_passwd[new_password][second]' => '',
+ ),
+ 'Wrong value for your current password',
+ ),
+ array(
+ array(
+ 'change_passwd[old_password]' => 'mypassword',
+ 'change_passwd[new_password][first]' => '',
+ 'change_passwd[new_password][second]' => '',
+ ),
+ 'This value should not be blank',
+ ),
+ array(
+ array(
+ 'change_passwd[old_password]' => 'mypassword',
+ 'change_passwd[new_password][first]' => 'hop',
+ 'change_passwd[new_password][second]' => '',
+ ),
+ 'The password fields must match',
+ ),
+ array(
+ array(
+ 'change_passwd[old_password]' => 'mypassword',
+ 'change_passwd[new_password][first]' => 'hop',
+ 'change_passwd[new_password][second]' => 'hop',
+ ),
+ 'Password should by at least',
+ ),
+ );
+ }
+
+ /**
+ * @dataProvider dataForChangePasswordFailed
+ */
+ public function testChangePasswordFailed($data, $expectedMessage)
+ {
+ $this->logInAs('admin');
+ $client = $this->getClient();
+
+ $crawler = $client->request('GET', '/config');
+
+ $this->assertEquals(200, $client->getResponse()->getStatusCode());
+
+ $form = $crawler->filter('button[id=change_passwd_save]')->form();
+
+ $crawler = $client->submit($form, $data);
+
+ $this->assertEquals(200, $client->getResponse()->getStatusCode());
+
+ $this->assertGreaterThan(1, $alert = $crawler->filter('body')->extract(array('_text')));
+ $this->assertContains($expectedMessage, $alert[0]);
+ }
+
+ public function testChangePassword()
+ {
+ $this->logInAs('admin');
+ $client = $this->getClient();
+
+ $crawler = $client->request('GET', '/config');
+
+ $this->assertEquals(200, $client->getResponse()->getStatusCode());
+
+ $form = $crawler->filter('button[id=change_passwd_save]')->form();
+
+ $data = array(
+ 'change_passwd[old_password]' => 'mypassword',
+ 'change_passwd[new_password][first]' => 'mypassword',
+ 'change_passwd[new_password][second]' => 'mypassword',
+ );
+
+ $client->submit($form, $data);
+
+ $this->assertEquals(302, $client->getResponse()->getStatusCode());
+
+ $crawler = $client->followRedirect();
+
+ $this->assertGreaterThan(1, $alert = $crawler->filter('div.flash-notice')->extract(array('_text')));
+ $this->assertContains('Password updated', $alert[0]);
+ }
+
+ public function dataForUserFailed()
+ {
+ return array(
+ array(
+ array(
+ 'user[username]' => '',
+ 'user[name]' => '',
+ 'user[email]' => '',
+ ),
+ 'This value should not be blank.',
+ ),
+ array(
+ array(
+ 'user[username]' => 'ad',
+ 'user[name]' => '',
+ 'user[email]' => '',
+ ),
+ 'This value is too short.',
+ ),
+ array(
+ array(
+ 'user[username]' => 'admin',
+ 'user[name]' => '',
+ 'user[email]' => 'test',
+ ),
+ 'This value is not a valid email address.',
+ ),
+ );
+ }
+
+ /**
+ * @dataProvider dataForUserFailed
+ */
+ public function testUserFailed($data, $expectedMessage)
+ {
+ $this->logInAs('admin');
+ $client = $this->getClient();
+
+ $crawler = $client->request('GET', '/config');
+
+ $this->assertEquals(200, $client->getResponse()->getStatusCode());
+
+ $form = $crawler->filter('button[id=user_save]')->form();
+
+ $crawler = $client->submit($form, $data);
+
+ $this->assertEquals(200, $client->getResponse()->getStatusCode());
+
+ $this->assertGreaterThan(1, $alert = $crawler->filter('body')->extract(array('_text')));
+ $this->assertContains($expectedMessage, $alert[0]);
+ }
+
+ public function testUserUpdate()
+ {
+ $this->logInAs('admin');
+ $client = $this->getClient();
+
+ $crawler = $client->request('GET', '/config');
+
+ $this->assertEquals(200, $client->getResponse()->getStatusCode());
+
+ $form = $crawler->filter('button[id=user_save]')->form();
+
+ $data = array(
+ 'user[username]' => 'admin',
+ 'user[name]' => 'new name',
+ 'user[email]' => 'admin@wallabag.io',
+ );
+
+ $client->submit($form, $data);
+
+ $this->assertEquals(302, $client->getResponse()->getStatusCode());
+
+ $crawler = $client->followRedirect();
+
+ $this->assertGreaterThan(1, $alert = $crawler->filter('div.flash-notice')->extract(array('_text')));
+ $this->assertContains('Information updated', $alert[0]);
+ }
+
+ public function dataForNewUserFailed()
+ {
+ return array(
+ array(
+ array(
+ 'new_user[username]' => '',
+ 'new_user[password]' => '',
+ 'new_user[email]' => '',
+ ),
+ 'This value should not be blank.',
+ ),
+ array(
+ array(
+ 'new_user[username]' => 'ad',
+ 'new_user[password]' => '',
+ 'new_user[email]' => '',
+ ),
+ 'This value is too short.',
+ ),
+ array(
+ array(
+ 'new_user[username]' => 'wallace',
+ 'new_user[password]' => '',
+ 'new_user[email]' => 'test',
+ ),
+ 'This value is not a valid email address.',
+ ),
+ array(
+ array(
+ 'new_user[username]' => 'wallace',
+ 'new_user[password]' => 'admin',
+ 'new_user[email]' => 'wallace@wallace.me',
+ ),
+ 'Password should by at least',
+ ),
+ );
+ }
+
+ /**
+ * @dataProvider dataForNewUserFailed
+ */
+ public function testNewUserFailed($data, $expectedMessage)
+ {
+ $this->logInAs('admin');
+ $client = $this->getClient();
+
+ $crawler = $client->request('GET', '/config');
+
+ $this->assertEquals(200, $client->getResponse()->getStatusCode());
+
+ $form = $crawler->filter('button[id=new_user_save]')->form();
+
+ $crawler = $client->submit($form, $data);
+
+ $this->assertEquals(200, $client->getResponse()->getStatusCode());
+
+ $this->assertGreaterThan(1, $alert = $crawler->filter('body')->extract(array('_text')));
+ $this->assertContains($expectedMessage, $alert[0]);
+ }
+
+ public function testNewUserCreated()
+ {
+ $this->logInAs('admin');
+ $client = $this->getClient();
+
+ $crawler = $client->request('GET', '/config');
+
+ $this->assertEquals(200, $client->getResponse()->getStatusCode());
+
+ $form = $crawler->filter('button[id=new_user_save]')->form();
+
+ $data = array(
+ 'new_user[username]' => 'wallace',
+ 'new_user[password]' => 'wallace1',
+ 'new_user[email]' => 'wallace@wallace.me',
+ );
+
+ $client->submit($form, $data);
+
+ $this->assertEquals(302, $client->getResponse()->getStatusCode());
+
+ $crawler = $client->followRedirect();
+
+ $this->assertGreaterThan(1, $alert = $crawler->filter('div.flash-notice')->extract(array('_text')));
+ $this->assertContains('User "wallace" added', $alert[0]);
+ }
+}
$form = $crawler->filter('button[type=submit]')->form();
$data = array(
- 'form[url]' => 'https://www.mailjet.com/blog/mailjet-zapier-integrations-made-easy/',
+ 'entry[url]' => 'https://www.mailjet.com/blog/mailjet-zapier-integrations-made-easy/',
);
$client->submit($form, $data);
$client->request('GET', '/api/salts/admin.json');
$salt = json_decode($client->getResponse()->getContent());
- $headers = $this->generateHeaders('admin', 'test', $salt[0]);
+ $headers = $this->generateHeaders('admin', 'mypassword', $salt[0]);
$entry = $client->getContainer()
->get('doctrine.orm.entity_manager')
$client->request('GET', '/api/salts/admin.json');
$salt = json_decode($client->getResponse()->getContent());
- $headers = $this->generateHeaders('admin', 'test', $salt[0]);
+ $headers = $this->generateHeaders('admin', 'mypassword', $salt[0]);
$entry = $client->getContainer()
->get('doctrine.orm.entity_manager')
$client->request('GET', '/api/salts/admin.json');
$salt = json_decode($client->getResponse()->getContent());
- $headers = $this->generateHeaders('admin', 'test', $salt[0]);
+ $headers = $this->generateHeaders('admin', 'mypassword', $salt[0]);
$client->request('GET', '/api/entries', array(), array(), $headers);
$client->request('GET', '/api/salts/admin.json');
$salt = json_decode($client->getResponse()->getContent());
- $headers = $this->generateHeaders('admin', 'test', $salt[0]);
+ $headers = $this->generateHeaders('admin', 'mypassword', $salt[0]);
$entry = $client->getContainer()
->get('doctrine.orm.entity_manager')
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
-class WallabagTestCase extends WebTestCase
+abstract class WallabagTestCase extends WebTestCase
{
private $client = null;
$form = $crawler->filter('button[type=submit]')->form();
$data = array(
'_username' => $username,
- '_password' => 'test',
+ '_password' => 'mypassword',
);
$this->client->submit($form, $data);