]> git.immae.eu Git - github/wallabag/wallabag.git/commitdiff
Merge pull request #1384 from wallabag/v2-fix-config-display
authorNicolas Lœuillet <nicolas@loeuillet.org>
Sun, 23 Aug 2015 11:17:21 +0000 (13:17 +0200)
committerNicolas Lœuillet <nicolas@loeuillet.org>
Sun, 23 Aug 2015 11:17:21 +0000 (13:17 +0200)
fix #1371 config screen: display bug in RSS tab

31 files changed:
.scrutinizer.yml
app/config/config.yml
composer.json
src/Wallabag/ApiBundle/Tests/Controller/WallabagRestControllerTest.php
src/Wallabag/CoreBundle/Controller/EntryController.php
src/Wallabag/CoreBundle/Controller/RssController.php
src/Wallabag/CoreBundle/Controller/StaticController.php
src/Wallabag/CoreBundle/Controller/TagController.php
src/Wallabag/CoreBundle/Entity/Entry.php
src/Wallabag/CoreBundle/Entity/Tag.php
src/Wallabag/CoreBundle/Filter/EntryFilterType.php
src/Wallabag/CoreBundle/Form/Type/NewTagType.php [new file with mode: 0644]
src/Wallabag/CoreBundle/Helper/Entry.php [deleted file]
src/Wallabag/CoreBundle/Helper/Tools.php [deleted file]
src/Wallabag/CoreBundle/Repository/EntryRepository.php
src/Wallabag/CoreBundle/Repository/TagRepository.php
src/Wallabag/CoreBundle/Resources/config/routing.yml
src/Wallabag/CoreBundle/Resources/views/Entry/entries.html.twig
src/Wallabag/CoreBundle/Resources/views/Entry/entry.html.twig
src/Wallabag/CoreBundle/Resources/views/Static/about.html.twig
src/Wallabag/CoreBundle/Resources/views/Tag/new_form.html.twig [new file with mode: 0644]
src/Wallabag/CoreBundle/Resources/views/base.html.twig
src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entries.html.twig
src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entry.html.twig
src/Wallabag/CoreBundle/Resources/views/themes/material/Static/about.html.twig
src/Wallabag/CoreBundle/Resources/views/themes/material/Tag/new_form.html.twig [new file with mode: 0644]
src/Wallabag/CoreBundle/Resources/views/themes/material/layout.html.twig
src/Wallabag/CoreBundle/Resources/views/themes/material/public/js/init.js
src/Wallabag/CoreBundle/Tests/Controller/EntryControllerTest.php
src/Wallabag/CoreBundle/Tests/Controller/TagControllerTest.php
src/Wallabag/CoreBundle/Tools/Utils.php

index 08f4a3e98cd9fa74e64c49794a26d551f2525275..8fbd8265f4c083a2b9d53a1424bb369e193d7db7 100644 (file)
@@ -21,3 +21,7 @@ tools:
     php_code_coverage: true
     php_sim: false
     php_cpd: false
+
+checks:
+    php:
+        code_rating: true
index 5b26b06fecf0d20f2132a4d220ada94556592db5..efc815b8e9a3211e122649c73d5883795a89bc0e 100644 (file)
@@ -51,6 +51,7 @@ twig:
     form:
         resources:
             - LexikFormFilterBundle:Form:form_div_layout.html.twig
+
 # Assetic Configuration
 assetic:
     debug:          "%kernel.debug%"
index 709b23ac8168855da63593f5bdd0bccffc51f21c..2c5111fdecc87e601a88899946a1fd11243814f0 100644 (file)
@@ -19,6 +19,7 @@
         },
         {
             "name": "Jérémy Benoist",
+            "homepage": "http://www.j0k3r.net",
             "role": "Developer"
         }
     ],
index 86c8de1ed0eb8d5d5352d5289af535b972b15a45..7ae54b576d577014bb3f5348235986973fc2b439 100644 (file)
@@ -170,6 +170,31 @@ class WallabagRestControllerTest extends WebTestCase
         $client = $this->createClient();
         $headers = $this->generateHeaders('admin', 'mypassword');
 
+        $client->request('GET', '/api/entries', array('star' => 1, 'sort' => 'updated'), array(), $headers);
+
+        $this->assertEquals(200, $client->getResponse()->getStatusCode());
+
+        $content = json_decode($client->getResponse()->getContent(), true);
+
+        $this->assertGreaterThanOrEqual(1, count($content));
+        $this->assertNotEmpty($content['_embedded']['items']);
+        $this->assertGreaterThanOrEqual(1, $content['total']);
+        $this->assertEquals(1, $content['page']);
+        $this->assertGreaterThanOrEqual(1, $content['pages']);
+
+        $this->assertTrue(
+            $client->getResponse()->headers->contains(
+                'Content-Type',
+                'application/json'
+            )
+        );
+    }
+
+    public function testGetArchiveEntries()
+    {
+        $client = $this->createClient();
+        $headers = $this->generateHeaders('admin', 'mypassword');
+
         $client->request('GET', '/api/entries', array('archive' => 1), array(), $headers);
 
         $this->assertEquals(200, $client->getResponse()->getStatusCode());
index 006fa396476019c3055ed9c83da58b6f542acdba..b73e9eecd52e5aeb81e0c72e75fdf3a97f0c290a 100644 (file)
@@ -101,6 +101,21 @@ class EntryController extends Controller
         ));
     }
 
