diff --git a/.github/workflows/assets.yml b/.github/workflows/assets.yml index 71fffdebb..286b67039 100644 --- a/.github/workflows/assets.yml +++ b/.github/workflows/assets.yml @@ -5,7 +5,7 @@ on: push: branches: - master - - 2.* + - "2.**" permissions: contents: read diff --git a/.github/workflows/coding-standards.yml b/.github/workflows/coding-standards.yml index 0d8bcb251..fe7e65765 100644 --- a/.github/workflows/coding-standards.yml +++ b/.github/workflows/coding-standards.yml @@ -5,7 +5,7 @@ on: push: branches: - master - - 2.* + - "2.**" permissions: contents: read diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index a1f124dad..a5b77d0d8 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -5,7 +5,7 @@ on: push: branches: - master - - 2.* + - "2.**" env: PGPASSWORD: wallabagrocks diff --git a/.github/workflows/translations.yml b/.github/workflows/translations.yml index 05405edc2..8b1658d31 100644 --- a/.github/workflows/translations.yml +++ b/.github/workflows/translations.yml @@ -5,7 +5,7 @@ on: push: branches: - master - - 2.* + - "2.**" permissions: contents: read diff --git a/CHANGELOG.md b/CHANGELOG.md index 75e96b07d..baa9ef0af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,32 @@ # Changelog +## [2.6.6](https://github.com/wallabag/wallabag/tree/2.6.6) +[Full Changelog](https://github.com/wallabag/wallabag/compare/2.6.5...2.6.6) + +### Security fix +* Force secure cookie on HTTPS connection by @j0k3r in https://github.com/wallabag/wallabag/pull/6924 + +### Fixes +* Fix checkboxes pointer events issue by @Simounet in https://github.com/wallabag/wallabag/pull/6897 +* Add Google mailer by @j0k3r in https://github.com/wallabag/wallabag/pull/6899 +* Improve performance on homepage by @Simounet in https://github.com/wallabag/wallabag/pull/6909 +* Mass action layout improved by @Simounet in https://github.com/wallabag/wallabag/pull/6912 + +## [2.6.5](https://github.com/wallabag/wallabag/tree/2.6.5) +[Full Changelog](https://github.com/wallabag/wallabag/compare/2.6.4...2.6.5) + +### Fixes +* "Fix checkboxes pointer-events disabled" by @Simounet https://github.com/wallabag/wallabag/pull/6874 +* "Fix nav input styles" by @Simounet https://github.com/wallabag/wallabag/pull/6877 +* "Change domain status filters html types" by @Simounet https://github.com/wallabag/wallabag/pull/6888 + +## [2.6.4](https://github.com/wallabag/wallabag/tree/2.6.4) +[Full Changelog](https://github.com/wallabag/wallabag/compare/2.6.3...2.6.4) + +### Fixes +* Fix API token generation by @nicosomb https://github.com/wallabag/wallabag/pull/6869 +* Fix checkboxes which were broken by @nicosomb https://github.com/wallabag/wallabag/pull/6864 + ## [2.6.3](https://github.com/wallabag/wallabag/tree/2.6.3) [Full Changelog](https://github.com/wallabag/wallabag/compare/2.6.2...2.6.3) diff --git a/app/Resources/static/themes/material/css/entries.scss b/app/Resources/static/themes/material/css/entries.scss index cc22552d2..b79a201da 100644 --- a/app/Resources/static/themes/material/css/entries.scss +++ b/app/Resources/static/themes/material/css/entries.scss @@ -14,25 +14,30 @@ } .mass-action { - margin: 10px 5px 10px 20px; + margin: 20px 5px 10px 20px; } .mass-action-group { display: flex; padding: 3px; - gap: 10px; + align-items: center; + gap: 30px; } .mass-action-button { - height: 24px; - line-height: 24px; - padding: 0 0.5rem; + height: 36px; + line-height: 36px; + padding: 0 0.7rem; i { font-size: 1rem; } } +.mass-action-button--tags { + border-radius: 2px 0 0 2px; +} + .entry-checkbox { margin: 10px 15px 10px 5px; @@ -64,11 +69,19 @@ .mass-action-tags { display: flex; - align-items: center; - gap: 10px; + margin-top: 10px; - .mass-action-tags-input { + .mass-action-tags-input.mass-action-tags-input { margin: 0; + padding: 0 5px; + height: 34px; + background: white; + border-bottom: 3px solid #c5ebef; + } + + .mass-action-tags-input.mass-action-tags-input.mass-action-tags-input:focus { + border-bottom: 3px solid $blue-accent-color; + box-shadow: none; } } @@ -88,13 +101,16 @@ .results { display: flex; + margin-bottom: 10px; padding: 1rem 1rem 0; flex-wrap: wrap; justify-content: space-between; +} - .nb-results { - display: inline-flex; - } +.nb-results { + display: inline-flex; + margin-bottom: 20px; + gap: 30px; } .results-item { @@ -173,9 +189,38 @@ footer { } @media screen and (min-width: 993px) { + .results { + margin-bottom: 0; + } + + .nb-results { + margin-bottom: 0; + gap: 0; + } + + .mass-action-button { + height: 24px; + line-height: 24px; + padding: 0 0.5rem; + } + + .mass-action-group { + gap: 10px; + } + + .mass-action-tags { + margin-top: 0; + margin-left: 7px; + flex-wrap: initial; + } + .mass-action { display: flex; + margin-top: 10px; align-items: center; - gap: 30px; + + .mass-action-tags-input.mass-action-tags-input { + height: 21px; + } } } diff --git a/app/Resources/static/themes/material/css/nav.scss b/app/Resources/static/themes/material/css/nav.scss index 1630489ac..a085febd9 100644 --- a/app/Resources/static/themes/material/css/nav.scss +++ b/app/Resources/static/themes/material/css/nav.scss @@ -70,6 +70,7 @@ nav { .input-field input { display: block; + font-size: 1.2rem; line-height: inherit; height: 3rem; } @@ -79,6 +80,17 @@ nav { box-shadow: none; color: #444; } + + /* materializecss override */ + .input-field.input-field input { + margin-bottom: 0; + border-bottom: none; + } + + .input-field.input-field input:focus { + border-bottom: none; + box-shadow: initial; + } } .nav-panel-top { diff --git a/app/Resources/static/themes/material/css/various.scss b/app/Resources/static/themes/material/css/various.scss index ba6570f21..ad0703afa 100644 --- a/app/Resources/static/themes/material/css/various.scss +++ b/app/Resources/static/themes/material/css/various.scss @@ -15,6 +15,13 @@ div.settings div.file-field { } } +/* override materializecss pointer-event disabled on checkboxes */ +[type="checkbox"]:not(:checked), +[type="checkbox"]:checked, +.input-field label { + pointer-events: initial; +} + .input-field label.active { font-size: 1rem; } diff --git a/app/config/config.yml b/app/config/config.yml index a40400cc9..7f0a4ca6c 100644 --- a/app/config/config.yml +++ b/app/config/config.yml @@ -29,6 +29,7 @@ framework: # handler_id set to null will use default session handler from php.ini handler_id: session.handler.native_file save_path: "%kernel.project_dir%/var/sessions/%kernel.environment%" + cookie_secure: auto fragments: ~ http_method_override: true assets: ~ @@ -84,13 +85,8 @@ doctrine_migrations: executed_at_column_name: 'executed_at' fos_rest: - zone: - - { path: ^/api } - - { path: ^/annotations } param_fetcher_listener: true body_listener: true - exception: - serializer_error_renderer: true view: mime_types: csv: @@ -116,6 +112,9 @@ fos_rest: - { path: "^/api/entries/([0-9]+)/export.(.*)", priorities: ['epub', 'pdf', 'txt', 'csv'], fallback_format: json, prefer_extension: false } - { path: "^/api", priorities: ['json', 'xml'], fallback_format: json, prefer_extension: false } - { path: "^/annotations", priorities: ['json', 'xml'], fallback_format: json, prefer_extension: false } + # for an unknown reason, EACH REQUEST goes to FOS\RestBundle\EventListener\FormatListener + # so we need to add custom rule for custom api export but also for all other routes of the application... + - { path: '^/', priorities: ['text/html', '*/*'], fallback_format: html, prefer_extension: false } nelmio_api_doc: areas: diff --git a/app/config/wallabag.yml b/app/config/wallabag.yml index 047bda7b0..bd57d6377 100644 --- a/app/config/wallabag.yml +++ b/app/config/wallabag.yml @@ -1,5 +1,5 @@ wallabag_core: - version: 2.6.3 + version: 2.6.6 paypal_url: "https://liberapay.com/wallabag/donate" languages: en: 'English' diff --git a/composer.json b/composer.json index f1801b47b..508e0f874 100644 --- a/composer.json +++ b/composer.json @@ -130,6 +130,7 @@ "symfony/finder": "^4.4", "symfony/form": "^4.4", "symfony/framework-bundle": "^4.4", + "symfony/google-mailer": "^4.4", "symfony/http-foundation": "^4.4", "symfony/http-kernel": "^4.4", "symfony/mailer": "^4.4", @@ -209,7 +210,11 @@ "incenteev-parameters": { "file": "app/config/parameters.yml" }, - "public-dir": "web" + "public-dir": "web", + "symfony": { + "allow-contrib": true, + "require": "4.4.*" + } }, "scripts": { "post-install-cmd": [ diff --git a/src/Wallabag/CoreBundle/Controller/EntryController.php b/src/Wallabag/CoreBundle/Controller/EntryController.php index 0084e08bd..c97ee1f49 100644 --- a/src/Wallabag/CoreBundle/Controller/EntryController.php +++ b/src/Wallabag/CoreBundle/Controller/EntryController.php @@ -675,9 +675,6 @@ class EntryController extends AbstractController } } - $nbEntriesUntagged = $this->entryRepository - ->countUntaggedEntriesByUser($this->getUser()->getId()); - return $this->render( '@WallabagCore/Entry/entries.html.twig', [ 'form' => $form->createView(), @@ -685,7 +682,6 @@ class EntryController extends AbstractController 'currentPage' => $page, 'searchTerm' => $searchTerm, 'isFiltered' => $form->isSubmitted(), - 'nbEntriesUntagged' => $nbEntriesUntagged, ] ); } diff --git a/src/Wallabag/CoreBundle/Form/Type/EntryFilterType.php b/src/Wallabag/CoreBundle/Form/Type/EntryFilterType.php index 2b335db50..0f444fff2 100644 --- a/src/Wallabag/CoreBundle/Form/Type/EntryFilterType.php +++ b/src/Wallabag/CoreBundle/Form/Type/EntryFilterType.php @@ -6,6 +6,7 @@ use Lexik\Bundle\FormFilterBundle\Filter\FilterOperands; use Lexik\Bundle\FormFilterBundle\Filter\Form\Type\CheckboxFilterType; use Lexik\Bundle\FormFilterBundle\Filter\Form\Type\ChoiceFilterType; use Lexik\Bundle\FormFilterBundle\Filter\Form\Type\DateRangeFilterType; +use Lexik\Bundle\FormFilterBundle\Filter\Form\Type\NumberFilterType; use Lexik\Bundle\FormFilterBundle\Filter\Form\Type\NumberRangeFilterType; use Lexik\Bundle\FormFilterBundle\Filter\Form\Type\TextFilterType; use Lexik\Bundle\FormFilterBundle\Filter\Query\QueryInterface; @@ -102,10 +103,13 @@ class EntryFilterType extends AbstractType return $filterQuery->createCondition($expression); }, 'label' => 'entry.filters.domain_label', + 'attr' => [ + 'autocapitalize' => 'off', + ], ]) - ->add('httpStatus', TextFilterType::class, [ + ->add('httpStatus', NumberFilterType::class, [ 'apply_filter' => function (QueryInterface $filterQuery, $field, $values) { - $value = $values['value']; + $value = (int) $values['value']; if (false === \array_key_exists($value, Response::$statusTexts)) { return false; } @@ -117,6 +121,11 @@ class EntryFilterType extends AbstractType return $filterQuery->createCondition($expression, $parameters); }, 'label' => 'entry.filters.http_status_label', + 'html5' => true, + 'attr' => [ + 'min' => 100, + 'max' => 527, + ], ]) ->add('isArchived', CheckboxFilterType::class, [ 'label' => 'entry.filters.archived_label', diff --git a/src/Wallabag/CoreBundle/Repository/EntryRepository.php b/src/Wallabag/CoreBundle/Repository/EntryRepository.php index 3e1c343b1..f30999484 100644 --- a/src/Wallabag/CoreBundle/Repository/EntryRepository.php +++ b/src/Wallabag/CoreBundle/Repository/EntryRepository.php @@ -37,6 +37,20 @@ class EntryRepository extends ServiceEntityRepository ; } + /** + * Retrieves all entries count for a user. + * + * @param int $userId + * + * @return QueryBuilder + */ + public function getCountBuilderForAllByUser($userId) + { + return $this + ->getQueryBuilderByUser($userId) + ; + } + /** * Retrieves unread entries for a user. * @@ -52,6 +66,21 @@ class EntryRepository extends ServiceEntityRepository ; } + /** + * Retrieves unread entries count for a user. + * + * @param int $userId + * + * @return QueryBuilder + */ + public function getCountBuilderForUnreadByUser($userId) + { + return $this + ->getQueryBuilderByUser($userId) + ->andWhere('e.isArchived = false') + ; + } + /** * Retrieves entries with the same domain. * @@ -94,6 +123,21 @@ class EntryRepository extends ServiceEntityRepository ; } + /** + * Retrieves read entries count for a user. + * + * @param int $userId + * + * @return QueryBuilder + */ + public function getCountBuilderForArchiveByUser($userId) + { + return $this + ->getQueryBuilderByUser($userId) + ->andWhere('e.isArchived = true') + ; + } + /** * Retrieves starred entries for a user. * @@ -109,6 +153,21 @@ class EntryRepository extends ServiceEntityRepository ; } + /** + * Retrieves starred entries count for a user. + * + * @param int $userId + * + * @return QueryBuilder + */ + public function getCountBuilderForStarredByUser($userId) + { + return $this + ->getQueryBuilderByUser($userId) + ->andWhere('e.isStarred = true') + ; + } + /** * Retrieves entries filtered with a search term for a user. * @@ -169,6 +228,21 @@ class EntryRepository extends ServiceEntityRepository ; } + /** + * Retrieve entries with annotations count for a user. + * + * @param int $userId + * + * @return QueryBuilder + */ + public function getCountBuilderForAnnotationsByUser($userId) + { + return $this + ->getQueryBuilderByUser($userId) + ->innerJoin('e.annotations', 'a') + ; + } + /** * Retrieve untagged entries for a user. * @@ -588,6 +662,23 @@ class EntryRepository extends ServiceEntityRepository return $qb->getQuery()->getArrayResult(); } + /** + * @param int $userId + * + * @return array + */ + public function findEmptyEntriesIdByUserId($userId = null) + { + $qb = $this->createQueryBuilder('e') + ->select('e.id'); + + if (null !== $userId) { + $qb->where('e.user = :userid AND e.content IS NULL')->setParameter(':userid', $userId); + } + + return $qb->getQuery()->getArrayResult(); + } + /** * Find all entries by url and owner. * diff --git a/src/Wallabag/CoreBundle/Resources/views/Entry/entries.html.twig b/src/Wallabag/CoreBundle/Resources/views/Entry/entries.html.twig index 55b58180b..d744f87cf 100644 --- a/src/Wallabag/CoreBundle/Resources/views/Entry/entries.html.twig +++ b/src/Wallabag/CoreBundle/Resources/views/Entry/entries.html.twig @@ -53,13 +53,11 @@ - -
+ -
@@ -117,9 +115,9 @@

