Compare commits

..

98 Commits
2.3.3 ... 2.3.4

Author SHA1 Message Date
2555c0a40f Release wallabag 2.3.4 2018-11-24 21:20:28 +01:00
bffe65478d Merge pull request #3775 from wallabag/prepare-2.3.4
Prepare 2.3.4 release
2018-11-24 21:13:03 +01:00
5a4cbf5b70 Create a custom swap to avoid error 2018-11-23 22:11:10 +01:00
ea60d76e60 Use latest bridge to avoid PHPUnit break
Because branch 4.8 & 5.7 have been removed
2018-11-23 22:11:10 +01:00
d470f817ac Use new Travis infra 2018-11-23 22:11:04 +01:00
dc90eab32b Prepare 2.3.4 release 2018-11-23 08:34:13 +01:00
e01b00298a Merge pull request #3774 from Simounet/feature/card-ui
Card no preview replaced by wallabag logo
2018-11-21 20:56:09 +01:00
2daae77094 Card no preview replaced by wallabag logo 2018-11-21 18:51:44 +01:00
59a31ed07f Merge pull request #3772 from techexo/patch-1
Update translation when marking as read
2018-11-16 14:34:41 +01:00
060f3ce34c Update messages.en.yml 2018-11-16 14:16:38 +01:00
ccc3b0a531 Update messages.fr.yml 2018-11-16 01:45:16 +01:00
0ddfb2956c Update messages.fr.yml
Modification pour éclaircir le sens de l'option
2018-11-16 01:37:53 +01:00
b3bcc6aa06 Merge pull request #3769 from burkemw3/patch-4
Add rel=noopener to target=_blank links
2018-11-09 16:08:27 +01:00
a22b8043b4 Add rel=noopener to target=_blank links
From https://developers.google.com/web/tools/lighthouse/audits/noopener:
- new pages will run in separate processes, avoiding any performance
  intermingling impacts
- new pages won't have access to current page's window object via JavaScript
2018-11-07 20:34:54 -05:00
078d54411b Merge pull request #3766 from merwan/patch-1
Fix link to wallabag requirements in documentation
2018-11-06 05:20:28 +00:00
c7e9610b3f Fix link to wallabag requirements in documentation 2018-11-05 10:17:50 +01:00
3a69628007 Merge pull request #3764 from shtrom/fix-matches-notmatches-language
Fix documentation wording for matches/notmatches tagging rules
2018-11-01 12:04:55 +01:00
3f0dd0f0bc fixup! fixup! Fix documentation wording for matches/notmatches tagging rules 2018-11-01 19:40:17 +11:00
da7b8e256f fixup! Fix documentation wording for matches/notmatches tagging rules 2018-11-01 19:26:06 +11:00
0452e869ec Fix documentation wording for matches/notmatches tagging rules
Signed-off-by: Olivier Mehani <shtrom@ssji.net>
2018-10-31 19:56:48 +11:00
385dac6d31 Merge pull request #3763 from wallabag/ghpr-template-update
Add note on GitHub PR template to auto-close issues
2018-10-27 06:15:03 +00:00
96823682f9 misc: add note on GitHub PR template to auto-close issues
Signed-off-by: Kevin Decherf <kevin@kdecherf.com>
2018-10-26 23:50:55 +02:00
a6e4e83809 Merge pull request #3553 from wallabag/url-3529
Swap entry url with origin url if graby provides an updated one
2018-10-26 11:31:41 +02:00
1b220426e2 phpcs
Signed-off-by: Kevin Decherf <kevin@kdecherf.com>
2018-10-24 22:33:32 +02:00
6059967951 updateOriginUrl: remove 'query string' case from ignore list
Two urls with a different query string may refer to two different pages
so keep them both.

Signed-off-by: Kevin Decherf <kevin@kdecherf.com>
2018-10-24 22:27:27 +02:00
44e63667d9 updateOriginUrl: add comment blocks for the parse_url diff check
Signed-off-by: Kevin Decherf <kevin@kdecherf.com>
2018-10-24 22:13:03 +02:00
5ba5e22a09 updateOriginUrl: rewrite some if, resolving feedbacks from PR
Signed-off-by: Kevin Decherf <kevin@kdecherf.com>
2018-10-24 21:54:09 +02:00
b49c87acf1 ignoreOriginUrl: add initial support of ignore lists
Add the ability to specify hosts and patterns lists to ignore the given
entry url and replace it with the fetched content url without touching
to origin_url.

This initial support should be reworked in the following months to move
the hardcoded ignore lists in the database.

Signed-off-by: Kevin Decherf <kevin@kdecherf.com>
2018-10-22 23:42:09 +02:00
fc040c749d updateOriginUrl: add behavior when diff is fragment and query
Signed-off-by: Kevin Decherf <kevin@kdecherf.com>
2018-10-22 23:08:58 +02:00
e07fadea76 Refactor updateOriginUrl to include new behaviors behaviors
- Leave origin_url unchanged if difference is an ending slash
- Leave origin_url unchanged if difference is scheme
- Ignore (noop) if difference is query string or fragment

Signed-off-by: Kevin Decherf <kevin@kdecherf.com>
2018-10-22 23:01:16 +02:00
781864b954 ContentProxy: swap entry url to origin_url and set new url according to graby content
Closes #3529

Signed-off-by: Kevin Decherf <kevin@kdecherf.com>
2018-10-21 16:15:31 +02:00
4a81360efc ContentProxy: fix a corner case when entry.url is empty in updateEntry
Signed-off-by: Kevin Decherf <kevin@kdecherf.com>
2018-10-21 16:13:20 +02:00
ae4f7dceec Merge pull request #3733 from Deuchnord/root-abort-update
Abort running install and update script if root
2018-10-17 11:26:12 +02:00
c2bd272cf3 Added argument to ignore the warning. 2018-10-16 20:34:28 +02:00
0658ce4f11 Merge pull request #3745 from burkemw3/patch-3
Add placeholder image to card-based gallery entries page
2018-10-16 07:45:22 +00:00
cefbe6a87f Add placeholder image to card-based gallery entries page for #3651
I referenced https://css-tricks.com/using-svg/ for using the SVG icon.
The icon has a black foreground and white background, which doesn't
look very good as a placeholder. So, using the background-image style
to show the svg and the inversion filter to make it white on gray.

With the image always present, there isn't much difference between
the with and without image preview templates, so I merged them.

