Compare commits

..

1 Commits

Author SHA1 Message Date
8f563c3073 Change read icon
Fix #8322
2025-07-02 16:19:29 +02:00
32 changed files with 450 additions and 950 deletions

2
.github/CODEOWNERS vendored
View File

@ -1,2 +0,0 @@
# Migrated rules from dependabot.yml
composer.* @Kdecherf @j0k3r @yguedidi

View File

@ -35,6 +35,10 @@ updates:
phpstan-dependencies:
patterns:
- "phpstan/*"
reviewers:
- j0k3r
- yguedidi
- Kdecherf
ignore:
- dependency-name: symfony/*
update-types: [ "version-update:semver-major" ]

View File

@ -17,7 +17,7 @@ jobs:
steps:
- name: "Checkout"
uses: "actions/checkout@v5"
uses: "actions/checkout@v4"
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"

View File

@ -41,7 +41,7 @@ jobs:
steps:
- name: "Checkout"
uses: "actions/checkout@v5"
uses: "actions/checkout@v4"
with:
fetch-depth: 2
@ -116,7 +116,7 @@ jobs:
steps:
- name: "Checkout"
uses: "actions/checkout@v5"
uses: "actions/checkout@v4"
with:
fetch-depth: 2
@ -187,7 +187,7 @@ jobs:
steps:
- name: "Checkout"
uses: "actions/checkout@v5"
uses: "actions/checkout@v4"
with:
fetch-depth: 2

View File

@ -22,7 +22,7 @@ jobs:
steps:
- name: "Checkout"
uses: "actions/checkout@v5"
uses: "actions/checkout@v4"
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"

View File

@ -16,7 +16,7 @@ jobs:
steps:
- name: "Checkout"
uses: "actions/checkout@v5"
uses: "actions/checkout@v4"
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"

View File

@ -1,6 +1,6 @@
# wallabag
[![CI](https://github.com/wallabag/wallabag/actions/workflows/continuous-integration.yml/badge.svg?branch=master)](https://github.com/wallabag/wallabag/actions/workflows/continuous-integration.yml?query=branch%3Amaster)
![CI](https://github.com/wallabag/wallabag/workflows/CI/badge.svg)
[![Matrix](https://matrix.to/img/matrix-badge.svg)](https://matrix.to/#/#wallabag:matrix.org)
[![Donation Status](https://img.shields.io/liberapay/goal/wallabag.svg?logo=liberapay)](https://liberapay.com/wallabag/donate)
[![Translation status](https://hosted.weblate.org/widgets/wallabag/-/svg-badge.svg)](https://hosted.weblate.org/engage/wallabag/?utm_source=widget)

View File

@ -70,7 +70,7 @@
"doctrine/orm": "^2.20.2",
"doctrine/persistence": "^3.4",
"egulias/email-validator": "^4.0.4",
"enshrined/svg-sanitize": "^0.22",
"enshrined/svg-sanitize": "^0.21",
"friendsofsymfony/jsrouting-bundle": "^3.5",
"friendsofsymfony/oauth-server-bundle": "dev-master#dc8ff343363cf794d30eb1a123610d186a43f162",
"friendsofsymfony/rest-bundle": "^3.8",

138
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "58a7696a7e938ab8a69c7faf2444cd09",
"content-hash": "8a12584ee6ea6887963779b321b4860e",
"packages": [
{
"name": "babdev/pagerfanta-bundle",
@ -1893,16 +1893,16 @@
},
{
"name": "enshrined/svg-sanitize",
"version": "0.22.0",
"version": "0.21.0",
"source": {
"type": "git",
"url": "https://github.com/darylldoyle/svg-sanitizer.git",
"reference": "0afa95ea74be155a7bcd6c6fb60c276c39984500"
"reference": "5e477468fac5c5ce933dce53af3e8e4e58dcccc9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/darylldoyle/svg-sanitizer/zipball/0afa95ea74be155a7bcd6c6fb60c276c39984500",
"reference": "0afa95ea74be155a7bcd6c6fb60c276c39984500",
"url": "https://api.github.com/repos/darylldoyle/svg-sanitizer/zipball/5e477468fac5c5ce933dce53af3e8e4e58dcccc9",
"reference": "5e477468fac5c5ce933dce53af3e8e4e58dcccc9",
"shasum": ""
},
"require": {
@ -1932,9 +1932,9 @@
"description": "An SVG sanitizer for PHP",
"support": {
"issues": "https://github.com/darylldoyle/svg-sanitizer/issues",
"source": "https://github.com/darylldoyle/svg-sanitizer/tree/0.22.0"
"source": "https://github.com/darylldoyle/svg-sanitizer/tree/0.21.0"
},
"time": "2025-08-12T10:13:48+00:00"
"time": "2025-01-13T09:32:25+00:00"
},
{
"name": "fossar/htmlawed",
@ -4146,16 +4146,16 @@
},
{
"name": "j0k3r/graby-site-config",
"version": "1.0.202",
"version": "1.0.200",
"source": {
"type": "git",
"url": "https://github.com/j0k3r/graby-site-config.git",
"reference": "870ff8f1f35cdbf87ff000b68c7bef5e712fd533"
"reference": "e9cbbc776a7efcc9c06c07ff360744f266bbfc09"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/j0k3r/graby-site-config/zipball/870ff8f1f35cdbf87ff000b68c7bef5e712fd533",
"reference": "870ff8f1f35cdbf87ff000b68c7bef5e712fd533",
"url": "https://api.github.com/repos/j0k3r/graby-site-config/zipball/e9cbbc776a7efcc9c06c07ff360744f266bbfc09",
"reference": "e9cbbc776a7efcc9c06c07ff360744f266bbfc09",
"shasum": ""
},
"require": {
@ -4184,9 +4184,9 @@
"description": "Graby site config files",
"support": {
"issues": "https://github.com/j0k3r/graby-site-config/issues",
"source": "https://github.com/j0k3r/graby-site-config/tree/1.0.202"
"source": "https://github.com/j0k3r/graby-site-config/tree/1.0.200"
},
"time": "2025-08-01T07:03:24+00:00"
"time": "2025-06-01T02:42:59+00:00"
},
{
"name": "j0k3r/httplug-ssrf-plugin",
@ -4405,16 +4405,16 @@
},
{
"name": "jean85/pretty-package-versions",
"version": "2.1.1",
"version": "2.1.0",
"source": {
"type": "git",
"url": "https://github.com/Jean85/pretty-package-versions.git",
"reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a"
"reference": "3c4e5f62ba8d7de1734312e4fff32f67a8daaf10"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/4d7aa5dab42e2a76d99559706022885de0e18e1a",
"reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a",
"url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/3c4e5f62ba8d7de1734312e4fff32f67a8daaf10",
"reference": "3c4e5f62ba8d7de1734312e4fff32f67a8daaf10",
"shasum": ""
},
"require": {
@ -4424,9 +4424,8 @@
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.2",
"jean85/composer-provided-replaced-stub-package": "^1.0",
"phpstan/phpstan": "^2.0",
"phpstan/phpstan": "^1.4",
"phpunit/phpunit": "^7.5|^8.5|^9.6",
"rector/rector": "^2.0",
"vimeo/psalm": "^4.3 || ^5.0"
},
"type": "library",
@ -4459,9 +4458,9 @@
],
"support": {
"issues": "https://github.com/Jean85/pretty-package-versions/issues",
"source": "https://github.com/Jean85/pretty-package-versions/tree/2.1.1"
"source": "https://github.com/Jean85/pretty-package-versions/tree/2.1.0"
},
"time": "2025-03-19T14:43:43+00:00"
"time": "2024-11-18T16:19:46+00:00"
},
{
"name": "jms/metadata",
@ -8050,16 +8049,16 @@
},
{
"name": "sentry/sentry",
"version": "4.14.1",
"version": "4.10.0",
"source": {
"type": "git",
"url": "https://github.com/getsentry/sentry-php.git",
"reference": "a28c4a6f5fda2bf730789a638501d7a737a64eda"
"reference": "2af937d47d8aadb8dab0b1d7b9557e495dd12856"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/getsentry/sentry-php/zipball/a28c4a6f5fda2bf730789a638501d7a737a64eda",
"reference": "a28c4a6f5fda2bf730789a638501d7a737a64eda",
"url": "https://api.github.com/repos/getsentry/sentry-php/zipball/2af937d47d8aadb8dab0b1d7b9557e495dd12856",
"reference": "2af937d47d8aadb8dab0b1d7b9557e495dd12856",
"shasum": ""
},
"require": {
@ -8123,7 +8122,7 @@
],
"support": {
"issues": "https://github.com/getsentry/sentry-php/issues",
"source": "https://github.com/getsentry/sentry-php/tree/4.14.1"
"source": "https://github.com/getsentry/sentry-php/tree/4.10.0"
},
"funding": [
{
@ -8135,27 +8134,27 @@
"type": "custom"
}
],
"time": "2025-06-23T15:25:52+00:00"
"time": "2024-11-06T07:44:19+00:00"
},
{
"name": "sentry/sentry-symfony",
"version": "5.3.0",
"version": "5.2.0",
"source": {
"type": "git",
"url": "https://github.com/getsentry/sentry-symfony.git",
"reference": "5081e7d842424ec09a96e3c79c5b48b89d1195b2"
"reference": "394576244d8ac03fd2f305b82d23a6fd7a083b45"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/getsentry/sentry-symfony/zipball/5081e7d842424ec09a96e3c79c5b48b89d1195b2",
"reference": "5081e7d842424ec09a96e3c79c5b48b89d1195b2",
"url": "https://api.github.com/repos/getsentry/sentry-symfony/zipball/394576244d8ac03fd2f305b82d23a6fd7a083b45",
"reference": "394576244d8ac03fd2f305b82d23a6fd7a083b45",
"shasum": ""
},
"require": {
"guzzlehttp/psr7": "^2.1.1",
"jean85/pretty-package-versions": "^1.5||^2.0",
"php": "^7.2||^8.0",
"sentry/sentry": "^4.14.1",
"sentry/sentry": "^4.10.0",
"symfony/cache-contracts": "^1.1||^2.4||^3.0",
"symfony/config": "^4.4.20||^5.0.11||^6.0||^7.0",
"symfony/console": "^4.4.20||^5.0.11||^6.0||^7.0",
@ -8225,7 +8224,7 @@
],
"support": {
"issues": "https://github.com/getsentry/sentry-symfony/issues",
"source": "https://github.com/getsentry/sentry-symfony/tree/5.3.0"
"source": "https://github.com/getsentry/sentry-symfony/tree/5.2.0"
},
"funding": [
{
@ -8237,7 +8236,7 @@
"type": "custom"
}
],
"time": "2025-07-07T14:14:08+00:00"
"time": "2025-03-03T07:47:12+00:00"
},
{
"name": "simplepie/simplepie",
@ -8816,16 +8815,16 @@
},
{
"name": "symfony/cache-contracts",
"version": "v3.6.0",
"version": "v3.5.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/cache-contracts.git",
"reference": "5d68a57d66910405e5c0b63d6f0af941e66fc868"
"reference": "15a4f8e5cd3bce9aeafc882b1acab39ec8de2c1b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/cache-contracts/zipball/5d68a57d66910405e5c0b63d6f0af941e66fc868",
"reference": "5d68a57d66910405e5c0b63d6f0af941e66fc868",
"url": "https://api.github.com/repos/symfony/cache-contracts/zipball/15a4f8e5cd3bce9aeafc882b1acab39ec8de2c1b",
"reference": "15a4f8e5cd3bce9aeafc882b1acab39ec8de2c1b",
"shasum": ""
},
"require": {
@ -8839,7 +8838,7 @@
"name": "symfony/contracts"
},
"branch-alias": {
"dev-main": "3.6-dev"
"dev-main": "3.5-dev"
}
},
"autoload": {
@ -8872,7 +8871,7 @@
"standards"
],
"support": {
"source": "https://github.com/symfony/cache-contracts/tree/v3.6.0"
"source": "https://github.com/symfony/cache-contracts/tree/v3.5.1"
},
"funding": [
{
@ -8888,7 +8887,7 @@
"type": "tidelift"
}
],
"time": "2025-03-13T15:25:07+00:00"
"time": "2024-09-25T14:20:29+00:00"
},
{
"name": "symfony/config",
@ -15847,59 +15846,58 @@
},
{
"name": "friendsofphp/php-cs-fixer",
"version": "v3.84.0",
"version": "v3.75.0",
"source": {
"type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "38dad0767bf2a9b516b976852200ae722fe984ca"
"reference": "399a128ff2fdaf4281e4e79b755693286cdf325c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/38dad0767bf2a9b516b976852200ae722fe984ca",
"reference": "38dad0767bf2a9b516b976852200ae722fe984ca",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/399a128ff2fdaf4281e4e79b755693286cdf325c",
"reference": "399a128ff2fdaf4281e4e79b755693286cdf325c",
"shasum": ""
},
"require": {
"clue/ndjson-react": "^1.0",
"composer/semver": "^3.4",
"composer/xdebug-handler": "^3.0.5",
"composer/xdebug-handler": "^3.0.3",
"ext-filter": "*",
"ext-hash": "*",
"ext-json": "*",
"ext-tokenizer": "*",
"fidry/cpu-core-counter": "^1.2",
"php": "^7.4 || ^8.0",
"react/child-process": "^0.6.6",
"react/child-process": "^0.6.5",
"react/event-loop": "^1.0",
"react/promise": "^2.11 || ^3.0",
"react/promise": "^2.0 || ^3.0",
"react/socket": "^1.0",
"react/stream": "^1.0",
"sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0",
"symfony/console": "^5.4.45 || ^6.4.13 || ^7.0",
"symfony/event-dispatcher": "^5.4.45 || ^6.4.13 || ^7.0",
"symfony/filesystem": "^5.4.45 || ^6.4.13 || ^7.0",
"symfony/finder": "^5.4.45 || ^6.4.17 || ^7.0",
"symfony/options-resolver": "^5.4.45 || ^6.4.16 || ^7.0",
"symfony/polyfill-mbstring": "^1.32",
"symfony/polyfill-php80": "^1.32",
"symfony/polyfill-php81": "^1.32",
"symfony/process": "^5.4.47 || ^6.4.20 || ^7.2",
"symfony/stopwatch": "^5.4.45 || ^6.4.19 || ^7.0"
"sebastian/diff": "^4.0 || ^5.1 || ^6.0 || ^7.0",
"symfony/console": "^5.4 || ^6.4 || ^7.0",
"symfony/event-dispatcher": "^5.4 || ^6.4 || ^7.0",
"symfony/filesystem": "^5.4 || ^6.4 || ^7.0",
"symfony/finder": "^5.4 || ^6.4 || ^7.0",
"symfony/options-resolver": "^5.4 || ^6.4 || ^7.0",
"symfony/polyfill-mbstring": "^1.31",
"symfony/polyfill-php80": "^1.31",
"symfony/polyfill-php81": "^1.31",
"symfony/process": "^5.4 || ^6.4 || ^7.2",
"symfony/stopwatch": "^5.4 || ^6.4 || ^7.0"
},
"require-dev": {
"facile-it/paraunit": "^1.3.1 || ^2.6",
"infection/infection": "^0.29.14",
"justinrainbow/json-schema": "^5.3 || ^6.4",
"keradus/cli-executor": "^2.2",
"justinrainbow/json-schema": "^5.3 || ^6.2",
"keradus/cli-executor": "^2.1",
"mikey179/vfsstream": "^1.6.12",
"php-coveralls/php-coveralls": "^2.8",
"php-coveralls/php-coveralls": "^2.7",
"php-cs-fixer/accessible-object": "^1.1",
"php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6",
"php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6",
"phpunit/phpunit": "^9.6.23 || ^10.5.47 || ^11.5.25",
"symfony/polyfill-php84": "^1.32",
"symfony/var-dumper": "^5.4.48 || ^6.4.23 || ^7.3.1",
"symfony/yaml": "^5.4.45 || ^6.4.23 || ^7.3.1"
"phpunit/phpunit": "^9.6.22 || ^10.5.45 || ^11.5.12",
"symfony/var-dumper": "^5.4.48 || ^6.4.18 || ^7.2.3",
"symfony/yaml": "^5.4.45 || ^6.4.18 || ^7.2.3"
},
"suggest": {
"ext-dom": "For handling output formats in XML",
@ -15940,7 +15938,7 @@
],
"support": {
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.84.0"
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.75.0"
},
"funding": [
{
@ -15948,7 +15946,7 @@
"type": "github"
}
],
"time": "2025-07-15T18:21:57+00:00"
"time": "2025-03-31T18:40:42+00:00"
},
{
"name": "friendsoftwig/twigcs",
@ -19455,6 +19453,6 @@
"ext-tokenizer": "*",
"ext-xml": "*"
},
"platform-dev": {},
"plugin-api-version": "2.6.0"
"platform-dev": [],
"plugin-api-version": "2.3.0"
}

View File

@ -1,47 +0,0 @@
<?php
declare(strict_types=1);
namespace Application\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Wallabag\Doctrine\WallabagMigration;
/**
* Add boolean for two-step setup for google authenticator.
*/
final class Version20250413133131 extends WallabagMigration
{
public function up(Schema $schema): void
{
$userTable = $schema->getTable($this->getTable('user'));
$this->skipIf($userTable->hasColumn('google_authenticator'), 'It seems that you already played this migration.');
$userTable->addColumn('google_authenticator', 'boolean', [
'default' => false,
'notnull' => true,
]);
}
/**
* Query to update data in user table, as it's not possible to perform this in the `up` method.
*/
public function postUp(Schema $schema): void
{
$this->skipIf(!$schema->getTable($this->getTable('user'))->hasColumn('google_authenticator'), 'Unable to update google_authenticator column');
$this->connection->executeQuery(
'UPDATE ' . $this->getTable('user') . ' SET google_authenticator = :googleAuthenticator WHERE googleAuthenticatorSecret IS NOT NULL AND googleAuthenticatorSecret <> :emptyString',
[
'googleAuthenticator' => true,
'emptyString' => '',
]
);
}
public function down(Schema $schema): void
{
$userTable = $schema->getTable($this->getTable('user'));
$userTable->dropColumn('google_authenticator');
}
}