+    /**
+     * Shows all entries for current user.
+     *
+     * @param Request $request
+     * @param int     $page
+     *
+     * @Route("/all/list/{page}", name="all", defaults={"page" = "1"})
+     *
+     * @return \Symfony\Component\HttpFoundation\Response
+     */
+    public function showAllAction(Request $request, $page)
+    {
+        return $this->showEntries('all', $request, $page);
+    }
+
     /**
      * Shows unread entries for current user.
      *
@@ -113,34 +128,7 @@ class EntryController extends Controller
      */
     public function showUnreadAction(Request $request, $page)
     {
-        $form = $this->get('form.factory')->create(new EntryFilterType());
-
-        $filterBuilder = $this->getDoctrine()
-            ->getRepository('WallabagCoreBundle:Entry')
-            ->findUnreadByUser($this->getUser()->getId());
-
-        if ($request->query->has($form->getName())) {
-            // manually bind values from the request
-            $form->submit($request->query->get($form->getName()));
-
-            // build the query from the given form object
-            $this->get('lexik_form_filter.query_builder_updater')->addFilterConditions($form, $filterBuilder);
-        }
-
-        $pagerAdapter = new DoctrineORMAdapter($filterBuilder->getQuery());
-        $entries = new Pagerfanta($pagerAdapter);
-
-        $entries->setMaxPerPage($this->getUser()->getConfig()->getItemsPerPage());
-        $entries->setCurrentPage($page);
-
-        return $this->render(
-            'WallabagCoreBundle:Entry:entries.html.twig',
-            array(
-                'form' => $form->createView(),
-                'entries' => $entries,
-                'currentPage' => $page,
-            )
-        );
+        return $this->showEntries('unread', $request, $page);
     }
 
     /**
@@ -155,34 +143,7 @@ class EntryController extends Controller
      */
     public function showArchiveAction(Request $request, $page)
     {
-        $form = $this->get('form.factory')->create(new EntryFilterType());
-
-        $filterBuilder = $this->getDoctrine()
-            ->getRepository('WallabagCoreBundle:Entry')
-            ->findArchiveByUser($this->getUser()->getId());
-
-        if ($request->query->has($form->getName())) {
-            // manually bind values from the request
-            $form->submit($request->query->get($form->getName()));
-
-            // build the query from the given form object
-            $this->get('lexik_form_filter.query_builder_updater')->addFilterConditions($form, $filterBuilder);
-        }
-
-        $pagerAdapter = new DoctrineORMAdapter($filterBuilder->getQuery());
-        $entries = new Pagerfanta($pagerAdapter);
-
-        $entries->setMaxPerPage($this->getUser()->getConfig()->getItemsPerPage());
-        $entries->setCurrentPage($page);
-
-        return $this->render(
-            'WallabagCoreBundle:Entry:entries.html.twig',
-            array(
-                'form' => $form->createView(),
-                'entries' => $entries,
-                'currentPage' => $page,
-            )
-        );
+        return $this->showEntries('archive', $request, $page);
     }
 
     /**
@@ -197,21 +158,55 @@ class EntryController extends Controller
      */
     public function showStarredAction(Request $request, $page)
     {
-        $form = $this->get('form.factory')->create(new EntryFilterType());
+        return $this->showEntries('starred', $request, $page);
+    }
+
+    /**
+     * Global method to retrieve entries depending on the given type
+     * It returns the response to be send.
+     *
+     * @param string  $type    Entries type: unread, starred or archive
+     * @param Request $request
+     * @param int     $page
+     *
+     * @return \Symfony\Component\HttpFoundation\Response
+     */
+    private function showEntries($type, Request $request, $page)
+    {
+        $repository = $this->getDoctrine()->getRepository('WallabagCoreBundle:Entry');
+
+        switch ($type) {
+            case 'starred':
+                $qb = $repository->getBuilderForStarredByUser($this->getUser()->getId());
+                break;
+
+            case 'archive':
+                $qb = $repository->getBuilderForArchiveByUser($this->getUser()->getId());
+                break;
+
+            case 'unread':
+                $qb = $repository->getBuilderForUnreadByUser($this->getUser()->getId());
+                break;
 
-        $filterBuilder = $this->getDoctrine()
-            ->getRepository('WallabagCoreBundle:Entry')
-            ->findStarredByUser($this->getUser()->getId());
+            case 'all':
+                $qb = $repository->getBuilderForAllByUser($this->getUser()->getId());
+                break;
+
+            default:
+                throw new \InvalidArgumentException(sprintf('Type "%s" is not implemented.', $type));
+        }
+
+        $form = $this->get('form.factory')->create(new EntryFilterType());
 
         if ($request->query->has($form->getName())) {
             // manually bind values from the request
             $form->submit($request->query->get($form->getName()));
 
             // build the query from the given form object
-            $this->get('lexik_form_filter.query_builder_updater')->addFilterConditions($form, $filterBuilder);
+            $this->get('lexik_form_filter.query_builder_updater')->addFilterConditions($form, $qb);
         }
 
-        $pagerAdapter = new DoctrineORMAdapter($filterBuilder->getQuery());
+        $pagerAdapter = new DoctrineORMAdapter($qb->getQuery());
         $entries = new Pagerfanta($pagerAdapter);
 
         $entries->setMaxPerPage($this->getUser()->getConfig()->getItemsPerPage());
index 0558c53b0fcbe4ba84572366523702a30aac35d7..6121f36142ab730b503933f657c9a1700e7fabf2 100644 (file)
@@ -22,22 +22,7 @@ class RssController extends Controller
      */
     public function showUnreadAction(User $user)
     {
-        $qb = $this->getDoctrine()
-            ->getRepository('WallabagCoreBundle:Entry')
-            ->findUnreadByUser(
-                $user->getId()
-            );
-
-        $pagerAdapter = new DoctrineORMAdapter($qb->getQuery());
-        $entries = new Pagerfanta($pagerAdapter);
-
-        $perPage = $user->getConfig()->getRssLimit() ?: $this->container->getParameter('rss_limit');
-        $entries->setMaxPerPage($perPage);
-
-        return $this->render('WallabagCoreBundle:Entry:entries.xml.twig', array(
-            'type' => 'unread',
-            'entries' => $entries,
-        ));
+        return $this->showEntries('unread', $user);
     }
 
     /**
@@ -50,22 +35,7 @@ class RssController extends Controller
      */
     public function showArchiveAction(User $user)
     {
-        $qb = $this->getDoctrine()
-            ->getRepository('WallabagCoreBundle:Entry')
-            ->findArchiveByUser(
-                $user->getId()
-            );
-
-        $pagerAdapter = new DoctrineORMAdapter($qb->getQuery());
-        $entries = new Pagerfanta($pagerAdapter);
-
-        $perPage = $user->getConfig()->getRssLimit() ?: $this->container->getParameter('rss_limit');
-        $entries->setMaxPerPage($perPage);
-
-        return $this->render('WallabagCoreBundle:Entry:entries.xml.twig', array(
-            'type' => 'archive',
-            'entries' => $entries,
-        ));
+        return $this->showEntries('archive', $user);
     }
 
     /**
@@ -78,11 +48,38 @@ class RssController extends Controller
      */
     public function showStarredAction(User $user)
     {
-        $qb = $this->getDoctrine()
-            ->getRepository('WallabagCoreBundle:Entry')
-            ->findStarredByUser(
-                $user->getId()
-            );
+        return $this->showEntries('starred', $user);
+    }
+
+    /**
+     * Global method to retrieve entries depending on the given type
+     * It returns the response to be send.
+     *
+     * @param string $type Entries type: unread, starred or archive
+     * @param User   $user
+     *
+     * @return \Symfony\Component\HttpFoundation\Response
+     */
+    private function showEntries($type, User $user)
+    {
+        $repository = $this->getDoctrine()->getRepository('WallabagCoreBundle:Entry');
+
+        switch ($type) {
+            case 'starred':
+                $qb = $repository->getBuilderForStarredByUser($user->getId());
+                break;
+
+            case 'archive':
+                $qb = $repository->getBuilderForArchiveByUser($user->getId());
+                break;
+
+            case 'unread':
+                $qb = $repository->getBuilderForUnreadByUser($user->getId());
+                break;
+
+            default:
+                throw new \InvalidArgumentException(sprintf('Type "%s" is not implemented.', $type));
+        }
 
         $pagerAdapter = new DoctrineORMAdapter($qb->getQuery());
         $entries = new Pagerfanta($pagerAdapter);
@@ -91,7 +88,7 @@ class RssController extends Controller
         $entries->setMaxPerPage($perPage);
 
         return $this->render('WallabagCoreBundle:Entry:entries.xml.twig', array(
-            'type' => 'starred',
+            'type' => $type,
             'entries' => $entries,
         ));
     }
index 3b844b44c500054cb47bd1412d9c7215399941d2..64875a6654c3723de9facd382a7f4552b8eee90d 100644 (file)
@@ -28,12 +28,4 @@ class StaticController extends Controller
             array()
         );
     }