{{ 'entry.filters.title'|trans }}

- {% if current_route != 'untagged' and nbEntriesUntagged != 0 %} + {% if current_route != 'untagged' %}
- {{ 'tag.list.see_untagged_entries'|trans }} ({{ nbEntriesUntagged }}) + {{ 'tag.list.see_untagged_entries'|trans }}
{% endif %} diff --git a/src/Wallabag/CoreBundle/Resources/views/layout.html.twig b/src/Wallabag/CoreBundle/Resources/views/layout.html.twig index f1565f17b..10d92133e 100644 --- a/src/Wallabag/CoreBundle/Resources/views/layout.html.twig +++ b/src/Wallabag/CoreBundle/Resources/views/layout.html.twig @@ -168,7 +168,7 @@
-
diff --git a/src/Wallabag/CoreBundle/Twig/WallabagExtension.php b/src/Wallabag/CoreBundle/Twig/WallabagExtension.php index 676f0fce0..e7a52680c 100644 --- a/src/Wallabag/CoreBundle/Twig/WallabagExtension.php +++ b/src/Wallabag/CoreBundle/Twig/WallabagExtension.php @@ -96,35 +96,32 @@ class WallabagExtension extends AbstractExtension implements GlobalsInterface switch ($type) { case 'starred': - $qb = $this->entryRepository->getBuilderForStarredByUser($user->getId()); + $qb = $this->entryRepository->getCountBuilderForStarredByUser($user->getId()); break; case 'archive': - $qb = $this->entryRepository->getBuilderForArchiveByUser($user->getId()); + $qb = $this->entryRepository->getCountBuilderForArchiveByUser($user->getId()); break; case 'unread': - $qb = $this->entryRepository->getBuilderForUnreadByUser($user->getId()); + $qb = $this->entryRepository->getCountBuilderForUnreadByUser($user->getId()); break; case 'annotated': - $qb = $this->entryRepository->getBuilderForAnnotationsByUser($user->getId()); + $qb = $this->entryRepository->getCountBuilderForAnnotationsByUser($user->getId()); break; case 'all': - $qb = $this->entryRepository->getBuilderForAllByUser($user->getId()); + $qb = $this->entryRepository->getCountBuilderForAllByUser($user->getId()); break; default: throw new \InvalidArgumentException(sprintf('Type "%s" is not implemented.', $type)); } - // THANKS to PostgreSQL we CAN'T make a DEAD SIMPLE count(e.id) - // ERROR: column "e0_.id" must appear in the GROUP BY clause or be used in an aggregate function $query = $qb - ->select('e.id') - ->groupBy('e.id') + ->select('COUNT(e.id)') ->getQuery(); $query->useQueryCache(true); $query->enableResultCache($this->lifeTime); - return \count($query->getArrayResult()); + return $query->getSingleScalarResult(); } /** @@ -156,15 +153,14 @@ class WallabagExtension extends AbstractExtension implements GlobalsInterface return ''; } - $query = $this->entryRepository->getBuilderForArchiveByUser($user->getId()) - ->select('e.id') - ->groupBy('e.id') + $query = $this->entryRepository->getCountBuilderForArchiveByUser($user->getId()) + ->select('COUNT(e.id)') ->getQuery(); $query->useQueryCache(true); $query->enableResultCache($this->lifeTime); - $nbArchives = \count($query->getArrayResult()); + $nbArchives = $query->getSingleScalarResult(); $interval = $user->getCreatedAt()->diff(new \DateTime('now')); $nbDays = (int) $interval->format('%a') ?: 1;