View File

@ -41,13 +41,13 @@
"url": "https://github.com/wallabag/wallabag/issues"
},
"devDependencies": {
"@babel/core": "^7.28.3",
"@babel/eslint-parser": "^7.28.0",
"@babel/preset-env": "^7.28.3",
"@babel/core": "^7.27.7",
"@babel/eslint-parser": "^7.27.5",
"@babel/preset-env": "^7.27.2",
"@symfony/webpack-encore": "^5.1.0",
"autoprefixer": "^10.4.21",
"babel-loader": "^10.0.0",
"core-js": "^3.45.0",
"core-js": "^3.43.0",
"css-loader": "^7.1.2",
"eslint": "^8.57.1",
"eslint-config-airbnb-base": "^15.0.0",
@ -55,12 +55,12 @@
"eslint-webpack-plugin": "^5.0.2",
"file-loader": "^6.2.0",
"lato-font": "^3.0.0",
"mini-css-extract-plugin": "^2.9.4",
"mini-css-extract-plugin": "^2.9.2",
"postcss": "^8.5.6",
"postcss-loader": "^8.1.1",
"postcss-scss": "^4.0.9",
"regenerator-runtime": "^0.14.1",
"sass-embedded": "^1.90.0",
"sass-embedded": "^1.89.2",
"sass-loader": "^16.0.5",
"style-loader": "^4.0.0",
"stylelint": "^15.11.0",
@ -69,7 +69,7 @@
"stylelint-scss": "^5.3.2",
"terser-webpack-plugin": "^5.3.14",
"url-loader": "^4.1.1",
"webpack": "^5.101.2",
"webpack": "^5.99.9",
"webpack-cli": "^5.1.4",
"webpack-manifest-plugin": "^5.0.1",
"webpack-merge": "^6.0.1",

View File

@ -290,17 +290,6 @@ class EntryRestController extends WallabagRestController
* example="200",
* )
* ),
* @OA\Parameter(
* name="annotations",
* in="query",
* description="filter entries with annotations. all entries by default",
* required=false,
* @OA\Schema(
* type="integer",
* enum={"1", "0"},
* default="0"
* )
* ),
* @OA\Response(
* response="200",
* description="Returned when successful"
@ -326,7 +315,6 @@ class EntryRestController extends WallabagRestController
$detail = strtolower($request->query->get('detail', 'full'));
$domainName = (null === $request->query->get('domain_name')) ? '' : (string) $request->query->get('domain_name');
$httpStatus = (!\array_key_exists((int) $request->query->get('http_status'), Response::$statusTexts)) ? null : (int) $request->query->get('http_status');
$hasAnnotations = (null === $request->query->get('annotations')) ? null : (bool) $request->query->get('annotations');
try {
/** @var Pagerfanta $pager */
@ -342,8 +330,7 @@ class EntryRestController extends WallabagRestController
$detail,
$domainName,
$isNotParsed,
$httpStatus,
$hasAnnotations
$httpStatus
);
} catch (\Exception $e) {
throw new BadRequestHttpException($e->getMessage());
@ -369,7 +356,6 @@ class EntryRestController extends WallabagRestController
'tags' => $tags,
'since' => $since,
'detail' => $detail,
'annotations' => $hasAnnotations,
],
true
)