-
-    /**
-     * @Route("/", name="homepage")
-     */
-    public function apiAction()
-    {
-        return $this->redirect($this->generateUrl('nelmio_api_doc_index'));
-    }
 }
index a342ec0b91e21a44fa6c8ad7d3aa13a1c5c2817c..fd2069e0315982f7eb42e7ef6a769899dd5804c2 100644 (file)
@@ -4,9 +4,59 @@ namespace Wallabag\CoreBundle\Controller;
 
 use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
 use Symfony\Bundle\FrameworkBundle\Controller\Controller;
+use Symfony\Component\HttpFoundation\Request;
+use Wallabag\CoreBundle\Form\Type\NewTagType;
+use Wallabag\CoreBundle\Entity\Tag;
+use Wallabag\CoreBundle\Entity\Entry;
 
 class TagController extends Controller
 {
+    /**
+     * @param Request $request
+     *
+     * @Route("/new-tag/{entry}", requirements={"entry" = "\d+"}, name="new_tag")
+     *
+     * @return \Symfony\Component\HttpFoundation\Response
+     */
+    public function addTagFormAction(Request $request, Entry $entry)
+    {
+        $tag = new Tag($this->getUser());
+        $form = $this->createForm(new NewTagType(), $tag);
+        $form->handleRequest($request);
+
+        if ($form->isValid()) {
+            $existingTag = $this->getDoctrine()
+                ->getRepository('WallabagCoreBundle:Tag')
+                ->findOneByLabelAndUserId($tag->getLabel(), $this->getUser()->getId());
+
+            $em = $this->getDoctrine()->getManager();
+
+            if (is_null($existingTag)) {
+                $entry->addTag($tag);
+                $em->persist($tag);
+            } else {
+                if (!$existingTag->hasEntry($entry)) {
+                    $entry->addTag($existingTag);
+                    $em->persist($existingTag);
+                }
+            }
+
+            $em->flush();
+
+            $this->get('session')->getFlashBag()->add(
+                'notice',
+                'Tag added'
+            );
+
+            return $this->redirect($this->generateUrl('view', array('id' => $entry->getId())));
+        }
+
+        return $this->render('WallabagCoreBundle:Tag:new_form.html.twig', array(
+            'form' => $form->createView(),
+            'entry' => $entry,
+        ));
+    }
+
     /**
      * Shows tags for current user.
      *
index 7d2d2027f1bbf43478db1887339a2ed8a38cef56..f88d189d3f6e376f08fec5851999d478c0dede8d 100644 (file)
@@ -7,7 +7,7 @@ use Doctrine\ORM\Mapping as ORM;
 use Symfony\Component\Validator\Constraints as Assert;
 use Hateoas\Configuration\Annotation as Hateoas;
 use JMS\Serializer\Annotation\XmlRoot;
-use Wallabag\CoreBundle\Helper\Tools;
+use Wallabag\CoreBundle\Tools\Utils;
 
 /**
  * Entry.
@@ -265,7 +265,7 @@ class Entry
     public function setContent($content)
     {
         $this->content = $content;
-        $this->readingTime = Tools::getReadingTime($content);
+        $this->readingTime = Utils::getReadingTime($content);
         $this->domainName = parse_url($this->url, PHP_URL_HOST);
 
         return $this;
index 6f005314f69fd8e33a2e253a0d135bf74913171c..97c4579f70e8c1a607817a28ca5985912a2c8b3d 100644 (file)
@@ -96,6 +96,11 @@ class Tag
         $this->entries[] = $entry;
     }
 
+    public function hasEntry($entry)
+    {
+        return $this->entries->contains($entry);
+    }
+
     /**
      * @return User
      */
index 771daef1799edb9c02637570a82b89877e93bbac..ff51785b8411ca733def9bdc0cdfe12b84fd902e 100644 (file)
@@ -39,7 +39,9 @@ class EntryFilterType extends AbstractType
 
                         return $filterQuery->createCondition($expression);
                     },
-            ));
+            ))
+            ->add('isArchived', 'filter_checkbox')
+            ->add('isStarred', 'filter_checkbox');
     }
 
     public function getName()
