Merge pull request #3526 from wallabag/add-random-article

Add random feature
This commit is contained in:
Kevin Decherf 2019-01-22 18:30:02 +01:00 committed by GitHub
commit 3527c30021
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 214 additions and 67 deletions

View file

@ -8,7 +8,7 @@ import 'materialize-css/dist/js/materialize';
import '../_global/index';
/* Tools */
import { initExport, initFilters } from './js/tools';
import { initExport, initFilters, initRandom } from './js/tools';
/* Import shortcuts */
import './js/shortcuts/main';
@ -32,8 +32,10 @@ $(document).ready(() => {
format: 'dd/mm/yyyy',
container: 'body',
});
initFilters();
initExport();
initRandom();
const toggleNav = (toShow, toFocus) => {
$('.nav-panel-actions').hide(100);

View file

@ -8,6 +8,7 @@ function initFilters() {
$('#clear_form_filters').on('click', () => {
$('#filters input').val('');
$('#filters :checked').removeAttr('checked');
return false;
});
}
@ -21,4 +22,15 @@ function initExport() {
}
}
export { initExport, initFilters };
function initRandom() {
// no display if export (ie: entries) not available
if ($('div').is('#export')) {
$('#button_random').show();
}
}
export {
initExport,
initFilters,
initRandom,
};

View file