View File

@ -313,7 +313,6 @@ class ConfigController extends AbstractController
$user = $this->getUser();
$user->setGoogleAuthenticatorSecret('');
$user->setGoogleAuthenticator(false);
$user->setBackupCodes(null);
$this->userManager->updateUser($user);
@ -355,6 +354,11 @@ class ConfigController extends AbstractController
$this->userManager->updateUser($user);
$this->entityManager->flush();
$this->addFlash(
'notice',
'flashes.config.notice.otp_enabled'
);
return $this->render('Config/otp_app.html.twig', [
'backupCodes' => $backupCodes,
'qr_code' => $googleAuthenticator->getQRContent($user),
@ -404,9 +408,6 @@ class ConfigController extends AbstractController
'notice',
'flashes.config.notice.otp_enabled'
);
$user->setGoogleAuthenticator(true);
$this->userManager->updateUser($user);
$this->entityManager->flush();
return $this->redirect($this->generateUrl('config') . '#set3');
}
@ -420,9 +421,8 @@ class ConfigController extends AbstractController
$user->setBackupCodes(null);
$this->userManager->updateUser($user);
$this->entityManager->flush();
return $this->redirect($this->generateUrl('config_otp_app'), 307);
return $this->redirect($this->generateUrl('config') . '#set3');
}
/**

View File

@ -627,16 +627,6 @@ class EntryController extends AbstractController
$currentEntryId = $request->attributes->getInt('id');
$formOptions = [];
$direction = 'desc';
$sortBy = null;
if (null !== $request->get('entry_filter') && null !== $request->get('entry_filter')['sortType'] && '' !== $request->get('entry_filter')['sortType']) {
$direction = (null !== $request->get('entry_filter')['sortOrder'] && \in_array($request->get('entry_filter')['sortOrder'], ['asc', 'desc'], true)) ? $request->get('entry_filter')['sortOrder'] : 'desc';
if (\in_array($request->get('entry_filter')['sortType'], ['id','title','createdAt', 'url', 'readingTime'], true)) {
$sortBy = $request->get('entry_filter')['sortType'];
}
}
switch ($type) {
case 'search':
@ -664,7 +654,7 @@ class EntryController extends AbstractController
$qb = $this->entryRepository->getBuilderForSameDomainByUser($this->getUser()->getId(), $currentEntryId);
break;
case 'all':
$qb = $this->entryRepository->getBuilderForAllByUser($this->getUser()->getId(), $sortBy, $direction);
$qb = $this->entryRepository->getBuilderForAllByUser($this->getUser()->getId());
break;
default:
throw new \InvalidArgumentException(\sprintf('Type "%s" is not implemented.', $type));

View File

@ -8,12 +8,10 @@ use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Contracts\Translation\TranslatorInterface;
use Wallabag\Controller\AbstractController;
use Wallabag\Form\Type\UploadImportType;
use Wallabag\Import\PocketCsvImport;
use Wallabag\Redis\Producer as RedisProducer;
class PocketCsvController extends AbstractController
class PocketCsvController extends HtmlController
{
public function __construct(
private readonly PocketCsvImport $pocketCsvImport,
@ -27,58 +25,7 @@ class PocketCsvController extends AbstractController
#[IsGranted('IMPORT_ENTRIES')]
public function indexAction(Request $request, TranslatorInterface $translator)
{
$form = $this->createForm(UploadImportType::class);
$form->handleRequest($request);
$this->pocketCsvImport->setUser($this->getUser());
if ($this->craueConfig->get('import_with_rabbitmq')) {
$this->pocketCsvImport->setProducer($this->rabbitMqProducer);
} elseif ($this->craueConfig->get('import_with_redis')) {
$this->pocketCsvImport->setProducer($this->redisProducer);
}
if ($form->isSubmitted() && $form->isValid()) {
$file = $form->get('file')->getData();
$markAsRead = $form->get('mark_as_read')->getData();
$name = 'pocket_' . $this->getUser()->getId() . '.csv';
if (null !== $file && \in_array($file->getClientMimeType(), $this->getParameter('wallabag.allow_mimetypes'), true) && $file->move($this->getParameter('wallabag.resource_dir'), $name)) {
$res = $this->pocketCsvImport
->setFilepath($this->getParameter('wallabag.resource_dir') . '/' . $name)
->setMarkAsRead($markAsRead)
->import();
$message = 'flashes.import.notice.failed';
if (true === $res) {
$summary = $this->pocketCsvImport->getSummary();
$message = $translator->trans('flashes.import.notice.summary', [
'%imported%' => $summary['imported'],
'%skipped%' => $summary['skipped'],
]);
if (0 < $summary['queued']) {
$message = $translator->trans('flashes.import.notice.summary_with_queue', [
'%queued%' => $summary['queued'],
]);
}
unlink($this->getParameter('wallabag.resource_dir') . '/' . $name);
}
$this->addFlash('notice', $message);
return $this->redirect($this->generateUrl('homepage'));
}
$this->addFlash('notice', 'flashes.import.notice.failed_on_file');
}
return $this->render('Import/PocketCsv/index.html.twig', [
'form' => $form->createView(),
'import' => $this->pocketCsvImport,
]);
return parent::indexAction($request, $translator);
}
protected function getImportService()

View File

@ -147,11 +147,6 @@ class User extends BaseUser implements EmailTwoFactorInterface, GoogleTwoFactorI
#[ORM\Column(name: 'googleAuthenticatorSecret', type: 'string', nullable: true)]
private $googleAuthenticatorSecret;
// default value is explicitly set to false here to ensure that Doctrine
// does not complain about schema mapping mismatch
#[ORM\Column(name: 'google_authenticator', type: 'boolean', options: ['default' => false])]
private $googleAuthenticator = false;
/**
* @var array
*/
@ -269,11 +264,6 @@ class User extends BaseUser implements EmailTwoFactorInterface, GoogleTwoFactorI
$this->emailTwoFactor = $emailTwoFactor;
}
public function setGoogleAuthenticator(bool $googleAuthenticator): void
{
$this->googleAuthenticator = $googleAuthenticator;
}
/**
* Used in the user config form to be "like" the email option.
*/
@ -304,7 +294,7 @@ class User extends BaseUser implements EmailTwoFactorInterface, GoogleTwoFactorI
public function isGoogleAuthenticatorEnabled(): bool
{
return $this->googleAuthenticator;
return $this->googleAuthenticatorSecret ? true : false;
}
public function getGoogleAuthenticatorUsername(): string