Tested on Chrome, Firefox, and Safari on OSX.
2018-10-15 23:59:39 -04:00
fbdc665487 Merge pull request #3606 from mritzmann/master
add manifest.json for android pwa
2018-10-15 08:30:26 +00:00
0ebf595c11 Merge pull request #3706 from shtrom/fix/gnu-make-bash
Makefile fixes for non GNU systems
2018-10-14 20:07:42 +02:00
ba69c04c27 Merge pull request #3743 from burkemw3/patch-1
Show tags on non-image gallery preview card
2018-10-13 12:00:00 +00:00
78ef840eda Merge pull request #3734 from davidmohamedfr/fix-dockerfile-php72
Fix dockerfile php72
2018-10-13 06:30:10 +00:00
d73bfdbddb Show tags on non-image gallery preview card
Tags and images aren't coupled, so they shouldn't be coupled in
the UI, either. This also makes the titles and source domains show
up consistently for image and non-image entry cards.
2018-10-11 19:47:48 -04:00
f5c862640b libpng12-dev was rename as libpng-dev 2018-10-06 14:15:59 +02:00
9e83c3bdf0 remove mcrypt since openssl is included in php 7.2
mcrypt extension doesn't exist anymore
2018-10-06 14:15:01 +02:00
23aa47d828 Abort running install and update script if root (closes #3590) 2018-10-06 12:23:40 +02:00
0f5c15d543 Merge pull request #3725 from Tobi823/master
Bugfix: Sanitize the title of a saved webpage from invalid UTF-8 characters.
2018-09-24 18:34:16 +02:00
28cc645b93 Run php-cs-fixer for fixing coding standard issues (on ContentProxyTest) 2018-09-23 23:42:05 +02:00
83f1c3274f Run php-cs-fixer for fixing coding standard issues 2018-09-23 22:20:43 +02:00
d64139d812 Make helper methods strToHex and hexToStr in ContentProxyTest.php private
to prevent misusage (from outside this class)
2018-09-21 13:31:28 +02:00
7a65c2017b Override the value of the given parameter ($title) with the (hopefully)
correct (to UTF-8) converted PDF title
2018-09-21 13:23:39 +02:00
c01d953292 Add tests for logic
Try to translate the title of a PDF from UTF-8 (then UTF-16BE, then WINDOWS-1252) to UTF-8
2018-09-21 13:15:00 +02:00
f80f16dfc8 Try to detect the character encoding in PDFs and try to translate
the title from the PDF to UTF-8
2018-09-21 13:15:00 +02:00
8648f0c005 Remove type declaration for PHP 5 compatibility 2018-09-21 13:15:00 +02:00
d76a5a6d60 Bugfix: Sanitize the title of a saved webpage from invalid UTF-8 characters 2018-09-21 13:15:00 +02:00
759c91940b Merge pull request #3726 from wallabag/fix-tests
Liberation goes https
2018-09-21 08:16:27 +00:00
84b3bdaac0 Liberation goes https 2018-09-21 09:07:40 +02:00
8013f35d96 Merge pull request #3719 from wallabag/fix-sort-parameters
Fix sort parameters
2018-09-07 17:02:39 +02:00
c0b65ad1c9 Updating node to 6.10
Same as defined in .travis.yml
2018-09-07 15:18:00 +02:00
4408ebd40a Fix tests 2018-09-07 15:18:00 +02:00
8d109df851 Jump to the new Scrutinizer PHP analysis engine 2018-09-07 15:18:00 +02:00
17476f4d8d Add missing parameters
Parameters weren’t passed to the sub function.
2018-09-07 13:56:14 +02:00
495f83c925 Merge pull request #3712 from wallabag/refactor-entry-queries
Rename getBuilderByUser and refactor query for untagged entries
2018-09-07 11:43:07 +00:00
eb5e3f1d1d Merge pull request #3717 from wallabag/php-72
PHP 7.2 shouldn't fail
2018-09-06 11:28:57 +02:00
04f85fd9a8 PHP 7.2 shouldn't fail 2018-09-06 07:58:26 +02:00
b8115ff46b php-cs-fixer
Signed-off-by: Kevin Decherf <kevin@kdecherf.com>
2018-09-05 18:44:08 +02:00
0636697289 EntryRepository: refactor getBuilderForUntaggedByUser
Improve SQL performance by replacing size(e.tags) with a left join and a
null condition

Move the QueryBuilder logic into getRawBuilderForUntaggedByUser

Signed-off-by: Kevin Decherf <kevin@kdecherf.com>
2018-09-05 18:44:08 +02:00
b7c5fda512 EntryRepository: refactor getBuilderByUser
We refactor getBuilderByUser to separate QueryBuilder getter and the
orderBy(). The previous code of getBuilderByUser() has been moved to
getSortedQueryBuilderByUser(). getBuildByUser() now returns a
QueryBuilder without the call to orderBy().

A new method named sortQueryBuilder() returns a given QueryBuilder with
an orderBy() call using given sort parameters.

Signed-off-by: Kevin Decherf <kevin@kdecherf.com>
2018-09-05 18:44:08 +02:00
69b563948d AnnotationRepository: rename getBuilderByUser
We rename getBuilderByUser to getSortedQueryBuilderByUser as long as the
method currently returns a QueryBuilder with an orderBy()

Signed-off-by: Kevin Decherf <kevin@kdecherf.com>
2018-09-05 18:44:08 +02:00
ada5d5b269 Merge pull request #3716 from wallabag/csfixer
php-cs-fixer: native_function_invocation
2018-09-05 16:38:20 +00:00
2a1ceb67b4 php-cs-fixer
Signed-off-by: Kevin Decherf <kevin@kdecherf.com>
2018-09-05 14:25:32 +02:00
685a5d745e Merge pull request #3707 from wallabag/fix-test-de-at
Removing failing test from Travis
2018-08-19 18:15:11 +02:00
72efc80fdc Removing failing test from Travis 2018-08-19 16:38:48 +02:00
fea06d19f3 Merge pull request #3705 from wallabag/update-release
Update release script
2018-08-19 09:17:40 +02:00
681e04b060 Makefile fixes for non GNU systems
* Move `Makefile` to `GNUmakefile`, which GNU `make` picks first
* Add `Makefile` so other `make`s forward to `gmake`
* Set the `SHELL` variable and let `make` handle the shell

Signed-off-by: Olivier Mehani <shtrom@ssji.net>
2018-08-18 18:50:18 +10:00
2bf568ff46 Update release script 2018-08-17 14:40:06 +02:00
1421236800 Merge pull request #3702 from aleksandar-todorovic/patch-1
Fixes a typo
2018-08-16 14:45:23 +02:00
e0a9791412 Fixes another typo 2018-08-10 20:28:48 +02:00
7fd4ad6564 Fixes a typo 2018-08-10 20:15:46 +02:00
d29b18e82e Merge pull request #3690 from Simounet/fix/srcset-attribute
More robust srcset image attribute handling
2018-07-23 16:24:20 +00:00
e6f12c0734 More robust srcset image attribute handling
Linked to HTMLawed PR https://github.com/kesar/HTMLawed/pull/17
2018-07-12 14:29:30 +02:00
0f36a88e16 Merge pull request #3692 from Simounet/feature/svg-logo
Feature/svg logo
2018-07-12 11:31:30 +00:00
e93f37206a wallabag logo (side-nav) replaced by an SVG one 2018-07-11 21:57:53 +02:00
4c78612eb4 wallabag logo with typo replaced by an SVG one 2018-07-11 21:51:52 +02:00
2fdd512488 Fake wallabag logo data image replaced by real SVG 2018-07-11 21:25:46 +02:00
9b12f822e1 Merge pull request #3691 from Simounet/feature/login-username-autofocus
Autofocus the username field on the login page
2018-07-11 18:49:38 +00:00
d02e6850c2 Autofocus the username field on the login page 2018-07-11 20:10:51 +02:00
6337b46e80 Merge pull request #3678 from anmol26s/patch-1
Propose YunoHost badge for installing
2018-07-06 05:40:03 +00:00
9216bab8c9 Merge pull request #3683 from Simounet/fix/iframe-video-width-shared
Fix mobile viewport on big iframe and video elements
2018-07-06 05:38:39 +00:00
9bc086071a Merge pull request #3686 from wallabag/remove-trusted-proxies
Remove remaining deprecation notices
2018-07-06 05:38:02 +00:00
80336f77fd The 'framework.trusted_proxies' configuration key has been deprecated in Symfony 3.3 2018-07-05 21:01:45 +02:00
200392b462 Merge pull request #3685 from wallabag/fix-random-failing-test
Replace SO url by lemonde.fr to avoid random failing test
2018-07-05 13:37:28 +00:00
9c48053b14 Replace SO url by lemonde
Looks like we got a lot of random failing while grabing SO content, replacing it might fix the problem.
2018-07-05 14:50:27 +02:00
18167b9a24 Merge pull request #3684 from Simounet/fix/empty-image-download-error
Fix image downloading on null image path
2018-07-05 12:15:50 +00:00
3fbbe0d9f1 Fix image downloading on null image path 2018-07-05 11:40:51 +02:00
6cf7f21dcb Fix mobile viewport on big iframe and video elements 2018-06-28 15:22:51 +02:00
8d09f57c0d Propose YunoHost badge for installing
The YunoHost have working Wallabag package with working install,remove,update,backup and restore scripts. YunoHost makes self -hosting easy for the end users. Please add the this badge so that people can find this solution.
2018-06-22 02:43:43 +05:30
ed148f6685 add 512x512 appicon 2018-03-20 08:43:46 +01:00
10f31ac572 change href link for manifest.json 2018-03-19 08:32:33 +01:00
35ce4d5e5f add manifest.json for android pwa 2018-03-18 19:58:34 +01:00
116 changed files with 1420 additions and 1143 deletions

View File

@ -8,9 +8,15 @@
| Documentation | yes/no
| Translation | yes/no
| CHANGELOG.md | yes/no
| Fixed tickets | #...
| License | MIT
<!--
Please list the issues your PR fixes using special keywords, see
https://help.github.com/articles/closing-issues-using-keywords/
Fixes #…
-->
<!--
- Please fill in this template according to the PR you're about to submit.
- Replace this comment by a description of what your PR is solving.

View File

@ -26,3 +26,18 @@ tools:
checks:
php:
code_rating: true
# use the new PHP analysis engine
# https://scrutinizer-ci.com/docs/tools/php/php-analyzer/guides/migrate_to_new_php_analysis
build:
nodes:
analysis:
tests:
override:
- php-scrutinizer-run
dependencies:
override:
- npm install -g 'yarn'
- yarn install --force
- COMPOSER_MEMORY_LIMIT=-1 composer install -o --no-interaction --no-progress --prefer-dist

View File

@ -1,17 +1,14 @@
language: php
services:
- rabbitmq
- redis
# faster builds on docker-container setup
sudo: false
- rabbitmq
- redis
# used for HHVM
addons:
apt:
packages:
- tidy
apt:
packages:
- tidy
# cache vendor dirs
cache:
@ -44,7 +41,6 @@ matrix:
- php: 7.0
env: CS_FIXER=run VALIDATE_TRANSLATION_FILE=run ASSETS=build DB=sqlite
allow_failures:
- php: 7.2
- php: nightly
# exclude v1 branches
@ -52,6 +48,14 @@ branches:
except:
- legacy
before_install:
- if [[ $TRAVIS_REPO_SLUG = wallabag/wallabag ]]; then cp .composer-auth.json ~/.composer/auth.json; fi;
install:
- if [[ $ASSETS = build ]]; then source ~/.nvm/nvm.sh && nvm install 6.10; fi;
- if [[ $ASSETS = build ]]; then npm install -g yarn@latest; fi;
- if [[ $ASSETS = build ]]; then yarn install; fi;
before_script:
- PHP=$TRAVIS_PHP_VERSION
- if [[ ! $PHP = hhvm* ]]; then echo "memory_limit=-1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini; fi;
@ -59,17 +63,17 @@ before_script:
- if [[ ! $PHP = hhvm* ]]; then phpenv config-rm xdebug.ini || echo "xdebug not available"; fi
- composer self-update --no-progress
- if [[ $DB = pgsql ]]; then psql -c 'create database wallabag_test;' -U postgres; fi;
install:
- if [[ $ASSETS = build ]]; then source ~/.nvm/nvm.sh && nvm install 6.10; fi;
- if [[ $ASSETS = build ]]; then npm install -g yarn@latest; fi;
- if [[ $ASSETS = build ]]; then yarn install; fi;
before_install:
- if [[ $TRAVIS_REPO_SLUG = wallabag/wallabag ]]; then cp .composer-auth.json ~/.composer/auth.json; fi;
# increase swap to avoid "proc_open(): fork failed - Cannot allocate memory"
# this should be removed when no more PHP 5 build will be defined
- sudo swapon -s
- sudo fallocate -l 4G /swapfile
- sudo chmod 600 /swapfile
- sudo mkswap /swapfile
- sudo swapon /swapfile
- sudo swapon -s
script:
- travis_wait composer update --no-interaction --no-progress
- travis_wait bash composer update -o --no-interaction --no-progress --prefer-dist
- echo "travis_fold:start:prepare"
- make prepare DB=$DB
- echo "travis_fold:end:prepare"

View File

@ -1,5 +1,42 @@
# Changelog
## [2.3.4](https://github.com/wallabag/wallabag/tree/2.3.4)
[Full Changelog](https://github.com/wallabag/wallabag/compare/2.3.3...2.3.4)
### Fixes
- Fix image downloading on null image path [#3684](https://github.com/wallabag/wallabag/pull/3684)
- Remove remaining deprecation notices [#3686](https://github.com/wallabag/wallabag/pull/3686)
- Fix mobile viewport on big iframe and video elements [#3683](https://github.com/wallabag/wallabag/pull/3683)
- Autofocus the username field on the login page [#3691](https://github.com/wallabag/wallabag/pull/3691)
- Feature/svg logo [#3692](https://github.com/wallabag/wallabag/pull/3692)
- Fixes a typo [#3702](https://github.com/wallabag/wallabag/pull/3702)
- Update release script [#3705](https://github.com/wallabag/wallabag/pull/3705)
- Removing failing test from Travis [#3707](https://github.com/wallabag/wallabag/pull/3707)
- Replace SO url by lemonde.fr to avoid random failing test [#3685](https://github.com/wallabag/wallabag/pull/3685)
- php-cs-fixer: native_function_invocation [#3716](https://github.com/wallabag/wallabag/pull/3716)
- PHP 7.2 shouldn't fail [#3717](https://github.com/wallabag/wallabag/pull/3717)
- Liberation goes https [#3726](https://github.com/wallabag/wallabag/pull/3726)
- Bugfix: Sanitize the title of a saved webpage from invalid UTF-8 characters. [#3725](https://github.com/wallabag/wallabag/pull/3725)
- Fix dockerfile php72 [#3734](https://github.com/wallabag/wallabag/pull/3734)
- Fix sort parameters [#3719](https://github.com/wallabag/wallabag/pull/3719)
- Add note on GitHub PR template to auto-close issues [#3763](https://github.com/wallabag/wallabag/pull/3763)
- Fix link to wallabag requirements in documentation [#3766](https://github.com/wallabag/wallabag/pull/3766)
- Update translation when marking as read [#3772](https://github.com/wallabag/wallabag/pull/3772)
- Makefile fixes for non GNU systems [#3706](https://github.com/wallabag/wallabag/pull/3706)
- Card no preview replaced by wallabag logo [#3774](https://github.com/wallabag/wallabag/pull/3774)
### Changes
- Propose YunoHost badge for installing [#3678](https://github.com/wallabag/wallabag/pull/3678)
- More robust srcset image attribute handling [#3690](https://github.com/wallabag/wallabag/pull/3690)
- Rename getBuilderByUser and refactor query for untagged entries [#3712](https://github.com/wallabag/wallabag/pull/3712)
- Show tags on non-image gallery preview card [#3743](https://github.com/wallabag/wallabag/pull/3743)
- add manifest.json for android pwa [#3606](https://github.com/wallabag/wallabag/pull/3606)
- Add placeholder image to card-based gallery entries page [#3745](https://github.com/wallabag/wallabag/pull/3745)
- Abort running install and update script if root [#3733](https://github.com/wallabag/wallabag/pull/3733)
- Swap entry url with origin url if graby provides an updated one [#3553](https://github.com/wallabag/wallabag/pull/3553)
## [2.3.3](https://github.com/wallabag/wallabag/tree/2.3.3)
[Full Changelog](https://github.com/wallabag/wallabag/compare/2.3.2...2.3.3)

53
GNUmakefile Executable file
View File

@ -0,0 +1,53 @@
SHELL=bash
TMP_FOLDER=/tmp
RELEASE_FOLDER=wllbg-release
ENV ?= prod
help: ## Display this help menu
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
clean: ## Clear the application cache
rm -rf var/cache/*
install: ## Install wallabag with the latest version
@./scripts/install.sh $(ENV)
update: ## Update the wallabag installation to the latest version
@./scripts/update.sh $(ENV)
dev: ## Install the latest dev version
@./scripts/dev.sh
run: ## Run the wallabag built-in server
@php bin/console server:run --env=dev
build: ## Run webpack
@npm run build:$(ENV)
prepare: clean ## Prepare database for testsuite
ifdef DB
cp app/config/tests/parameters_test.$(DB).yml app/config/parameters_test.yml
endif
-php bin/console doctrine:database:drop --force --env=test
php bin/console doctrine:database:create --env=test
php bin/console doctrine:migrations:migrate --no-interaction --env=test
fixtures: ## Load fixtures into database
php bin/console doctrine:fixtures:load --no-interaction --env=test
test: prepare fixtures ## Launch wallabag testsuite
bin/simple-phpunit -v
release: ## Create a package. Need a VERSION parameter (eg: `make release VERSION=master`).
ifndef VERSION
$(error VERSION is not set)
endif
@./scripts/release.sh $(VERSION) $(TMP_FOLDER) $(RELEASE_FOLDER) $(ENV)
deploy: ## Deploy wallabag
@bundle exec cap staging deploy
.PHONY: help clean prepare install fixtures update build test release deploy run dev
.DEFAULT_GOAL := install

54
Makefile Executable file → Normal file
View File

@ -1,52 +1,2 @@
TMP_FOLDER=/tmp
RELEASE_FOLDER=wllbg-release
ENV ?= prod
help: ## Display this help menu
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
clean: ## Clear the application cache
rm -rf var/cache/*
install: ## Install wallabag with the latest version
@sh scripts/install.sh $(ENV)
update: ## Update the wallabag installation to the latest version
@sh scripts/update.sh $(ENV)
dev: ## Install the latest dev version
@sh scripts/dev.sh
run: ## Run the wallabag built-in server
@php bin/console server:run --env=dev
build: ## Run webpack
@npm run build:$(ENV)
prepare: clean ## Prepare database for testsuite
ifdef DB
cp app/config/tests/parameters_test.$(DB).yml app/config/parameters_test.yml
endif
-php bin/console doctrine:database:drop --force --env=test
php bin/console doctrine:database:create --env=test
php bin/console doctrine:migrations:migrate --no-interaction --env=test
fixtures: ## Load fixtures into database
php bin/console doctrine:fixtures:load --no-interaction --env=test
test: prepare fixtures ## Launch wallabag testsuite
bin/simple-phpunit -v
release: ## Create a package. Need a VERSION parameter (eg: `make release VERSION=master`).
ifndef VERSION
$(error VERSION is not set)
endif
@sh scripts/release.sh $(VERSION) $(TMP_FOLDER) $(RELEASE_FOLDER) $(ENV)
deploy: ## Deploy wallabag
@bundle exec cap staging deploy
.PHONY: help clean prepare install fixtures update build test release deploy run dev
.DEFAULT_GOAL := install
.DEFAULT:
gmake $@

View File

@ -13,7 +13,7 @@ If you do not have your own server, consider [the wallabag.it hosting solution](
![wallabag logo](https://raw.githubusercontent.com/wallabag/logo/master/_default/typo-horizontal/png/sm/logo-typo-horizontal-black-no-bg-no-border-sm.png)
# Install wallabag
Please read [the documentation to see the wallabag requirements](http://doc.wallabag.org/en/master/user/installation.html#requirements).
Please read [the documentation to see the wallabag requirements](https://doc.wallabag.org/en/admin/installation/requirements.html).
Then you can install wallabag by executing the following commands:
@ -24,6 +24,11 @@ cd wallabag && make install
Now, [configure a virtual host](https://doc.wallabag.org/en/admin/installation/virtualhosts.html) to use your wallabag.
# Run on YunoHost
[![Install Wallabag with YunoHost](https://install-app.yunohost.org/install-with-yunohost.png)](https://install-app.yunohost.org/?app=wallabag2)
Wallabag app for [YunoHost](https://yunohost.org). See [here](https://github.com/YunoHost-Apps/wallabag2_ynh)
# License
Copyright © 2013-2018 Nicolas Lœuillet <nicolas@loeuillet.org>
This work is free. You can redistribute it and/or modify it under the

View File

@ -55,7 +55,7 @@ class Version20170719231144 extends WallabagMigration
}
// Just in case...
if (count($ids) > 0) {
if (\count($ids) > 0) {
// Merge tags
$this->addSql('
UPDATE ' . $this->getTable('entry_tag') . '

View File

@ -27,7 +27,7 @@ export: "export"
import: "import"
misc: "misc"
modify_settings: "apply"
piwik_host: Host of your website in Piwik (without http:// ou https://)
piwik_host: Host of your website in Piwik (without http:// or https://)
piwik_site_id: ID of your website in Piwik
piwik_enabled: Enable Piwik
demo_mode_enabled: "Enable demo mode? (Only used for the public wallabag demo)"
@ -37,4 +37,4 @@ download_images_enabled: Download images locally
restricted_access: Enable authentication for paywalled websites
api_user_registration: Enable user to be registered using the API
store_article_headers: Enable if wallabag stores HTTP headers for each article
shaarli_share_origin_url: Enable sharing origin url to Shaarli, if the service is enabled
shaarli_share_origin_url: Enable sharing origin url to Shaarli, if the service is enabled

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path fill="none" d="M0 0h200v200H0z"/><path d="M75.899 72.438c1.597-.981 10.207-5.556 24.098.178 14.289 5.897 23.154.776 24.253.079-3.454-5.678-7.562-10.62-12.104-13.943.303-.083.612-.167.939-.263 6.023-1.742 7.553-6.842 7.875-11.21.364-4.954.616-5.03 1.692-9.487 1.032-4.281-.119-5.137-1.181-4.273-.572.465-5.552 1.616-8.505 3.919-4.768 3.72-7.707 10.794-9.039 14.706-.025.06-.205.604-.265.792-.621 1.498-1.857 1.494-1.857 1.494v.001c-.6-.065-1.202-.1-1.809-.1-.54 0-1.079.029-1.616.081-.012.002-.019 0-.031.001-1.581.233-2.45-1.697-2.632-2.157-1.847-5.304-6.816-15.763-17.984-18.577 0 0-2.028-1.554-1.41 1.074.588 2.511 1.804 5.049 1.534 8.741-.124 1.704-1.181 10.442 6.85 14.99.763.432 1.441.795 2.051 1.101-4.042 3.235-7.716 7.74-10.859 12.853zM128.626 152.353c-9.842-6.098-13.153-8.242-12.946-10.575 0 0 .002-.379.099-.957.239-1.236.995-3.348 3.407-4.552.079-.039.146-.084.208-.129 7.668-4.45 13.27-11.614 15.246-20.56-1.99 4.941-16.735 8.78-34.645 8.78-17.903 0-32.651-3.839-34.641-8.78.442 2.008 1.073 3.923 1.864 5.742.666 3.745 1.562 12.563-2.673 20.282-3.731 6.8-22.15 16.069-49.485 10.748 0 0-1.096-.766-1.428-.136-.491.932 1.517 1.685 3.583 2.229 19.031 5.04 47.756 2.989 56.777-4.443 4.116-3.388 5.704-7.953 6.107-12.865l.003.008s.11-1.287 1.719-.32c.461.277 2.125 1.36 2.39 2.585.232 1.743.248 3.883-.652 5.382-1.287 2.144-1.301 2.452.393 3.662 1.04.742 5.287 3.864 11.198 7.415.015.01.023.019.038.027 1.25.753 2.987 2.597 2.987 2.597 2.662 3.079 8.452 9.275 10.972 8.108 1.19-.551-.051-3.032-.051-3.032s1.98 2.571 3.043 1.694c.809-.668-.473-3.229-.473-3.229s1.729 1.499 2.757.944c1.258-.679-.187-4.614-10.079-10.627-9.896-6.018-12.578-6.94-12.814-9.626 0 0-.004-.135.004-.366.077-.593.414-1.847 1.852-1.712 2.141.346 4.348.531 6.608.531 2.587 0 5.107-.237 7.536-.69l.001.003s.127-.025.164-.031c.284-.036.838-.018.84.671-.09.873-.331 1.751-.845 2.519-1.447 2.168-.972 2.466.54 3.859.933.859 5.211 4.622 11.07 8.264.012.009.017.016.031.023 1.249.752 3.41 2.816 3.41 2.816v-.001c2.428 2.466 6.894 6.596 9.327 6.347 1.646-.168.306-3.002.306-3.002s2.078 2.006 3.099 1.416c1.142-.659-.474-2.755-.474-2.755s1.338.708 2.283.473c.948-.236 1.185-2.644-8.656-8.737z"/><path d="M117.631 83.452c-1.181 0-2.161.355-2.912 1.057-.76.71-1.144 1.531-1.144 2.438v16.056c0 2.154-.382 3.742-1.135 4.721-.728.946-1.892 1.406-3.556 1.406-1.703 0-2.863-.457-3.549-1.396-.716-.979-1.078-2.571-1.078-4.731V86.884c0-1.098-.5-1.996-1.448-2.596-1.289-.812-2.57-1.105-4.129-.587-.476.159-.924.366-1.333.615-.435.265-.802.597-1.093.985-.322.432-.486.901-.486 1.396v16.307c0 2.158-.363 3.75-1.079 4.73-.688.939-1.849 1.396-3.548 1.396-1.705 0-2.877-.459-3.584-1.401-.734-.979-1.107-2.57-1.107-4.726V86.947c0-.908-.384-1.728-1.145-2.438-.751-.702-1.751-1.057-2.973-1.057-1.258 0-2.296.352-3.085 1.045-.811.71-1.222 1.535-1.222 2.45v15.806c0 1.988.194 3.869.575 5.588.393 1.758 1.077 3.3 2.035 4.586.968 1.299 2.282 2.323 3.906 3.05 1.607.716 3.617 1.079 5.975 1.079 2.457 0 4.515-.455 6.115-1.354 1.342-.754 2.473-1.744 3.371-2.951.866 1.207 1.971 2.197 3.294 2.95 1.58.899 3.669 1.354 6.211 1.354 2.357 0 4.359-.364 5.947-1.081 1.601-.726 2.902-1.751 3.872-3.048.96-1.29 1.645-2.833 2.034-4.586.381-1.719.575-3.6.575-5.588V86.947c0-.911-.398-1.733-1.184-2.445-.767-.697-1.818-1.05-3.12-1.05z"/></svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@ -221,6 +221,12 @@ ol li:last-child {
padding-bottom: 0;
}
iframe,
video {
max-width: 100%;
height: auto;
}
mark {
padding: 0 0.2em;
}

View File

@ -118,9 +118,14 @@ main {
.card-image .preview,
.card-fullimage .preview {
height: 14em;
height: 100%;
background: no-repeat 50%/cover;
background-color: #efefef;
display: block;
&--default {
background-size: contain;
}
}
&.sw {

View File

@ -24,6 +24,10 @@ main,
height: 100%;
}
.typo-logo {
max-width: 150px;
}
#main {
flex: 1 0 auto;

View File

@ -26,7 +26,6 @@ framework:
engines: ['twig']
default_locale: "%locale%"
trusted_hosts: ~
trusted_proxies: ~
session:
# handler_id set to null will use default session handler from php.ini
handler_id: session.handler.native_file

View File

@ -1,5 +1,5 @@
wallabag_core:
version: 2.3.3
version: 2.3.4
paypal_url: "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9UBA65LG3FX9Y&lc=gb"
languages:
en: 'English'

View File

@ -91,7 +91,7 @@
"doctrine/doctrine-fixtures-bundle": "~2.2",
"doctrine/data-fixtures": "~1.1",
"sensio/generator-bundle": "^3.0",
"symfony/phpunit-bridge": "^3.3",
"symfony/phpunit-bridge": "3.4.x-dev",
"friendsofphp/php-cs-fixer": "~2.0",
"m6web/redis-mock": "^2.0",
"dama/doctrine-test-bundle": "^4.0"

586
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -4,10 +4,10 @@ FROM php:fpm
ARG timezone='Europe/Paris'
RUN apt-get update && apt-get install -y \
libmcrypt-dev libicu-dev libpq-dev libxml2-dev libpng12-dev libjpeg-dev \
libmcrypt-dev libicu-dev libpq-dev libxml2-dev libpng-dev libjpeg-dev \
&& /usr/local/bin/docker-php-ext-configure gd --with-jpeg-dir=/usr/include \
&& docker-php-ext-install \
iconv mcrypt mbstring intl pdo pdo_mysql pdo_pgsql gd
iconv mbstring intl pdo pdo_mysql pdo_pgsql gd
RUN echo "date.timezone="$timezone > /usr/local/etc/php/conf.d/date_timezone.ini

View File

@ -1,13 +1,13 @@
{
"name": "wallabag",
"version": "2.2.2",
"version": "2.3.3",
"description": "wallabag is a self hostable application for saving web pages",
"private": true,
"directories": {
"doc": "docs"
},
"engines": {
"node": ">4.8"
"node": ">=6.10"
},
"repository": {
"type": "git",

0
scripts/dev.sh Normal file → Executable file
View File

14
scripts/install.sh Normal file → Executable file
View File

@ -2,6 +2,20 @@
# You can execute this file to install wallabag
# eg: `sh install.sh prod`
IGNORE_ROOT_ARG="--ignore-root-warning"
IGNORE_ROOT=0
if [ "$1" == "$IGNORE_ROOT_ARG" ]; then
IGNORE_ROOT=1
fi
# Abort running this script if root
if [ "$IGNORE_ROOT" -eq 0 ] && [ "$EUID" == "0" ]; then
echo "Do not run this script as root!" >&2
echo "Use $IGNORE_ROOT_ARG to ignore this error." >&2
exit 1
fi
COMPOSER_COMMAND='composer'
DIR="${BASH_SOURCE}"

8
scripts/release.sh Normal file → Executable file
View File

@ -1,6 +1,6 @@
#! /usr/bin/env bash
# You can execute this file to create a new package for wallabag
# eg: `sh release.sh master /tmp wllbg-release prod`
# eg: `sh release.sh 2.3.3 /tmp wllbg-release prod`
VERSION=$1
TMP_FOLDER=$2
@ -10,11 +10,11 @@ ENV=$4
rm -rf $TMP_FOLDER/$RELEASE_FOLDER
mkdir $TMP_FOLDER/$RELEASE_FOLDER
git clone git@github.com:wallabag/wallabag.git -b $VERSION $TMP_FOLDER/$RELEASE_FOLDER/$VERSION
cd $TMP_FOLDER/$RELEASE_FOLDER/$VERSION && SYMFONY_ENV=$ENV composer up -n --no-dev
cd $TMP_FOLDER/$RELEASE_FOLDER/$VERSION && SYMFONY_ENV=$ENV COMPOSER_MEMORY_LIMIT=-1 composer up -n --no-dev
cd $TMP_FOLDER/$RELEASE_FOLDER/$VERSION && php bin/console wallabag:install --env=$ENV
cd $TMP_FOLDER/$RELEASE_FOLDER/$VERSION && php bin/console assets:install --env=$ENV --symlink --relative
cd $TMP_FOLDER/$RELEASE_FOLDER && tar czf wallabag-$VERSION.tar.gz --exclude="var/cache/*" --exclude="var/logs/*" --exclude="var/sessions/*" --exclude=".git" $VERSION
echo "MD5 checksum of the package for wallabag $VERSION"
md5 $TMP_FOLDER/$RELEASE_FOLDER/wallabag-$VERSION.tar.gz
scp $TMP_FOLDER/$RELEASE_FOLDER/wallabag-$VERSION.tar.gz framasoft_bag@78.46.248.87:/var/www/framabag.org/web
rm -rf $TMP_FOLDER/$RELEASE_FOLDER
echo "Package to upload to the release server:"
echo $TMP_FOLDER/$RELEASE_FOLDER/wallabag-$VERSION.tar.gz

0
scripts/require.sh Normal file → Executable file
View File

14
scripts/update.sh Normal file → Executable file
View File

@ -2,6 +2,20 @@
# You can execute this file to update wallabag
# eg: `sh update.sh prod`
IGNORE_ROOT_ARG="--ignore-root-warning"
IGNORE_ROOT=0
if [ "$1" == "$IGNORE_ROOT_ARG" ]; then
IGNORE_ROOT=1
fi
# Abort running this script if root
if [ "$IGNORE_ROOT" -eq 0 ] && [ "$EUID" == "0" ]; then
echo "Do not run this script as root!" >&2
echo "Use $IGNORE_ROOT_ARG to ignore this error." >&2
exit 1
fi
set -e
set -u

View File

@ -28,7 +28,7 @@ class WallabagAnnotationController extends FOSRestController
->getDoctrine()
->getRepository('WallabagAnnotationBundle:Annotation')
->findAnnotationsByPageId($entry->getId(), $this->getUser()->getId());
$total = count($annotationRows);
$total = \count($annotationRows);
$annotations = ['total' => $total, 'rows' => $annotationRows];
$json = $this->get('jms_serializer')->serialize($annotations, 'json');

View File

@ -21,7 +21,7 @@ class AnnotationRepository extends EntityRepository
public function getBuilderForAllByUser($userId)
{
return $this
->getBuilderByUser($userId)
->getSortedQueryBuilderByUser($userId)
;
}
@ -133,7 +133,7 @@ class AnnotationRepository extends EntityRepository
*
* @return QueryBuilder
*/
private function getBuilderByUser($userId)
private function getSortedQueryBuilderByUser($userId)
{
return $this->createQueryBuilder('a')
->leftJoin('a.user', 'u')

View File

@ -102,7 +102,7 @@ class EntryRestController extends WallabagRestController
$order = $request->query->get('order', 'desc');
$page = (int) $request->query->get('page', 1);
$perPage = (int) $request->query->get('perPage', 30);
$tags = is_array($request->query->get('tags')) ? '' : (string) $request->query->get('tags', '');
$tags = \is_array($request->query->get('tags')) ? '' : (string) $request->query->get('tags', '');
$since = $request->query->get('since', 0);
/** @var \Pagerfanta\Pagerfanta $pager */
@ -253,7 +253,7 @@ class EntryRestController extends WallabagRestController
$limit = $this->container->getParameter('wallabag_core.api_limit_mass_actions');
if (count($urls) > $limit) {
if (\count($urls) > $limit) {
throw new HttpException(400, 'API limit reached');
}
@ -347,7 +347,7 @@ class EntryRestController extends WallabagRestController
'open_graph' => [
'og_image' => !empty($data['picture']) ? $data['picture'] : $entry->getPreviewPicture(),
],
'authors' => is_string($data['authors']) ? explode(',', $data['authors']) : $entry->getPublishedBy(),
'authors' => \is_string($data['authors']) ? explode(',', $data['authors']) : $entry->getPublishedBy(),
]
);
} catch (\Exception $e) {
@ -461,7 +461,7 @@ class EntryRestController extends WallabagRestController
$contentProxy->updateLanguage($entry, $data['language']);
}
if (!empty($data['authors']) && is_string($data['authors'])) {
if (!empty($data['authors']) && \is_string($data['authors'])) {
$entry->setPublishedBy(explode(',', $data['authors']));
}

View File

@ -138,14 +138,14 @@ class TagRestController extends WallabagRestController
*/
private function cleanOrphanTag($tags)
{
if (!is_array($tags)) {
if (!\is_array($tags)) {
$tags = [$tags];
}
$em = $this->getDoctrine()->getManager();
foreach ($tags as $tag) {
if (0 === count($tag->getEntries())) {
if (0 === \count($tag->getEntries())) {
$em->remove($tag);
}
}

View File

@ -51,7 +51,7 @@ class CleanDuplicatesCommand extends ContainerAwareCommand
} else {
$users = $this->getContainer()->get('wallabag_user.user_repository')->findAll();
$this->io->text(sprintf('Cleaning through <info>%d</info> user accounts', count($users)));
$this->io->text(sprintf('Cleaning through <info>%d</info> user accounts', \count($users)));
foreach ($users as $user) {
$this->io->text(sprintf('Processing user <info>%s</info>', $user->getUsername()));
@ -79,7 +79,7 @@ class CleanDuplicatesCommand extends ContainerAwareCommand
$url = $this->similarUrl($entry['url']);
/* @var $entry Entry */
if (in_array($url, $urls, true)) {
if (\in_array($url, $urls, true)) {
++$duplicatesCount;
$em->remove($repo->find($entry['id']));
@ -96,8 +96,8 @@ class CleanDuplicatesCommand extends ContainerAwareCommand
private function similarUrl($url)
{
if (in_array(substr($url, -1), ['/', '#'], true)) { // get rid of "/" and "#" and the end of urls
return substr($url, 0, strlen($url));
if (\in_array(substr($url, -1), ['/', '#'], true)) { // get rid of "/" and "#" and the end of urls
return substr($url, 0, \strlen($url));
}
return $url;

View File

@ -47,7 +47,7 @@ class ExportCommand extends ContainerAwareCommand
->getQuery()
->getResult();
$io->text(sprintf('Exporting <info>%d</info> entrie(s) for user <info>%s</info>...', count($entries), $user->getUserName()));
$io->text(sprintf('Exporting <info>%d</info> entrie(s) for user <info>%s</info>...', \count($entries), $user->getUserName()));
$filePath = $input->getArgument('filepath');

View File

@ -81,7 +81,7 @@ class InstallCommand extends ContainerAwareCommand
$status = '<info>OK!</info>';
$help = '';
if (!extension_loaded($this->getContainer()->getParameter('database_driver'))) {
if (!\extension_loaded($this->getContainer()->getParameter('database_driver'))) {
$fulfilled = false;
$status = '<error>ERROR!</error>';
$help = 'Database driver "' . $this->getContainer()->getParameter('database_driver') . '" is not installed.';
@ -146,7 +146,7 @@ class InstallCommand extends ContainerAwareCommand
$status = '<info>OK!</info>';
$help = '';
if (!function_exists($functionRequired)) {
if (!\function_exists($functionRequired)) {
$fulfilled = false;
$status = '<error>ERROR!</error>';
$help = 'You need the ' . $functionRequired . ' function activated';
@ -371,7 +371,7 @@ class InstallCommand extends ContainerAwareCommand
}
try {
return in_array($databaseName, $schemaManager->listDatabases(), true);
return \in_array($databaseName, $schemaManager->listDatabases(), true);
} catch (\Doctrine\DBAL\Exception\DriverException $e) {
// it means we weren't able to get database list, assume the database doesn't exist
@ -389,6 +389,6 @@ class InstallCommand extends ContainerAwareCommand
{
$schemaManager = $this->getContainer()->get('doctrine')->getManager()->getConnection()->getSchemaManager();
return count($schemaManager->listTableNames()) > 0 ? true : false;
return \count($schemaManager->listTableNames()) > 0 ? true : false;
}
}

View File

@ -50,7 +50,7 @@ class ListUserCommand extends ContainerAwareCommand
$io->success(
sprintf(
'%s/%s%s user(s) displayed.',
count($users),
\count($users),
$nbUsers,
null === $input->getArgument('search') ? '' : ' (filtered)'
)

View File

@ -43,7 +43,7 @@ class ReloadEntryCommand extends ContainerAwareCommand
$entryRepository = $this->getContainer()->get('wallabag_core.entry_repository');
$entryIds = $entryRepository->findAllEntriesIdByUserId($userId);
$nbEntries = count($entryIds);
$nbEntries = \count($entryIds);
if (!$nbEntries) {
$io->success('No entry to reload.');

View File

@ -348,7 +348,7 @@ class ConfigController extends Controller
$em = $this->getDoctrine()->getManager();
foreach ($tags as $tag) {
if (0 === count($tag->getEntries())) {
if (0 === \count($tag->getEntries())) {
$em->remove($tag);
}
}

View File

@ -65,7 +65,7 @@ class TagController extends Controller
$em->flush();
// remove orphan tag in case no entries are associated to it
if (0 === count($tag->getEntries())) {
if (0 === \count($tag->getEntries())) {
$em->remove($tag);
$em->flush();
}

View File

@ -33,7 +33,7 @@ class EntryFilterType extends AbstractType
$this->user = $tokenStorage->getToken() ? $tokenStorage->getToken()->getUser() : null;
if (null === $this->user || !is_object($this->user)) {
if (null === $this->user || !\is_object($this->user)) {
return;
}
}
@ -96,7 +96,7 @@ class EntryFilterType extends AbstractType
->add('domainName', TextFilterType::class, [
'apply_filter' => function (QueryInterface $filterQuery, $field, $values) {
$value = $values['value'];
if (strlen($value) <= 2 || empty($value)) {
if (\strlen($value) <= 2 || empty($value)) {
return;
}
$expression = $filterQuery->getExpr()->like($field, $filterQuery->getExpr()->lower($filterQuery->getExpr()->literal('%' . $value . '%')));

View File

@ -107,7 +107,7 @@ class GrabySiteConfigBuilder implements SiteConfigBuilder
*/
protected function processExtraFields($extraFieldsStrings)
{
if (!is_array($extraFieldsStrings)) {
if (!\is_array($extraFieldsStrings)) {
return [];
}

View File

@ -53,6 +53,7 @@ class ContentProxy
if ((empty($content) || false === $this->validateContent($content)) && false === $disableContentUpdate) {
$fetchedContent = $this->graby->fetchContent($url);
$fetchedContent['title'] = $this->sanitizeContentTitle($fetchedContent['title'], $fetchedContent['content_type']);
// when content is imported, we have information in $content
// in case fetching content goes bad, we'll keep the imported information instead of overriding them
@ -65,6 +66,13 @@ class ContentProxy
// so we'll be able to refetch it in the future
$content['url'] = !empty($content['url']) ? $content['url'] : $url;
// In one case (at least in tests), url is empty here
// so we set it using $url provided in the updateEntry call.
// Not sure what are the other possible cases where this property is empty
if (empty($entry->getUrl()) && !empty($url)) {
$entry->setUrl($url);
}
$this->stockEntry($entry, $content);
}
@ -85,7 +93,7 @@ class ContentProxy
(new LocaleConstraint())
);
if (0 === count($errors)) {
if (0 === \count($errors)) {
$entry->setLanguage($value);
return;
@ -107,7 +115,7 @@ class ContentProxy
(new UrlConstraint())
);
if (0 === count($errors)) {
if (0 === \count($errors)) {
$entry->setPreviewPicture($value);
return;
@ -176,6 +184,59 @@ class ContentProxy
$entry->setTitle($path);
}
/**
* Try to sanitize the title of the fetched content from wrong character encodings and invalid UTF-8 character.
*
* @param $title
* @param $contentType
*
* @return string
*/
private function sanitizeContentTitle($title, $contentType)
{
if ('application/pdf' === $contentType) {
$title = $this->convertPdfEncodingToUTF8($title);
}
return $this->sanitizeUTF8Text($title);
}
/**
* If the title from the fetched content comes from a PDF, then its very possible that the character encoding is not
* UTF-8. This methods tries to identify the character encoding and translate the title to UTF-8.
*
* @param $title
*
* @return string (maybe contains invalid UTF-8 character)
*/
private function convertPdfEncodingToUTF8($title)
{
// first try UTF-8 because its easier to detect its present/absence
foreach (['UTF-8', 'UTF-16BE', 'WINDOWS-1252'] as $encoding) {
if (mb_check_encoding($title, $encoding)) {
return mb_convert_encoding($title, 'UTF-8', $encoding);
}
}
return $title;
}
/**
* Remove invalid UTF-8 characters from the given string.
*
* @param string $rawText
*
* @return string
*/
private function sanitizeUTF8Text($rawText)
{
if (mb_check_encoding($rawText, 'UTF-8')) {
return $rawText;
}
return iconv('UTF-8', 'UTF-8//IGNORE', $rawText);
}
/**
* Stock entry with fetched or imported content.
* Will fall back to OpenGraph data if available.
@ -185,7 +246,7 @@ class ContentProxy
*/
private function stockEntry(Entry $entry, array $content)
{
$entry->setUrl($content['url']);
$this->updateOriginUrl($entry, $content['url']);
$this->setEntryDomainName($entry);
@ -212,7 +273,7 @@ class ContentProxy
$entry->setHttpStatus($content['status']);
}
if (!empty($content['authors']) && is_array($content['authors'])) {
if (!empty($content['authors']) && \is_array($content['authors'])) {
$entry->setPublishedBy($content['authors']);
}
@ -233,7 +294,7 @@ class ContentProxy
}
// if content is an image, define it as a preview too
if (!empty($content['content_type']) && in_array($this->mimeGuesser->guess($content['content_type']), ['jpeg', 'jpg', 'gif', 'png'], true)) {
if (!empty($content['content_type']) && \in_array($this->mimeGuesser->guess($content['content_type']), ['jpeg', 'jpg', 'gif', 'png'], true)) {
$this->updatePreviewPicture($entry, $content['url']);
}
@ -251,6 +312,115 @@ class ContentProxy
}
}
/**
* Update the origin_url field when a redirection occurs
* This field is set if it is empty and new url does not match ignore list.
*
* @param Entry $entry
* @param string $url
*/
private function updateOriginUrl(Entry $entry, $url)
{
if (empty($url) || $entry->getUrl() === $url) {
return false;
}
$parsed_entry_url = parse_url($entry->getUrl());
$parsed_content_url = parse_url($url);
/**
* The following part computes the list of part changes between two
* parse_url arrays.
*
* As array_diff_assoc only computes changes to go from the left array
* to the right one, we make two differents arrays to have both
* directions. We merge these two arrays and sort keys before passing
* the result to the switch.
*
* The resulting array gives us all changing parts between the two
* urls: scheme, host, path, query and/or fragment.
*/
$diff_ec = array_diff_assoc($parsed_entry_url, $parsed_content_url);
$diff_ce = array_diff_assoc($parsed_content_url, $parsed_entry_url);
$diff = array_merge($diff_ec, $diff_ce);
$diff_keys = array_keys($diff);
sort($diff_keys);
if ($this->ignoreUrl($entry->getUrl())) {
$entry->setUrl($url);
return false;
}
/**
* This switch case lets us apply different behaviors according to
* changing parts of urls.
*
* As $diff_keys is an array, we provide arrays as cases. ['path'] means
* 'only the path is different between the two urls' whereas
* ['fragment', 'query'] means 'only fragment and query string parts are
* different between the two urls'.
*
* Note that values in $diff_keys are sorted.
*/
switch ($diff_keys) {
case ['path']:
if (($parsed_entry_url['path'] . '/' === $parsed_content_url['path']) // diff is trailing slash, we only replace the url of the entry
|| ($url === urldecode($entry->getUrl()))) { // we update entry url if new url is a decoded version of it, see EntryRepository#findByUrlAndUserId
$entry->setUrl($url);
}
break;
case ['scheme']:
$entry->setUrl($url);
break;
case ['fragment']:
// noop
break;
default:
if (empty($entry->getOriginUrl())) {
$entry->setOriginUrl($entry->getUrl());
}
$entry->setUrl($url);
break;
}
}
/**
* Check entry url against an ignore list to replace with content url.
*
* XXX: move the ignore list in the database to let users handle it
*
* @param string $url url to test
*
* @return bool true if url matches ignore list otherwise false
*/
private function ignoreUrl($url)
{
$ignored_hosts = ['feedproxy.google.com', 'feeds.reuters.com'];
$ignored_patterns = ['https?://www\.lemonde\.fr/tiny.*'];
$parsed_url = parse_url($url);
$filtered = array_filter($ignored_hosts, function ($var) use ($parsed_url) {
return $var === $parsed_url['host'];
});
if ([] !== $filtered) {
return true;
}
$filtered = array_filter($ignored_patterns, function ($var) use ($url) {
return preg_match("`$var`i", $url);
});
if ([] !== $filtered) {
return true;
}
return false;
}
/**
* Validate that the given content has at least a title, an html and a url.
*

View File

@ -81,6 +81,6 @@ class CryptoProxy
*/
private function mask($value)
{
return strlen($value) > 0 ? $value[0] . '*****' . $value[strlen($value) - 1] : 'Empty value';
return \strlen($value) > 0 ? $value[0] . '*****' . $value[\strlen($value) - 1] : 'Empty value';
}
}

View File

@ -85,6 +85,10 @@ class DownloadImages
*/
public function processSingleImage($entryId, $imagePath, $url, $relativePath = null)
{
if (null === $imagePath) {
return false;
}
if (null === $relativePath) {
$relativePath = $this->getRelativePath($entryId);
}
@ -181,7 +185,7 @@ class DownloadImages
*
* @return array An array of urls
*/
protected function getSrcsetUrls(Crawler $imagesCrawler)
private function getSrcsetUrls(Crawler $imagesCrawler)
{
$urls = [];
$iterator = $imagesCrawler
@ -189,9 +193,14 @@ class DownloadImages
while ($iterator->valid()) {
$srcsetAttribute = $iterator->current()->getAttribute('srcset');
if ('' !== $srcsetAttribute) {
$srcset = array_map('trim', explode(',', $srcsetAttribute));
// Couldn't start with " OR ' OR a white space
// Could be one or more white space
// Must be one or more digits followed by w OR x
$pattern = "/(?:[^\"'\s]+\s*(?:\d+[wx])+)/";
preg_match_all($pattern, $srcsetAttribute, $matches);
$srcset = \call_user_func_array('array_merge', $matches);
$srcsetUrls = array_map(function ($src) {
return explode(' ', $src)[0];
return trim(explode(' ', $src, 2)[0]);
}, $srcset);
$urls = array_merge($srcsetUrls, $urls);
}
@ -299,7 +308,7 @@ class DownloadImages
$this->logger->debug('DownloadImages: Checking extension (alternative)', ['ext' => $ext]);
}
if (!in_array($ext, ['jpeg', 'jpg', 'gif', 'png'], true)) {
if (!\in_array($ext, ['jpeg', 'jpg', 'gif', 'png'], true)) {
$this->logger->error('DownloadImages: Processed image with not allowed extension. Skipping: ' . $imagePath);
return false;

View File

@ -45,7 +45,7 @@ class EntriesExport
*/
public function setEntries($entries)
{
if (!is_array($entries)) {
if (!\is_array($entries)) {
$this->language = $entries->getLanguage();
$entries = [$entries];
}
@ -325,7 +325,7 @@ class EntriesExport
{
$delimiter = ';';
$enclosure = '"';
$handle = fopen('php://memory', 'rb+');
$handle = fopen('php://memory', 'b+r');
fputcsv($handle, ['Title', 'URL', 'Content', 'Tags', 'MIME Type', 'Language', 'Creation date'], $delimiter, $enclosure);

View File

@ -31,7 +31,7 @@ class PreparePagerForEntries
$user = $this->tokenStorage->getToken() ? $this->tokenStorage->getToken()->getUser() : null;
}
if (null === $user || !is_object($user)) {
if (null === $user || !\is_object($user)) {
return;
}

View File

@ -31,7 +31,7 @@ class Redirect
{
$user = $this->tokenStorage->getToken() ? $this->tokenStorage->getToken()->getUser() : null;
if (null === $user || !is_object($user)) {
if (null === $user || !\is_object($user)) {
return $url;
}

View File

@ -32,7 +32,7 @@ class TagsAssigner
{
$tagsEntities = [];
if (!is_array($tags)) {
if (!\is_array($tags)) {
$tags = explode(',', $tags);
}
@ -48,7 +48,7 @@ class TagsAssigner
$label = trim(mb_convert_case($label, MB_CASE_LOWER));
// avoid empty tag
if (0 === strlen($label)) {
if (0 === \strlen($label)) {
continue;
}

View File

@ -36,7 +36,7 @@ class UsernameRssTokenConverter implements ParamConverterInterface
{
// If there is no manager, this means that only Doctrine DBAL is configured
// In this case we can do nothing and just return
if (null === $this->registry || !count($this->registry->getManagers())) {
if (null === $this->registry || !\count($this->registry->getManagers())) {
return false;
}

View File

@ -21,7 +21,7 @@ class EntryRepository extends EntityRepository
public function getBuilderForAllByUser($userId)
{
return $this
->getBuilderByUser($userId)
->getSortedQueryBuilderByUser($userId)
;
}
@ -35,7 +35,7 @@ class EntryRepository extends EntityRepository
public function getBuilderForUnreadByUser($userId)
{
return $this
->getBuilderByUser($userId)
->getSortedQueryBuilderByUser($userId)
->andWhere('e.isArchived = false')
;
}
@ -50,7 +50,7 @@ class EntryRepository extends EntityRepository
public function getBuilderForArchiveByUser($userId)
{
return $this
->getBuilderByUser($userId)
->getSortedQueryBuilderByUser($userId)
->andWhere('e.isArchived = true')
;
}
@ -65,7 +65,7 @@ class EntryRepository extends EntityRepository
public function getBuilderForStarredByUser($userId)
{
return $this
->getBuilderByUser($userId, 'starredAt', 'desc')
->getSortedQueryBuilderByUser($userId, 'starredAt', 'desc')
->andWhere('e.isStarred = true')
;
}
@ -82,7 +82,7 @@ class EntryRepository extends EntityRepository
public function getBuilderForSearchByUser($userId, $term, $currentRoute)
{
$qb = $this
->getBuilderByUser($userId);
->getSortedQueryBuilderByUser($userId);
if ('starred' === $currentRoute) {
$qb->andWhere('e.isStarred = true');
@ -102,7 +102,7 @@ class EntryRepository extends EntityRepository
}
/**
* Retrieves untagged entries for a user.
* Retrieve a sorted list of untagged entries for a user.
*
* @param int $userId
*
@ -111,8 +111,21 @@ class EntryRepository extends EntityRepository
public function getBuilderForUntaggedByUser($userId)
{
return $this
->getBuilderByUser($userId)
->andWhere('size(e.tags) = 0');
->sortQueryBuilder($this->getRawBuilderForUntaggedByUser($userId));
}
/**
* Retrieve untagged entries for a user.
*
* @param int $userId
*
* @return QueryBuilder
*/
public function getRawBuilderForUntaggedByUser($userId)
{
return $this->getQueryBuilderByUser($userId)
->leftJoin('e.tags', 't')
->andWhere('t.id is null');
}
/**
@ -151,7 +164,7 @@ class EntryRepository extends EntityRepository
$qb->andWhere('e.updatedAt > :since')->setParameter('since', new \DateTime(date('Y-m-d H:i:s', $since)));
}
if (is_string($tags) && '' !== $tags) {
if (\is_string($tags) && '' !== $tags) {
foreach (explode(',', $tags) as $i => $tag) {
$entryAlias = 'e' . $i;
$tagAlias = 't' . $i;
@ -260,7 +273,7 @@ class EntryRepository extends EntityRepository
*/
public function removeTag($userId, Tag $tag)
{
$entries = $this->getBuilderByUser($userId)
$entries = $this->getSortedQueryBuilderByUser($userId)
->innerJoin('e.tags', 't')
->andWhere('t.id = :tagId')->setParameter('tagId', $tag->getId())
->getQuery()
@ -296,7 +309,7 @@ class EntryRepository extends EntityRepository
*/
public function findAllByTagId($userId, $tagId)
{
return $this->getBuilderByUser($userId)
return $this->getSortedQueryBuilderByUser($userId)
->innerJoin('e.tags', 't')
->andWhere('t.id = :tagId')->setParameter('tagId', $tagId)
->getQuery()
@ -320,7 +333,7 @@ class EntryRepository extends EntityRepository
->getQuery()
->getResult();
if (count($res)) {
if (\count($res)) {
return current($res);
}
@ -414,7 +427,20 @@ class EntryRepository extends EntityRepository
}
/**
* Return a query builder to used by other getBuilderFor* method.
* Return a query builder to be used by other getBuilderFor* method.
*
* @param int $userId
*
* @return QueryBuilder
*/
private function getQueryBuilderByUser($userId)
{
return $this->createQueryBuilder('e')
->andWhere('e.user = :userId')->setParameter('userId', $userId);
}
/**
* Return a sorted query builder to be used by other getBuilderFor* method.
*
* @param int $userId
* @param string $sortBy
@ -422,10 +448,23 @@ class EntryRepository extends EntityRepository
*
* @return QueryBuilder
*/
private function getBuilderByUser($userId, $sortBy = 'createdAt', $direction = 'desc')
private function getSortedQueryBuilderByUser($userId, $sortBy = 'createdAt', $direction = 'desc')
{
return $this->createQueryBuilder('e')
->andWhere('e.user = :userId')->setParameter('userId', $userId)
return $this->sortQueryBuilder($this->getQueryBuilderByUser($userId), $sortBy, $direction);
}
/**
* Return the given QueryBuilder with an orderBy() call.
*
* @param QueryBuilder $qb
* @param string $sortBy
* @param string $direction
*
* @return QueryBuilder
*/
private function sortQueryBuilder(QueryBuilder $qb, $sortBy = 'createdAt', $direction = 'desc')
{
return $qb
->orderBy(sprintf('e.%s', $sortBy), $direction);
}
}

View File

@ -30,7 +30,7 @@ class TagRepository extends EntityRepository
$query->setResultCacheLifetime($cacheLifeTime);
}
return count($query->getArrayResult());
return \count($query->getArrayResult());
}
/**

View File

@ -157,8 +157,8 @@ config:
# not_equal_to: 'Not equal to...'
# or: 'One rule OR another'
# and: 'One rule AND another'
# matches: 'Tests that a <i>subject</i> is matches a <i>search</i> (case-insensitive).<br />Example: <code>title matches "football"</code>'
# notmatches: 'Tests that a <i>subject</i> is not matches a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>'
# matches: 'Tests that a <i>subject</i> matches a <i>search</i> (case-insensitive).<br />Example: <code>title matches "football"</code>'
# notmatches: 'Tests that a <i>subject</i> doesn''t match match a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>'
entry:
# default_title: 'Title of the entry'

View File

@ -72,9 +72,9 @@ config:
300_word: 'I read ~300 words per minute'
400_word: 'I read ~400 words per minute'
action_mark_as_read:
label: 'Where do you want to be redirected to after marking an article as read?'
redirect_homepage: 'To the homepage'
redirect_current_page: 'To the current page'
label: 'What to do after removing, starring or marking as read an article?'
redirect_homepage: 'Go to the homepage'
redirect_current_page: 'Stay on the current page'
pocket_consumer_key_label: Consumer key for Pocket to import contents
android_configuration: Configure your Android application
android_instruction: "Touch here to prefill your Android application"
@ -116,7 +116,7 @@ config:
archived: Remove ALL archived entries
confirm: Are you really sure? (THIS CAN'T BE UNDONE)
form_password:
description: "You can change your password here. Your new password should by at least 8 characters long."
description: "You can change your password here. Your new password should be at least 8 characters long."
old_password_label: 'Current password'
new_password_label: 'New password'
repeat_new_password_label: 'Repeat new password'
@ -157,8 +157,8 @@ config:
not_equal_to: 'Not equal to...'
or: 'One rule OR another'
and: 'One rule AND another'
matches: 'Tests that a <i>subject</i> is matches a <i>search</i> (case-insensitive).<br />Example: <code>title matches "football"</code>'
notmatches: 'Tests that a <i>subject</i> is not matches a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>'
matches: 'Tests that a <i>subject</i> matches a <i>search</i> (case-insensitive).<br />Example: <code>title matches "football"</code>'
notmatches: 'Tests that a <i>subject</i> doesn''t match match a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>'
entry:
default_title: 'Title of the entry'

View File

@ -158,7 +158,7 @@ config:
or: 'Una regla U otra'
and: 'Una regla Y la otra'
matches: 'Prueba si un <i>sujeto</i> corresponde a una <i>búsqueda</i> (insensible a mayusculas).<br />Ejemplo : <code>title matches "fútbol"</code>'
# notmatches: 'Tests that a <i>subject</i> is not matches a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>'
# notmatches: 'Tests that a <i>subject</i> doesn''t match match a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>'
entry:
default_title: 'Título del artículo'

View File

@ -157,8 +157,8 @@ config:
# not_equal_to: 'Not equal to...'
# or: 'One rule OR another'
# and: 'One rule AND another'
# matches: 'Tests that a <i>subject</i> is matches a <i>search</i> (case-insensitive).<br />Example: <code>title matches "football"</code>'
# notmatches: 'Tests that a <i>subject</i> is not matches a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>'
# matches: 'Tests that a <i>subject</i> matches a <i>search</i> (case-insensitive).<br />Example: <code>title matches "football"</code>'
# notmatches: 'Tests that a <i>subject</i> doesn''t match match a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>'
entry:
# default_title: 'Title of the entry'

View File

@ -72,9 +72,9 @@ config:
300_word: "Je lis environ 300 mots par minute"
400_word: "Je lis environ 400 mots par minute"
action_mark_as_read:
label: "Où souhaitez-vous être redirigé après avoir marqué un article comme lu ?"
redirect_homepage: "À la page daccueil"
redirect_current_page: "À la page courante"
label: "Que faire lorsqu'un article est supprimé, marqué comme lu ou marqué comme favoris ?"
redirect_homepage: "Retourner à la page daccueil"
redirect_current_page: "Rester sur la page actuelle"
pocket_consumer_key_label: "Clé dauthentification Pocket pour importer les données"
android_configuration: "Configurez votre application Android"
android_instruction: "Appuyez ici pour préremplir votre application Android"

View File

@ -158,7 +158,7 @@ config:
or: "Una regola O un'altra"
and: "Una regola E un'altra"
matches: 'Verifica che un <i>oggetto</i> risulti in una <i>ricerca</i> (case-insensitive).<br />Esempio: <code>titolo contiene "football"</code>'
# notmatches: 'Tests that a <i>subject</i> is not matches a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>'
# notmatches: 'Tests that a <i>subject</i> doesn''t match match a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>'
entry:
default_title: "Titolo del contenuto"

View File

@ -158,7 +158,7 @@ config:
or: 'Uma regra OU outra'
and: 'Uma regra E outra'
matches: 'Testa que um <i>assunto</i> corresponde a uma <i>pesquisa</i> (maiúscula ou minúscula).<br />Exemplo: <code>título corresponde a "futebol"</code>'
# notmatches: 'Tests that a <i>subject</i> is not matches a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>'
# notmatches: 'Tests that a <i>subject</i> doesn''t match match a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>'
entry:
default_title: 'Título da entrada'

View File

@ -157,8 +157,8 @@ config:
# not_equal_to: 'Not equal to...'
# or: 'One rule OR another'
# and: 'One rule AND another'
# matches: 'Tests that a <i>subject</i> is matches a <i>search</i> (case-insensitive).<br />Example: <code>title matches "football"</code>'
# notmatches: 'Tests that a <i>subject</i> is not matches a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>'
# matches: 'Tests that a <i>subject</i> matches a <i>search</i> (case-insensitive).<br />Example: <code>title matches "football"</code>'
# notmatches: 'Tests that a <i>subject</i> doesn''t match match a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>'
entry:
# default_title: 'Title of the entry'

View File

@ -157,8 +157,8 @@ config:
not_equal_to: 'Eşit değildir…'
or: 'Bir kural veya birbaşkası'
and: 'Bir kural ve diğeri'
# matches: 'Tests that a <i>subject</i> is matches a <i>search</i> (case-insensitive).<br />Example: <code>title matches "football"</code>'
# notmatches: 'Tests that a <i>subject</i> is not matches a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>'
# matches: 'Tests that a <i>subject</i> matches a <i>search</i> (case-insensitive).<br />Example: <code>title matches "football"</code>'
# notmatches: 'Tests that a <i>subject</i> doesn''t match match a <i>search</i> (case-insensitive).<br />Example: <code>title notmatches "football"</code>'
entry:
default_title: 'Makalenin başlığı'

View File

@ -38,6 +38,8 @@
<link rel="shortcut icon" type="image/x-icon" href="{{ asset('wallassets/themes/_global/img/appicon/favicon.ico') }}">
<link rel="manifest" href="{{ asset('manifest.json') }}">
{% block css %}
{% endblock %}
{% block scripts %}

View File

@ -57,7 +57,7 @@
</div>
<ul class="tools links">
<li><a href="{{ entry.url|e }}" target="_blank" title="{{ 'entry.list.original_article'|trans }} : {{ entry.title|e }}"><span>{{ entry.domainName|removeWww }}</span></a></li>
<li><a href="{{ entry.url|e }}" target="_blank" rel="noopener" title="{{ 'entry.list.original_article'|trans }} : {{ entry.title|e }}"><span>{{ entry.domainName|removeWww }}</span></a></li>
<li><a title="{{ 'entry.list.toogle_as_read'|trans }}" class="tool icon {% if entry.isArchived == 0 %}archive-off{% else %}archive{% endif %}" href="{{ path('archive_entry', { 'id': entry.id }) }}"><i class="material-icons md-24 vertical-align-middle">check</i><span>{{ 'entry.list.toogle_as_read'|trans }}</span></a></li>
<li><a title="{{ 'entry.list.toogle_as_star'|trans }}" class="tool icon {% if entry.isStarred == 0 %}fav-off{% else %}fav{% endif %}" href="{{ path('star_entry', { 'id': entry.id }) }}"><i class="material-icons md-24 vertical-align-middle">star_rate</i><span>{{ 'entry.list.toogle_as_star'|trans }}</span></a></li>
<li><a title="{{ 'entry.list.delete'|trans }}" class="tool icon" onclick="return confirm('{{ 'entry.confirm.delete'|trans|escape('js') }}')" href="{{ path('delete_entry', { 'id': entry.id }) }}"><i class="material-icons md-24 vertical-align-middle">delete</i><span>{{ 'entry.list.delete'|trans }}</span></a></li>

View File

@ -11,7 +11,7 @@
<div id="article_toolbar">
<ul class="links">
<li class="topPosF"><a href="#top" title="{{ 'entry.view.left_menu.back_to_top'|trans }}" class="tool top icon icon-arrow-up-thick"><span>{{ 'entry.view.left_menu.set_as_read'|trans }}</span></a></li>
<li><a href="{{ entry.url|e }}" target="_blank" title="{{ 'entry.view.original_article'|trans }} : {{ entry.title|e }}" class="tool link icon icon-link original"><span>{{ entry.domainName|removeWww }}</span></a></li>
<li><a href="{{ entry.url|e }}" target="_blank" rel="noopener" title="{{ 'entry.view.original_article'|trans }} : {{ entry.title|e }}" class="tool link icon icon-link original"><span>{{ entry.domainName|removeWww }}</span></a></li>
<li><a title="{{ 'entry.view.left_menu.re_fetch_content'|trans }}" class="tool icon icon-reload" href="{{ path('reload_entry', { 'id': entry.id }) }}"><span>{{ 'entry.view.left_menu.re_fetch_content'|trans }}</span></a></li>
{% set markAsReadLabel = 'entry.view.left_menu.set_as_unread' %}
@ -27,13 +27,13 @@
<li><a href="{{ path('share', {'id': entry.id }) }}" target="_blank" class="tool icon icon-eye" title="{{ 'entry.view.left_menu.public_link'|trans }}"><span>{{ 'entry.view.left_menu.public_link'|trans }}</span></a></li>
<li><a href="{{ path('delete_share', {'id': entry.id }) }}" class="tool icon icon-no-eye" title="{{ 'entry.view.left_menu.delete_public_link'|trans }}"><span>{{ 'entry.view.left_menu.delete_public_link'|trans }}</span></a></li>
{% endif %}
{% if craue_setting('share_twitter') %}<li><a href="https://twitter.com/home?status={{entry.title|url_encode}}%20{{ entry.url|url_encode }}%20via%20@wallabagapp" target="_blank" class="tool twitter icon icon-twitter" title="Tweet"><span>Tweet</span></a></li>{% endif %}
{% if craue_setting('share_twitter') %}<li><a href="https://twitter.com/home?status={{entry.title|url_encode}}%20{{ entry.url|url_encode }}%20via%20@wallabagapp" target="_blank" rel="noopener" class="tool twitter icon icon-twitter" title="Tweet"><span>Tweet</span></a></li>{% endif %}
{% if craue_setting('share_mail') %}<li><a href="mailto:?subject={{ entry.title|url_encode }}&amp;body={{ entry.url|url_encode }}%20via%20@wallabagapp" class="tool email icon icon-mail" title="Email"><span>Email</span></a></li>{% endif %}
{% if craue_setting('share_shaarli') %}<li><a href="{{ craue_setting('shaarli_url') }}/index.php?post={{ entry.url|url_encode }}&amp;title={{ entry.title|url_encode }}&amp;tags={{ entry.tags|join(',')|url_encode }}{% if craue_setting('shaarli_share_origin_url') %}&amp;original_url={{ entry.originUrl|url_encode }}{% endif %}" target="_blank" class="tool icon-image icon-image--shaarli" title="shaarli"><span>shaarli</span></a></li>{% endif %}
{% if craue_setting('share_scuttle') %}<li><a href="{{ craue_setting('scuttle_url') }}/bookmarks.php?action=add&amp;address={{ entry.url|url_encode }}&amp;title={{ entry.title|url_encode }}&amp;tags={{ entry.tags|join(',')|url_encode }}" target="_blank" class="tool icon-image icon-image--scuttle" title="scuttle"><span>scuttle</span></a></li>{% endif %}
{% if craue_setting('share_diaspora') %}<li><a href="{{ craue_setting('diaspora_url') }}/bookmarklet?url={{ entry.url|url_encode }}&title={{ entry.title|url_encode }}&notes=&v=1&noui=1&jump=doclose" target="_blank" class="tool diaspora icon-image icon-image--diaspora" title="diaspora"><span>diaspora</span></a></li>{% endif %}
{% if craue_setting('share_unmark') %}<li><a href="{{ craue_setting('unmark_url') }}/mark/add?url={{ entry.url|url_encode }}&amp;title={{entry.title|url_encode}}&amp;v=6" target="_blank" class="tool unmark icon-image icon-image--unmark" title="unmark"><span>unmark.it</span></a></li>{% endif %}
{% if craue_setting('carrot') %}<li><a href="https://secure.carrot.org/GiveAndGetBack.do?url={{ entry.url|url_encode }}&title={{ entry.title|url_encode }}" class="tool carrot icon-image icon-image--carrot" target="_blank" title="carrot"><span>Carrot</span></a></li>{% endif %}
{% if craue_setting('share_shaarli') %}<li><a href="{{ craue_setting('shaarli_url') }}/index.php?post={{ entry.url|url_encode }}&amp;title={{ entry.title|url_encode }}&amp;tags={{ entry.tags|join(',')|url_encode }}{% if craue_setting('shaarli_share_origin_url') %}&amp;original_url={{ entry.originUrl|url_encode }}{% endif %}" target="_blank" rel="noopener" class="tool icon-image icon-image--shaarli" title="shaarli"><span>shaarli</span></a></li>{% endif %}
{% if craue_setting('share_scuttle') %}<li><a href="{{ craue_setting('scuttle_url') }}/bookmarks.php?action=add&amp;address={{ entry.url|url_encode }}&amp;title={{ entry.title|url_encode }}&amp;tags={{ entry.tags|join(',')|url_encode }}" target="_blank" rel="noopener" class="tool icon-image icon-image--scuttle" title="scuttle"><span>scuttle</span></a></li>{% endif %}
{% if craue_setting('share_diaspora') %}<li><a href="{{ craue_setting('diaspora_url') }}/bookmarklet?url={{ entry.url|url_encode }}&title={{ entry.title|url_encode }}&notes=&v=1&noui=1&jump=doclose" target="_blank" rel="noopener" class="tool diaspora icon-image icon-image--diaspora" title="diaspora"><span>diaspora</span></a></li>{% endif %}
{% if craue_setting('share_unmark') %}<li><a href="{{ craue_setting('unmark_url') }}/mark/add?url={{ entry.url|url_encode }}&amp;title={{entry.title|url_encode}}&amp;v=6" target="_blank" rel="noopener" class="tool unmark icon-image icon-image--unmark" title="unmark"><span>unmark.it</span></a></li>{% endif %}
{% if craue_setting('carrot') %}<li><a href="https://secure.carrot.org/GiveAndGetBack.do?url={{ entry.url|url_encode }}&title={{ entry.title|url_encode }}" class="tool carrot icon-image icon-image--carrot" target="_blank" rel="noopener" title="carrot"><span>Carrot</span></a></li>{% endif %}
{% if craue_setting('show_printlink') %}<li><a title="{{ 'entry.view.left_menu.print'|trans }}" class="tool icon icon-print" href="javascript: window.print();"><span>{{ 'entry.view.left_menu.print'|trans }}</span></a></li>{% endif %}
{% if craue_setting('export_epub') %}<li><a href="{{ path('export_entry', { 'id': entry.id, 'format': 'epub' }) }}" title="Generate ePub file">EPUB</a></li>{% endif %}
{% if craue_setting('export_mobi') %}<li><a href="{{ path('export_entry', { 'id': entry.id, 'format': 'mobi' }) }}" title="Generate Mobi file">MOBI</a></li>{% endif %}
@ -74,7 +74,7 @@
{% if entry.originUrl is not empty %}
<i class="material-icons" title="{{ 'entry.view.provided_by'|trans }}">launch</i>
<a href="{{ entry.originUrl|e }}" target="_blank" class="tool">
<a href="{{ entry.originUrl|e }}" target="_blank" rel="noopener" class="tool">
{{ entry.originUrl|striptags|removeSchemeAndWww|truncate(32) }}
</a>
{% endif %}

View File

@ -7,7 +7,7 @@
<meta property="og:title" content="{{ entry.title|e|raw }}" />
<meta property="og:type" content="article" />
<meta property="og:url" content="{{ app.request.uri }}" />
{% set picturePath = app.request.schemeAndHttpHost ~ asset('wallassets/themes/_global/img/logo-other_themes.png') %}
{% set picturePath = app.request.schemeAndHttpHost ~ asset('wallassets/themes/_global/img/logo-wallabag.svg') %}
{% if entry.previewPicture is not null %}
{% set picturePath = entry.previewPicture %}
{% endif %}
@ -27,7 +27,7 @@
<body>
<header class="block">
<h1>{{ entry.title|e|raw }}</h1>
<a href="{{ entry.url|e }}" target="_blank" title="{{ 'entry.view.original_article'|trans }} : {{ entry.title|e|raw }}" class="tool">{{ entry.domainName|removeWww }}</a>
<a href="{{ entry.url|e }}" target="_blank" rel="noopener" title="{{ 'entry.view.original_article'|trans }} : {{ entry.title|e|raw }}" class="tool">{{ entry.domainName|removeWww }}</a>
<p class="shared-by">{{ "entry.public.shared_by_wallabag"|trans({'%wallabag_instance%': url('homepage'), '%username%': entry.user.username})|raw }}.</p>
{% if entry.previewPicture is not null %}
<img class="preview" src="{{ entry.previewPicture }}" alt="{{ entry.title|striptags|e('html_attr') }}" />

View File

@ -28,18 +28,18 @@
<div class="col s12">
<h5>{{ 'howto.top_menu.browser_addons'|trans }}</h5>
<ul>
<li><a href="{{ addonsUrl.firefox }}" target="_blank">{{ 'howto.browser_addons.firefox'|trans }}</a></li>
<li><a href="{{ addonsUrl.chrome }}" target="_blank">{{ 'howto.browser_addons.chrome'|trans }}</a></li>
<li><a href="{{ addonsUrl.opera }}" target="_blank">{{ 'howto.browser_addons.opera'|trans }}</a></li>
<li><a href="{{ addonsUrl.firefox }}" target="_blank" rel="noopener">{{ 'howto.browser_addons.firefox'|trans }}</a></li>
<li><a href="{{ addonsUrl.chrome }}" target="_blank" rel="noopener">{{ 'howto.browser_addons.chrome'|trans }}</a></li>
<li><a href="{{ addonsUrl.opera }}" target="_blank" rel="noopener">{{ 'howto.browser_addons.opera'|trans }}</a></li>
</ul>
</div>
<div class="col s12">
<h5>{{ 'howto.top_menu.mobile_apps'|trans }}</h5>
<ul>
<li>Android: <a href="{{ addonsUrl.f_droid }}" target="_blank">{{ 'howto.mobile_apps.android.via_f_droid'|trans }}</a> / <a href="{{ addonsUrl.google_play }}" target="_blank">{{ 'howto.mobile_apps.android.via_google_play'|trans }}</a></li>
<li>Android: <a href="{{ addonsUrl.f_droid }}" target="_blank" rel="noopener">{{ 'howto.mobile_apps.android.via_f_droid'|trans }}</a> / <a href="{{ addonsUrl.google_play }}" target="_blank" rel="noopener">{{ 'howto.mobile_apps.android.via_google_play'|trans }}</a></li>
<li>iOS: <a href="{{ addonsUrl.ios }}" target="_blank">{{ 'howto.mobile_apps.ios'|trans }}</a></li>
<li>Windows Phone: <a href="{{ addonsUrl.windows }}" target="_blank">{{ 'howto.mobile_apps.windows'|trans }}</a></li>
<li>Windows Phone: <a href="{{ addonsUrl.windows }}" target="_blank" rel="noopener">{{ 'howto.mobile_apps.windows'|trans }}</a></li>
</ul>
</div>

View File

@ -1,7 +0,0 @@
<div class="card">
<div class="card-body">
{% include "@WallabagCore/themes/material/Entry/Card/_content.html.twig" with {'entry': entry} only %}
</div>
{% include "@WallabagCore/themes/material/Entry/_card_actions.html.twig" with {'entry': entry} only %}
</div>

View File

@ -7,7 +7,8 @@
{% endfor %}
</ul>
<a href="{{ path('view', { 'id': entry.id }) }}">
<span class="preview" style="background-image: url({{ entry.previewPicture }})"></span>
{% set previewClassModifier = entry.previewPicture ? '' : ' preview--default' %}
<span class="preview{{ previewClassModifier }}" style="background-image: url({{ entry.previewPicture | default(asset('wallassets/themes/_global/img/logo-square.svg')) }})"></span>
</a>
</div>
{% include "@WallabagCore/themes/material/Entry/Card/_content.html.twig" with {'entry': entry, 'withPreview': true} only %}

View File

@ -39,11 +39,9 @@
<li id="entry-{{ entry.id|e }}" class="col {% if listMode == 0 %}l3 m6{% else %}collection-item{% endif %} s12">
{% if listMode == 1 %}
{% include "@WallabagCore/themes/material/Entry/_card_list.html.twig" with {'entry': entry} only %}
{% elseif entry.previewPicture is null %}
{% include "@WallabagCore/themes/material/Entry/_card_no_preview.html.twig" with {'entry': entry} only %}
{% elseif not entry.previewPicture is null and entry.mimetype starts with 'image/' %}
{% include "@WallabagCore/themes/material/Entry/_card_full_image.html.twig" with {'entry': entry} only %}
{% elseif not entry.previewPicture is null %}
{% else %}
{% include "@WallabagCore/themes/material/Entry/_card_preview.html.twig" with {'entry': entry} only %}
{% endif %}
</li>

View File

@ -46,7 +46,7 @@
</li>
<li class="bold border-bottom hide-on-med-and-down">
<a class="waves-effect collapsible-header original" href="{{ entry.url|e }}" target="_blank">
<a class="waves-effect collapsible-header original" href="{{ entry.url|e }}" target="_blank" rel="noopener">
<i class="material-icons small">link</i>
<span>{{ 'entry.view.left_menu.view_original_article'|trans }}</span>
</a>
@ -127,42 +127,42 @@
{% endif %}
{% if craue_setting('share_twitter') %}
<li>
<a href="https://twitter.com/home?status={{entry.title|striptags|url_encode}}%20{{ entry.url|url_encode }}%20via%20@wallabagapp" target="_blank" class="tool icon-twitter" title="twitter">
<a href="https://twitter.com/home?status={{entry.title|striptags|url_encode}}%20{{ entry.url|url_encode }}%20via%20@wallabagapp" target="_blank" rel="noopener" class="tool icon-twitter" title="twitter">
<span>twitter</span>
</a>
</li>
{% endif %}
{% if craue_setting('share_shaarli') %}
<li>
<a href="{{ craue_setting('shaarli_url') }}/index.php?post={{ entry.url|url_encode }}&amp;title={{ entry.title|striptags|url_encode }}&amp;tags={{ entry.tags|join(',')|striptags|url_encode }}{% if craue_setting('shaarli_share_origin_url') %}&amp;original_url={{ entry.originUrl|url_encode }}{% endif %}" target="_blank" title="shaarli" class="tool icon-image shaarli">
<a href="{{ craue_setting('shaarli_url') }}/index.php?post={{ entry.url|url_encode }}&amp;title={{ entry.title|striptags|url_encode }}&amp;tags={{ entry.tags|join(',')|striptags|url_encode }}{% if craue_setting('shaarli_share_origin_url') %}&amp;original_url={{ entry.originUrl|url_encode }}{% endif %}" target="_blank" rel="noopener" title="shaarli" class="tool icon-image shaarli">
<span>shaarli</span>
</a>
</li>
{% endif %}
{% if craue_setting('share_scuttle') %}
<li>
<a href="{{ craue_setting('scuttle_url') }}/bookmarks.php?action=add&amp;address={{ entry.url|url_encode }}&amp;title={{ entry.title|striptags|url_encode }}&amp;tags={{ entry.tags|join(',')|striptags|url_encode }}" target="_blank" title="scuttle" class="tool icon-image scuttle">
<a href="{{ craue_setting('scuttle_url') }}/bookmarks.php?action=add&amp;address={{ entry.url|url_encode }}&amp;title={{ entry.title|striptags|url_encode }}&amp;tags={{ entry.tags|join(',')|striptags|url_encode }}" target="_blank" rel="noopener" title="scuttle" class="tool icon-image scuttle">
<span>scuttle</span>
</a>
</li>
{% endif %}
{% if craue_setting('share_diaspora') %}
<li>
<a href="{{ craue_setting('diaspora_url') }}/bookmarklet?url={{ entry.url|url_encode }}&amp;title={{ entry.title|striptags|url_encode }}&amp;notes=&amp;v=1&amp;noui=1&amp;jump=doclose" target="_blank" class="tool icon-image diaspora" title="diaspora">
<a href="{{ craue_setting('diaspora_url') }}/bookmarklet?url={{ entry.url|url_encode }}&amp;title={{ entry.title|striptags|url_encode }}&amp;notes=&amp;v=1&amp;noui=1&amp;jump=doclose" target="_blank" rel="noopener" class="tool icon-image diaspora" title="diaspora">
<span>diaspora*</span>
</a>
</li>
{% endif %}
{% if craue_setting('share_unmark') %}
<li>
<a href="{{ craue_setting('unmark_url') }}/mark/add?url={{ entry.url|url_encode }}&amp;title={{entry.title|striptags|url_encode}}&amp;v=6" target="_blank" class="tool icon-image unmark" title="unmark">
<a href="{{ craue_setting('unmark_url') }}/mark/add?url={{ entry.url|url_encode }}&amp;title={{entry.title|striptags|url_encode}}&amp;v=6" target="_blank" rel="noopener" class="tool icon-image unmark" title="unmark">
<span>unmark.it</span>
</a>
</li>
{% endif %}
{% if craue_setting('carrot') %}
<li>
<a href="https://secure.carrot.org/GiveAndGetBack.do?url={{ entry.url|url_encode }}&amp;title={{ entry.title|striptags|url_encode }}" target="_blank" title="carrot" class="tool icon-image carrot">
<a href="https://secure.carrot.org/GiveAndGetBack.do?url={{ entry.url|url_encode }}&amp;title={{ entry.title|striptags|url_encode }}" target="_blank" rel="noopener" title="carrot" class="tool icon-image carrot">
<span>Carrot</span>
</a>
</li>
@ -251,7 +251,7 @@
{% endif %}
<li>
<i class="material-icons link">link</i>
<a href="{{ entry.url|e }}" target="_blank" title="{{ 'entry.view.original_article'|trans }} : {{ entry.title|striptags }}" class="tool">
<a href="{{ entry.url|e }}" target="_blank" rel="noopener" title="{{ 'entry.view.original_article'|trans }} : {{ entry.title|striptags }}" class="tool">
{{ entry.domainName|removeWww }}
</a>
</li>
@ -262,7 +262,7 @@
{% if entry.originUrl is not empty %}
<li>
<i class="material-icons" title="{{ 'entry.view.provided_by'|trans }}">launch</i>
<a href="{{ entry.originUrl|e }}" target="_blank" class="tool">
<a href="{{ entry.originUrl|e }}" target="_blank" rel="noopener" class="tool">
{{ entry.originUrl|striptags|removeSchemeAndWww|truncate(32) }}
</a>
</li>

View File

@ -11,7 +11,7 @@
<main class="valign-wrapper">
<div class="valign row">
<div class="card sw">
<div class="center"><img src="{{ asset('wallassets/themes/_global/img/logo-other_themes.png') }}" alt="wallabag logo" /></div>
<div class="center"><img src="{{ asset('wallassets/themes/_global/img/logo-wallabag.svg') }}" alt="wallabag logo" class="typo-logo" /></div>
<div class="card-content">
<div class="row">
<h5>{{ status_code }}: {{ status_text }}</h5>

View File

@ -29,7 +29,7 @@
{% block logo %}
<li class="logo border-bottom">
<a title="{{ 'menu.left.back_to_unread'|trans }}" href="{{ path('unread') }}">
<img src="{{ asset('wallassets/themes/_global/img/logo-square.png') }}" alt="wallabag logo" />
<img src="{{ asset('wallassets/themes/_global/img/logo-square.svg') }}" alt="wallabag logo" />
</a>
</li>
{% endblock %}
@ -143,7 +143,7 @@
</div>
<div class="col s12 l4">
<p class="footer-text">
{{ 'footer.wallabag.powered_by'|trans }} <a target="_blank" href="https://wallabag.org" class="grey-text text-lighten-4">wallabag</a>
{{ 'footer.wallabag.powered_by'|trans }} <a target="_blank" rel="noopener" href="https://wallabag.org" class="grey-text text-lighten-4">wallabag</a>
<a class="grey-text text-lighten-4" href="{{ path('about') }}">{{ 'footer.wallabag.about'|trans|lower }}</a>
</p>
</div>

View File

@ -29,6 +29,6 @@ class Utils
*/
public static function getReadingTime($text)
{
return floor(count(preg_split('~[^\p{L}\p{N}\']+~u', strip_tags($text))) / 200);
return floor(\count(preg_split('~[^\p{L}\p{N}\']+~u', strip_tags($text))) / 200);
}
}

View File

@ -64,7 +64,7 @@ class WallabagExtension extends \Twig_Extension implements \Twig_Extension_Globa
{
$user = $this->tokenStorage->getToken() ? $this->tokenStorage->getToken()->getUser() : null;
if (null === $user || !is_object($user)) {
if (null === $user || !\is_object($user)) {
return 0;
}
@ -96,7 +96,7 @@ class WallabagExtension extends \Twig_Extension implements \Twig_Extension_Globa
$query->useResultCache(true);
$query->setResultCacheLifetime($this->lifeTime);
return count($query->getArrayResult());
return \count($query->getArrayResult());
}
/**
@ -108,7 +108,7 @@ class WallabagExtension extends \Twig_Extension implements \Twig_Extension_Globa
{
$user = $this->tokenStorage->getToken() ? $this->tokenStorage->getToken()->getUser() : null;
if (null === $user || !is_object($user)) {
if (null === $user || !\is_object($user)) {
return 0;
}
@ -124,7 +124,7 @@ class WallabagExtension extends \Twig_Extension implements \Twig_Extension_Globa
{
$user = $this->tokenStorage->getToken() ? $this->tokenStorage->getToken()->getUser() : null;
if (null === $user || !is_object($user)) {
if (null === $user || !\is_object($user)) {
return 0;
}
@ -137,7 +137,7 @@ class WallabagExtension extends \Twig_Extension implements \Twig_Extension_Globa
$query->useResultCache(true);
$query->setResultCacheLifetime($this->lifeTime);
$nbArchives = count($query->getArrayResult());
$nbArchives = \count($query->getArrayResult());
$interval = $user->getCreatedAt()->diff(new \DateTime('now'));
$nbDays = (int) $interval->format('%a') ?: 1;

View File

@ -43,7 +43,7 @@ class ImportCommand extends ContainerAwareCommand
$user = $em->getRepository('WallabagUserBundle:User')->findOneByUsername($input->getArgument('username'));
}
if (!is_object($user)) {
if (!\is_object($user)) {
throw new Exception(sprintf('User "%s" not found', $input->getArgument('username')));
}

View File

@ -30,7 +30,7 @@ abstract class BrowserController extends Controller
$markAsRead = $form->get('mark_as_read')->getData();
$name = $this->getUser()->getId() . '.json';
if (null !== $file && in_array($file->getClientMimeType(), $this->getParameter('wallabag_import.allow_mimetypes'), true) && $file->move($this->getParameter('wallabag_import.resource_dir'), $name)) {
if (null !== $file && \in_array($file->getClientMimeType(), $this->getParameter('wallabag_import.allow_mimetypes'), true) && $file->move($this->getParameter('wallabag_import.resource_dir'), $name)) {
$res = $wallabag
->setFilepath($this->getParameter('wallabag_import.resource_dir') . '/' . $name)
->setMarkAsRead($markAsRead)

View File

@ -31,7 +31,7 @@ class InstapaperController extends Controller
$markAsRead = $form->get('mark_as_read')->getData();
$name = 'instapaper_' . $this->getUser()->getId() . '.csv';
if (null !== $file && in_array($file->getClientMimeType(), $this->getParameter('wallabag_import.allow_mimetypes'), true) && $file->move($this->getParameter('wallabag_import.resource_dir'), $name)) {
if (null !== $file && \in_array($file->getClientMimeType(), $this->getParameter('wallabag_import.allow_mimetypes'), true) && $file->move($this->getParameter('wallabag_import.resource_dir'), $name)) {
$res = $instapaper
->setFilepath($this->getParameter('wallabag_import.resource_dir') . '/' . $name)
->setMarkAsRead($markAsRead)

View File

@ -31,7 +31,7 @@ class PinboardController extends Controller
$markAsRead = $form->get('mark_as_read')->getData();
$name = 'pinboard_' . $this->getUser()->getId() . '.json';
if (null !== $file && in_array($file->getClientMimeType(), $this->getParameter('wallabag_import.allow_mimetypes'), true) && $file->move($this->getParameter('wallabag_import.resource_dir'), $name)) {
if (null !== $file && \in_array($file->getClientMimeType(), $this->getParameter('wallabag_import.allow_mimetypes'), true) && $file->move($this->getParameter('wallabag_import.resource_dir'), $name)) {
$res = $pinboard
->setFilepath($this->getParameter('wallabag_import.resource_dir') . '/' . $name)
->setMarkAsRead($markAsRead)

View File

@ -31,7 +31,7 @@ class ReadabilityController extends Controller
$markAsRead = $form->get('mark_as_read')->getData();
$name = 'readability_' . $this->getUser()->getId() . '.json';
if (null !== $file && in_array($file->getClientMimeType(), $this->getParameter('wallabag_import.allow_mimetypes'), true) && $file->move($this->getParameter('wallabag_import.resource_dir'), $name)) {
if (null !== $file && \in_array($file->getClientMimeType(), $this->getParameter('wallabag_import.allow_mimetypes'), true) && $file->move($this->getParameter('wallabag_import.resource_dir'), $name)) {
$res = $readability
->setFilepath($this->getParameter('wallabag_import.resource_dir') . '/' . $name)
->setMarkAsRead($markAsRead)

View File

@ -33,7 +33,7 @@ abstract class WallabagController extends Controller
$markAsRead = $form->get('mark_as_read')->getData();
$name = $this->getUser()->getId() . '.json';
if (null !== $file && in_array($file->getClientMimeType(), $this->getParameter('wallabag_import.allow_mimetypes'), true) && $file->move($this->getParameter('wallabag_import.resource_dir'), $name)) {
if (null !== $file && \in_array($file->getClientMimeType(), $this->getParameter('wallabag_import.allow_mimetypes'), true) && $file->move($this->getParameter('wallabag_import.resource_dir'), $name)) {
$res = $wallabag
->setFilepath($this->getParameter('wallabag_import.resource_dir') . '/' . $name)
->setMarkAsRead($markAsRead)

View File

@ -77,7 +77,7 @@ abstract class BrowserImport extends AbstractImport
*/
public function parseEntry(array $importedEntry)
{
if ((!array_key_exists('guid', $importedEntry) || (!array_key_exists('id', $importedEntry))) && is_array(reset($importedEntry))) {
if ((!array_key_exists('guid', $importedEntry) || (!array_key_exists('id', $importedEntry))) && \is_array(reset($importedEntry))) {
if ($this->producer) {
$this->parseEntriesForProducer($importedEntry);

View File

@ -62,7 +62,7 @@ class InstapaperImport extends AbstractImport
}
$entries = [];
$handle = fopen($this->filepath, 'r');
$handle = fopen($this->filepath, 'rb');
while (false !== ($data = fgetcsv($handle, 10240))) {
if ('URL' === $data[0]) {
continue;
@ -72,7 +72,7 @@ class InstapaperImport extends AbstractImport
// BUT it can also be the status (since status = folder in Instapaper)
// and we don't want archive, unread & starred to become a tag
$tags = null;
if (false === in_array($data[3], ['Archive', 'Unread', 'Starred'], true)) {
if (false === \in_array($data[3], ['Archive', 'Unread', 'Starred'], true)) {
$tags = [$data[3]];
}

View File

@ -149,7 +149,7 @@ class PocketImport extends AbstractImport
// - first call get 5k offset 0
// - second call get 5k offset 5k
// - and so on
if (self::NB_ELEMENTS === count($entries['list'])) {
if (self::NB_ELEMENTS === \count($entries['list'])) {
++$run;
return $this->import(self::NB_ELEMENTS * $run);

View File

@ -56,7 +56,7 @@ class WallabagV1Import extends WallabagImport
// In case of a bad fetch in v1, replace title and content with v2 error strings
// If fetching fails again, they will get this instead of the v1 strings
if (in_array($entry['title'], $this->untitled, true)) {
if (\in_array($entry['title'], $this->untitled, true)) {
$data['title'] = $this->fetchingErrorMessageTitle;
$data['html'] = $this->fetchingErrorMessage;
}

View File

@ -16,7 +16,7 @@
<div class="input-field col s12">
<label for="username">{{ 'security.login.username'|trans }}</label>
<input type="text" id="username" name="_username" value="{{ last_username }}" />
<input type="text" id="username" name="_username" value="{{ last_username }}" autofocus />
</div>
<div class="input-field col s12">

View File

@ -11,7 +11,7 @@
<main class="valign-wrapper">
<div class="valign row">
<div class="card sw">
<div class="center"><img src="{{ asset('wallassets/themes/_global/img/logo-other_themes.png') }}" alt="wallabag logo" /></div>
<div class="center"><img src="{{ asset('wallassets/themes/_global/img/logo-wallabag.svg') }}" alt="wallabag logo" class="typo-logo" /></div>
{% block fos_user_content %}
{% endblock fos_user_content %}
</div>

View File

@ -28,7 +28,7 @@ class DeveloperControllerTest extends WallabagCoreTestCase
$this->assertSame(200, $client->getResponse()->getStatusCode());
$newNbClients = $em->getRepository('WallabagApiBundle:Client')->findAll();
$this->assertGreaterThan(count($nbClients), count($newNbClients));
$this->assertGreaterThan(\count($nbClients), \count($newNbClients));
$this->assertGreaterThan(1, $alert = $crawler->filter('.settings ul li strong')->extract(['_text']));
$this->assertContains('My app', $alert[0]);
@ -65,7 +65,7 @@ class DeveloperControllerTest extends WallabagCoreTestCase
$crawler = $client->request('GET', '/developer');
$this->assertSame(200, $client->getResponse()->getStatusCode());
$this->assertSame(count($nbClients), $crawler->filter('ul[class=collapsible] li')->count());
$this->assertSame(\count($nbClients), $crawler->filter('ul[class=collapsible] li')->count());
}
public function testDeveloperHowto()

View File

@ -28,7 +28,7 @@ class EntryRestControllerTest extends WallabagApiTestCase
$this->assertSame($entry->getTitle(), $content['title']);
$this->assertSame($entry->getUrl(), $content['url']);
$this->assertCount(count($entry->getTags()), $content['tags']);
$this->assertCount(\count($entry->getTags()), $content['tags']);
$this->assertSame($entry->getUserName(), $content['user_name']);
$this->assertSame($entry->getUserEmail(), $content['user_email']);
$this->assertSame($entry->getUserId(), $content['user_id']);
@ -127,7 +127,7 @@ class EntryRestControllerTest extends WallabagApiTestCase
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThanOrEqual(1, count($content));
$this->assertGreaterThanOrEqual(1, \count($content));
$this->assertNotEmpty($content['_embedded']['items']);
$this->assertGreaterThanOrEqual(1, $content['total']);
$this->assertSame(1, $content['page']);
@ -154,7 +154,7 @@ class EntryRestControllerTest extends WallabagApiTestCase
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThanOrEqual(1, count($content));
$this->assertGreaterThanOrEqual(1, \count($content));
$this->assertArrayHasKey('items', $content['_embedded']);
$this->assertGreaterThanOrEqual(0, $content['total']);
$this->assertSame(1, $content['page']);
@ -206,7 +206,7 @@ class EntryRestControllerTest extends WallabagApiTestCase
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThanOrEqual(1, count($content));
$this->assertGreaterThanOrEqual(1, \count($content));
$this->assertArrayHasKey('items', $content['_embedded']);
$this->assertGreaterThanOrEqual(1, $content['total']);
$this->assertSame(1, $content['page']);
@ -250,7 +250,7 @@ class EntryRestControllerTest extends WallabagApiTestCase
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThanOrEqual(1, count($content));
$this->assertGreaterThanOrEqual(1, \count($content));
$this->assertNotEmpty($content['_embedded']['items']);
$this->assertGreaterThanOrEqual(1, $content['total']);
$this->assertSame(1, $content['page']);
@ -278,7 +278,7 @@ class EntryRestControllerTest extends WallabagApiTestCase
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThanOrEqual(1, count($content));
$this->assertGreaterThanOrEqual(1, \count($content));
$this->assertNotEmpty($content['_embedded']['items']);
$this->assertGreaterThanOrEqual(1, $content['total']);
$this->assertSame(1, $content['page']);
@ -305,7 +305,7 @@ class EntryRestControllerTest extends WallabagApiTestCase
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThanOrEqual(1, count($content));
$this->assertGreaterThanOrEqual(1, \count($content));
$this->assertNotEmpty($content['_embedded']['items']);
$this->assertGreaterThanOrEqual(1, $content['total']);
$this->assertSame(1, $content['page']);
@ -342,7 +342,7 @@ class EntryRestControllerTest extends WallabagApiTestCase
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThanOrEqual(1, count($content));
$this->assertGreaterThanOrEqual(1, \count($content));
$this->assertNotEmpty($content['_embedded']['items']);
$this->assertGreaterThanOrEqual(1, $content['total']);
$this->assertSame(1, $content['page']);
@ -370,7 +370,7 @@ class EntryRestControllerTest extends WallabagApiTestCase
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThanOrEqual(1, count($content));
$this->assertGreaterThanOrEqual(1, \count($content));
$this->assertEmpty($content['_embedded']['items']);
$this->assertSame(0, $content['total']);
$this->assertSame(1, $content['page']);
@ -608,7 +608,7 @@ class EntryRestControllerTest extends WallabagApiTestCase
$this->assertSame($entry->getId(), $content['id']);
$this->assertSame($entry->getUrl(), $content['url']);
$this->assertSame('New awesome title', $content['title']);
$this->assertGreaterThanOrEqual(1, count($content['tags']), 'We force only one tag');
$this->assertGreaterThanOrEqual(1, \count($content['tags']), 'We force only one tag');
$this->assertSame(1, $content['user_id']);
$this->assertSame('de_AT', $content['language']);
$this->assertSame('http://preview.io/picture.jpg', $content['preview_picture']);
@ -647,7 +647,7 @@ class EntryRestControllerTest extends WallabagApiTestCase
$this->assertSame($entry->getId(), $content['id']);
$this->assertSame($entry->getUrl(), $content['url']);
$this->assertGreaterThanOrEqual(1, count($content['tags']), 'We force only one tag');
$this->assertGreaterThanOrEqual(1, \count($content['tags']), 'We force only one tag');
$this->assertEmpty($content['published_by'], 'Authors were not saved because of an array instead of a string');
$this->assertSame($previousContent, $content['content'], 'Ensure content has not moved');
$this->assertSame($previousLanguage, $content['language'], 'Ensure language has not moved');
@ -772,7 +772,7 @@ class EntryRestControllerTest extends WallabagApiTestCase
$this->markTestSkipped('No content found in db.');
}
$nbTags = count($entry->getTags());
$nbTags = \count($entry->getTags());
$newTags = 'tag1,tag2,tag3';
@ -783,7 +783,7 @@ class EntryRestControllerTest extends WallabagApiTestCase
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertArrayHasKey('tags', $content);
$this->assertSame($nbTags + 3, count($content['tags']));
$this->assertSame($nbTags + 3, \count($content['tags']));
$entryDB = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
@ -813,7 +813,7 @@ class EntryRestControllerTest extends WallabagApiTestCase
}
// hydrate the tags relations
$nbTags = count($entry->getTags());
$nbTags = \count($entry->getTags());
$tag = $entry->getTags()[0];
$this->client->request('DELETE', '/api/entries/' . $entry->getId() . '/tags/' . $tag->getId() . '.json');
@ -823,7 +823,7 @@ class EntryRestControllerTest extends WallabagApiTestCase
$content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertArrayHasKey('tags', $content);
$this->assertSame($nbTags - 1, count($content['tags']));
$this->assertSame($nbTags - 1, \count($content['tags']));
}
public function testSaveIsArchivedAfterPost()

View File

@ -166,15 +166,14 @@ class EntryControllerTest extends WallabagCoreTestCase
$this->assertSame($this->url, $content->getUrl());
$this->assertContains('Google', $content->getTitle());
$this->assertSame('fr', $content->getLanguage());
$this->assertSame('2015-03-28 11:43:19', $content->getPublishedAt()->format('Y-m-d H:i:s'));
$this->assertSame('Morgane Tual', $author[0]);
$this->assertArrayHasKey('x-varnish1', $content->getHeaders());
$this->assertSame('2016-04-07 19:01:35', $content->getPublishedAt()->format('Y-m-d H:i:s'));
$this->assertArrayHasKey('x-frame-options', $content->getHeaders());
$client->getContainer()->get('craue_config')->set('store_article_headers', 0);
}
public function testPostWithMultipleAuthors()
{
$url = 'http://www.liberation.fr/planete/2017/04/05/donald-trump-et-xi-jinping-tentative-de-flirt-en-floride_1560768';
$url = 'https://www.liberation.fr/planete/2017/04/05/donald-trump-et-xi-jinping-tentative-de-flirt-en-floride_1560768';
$this->logInAs('admin');
$client = $this->getClient();
@ -197,6 +196,7 @@ class EntryControllerTest extends WallabagCoreTestCase
->getRepository('WallabagCoreBundle:Entry')
->findByUrlAndUserId($url, $this->getLoggedInUserId());
$this->assertInstanceOf('Wallabag\CoreBundle\Entity\Entry', $content);
$authors = $content->getPublishedBy();
$this->assertSame('2017-04-05 19:26:13', $content->getPublishedAt()->format('Y-m-d H:i:s'));
$this->assertSame('fr', $content->getLanguage());
@ -524,7 +524,7 @@ class EntryControllerTest extends WallabagCoreTestCase
$this->assertGreaterThan(1, $title = $crawler->filter('div[id=article] h1')->extract(['_text']));
$this->assertContains('My updated title hehe :)', $title[0]);
$this->assertSame(1, count($stats = $crawler->filter('div[class=tools] ul[class=stats] li a[class=tool]')->extract(['_text'])));
$this->assertSame(1, \count($stats = $crawler->filter('div[class=tools] ul[class=stats] li a[class=tool]')->extract(['_text'])));
$this->assertNotContains('example.io', trim($stats[0]));
}
@ -1325,16 +1325,12 @@ class EntryControllerTest extends WallabagCoreTestCase
'http://www.hao123.com/shequ?__noscript__-=1',
'zh_CN',
],
'de_AT' => [
'https://buy.garmin.com/de-AT/AT/catalog/product/compareResult.ep?compareProduct=112885&compareProduct=36728',
'de_AT',
],
'ru_RU' => [
'http://netler.ru/ikt/windows-error-reporting.htm',
'ru_RU',
'ru' => [
'https://www.kp.ru/daily/26879.7/3921982/',
'ru',
],
'pt_BR' => [
'http://precodoscombustiveis.com.br/postos/cidade/4121/pr/maringa',
'https://politica.estadao.com.br/noticias/eleicoes,campanha-catatonica,70002491983',
'pt_BR',
],
'fucked_list_of_languages' => [

View File

@ -180,7 +180,7 @@ class ExportControllerTest extends WallabagCoreTestCase
$this->assertGreaterThan(1, $csv);
// +1 for title line
$this->assertSame(count($contentInDB) + 1, count($csv));
$this->assertSame(\count($contentInDB) + 1, \count($csv));
$this->assertSame('Title;URL;Content;Tags;"MIME Type";Language;"Creation date"', $csv[0]);
$this->assertContains($contentInDB[0]['title'], $csv[1]);
$this->assertContains($contentInDB[0]['url'], $csv[1]);
@ -272,7 +272,7 @@ class ExportControllerTest extends WallabagCoreTestCase
$content = new \SimpleXMLElement($client->getResponse()->getContent());
$this->assertGreaterThan(0, $content->count());
$this->assertSame(count($contentInDB), $content->count());
$this->assertSame(\count($contentInDB), $content->count());
$this->assertNotEmpty('id', (string) $content->entry[0]->id);
$this->assertNotEmpty('title', (string) $content->entry[0]->title);
$this->assertNotEmpty('url', (string) $content->entry[0]->url);

View File

@ -98,7 +98,7 @@ class TagControllerTest extends WallabagCoreTestCase
$tags[$key] = $tag->getLabel();
}
$this->assertGreaterThanOrEqual(2, count($tags));
$this->assertGreaterThanOrEqual(2, \count($tags));
$this->assertNotFalse(array_search('foo2', $tags, true), 'Tag foo2 is assigned to the entry');
$this->assertNotFalse(array_search('bar2', $tags, true), 'Tag bar2 is assigned to the entry');
}

View File

@ -531,6 +531,377 @@ class ContentProxyTest extends TestCase
$this->assertSame('1.1.1.1', $entry->getDomainName());
}
public function testWebsiteWithValidUTF8Title_doNothing()
{
// You can use https://www.online-toolz.com/tools/text-hex-convertor.php to convert UTF-8 text <=> hex
// See http://graphemica.com for more info about the characters
// '😻z' (U+1F63B or F09F98BB; U+2124 or E284A4; U+007A or 7A) in hexadecimal and UTF-8
$actualTitle = $this->hexToStr('F09F98BB' . 'E284A4' . '7A');
$tagger = $this->getTaggerMock();
$tagger->expects($this->once())
->method('tag');
$graby = $this->getMockBuilder('Graby\Graby')
->setMethods(['fetchContent'])
->disableOriginalConstructor()
->getMock();
$graby->expects($this->any())
->method('fetchContent')
->willReturn([
'html' => false,
'title' => $actualTitle,
'url' => '',
'content_type' => 'text/html',
'language' => '',
]);
$proxy = new ContentProxy($graby, $tagger, $this->getValidator(), $this->getLogger(), $this->fetchingErrorMessage);
$entry = new Entry(new User());
$proxy->updateEntry($entry, 'http://0.0.0.0');
// '😻z' (U+1F63B or F09F98BB; U+2124 or E284A4; U+007A or 7A) in hexadecimal and UTF-8
$expectedTitle = 'F09F98BB' . 'E284A4' . '7A';
$this->assertSame($expectedTitle, $this->strToHex($entry->getTitle()));
}
public function testWebsiteWithInvalidUTF8Title_removeInvalidCharacter()
{
// See http://graphemica.com for more info about the characters
// 'a€b' (61;80;62) in hexadecimal and WINDOWS-1252 - but 80 is a invalid UTF-8 character.
// The correct UTF-8 € character (U+20AC) is E282AC
$actualTitle = $this->hexToStr('61' . '80' . '62');
$tagger = $this->getTaggerMock();
$tagger->expects($this->once())
->method('tag');
$graby = $this->getMockBuilder('Graby\Graby')
->setMethods(['fetchContent'])
->disableOriginalConstructor()
->getMock();
$graby->expects($this->any())
->method('fetchContent')
->willReturn([
'html' => false,
'title' => $actualTitle,
'url' => '',
'content_type' => 'text/html',
'language' => '',
]);
$proxy = new ContentProxy($graby, $tagger, $this->getValidator(), $this->getLogger(), $this->fetchingErrorMessage);
$entry = new Entry(new User());
$proxy->updateEntry($entry, 'http://0.0.0.0');
// 'ab' (61;62) because all invalid UTF-8 character (like 80) are removed
$expectedTitle = '61' . '62';
$this->assertSame($expectedTitle, $this->strToHex($entry->getTitle()));
}
public function testPdfWithUTF16BETitle_convertToUTF8()
{
// See http://graphemica.com for more info about the characters
// '😻' (U+1F63B;D83DDE3B) in hexadecimal and as UTF16BE
$actualTitle = $this->hexToStr('D83DDE3B');
$tagger = $this->getTaggerMock();
$tagger->expects($this->once())
->method('tag');
$graby = $this->getMockBuilder('Graby\Graby')
->setMethods(['fetchContent'])
->disableOriginalConstructor()
->getMock();
$graby->expects($this->any())
->method('fetchContent')
->willReturn([
'html' => false,
'title' => $actualTitle,
'url' => '',
'content_type' => 'application/pdf',
'language' => '',
]);
$proxy = new ContentProxy($graby, $tagger, $this->getValidator(), $this->getLogger(), $this->fetchingErrorMessage);
$entry = new Entry(new User());
$proxy->updateEntry($entry, 'http://0.0.0.0');
// '😻' (U+1F63B or F09F98BB) in hexadecimal and UTF-8
$expectedTitle = 'F09F98BB';
$this->assertSame($expectedTitle, $this->strToHex($entry->getTitle()));
}
public function testPdfWithUTF8Title_doNothing()
{
// See http://graphemica.com for more info about the characters
// '😻' (U+1F63B;D83DDE3B) in hexadecimal and as UTF8
$actualTitle = $this->hexToStr('F09F98BB');
$tagger = $this->getTaggerMock();
$tagger->expects($this->once())
->method('tag');
$graby = $this->getMockBuilder('Graby\Graby')
->setMethods(['fetchContent'])
->disableOriginalConstructor()
->getMock();
$graby->expects($this->any())
->method('fetchContent')
->willReturn([
'html' => false,
'title' => $actualTitle,
'url' => '',
'content_type' => 'application/pdf',
'language' => '',
]);
$proxy = new ContentProxy($graby, $tagger, $this->getValidator(), $this->getLogger(), $this->fetchingErrorMessage);
$entry = new Entry(new User());
$proxy->updateEntry($entry, 'http://0.0.0.0');
// '😻' (U+1F63B or F09F98BB) in hexadecimal and UTF-8
$expectedTitle = 'F09F98BB';
$this->assertSame($expectedTitle, $this->strToHex($entry->getTitle()));
}
public function testPdfWithWINDOWS1252Title_convertToUTF8()
{
// See http://graphemica.com for more info about the characters
// '€' (80) in hexadecimal and WINDOWS-1252
$actualTitle = $this->hexToStr('80');
$tagger = $this->getTaggerMock();
$tagger->expects($this->once())
->method('tag');
$graby = $this->getMockBuilder('Graby\Graby')
->setMethods(['fetchContent'])
->disableOriginalConstructor()
->getMock();
$graby->expects($this->any())
->method('fetchContent')
->willReturn([
'html' => false,
'title' => $actualTitle,
'url' => '',
'content_type' => 'application/pdf',
'language' => '',
]);
$proxy = new ContentProxy($graby, $tagger, $this->getValidator(), $this->getLogger(), $this->fetchingErrorMessage);
$entry = new Entry(new User());
$proxy->updateEntry($entry, 'http://0.0.0.0');
// '€' (U+20AC or E282AC) in hexadecimal and UTF-8
$expectedTitle = 'E282AC';
$this->assertSame($expectedTitle, $this->strToHex($entry->getTitle()));
}
public function testPdfWithInvalidCharacterInTitle_removeInvalidCharacter()
{
// See http://graphemica.com for more info about the characters
// '😻<F09F98BB>z' (U+1F63B or F09F98BB; U+2124 or E284A4; invalid character 81; U+007A or 7A) in hexadecimal and UTF-8
// 0x81 is not a valid character for UTF16, UTF8 and WINDOWS-1252
$actualTitle = $this->hexToStr('F09F98BB' . 'E284A4' . '81' . '7A');
$tagger = $this->getTaggerMock();
$tagger->expects($this->once())
->method('tag');
$graby = $this->getMockBuilder('Graby\Graby')
->setMethods(['fetchContent'])
->disableOriginalConstructor()
->getMock();
$graby->expects($this->any())
->method('fetchContent')
->willReturn([
'html' => false,
'title' => $actualTitle,
'url' => '',
'content_type' => 'application/pdf',
'language' => '',
]);
$proxy = new ContentProxy($graby, $tagger, $this->getValidator(), $this->getLogger(), $this->fetchingErrorMessage);
$entry = new Entry(new User());
$proxy->updateEntry($entry, 'http://0.0.0.0');
// '😻z' (U+1F63B or F09F98BB; U+2124 or E284A4; U+007A or 7A) in hexadecimal and UTF-8
// the 0x81 (represented by <20>) is invalid for UTF16, UTF8 and WINDOWS-1252 and is removed
$expectedTitle = 'F09F98BB' . 'E284A4' . '7A';
$this->assertSame($expectedTitle, $this->strToHex($entry->getTitle()));
}
/**
* Data provider for testWithChangedUrl.
*
* Arrays contain the following values:
* $entry_url
* $origin_url
* $content_url
* $expected_entry_url
* $expected_origin_url
* $expected_domain
*/
public function dataForChangedUrl()
{
return [
'normal' => [
'http://0.0.0.0',
null,
'http://1.1.1.1',
'http://1.1.1.1',
'http://0.0.0.0',
'1.1.1.1',
],
'origin already set' => [
'http://0.0.0.0',
'http://hello',
'http://1.1.1.1',
'http://1.1.1.1',
'http://hello',
'1.1.1.1',
],
'trailing slash' => [
'https://example.com/hello-world',
null,
'https://example.com/hello-world/',
'https://example.com/hello-world/',
null,
'example.com',
],
'query string in fetched content' => [
'https://example.org/hello',
null,
'https://example.org/hello?world=1',
'https://example.org/hello?world=1',
'https://example.org/hello',
'example.org',
],
'fragment in fetched content' => [
'https://example.org/hello',
null,
'https://example.org/hello#world',
'https://example.org/hello',
null,
'example.org',
],
'fragment and query string in fetched content' => [
'https://example.org/hello',
null,
'https://example.org/hello?foo#world',
'https://example.org/hello?foo#world',
'https://example.org/hello',
'example.org',
],
'different path and query string in fetch content' => [
'https://example.org/hello',
null,
'https://example.org/world?foo',
'https://example.org/world?foo',
'https://example.org/hello',
'example.org',
],
'feedproxy ignore list test' => [
'http://feedproxy.google.com/~r/Wallabag/~3/helloworld',
null,
'https://example.org/hello-wallabag',
'https://example.org/hello-wallabag',
null,
'example.org',
],
'feedproxy ignore list test with origin url already set' => [
'http://feedproxy.google.com/~r/Wallabag/~3/helloworld',
'https://example.org/this-is-source',
'https://example.org/hello-wallabag',
'https://example.org/hello-wallabag',
'https://example.org/this-is-source',
'example.org',
],
'lemonde ignore pattern test' => [
'http://www.lemonde.fr/tiny/url',
null,
'http://example.com/hello-world',
'http://example.com/hello-world',
null,
'example.com',
],
];
}
/**
* @dataProvider dataForChangedUrl
*/
public function testWithChangedUrl($entry_url, $origin_url, $content_url, $expected_entry_url, $expected_origin_url, $expected_domain)
{
$tagger = $this->getTaggerMock();
$tagger->expects($this->once())
->method('tag');
$proxy = new ContentProxy((new Graby()), $tagger, $this->getValidator(), $this->getLogger(), $this->fetchingErrorMessage, true);
$entry = new Entry(new User());
$entry->setOriginUrl($origin_url);
$proxy->updateEntry(
$entry,
$entry_url,
[
'html' => false,
'title' => '',
'url' => $content_url,
'content_type' => '',
'language' => '',
],
true
);
$this->assertSame($expected_entry_url, $entry->getUrl());
$this->assertSame($expected_domain, $entry->getDomainName());
$this->assertSame($expected_origin_url, $entry->getOriginUrl());
}
/**
* https://stackoverflow.com/a/18506801.
*
* @param $string
*
* @return string
*/
private function strToHex($string)
{
$hex = '';
for ($i = 0; $i < \strlen($string); ++$i) {
$ord = \ord($string[$i]);
$hexCode = dechex($ord);
$hex .= substr('0' . $hexCode, -2);
}
return strtoupper($hex);
}
/**
* https://stackoverflow.com/a/18506801.
*
* @param $hex
*
* @return string
*/
private function hexToStr($hex)
{
$string = '';
for ($i = 0; $i < \strlen($hex) - 1; $i += 2) {
$string .= \chr(hexdec($hex[$i] . $hex[$i + 1]));
}
return $string;
}
private function getTaggerMock()
{
return $this->getMockBuilder(RuleBasedTagger::class)

View File

@ -204,4 +204,52 @@ class DownloadImagesTest extends TestCase
$this->assertNotContains('http://piketty.blog.lemonde.fr/', $res, 'Image srcset attribute were not replaced');
}
public function testProcessImageWithTrickySrcset()
{
$client = new Client();
$mock = new Mock([
new Response(200, ['content-type' => 'image/jpeg'], Stream::factory(file_get_contents(__DIR__ . '/../fixtures/image-no-content-type.jpg'))),
new Response(200, ['content-type' => 'image/jpeg'], Stream::factory(file_get_contents(__DIR__ . '/../fixtures/image-no-content-type.jpg'))),
new Response(200, ['content-type' => 'image/jpeg'], Stream::factory(file_get_contents(__DIR__ . '/../fixtures/image-no-content-type.jpg'))),
]);
$client->getEmitter()->attach($mock);
$logHandler = new TestHandler();
$logger = new Logger('test', [$logHandler]);
$download = new DownloadImages($client, sys_get_temp_dir() . '/wallabag_test', 'http://wallabag.io/', $logger);
$res = $download->processHtml(123, '<figure id="post-257260" class="align-none media-257260"><img src="https://cdn.css-tricks.com/wp-content/uploads/2017/08/the-critical-request.png" srcset="https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_1000,f_auto,q_auto/v1501594717/the-critical-request_bqdfaa.png 1000w, https://res.cloudinary.com/css-tricks/image/upload/c_scale,w_200,f_auto,q_auto/v1501594717/the-critical-request_bqdfaa.png 200w" sizes="(min-width: 1850px) calc( (100vw - 555px) / 3 )
(min-width: 1251px) calc( (100vw - 530px) / 2 )
(min-width: 1086px) calc(100vw - 480px)
(min-width: 626px) calc(100vw - 335px)
calc(100vw - 30px)" alt="" /></figure>', 'https://css-tricks.com/the-critical-request/');
$this->assertNotContains('f_auto,q_auto', $res, 'Image srcset attribute were not replaced');
}
public function testProcessImageWithNullPath()
{
$client = new Client();
$mock = new Mock([
new Response(200, ['content-type' => null], Stream::factory(file_get_contents(__DIR__ . '/../fixtures/image-no-content-type.jpg'))),
]);
$client->getEmitter()->attach($mock);
$logHandler = new TestHandler();
$logger = new Logger('test', [$logHandler]);
$download = new DownloadImages($client, sys_get_temp_dir() . '/wallabag_test', 'http://wallabag.io/', $logger);
$res = $download->processSingleImage(
123,
null,
'https://framablog.org/2018/06/30/engagement-atypique/'
);
$this->assertFalse($res);
}
}

View File

@ -121,7 +121,7 @@ class ChromeControllerTest extends WallabagCoreTestCase
$this->assertInstanceOf('Wallabag\CoreBundle\Entity\Entry', $content);
$this->assertNotEmpty($content->getPreviewPicture(), 'Preview picture for http://www.usinenouvelle.com is ok');
$this->assertNotEmpty($content->getLanguage(), 'Language for http://www.usinenouvelle.com is ok');
$this->assertSame(1, count($content->getTags()));
$this->assertSame(1, \count($content->getTags()));
$createdAt = $content->getCreatedAt();
$this->assertSame('2011', $createdAt->format('Y'));

View File

@ -122,20 +122,20 @@ class FirefoxControllerTest extends WallabagCoreTestCase
$this->assertNotEmpty($content->getMimetype(), 'Mimetype for http://lexpansion.lexpress.fr is ok');
$this->assertNotEmpty($content->getPreviewPicture(), 'Preview picture for http://lexpansion.lexpress.fr is ok');
$this->assertNotEmpty($content->getLanguage(), 'Language for http://lexpansion.lexpress.fr is ok');
$this->assertSame(3, count($content->getTags()));
$this->assertSame(3, \count($content->getTags()));
$content = $client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findByUrlAndUserId(
'https://stackoverflow.com/questions/15017163/parser-for-exported-bookmarks-html-file-of-google-chrome-and-mozilla-in-java',
'https://www.lemonde.fr/disparitions/article/2018/07/05/le-journaliste-et-cineaste-claude-lanzmann-est-mort_5326313_3382.html',
$this->getLoggedInUserId()
);
$this->assertInstanceOf('Wallabag\CoreBundle\Entity\Entry', $content);
$this->assertNotEmpty($content->getMimetype(), 'Mimetype for https://stackoverflow.com is ok');
$this->assertNotEmpty($content->getPreviewPicture(), 'Preview picture for https://stackoverflow.com is ok');
$this->assertEmpty($content->getLanguage(), 'Language for https://stackoverflow.com is ok');
$this->assertNotEmpty($content->getMimetype(), 'Mimetype for https://www.lemonde.fr is ok');
$this->assertNotEmpty($content->getPreviewPicture(), 'Preview picture for https://www.lemonde.fr is ok');
$this->assertNotEmpty($content->getLanguage(), 'Language for https://www.lemonde.fr is ok');
$createdAt = $content->getCreatedAt();
$this->assertSame('2013', $createdAt->format('Y'));

View File

@ -114,15 +114,17 @@ class InstapaperControllerTest extends WallabagCoreTestCase
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findByUrlAndUserId(
'http://www.liberation.fr/societe/2012/12/06/baumettes-un-tour-en-cellule_865551',
'https://www.liberation.fr/societe/2012/12/06/baumettes-un-tour-en-cellule_865551',
$this->getLoggedInUserId()
);
$this->assertNotEmpty($content->getMimetype(), 'Mimetype for http://www.liberation.fr is ok');
$this->assertNotEmpty($content->getPreviewPicture(), 'Preview picture for http://www.liberation.fr is ok');
$this->assertNotEmpty($content->getLanguage(), 'Language for http://www.liberation.fr is ok');
$this->assertInstanceOf('Wallabag\CoreBundle\Entity\Entry', $content);
$this->assertNotEmpty($content->getMimetype(), 'Mimetype for https://www.liberation.fr is ok');
$this->assertNotEmpty($content->getPreviewPicture(), 'Preview picture for https://www.liberation.fr is ok');
$this->assertNotEmpty($content->getLanguage(), 'Language for https://www.liberation.fr is ok');
$this->assertContains('foot', $content->getTags(), 'It includes the "foot" tag');
$this->assertSame(1, count($content->getTags()));
$this->assertSame(1, \count($content->getTags()));
$this->assertInstanceOf(\DateTime::class, $content->getCreatedAt());
$content = $client->getContainer()
@ -136,7 +138,7 @@ class InstapaperControllerTest extends WallabagCoreTestCase
$this->assertContains('foot', $content->getTags());
$this->assertContains('test_tag', $content->getTags());
$this->assertSame(2, count($content->getTags()));
$this->assertSame(2, \count($content->getTags()));
}
public function testImportInstapaperWithFileAndMarkAllAsRead()

View File

@ -127,7 +127,7 @@ class PinboardControllerTest extends WallabagCoreTestCase
$this->assertContains('foot', $tags, 'It includes the "foot" tag');
$this->assertContains('varnish', $tags, 'It includes the "varnish" tag');
$this->assertContains('php', $tags, 'It includes the "php" tag');
$this->assertSame(3, count($tags));
$this->assertSame(3, \count($tags));
$this->assertInstanceOf(\DateTime::class, $content->getCreatedAt());
$this->assertSame('2016-10-26', $content->getCreatedAt()->format('Y-m-d'));

View File

@ -125,7 +125,7 @@ class ReadabilityControllerTest extends WallabagCoreTestCase
$tags = $content->getTags();
$this->assertContains('foot', $tags, 'It includes the "foot" tag');
$this->assertSame(1, count($tags));
$this->assertSame(1, \count($tags));
$this->assertInstanceOf(\DateTime::class, $content->getCreatedAt());
$this->assertSame('2016-09-08', $content->getCreatedAt()->format('Y-m-d'));

View File

@ -127,7 +127,7 @@ class WallabagV1ControllerTest extends WallabagCoreTestCase
$tags = $content->getTags();
$this->assertContains('foot', $tags, 'It includes the "foot" tag');
$this->assertContains('framabag', $tags, 'It includes the "framabag" tag');
$this->assertSame(2, count($tags));
$this->assertSame(2, \count($tags));
$this->assertInstanceOf(\DateTime::class, $content->getCreatedAt());
}

View File

@ -115,20 +115,20 @@ class WallabagV2ControllerTest extends WallabagCoreTestCase
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findByUrlAndUserId(
'http://www.liberation.fr/planete/2015/10/26/refugies-l-ue-va-creer-100-000-places-d-accueil-dans-les-balkans_1408867',
'https://www.liberation.fr/planete/2015/10/26/refugies-l-ue-va-creer-100-000-places-d-accueil-dans-les-balkans_1408867',
$this->getLoggedInUserId()
);
$this->assertInstanceOf('Wallabag\CoreBundle\Entity\Entry', $content);
// empty because it wasn't re-imported
$this->assertEmpty($content->getMimetype(), 'Mimetype for http://www.liberation.fr is empty');
$this->assertEmpty($content->getPreviewPicture(), 'Preview picture for http://www.liberation.fr is empty');
$this->assertEmpty($content->getLanguage(), 'Language for http://www.liberation.fr is empty');
$this->assertEmpty($content->getMimetype(), 'Mimetype for https://www.liberation.fr is empty');
$this->assertEmpty($content->getPreviewPicture(), 'Preview picture for https://www.liberation.fr is empty');
$this->assertEmpty($content->getLanguage(), 'Language for https://www.liberation.fr is empty');
$tags = $content->getTags();
$this->assertContains('foot', $tags, 'It includes the "foot" tag');
$this->assertSame(1, count($tags));
$this->assertSame(1, \count($tags));
$content = $client->getContainer()
->get('doctrine.orm.entity_manager')
@ -147,7 +147,7 @@ class WallabagV2ControllerTest extends WallabagCoreTestCase
$this->assertContains('foot', $tags, 'It includes the "foot" tag');
$this->assertContains('mediapart', $tags, 'It includes the "mediapart" tag');
$this->assertContains('blog', $tags, 'It includes the "blog" tag');
$this->assertSame(3, count($tags));
$this->assertSame(3, \count($tags));
$this->assertInstanceOf(\DateTime::class, $content->getCreatedAt());
$this->assertSame('2016-09-08', $content->getCreatedAt()->format('Y-m-d'));

View File

@ -39,13 +39,13 @@
},
{
"guid": "E385l9vZ_LVn",
"title": "Parser for Exported Bookmarks HTML file of Google Chrome and Mozilla in Java",
"title": "Le journaliste et cinéaste Claude Lanzmann est mort",
"index": 1,
"dateAdded": 1388166091544000,
"lastModified": 1388166091545000,
"id": 5,
"type": "text/x-moz-place",
"uri": "http://stackoverflow.com/questions/15017163/parser-for-exported-bookmarks-html-file-of-google-chrome-and-mozilla-in-java"
"uri": "https://www.lemonde.fr/disparitions/article/2018/07/05/le-journaliste-et-cineaste-claude-lanzmann-est-mort_5326313_3382.html"
}
]
},

View File

@ -1,5 +1,5 @@
URL,Title,Selection,Folder
http://www.liberation.fr/societe/2012/12/06/baumettes-un-tour-en-cellule_865551,Baumettes : un tour en cellule,,Unread
https://www.liberation.fr/societe/2012/12/06/baumettes-un-tour-en-cellule_865551,Baumettes : un tour en cellule,,Unread
https://redditblog.com/2016/09/20/amp-and-reactredux/,AMP and React+Redux: Why Not?,,Archive
https://medium.com/@the_minh/why-foursquare-swarm-is-still-my-favourite-social-network-e38228493e6c,Why Foursquare / Swarm is still my favourite social network,,Starred
https://www.20minutes.fr/high-tech/2077615-20170531-quoi-exactement-tweet-covfefe-donald-trump-persiste-signe,"Dis donc Donald Trump, c'est quoi exactement «covfefe»?",,test_tag

1 URL Title Selection Folder
2 http://www.liberation.fr/societe/2012/12/06/baumettes-un-tour-en-cellule_865551 https://www.liberation.fr/societe/2012/12/06/baumettes-un-tour-en-cellule_865551 Baumettes : un tour en cellule Unread
3 https://redditblog.com/2016/09/20/amp-and-reactredux/ AMP and React+Redux: Why Not? Archive
4 https://medium.com/@the_minh/why-foursquare-swarm-is-still-my-favourite-social-network-e38228493e6c Why Foursquare / Swarm is still my favourite social network Starred
5 https://www.20minutes.fr/high-tech/2077615-20170531-quoi-exactement-tweet-covfefe-donald-trump-persiste-signe Dis donc Donald Trump, c'est quoi exactement «covfefe»? test_tag

Some files were not shown because too many files have changed in this diff Show More