@ -2,6 +2,7 @@
namespace Wallabag\CoreBundle\Controller;
use Doctrine\ORM\NoResultException;
use Pagerfanta\Adapter\DoctrineORMAdapter;
use Pagerfanta\Exception\OutOfRangeCurrentPageException;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
@ -232,6 +233,46 @@ class EntryController extends Controller
return $this->showEntries('starred', $request, $page);
}
/**
* Shows untagged articles for current user.
*
* @param Request $request
* @param int $page
*
* @Route("/untagged/list/{page}", name="untagged", defaults={"page" = "1"})
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function showUntaggedEntriesAction(Request $request, $page)
{
return $this->showEntries('untagged', $request, $page);
}
/**
* Shows random entry depending on the given type.
*
* @param string $type
*
* @Route("/{type}/random", name="random_entry", requirements={"type": "unread|starred|archive|untagged|all"})
*
* @return \Symfony\Component\HttpFoundation\RedirectResponse
*/
public function redirectRandomEntryAction($type = 'all')
{
try {
$entry = $this->get('wallabag_core.entry_repository')
->getRandomEntry($this->getUser()->getId(), $type);
} catch (NoResultException $e) {
$bag = $this->get('session')->getFlashBag();
$bag->clear();
$bag->add('notice', 'flashes.entry.notice.no_random_entry');
return $this->redirect($this->generateUrl($type));
}
return $this->redirect($this->generateUrl('view', ['id' => $entry->getId()]));
}
/**
* Shows entry content.
*
@ -465,54 +506,6 @@ class EntryController extends Controller
);
}
/**
* Shows untagged articles for current user.
*
* @param Request $request
* @param int $page
*
* @Route("/untagged/list/{page}", name="untagged", defaults={"page" = "1"})
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function showUntaggedEntriesAction(Request $request, $page)
{
return $this->showEntries('untagged', $request, $page);
}
/**
* Fetch content and update entry.
* In case it fails, $entry->getContent will return an error message.
*
* @param Entry $entry
* @param string $prefixMessage Should be the translation key: entry_saved or entry_reloaded
*/
private function updateEntry(Entry $entry, $prefixMessage = 'entry_saved')
{
$message = 'flashes.entry.notice.' . $prefixMessage;
try {
$this->get('wallabag_core.content_proxy')->updateEntry($entry, $entry->getUrl());
} catch (\Exception $e) {
$this->get('logger')->error('Error while saving an entry', [
'exception' => $e,
'entry' => $entry,
]);
$message = 'flashes.entry.notice.' . $prefixMessage . '_failed';
}
if (empty($entry->getDomainName())) {
$this->get('wallabag_core.content_proxy')->setEntryDomainName($entry);
}
if (empty($entry->getTitle())) {
$this->get('wallabag_core.content_proxy')->setDefaultEntryTitle($entry);
}
$this->get('session')->getFlashBag()->add('notice', $message);
}
/**
* Global method to retrieve entries depending on the given type
* It returns the response to be send.
@ -585,6 +578,39 @@ class EntryController extends Controller
);
}
/**
* Fetch content and update entry.
* In case it fails, $entry->getContent will return an error message.
*
* @param Entry $entry
* @param string $prefixMessage Should be the translation key: entry_saved or entry_reloaded
*/
private function updateEntry(Entry $entry, $prefixMessage = 'entry_saved')
{
$message = 'flashes.entry.notice.' . $prefixMessage;
try {
$this->get('wallabag_core.content_proxy')->updateEntry($entry, $entry->getUrl());
} catch (\Exception $e) {
$this->get('logger')->error('Error while saving an entry', [
'exception' => $e,
'entry' => $entry,
]);
$message = 'flashes.entry.notice.' . $prefixMessage . '_failed';
}
if (empty($entry->getDomainName())) {
$this->get('wallabag_core.content_proxy')->setEntryDomainName($entry);
}
if (empty($entry->getTitle())) {
$this->get('wallabag_core.content_proxy')->setDefaultEntryTitle($entry);
}
$this->get('session')->getFlashBag()->add('notice', $message);
}
/**
* Check if the logged user can manage the given entry.
*

View file

@ -3,6 +3,7 @@
namespace Wallabag\CoreBundle\Repository;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\NoResultException;
use Doctrine\ORM\QueryBuilder;
use Pagerfanta\Adapter\DoctrineORMAdapter;
use Pagerfanta\Pagerfanta;
@ -110,8 +111,7 @@ class EntryRepository extends EntityRepository
*/
public function getBuilderForUntaggedByUser($userId)
{
return $this
->sortQueryBuilder($this->getRawBuilderForUntaggedByUser($userId));
return $this->sortQueryBuilder($this->getRawBuilderForUntaggedByUser($userId));
}
/**
@ -326,8 +326,8 @@ class EntryRepository extends EntityRepository
* Find an entry by its url and its owner.
* If it exists, return the entry otherwise return false.
*
* @param $url
* @param $userId
* @param string $url
* @param int $userId
*
* @return Entry|bool
*/
@ -418,8 +418,8 @@ class EntryRepository extends EntityRepository
/**
* Find all entries by url and owner.
*
* @param $url
* @param $userId
* @param string $url
* @param int $userId
*
* @return array
*/
@ -432,6 +432,49 @@ class EntryRepository extends EntityRepository
->getResult();
}
/**
* Returns a random entry, filtering by status.
*
* @param int $userId
* @param string $type Can be unread, archive, starred, etc
*
* @throws NoResultException
*
* @return Entry
*/
public function getRandomEntry($userId, $type = '')
{
$qb = $this->getQueryBuilderByUser($userId)
->select('e.id');
switch ($type) {
case 'unread':
$qb->andWhere('e.isArchived = false');
break;
case 'archive':
$qb->andWhere('e.isArchived = true');
break;
case 'starred':
$qb->andWhere('e.isStarred = true');
break;
case 'untagged':
$qb->leftJoin('e.tags', 't');
$qb->andWhere('t.id is null');
break;
}
$ids = $qb->getQuery()->getArrayResult();
if (empty($ids)) {
throw new NoResultException();
}
// random select one in the list
$randomId = $ids[mt_rand(0, \count($ids) - 1)]['id'];
return $this->find($randomId);
}
/**
* Return a query builder to be used by other getBuilderFor* method.
*
@ -470,7 +513,6 @@ class EntryRepository extends EntityRepository
*/
private function sortQueryBuilder(QueryBuilder $qb, $sortBy = 'createdAt', $direction = 'desc')
{
return $qb
->orderBy(sprintf('e.%s', $sortBy), $direction);
return $qb->orderBy(sprintf('e.%s', $sortBy), $direction);
}
}

View file

@ -37,6 +37,7 @@ menu:
add_new_entry: 'Tilføj ny artikel'
search: 'Søg'
filter_entries: 'Filtrer artikler'
# random_entry: Jump to a random entry from that list
# export: 'Export'
search_form:
input_label: 'Indtast søgning'
@ -590,6 +591,7 @@ flashes:
entry_starred: 'Artikel markeret som favorit'
entry_unstarred: 'Artikel ikke længere markeret som favorit'
entry_deleted: 'Artikel slettet'
# no_random_entry: 'No article with these criterias was found'
tag:
notice:
# tag_added: 'Tag added'

View file

@ -37,6 +37,7 @@ menu:
add_new_entry: 'Neuen Artikel hinzufügen'
search: 'Suche'
filter_entries: 'Artikel filtern'
# random_entry: Jump to a random entry from that list
export: 'Exportieren'
search_form:
input_label: 'Suchbegriff hier eingeben'
@ -590,6 +591,7 @@ flashes:
entry_starred: 'Eintrag favorisiert'
entry_unstarred: 'Eintrag defavorisiert'
entry_deleted: 'Eintrag gelöscht'
# no_random_entry: 'No article with these criterias was found'
tag:
notice:
tag_added: 'Tag hinzugefügt'