diff --git a/src/Wallabag/CoreBundle/Form/Type/NewTagType.php b/src/Wallabag/CoreBundle/Form/Type/NewTagType.php
new file mode 100644 (file)
index 0000000..8e4ab64
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+
+namespace Wallabag\CoreBundle\Form\Type;
+
+use Symfony\Component\Form\AbstractType;
+use Symfony\Component\Form\FormBuilderInterface;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+
+class NewTagType extends AbstractType
+{
+    public function buildForm(FormBuilderInterface $builder, array $options)
+    {
+        $builder
+            ->add('label', 'text', array('required' => true))
+            ->add('save', 'submit')
+        ;
+    }
+
+    public function configureOptions(OptionsResolver $resolver)
+    {
+        $resolver->setDefaults(array(
+            'data_class' => 'Wallabag\CoreBundle\Entity\Tag',
+        ));
+    }
+
+    public function getName()
+    {
+        return 'tag';
+    }
+}
diff --git a/src/Wallabag/CoreBundle/Helper/Entry.php b/src/Wallabag/CoreBundle/Helper/Entry.php
deleted file mode 100644 (file)
index 219711b..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-<?php
-
-namespace Wallabag\CoreBundle\Helper;
-
-class Entry
-{
-}
diff --git a/src/Wallabag/CoreBundle/Helper/Tools.php b/src/Wallabag/CoreBundle/Helper/Tools.php
deleted file mode 100755 (executable)
index d368ee7..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-<?php
-
-namespace Wallabag\CoreBundle\Helper;
-
-final class Tools
-{
-    /**
-     * Download a file (typically, for downloading pictures on web server).
-     *
-     * @param $url
-     *
-     * @return bool|mixed|string
-     */
-    public static function getFile($url)
-    {
-        $timeout = 15;
-        $useragent = 'Mozilla/5.0 (Windows NT 5.1; rv:18.0) Gecko/20100101 Firefox/18.0';
-
-        if (in_array('curl', get_loaded_extensions())) {
-            # Fetch feed from URL
-            $curl = curl_init();
-            curl_setopt($curl, CURLOPT_URL, $url);
-            curl_setopt($curl, CURLOPT_TIMEOUT, $timeout);
-            if (!ini_get('open_basedir') && !ini_get('safe_mode')) {
-                curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
-            }
-            curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
-            curl_setopt($curl, CURLOPT_HEADER, false);
-
-            # for ssl, do not verified certificate
-            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
-            curl_setopt($curl, CURLOPT_AUTOREFERER, true);
-
-            # FeedBurner requires a proper USER-AGENT...
-            curl_setopt($curl, CURL_HTTP_VERSION_1_1, true);
-            curl_setopt($curl, CURLOPT_ENCODING, 'gzip, deflate');
-            curl_setopt($curl, CURLOPT_USERAGENT, $useragent);
-
-            $data = curl_exec($curl);
-            $httpcode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
-            $httpcodeOK = isset($httpcode) and ($httpcode == 200 or $httpcode == 301);
-            curl_close($curl);
-        } else {
-            # create http context and add timeout and user-agent
-            $context = stream_context_create(
-                array(
-                    'http' => array(
-                        'timeout' => $timeout,
-                        'header' => 'User-Agent: '.$useragent,
-                        'follow_location' => true,
-                    ),
-                    'ssl' => array(
-                        'verify_peer' => false,
-                        'allow_self_signed' => true,
-                    ),
-                )
-            );
-
-            # only download page lesser than 4MB
-            $data = @file_get_contents($url, false, $context, -1, 4000000);
-
-            if (isset($http_response_header) and isset($http_response_header[0])) {
-                $httpcodeOK = isset($http_response_header) and isset($http_response_header[0]) and ((strpos($http_response_header[0], '200 OK') !== false) or (strpos($http_response_header[0], '301 Moved Permanently') !== false));
-            }
-        }
-
-        # if response is not empty and response is OK
-        if (isset($data) and isset($httpcodeOK) and $httpcodeOK) {
-            # take charset of page and get it
-            preg_match('#<meta .*charset=.*>#Usi', $data, $meta);
-
-            # if meta tag is found
-            if (!empty($meta[0])) {
-                preg_match('#charset="?(.*)"#si', $meta[0], $encoding);
-                # if charset is found set it otherwise, set it to utf-8
-                $html_charset = (!empty($encoding[1])) ? strtolower($encoding[1]) : 'utf-8';
-                if (empty($encoding[1])) {
-                    $encoding[1] = 'utf-8';
-                }
-            } else {
-                $html_charset = 'utf-8';
-                $encoding[1] = '';
-            }
-
-            # replace charset of url to charset of page
-            $data = str_replace('charset='.$encoding[1], 'charset='.$html_charset, $data);
-
-            return $data;
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Encode a URL by using a salt.
-     *
-     * @param $string
-     *
-     * @return string
-     */
-    public static function encodeString($string)
-    {
-        return sha1($string.SALT);
-    }
-
-    public static function generateToken()
-    {
-        if (ini_get('open_basedir') === '') {
-            if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
-                // alternative to /dev/urandom for Windows
-                $token = substr(base64_encode(uniqid(mt_rand(), true)), 0, 20);
-            } else {
-                $token = substr(base64_encode(file_get_contents('/dev/urandom', false, null, 0, 20)), 0, 15);
-            }
-        } else {
-            $token = substr(base64_encode(uniqid(mt_rand(), true)), 0, 20);
-        }
-
-        return str_replace('+', '', $token);
-    }
-
-    /**
-     * For a given text, we calculate reading time for an article.
-     *
-     * @param $text
-     *
-     * @return float
-     */
-    public static function getReadingTime($text)
-    {
-        return floor(str_word_count(strip_tags($text)) / 200);
-    }
-}
index f885ee94fd09a98cae60c6c7ccbd86c9b957dda4..e764e8f707e6566e2fcdec8914ae286904a930f6 100644 (file)
@@ -9,19 +9,48 @@ use Pagerfanta\Pagerfanta;
 class EntryRepository extends EntityRepository
 {
     /**
-     * Retrieves unread entries for a user.
+     * Return a query builder to used by other getBuilderFor* method.
      *
      * @param int $userId
      *
      * @return QueryBuilder
      */
-    public function findUnreadByUser($userId)
+    private function getBuilderByUser($userId)
     {
         return $this->createQueryBuilder('e')
             ->leftJoin('e.user', 'u')
-            ->where('e.isArchived = false')
-            ->andWhere('u.id =:userId')->setParameter('userId', $userId)
-            ->orderBy('e.id', 'desc');
+            ->andWhere('u.id = :userId')->setParameter('userId', $userId)
+            ->orderBy('e.id', 'desc')
+        ;
+    }
+
+    /**
+     * Retrieves all entries for a user.
+     *
+     * @param int $userId
+     *
+     * @return QueryBuilder
+     */
+    public function getBuilderForAllByUser($userId)
+    {
+        return $this
+            ->getBuilderByUser($userId)
+        ;
+    }
+
+    /**
+     * Retrieves unread entries for a user.
+     *
+     * @param int $userId
+     *
+     * @return QueryBuilder
+     */
+    public function getBuilderForUnreadByUser($userId)
+    {
+        return $this
+            ->getBuilderByUser($userId)
+            ->andWhere('e.isArchived = false')
+        ;
     }
 
     /**
@@ -31,13 +60,12 @@ class EntryRepository extends EntityRepository
      *
      * @return QueryBuilder
      */
-    public function findArchiveByUser($userId)
+    public function getBuilderForArchiveByUser($userId)
     {
-        return $this->createQueryBuilder('e')
-            ->leftJoin('e.user', 'u')
-            ->where('e.isArchived = true')
-            ->andWhere('u.id =:userId')->setParameter('userId', $userId)
-            ->orderBy('e.id', 'desc');
+        return $this
+            ->getBuilderByUser($userId)
+            ->andWhere('e.isArchived = true')
+        ;
     }
 
     /**
@@ -47,13 +75,12 @@ class EntryRepository extends EntityRepository
      *
      * @return QueryBuilder
      */
-    public function findStarredByUser($userId)
+    public function getBuilderForStarredByUser($userId)
     {
-        return $this->createQueryBuilder('e')
-            ->leftJoin('e.user', 'u')
-            ->where('e.isStarred = true')
-            ->andWhere('u.id =:userId')->setParameter('userId', $userId)
-            ->orderBy('e.id', 'desc');
+        return $this
+            ->getBuilderByUser($userId)
+            ->andWhere('e.isStarred = true')
+        ;
     }
 
     /**
index 9c4096072626273dd98372a69f95f85a07c61262..ac3145a1dc8896d5ec8a980f3fee4769d37817ed 100644 (file)
@@ -24,4 +24,21 @@ class TagRepository extends EntityRepository
 
         return new Pagerfanta($pagerAdapter);
     }
+
+    /**
+     * Find a tag by its label and its owner.
+     *
+     * @param string $label
+     * @param int    $userId
+     *
+     * @return Tag|null
+     */
+    public function findOneByLabelAndUserId($label, $userId)
+    {
+        return $this->createQueryBuilder('t')
+            ->where('t.label = :label')->setParameter('label', $label)
+            ->andWhere('t.user = :user_id')->setParameter('user_id', $userId)
+            ->getQuery()
+            ->getOneOrNullResult();
+    }
 }
index f3502e156e6c80e884e9beb89bd42f76ec55bdd7..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 (file)
@@ -1,7 +0,0 @@
-entry:
-    resource: "@WallabagCoreBundle/Controller/EntryController.php"
-    type:     annotation
-
-config:
-    resource: "@WallabagCoreBundle/Controller/ConfigController.php"
-    type:     annotation
index a794df0e57e22e47a8898ef9f5c5aac96d77f688..118a2f4bedfa39c3f7b207b6d5ef98700a581f12 100644 (file)
@@ -21,7 +21,7 @@
     {% if entries is empty %}
         <div class="messages warning"><p>{% trans %}No articles found.{% endtrans %}</p></div>
     {% else %}
-        <div><form>{{ form_rest(form) }}<button class="btn waves-effect waves-light" type="submit" id="submit-filter" value="filter">Filter</button></form></div>
+        <div><form action="{{ path('all') }}">{{ form_rest(form) }}<button class="btn waves-effect waves-light" type="submit" id="submit-filter" value="filter">Filter</button></form></div>
         {% for entry in entries %}
             <div id="entry-{{ entry.id|e }}" class="entry">
                 <h2><a href="{{ path('view', { 'id': entry.id }) }}">{{ entry.title|raw }}</a></h2>
index 00480d1aa6d6fc6fe1e12527548919aefa909d07..18cfd59d2d036e586980fe14e8439dd161fe8acf 100644 (file)
@@ -28,7 +28,8 @@
             <h1>{{ entry.title|raw }} <a href="{{ path('edit', { 'id': entry.id }) }}" title="{% trans %}Edit tags{% endtrans %}">✎</a></h1>
         </header>
         <aside class="tags">
-            tags: {% for tag in entry.tags %}<a href="./?view=tag&amp;id={{ tag.id }}">{{ tag.label }}</a> {% endfor %}<a href="./?view=edit-tags&amp;id={{ entry.id }}" title="{% trans %}Edit tags{% endtrans %}">✎</a>
+            {% for tag in entry.tags %}<span class="mdi-action-label-outline">{{ tag.label }}</span>{% endfor %}
+            {{ render(controller( "WallabagCoreBundle:Tag:addTagForm", { 'id': entry.id } )) }}
         </aside>
         <article>
             {{ entry.content | raw }}
index 9e188cd92d51bf368d2a8785a469bf333cd6835b..311b5067a21ad72d9839a07a09a3d86f4f8bcd4f 100755 (executable)
@@ -3,38 +3,34 @@
 {% block title %}{% trans %}About{% endtrans %}{% endblock %}
 
 {% block content %}
-    <h2>{% trans %}About wallabag{% endtrans %}</h2>
+    <h2>{% trans %}Who is behind wallabag{% endtrans %}</h2>
 
     <dl>
-        <dt>{% trans %}Project website{% endtrans %}</dt>
-        <dd><a href="https://www.wallabag.org">https://www.wallabag.org</a></dd>
-
-        <dt>{% trans %}Main developer{% endtrans %}</dt>
+        <dt>{% trans %}Developed by{% endtrans %}</dt>
         <dd><a href="mailto:nicolas@loeuillet.org">Nicolas Lœuillet</a> — <a href="http://cdetc.fr">{% trans %}website{% endtrans %}</a></dd>
+        <dd>Thomas Citharel — <a href="https://tcit.fr">{% trans %}website{% endtrans %}</a></dd>
+        <dd>Jérémy Benoist — <a href="http://www.j0k3r.net">{% trans %}website{% endtrans %}</a></dd>
 
-        <dt>{% trans %}Contributors ♥:{% endtrans %}</dt>
-        <dd><a href="https://github.com/wallabag/wallabag/graphs/contributors">{% trans %}on Github{% endtrans %}</a></dd>
+        <dt>{% trans %}And many others contributors ♥{% endtrans %} <a href="https://github.com/wallabag/wallabag/graphs/contributors">{% trans %}on Github{% endtrans %}</a></dt>
 
-        <dt>{% trans %}Bug reports{% endtrans %}</dt>
-        <dd><a href="https://support.wallabag.org">{% trans %}On our support website{% endtrans %}</a> {% trans %}or{% endtrans %} <a href="https://github.com/wallabag/wallabag/issues">{% trans %}on Github{% endtrans %}</a></dd>
+        <dt>{% trans %}Project website{% endtrans %}</dt>
+        <dd><a href="https://www.wallabag.org">https://www.wallabag.org</a></dd>
 
-        <dt>{% trans %}License{% endtrans %}</dt>
-        <dd><a href="http://en.wikipedia.org/wiki/MIT_License">MIT</a></dd>
+        <dt>{% trans %}License{% endtrans %}: <a href="http://en.wikipedia.org/wiki/MIT_License">MIT</a></dt>
 
-        <dt>{% trans %}Version{% endtrans %}</dt>
-        <dd>{{ version }}</dd>
+        <dt>{% trans %}Version{% endtrans %}: {{ version }}</dt>
     </dl>
 
-    <p>{% trans %}wallabag is a read-it-later application: you can save a web page by keeping only content. Elements like ads or menus are deleted.{% endtrans %}</p>
-
     <h2>{% trans %}Getting help{% endtrans %}</h2>
 
     <dl>
         <dt>{% trans %}Documentation{% endtrans %}</dt>
-        <dd><a href="https://doc.wallabag.org/">Online documentation</a></dd>
+        <dd><a href="https://doc.wallabag.org/en">english</a></dd>
+        <dd><a href="https://doc.wallabag.org/fr">français</a></dd>
+        <dd><a href="https://doc.wallabag.org/de">deutsch</a></dd>
 
-        <dt>{% trans %}Support{% endtrans %}</dt>
-        <dd><a href="http://support.wallabag.org/">http://support.wallabag.org/</a></dd>
+        <dt>{% trans %}Bug reports{% endtrans %}</dt>
+        <dd><a href="https://support.wallabag.org">{% trans %}On our support website{% endtrans %}</a> {% trans %}or{% endtrans %} <a href="https://github.com/wallabag/wallabag/issues">{% trans %}on Github{% endtrans %}</a></dd>
     </dl>
 
     <h2>{% trans %}Helping wallabag{% endtrans %}</h2>
     <p>{% trans %}wallabag is free and opensource. You can help us:{% endtrans %}</p>
 
     <dl>
-        <dt><a href="{{ paypal_url }}">{% trans %}via Paypal{% endtrans %}</a></dt>
+        <dt>{% trans %}wallabag is free and opensource. You can help us:{% endtrans %}</dt>
+        <dd>by contributing to the project: <a href="https://github.com/wallabag/wallabag/issues/1254">an issue lists all our needs</a></dd>
+        <dd><a href="{{ paypal_url }}">{% trans %}via Paypal{% endtrans %}</a></dd>
 
-        <dt><a href="{{ flattr_url }}">{% trans %}via Flattr{% endtrans %}</a></dt>
+        <dd><a href="{{ flattr_url }}">{% trans %}via Flattr{% endtrans %}</a></dd>
     </dl>
 {% endblock %}
diff --git a/src/Wallabag/CoreBundle/Resources/views/Tag/new_form.html.twig b/src/Wallabag/CoreBundle/Resources/views/Tag/new_form.html.twig
new file mode 100644 (file)
index 0000000..0b5a530
--- /dev/null
@@ -0,0 +1,15 @@
+<form name="tag" method="post" action="{{ path('new_tag', { 'entry': entry.id })}}">
+
+        {% if form_errors(form) %}
+            <span class="black-text">{{ form_errors(form) }}</span>
+        {% endif %}
+
+        {% if form_errors(form.label) %}
+            <span class="black-text">{{ form_errors(form.label) }}</span>
+        {% endif %}
+
+        {{ form_widget(form.label, { 'attr': {'autocomplete': 'off'} }) }}
+        {{ form_widget(form.save, { 'attr': {'class': 'btn waves-effect waves-light'}, 'label': 'add tag' }) }}
+
+    <div class="hidden">{{ form_rest(form) }}</div>
+</form>
index e27aceae492e658ade3ff47f2ed08802f3410836..3ad776b99c3f8a8cae8d5423df9c2009b48a0f5e 100644 (file)
@@ -71,6 +71,7 @@
                     <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="{{ path('all') }}"}>{% trans %}all{% endtrans %}</a></li>
                     <li><a href="{{ path ('tag') }}">{% trans %}tags{% endtrans %}</a></li>
                     <li><a href="{{ path('new') }}">{% trans %}save a link{% endtrans %}</a></li>
                     <li style="position: relative;"><a href="javascript: void(null);" id="search">{% trans %}search{% endtrans %}</a>
