forked from wallabag/wallabag
Compare commits
30 Commits
2.6.5
...
all_mailer
| Author | SHA1 | Date | |
|---|---|---|---|
| 8a22f0f9db | |||
| 5b4b99d914 | |||
| 88cd6263bc | |||
| 60623246ae | |||
| fa107116cc | |||
| 0cfdddc2eb | |||
| aa06e8328e | |||
| 5240684be9 | |||
| 9ec351e8b6 | |||
| 6fab27f3ce | |||
| e4d69cafe4 | |||
| 34e51243d9 | |||
| 9bc026f343 | |||
| a46fd5fc9f | |||
| f06a826c6d | |||
| c7e5ba6dd0 | |||
| 62ab325ad4 | |||
| c5d21025c4 | |||
| 8ac80e934e | |||
| 4b04cd5746 | |||
| dbed27f8d8 | |||
| 137c8ab756 | |||
| 0fdffb0b96 | |||
| 2d7d16ee6c | |||
| 18615738c0 | |||
| 452362c17a | |||
| ec4d23b584 | |||
| c0414355aa | |||
| aaf4ab2d2a | |||
| 56983b01a8 |
2
.github/workflows/assets.yml
vendored
2
.github/workflows/assets.yml
vendored
@ -5,7 +5,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
- 2.*
|
- "2.**"
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|||||||
2
.github/workflows/coding-standards.yml
vendored
2
.github/workflows/coding-standards.yml
vendored
@ -5,7 +5,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
- 2.*
|
- "2.**"
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|||||||
2
.github/workflows/continuous-integration.yml
vendored
2
.github/workflows/continuous-integration.yml
vendored
@ -5,7 +5,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
- 2.*
|
- "2.**"
|
||||||
|
|
||||||
env:
|
env:
|
||||||
PGPASSWORD: wallabagrocks
|
PGPASSWORD: wallabagrocks
|
||||||
|
|||||||
2
.github/workflows/translations.yml
vendored
2
.github/workflows/translations.yml
vendored
@ -5,7 +5,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
- 2.*
|
- "2.**"
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|||||||
8
.github/workflows/upload-release-package.yml
vendored
8
.github/workflows/upload-release-package.yml
vendored
@ -33,7 +33,9 @@ jobs:
|
|||||||
run: make release VERSION=${{ github.event.release.tag_name }}
|
run: make release VERSION=${{ github.event.release.tag_name }}
|
||||||
|
|
||||||
- name: Upload the package to the release
|
- name: Upload the package to the release
|
||||||
uses: shogo82148/actions-upload-release-asset@v1
|
uses: pierrotdelalune/Form_Data_HTTP_POST_Action@main
|
||||||
with:
|
with:
|
||||||
upload_url: ${{ github.event.release.upload_url }}
|
url: ${{ github.event.release.upload_url }}
|
||||||
asset_path: /tmp/wllbg-release/wallabag-${{ github.event.release.tag_name }}.tar.gz
|
headers: "{\"Authorization\": \"token ${{ secrets.GITEA_TOKEN }}\"}"
|
||||||
|
file: /tmp/wllbg-release/wallabag-${{ github.event.release.tag_name }}.tar.gz
|
||||||
|
name: wallabag-${{ github.event.release.tag_name }}.tar.gz
|
||||||
|
|||||||
1
.secrets.EXAMPLE
Normal file
1
.secrets.EXAMPLE
Normal file
@ -0,0 +1 @@
|
|||||||
|
GITEA_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||||
23
CHANGELOG.md
23
CHANGELOG.md
@ -1,5 +1,28 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [2.6.7](https://github.com/wallabag/wallabag/tree/2.6.7)
|
||||||
|
[Full Changelog](https://github.com/wallabag/wallabag/compare/2.6.6...2.6.7)
|
||||||
|
|
||||||
|
### Security fix
|
||||||
|
* A user can disable her 2FA unintentionally by @kdecherf in https://github.com/wallabag/wallabag/commit/0cfdddc2eb0aee5ffb69bf499d377d75655ba157
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
* Fix deprecated null tag parameter by @Simounet in https://github.com/wallabag/wallabag/pull/6985
|
||||||
|
* Full clickable card on mass action by @Simounet in https://github.com/wallabag/wallabag/pull/6991
|
||||||
|
* Add tag form submit button always displayed by @Simounet in https://github.com/wallabag/wallabag/pull/6986
|
||||||
|
|
||||||
|
## [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)
|
## [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)
|
[Full Changelog](https://github.com/wallabag/wallabag/compare/2.6.4...2.6.5)
|
||||||
|
|
||||||
|
|||||||
7
README_GITEA.txt
Normal file
7
README_GITEA.txt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
1. Copy release_event.json.EXAMPLE to elease_event.json and adapt it
|
||||||
|
2. Copy .secrets.EXAMPLE to .secrets and adapt it
|
||||||
|
3. Run
|
||||||
|
$ act release -e release_event.json
|
||||||
|
or
|
||||||
|
$ act release -e release_event.json --pull=false
|
||||||
|
to avoid pulling act image at each run
|
||||||
@ -235,6 +235,12 @@
|
|||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tags-add-form {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 640px) {
|
@media only screen and (max-width: 640px) {
|
||||||
.entry-info {
|
.entry-info {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
@ -258,4 +264,12 @@
|
|||||||
#article .entry-info .chip-action {
|
#article .entry-info .chip-action {
|
||||||
min-width: 40px;
|
min-width: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tags-add-form {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tags-add-form-submit {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,44 +14,53 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.mass-action {
|
.mass-action {
|
||||||
margin: 10px 5px 10px 20px;
|
margin: 20px 5px 10px 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mass-action-group {
|
.mass-action-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
gap: 10px;
|
align-items: center;
|
||||||
|
gap: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mass-action-button {
|
.mass-action-button {
|
||||||
height: 24px;
|
height: 36px;
|
||||||
line-height: 24px;
|
line-height: 36px;
|
||||||
padding: 0 0.5rem;
|
padding: 0 0.7rem;
|
||||||
|
|
||||||
i {
|
i {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.entry-checkbox {
|
.mass-action-button--tags {
|
||||||
margin: 10px 15px 10px 5px;
|
border-radius: 2px 0 0 2px;
|
||||||
|
}
|
||||||
|
|
||||||
.card & {
|
.card-stacked .entry-checkbox {
|
||||||
float: right;
|
margin: 10px 15px 10px 5px;
|
||||||
margin-right: 0;
|
}
|
||||||
padding: 10px;
|
|
||||||
}
|
.card .entry-checkbox {
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
padding: 10px;
|
||||||
|
inset: 0;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: start;
|
||||||
|
background-color: rgb(0 172 193 / 20%);
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entries .entry-checkbox-input,
|
.entries .entry-checkbox-input,
|
||||||
.mass-action .entry-checkbox-input {
|
.mass-action .entry-checkbox-input {
|
||||||
position: relative;
|
position: relative;
|
||||||
left: 0;
|
|
||||||
width: 20px;
|
width: 20px;
|
||||||
min-height: 25px;
|
min-height: 25px;
|
||||||
height: 100%;
|
|
||||||
vertical-align: middle;
|
|
||||||
opacity: initial;
|
opacity: initial;
|
||||||
|
cursor: pointer;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,11 +73,19 @@
|
|||||||
|
|
||||||
.mass-action-tags {
|
.mass-action-tags {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
margin-top: 10px;
|
||||||
gap: 10px;
|
|
||||||
|
|
||||||
.mass-action-tags-input {
|
.mass-action-tags-input.mass-action-tags-input {
|
||||||
margin: 0;
|
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 +105,16 @@
|
|||||||
|
|
||||||
.results {
|
.results {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
margin-bottom: 10px;
|
||||||
padding: 1rem 1rem 0;
|
padding: 1rem 1rem 0;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
.nb-results {
|
.nb-results {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
}
|
margin-bottom: 20px;
|
||||||
|
gap: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.results-item {
|
.results-item {
|
||||||
@ -173,9 +193,38 @@ footer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 993px) {
|
@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 {
|
.mass-action {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
margin-top: 10px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 30px;
|
|
||||||
|
.mass-action-tags-input.mass-action-tags-input {
|
||||||
|
height: 21px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,7 +17,8 @@ div.settings div.file-field {
|
|||||||
|
|
||||||
/* override materializecss pointer-event disabled on checkboxes */
|
/* override materializecss pointer-event disabled on checkboxes */
|
||||||
[type="checkbox"]:not(:checked),
|
[type="checkbox"]:not(:checked),
|
||||||
[type="checkbox"]:checked {
|
[type="checkbox"]:checked,
|
||||||
|
.input-field label {
|
||||||
pointer-events: initial;
|
pointer-events: initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -29,6 +29,7 @@ framework:
|
|||||||
# handler_id set to null will use default session handler from php.ini
|
# handler_id set to null will use default session handler from php.ini
|
||||||
handler_id: session.handler.native_file
|
handler_id: session.handler.native_file
|
||||||
save_path: "%kernel.project_dir%/var/sessions/%kernel.environment%"
|
save_path: "%kernel.project_dir%/var/sessions/%kernel.environment%"
|
||||||
|
cookie_secure: auto
|
||||||
fragments: ~
|
fragments: ~
|
||||||
http_method_override: true
|
http_method_override: true
|
||||||
assets: ~
|
assets: ~
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
wallabag_core:
|
wallabag_core:
|
||||||
version: 2.6.5
|
version: 2.6.7
|
||||||
paypal_url: "https://liberapay.com/wallabag/donate"
|
paypal_url: "https://liberapay.com/wallabag/donate"
|
||||||
languages:
|
languages:
|
||||||
en: 'English'
|
en: 'English'
|
||||||
|
|||||||
@ -129,6 +129,8 @@
|
|||||||
"symfony/finder": "^4.4",
|
"symfony/finder": "^4.4",
|
||||||
"symfony/form": "^4.4",
|
"symfony/form": "^4.4",
|
||||||
"symfony/framework-bundle": "^4.4",
|
"symfony/framework-bundle": "^4.4",
|
||||||
|
"symfony/google-mailer": "^4.4",
|
||||||
|
"symfony/amazon-mailer": "^5.2",
|
||||||
"symfony/http-foundation": "^4.4",
|
"symfony/http-foundation": "^4.4",
|
||||||
"symfony/http-kernel": "^4.4",
|
"symfony/http-kernel": "^4.4",
|
||||||
"symfony/mailer": "^4.4",
|
"symfony/mailer": "^4.4",
|
||||||
@ -209,7 +211,11 @@
|
|||||||
"incenteev-parameters": {
|
"incenteev-parameters": {
|
||||||
"file": "app/config/parameters.yml"
|
"file": "app/config/parameters.yml"
|
||||||
},
|
},
|
||||||
"public-dir": "web"
|
"public-dir": "web",
|
||||||
|
"symfony": {
|
||||||
|
"allow-contrib": true,
|
||||||
|
"require": "4.4.*"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"post-install-cmd": [
|
"post-install-cmd": [
|
||||||
|
|||||||
1368
composer.lock
generated
1368
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -62,5 +62,5 @@ parameters:
|
|||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Method FOS\\\\UserBundle\\\\Model\\\\UserManagerInterface\\:\\:updateUser()#"
|
message: "#^Method FOS\\\\UserBundle\\\\Model\\\\UserManagerInterface\\:\\:updateUser()#"
|
||||||
count: 7
|
count: 6
|
||||||
path: src/Wallabag/CoreBundle/Controller/ConfigController.php
|
path: src/Wallabag/CoreBundle/Controller/ConfigController.php
|
||||||
|
|||||||
7
release_event.json.EXAMPLE
Normal file
7
release_event.json.EXAMPLE
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"release" :
|
||||||
|
{
|
||||||
|
"tag_name" : "2.6.7_all_mailers",
|
||||||
|
"upload_url" : "https://gyokuro.ile-australe.eu/api/v1/repos/pierre/wallabag/releases/1918/assets"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -9,7 +9,7 @@ ENV=$4
|
|||||||
|
|
||||||
rm -rf "${TMP_FOLDER:?}"/"$RELEASE_FOLDER"
|
rm -rf "${TMP_FOLDER:?}"/"$RELEASE_FOLDER"
|
||||||
mkdir "$TMP_FOLDER"/"$RELEASE_FOLDER"
|
mkdir "$TMP_FOLDER"/"$RELEASE_FOLDER"
|
||||||
git clone https://github.com/wallabag/wallabag.git --single-branch --depth 1 --branch $1 "$TMP_FOLDER"/"$RELEASE_FOLDER"/"$VERSION"
|
git clone https://gyokuro.ile-australe.eu/pierre/wallabag.git --single-branch --depth 1 --branch $1 "$TMP_FOLDER"/"$RELEASE_FOLDER"/"$VERSION"
|
||||||
cd "$TMP_FOLDER"/"$RELEASE_FOLDER"/"$VERSION" && SYMFONY_ENV="$ENV" COMPOSER_MEMORY_LIMIT=-1 composer install -n --no-dev
|
cd "$TMP_FOLDER"/"$RELEASE_FOLDER"/"$VERSION" && SYMFONY_ENV="$ENV" COMPOSER_MEMORY_LIMIT=-1 composer install -n --no-dev
|
||||||
cd "$TMP_FOLDER"/"$RELEASE_FOLDER"/"$VERSION" && php bin/console wallabag:install --env="$ENV" -n
|
cd "$TMP_FOLDER"/"$RELEASE_FOLDER"/"$VERSION" && php bin/console wallabag:install --env="$ENV" -n
|
||||||
cd "$TMP_FOLDER"/"$RELEASE_FOLDER"/"$VERSION" && php bin/console assets:install --env="$ENV" --symlink --relative
|
cd "$TMP_FOLDER"/"$RELEASE_FOLDER"/"$VERSION" && php bin/console assets:install --env="$ENV" --symlink --relative
|
||||||
|
|||||||
@ -254,10 +254,14 @@ class ConfigController extends AbstractController
|
|||||||
/**
|
/**
|
||||||
* Disable 2FA using email.
|
* Disable 2FA using email.
|
||||||
*
|
*
|
||||||
* @Route("/config/otp/email/disable", name="disable_otp_email")
|
* @Route("/config/otp/email/disable", name="disable_otp_email", methods={"POST"})
|
||||||
*/
|
*/
|
||||||
public function disableOtpEmailAction()
|
public function disableOtpEmailAction(Request $request)
|
||||||
{
|
{
|
||||||
|
if (!$this->isCsrfTokenValid('otp', $request->request->get('token'))) {
|
||||||
|
throw $this->createAccessDeniedException('Bad CSRF token.');
|
||||||
|
}
|
||||||
|
|
||||||
$user = $this->getUser();
|
$user = $this->getUser();
|
||||||
$user->setEmailTwoFactor(false);
|
$user->setEmailTwoFactor(false);
|
||||||
|
|
||||||
@ -274,10 +278,14 @@ class ConfigController extends AbstractController
|
|||||||
/**
|
/**
|
||||||
* Enable 2FA using email.
|
* Enable 2FA using email.
|
||||||
*
|
*
|
||||||
* @Route("/config/otp/email", name="config_otp_email")
|
* @Route("/config/otp/email", name="config_otp_email", methods={"POST"})
|
||||||
*/
|
*/
|
||||||
public function otpEmailAction()
|
public function otpEmailAction(Request $request)
|
||||||
{
|
{
|
||||||
|
if (!$this->isCsrfTokenValid('otp', $request->request->get('token'))) {
|
||||||
|
throw $this->createAccessDeniedException('Bad CSRF token.');
|
||||||
|
}
|
||||||
|
|
||||||
$user = $this->getUser();
|
$user = $this->getUser();
|
||||||
|
|
||||||
$user->setGoogleAuthenticatorSecret(null);
|
$user->setGoogleAuthenticatorSecret(null);
|
||||||
@ -297,10 +305,14 @@ class ConfigController extends AbstractController
|
|||||||
/**
|
/**
|
||||||
* Disable 2FA using OTP app.
|
* Disable 2FA using OTP app.
|
||||||
*
|
*
|
||||||
* @Route("/config/otp/app/disable", name="disable_otp_app")
|
* @Route("/config/otp/app/disable", name="disable_otp_app", methods={"POST"})
|
||||||
*/
|
*/
|
||||||
public function disableOtpAppAction()
|
public function disableOtpAppAction(Request $request)
|
||||||
{
|
{
|
||||||
|
if (!$this->isCsrfTokenValid('otp', $request->request->get('token'))) {
|
||||||
|
throw $this->createAccessDeniedException('Bad CSRF token.');
|
||||||
|
}
|
||||||
|
|
||||||
$user = $this->getUser();
|
$user = $this->getUser();
|
||||||
|
|
||||||
$user->setGoogleAuthenticatorSecret('');
|
$user->setGoogleAuthenticatorSecret('');
|
||||||
@ -319,10 +331,14 @@ class ConfigController extends AbstractController
|
|||||||
/**
|
/**
|
||||||
* Enable 2FA using OTP app, user will need to confirm the generated code from the app.
|
* Enable 2FA using OTP app, user will need to confirm the generated code from the app.
|
||||||
*
|
*
|
||||||
* @Route("/config/otp/app", name="config_otp_app")
|
* @Route("/config/otp/app", name="config_otp_app", methods={"POST"})
|
||||||
*/
|
*/
|
||||||
public function otpAppAction(GoogleAuthenticatorInterface $googleAuthenticator)
|
public function otpAppAction(Request $request, GoogleAuthenticatorInterface $googleAuthenticator)
|
||||||
{
|
{
|
||||||
|
if (!$this->isCsrfTokenValid('otp', $request->request->get('token'))) {
|
||||||
|
throw $this->createAccessDeniedException('Bad CSRF token.');
|
||||||
|
}
|
||||||
|
|
||||||
$user = $this->getUser();
|
$user = $this->getUser();
|
||||||
$secret = $googleAuthenticator->generateSecret();
|
$secret = $googleAuthenticator->generateSecret();
|
||||||
|
|
||||||
@ -357,8 +373,10 @@ class ConfigController extends AbstractController
|
|||||||
* Cancelling 2FA using OTP app.
|
* Cancelling 2FA using OTP app.
|
||||||
*
|
*
|
||||||
* @Route("/config/otp/app/cancel", name="config_otp_app_cancel")
|
* @Route("/config/otp/app/cancel", name="config_otp_app_cancel")
|
||||||
|
*
|
||||||
|
* XXX: commented until we rewrite 2fa with a real two-steps activation
|
||||||
*/
|
*/
|
||||||
public function otpAppCancelAction()
|
/*public function otpAppCancelAction()
|
||||||
{
|
{
|
||||||
$user = $this->getUser();
|
$user = $this->getUser();
|
||||||
$user->setGoogleAuthenticatorSecret(null);
|
$user->setGoogleAuthenticatorSecret(null);
|
||||||
@ -367,15 +385,19 @@ class ConfigController extends AbstractController
|
|||||||
$this->userManager->updateUser($user, true);
|
$this->userManager->updateUser($user, true);
|
||||||
|
|
||||||
return $this->redirect($this->generateUrl('config') . '#set3');
|
return $this->redirect($this->generateUrl('config') . '#set3');
|
||||||
}
|
}*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate OTP code.
|
* Validate OTP code.
|
||||||
*
|
*
|
||||||
* @Route("/config/otp/app/check", name="config_otp_app_check")
|
* @Route("/config/otp/app/check", name="config_otp_app_check", methods={"POST"})
|
||||||
*/
|
*/
|
||||||
public function otpAppCheckAction(Request $request, GoogleAuthenticatorInterface $googleAuthenticator)
|
public function otpAppCheckAction(Request $request, GoogleAuthenticatorInterface $googleAuthenticator)
|
||||||
{
|
{
|
||||||
|
if (!$this->isCsrfTokenValid('otp', $request->request->get('token'))) {
|
||||||
|
throw $this->createAccessDeniedException('Bad CSRF token.');
|
||||||
|
}
|
||||||
|
|
||||||
$isValid = $googleAuthenticator->checkCode(
|
$isValid = $googleAuthenticator->checkCode(
|
||||||
$this->getUser(),
|
$this->getUser(),
|
||||||
$request->get('_auth_code')
|
$request->get('_auth_code')
|
||||||
@ -395,7 +417,12 @@ class ConfigController extends AbstractController
|
|||||||
'scheb_two_factor.code_invalid'
|
'scheb_two_factor.code_invalid'
|
||||||
);
|
);
|
||||||
|
|
||||||
return $this->redirect($this->generateUrl('config_otp_app'));
|
$this->addFlash(
|
||||||
|
'notice',
|
||||||
|
'scheb_two_factor.code_invalid'
|
||||||
|
);
|
||||||
|
|
||||||
|
return $this->redirect($this->generateUrl('config') . '#set3');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -675,9 +675,6 @@ class EntryController extends AbstractController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$nbEntriesUntagged = $this->entryRepository
|
|
||||||
->countUntaggedEntriesByUser($this->getUser()->getId());
|
|
||||||
|
|
||||||
return $this->render(
|
return $this->render(
|
||||||
'@WallabagCore/Entry/entries.html.twig', [
|
'@WallabagCore/Entry/entries.html.twig', [
|
||||||
'form' => $form->createView(),
|
'form' => $form->createView(),
|
||||||
@ -685,7 +682,6 @@ class EntryController extends AbstractController
|
|||||||
'currentPage' => $page,
|
'currentPage' => $page,
|
||||||
'searchTerm' => $searchTerm,
|
'searchTerm' => $searchTerm,
|
||||||
'isFiltered' => $form->isSubmitted(),
|
'isFiltered' => $form->isSubmitted(),
|
||||||
'nbEntriesUntagged' => $nbEntriesUntagged,
|
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,7 +45,7 @@ class TagController extends AbstractController
|
|||||||
$form = $this->createForm(NewTagType::class, new Tag());
|
$form = $this->createForm(NewTagType::class, new Tag());
|
||||||
$form->handleRequest($request);
|
$form->handleRequest($request);
|
||||||
|
|
||||||
$tags = $form->get('label')->getData();
|
$tags = $form->get('label')->getData() ?? '';
|
||||||
$tagsExploded = explode(',', $tags);
|
$tagsExploded = explode(',', $tags);
|
||||||
|
|
||||||
// avoid too much tag to be added
|
// avoid too much tag to be added
|
||||||
|
|||||||
@ -4,7 +4,6 @@ namespace Wallabag\CoreBundle\Form\Type;
|
|||||||
|
|
||||||
use FOS\UserBundle\Form\Type\RegistrationFormType;
|
use FOS\UserBundle\Form\Type\RegistrationFormType;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
|
||||||
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||||
@ -23,15 +22,6 @@ class UserInformationType extends AbstractType
|
|||||||
->add('email', EmailType::class, [
|
->add('email', EmailType::class, [
|
||||||
'label' => 'config.form_user.email_label',
|
'label' => 'config.form_user.email_label',
|
||||||
])
|
])
|
||||||
->add('emailTwoFactor', CheckboxType::class, [
|
|
||||||
'required' => false,
|
|
||||||
'label' => 'config.form_user.emailTwoFactor_label',
|
|
||||||
])
|
|
||||||
->add('googleTwoFactor', CheckboxType::class, [
|
|
||||||
'required' => false,
|
|
||||||
'label' => 'config.form_user.googleTwoFactor_label',
|
|
||||||
'mapped' => false,
|
|
||||||
])
|
|
||||||
->add('save', SubmitType::class, [
|
->add('save', SubmitType::class, [
|
||||||
'label' => 'config.form.save',
|
'label' => 'config.form.save',
|
||||||
])
|
])
|
||||||
|
|||||||
@ -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.
|
* 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.
|
* 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.
|
* 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.
|
* Retrieves entries filtered with a search term for a user.
|
||||||
*
|
*
|
||||||
@ -167,6 +226,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.
|
* Retrieve untagged entries for a user.
|
||||||
*
|
*
|
||||||
@ -563,6 +637,23 @@ class EntryRepository extends ServiceEntityRepository
|
|||||||
return $qb->getQuery()->getArrayResult();
|
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.
|
* Find all entries by url and owner.
|
||||||
*
|
*
|
||||||
|
|||||||
@ -209,38 +209,66 @@
|
|||||||
|
|
||||||
{{ form_widget(form.user.save, {'attr': {'class': 'btn waves-effect waves-light'}}) }}
|
{{ form_widget(form.user.save, {'attr': {'class': 'btn waves-effect waves-light'}}) }}
|
||||||
|
|
||||||
<br/>
|
|
||||||
<br/>
|
|
||||||
<div class="row">
|
|
||||||
<h5>{{ 'config.otp.page_title'|trans }}</h5>
|
|
||||||
|
|
||||||
<p>{{ 'config.form_user.two_factor_description'|trans }}</p>
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>{{ 'config.form_user.two_factor.table_method'|trans }}</th>
|
|
||||||
<th>{{ 'config.form_user.two_factor.table_state'|trans }}</th>
|
|
||||||
<th>{{ 'config.form_user.two_factor.table_action'|trans }}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>{{ 'config.form_user.two_factor.emailTwoFactor_label'|trans }}</td>
|
|
||||||
<td>{% if app.user.isEmailTwoFactor %}<b>{{ 'config.form_user.two_factor.state_enabled'|trans }}</b>{% else %}{{ 'config.form_user.two_factor.state_disabled'|trans }}{% endif %}</td>
|
|
||||||
<td><a href="{{ path('config_otp_email') }}" class="waves-effect waves-light btn{% if app.user.isEmailTwoFactor %} disabled{% endif %}">{{ 'config.form_user.two_factor.action_email'|trans }}</a> {% if app.user.isEmailTwoFactor %}<a href="{{ path('disable_otp_email') }}" class="waves-effect waves-light btn red">Disable</a>{% endif %}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>{{ 'config.form_user.two_factor.googleTwoFactor_label'|trans }}</td>
|
|
||||||
<td>{% if app.user.isGoogleTwoFactor %}<b>{{ 'config.form_user.two_factor.state_enabled'|trans }}</b>{% else %}{{ 'config.form_user.two_factor.state_disabled'|trans }}{% endif %}</td>
|
|
||||||
<td><a href="{{ path('config_otp_app') }}" class="waves-effect waves-light btn{% if app.user.isGoogleTwoFactor %} disabled{% endif %}">{{ 'config.form_user.two_factor.action_app'|trans }}</a> {% if app.user.isGoogleTwoFactor %}<a href="{{ path('disable_otp_app') }}" class="waves-effect waves-light btn red">Disable</a>{% endif %}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
{{ form_widget(form.user._token) }}
|
{{ form_widget(form.user._token) }}
|
||||||
</form>
|
|
||||||
|
{{ form_end(form.user) }}
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<div class="row">
|
||||||
|
<h5>{{ 'config.otp.page_title'|trans }}</h5>
|
||||||
|
|
||||||
|
<p>{{ 'config.form_user.two_factor_description'|trans }}</p>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{{ 'config.form_user.two_factor.table_method'|trans }}</th>
|
||||||
|
<th>{{ 'config.form_user.two_factor.table_state'|trans }}</th>
|
||||||
|
<th>{{ 'config.form_user.two_factor.table_action'|trans }}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>{{ 'config.form_user.two_factor.emailTwoFactor_label'|trans }}</td>
|
||||||
|
<td>{% if app.user.isEmailTwoFactor %}<b>{{ 'config.form_user.two_factor.state_enabled'|trans }}</b>{% else %}{{ 'config.form_user.two_factor.state_disabled'|trans }}{% endif %}</td>
|
||||||
|
<td>
|
||||||
|
<form action="{{ path('config_otp_email') }}" method="post" name="config_otp_email">
|
||||||
|
<input type="hidden" name="token" value="{{ csrf_token('otp') }}" />
|
||||||
|
|
||||||
|
<button class="waves-effect waves-light btn{% if app.user.isEmailTwoFactor %} disabled{% endif %}" type="submit">{{ 'config.form_user.two_factor.action_email'|trans }}</button>
|
||||||
|
</form>
|
||||||
|
{% if app.user.isEmailTwoFactor %}
|
||||||
|
<form action="{{ path('disable_otp_email') }}" method="post" name="disable_otp_email">
|
||||||
|
<input type="hidden" name="token" value="{{ csrf_token('otp') }}" />
|
||||||
|
|
||||||
|
<button class="waves-effect waves-light btn red" type="submit">Disable</button>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>{{ 'config.form_user.two_factor.googleTwoFactor_label'|trans }}</td>
|
||||||
|
<td>{% if app.user.isGoogleTwoFactor %}<b>{{ 'config.form_user.two_factor.state_enabled'|trans }}</b>{% else %}{{ 'config.form_user.two_factor.state_disabled'|trans }}{% endif %}</td>
|
||||||
|
<td>
|
||||||
|
<form action="{{ path('config_otp_app') }}" method="post" name="config_otp_app">
|
||||||
|
<input type="hidden" name="token" value="{{ csrf_token('otp') }}" />
|
||||||
|
|
||||||
|
<button class="waves-effect waves-light btn{% if app.user.isGoogleTwoFactor %} disabled{% endif %}" type="submit">{{ 'config.form_user.two_factor.action_app'|trans }}</button>
|
||||||
|
</form>
|
||||||
|
{% if app.user.isGoogleTwoFactor %}
|
||||||
|
<form action="{{ path('disable_otp_app') }}" method="post" name="disable_otp_app">
|
||||||
|
<input type="hidden" name="token" value="{{ csrf_token('otp') }}" />
|
||||||
|
|
||||||
|
<button class="waves-effect waves-light btn red" type="submit">Disable</button>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="set4" class="col s12">
|
<div id="set4" class="col s12">
|
||||||
|
|||||||
@ -40,6 +40,7 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
<form class="form" action="{{ path("config_otp_app_check") }}" method="post">
|
<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="card-content">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="input-field col s12">
|
<div class="input-field col s12">
|
||||||
@ -49,9 +50,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-action">
|
<div class="card-action">
|
||||||
<a href="{{ path('config_otp_app_cancel') }}" class="waves-effect waves-light grey btn">
|
|
||||||
{{ 'config.otp.app.cancel'|trans }}
|
|
||||||
</a>
|
|
||||||
<button class="btn waves-effect waves-light" type="submit" name="send">
|
<button class="btn waves-effect waves-light" type="submit" name="send">
|
||||||
{{ 'config.otp.app.enable'|trans }}
|
{{ 'config.otp.app.enable'|trans }}
|
||||||
<i class="material-icons right">send</i>
|
<i class="material-icons right">send</i>
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
<div class="entry-checkbox">
|
<label class="entry-checkbox">
|
||||||
<input type="checkbox" class="entry-checkbox-input" data-js="entry-checkbox" name="entry-checkbox[]" value="{{ entry.id }}" />
|
<input type="checkbox" class="entry-checkbox-input" data-js="entry-checkbox" name="entry-checkbox[]" value="{{ entry.id }}" />
|
||||||
</div>
|
</label>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<div class="card entry-card{% if currentRoute in routes and entry.isArchived %} archived{% endif %}">
|
<div class="card entry-card{% if currentRoute in routes and entry.isArchived %} archived{% endif %}">
|
||||||
|
{% include "@WallabagCore/Entry/Card/_mass_checkbox.html.twig" with {'entry': entry} only %}
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="{% if app.user.config.displayThumbnails %}card-image{% endif %} waves-effect waves-block waves-light">
|
<div class="{% if app.user.config.displayThumbnails %}card-image{% endif %} waves-effect waves-block waves-light">
|
||||||
{% include "@WallabagCore/Entry/Card/_mass_checkbox.html.twig" with {'entry': entry} only %}
|
|
||||||
<ul class="card-entry-labels">
|
<ul class="card-entry-labels">
|
||||||
{% for tag in entry.tags|slice(0, 3) %}
|
{% for tag in entry.tags|slice(0, 3) %}
|
||||||
<li title="{{ tag.label }}"><a href="{{ path('tag_entries', {'slug': tag.slug}) }}">{{ tag.label }}</a></li>
|
<li title="{{ tag.label }}"><a href="{{ path('tag_entries', {'slug': tag.slug}) }}">{{ tag.label }}</a></li>
|
||||||
|
|||||||
@ -53,13 +53,11 @@
|
|||||||
<button class="mass-action-button btn cyan darken-1" type="submit" 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" 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" 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" 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" name="delete" onclick="return confirm('{{ 'entry.confirm.delete_entries'|trans|escape('js') }}')" title="{{ 'entry.list.delete'|trans }}"><i class="material-icons">delete</i></button>
|
<button class="mass-action-button btn cyan darken-1" type="submit" name="delete" onclick="return confirm('{{ 'entry.confirm.delete_entries'|trans|escape('js') }}')" title="{{ 'entry.list.delete'|trans }}"><i class="material-icons">delete</i></button>
|
||||||
<label for="mass-action-tags-displayed" class="mass-action-button btn cyan darken-1" type="button" title="{{ 'entry.list.add_tags'|trans }}"><i class="material-icons">label</i></label>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<input id="mass-action-tags-displayed" class="toggle-checkbox" type="checkbox" />
|
|
||||||
<div class="mass-action-tags">
|
<div class="mass-action-tags">
|
||||||
|
<button class="btn cyan darken-1 mass-action-button mass-action-button--tags" type="submit" name="tag" title="{{ 'entry.list.add_tags'|trans }}"><i class="material-icons">label</i></button>
|
||||||
<input type="text" class="mass-action-tags-input" name="tags" placeholder="{{ 'entry.list.mass_action_tags_input_placeholder'|trans }}" />
|
<input type="text" class="mass-action-tags-input" name="tags" placeholder="{{ 'entry.list.mass_action_tags_input_placeholder'|trans }}" />
|
||||||
<button class="btn cyan darken-1" type="submit" name="tag">{{ 'entry.list.add_tags'|trans }}</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -118,9 +116,9 @@
|
|||||||
<h4 class="center">{{ 'entry.filters.title'|trans }}</h4>
|
<h4 class="center">{{ 'entry.filters.title'|trans }}</h4>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% if current_route != 'untagged' and nbEntriesUntagged != 0 %}
|
{% if current_route != 'untagged' %}
|
||||||
<div class="col s12 center-align">
|
<div class="col s12 center-align">
|
||||||
<a href="{{ path('untagged') }}">{{ 'tag.list.see_untagged_entries'|trans }} ({{ nbEntriesUntagged }})</a>
|
<a href="{{ path('untagged') }}">{{ 'tag.list.see_untagged_entries'|trans }}</a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
<form name="tag" method="post" action="{{ path('new_tag', {'entry': entry.id}) }}">
|
<form class="tags-add-form" name="tag" method="post" action="{{ path('new_tag', {'entry': entry.id}) }}">
|
||||||
{% if form_errors(form) %}
|
{% if form_errors(form) %}
|
||||||
<span class="black-text">{{ form_errors(form) }}</span>
|
<span class="black-text">{{ form_errors(form) }}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -9,6 +9,6 @@
|
|||||||
|
|
||||||
{{ form_widget(form.label, {'attr': {'autocomplete': 'off'}}) }}
|
{{ form_widget(form.label, {'attr': {'autocomplete': 'off'}}) }}
|
||||||
|
|
||||||
{{ form_widget(form.add, {'attr': {'class': 'btn waves-effect waves-light hide-on-large-only'}}) }}
|
{{ form_widget(form.add, {'attr': {'class': 'btn waves-effect waves-light tags-add-form-submit'}}) }}
|
||||||
{{ form_widget(form._token) }}
|
{{ form_widget(form._token) }}
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@ -168,7 +168,7 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col m12 l8">
|
<div class="col m12 l8">
|
||||||
<p class="footer-text" title="{{ display_stats()|raw|striptags }}">
|
<p class="footer-text">
|
||||||
{{ display_stats() }}
|
{{ display_stats() }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -88,35 +88,32 @@ class WallabagExtension extends AbstractExtension implements GlobalsInterface
|
|||||||
|
|
||||||
switch ($type) {
|
switch ($type) {
|
||||||
case 'starred':
|
case 'starred':
|
||||||
$qb = $this->entryRepository->getBuilderForStarredByUser($user->getId());
|
$qb = $this->entryRepository->getCountBuilderForStarredByUser($user->getId());
|
||||||
break;
|
break;
|
||||||
case 'archive':
|
case 'archive':
|
||||||
$qb = $this->entryRepository->getBuilderForArchiveByUser($user->getId());
|
$qb = $this->entryRepository->getCountBuilderForArchiveByUser($user->getId());
|
||||||
break;
|
break;
|
||||||
case 'unread':
|
case 'unread':
|
||||||
$qb = $this->entryRepository->getBuilderForUnreadByUser($user->getId());
|
$qb = $this->entryRepository->getCountBuilderForUnreadByUser($user->getId());
|
||||||
break;
|
break;
|
||||||
case 'annotated':
|
case 'annotated':
|
||||||
$qb = $this->entryRepository->getBuilderForAnnotationsByUser($user->getId());
|
$qb = $this->entryRepository->getCountBuilderForAnnotationsByUser($user->getId());
|
||||||
break;
|
break;
|
||||||
case 'all':
|
case 'all':
|
||||||
$qb = $this->entryRepository->getBuilderForAllByUser($user->getId());
|
$qb = $this->entryRepository->getCountBuilderForAllByUser($user->getId());
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new \InvalidArgumentException(sprintf('Type "%s" is not implemented.', $type));
|
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
|
$query = $qb
|
||||||
->select('e.id')
|
->select('COUNT(e.id)')
|
||||||
->groupBy('e.id')
|
|
||||||
->getQuery();
|
->getQuery();
|
||||||
|
|
||||||
$query->useQueryCache(true);
|
$query->useQueryCache(true);
|
||||||
$query->enableResultCache($this->lifeTime);
|
$query->enableResultCache($this->lifeTime);
|
||||||
|
|
||||||
return \count($query->getArrayResult());
|
return $query->getSingleScalarResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -148,15 +145,14 @@ class WallabagExtension extends AbstractExtension implements GlobalsInterface
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
$query = $this->entryRepository->getBuilderForArchiveByUser($user->getId())
|
$query = $this->entryRepository->getCountBuilderForArchiveByUser($user->getId())
|
||||||
->select('e.id')
|
->select('COUNT(e.id)')
|
||||||
->groupBy('e.id')
|
|
||||||
->getQuery();
|
->getQuery();
|
||||||
|
|
||||||
$query->useQueryCache(true);
|
$query->useQueryCache(true);
|
||||||
$query->enableResultCache($this->lifeTime);
|
$query->enableResultCache($this->lifeTime);
|
||||||
|
|
||||||
$nbArchives = \count($query->getArrayResult());
|
$nbArchives = $query->getSingleScalarResult();
|
||||||
|
|
||||||
$interval = $user->getCreatedAt()->diff(new \DateTime('now'));
|
$interval = $user->getCreatedAt()->diff(new \DateTime('now'));
|
||||||
$nbDays = (int) $interval->format('%a') ?: 1;
|
$nbDays = (int) $interval->format('%a') ?: 1;
|
||||||
|
|||||||
@ -1174,14 +1174,13 @@ class ConfigControllerTest extends WallabagCoreTestCase
|
|||||||
$this->logInAs('admin');
|
$this->logInAs('admin');
|
||||||
$client = $this->getTestClient();
|
$client = $this->getTestClient();
|
||||||
|
|
||||||
$crawler = $client->request('GET', '/config/otp/email');
|
$crawler = $client->request('GET', '/config');
|
||||||
|
|
||||||
|
$form = $crawler->filter('form[name=config_otp_email]')->form();
|
||||||
|
$client->submit($form);
|
||||||
|
|
||||||
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
||||||
|
$this->assertStringContainsString('flashes.config.notice.otp_enabled', $client->getContainer()->get(SessionInterface::class)->getFlashBag()->get('notice')[0]);
|
||||||
$crawler = $client->followRedirect();
|
|
||||||
|
|
||||||
$this->assertGreaterThan(1, $alert = $crawler->filter('body')->extract(['_text']));
|
|
||||||
$this->assertStringContainsString('flashes.config.notice.otp_enabled', $alert[0]);
|
|
||||||
|
|
||||||
// restore user
|
// restore user
|
||||||
$em = $this->getEntityManager();
|
$em = $this->getEntityManager();
|
||||||
@ -1201,14 +1200,23 @@ class ConfigControllerTest extends WallabagCoreTestCase
|
|||||||
$this->logInAs('admin');
|
$this->logInAs('admin');
|
||||||
$client = $this->getTestClient();
|
$client = $this->getTestClient();
|
||||||
|
|
||||||
$crawler = $client->request('GET', '/config/otp/email/disable');
|
$em = $this->getEntityManager();
|
||||||
|
$user = $em
|
||||||
|
->getRepository(User::class)
|
||||||
|
->findOneByUsername('admin');
|
||||||
|
|
||||||
|
$user->setEmailTwoFactor(true);
|
||||||
|
$em->persist($user);
|
||||||
|
$em->flush();
|
||||||
|
|
||||||
|
$crawler = $client->request('GET', '/config');
|
||||||
|
|
||||||
|
$form = $crawler->filter('form[name=disable_otp_email]')->form();
|
||||||
|
$client->submit($form);
|
||||||
|
|
||||||
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
||||||
|
|
||||||
$crawler = $client->followRedirect();
|
$this->assertStringContainsString('flashes.config.notice.otp_disabled', $client->getContainer()->get(SessionInterface::class)->getFlashBag()->get('notice')[0]);
|
||||||
|
|
||||||
$this->assertGreaterThan(1, $alert = $crawler->filter('body')->extract(['_text']));
|
|
||||||
$this->assertStringContainsString('flashes.config.notice.otp_disabled', $alert[0]);
|
|
||||||
|
|
||||||
// restore user
|
// restore user
|
||||||
$em = $this->getEntityManager();
|
$em = $this->getEntityManager();
|
||||||
@ -1224,7 +1232,10 @@ class ConfigControllerTest extends WallabagCoreTestCase
|
|||||||
$this->logInAs('admin');
|
$this->logInAs('admin');
|
||||||
$client = $this->getTestClient();
|
$client = $this->getTestClient();
|
||||||
|
|
||||||
$crawler = $client->request('GET', '/config/otp/app');
|
$crawler = $client->request('GET', '/config');
|
||||||
|
|
||||||
|
$form = $crawler->filter('form[name=config_otp_app]')->form();
|
||||||
|
$client->submit($form);
|
||||||
|
|
||||||
$this->assertSame(200, $client->getResponse()->getStatusCode());
|
$this->assertSame(200, $client->getResponse()->getStatusCode());
|
||||||
|
|
||||||
@ -1243,49 +1254,28 @@ class ConfigControllerTest extends WallabagCoreTestCase
|
|||||||
$em->flush();
|
$em->flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testUserEnable2faGoogleCancel()
|
|
||||||
{
|
|
||||||
$this->logInAs('admin');
|
|
||||||
$client = $this->getTestClient();
|
|
||||||
|
|
||||||
$crawler = $client->request('GET', '/config/otp/app');
|
|
||||||
|
|
||||||
$this->assertSame(200, $client->getResponse()->getStatusCode());
|
|
||||||
|
|
||||||
// restore user
|
|
||||||
$em = $this->getEntityManager();
|
|
||||||
$user = $em
|
|
||||||
->getRepository(User::class)
|
|
||||||
->findOneByUsername('admin');
|
|
||||||
|
|
||||||
$this->assertTrue($user->isGoogleTwoFactor());
|
|
||||||
$this->assertGreaterThan(0, $user->getBackupCodes());
|
|
||||||
|
|
||||||
$crawler = $client->request('GET', '/config/otp/app/cancel');
|
|
||||||
|
|
||||||
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
|
||||||
|
|
||||||
$user = $em
|
|
||||||
->getRepository(User::class)
|
|
||||||
->findOneByUsername('admin');
|
|
||||||
|
|
||||||
$this->assertFalse($user->isGoogleTwoFactor());
|
|
||||||
$this->assertEmpty($user->getBackupCodes());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testUserDisable2faGoogle()
|
public function testUserDisable2faGoogle()
|
||||||
{
|
{
|
||||||
$this->logInAs('admin');
|
$this->logInAs('admin');
|
||||||
$client = $this->getTestClient();
|
$client = $this->getTestClient();
|
||||||
|
|
||||||
$crawler = $client->request('GET', '/config/otp/app/disable');
|
$em = $this->getEntityManager();
|
||||||
|
$user = $em
|
||||||
|
->getRepository(User::class)
|
||||||
|
->findOneByUsername('admin');
|
||||||
|
|
||||||
|
$user->setGoogleAuthenticatorSecret('Google2FA');
|
||||||
|
$em->persist($user);
|
||||||
|
$em->flush();
|
||||||
|
|
||||||
|
$crawler = $client->request('GET', '/config');
|
||||||
|
|
||||||
|
$form = $crawler->filter('form[name=disable_otp_app]')->form();
|
||||||
|
$client->submit($form);
|
||||||
|
|
||||||
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
$this->assertSame(302, $client->getResponse()->getStatusCode());
|
||||||
|
|
||||||
$crawler = $client->followRedirect();
|
$this->assertStringContainsString('flashes.config.notice.otp_disabled', $client->getContainer()->get(SessionInterface::class)->getFlashBag()->get('notice')[0]);
|
||||||
|
|
||||||
$this->assertGreaterThan(1, $alert = $crawler->filter('body')->extract(['_text']));
|
|
||||||
$this->assertStringContainsString('flashes.config.notice.otp_disabled', $alert[0]);
|
|
||||||
|
|
||||||
// restore user
|
// restore user
|
||||||
$em = $this->getEntityManager();
|
$em = $this->getEntityManager();
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user