View file

@ -37,6 +37,7 @@ menu:
add_new_entry: 'Add a new entry'
search: 'Search'
filter_entries: 'Filter entries'
random_entry: Jump to a random entry from that list
export: 'Export'
search_form:
input_label: 'Enter your search here'
@ -590,6 +591,7 @@ flashes:
entry_starred: 'Entry starred'
entry_unstarred: 'Entry unstarred'
entry_deleted: 'Entry deleted'
no_random_entry: 'No article with these criterias was found'
tag:
notice:
tag_added: 'Tag added'

View file

@ -37,6 +37,7 @@ menu:
add_new_entry: 'Añadir un nuevo artículo'
search: 'Buscar'
filter_entries: 'Filtrar los artículos'
# random_entry: Jump to a random entry from that list
export: 'Exportar'
search_form:
input_label: 'Introduzca su búsqueda aquí'
@ -590,6 +591,7 @@ flashes:
entry_starred: 'Artículo marcado como favorito'
entry_unstarred: 'Artículo desmarcado como favorito'
entry_deleted: 'Artículo eliminado'
# no_random_entry: 'No article with these criterias was found'
tag:
notice:
tag_added: 'Etiqueta añadida'

View file

@ -37,6 +37,7 @@ menu:
add_new_entry: 'افزودن مقالهٔ تازه'
search: 'جستجو'
filter_entries: 'فیلترکردن مقاله‌ها'
# random_entry: Jump to a random entry from that list
export: 'برون‌بری'
search_form:
input_label: 'جستجوی خود را این‌جا بنویسید:'
@ -590,6 +591,7 @@ flashes:
entry_starred: 'مقاله برگزیده شد'
entry_unstarred: 'مقاله نابرگزیده شد'
entry_deleted: 'مقاله پاک شد'
# no_random_entry: 'No article with these criterias was found'
tag:
notice:
tag_added: 'برچسب افزوده شد'

View file

@ -37,6 +37,7 @@ menu:
add_new_entry: "Sauvegarder un nouvel article"
search: "Rechercher"
filter_entries: "Filtrer les articles"
random_entry: Aller à un article aléatoire de cette liste
export: "Exporter"
search_form:
input_label: "Saisissez votre terme de recherche"
@ -590,6 +591,7 @@ flashes:
entry_starred: "Article ajouté dans les favoris"
entry_unstarred: "Article retiré des favoris"
entry_deleted: "Article supprimé"
no_random_entry: "Aucun article correspond aux critères n'a été trouvé"
tag:
notice:
tag_added: "Tag ajouté"

View file

@ -37,6 +37,7 @@ menu:
add_new_entry: 'Aggiungi un nuovo contenuto'
search: 'Cerca'
filter_entries: 'Filtra contenuti'
# random_entry: Jump to a random entry from that list
export: 'Esporta'
search_form:
input_label: 'Inserisci qui la tua ricerca'
@ -590,6 +591,7 @@ flashes:
entry_starred: 'Contenuto segnato come preferito'
entry_unstarred: 'Contenuto rimosso dai preferiti'
entry_deleted: 'Contenuto eliminato'
# no_random_entry: 'No article with these criterias was found'
tag:
notice:
tag_added: 'Etichetta aggiunta'

View file

@ -37,6 +37,7 @@ menu:
add_new_entry: 'Enregistrar un novèl article'
search: 'Cercar'
filter_entries: 'Filtrar los articles'
# random_entry: Jump to a random entry from that list
export: 'Exportar'
search_form:
input_label: 'Picatz vòstre mot-clau a cercar aquí'
@ -590,6 +591,7 @@ flashes:
entry_starred: 'Article ajustat dins los favorits'
entry_unstarred: 'Article quitat dels favorits'
entry_deleted: 'Article suprimit'
# no_random_entry: 'No article with these criterias was found'
tag:
notice:
tag_added: 'Etiqueta ajustada'

View file

@ -37,6 +37,7 @@ menu:
add_new_entry: 'Dodaj nowy wpis'
search: 'Szukaj'
filter_entries: 'Filtruj wpisy'
# random_entry: Jump to a random entry from that list
export: 'Eksportuj'
search_form:
input_label: 'Wpisz swoje zapytanie tutaj'
@ -590,6 +591,7 @@ flashes:
entry_starred: 'Wpis oznaczony gwiazdką'
entry_unstarred: 'Wpis odznaczony gwiazdką'
entry_deleted: 'Wpis usunięty'
# no_random_entry: 'No article with these criterias was found'
tag:
notice:
tag_added: 'Tag dodany'