index bd64067c567de99ab798ea61e62ae402070de1c9..b45552f2df8a00a79e90f04b11e425535b01b3a4 100644 (file)
@@ -7,6 +7,8 @@
     {% trans %}Starred{% endtrans %}
   {% elseif currentRoute == 'archive' %}
     {% trans %}Archive{% endtrans %}
+  {% elseif currentRoute == 'all' %}
+    {% trans %}Filtered{% endtrans %}
   {% else %}
     {% trans %}Unread{% endtrans %}
   {% endif %}
 
     <!-- Filters -->
     <div id="filters" class="side-nav fixed right-aligned">
-        <form>
+        <form action="{{ path('all') }}">
 
             <h4 class="center">{% trans %}Filters{% endtrans %}</h1>
 
             <div class="row">
 
+
+                <div class="col s12">
+                    <label>{% trans %}Status{% endtrans %}</label>
+                </div>
+                <div class="input-field col s6">
+                    {{ form_widget(form.isArchived) }}
+                    <label for="entry_filter_isArchived">{% trans %}Archived{% endtrans %}</label>
+                </div>
+
+                <div class="input-field col s6">
+                    {{ form_widget(form.isStarred) }}
+                    <label for="entry_filter_isStarred">{% trans %}Starred{% endtrans %}</label>
+                </div>
+
                 <div class="col s12">
                     <label>{% trans %}Reading time in minutes{% endtrans %}</label>
                 </div>
