mirror of
https://github.com/wallabag/wallabag.git
synced 2025-04-25 19:34:07 +00:00
Merge pull request #1297 from wallabag/v2-estimated-time
store estimated reading time in database (#393)
This commit is contained in:
commit
930334cd6d
20 changed files with 433 additions and 279 deletions
|
@ -25,6 +25,7 @@ class AppKernel extends Kernel
|
||||||
new Wallabag\CoreBundle\WallabagCoreBundle(),
|
new Wallabag\CoreBundle\WallabagCoreBundle(),
|
||||||
new Wallabag\ApiBundle\WallabagApiBundle(),
|
new Wallabag\ApiBundle\WallabagApiBundle(),
|
||||||
new Bazinga\Bundle\HateoasBundle\BazingaHateoasBundle(),
|
new Bazinga\Bundle\HateoasBundle\BazingaHateoasBundle(),
|
||||||
|
new Lexik\Bundle\FormFilterBundle\LexikFormFilterBundle(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (in_array($this->getEnvironment(), array('dev', 'test'))) {
|
if (in_array($this->getEnvironment(), array('dev', 'test'))) {
|
||||||
|
|
|
@ -638,7 +638,7 @@ class SymfonyRequirements extends RequirementCollection
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->addRecommendation(
|
$this->addRecommendation(
|
||||||
class_exists('Locale'),
|
extension_loaded('intl'),
|
||||||
'intl extension should be available',
|
'intl extension should be available',
|
||||||
'Install and enable the <strong>intl</strong> extension (used for validators).'
|
'Install and enable the <strong>intl</strong> extension (used for validators).'
|
||||||
);
|
);
|
||||||
|
|
|
@ -42,9 +42,9 @@ foreach ($symfonyRequirements->getRecommendations() as $req) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($checkPassed) {
|
if ($checkPassed) {
|
||||||
echo_block('success', 'OK', 'Your system is ready to run Symfony2 projects', true);
|
echo_block('success', 'OK', 'Your system is ready to run Symfony2 projects');
|
||||||
} else {
|
} else {
|
||||||
echo_block('error', 'ERROR', 'Your system is not ready to run Symfony2 projects', true);
|
echo_block('error', 'ERROR', 'Your system is not ready to run Symfony2 projects');
|
||||||
|
|
||||||
echo_title('Fix the following mandatory requirements', 'red');
|
echo_title('Fix the following mandatory requirements', 'red');
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,9 @@ twig:
|
||||||
version: %app.version%
|
version: %app.version%
|
||||||
paypal_url: "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9UBA65LG3FX9Y&lc=gb"
|
paypal_url: "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9UBA65LG3FX9Y&lc=gb"
|
||||||
flattr_url: "https://flattr.com/thing/1265480"
|
flattr_url: "https://flattr.com/thing/1265480"
|
||||||
|
form:
|
||||||
|
resources:
|
||||||
|
- LexikFormFilterBundle:Form:form_div_layout.html.twig
|
||||||
# Assetic Configuration
|
# Assetic Configuration
|
||||||
assetic:
|
assetic:
|
||||||
debug: "%kernel.debug%"
|
debug: "%kernel.debug%"
|
||||||
|
|
|
@ -83,7 +83,8 @@
|
||||||
"wallabag/php-readability": "dev-master",
|
"wallabag/php-readability": "dev-master",
|
||||||
"wallabag/phpMobi": "dev-master",
|
"wallabag/phpMobi": "dev-master",
|
||||||
"wallabag/Fivefilters_Libraries": "dev-master",
|
"wallabag/Fivefilters_Libraries": "dev-master",
|
||||||
"pagerfanta/pagerfanta": "~1.0.3"
|
"pagerfanta/pagerfanta": "~1.0.3",
|
||||||
|
"lexik/form-filter-bundle": "~4.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"doctrine/doctrine-fixtures-bundle": "~2.2.0",
|
"doctrine/doctrine-fixtures-bundle": "~2.2.0",
|
||||||
|
|
427
composer.lock
generated
427
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -9,6 +9,9 @@ use Wallabag\CoreBundle\Entity\Entry;
|
||||||
use Wallabag\CoreBundle\Service\Extractor;
|
use Wallabag\CoreBundle\Service\Extractor;
|
||||||
use Wallabag\CoreBundle\Form\Type\NewEntryType;
|
use Wallabag\CoreBundle\Form\Type\NewEntryType;
|
||||||
use Wallabag\CoreBundle\Form\Type\EditEntryType;
|
use Wallabag\CoreBundle\Form\Type\EditEntryType;
|
||||||
|
use Wallabag\CoreBundle\Filter\EntryFilterType;
|
||||||
|
use Pagerfanta\Adapter\DoctrineORMAdapter;
|
||||||
|
use Pagerfanta\Pagerfanta;
|
||||||
|
|
||||||
class EntryController extends Controller
|
class EntryController extends Controller
|
||||||
{
|
{
|
||||||
|
@ -89,22 +92,39 @@ class EntryController extends Controller
|
||||||
/**
|
/**
|
||||||
* Shows unread entries for current user.
|
* Shows unread entries for current user.
|
||||||
*
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @param int $page
|
||||||
|
*
|
||||||
* @Route("/unread/list/{page}", name="unread", defaults={"page" = "1"})
|
* @Route("/unread/list/{page}", name="unread", defaults={"page" = "1"})
|
||||||
*
|
*
|
||||||
* @return \Symfony\Component\HttpFoundation\Response
|
* @return \Symfony\Component\HttpFoundation\Response
|
||||||
*/
|
*/
|
||||||
public function showUnreadAction($page)
|
public function showUnreadAction(Request $request, $page)
|
||||||
{
|
{
|
||||||
$entries = $this->getDoctrine()
|
$form = $this->get('form.factory')->create(new EntryFilterType());
|
||||||
|
|
||||||
|
$filterBuilder = $this->getDoctrine()
|
||||||
->getRepository('WallabagCoreBundle:Entry')
|
->getRepository('WallabagCoreBundle:Entry')
|
||||||
->findUnreadByUser($this->getUser()->getId());
|
->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->setMaxPerPage($this->getUser()->getConfig()->getItemsPerPage());
|
||||||
$entries->setCurrentPage($page);
|
$entries->setCurrentPage($page);
|
||||||
|
|
||||||
return $this->render(
|
return $this->render(
|
||||||
'WallabagCoreBundle:Entry:entries.html.twig',
|
'WallabagCoreBundle:Entry:entries.html.twig',
|
||||||
array(
|
array(
|
||||||
|
'form' => $form->createView(),
|
||||||
'entries' => $entries,
|
'entries' => $entries,
|
||||||
'currentPage' => $page
|
'currentPage' => $page
|
||||||
)
|
)
|
||||||
|
@ -114,22 +134,39 @@ class EntryController extends Controller
|
||||||
/**
|
/**
|
||||||
* Shows read entries for current user.
|
* Shows read entries for current user.
|
||||||
*
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @param int $page
|
||||||
|
*
|
||||||
* @Route("/archive/list/{page}", name="archive", defaults={"page" = "1"})
|
* @Route("/archive/list/{page}", name="archive", defaults={"page" = "1"})
|
||||||
*
|
*
|
||||||
* @return \Symfony\Component\HttpFoundation\Response
|
* @return \Symfony\Component\HttpFoundation\Response
|
||||||
*/
|
*/
|
||||||
public function showArchiveAction($page)
|
public function showArchiveAction(Request $request, $page)
|
||||||
{
|
{
|
||||||
$entries = $this->getDoctrine()
|
$form = $this->get('form.factory')->create(new EntryFilterType());
|
||||||
|
|
||||||
|
$filterBuilder = $this->getDoctrine()
|
||||||
->getRepository('WallabagCoreBundle:Entry')
|
->getRepository('WallabagCoreBundle:Entry')
|
||||||
->findArchiveByUser($this->getUser()->getId());
|
->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->setMaxPerPage($this->getUser()->getConfig()->getItemsPerPage());
|
||||||
$entries->setCurrentPage($page);
|
$entries->setCurrentPage($page);
|
||||||
|
|
||||||
return $this->render(
|
return $this->render(
|
||||||
'WallabagCoreBundle:Entry:entries.html.twig',
|
'WallabagCoreBundle:Entry:entries.html.twig',
|
||||||
array(
|
array(
|
||||||
|
'form' => $form->createView(),
|
||||||
'entries' => $entries,
|
'entries' => $entries,
|
||||||
'currentPage' => $page
|
'currentPage' => $page
|
||||||
)
|
)
|
||||||
|
@ -139,22 +176,39 @@ class EntryController extends Controller
|
||||||
/**
|
/**
|
||||||
* Shows starred entries for current user.
|
* Shows starred entries for current user.
|
||||||
*
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @param int $page
|
||||||
|
*
|
||||||
* @Route("/starred/list/{page}", name="starred", defaults={"page" = "1"})
|
* @Route("/starred/list/{page}", name="starred", defaults={"page" = "1"})
|
||||||
*
|
*
|
||||||
* @return \Symfony\Component\HttpFoundation\Response
|
* @return \Symfony\Component\HttpFoundation\Response
|
||||||
*/
|
*/
|
||||||
public function showStarredAction($page)
|
public function showStarredAction(Request $request, $page)
|
||||||
{
|
{
|
||||||
$entries = $this->getDoctrine()
|
$form = $this->get('form.factory')->create(new EntryFilterType());
|
||||||
|
|
||||||
|
$filterBuilder = $this->getDoctrine()
|
||||||
->getRepository('WallabagCoreBundle:Entry')
|
->getRepository('WallabagCoreBundle:Entry')
|
||||||
->findStarredByUser($this->getUser()->getId());
|
->findStarredByUser($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->setMaxPerPage($this->getUser()->getConfig()->getItemsPerPage());
|
||||||
$entries->setCurrentPage($page);
|
$entries->setCurrentPage($page);
|
||||||
|
|
||||||
return $this->render(
|
return $this->render(
|
||||||
'WallabagCoreBundle:Entry:entries.html.twig',
|
'WallabagCoreBundle:Entry:entries.html.twig',
|
||||||
array(
|
array(
|
||||||
|
'form' => $form->createView(),
|
||||||
'entries' => $entries,
|
'entries' => $entries,
|
||||||
'currentPage' => $page
|
'currentPage' => $page
|
||||||
)
|
)
|
||||||
|
|
|
@ -7,6 +7,8 @@ use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
||||||
use Wallabag\CoreBundle\Entity\User;
|
use Wallabag\CoreBundle\Entity\User;
|
||||||
use Wallabag\CoreBundle\Entity\Entry;
|
use Wallabag\CoreBundle\Entity\Entry;
|
||||||
|
use Pagerfanta\Adapter\DoctrineORMAdapter;
|
||||||
|
use Pagerfanta\Pagerfanta;
|
||||||
|
|
||||||
class RssController extends Controller
|
class RssController extends Controller
|
||||||
{
|
{
|
||||||
|
@ -20,12 +22,15 @@ class RssController extends Controller
|
||||||
*/
|
*/
|
||||||
public function showUnreadAction(User $user)
|
public function showUnreadAction(User $user)
|
||||||
{
|
{
|
||||||
$entries = $this->getDoctrine()
|
$qb = $this->getDoctrine()
|
||||||
->getRepository('WallabagCoreBundle:Entry')
|
->getRepository('WallabagCoreBundle:Entry')
|
||||||
->findUnreadByUser(
|
->findUnreadByUser(
|
||||||
$user->getId()
|
$user->getId()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$pagerAdapter = new DoctrineORMAdapter($qb->getQuery());
|
||||||
|
$entries = new Pagerfanta($pagerAdapter);
|
||||||
|
|
||||||
$perPage = $user->getConfig()->getRssLimit() ?: $this->container->getParameter('rss_limit');
|
$perPage = $user->getConfig()->getRssLimit() ?: $this->container->getParameter('rss_limit');
|
||||||
$entries->setMaxPerPage($perPage);
|
$entries->setMaxPerPage($perPage);
|
||||||
|
|
||||||
|
@ -45,12 +50,15 @@ class RssController extends Controller
|
||||||
*/
|
*/
|
||||||
public function showArchiveAction(User $user)
|
public function showArchiveAction(User $user)
|
||||||
{
|
{
|
||||||
$entries = $this->getDoctrine()
|
$qb = $this->getDoctrine()
|
||||||
->getRepository('WallabagCoreBundle:Entry')
|
->getRepository('WallabagCoreBundle:Entry')
|
||||||
->findArchiveByUser(
|
->findArchiveByUser(
|
||||||
$user->getId()
|
$user->getId()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$pagerAdapter = new DoctrineORMAdapter($qb->getQuery());
|
||||||
|
$entries = new Pagerfanta($pagerAdapter);
|
||||||
|
|
||||||
$perPage = $user->getConfig()->getRssLimit() ?: $this->container->getParameter('rss_limit');
|
$perPage = $user->getConfig()->getRssLimit() ?: $this->container->getParameter('rss_limit');
|
||||||
$entries->setMaxPerPage($perPage);
|
$entries->setMaxPerPage($perPage);
|
||||||
|
|
||||||
|
@ -70,12 +78,15 @@ class RssController extends Controller
|
||||||
*/
|
*/
|
||||||
public function showStarredAction(User $user)
|
public function showStarredAction(User $user)
|
||||||
{
|
{
|
||||||
$entries = $this->getDoctrine()
|
$qb = $this->getDoctrine()
|
||||||
->getRepository('WallabagCoreBundle:Entry')
|
->getRepository('WallabagCoreBundle:Entry')
|
||||||
->findStarredByUser(
|
->findStarredByUser(
|
||||||
$user->getId()
|
$user->getId()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$pagerAdapter = new DoctrineORMAdapter($qb->getQuery());
|
||||||
|
$entries = new Pagerfanta($pagerAdapter);
|
||||||
|
|
||||||
$perPage = $user->getConfig()->getRssLimit() ?: $this->container->getParameter('rss_limit');
|
$perPage = $user->getConfig()->getRssLimit() ?: $this->container->getParameter('rss_limit');
|
||||||
$entries->setMaxPerPage($perPage);
|
$entries->setMaxPerPage($perPage);
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ use Doctrine\ORM\Mapping as ORM;
|
||||||
use Symfony\Component\Validator\Constraints as Assert;
|
use Symfony\Component\Validator\Constraints as Assert;
|
||||||
use Hateoas\Configuration\Annotation as Hateoas;
|
use Hateoas\Configuration\Annotation as Hateoas;
|
||||||
use JMS\Serializer\Annotation\XmlRoot;
|
use JMS\Serializer\Annotation\XmlRoot;
|
||||||
|
use Wallabag\CoreBundle\Helper\Tools;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entry.
|
* Entry.
|
||||||
|
@ -96,7 +97,7 @@ class Entry
|
||||||
/**
|
/**
|
||||||
* @var int
|
* @var int
|
||||||
*
|
*
|
||||||
* @ORM\Column(name="reading_type", type="integer", nullable=true)
|
* @ORM\Column(name="reading_time", type="integer", nullable=true)
|
||||||
*/
|
*/
|
||||||
private $readingTime;
|
private $readingTime;
|
||||||
|
|
||||||
|
@ -264,6 +265,7 @@ class Entry
|
||||||
public function setContent($content)
|
public function setContent($content)
|
||||||
{
|
{
|
||||||
$this->content = $content;
|
$this->content = $content;
|
||||||
|
$this->readingTime = Tools::getReadingTime($content);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
28
src/Wallabag/CoreBundle/Filter/EntryFilterType.php
Normal file
28
src/Wallabag/CoreBundle/Filter/EntryFilterType.php
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Wallabag\CoreBundle\Filter;
|
||||||
|
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
|
class EntryFilterType extends AbstractType
|
||||||
|
{
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
|
{
|
||||||
|
$builder->add('readingTime', 'filter_number_range');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
return 'entry_filter';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function configureOptions(OptionsResolver $resolver)
|
||||||
|
{
|
||||||
|
$resolver->setDefaults(array(
|
||||||
|
'csrf_protection' => false,
|
||||||
|
'validation_groups' => array('filtering')
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
|
@ -118,4 +118,16 @@ final class Tools
|
||||||
|
|
||||||
return str_replace('+', '', $token);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,20 +13,15 @@ class EntryRepository extends EntityRepository
|
||||||
*
|
*
|
||||||
* @param int $userId
|
* @param int $userId
|
||||||
*
|
*
|
||||||
* @return Pagerfanta
|
* @return QueryBuilder
|
||||||
*/
|
*/
|
||||||
public function findUnreadByUser($userId)
|
public function findUnreadByUser($userId)
|
||||||
{
|
{
|
||||||
$qb = $this->createQueryBuilder('e')
|
return $this->createQueryBuilder('e')
|
||||||
->leftJoin('e.user', 'u')
|
->leftJoin('e.user', 'u')
|
||||||
->where('e.isArchived = false')
|
->where('e.isArchived = false')
|
||||||
->andWhere('u.id =:userId')->setParameter('userId', $userId)
|
->andWhere('u.id =:userId')->setParameter('userId', $userId)
|
||||||
->orderBy('e.id', 'desc')
|
->orderBy('e.id', 'desc');
|
||||||
->getQuery();
|
|
||||||
|
|
||||||
$pagerAdapter = new DoctrineORMAdapter($qb);
|
|
||||||
|
|
||||||
return new Pagerfanta($pagerAdapter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,21 +29,15 @@ class EntryRepository extends EntityRepository
|
||||||
*
|
*
|
||||||
* @param int $userId
|
* @param int $userId
|
||||||
*
|
*
|
||||||
* @return Pagerfanta
|
* @return QueryBuilder
|
||||||
*/
|
*/
|
||||||
public function findArchiveByUser($userId)
|
public function findArchiveByUser($userId)
|
||||||
{
|
{
|
||||||
$qb = $this->createQueryBuilder('e')
|
return $this->createQueryBuilder('e')
|
||||||
->select('e')
|
|
||||||
->leftJoin('e.user', 'u')
|
->leftJoin('e.user', 'u')
|
||||||
->where('e.isArchived = true')
|
->where('e.isArchived = true')
|
||||||
->andWhere('u.id =:userId')->setParameter('userId', $userId)
|
->andWhere('u.id =:userId')->setParameter('userId', $userId)
|
||||||
->orderBy('e.id', 'desc')
|
->orderBy('e.id', 'desc');
|
||||||
->getQuery();
|
|
||||||
|
|
||||||
$pagerAdapter = new DoctrineORMAdapter($qb);
|
|
||||||
|
|
||||||
return new Pagerfanta($pagerAdapter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -56,22 +45,15 @@ class EntryRepository extends EntityRepository
|
||||||
*
|
*
|
||||||
* @param int $userId
|
* @param int $userId
|
||||||
*
|
*
|
||||||
* @return Pagerfanta
|
* @return QueryBuilder
|
||||||
*/
|
*/
|
||||||
public function findStarredByUser($userId)
|
public function findStarredByUser($userId)
|
||||||
{
|
{
|
||||||
|
return $this->createQueryBuilder('e')
|
||||||
$qb = $this->createQueryBuilder('e')
|
|
||||||
->select('e')
|
|
||||||
->leftJoin('e.user', 'u')
|
->leftJoin('e.user', 'u')
|
||||||
->where('e.isStarred = true')
|
->where('e.isStarred = true')
|
||||||
->andWhere('u.id =:userId')->setParameter('userId', $userId)
|
->andWhere('u.id =:userId')->setParameter('userId', $userId)
|
||||||
->orderBy('e.id', 'desc')
|
->orderBy('e.id', 'desc');
|
||||||
->getQuery();
|
|
||||||
|
|
||||||
$pagerAdapter = new DoctrineORMAdapter($qb);
|
|
||||||
|
|
||||||
return new Pagerfanta($pagerAdapter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -21,11 +21,12 @@
|
||||||
{% if entries is empty %}
|
{% if entries is empty %}
|
||||||
<div class="messages warning"><p>{% trans %}No articles found.{% endtrans %}</p></div>
|
<div class="messages warning"><p>{% trans %}No articles found.{% endtrans %}</p></div>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
<div><form>{{ 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 %}
|
{% for entry in entries %}
|
||||||
<div id="entry-{{ entry.id|e }}" class="entrie">
|
<div id="entry-{{ entry.id|e }}" class="entry">
|
||||||
<h2><a href="{{ path('view', { 'id': entry.id }) }}">{{ entry.title|raw }}</a></h2>
|
<h2><a href="{{ path('view', { 'id': entry.id }) }}">{{ entry.title|raw }}</a></h2>
|
||||||
{% if entry.content| readingTime > 0 %}
|
{% if entry.readingTime > 0 %}
|
||||||
<div class="estimatedTime"><span class="tool reading-time">{% trans %}estimated reading time :{% endtrans %} {{ entry.content| readingTime }} min</span></div>
|
<div class="estimatedTime"><span class="tool reading-time">{% trans %}estimated reading time :{% endtrans %} {{ entry.readingTime }} min</span></div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="estimatedTime"><span class="tool reading-time">{% trans %}estimated reading time :{% endtrans %} <small class="inferieur"><</small> 1 min</span></div>
|
<div class="estimatedTime"><span class="tool reading-time">{% trans %}estimated reading time :{% endtrans %} <small class="inferieur"><</small> 1 min</span></div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -17,8 +17,8 @@
|
||||||
<pubDate>{{ entry.createdAt|date('D, d M Y H:i:s') }}</pubDate>
|
<pubDate>{{ entry.createdAt|date('D, d M Y H:i:s') }}</pubDate>
|
||||||
<description>
|
<description>
|
||||||
<![CDATA[
|
<![CDATA[
|
||||||
{%- if entry.content|readingTime > 0 -%}
|
{%- if entry.readingTime > 0 -%}
|
||||||
{% trans %}estimated reading time :{% endtrans %} {{ entry.content|readingTime }} min
|
{% trans %}estimated reading time :{% endtrans %} {{ entry.readingTime }} min
|
||||||
{%- else -%}
|
{%- else -%}
|
||||||
{% trans %}estimated reading time :{% endtrans %} < 1 min
|
{% trans %}estimated reading time :{% endtrans %} < 1 min
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|
|
@ -121,7 +121,7 @@ a:visited {
|
||||||
font-size: 1.3em;
|
font-size: 1.3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#main #content .entrie {
|
#main #content .entry {
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
padding-bottom: 15px;
|
padding-bottom: 15px;
|
||||||
border-bottom: 1px dashed #222;
|
border-bottom: 1px dashed #222;
|
||||||
|
@ -129,19 +129,19 @@ a:visited {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* First entry */
|
/* First entry */
|
||||||
#main #content .results + .entrie {
|
#main #content .results + .entry {
|
||||||
clear: both;
|
clear: both;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#main .entrie .tools {
|
#main .entry .tools {
|
||||||
float: right;
|
float: right;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
#main .entrie .tools .tool span {
|
#main .entry .tools .tool span {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
|
|
|
@ -322,7 +322,7 @@ footer a {
|
||||||
letter-spacing:-5px;
|
letter-spacing:-5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.listmode .entrie {
|
.listmode .entry {
|
||||||
width: 100%!important;
|
width: 100%!important;
|
||||||
margin-left: 0!important;
|
margin-left: 0!important;
|
||||||
}
|
}
|
||||||
|
@ -343,7 +343,7 @@ footer a {
|
||||||
top: -1px;
|
top: -1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entrie {
|
.entry {
|
||||||
background-color: #FFF;
|
background-color: #FFF;
|
||||||
letter-spacing:normal;
|
letter-spacing:normal;
|
||||||
box-shadow: 0 3px 7px rgba(0,0,0,0.3);
|
box-shadow: 0 3px 7px rgba(0,0,0,0.3);
|
||||||
|
@ -366,7 +366,7 @@ footer a {
|
||||||
/* transition: all 0.5s ease; */
|
/* transition: all 0.5s ease; */
|
||||||
}
|
}
|
||||||
|
|
||||||
.entrie:before {
|
.entry:before {
|
||||||
content: "";
|
content: "";
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
|
@ -384,7 +384,7 @@ footer a {
|
||||||
transition: all 0.5s ease;
|
transition: all 0.5s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entrie:after {
|
.entry:after {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: 7px;
|
height: 7px;
|
||||||
|
@ -399,34 +399,34 @@ footer a {
|
||||||
transition: all 0.5s ease;
|
transition: all 0.5s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entrie:hover {
|
.entry:hover {
|
||||||
box-shadow: 0 3px 10px rgba(0,0,0,1);
|
box-shadow: 0 3px 10px rgba(0,0,0,1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.entrie:hover:after {
|
.entry:hover:after {
|
||||||
height: 40px;
|
height: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entrie:hover:before {
|
.entry:hover:before {
|
||||||
bottom: 2.4em;
|
bottom: 2.4em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entrie:hover h2 a {
|
.entry:hover h2 a {
|
||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entrie h2 {
|
.entry h2 {
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entrie h2:after {
|
.entry h2:after {
|
||||||
content: none;
|
content: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.entrie h2 a {
|
.entry h2 a {
|
||||||
display: block;
|
display: block;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: #000;
|
color: #000;
|
||||||
|
@ -438,7 +438,7 @@ footer a {
|
||||||
transition: all 0.5s ease;
|
transition: all 0.5s ease;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
.entrie h2 a:after {
|
.entry h2 a:after {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
@ -448,21 +448,21 @@ footer a {
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.entrie p {
|
.entry p {
|
||||||
color: #666;
|
color: #666;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
line-height: 1.7;
|
line-height: 1.7;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entrie h2 a:first-letter {
|
.entry h2 a:first-letter {
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entrie:hover .tools {
|
.entry:hover .tools {
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entrie .tools {
|
.entry .tools {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: -50px;
|
bottom: -50px;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
@ -477,22 +477,22 @@ footer a {
|
||||||
transition: all 0.5s ease;
|
transition: all 0.5s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entrie .tools a {
|
.entry .tools a {
|
||||||
color: #666;
|
color: #666;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
display: block;
|
display: block;
|
||||||
padding: 0.4em;
|
padding: 0.4em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entrie .tools a:hover {
|
.entry .tools a:hover {
|
||||||
color: #FFF;
|
color: #FFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entrie .tools li {
|
.entry .tools li {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entrie:nth-child(3n+1) {
|
.entry:nth-child(3n+1) {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -941,13 +941,13 @@ pre code {
|
||||||
========================================================================== */
|
========================================================================== */
|
||||||
|
|
||||||
@media screen and (max-width: 1050px) {
|
@media screen and (max-width: 1050px) {
|
||||||
.entrie {
|
.entry {
|
||||||
width: 49%;
|
width: 49%;
|
||||||
}
|
}
|
||||||
.entrie:nth-child(3n+1) {
|
.entry:nth-child(3n+1) {
|
||||||
margin-left: 1.5%;
|
margin-left: 1.5%;
|
||||||
}
|
}
|
||||||
.entrie:nth-child(2n+1) {
|
.entry:nth-child(2n+1) {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -962,7 +962,7 @@ pre code {
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 700px) {
|
@media screen and (max-width: 700px) {
|
||||||
.entrie {
|
.entry {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
|
@ -972,7 +972,7 @@ pre code {
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 500px) {
|
@media screen and (max-width: 500px) {
|
||||||
.entrie {
|
.entry {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
{% if entries is not empty %}
|
{% if entries is not empty %}
|
||||||
<div class="results clearfix">
|
<div class="results clearfix">
|
||||||
<div class="nb-results left">{{ entries.count }} {% trans %}entries{% endtrans %}</div>
|
<div class="nb-results left">{{ entries.count }} {% trans %}entries{% endtrans %}</div>
|
||||||
|
<div class="left"><form>{{ form_rest(form) }}<button class="btn waves-effect waves-light" type="submit" id="submit-filter" value="filter">Filter</button></form></div>
|
||||||
<ul class="pagination right">
|
<ul class="pagination right">
|
||||||
{% for p in range(1, entries.nbPages) %}
|
{% for p in range(1, entries.nbPages) %}
|
||||||
<li class="{{ currentPage == p ? 'active':'waves-effect'}}">
|
<li class="{{ currentPage == p ? 'active':'waves-effect'}}">
|
||||||
|
@ -38,8 +39,8 @@
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<span class="card-title"><a href="{{ path('view', { 'id': entry.id }) }}">{{ entry.title|raw }}</a></span>
|
<span class="card-title"><a href="{{ path('view', { 'id': entry.id }) }}">{{ entry.title|raw }}</a></span>
|
||||||
{% if entry.content| readingTime > 0 %}
|
{% if entry.readingTime > 0 %}
|
||||||
<div class="estimatedTime grey-text"><span class="tool reading-time">{% trans %}estimated reading time: {% endtrans %} {{ entry.content| readingTime }} min</span></div>
|
<div class="estimatedTime grey-text"><span class="tool reading-time">{% trans %}estimated reading time: {% endtrans %} {{ entry.readingTime }} min</span></div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="estimatedTime grey-text"><span class="tool reading-time">{% trans %}estimated reading time: {% endtrans %} <small class="inferieur"><</small> 1 min</span></div>
|
<div class="estimatedTime grey-text"><span class="tool reading-time">{% trans %}estimated reading time: {% endtrans %} <small class="inferieur"><</small> 1 min</span></div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -25,7 +25,7 @@ body > footer,
|
||||||
div.tools,
|
div.tools,
|
||||||
header div,
|
header div,
|
||||||
.messages,
|
.messages,
|
||||||
.entrie + .results {
|
.entry + .results {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -240,4 +240,23 @@ class EntryControllerTest extends WallabagCoreTestCase
|
||||||
|
|
||||||
$this->assertEquals(403, $client->getResponse()->getStatusCode());
|
$this->assertEquals(403, $client->getResponse()->getStatusCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testFilterOnUnreadeView()
|
||||||
|
{
|
||||||
|
$this->logInAs('admin');
|
||||||
|
$client = $this->getClient();
|
||||||
|
|
||||||
|
$crawler = $client->request('GET', '/unread/list');
|
||||||
|
|
||||||
|
$form = $crawler->filter('button[id=submit-filter]')->form();
|
||||||
|
|
||||||
|
$data = array(
|
||||||
|
'entry_filter[readingTime][right_number]' => 11,
|
||||||
|
'entry_filter[readingTime][left_number]' => 11
|
||||||
|
);
|
||||||
|
|
||||||
|
$crawler = $client->submit($form, $data);
|
||||||
|
|
||||||
|
$this->assertCount(1, $crawler->filter('div[class=entry]'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ class WallabagExtension extends \Twig_Extension
|
||||||
public function getFilters()
|
public function getFilters()
|
||||||
{
|
{
|
||||||
return array(
|
return array(
|
||||||
new \Twig_SimpleFilter('readingTime', array($this, 'getReadingTime')),
|
|
||||||
new \Twig_SimpleFilter('domainName', array($this, 'getDomainName')),
|
new \Twig_SimpleFilter('domainName', array($this, 'getDomainName')),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -24,18 +23,6 @@ class WallabagExtension extends \Twig_Extension
|
||||||
return parse_url($url, PHP_URL_HOST);
|
return parse_url($url, PHP_URL_HOST);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getName()
|
public function getName()
|
||||||
{
|
{
|
||||||
return 'wallabag_extension';
|
return 'wallabag_extension';
|
||||||
|
|
Loading…
Reference in a new issue