View file

@ -37,6 +37,7 @@ menu:
add_new_entry: 'Adicionar uma nova entrada'
search: 'Pesquisa'
filter_entries: 'Filtrar entradas'
# random_entry: Jump to a random entry from that list
export: 'Exportar'
search_form:
input_label: 'Digite aqui sua pesquisa'
@ -590,6 +591,7 @@ flashes:
entry_starred: 'Entrada destacada'
entry_unstarred: 'Entrada não destacada'
entry_deleted: 'Entrada apagada'
# no_random_entry: 'No article with these criterias was found'
tag:
notice:
tag_added: 'Tag adicionada'

View file

@ -37,6 +37,7 @@ menu:
add_new_entry: 'Introdu un nou articol'
search: 'Căutare'
filter_entries: 'Filtrează articolele'
# random_entry: Jump to a random entry from that list
# export: 'Export'
search_form:
input_label: 'Introdu căutarea ta'
@ -590,6 +591,7 @@ flashes:
entry_starred: 'Articol adăugat la favorite'
entry_unstarred: 'Articol șters de la favorite'
entry_deleted: 'Articol șters'
# no_random_entry: 'No article with these criterias was found'
tag:
notice:
# tag_added: 'Tag added'

View file

@ -36,6 +36,7 @@ menu:
add_new_entry: 'Добавить новую запись'
search: 'Поиск'
filter_entries: 'Фильтр записей'
# random_entry: Jump to a random entry from that list
export: 'Экспорт'
search_form:
input_label: 'Введите текст для поиска'
@ -555,6 +556,7 @@ flashes:
entry_starred: 'Запись помечена звездочкой'
entry_unstarred: 'Пометка звездочкой у записи убрана'
entry_deleted: 'Запись удалена'
# no_random_entry: 'No article with these criterias was found'
tag:
notice:
tag_added: 'Тег добавлен'

View file

@ -37,6 +37,7 @@ menu:
add_new_entry: 'เพิ่มรายการใหม่'
search: 'ค้นหา'
filter_entries: 'ตัวกรองรายการ'
# random_entry: Jump to a random entry from that list
export: 'นำข้อมูลออก'
search_form:
input_label: 'ค้นหาที่นี้'
@ -588,6 +589,7 @@ flashes:
entry_starred: 'รายการที่แสดง'
entry_unstarred: 'รายการที่ไม่ได้แสดง'
entry_deleted: 'รายการที่ถูกลบ'
# no_random_entry: 'No article with these criterias was found'
tag:
notice:
tag_added: 'แท็กที่เพิ่ม'

View file

@ -37,6 +37,7 @@ menu:
add_new_entry: 'Yeni bir makale ekle'
search: 'Ara'
filter_entries: 'Filtrele'
# random_entry: Jump to a random entry from that list
export: 'Dışa Aktar'
search_form:
input_label: 'Aramak istediğiniz herhangi bir şey yazın'
@ -568,6 +569,7 @@ flashes:
entry_starred: 'Makale favorilere eklendi'
entry_unstarred: 'Makale favorilerden çıkartıldı'
entry_deleted: 'Makale silindi'
# no_random_entry: 'No article with these criterias was found'
tag:
notice:
tag_added: 'Etiket eklendi'

View file

@ -20,6 +20,9 @@
{% block content %}
{% set currentRoute = app.request.attributes.get('_route') %}
{% if currentRoute == 'homepage' %}
{% set currentRoute = 'unread' %}
{% endif %}
{% set listMode = app.user.config.listMode %}
<div class="results">
<div class="nb-results">{{ 'entry.list.number_on_the_page'|transchoice(entries.count) }}</div>
@ -28,6 +31,9 @@
{% if app.user.config.rssToken %}
{% include "@WallabagCore/themes/common/Entry/_rss_link.html.twig" %}
{% endif %}
{% if currentRoute in ['unread', 'starred', 'archive', 'untagged', 'all'] %}
<a href="{{ path('random_entry', { 'type': currentRoute }) }}"><i class="btn-clickable material-icons md-24 js-random-action">casino</i></a>
{% endif %}
<i class="btn-clickable download-btn material-icons md-24 js-export-action">file_download</i>
<i class="btn-clickable filter-btn material-icons md-24 js-filters-action">filter_list</i>
{% if entries.getNbPages > 1 %}
@ -89,9 +95,6 @@
{% set currentTag = null %}
{% if tag is defined %}
{% set currentTag = tag %}
{% endif %}
{% if currentRoute == 'homepage' %}
{% set currentRoute = 'unread' %}
{% endif %}
<h2>{{ 'entry.list.export_title'|trans }}</h2>
<a href="javascript: void(null);" id="download-form-close" class="close-button--popup close-button">&times;</a>

