]> git.immae.eu Git - github/wallabag/wallabag.git/blobdiff - src/Wallabag/CoreBundle/Controller/ConfigController.php
Add new Ignore Origin rules tab, update ConfigController
[github/wallabag/wallabag.git] / src / Wallabag / CoreBundle / Controller / ConfigController.php
index b999c539f1aa32a7c387cc0b9b80dbc35c7ad91e..3efc7bb3218d4bd3caf098661983fde13f17b924 100644 (file)
@@ -2,17 +2,26 @@
 
 namespace Wallabag\CoreBundle\Controller;
 
-use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
+use JMS\Serializer\SerializationContext;
+use JMS\Serializer\SerializerBuilder;
+use PragmaRX\Recovery\Recovery as BackupCodes;
 use Symfony\Bundle\FrameworkBundle\Controller\Controller;
 use Symfony\Component\HttpFoundation\JsonResponse;
 use Symfony\Component\HttpFoundation\RedirectResponse;
 use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
+use Symfony\Component\Routing\Annotation\Route;
+use Symfony\Component\Validator\Constraints\Locale as LocaleConstraint;
 use Wallabag\CoreBundle\Entity\Config;
+use Wallabag\CoreBundle\Entity\IgnoreOriginUserRule;
+use Wallabag\CoreBundle\Entity\RuleInterface;
 use Wallabag\CoreBundle\Entity\TaggingRule;
 use Wallabag\CoreBundle\Form\Type\ChangePasswordType;
 use Wallabag\CoreBundle\Form\Type\ConfigType;
-use Wallabag\CoreBundle\Form\Type\RssType;
+use Wallabag\CoreBundle\Form\Type\FeedType;
+use Wallabag\CoreBundle\Form\Type\IgnoreOriginUserRuleType;
+use Wallabag\CoreBundle\Form\Type\TaggingRuleImportType;
 use Wallabag\CoreBundle\Form\Type\TaggingRuleType;
 use Wallabag\CoreBundle\Form\Type\UserInformationType;
 use Wallabag\CoreBundle\Tools\Utils;
@@ -20,8 +29,6 @@ use Wallabag\CoreBundle\Tools\Utils;
 class ConfigController extends Controller
 {
     /**
-     * @param Request $request
-     *
      * @Route("/config", name="config")
      */
     public function indexAction(Request $request)
@@ -45,7 +52,7 @@ class ConfigController extends Controller
             $activeTheme = $this->get('liip_theme.active_theme');
             $activeTheme->setName($config->getTheme());
 
-            $this->get('session')->getFlashBag()->add(
+            $this->addFlash(
                 'notice',
                 'flashes.config.notice.config_saved'
             );
@@ -67,7 +74,7 @@ class ConfigController extends Controller
                 $userManager->updateUser($user, true);
             }
 
-            $this->get('session')->getFlashBag()->add('notice', $message);
+            $this->addFlash('notice', $message);
 
             return $this->redirect($this->generateUrl('config') . '#set4');
         }