View File

@ -205,24 +205,6 @@ class EntryFilterType extends AbstractType
'choices' => array_flip($this->repository->findDistinctLanguageByUser($user->getId())),
'label' => 'entry.filters.language_label',
])
->add('sortOrder', ChoiceFilterType::class, [
'apply_filter' => function (QueryInterface $filterQuery, $field, $values) { },
'choices' => [
'entry.sort.ascending' => 'asc',
'entry.sort.descending' => 'desc',
],
'label' => 'entry.sort.order_label',
])
->add('sortType', ChoiceFilterType::class, [
'apply_filter' => function (QueryInterface $filterQuery, $field, $values) { },
'choices' => [
'entry.sort.by.creation_date' => 'createdAt',
'entry.sort.by.title' => 'title',
'entry.sort.by.url' => 'url',
'entry.sort.by.reading_time' => 'readingTime',
],
'label' => 'entry.sort.status_label',
])
;
}

View File

@ -30,11 +30,10 @@ class EntryRepository extends ServiceEntityRepository
*
* @return QueryBuilder
*/
public function getBuilderForAllByUser($userId, $sortBy = 'createdAt', $direction = 'desc')
public function getBuilderForAllByUser($userId)
{
$sortBy = $sortBy ?: 'createdAt';
return $this
->getSortedQueryBuilderByUser($userId, $sortBy, $direction)
->getSortedQueryBuilderByUser($userId)
;
}
@ -269,17 +268,16 @@ class EntryRepository extends ServiceEntityRepository
* @param string $order
* @param int $since
* @param string $tags
* @param string $detail 'metadata' or 'full'. Include content field if 'full'
* @param string $detail 'metadata' or 'full'. Include content field if 'full'
* @param string $domainName
* @param int $httpStatus
* @param bool $isNotParsed
* @param bool $hasAnnotations
*
* @todo Breaking change: replace default detail=full by detail=metadata in a future version
*
* @return Pagerfanta
*/
public function findEntries($userId, $isArchived = null, $isStarred = null, $isPublic = null, $sort = 'created', $order = 'asc', $since = 0, $tags = '', $detail = 'full', $domainName = '', $isNotParsed = null, $httpStatus = null, $hasAnnotations = null)
public function findEntries($userId, $isArchived = null, $isStarred = null, $isPublic = null, $sort = 'created', $order = 'asc', $since = 0, $tags = '', $detail = 'full', $domainName = '', $isNotParsed = null, $httpStatus = null)
{
if (!\in_array(strtolower($detail), ['full', 'metadata'], true)) {
throw new \Exception('Detail "' . $detail . '" parameter is wrong, allowed: full or metadata');
@ -344,16 +342,6 @@ class EntryRepository extends ServiceEntityRepository
$qb->andWhere('e.domainName = :domainName')->setParameter('domainName', $domainName);
}
if (null !== $hasAnnotations) {
if ($hasAnnotations) {
$qb->leftJoin('e.annotations', 'a')
->andWhere('a.id IS NOT NULL');
} else {
$qb->leftJoin('e.annotations', 'a')
->andWhere('a.id IS NULL');
}
}
if (!\in_array(strtolower($order), ['asc', 'desc'], true)) {
throw new \Exception('Order "' . $order . '" parameter is wrong, allowed: asc or desc');
}

View File

@ -18,14 +18,14 @@
<img data-controller="qrcode" data-qrcode-url-value="{{ qr_code|raw }}" class="hide-on-med-and-down" />
</p>
<div id="config_otp_app_secret">
<div data-controller="highlight">
{{ 'config.otp.app.two_factor_code_description_5'|trans }} <pre><code>{{ secret }}</code></pre>
</div>
</li>
<li>
<p>{{ 'config.otp.app.two_factor_code_description_3'|trans }}</p>
<div><pre><code>{{ backupCodes|join("\n") }}</code></pre></div>
<div data-controller="highlight"><pre><code>{{ backupCodes|join("\n") }}</code></pre></div>
</li>
<li>
<p>{{ 'config.otp.app.two_factor_code_description_4'|trans }}</p>
@ -36,7 +36,7 @@
</div>
{% endfor %}
<form class="form" action="{{ path("config_otp_app_check") }}" method="post" name="config_otp_app_check">
<form class="form" action="{{ path("config_otp_app_check") }}" method="post">
<input type="hidden" name="token" value="{{ csrf_token('otp') }}" />
<div class="card-content">
<div class="row">

View File

@ -21,7 +21,7 @@
<input type="hidden" name="token" value="{{ csrf_token('archive-entry') }}"/>
<button type="submit" class="btn-link tool grey-text" title="{{ 'entry.list.toogle_as_read'|trans }}">
<i class="material-icons">{% if entry.isArchived == 0 %}done{% else %}unarchive{% endif %}</i>
<i class="material-icons">{% if entry.isArchived == 0 %}archive{% else %}unarchive{% endif %}</i>
</button>
</form>
</li>

View File

@ -26,7 +26,7 @@
<input type="hidden" name="token" value="{{ csrf_token('archive-entry') }}"/>
<button type="submit" class="btn-link tool grey-text" title="{{ 'entry.list.toogle_as_read'|trans }}">
<i class="material-icons">{% if entry.isArchived == 0 %}done{% else %}unarchive{% endif %}</i>
<i class="material-icons">{% if entry.isArchived == 0 %}archive{% else %}unarchive{% endif %}</i>
</button>
</form>
{% endif %}

View File

@ -93,7 +93,7 @@
<div class="mass-action">
<div class="mass-action-group">
<input type="checkbox" form="form_mass_action" class="entry-checkbox-input" data-action="batch-edit#toggleSelection" />
<button class="mass-action-button btn cyan darken-1" type="submit" form="form_mass_action" name="toggle-read" title="{{ 'entry.list.toogle_as_read'|trans }}"><i class="material-icons">done</i></button>
<button class="mass-action-button btn cyan darken-1" type="submit" form="form_mass_action" name="toggle-read" title="{{ 'entry.list.toogle_as_read'|trans }}"><i class="material-icons">archive</i></button>
<button class="mass-action-button btn cyan darken-1" type="submit" form="form_mass_action" name="toggle-star" title="{{ 'entry.list.toogle_as_star'|trans }}" ><i class="material-icons">star</i></button>
<button class="mass-action-button btn cyan darken-1" type="submit" form="form_mass_action" name="delete" onclick="return confirm('{{ 'entry.confirm.delete_entries'|trans|escape('js') }}')" title="{{ 'entry.list.delete'|trans }}"><i class="material-icons">delete</i></button>
</div>
@ -276,22 +276,6 @@
<label for="entry_filter_createdAt_right_date" class="active">{{ 'entry.filters.created_at.to'|trans }}</label>
</div>
<div class="col s6">
{{ form_label(form.sortType) }}
</div>
<div class="col s6">
{{ form_label(form.sortOrder) }}
</div>
<div class="col s6">
{{ form_widget(form.sortType) }}
</div>
<div class="col s6">
{{ form_widget(form.sortOrder) }}
</div>
<div class="col s6">
<button type="reset" class="center waves-effect waves-green btn-flat">{{ 'entry.filters.action.clear'|trans }}</button>
</div>

View File

@ -31,7 +31,7 @@
<input type="hidden" name="token" value="{{ csrf_token('archive-entry') }}"/>
<button type="submit" class="waves-effect" title="{{ 'entry.view.left_menu.set_as_read'|trans }}">
<i class="material-icons small">{% if entry.isArchived == 0 %}done{% else %}unarchive{% endif %}</i>
<i class="material-icons small">{% if entry.isArchived == 0 %}archive{% else %}unarchive{% endif %}</i>
</button>
</form>
</li>
@ -92,7 +92,7 @@
<input type="hidden" name="token" value="{{ csrf_token('archive-entry') }}"/>
<button type="submit" class="waves-effect collapsible-header markasread" title="{{ mark_as_read_label|trans }}" data-shortcuts-target="markAsRead">
<i class="material-icons small">{% if entry.isArchived == 0 %}done{% else %}unarchive{% endif %}</i>
<i class="material-icons small">{% if entry.isArchived == 0 %}archive{% else %}unarchive{% endif %}</i>
<span>{{ mark_as_read_label|trans }}</span>
</button>
</form>
@ -381,7 +381,7 @@
<input type="hidden" name="token" value="{{ csrf_token('archive-entry') }}"/>
<button type="submit" class="btn-floating">
<i class="material-icons">{% if entry.isArchived == 0 %}done{% else %}unarchive{% endif %}</i>
<i class="material-icons">{% if entry.isArchived == 0 %}archive{% else %}unarchive{% endif %}</i>
</button>
</form>
</li>

View File

@ -20,11 +20,10 @@
<div id="set1" class="col s12">
<dt>{{ 'about.who_behind_wallabag.developped_by'|trans }}</dt>
<dd>Nicolas Lœuillet — <a href="https://nicolas.loeuillet.org">{{ 'about.who_behind_wallabag.website'|trans }}</a></dd>
<dd><a href="mailto:nicolas@loeuillet.org">Nicolas Lœuillet</a> — <a href="https://nicolas.loeuillet.org">{{ 'about.who_behind_wallabag.website'|trans }}</a></dd>
<dd>Thomas Citharel — <a href="https://tcit.fr">{{ 'about.who_behind_wallabag.website'|trans }}</a></dd>
<dd>Jérémy Benoist — <a href="https://www.j0k3r.net">{{ 'about.who_behind_wallabag.website'|trans }}</a></dd>
<dd>Kevin Decherf — <a href="https://kdecherf.com/">{{ 'about.who_behind_wallabag.website'|trans }}</a></dd>
<dd>Yassine Guedidi</dd>
<dt>{{ 'about.who_behind_wallabag.many_contributors'|trans|raw }}</dt>
<dt>{{ 'about.who_behind_wallabag.project_website'|trans }}</dt>
<dd><a href="https://www.wallabag.org">https://www.wallabag.org</a></dd>
@ -34,7 +33,11 @@
<div id="set2" class="col s12">
<dl>
<dt><a href="https://doc.wallabag.org/">{{ 'about.getting_help.documentation'|trans }}</a></dt>
<dt>{{ 'about.getting_help.documentation'|trans }}</dt>
<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>
<dd><a href="https://doc.wallabag.org/it/">italiano</a></dd>
<dt>{{ 'about.getting_help.bug_reports'|trans }}</dt>
<dd>{{ 'about.getting_help.support'|trans|raw }}</dd>

View File

@ -56,7 +56,7 @@
</div>
<div class="card-action">
<ul>
<li><a href="{{ path('import_pocket_csv') }}">{{ 'quickstart.migrate.pocket'|trans }}</a></li>
<li><a href="{{ path('import_pocket') }}">{{ 'quickstart.migrate.pocket'|trans }}</a></li>
<li><a href="{{ path('import_readability') }}">{{ 'quickstart.migrate.readability'|trans }}</a></li>
<li><a href="{{ path('import_instapaper') }}">{{ 'quickstart.migrate.instapaper'|trans }}</a></li>
<li><a href="{{ path('import') }}">{{ 'quickstart.more'|trans }}</a></li>
@ -75,6 +75,7 @@
<div class="card-action">
<ul>
<li><a href="{{ path('developer') }}">{{ 'quickstart.developer.create_application'|trans }}</a></li>
<li><a href="https://doc.wallabag.org/en/developer/docker.html">{{ 'quickstart.developer.use_docker'|trans }}</a></li>
<li><a href="https://doc.wallabag.org/">{{ 'quickstart.more'|trans }}</a></li>
</ul>
</div>

View File

@ -27,7 +27,7 @@
<form class="card-tag-form hidden" data-tag-target="form" action="{{ path('tag_rename', {'slug': tag.slug, redirect: current_path}) }}" method="POST">
{{ form_widget(renameForms[tag.id].label, {'attr': {'value': tag.label, 'data-tag-target': 'input'}}) }}
{{ form_rest(renameForms[tag.id]) }}
<button type="submit"><i class="material-icons">done</i></button>
<button type="submit"><i class="material-icons">archive</i></button>
</form>
<button type="button" data-tag-target="edit" data-action="tag#showForm">
<i class="material-icons">mode_edit</i>

View File

@ -227,7 +227,6 @@ class EntryRestControllerTest extends WallabagApiTestCase
'public' => 0,
'notParsed' => 0,
'http_status' => 200,
'annotations' => 1,
]);
$this->assertSame(200, $this->client->getResponse()->getStatusCode());
@ -255,7 +254,6 @@ class EntryRestControllerTest extends WallabagApiTestCase
$this->assertStringContainsString('tags=foo', $content['_links'][$link]['href']);
$this->assertStringContainsString('since=1443274283', $content['_links'][$link]['href']);
$this->assertStringContainsString('public=0', $content['_links'][$link]['href']);
$this->assertStringContainsString('annotations=1', $content['_links'][$link]['href']);
}
$this->assertSame('application/json', $this->client->getResponse()->headers->get('Content-Type'));
@ -307,86 +305,6 @@ class EntryRestControllerTest extends WallabagApiTestCase
$this->assertSame('application/json', $this->client->getResponse()->headers->get('Content-Type'));
}
public function testGetEntriesWithAnnotationsFilter()
{
// Test filter for entries WITH annotations
// From fixtures: entry1 and entry2 have annotations, entry4, entry5, entry6, entry7 don't
$this->client->request('GET', '/api/entries', [
'annotations' => 1,
]);
$this->assertSame(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertArrayHasKey('items', $content['_embedded']);
// Check that only entries with annotations are returned
$entriesWithAnnotations = ['http://0.0.0.0/entry1', 'http://0.0.0.0/entry2'];
$entriesWithoutAnnotations = ['http://0.0.0.0/entry4', 'http://0.0.0.0/entry5', 'http://0.0.0.0/entry6', 'http://0.0.0.0/entry7'];
foreach ($content['_embedded']['items'] as $item) {
if (\in_array($item['url'], $entriesWithAnnotations, true)) {
$this->assertNotEmpty($item['annotations'], 'Entry with URL ' . $item['url'] . ' should have annotations');
}
$this->assertNotContains($item['url'], $entriesWithoutAnnotations, 'Entry without annotations should NOT be in the results');
}
// Ensure we have at least the entries with annotations
$foundUrls = array_column($content['_embedded']['items'], 'url');
$this->assertContains('http://0.0.0.0/entry1', $foundUrls, 'entry1 with annotations should be in the results');
$this->assertContains('http://0.0.0.0/entry2', $foundUrls, 'entry2 with annotations should be in the results');
// Check pagination links contain the filter
$this->assertArrayHasKey('_links', $content);
foreach (['self', 'first', 'last'] as $link) {
$this->assertArrayHasKey('href', $content['_links'][$link]);
$this->assertStringContainsString('annotations=1', $content['_links'][$link]['href']);
}
$this->assertSame('application/json', $this->client->getResponse()->headers->get('Content-Type'));
}
public function testGetEntriesWithoutAnnotationsFilter()
{
// Test filter for entries WITHOUT annotations
// From fixtures: entry1 and entry2 have annotations, entry4, entry5, entry6, entry7 don't
$this->client->request('GET', '/api/entries', [
'annotations' => 0,
]);
$this->assertSame(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertArrayHasKey('items', $content['_embedded']);
// Check that only entries without annotations are returned
$entriesWithAnnotations = ['http://0.0.0.0/entry1', 'http://0.0.0.0/entry2'];
$entriesWithoutAnnotations = ['http://0.0.0.0/entry4', 'http://0.0.0.0/entry5', 'http://0.0.0.0/entry6', 'http://0.0.0.0/entry7'];
foreach ($content['_embedded']['items'] as $item) {
$this->assertNotContains($item['url'], $entriesWithAnnotations, 'Entry with annotations should NOT be in the results');
if (\in_array($item['url'], $entriesWithoutAnnotations, true)) {
$this->assertEmpty($item['annotations'], 'Entry with URL ' . $item['url'] . ' should not have annotations');
}
}
// Ensure we have at least some entries without annotations
$foundUrls = array_column($content['_embedded']['items'], 'url');
$foundWithoutAnnotations = array_intersect($foundUrls, $entriesWithoutAnnotations);
$this->assertNotEmpty($foundWithoutAnnotations, 'Should have at least one entry without annotations in the results');
// Check pagination links contain the filter
$this->assertArrayHasKey('_links', $content);
foreach (['self', 'first', 'last'] as $link) {
$this->assertArrayHasKey('href', $content['_links'][$link]);
$this->assertStringContainsString('annotations=0', $content['_links'][$link]['href']);
}
$this->assertSame('application/json', $this->client->getResponse()->headers->get('Content-Type'));
}
public function testGetEntriesOnPageTwo()
{
$this->client->request('GET', '/api/entries', [

View File

@ -3,7 +3,6 @@
namespace Tests\Wallabag\Controller;
use Doctrine\ORM\EntityManagerInterface;
use Scheb\TwoFactorBundle\Security\TwoFactor\Provider\Google\GoogleAuthenticatorInterface;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
@ -1224,74 +1223,27 @@ class ConfigControllerTest extends WallabagTestCase
public function testUserEnable2faGoogle()
{
$googleAuthenticatorMock = $this->createMock(GoogleAuthenticatorInterface::class);
$googleAuthenticatorMock
->method('generateSecret')
->willReturn('DUMMYSECRET');
$googleAuthenticatorMock
->method('checkCode')
->willReturnCallback(function ($user, $code) {
return '123456' === $code;
});
$this->logInAs('admin');
$client = $this->getTestClient();
$client->disableReboot(); // Disable reboot to keep the mock in the container
// name::class notation does not work in this context
self::getContainer()->set('scheb_two_factor.security.google_authenticator', $googleAuthenticatorMock);
$crawler = $client->request('GET', '/config');
$form = $crawler->filter('form[name=config_otp_app]')->form();
$crawler = $client->submit($form);
$client->submit($form);
$this->assertSame(200, $client->getResponse()->getStatusCode());
$secret = $crawler->filter('div#config_otp_app_secret pre code')->innerText();
$this->assertSame('DUMMYSECRET', $secret);
$em = $this->getEntityManager();
$user = $em
->getRepository(User::class)
->findOneByUsername('admin');
// At this phase, the user should not have 2FA enabled
$this->assertFalse($user->isGoogleTwoFactor());
// First test: send invalid OTP code
$form = $crawler->filter('form[name=config_otp_app_check]')->form();
$data = [
'_auth_code' => '000000',
];
$client->submit($form, $data);
$this->assertSame(307, $client->getResponse()->getStatusCode());
$this->assertStringContainsString('flashes.config.notice.otp_code_invalid', $client->getContainer()->get(SessionInterface::class)->getFlashBag()->get('notice')[0]);
// Follow the redirect to the OTP check form again
$crawler = $client->followRedirect();
// Second test: send valid OTP code
$form = $crawler->filter('form[name=config_otp_app_check]')->form();
$data = [
'_auth_code' => '123456',
];
$client->submit($form, $data);
$this->assertSame(302, $client->getResponse()->getStatusCode());
$this->assertStringContainsString('flashes.config.notice.otp_enabled', $client->getContainer()->get(SessionInterface::class)->getFlashBag()->get('notice')[0]);
// restore user
$em = $this->getEntityManager();
$user = $em
->getRepository(User::class)
->findOneByUsername('admin');
$this->assertTrue($user->isGoogleTwoFactor());
$this->assertGreaterThan(0, \count($user->getBackupCodes()));
$this->assertGreaterThan(0, $user->getBackupCodes());
// Restore user
$user->setGoogleAuthenticatorSecret('');
$user->setGoogleAuthenticator(false);
$user->setBackupCodes([]);
$user->setGoogleAuthenticatorSecret(false);
$user->setBackupCodes(null);
$em->persist($user);
$em->flush();
}
@ -1307,7 +1259,6 @@ class ConfigControllerTest extends WallabagTestCase
->findOneByUsername('admin');
$user->setGoogleAuthenticatorSecret('Google2FA');
$user->setGoogleAuthenticator(true);
$em->persist($user);
$em->flush();
@ -1320,6 +1271,7 @@ class ConfigControllerTest extends WallabagTestCase
$this->assertStringContainsString('flashes.config.notice.otp_disabled', $client->getContainer()->get(SessionInterface::class)->getFlashBag()->get('notice')[0]);
// restore user
$em = $this->getEntityManager();
$user = $em
->getRepository(User::class)
@ -1327,7 +1279,6 @@ class ConfigControllerTest extends WallabagTestCase
$this->assertEmpty($user->getGoogleAuthenticatorSecret());
$this->assertEmpty($user->getBackupCodes());
$this->assertFalse($user->isGoogleTwoFactor());
}
public function testExportTaggingRule()

View File

@ -66,7 +66,6 @@ class SecurityControllerTest extends WallabagTestCase
->getRepository(User::class)
->findOneByUsername('admin');
$user->setGoogleAuthenticatorSecret('26LDIHYGHNELOQEM');
$user->setGoogleAuthenticator(true);
$em->persist($user);
$em->flush();
@ -79,7 +78,6 @@ class SecurityControllerTest extends WallabagTestCase
->getRepository(User::class)
->findOneByUsername('admin');
$user->setGoogleAuthenticatorSecret(null);
$user->setGoogleAuthenticator(false);
$em->persist($user);
$em->flush();
}

View File

@ -566,7 +566,6 @@ import:
pocket_csv:
page_title: Import > Pocket CSV
description: Tento importér naimportuje všechny vaše záložky z Pocketu (prostřednictvím CSV exportu). Stačí přejít na https://getpocket.com/export a exportovat soubor. Stáhne se ZIP soubor (např. „pocket.zip“). Po rozbalení získáte CSV soubor, nazvaný „part_000000.csv“.
how_to: Zvolte soubor zálohy záložek a kliknutím na níže uvedené tlačítko jej naimportujte. Pamatujte na to, že tento proces může trvat dlouho, protože je třeba načíst všechny články.
flashes:
config:
notice:

View File

@ -237,16 +237,6 @@ entry:
untagged: Untagged entries
all: All entries
same_domain: Same domain
sort:
status_label: Sort by
order_label: Order
by:
creation_date: Creation date
title: Title
url: URL
reading_time: Reading time
ascending: Ascending
descending: Descending
list:
number_on_the_page: '{0} There are no entries.|{1} There is one entry.|]1,Inf[ There are %count% entries.'
reading_time: estimated reading time

826
yarn.lock

File diff suppressed because it is too large Load Diff