View file

@ -21,6 +21,9 @@
{% block content %}
{% set listMode = app.user.config.listMode %}
{% set currentRoute = app.request.attributes.get('_route') %}
{% if currentRoute == 'homepage' %}
{% set currentRoute = 'unread' %}
{% endif %}
<div class="results">
<div class="nb-results">
{{ 'entry.list.number_on_the_page'|transchoice(entries.count) }}
@ -59,9 +62,6 @@
{% set currentTag = null %}
{% if tag is defined %}
{% set currentTag = tag.slug %}
{% endif %}
{% if currentRoute == 'homepage' %}
{% set currentRoute = 'unread' %}
{% endif %}
<h4 class="center">{{ 'entry.list.export_title'|trans }}</h4>
<ul>

View file

@ -46,6 +46,8 @@
{% set activeRoute = 'starred' %}
{% elseif currentRoute == 'unread' or currentRoute == 'homepage' or currentRouteFromQueryParams == 'unread' %}
{% set activeRoute = 'unread' %}
{% elseif currentRoute == 'untagged' %}
{% set activeRoute = 'untagged' %}
{% endif %}
<li class="bold {% if activeRoute == 'unread' %}active{% endif %}">
@ -113,6 +115,13 @@
<i class="material-icons">search</i>
</a>
</li>
{% if activeRoute %}
<li id="button_random">
<a class="waves-effect tooltipped js-random-action" data-position="bottom" data-delay="50" data-tooltip="{{ 'menu.top.random_entry'|trans }}" href="{{ path('random_entry', { 'type': activeRoute }) }}">
<i class="material-icons">casino</i>
</a>
</li>
{% endif %}
<li id="button_filters">
<a class="nav-panel-menu button-collapse-right tooltipped js-filters-action" data-position="bottom" data-delay="50" data-tooltip="{{ 'menu.top.filter_entries'|trans }}" href="#" data-activates="filters">
<i class="material-icons">filter_list</i>
@ -125,7 +134,7 @@
</li>
</ul>
</div>
{{ render(controller("WallabagCoreBundle:Entry:searchForm", {'currentRoute': app.request.attributes.get('_route')})) }}
{{ render(controller("WallabagCoreBundle:Entry:searchForm", {'currentRoute': currentRoute})) }}
{{ render(controller("WallabagCoreBundle:Entry:addEntryForm")) }}
</div>
</nav>

View file

@ -79,7 +79,6 @@ class InstapaperImport extends AbstractImport
$entries[] = [
'url' => $data[0],
'title' => $data[1],
'status' => $data[3],
'is_archived' => 'Archive' === $data[3] || 'Starred' === $data[3],
'is_starred' => 'Starred' === $data[3],
'html' => false,

View file

@ -1495,4 +1495,30 @@ class EntryControllerTest extends WallabagCoreTestCase
$this->assertSame(sprintf('/remove-tag/%s/%s', $entry->getId(), $tag->getId()), $link);
}
public function testRandom()
{
$this->logInAs('admin');
$client = $this->getClient();
$client->request('GET', '/unread/random');
$this->assertSame(302, $client->getResponse()->getStatusCode());
$this->assertContains('/view/', $client->getResponse()->getTargetUrl(), 'Unread random');
$client->request('GET', '/starred/random');
$this->assertSame(302, $client->getResponse()->getStatusCode());
$this->assertContains('/view/', $client->getResponse()->getTargetUrl(), 'Starred random');
$client->request('GET', '/archive/random');
$this->assertSame(302, $client->getResponse()->getStatusCode());
$this->assertContains('/view/', $client->getResponse()->getTargetUrl(), 'Archive random');
$client->request('GET', '/untagged/random');
$this->assertSame(302, $client->getResponse()->getStatusCode());
$this->assertContains('/view/', $client->getResponse()->getTargetUrl(), 'Untagged random');
$client->request('GET', '/all/random');
$this->assertSame(302, $client->getResponse()->getStatusCode());
$this->assertContains('/view/', $client->getResponse()->getTargetUrl(), 'All random');
}
}

File diff suppressed because one or more lines are too long