@@ -77,7 +93,6 @@
                     <label for="entry_filter_readingTime_right_number">{% trans %}to{% endtrans %}</label>
                 </div>
 
-
                 <div class="input-field col s6">
                     {{ form_widget(form.domainName, {'type': 'text', 'attr' : {'placeholder': 'website.com'} }) }}
                     <label for="entry_filter_domainName">{% trans %}Domain name{% endtrans %}</label>
index b92c41b60b565808d820da1af886aac1af7680f1..31b2c664c17be5439b1f38f7f6014d4d0e6c9573 100644 (file)
@@ -137,7 +137,8 @@ main {
             <a href="{{ entry.url|e }}" target="_blank" title="{% trans %}original{% endtrans %} : {{ entry.title|e }}" class="tool link"><span>{{ entry.domainName }}</span></a>
         </header>
         <aside class="tags">
-            tags: {% for tag in entry.tags %}<a href="./?view=tag&amp;id={{ tag.id }}">{{ tag.label }}</a> {% endfor %}<a href="./?view=edit-tags&amp;id={{ entry.id }}" title="{% trans %}Edit tags{% endtrans %}">✎</a>
+            {% for tag in entry.tags %}<span class="mdi-action-label-outline">{{ tag.label }}</span>{% endfor %}
+            {{ render(controller( "WallabagCoreBundle:Tag:addTagForm", { 'id': entry.id } )) }}
         </aside>
         <article>
             {{ entry.content | raw }}
index 8c6269eca172f9107b9dbf637bb3e79c75c1d230..5de71d77c37f4c78c26f0a83c97432cf0b725bb9 100755 (executable)
@@ -21,7 +21,7 @@
                         <dt>{% trans %}Developed by{% endtrans %}</dt>
                         <dd><a href="mailto:nicolas@loeuillet.org">Nicolas Lœuillet</a> — <a href="http://cdetc.fr">{% trans %}website{% endtrans %}</a></dd>
                         <dd>Thomas Citharel — <a href="https://tcit.fr">{% trans %}website{% endtrans %}</a></dd>
-                        <dd>Jérémy Besnoit — <a href="http://wildtrip.net">{% trans %}website{% endtrans %}</a></dd>
+                        <dd>Jérémy Benoist — <a href="http://www.j0k3r.net">{% trans %}website{% endtrans %}</a></dd>
                         <dt>{% trans %}And many others contributors ♥{% endtrans %} <a href="https://github.com/wallabag/wallabag/graphs/contributors">{% trans %}on Github{% endtrans %}</a></dt>
                         <dt>{% trans %}Project website{% endtrans %}</dt>
                         <dd><a href="https://www.wallabag.org">https://www.wallabag.org</a></dd>
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/Tag/new_form.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Tag/new_form.html.twig
new file mode 100644 (file)
index 0000000..0b5a530
--- /dev/null
@@ -0,0 +1,15 @@
+<form name="tag" method="post" action="{{ path('new_tag', { 'entry': entry.id })}}">
+
+        {% if form_errors(form) %}
+            <span class="black-text">{{ form_errors(form) }}</span>
+        {% endif %}
+
+        {% if form_errors(form.label) %}
+            <span class="black-text">{{ form_errors(form.label) }}</span>
+        {% endif %}
+
+        {{ form_widget(form.label, { 'attr': {'autocomplete': 'off'} }) }}
+        {{ form_widget(form.save, { 'attr': {'class': 'btn waves-effect waves-light'}, 'label': 'add tag' }) }}
+
+    <div class="hidden">{{ form_rest(form) }}</div>
+</form>
index 554865d7fa1d56049bfcde7987d771a25f6a41ce..0ec2e082bdece743c06e79da174dd103ccf05881 100644 (file)
@@ -42,6 +42,7 @@
             <li class="bold {% if currentRoute == 'unread' or currentRoute == 'homepage' %}active{% endif %}"><a class="waves-effect" href="{{ path('unread') }}">{% trans %}unread{% endtrans %}</a></li>
             <li class="bold {% if currentRoute == 'starred' %}active{% endif %}"><a class="waves-effect" href="{{ path('starred') }}">{% trans %}starred{% endtrans %}</a></li>
             <li class="bold {% if currentRoute == 'archive' %}active{% endif %}"><a class="waves-effect" href="{{ path('archive') }}">{% trans %}archive{% endtrans %}</a></li>
+            <li class="bold {% if currentRoute == 'all' %}active{% endif %}"><a class="waves-effect" href="{{ path('all') }}">{% trans %}all{% endtrans %}</a></li>
             <li class="bold border-bottom {% if currentRoute == 'tags' %}active{% endif %}"><a class="waves-effect" href="{{ path('tag') }}">{% trans %}tags{% endtrans %}</a></li>
             <li class="bold {% if currentRoute == 'config' %}active{% endif %}"><a class="waves-effect" href="{{ path('config') }}">{% trans %}config{% endtrans %}</a></li>
             <li class="bold {% if currentRoute == 'howto' %}active{% endif %}"><a class="waves-effect" href="{{ path('howto') }}">{% trans %}howto{% endtrans %}</a></li>
@@ -55,9 +56,9 @@
             </div>
             <div class="input-field nav-panel-buttom">
                 <ul>
-                    <li class="bold"><a class="waves-effect" href="{{ path('new') }}" id="nav-btn-add"><i class="mdi-content-add"></i></a></li>
-                    <li><a class="waves-effect" href="javascript: void(null);" id="nav-btn-search"><i class="mdi-action-search"></i></a>
-                    <li id="button_filters"><a href="#" data-activates="filters" class="nav-panel-menu button-collapse-right"><i class="mdi-content-filter-list"></i></a></li>
+                    <li class="bold"><a title="{% trans %}Add a new entry{% endtrans %}" class="waves-effect" href="{{ path('new') }}" id="nav-btn-add"><i class="mdi-content-add"></i></a></li>
+                    <li><a title="{% trans %}Search{% endtrans %}" class="waves-effect" href="javascript: void(null);" id="nav-btn-search"><i class="mdi-action-search"></i></a>
+                    <li id="button_filters"><a title="{% trans %}Filter entries{% endtrans %}" href="#" data-activates="filters" class="nav-panel-menu button-collapse-right"><i class="mdi-content-filter-list"></i></a></li>
                 </ul>
             </div>
             <form method="get" action="index.php">
index c0700c2cbbeac312c3716093de2f247999e487a9..d397f8e5cb27e31b5e5a2c25f62575303903bb1c 100755 (executable)
@@ -5,6 +5,7 @@ function init_filters() {
         $('.button-collapse-right').sideNav({ edge: 'right' });
         $('#clear_form_filters').on('click', function(){
             $('#filters input').val('');
+            $('#filters :checked').removeAttr('checked');
             return false;
         });
     }
index 86a19f616e351490be7e640ada6233e79d8afdf9..5f0a6076388535821460c9c5eb83f0561c11d032 100644 (file)
@@ -276,7 +276,7 @@ class EntryControllerTest extends WallabagCoreTestCase
 
         $crawler = $client->submit($form, $data);
 
-        $this->assertCount(4, $crawler->filter('div[class=entry]'));
+        $this->assertCount(5, $crawler->filter('div[class=entry]'));
 
         $data = array(
             'entry_filter[createdAt][left_date]' => '01/01/1970',
@@ -307,6 +307,14 @@ class EntryControllerTest extends WallabagCoreTestCase
         $crawler = $client->request('GET', 'unread/list'.$parameters);
 
         $this->assertContains($parameters, $client->getResponse()->getContent());
+
+        // reset pagination
+        $crawler = $client->request('GET', '/config');
+        $form = $crawler->filter('button[id=config_save]')->form();
+        $data = array(
+            'config[items_per_page]' => '12',
+        );
+        $client->submit($form, $data);
     }
 
     public function testFilterOnDomainName()
@@ -331,4 +339,25 @@ class EntryControllerTest extends WallabagCoreTestCase
         $crawler = $client->submit($form, $data);
         $this->assertCount(0, $crawler->filter('div[class=entry]'));
     }