@@ -82,7 +89,7 @@ class ConfigController extends Controller
         if ($userForm->isSubmitted() && $userForm->isValid()) {
             $userManager->updateUser($user, true);
 
-            $this->get('session')->getFlashBag()->add(
+            $this->addFlash(
                 'notice',
                 'flashes.config.notice.user_updated'
             );
@@ -90,17 +97,17 @@ class ConfigController extends Controller
             return $this->redirect($this->generateUrl('config') . '#set3');
         }
 
-        // handle rss information
-        $rssForm = $this->createForm(RssType::class, $config, ['action' => $this->generateUrl('config') . '#set2']);
-        $rssForm->handleRequest($request);
+        // handle feed information
+        $feedForm = $this->createForm(FeedType::class, $config, ['action' => $this->generateUrl('config') . '#set2']);
+        $feedForm->handleRequest($request);
 
-        if ($rssForm->isSubmitted() && $rssForm->isValid()) {
+        if ($feedForm->isSubmitted() && $feedForm->isValid()) {
             $em->persist($config);
             $em->flush();
 
-            $this->get('session')->getFlashBag()->add(
+            $this->addFlash(
                 'notice',
-                'flashes.config.notice.rss_updated'
+                'flashes.config.notice.feed_updated'
             );
 
             return $this->redirect($this->generateUrl('config') . '#set2');
@@ -130,7 +137,7 @@ class ConfigController extends Controller
             $em->persist($taggingRule);
             $em->flush();
 
-            $this->get('session')->getFlashBag()->add(
+            $this->addFlash(
                 'notice',
                 'flashes.config.notice.tagging_rules_updated'
             );
@@ -138,28 +145,258 @@ class ConfigController extends Controller
             return $this->redirect($this->generateUrl('config') . '#set5');
         }
 
+        // handle tagging rules import
+        $taggingRulesImportform = $this->createForm(TaggingRuleImportType::class);
+        $taggingRulesImportform->handleRequest($request);
+
+        if ($taggingRulesImportform->isSubmitted() && $taggingRulesImportform->isValid()) {
+            $message = 'flashes.config.notice.tagging_rules_not_imported';
+            $file = $taggingRulesImportform->get('file')->getData();
+
+            if (null !== $file && $file->isValid() && \in_array($file->getClientMimeType(), ['application/json', 'application/octet-stream'], true)) {
+                $content = json_decode(file_get_contents($file->getPathname()), true);
+
+                if (\is_array($content)) {
+                    foreach ($content as $rule) {
+                        $taggingRule = new TaggingRule();
+                        $taggingRule->setRule($rule['rule']);
+                        $taggingRule->setTags($rule['tags']);
+                        $taggingRule->setConfig($config);
+                        $em->persist($taggingRule);
+                    }
+
+                    $em->flush();
+
+                    $message = 'flashes.config.notice.tagging_rules_imported';
+                }
+            }
+
+            $this->addFlash('notice', $message);
+
+            return $this->redirect($this->generateUrl('config') . '#set5');
+        }
+
+        // handle ignore origin rules
+        $ignoreOriginUserRule = new IgnoreOriginUserRule();
+        $action = $this->generateUrl('config') . '#set6';
+
+        if ($request->query->has('ignore-origin-user-rule')) {
+            $ignoreOriginUserRule = $this->getDoctrine()
+                ->getRepository('WallabagCoreBundle:IgnoreOriginUserRule')
+                ->find($request->query->get('ignore-origin-user-rule'));
+
+            if ($this->getUser()->getId() !== $ignoreOriginUserRule->getConfig()->getUser()->getId()) {
+                return $this->redirect($action);
+            }
+
+            $action = $this->generateUrl('config', [
+                'ignore-origin-user-rule' => $ignoreOriginUserRule->getId(),
+            ]) . '#set6';
+        }
+
+        $newIgnoreOriginUserRule = $this->createForm(IgnoreOriginUserRuleType::class, $ignoreOriginUserRule, ['action' => $action]);
+        $newIgnoreOriginUserRule->handleRequest($request);
+
+        if ($newIgnoreOriginUserRule->isSubmitted() && $newIgnoreOriginUserRule->isValid()) {
+            $ignoreOriginUserRule->setConfig($config);
+            $em->persist($ignoreOriginUserRule);
+            $em->flush();
+
+            $this->addFlash(
+                'notice',
+                'flashes.config.notice.ignore_origin_rules_updated'
+            );
+
+            return $this->redirect($this->generateUrl('config') . '#set6');
+        }
+
         return $this->render('WallabagCoreBundle:Config:index.html.twig', [
             'form' => [
                 'config' => $configForm->createView(),
-                'rss' => $rssForm->createView(),
+                'feed' => $feedForm->createView(),
                 'pwd' => $pwdForm->createView(),
                 'user' => $userForm->createView(),
                 'new_tagging_rule' => $newTaggingRule->createView(),
+                'import_tagging_rule' => $taggingRulesImportform->createView(),
+                'new_ignore_origin_user_rule' => $newIgnoreOriginUserRule->createView(),
             ],
-            'rss' => [
+            'feed' => [
                 'username' => $user->getUsername(),
-                'token' => $config->getRssToken(),
+                'token' => $config->getFeedToken(),
             ],
             'twofactor_auth' => $this->getParameter('twofactor_auth'),
             'wallabag_url' => $this->getParameter('domain_name'),
-            'enabled_users' => $this->get('wallabag_user.user_repository')
-                ->getSumEnabledUsers(),
+            'enabled_users' => $this->get('wallabag_user.user_repository')->getSumEnabledUsers(),
+        ]);
+    }
+
+    /**
+     * Disable 2FA using email.
+     *
+     * @Route("/config/otp/email/disable", name="disable_otp_email")
+     */
+    public function disableOtpEmailAction()
+    {
+        if (!$this->getParameter('twofactor_auth')) {
+            return $this->createNotFoundException('two_factor not enabled');
+        }
+
+        $user = $this->getUser();
+        $user->setEmailTwoFactor(false);
+
+        $this->container->get('fos_user.user_manager')->updateUser($user, true);
+
+        $this->addFlash(
+            'notice',
+            'flashes.config.notice.otp_disabled'
+        );
+
+        return $this->redirect($this->generateUrl('config') . '#set3');
+    }
+
+    /**
+     * Enable 2FA using email.
+     *
+     * @Route("/config/otp/email", name="config_otp_email")
+     */
+    public function otpEmailAction()
+    {
+        if (!$this->getParameter('twofactor_auth')) {
+            return $this->createNotFoundException('two_factor not enabled');
+        }
+
+        $user = $this->getUser();
+
+        $user->setGoogleAuthenticatorSecret(null);
+        $user->setBackupCodes(null);
+        $user->setEmailTwoFactor(true);
+
+        $this->container->get('fos_user.user_manager')->updateUser($user, true);
+
+        $this->addFlash(
+            'notice',
+            'flashes.config.notice.otp_enabled'
+        );
+
+        return $this->redirect($this->generateUrl('config') . '#set3');
+    }
+
+    /**
+     * Disable 2FA using OTP app.
+     *
+     * @Route("/config/otp/app/disable", name="disable_otp_app")
+     */
+    public function disableOtpAppAction()
+    {
+        if (!$this->getParameter('twofactor_auth')) {
+            return $this->createNotFoundException('two_factor not enabled');
+        }
+
+        $user = $this->getUser();
+
+        $user->setGoogleAuthenticatorSecret('');
+        $user->setBackupCodes(null);
+
+        $this->container->get('fos_user.user_manager')->updateUser($user, true);
+
+        $this->addFlash(
+            'notice',
+            'flashes.config.notice.otp_disabled'
+        );
+
+        return $this->redirect($this->generateUrl('config') . '#set3');
+    }
+
+    /**
+     * Enable 2FA using OTP app, user will need to confirm the generated code from the app.
+     *
+     * @Route("/config/otp/app", name="config_otp_app")
+     */
+    public function otpAppAction()
+    {
+        if (!$this->getParameter('twofactor_auth')) {
+            return $this->createNotFoundException('two_factor not enabled');
+        }
+
+        $user = $this->getUser();
+        $secret = $this->get('scheb_two_factor.security.google_authenticator')->generateSecret();
+
+        $user->setGoogleAuthenticatorSecret($secret);
+        $user->setEmailTwoFactor(false);
+
+        $backupCodes = (new BackupCodes())->toArray();
+        $backupCodesHashed = array_map(
+            function ($backupCode) {
+                return password_hash($backupCode, PASSWORD_DEFAULT);
+            },
+            $backupCodes
+        );
+
+        $user->setBackupCodes($backupCodesHashed);
+
+        $this->container->get('fos_user.user_manager')->updateUser($user, true);
+
+        $this->addFlash(
+            'notice',
+            'flashes.config.notice.otp_enabled'
+        );
+
+        return $this->render('WallabagCoreBundle:Config:otp_app.html.twig', [
+            'backupCodes' => $backupCodes,
+            'qr_code' => $this->get('scheb_two_factor.security.google_authenticator')->getQRContent($user),
         ]);
     }
 
     /**
-     * @param Request $request
+     * Cancelling 2FA using OTP app.
      *
+     * @Route("/config/otp/app/cancel", name="config_otp_app_cancel")
+     */
+    public function otpAppCancelAction()
+    {
+        if (!$this->getParameter('twofactor_auth')) {
+            return $this->createNotFoundException('two_factor not enabled');
+        }
+
+        $user = $this->getUser();
+        $user->setGoogleAuthenticatorSecret(null);
+        $user->setBackupCodes(null);
+
+        $this->container->get('fos_user.user_manager')->updateUser($user, true);
+
+        return $this->redirect($this->generateUrl('config') . '#set3');
+    }
+
+    /**
+     * Validate OTP code.
+     *
+     * @Route("/config/otp/app/check", name="config_otp_app_check")
+     */
+    public function otpAppCheckAction(Request $request)
+    {
+        $isValid = $this->get('scheb_two_factor.security.google_authenticator')->checkCode(
+            $this->getUser(),
+            $request->get('_auth_code')
+        );
+
+        if (true === $isValid) {
+            $this->addFlash(
+                'notice',
+                'flashes.config.notice.otp_enabled'
+            );
+
+            return $this->redirect($this->generateUrl('config') . '#set3');
+        }
+
+        $this->addFlash(
+            'two_factor',
+            'scheb_two_factor.code_invalid'
+        );
+
+        return $this->redirect($this->generateUrl('config_otp_app'));
+    }
+
+    /**
      * @Route("/generate-token", name="generate_token")
      *
      * @return RedirectResponse|JsonResponse
@@ -167,28 +404,52 @@ class ConfigController extends Controller
     public function generateTokenAction(Request $request)
     {
         $config = $this->getConfig();
-        $config->setRssToken(Utils::generateToken());
+        $config->setFeedToken(Utils::generateToken());
 
         $em = $this->getDoctrine()->getManager();
         $em->persist($config);
         $em->flush();
 
         if ($request->isXmlHttpRequest()) {
-            return new JsonResponse(['token' => $config->getRssToken()]);
+            return new JsonResponse(['token' => $config->getFeedToken()]);
         }
 
-        $this->get('session')->getFlashBag()->add(
+        $this->addFlash(
             'notice',
-            'flashes.config.notice.rss_token_updated'
+            'flashes.config.notice.feed_token_updated'
         );
 
         return $this->redirect($this->generateUrl('config') . '#set2');
     }
 
     /**
-     * Deletes a tagging rule and redirect to the config homepage.
+     * @Route("/revoke-token", name="revoke_token")
      *
-     * @param TaggingRule $rule
+     * @return RedirectResponse|JsonResponse
+     */
+    public function revokeTokenAction(Request $request)
+    {
+        $config = $this->getConfig();
+        $config->setFeedToken(null);
+
+        $em = $this->getDoctrine()->getManager();
+        $em->persist($config);
+        $em->flush();
+
+        if ($request->isXmlHttpRequest()) {
+            return new JsonResponse();
+        }
+
+        $this->addFlash(
+            'notice',
+            'flashes.config.notice.feed_token_revoked'
+        );
+
+        return $this->redirect($this->generateUrl('config') . '#set2');
+    }
+
+    /**
+     * Deletes a tagging rule and redirect to the config homepage.
      *
      * @Route("/tagging-rule/delete/{id}", requirements={"id" = "\d+"}, name="delete_tagging_rule")
      *
@@ -202,7 +463,7 @@ class ConfigController extends Controller
         $em->remove($rule);
         $em->flush();
 
-        $this->get('session')->getFlashBag()->add(
+        $this->addFlash(
             'notice',
             'flashes.config.notice.tagging_rules_deleted'
         );
@@ -213,8 +474,6 @@ class ConfigController extends Controller
     /**
      * Edit a tagging rule.
      *
-     * @param TaggingRule $rule
-     *
      * @Route("/tagging-rule/edit/{id}", requirements={"id" = "\d+"}, name="edit_tagging_rule")
      *
      * @return RedirectResponse
@@ -226,6 +485,43 @@ class ConfigController extends Controller
         return $this->redirect($this->generateUrl('config') . '?tagging-rule=' . $rule->getId() . '#set5');
     }
 
+    /**
+     * Deletes an ignore origin rule and redirect to the config homepage.
+     *
+     * @Route("/ignore-origin-user-rule/delete/{id}", requirements={"id" = "\d+"}, name="delete_ignore_origin_rule")
+     *
+     * @return RedirectResponse
+     */
+    public function deleteIgnoreOriginRuleAction(IgnoreOriginUserRule $rule)
+    {
+        $this->validateRuleAction($rule);
+
+        $em = $this->getDoctrine()->getManager();
+        $em->remove($rule);
+        $em->flush();
+
+        $this->addFlash(
+            'notice',
+            'flashes.config.notice.ignore_origin_rules_deleted'
+        );
+
+        return $this->redirect($this->generateUrl('config') . '#set6');
+    }
+
+    /**
+     * Edit an ignore origin rule.
+     *
+     * @Route("/ignore-origin-user-rule/edit/{id}", requirements={"id" = "\d+"}, name="edit_ignore_origin_rule")
+     *
+     * @return RedirectResponse
+     */
+    public function editIgnoreOriginRuleAction(IgnoreOriginUserRule $rule)
+    {
+        $this->validateRuleAction($rule);
+
+        return $this->redirect($this->generateUrl('config') . '?ignore-origin-user-rule=' . $rule->getId() . '#set6');
+    }
+
     /**
      * Remove all annotations OR tags OR entries for the current user.
      *
@@ -268,7 +564,7 @@ class ConfigController extends Controller
                 break;
         }
 
-        $this->get('session')->getFlashBag()->add(
+        $this->addFlash(
             'notice',
             'flashes.config.notice.' . $type . '_reset'
         );
@@ -281,8 +577,6 @@ class ConfigController extends Controller
      *
      * @Route("/account/delete", name="delete_account")
      *
-     * @param Request $request
-     *
      * @throws AccessDeniedHttpException
      *
      * @return \Symfony\Component\HttpFoundation\RedirectResponse
@@ -313,8 +607,6 @@ class ConfigController extends Controller
      *
      * @Route("/config/view-mode", name="switch_view_mode")
      *
-     * @param Request $request
-     *
      * @return \Symfony\Component\HttpFoundation\RedirectResponse
      */
     public function changeViewModeAction(Request $request)
@@ -329,6 +621,52 @@ class ConfigController extends Controller
         return $this->redirect($request->headers->get('referer'));
     }
 
+    /**
+     * Change the locale for the current user.
+     *
+     * @param string $language
+     *
+     * @Route("/locale/{language}", name="changeLocale")
+     *
+     * @return \Symfony\Component\HttpFoundation\RedirectResponse
+     */
+    public function setLocaleAction(Request $request, $language = null)
+    {
+        $errors = $this->get('validator')->validate($language, (new LocaleConstraint()));
+
+        if (0 === \count($errors)) {
+            $request->getSession()->set('_locale', $language);
+        }
+
+        return $this->redirect($request->headers->get('referer', $this->generateUrl('homepage')));
+    }
+
+    /**
+     * Export tagging rules for the logged in user.
+     *
+     * @Route("/tagging-rule/export", name="export_tagging_rule")
+     *
+     * @return Response
+     */
+    public function exportTaggingRulesAction()
+    {
+        $data = SerializerBuilder::create()->build()->serialize(
+            $this->getUser()->getConfig()->getTaggingRules(),
+            'json',
+            SerializationContext::create()->setGroups(['export_tagging_rule'])
+        );
+
+        return Response::create(
+            $data,
+            200,
+            [
+                'Content-type' => 'application/json',
+                'Content-Disposition' => 'attachment; filename="tagging_rules_' . $this->getUser()->getUsername() . '.json"',
+                'Content-Transfer-Encoding' => 'UTF-8',
+            ]
+        );
+    }
+
     /**
      * Remove all tags for given tags and a given user and cleanup orphan tags.
      *
@@ -395,13 +733,11 @@ class ConfigController extends Controller
 
     /**
      * Validate that a rule can be edited/deleted by the current user.
-     *
-     * @param TaggingRule $rule
      */
-    private function validateRuleAction(TaggingRule $rule)
+    private function validateRuleAction(RuleInterface $rule)
     {
         if ($this->getUser()->getId() !== $rule->getConfig()->getUser()->getId()) {
-            throw $this->createAccessDeniedException('You can not access this tagging rule.');
+            throw $this->createAccessDeniedException('You can not access this rule.');
         }
     }