+
+    public function testFilterOnStatus()
+    {
+        $this->logInAs('admin');
+        $client = $this->getClient();
+
+        $crawler = $client->request('GET', '/unread/list');
+        $form = $crawler->filter('button[id=submit-filter]')->form();
+        $form['entry_filter[isArchived]']->tick();
+        $form['entry_filter[isStarred]']->untick();
+
+        $crawler = $client->submit($form);
+        $this->assertCount(1, $crawler->filter('div[class=entry]'));
+
+        $form = $crawler->filter('button[id=submit-filter]')->form();
+        $form['entry_filter[isArchived]']->untick();
+        $form['entry_filter[isStarred]']->tick();
+
+        $crawler = $client->submit($form);
+        $this->assertCount(1, $crawler->filter('div[class=entry]'));
+    }
 }
index 4a43e04928124d1718673936d1322019e9d5932c..af39d6ce11ea28386cc00bef3a121c2fbaaa49ca 100644 (file)
@@ -15,4 +15,54 @@ class TagControllerTest extends WallabagCoreTestCase
 
         $this->assertEquals(200, $client->getResponse()->getStatusCode());
     }
+
+    public function testAddTagToEntry()
+    {
+        $this->logInAs('admin');
+        $client = $this->getClient();
+
+        $entry = $client->getContainer()
+            ->get('doctrine.orm.entity_manager')
+            ->getRepository('WallabagCoreBundle:Entry')
+            ->findOneByIsArchived(false);
+
+        $crawler = $client->request('GET', '/view/'.$entry->getId());
+
+        $form = $crawler->filter('button[id=tag_save]')->form();
+
+        $data = array(
+            'tag[label]' => 'opensource',
+        );
+
+        $client->submit($form, $data);
+        $this->assertEquals(302, $client->getResponse()->getStatusCode());
+
+        $this->assertEquals(1, count($entry->getTags()));
+
+        # tag already exists and already assigned
+        $client->submit($form, $data);
+        $this->assertEquals(302, $client->getResponse()->getStatusCode());
+
+        $newEntry = $client->getContainer()
+            ->get('doctrine.orm.entity_manager')
+            ->getRepository('WallabagCoreBundle:Entry')
+            ->findOneById($entry->getId());
+
+        $this->assertEquals(1, count($newEntry->getTags()));
+
+        # tag already exists but still not assigned to this entry
+        $data = array(
+            'tag[label]' => 'foo',
+        );
+
+        $client->submit($form, $data);
+        $this->assertEquals(302, $client->getResponse()->getStatusCode());
+
+        $newEntry = $client->getContainer()
+            ->get('doctrine.orm.entity_manager')
+            ->getRepository('WallabagCoreBundle:Entry')
+            ->findOneById($entry->getId());
+
+        $this->assertEquals(2, count($newEntry->getTags()));
+    }
 }
index 7e2968e77f45153fc55178fd9bac190450a7bb5a..a16baca97e0a09e27e7c35e7ecafdfdf0131416e 100644 (file)
@@ -25,4 +25,17 @@ class Utils
         // remove character which can broken the url
         return str_replace(array('+', '/'), '', $token);
     }
+
+    /**
+     * For a given text, we calculate reading time for an article
+     * based on 200 words per minute.
+     *
+     * @param $text
+     *
+     * @return float
+     */
+    public static function getReadingTime($text)
+    {
+        return floor(str_word_count(strip_tags($text)) / 200);
+    }
 }