forked from wallabag/wallabag
Compare commits
207 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9bbafdaad4 | |||
| 227a1a27f1 | |||
| cc181629a0 | |||
| eb06edba0c | |||
| b50ecd0c8a | |||
| a73cb8a689 | |||
| 295b714426 | |||
| 19822ecb31 | |||
| 9ae5bd9e10 | |||
| 2dbb5b2307 | |||
| 570113208b | |||
| 4b5b228650 | |||
| ea54c2adb1 | |||
| 35359bd3c6 | |||
| ff8f338dc2 | |||
| f45496336f | |||
| bfd69c74e5 | |||
| 8ab5dcc467 | |||
| 893f107fa1 | |||
| 8ca858ee73 | |||
| 41d476d7e7 | |||
| 7c553a6017 | |||
| a48ff25d4c | |||
| cf0010cf4a | |||
| 13d2fe6d2e | |||
| 3d7a1f4caf | |||
| 36fd8e5fde | |||
| f2aa373054 | |||
| 4ae4fa7b89 | |||
| 6640a922d1 | |||
| 246cc9ac93 | |||
| f1f1efb5de | |||
| 806635f7d5 | |||
| 05232afe32 | |||
| fb7dedf36c | |||
| 1e9c710fdd | |||
| d5154ee2ae | |||
| 4de99d1ab1 | |||
| 12a97c3522 | |||
| 508302042f | |||
| 9a7a0e1e6b | |||
| 4e0ed3368d | |||
| c01bda038e | |||
| b1992b340e | |||
| 44560c7767 | |||
| 0182cdaec4 | |||
| a9f61d3dbb | |||
| 47e4784110 | |||
| 7e04bd4ca4 | |||
| 755753e3ef | |||
| 8d082488e9 | |||
| a2e60dd393 | |||
| b992522d85 | |||
| 293730656d | |||
| 6b32cf0c73 | |||
| 1ec44a346b | |||
| f6b9e883c0 | |||
| 78e3fafa3f | |||
| a5e9a98aa3 | |||
| f083836cc4 | |||
| 314f647238 | |||
| c17d1ab033 | |||
| 3bed2e440e | |||
| 3625833b2c | |||
| 03663530ed | |||
| ca990600da | |||
| bb8ad42b27 | |||
| d4466a37fe | |||
| 3a2d4cf9fd | |||
| 5e1f27767b | |||
| dac93644e8 | |||
| ad5ef8bca0 | |||
| af83d05ce2 | |||
| 4944703edc | |||
| f810834623 | |||
| 30cf72bf55 | |||
| edd1825b58 | |||
| 063d5e7bda | |||
| 5de17117a1 | |||
| d2aec7096d | |||
| bf22266a62 | |||
| 7f8630b91c | |||
| 35983eb9bb | |||
| 8f5c4b083c | |||
| 9f8f188d92 | |||
| 2378fd6347 | |||
| 6c40d7fc85 | |||
| 2a0e0a47d8 | |||
| 0ee9848231 | |||
| 6708bf238d | |||
| bafb9744c8 | |||
| 5becf260fa | |||
| 4d0c632c70 | |||
| 4fd5f670fe | |||
| 45e26a7fc8 | |||
| a1272ab84d | |||
| 4840d618c2 | |||
| ac20dc6155 | |||
| 8bb381e78c | |||
| 63fb9d6f07 | |||
| 20898d5ef4 | |||
| 72efc8ceeb | |||
| 6f2287da69 | |||
| 8896f0f988 | |||
| bf483b628e | |||
| b34b489cfd | |||
| 13d94929ec | |||
| e5de9917fd | |||
| a03c4d90b2 | |||
| 038fccd244 | |||
| bffe65478d | |||
| 5a4cbf5b70 | |||
| ea60d76e60 | |||
| d470f817ac | |||
| dc90eab32b | |||
| e01b00298a | |||
| 2daae77094 | |||
| 59a31ed07f | |||
| 060f3ce34c | |||
| ccc3b0a531 | |||
| 0ddfb2956c | |||
| b3bcc6aa06 | |||
| a22b8043b4 | |||
| 078d54411b | |||
| c7e9610b3f | |||
| 3a69628007 | |||
| 3f0dd0f0bc | |||
| da7b8e256f | |||
| 0452e869ec | |||
| 385dac6d31 | |||
| 96823682f9 | |||
| a6e4e83809 | |||
| 1b220426e2 | |||
| 6059967951 | |||
| 44e63667d9 | |||
| 5ba5e22a09 | |||
| b49c87acf1 | |||
| fc040c749d | |||
| e07fadea76 | |||
| 781864b954 | |||
| 4a81360efc | |||
| ae4f7dceec | |||
| c2bd272cf3 | |||
| 0658ce4f11 | |||
| cefbe6a87f | |||
| fbdc665487 | |||
| 0ebf595c11 | |||
| ba69c04c27 | |||
| 78ef840eda | |||
| d73bfdbddb | |||
| f5c862640b | |||
| 9e83c3bdf0 | |||
| 23aa47d828 | |||
| 0f5c15d543 | |||
| 28cc645b93 | |||
| 83f1c3274f | |||
| d64139d812 | |||
| 7a65c2017b | |||
| c01d953292 | |||
| f80f16dfc8 | |||
| 8648f0c005 | |||
| d76a5a6d60 | |||
| 759c91940b | |||
| 84b3bdaac0 | |||
| 8013f35d96 | |||
| c0b65ad1c9 | |||
| 4408ebd40a | |||
| 8d109df851 | |||
| 17476f4d8d | |||
| 495f83c925 | |||
| eb5e3f1d1d | |||
| 04f85fd9a8 | |||
| b8115ff46b | |||
| 0636697289 | |||
| b7c5fda512 | |||
| 69b563948d | |||
| ada5d5b269 | |||
| 2a1ceb67b4 | |||
| 685a5d745e | |||
| 72efc80fdc | |||
| fea06d19f3 | |||
| 681e04b060 | |||
| 2bf568ff46 | |||
| 1421236800 | |||
| e0a9791412 | |||
| 7fd4ad6564 | |||
| d29b18e82e | |||
| e6f12c0734 | |||
| 0f36a88e16 | |||
| e93f37206a | |||
| 4c78612eb4 | |||
| 2fdd512488 | |||
| 9b12f822e1 | |||
| d02e6850c2 | |||
| 6337b46e80 | |||
| 9216bab8c9 | |||
| 9bc086071a | |||
| 80336f77fd | |||
| 200392b462 | |||
| 9c48053b14 | |||
| 18167b9a24 | |||
| 3fbbe0d9f1 | |||
| 6cf7f21dcb | |||
| 8d09f57c0d | |||
| ed148f6685 | |||
| 10f31ac572 | |||
| 35ce4d5e5f |
8
.github/PULL_REQUEST_TEMPLATE.md
vendored
8
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -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.
|
||||
|
||||
@ -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
|
||||
|
||||
40
.travis.yml
40
.travis.yml
@ -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 bash composer install -o --no-interaction --no-progress --prefer-dist
|
||||
- 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"
|
||||
|
||||
98
CHANGELOG.md
98
CHANGELOG.md
@ -1,5 +1,103 @@
|
||||
# Changelog
|
||||
|
||||
## [2.3.8](https://github.com/wallabag/wallabag/tree/2.3.8)
|
||||
[Full Changelog](https://github.com/wallabag/wallabag/compare/2.3.7...2.3.8)
|
||||
|
||||
### Fixes
|
||||
|
||||
- Jump to 2.3.8-dev [#3897](https://github.com/wallabag/wallabag/pull/3897)
|
||||
- material: fix left padding on non-entry pages [#3901](https://github.com/wallabag/wallabag/pull/3901)
|
||||
- Make dev/install/update script posix compatible [#3860](https://github.com/wallabag/wallabag/pull/3860)
|
||||
- epub: fix exception when articles have the same title [#3908](https://github.com/wallabag/wallabag/pull/3908)
|
||||
- Fix PHP warning [#3909](https://github.com/wallabag/wallabag/pull/3909)
|
||||
- Add ability to match many domains for credentials [#3937](https://github.com/wallabag/wallabag/pull/3937)
|
||||
- material: add metadata to list view [#3942](https://github.com/wallabag/wallabag/pull/3942)
|
||||
- Enable no-referrer on img tags, enable strict-origin-when-cross-origin by default [#3943](https://github.com/wallabag/wallabag/pull/3943)
|
||||
- Remove preview picture from share view page#3922
|
||||
- Fix Intl Locale issue [#3964](https://github.com/wallabag/wallabag/pull/3964)
|
||||
|
||||
## [2.3.7](https://github.com/wallabag/wallabag/tree/2.3.7)
|
||||
[Full Changelog](https://github.com/wallabag/wallabag/compare/2.3.6...2.3.7)
|
||||
|
||||
### Fixes
|
||||
|
||||
- Jump to 2.3.7-dev [#3837](https://github.com/wallabag/wallabag/pull/3837)
|
||||
- Fix bad order parameter in the API [#3841](https://github.com/wallabag/wallabag/pull/3841)
|
||||
- Update composer.json to add php-tidy (ext-tidy) [#3853](https://github.com/wallabag/wallabag/pull/3853)
|
||||
- Add dedicated email for site config issue [#3861](https://github.com/wallabag/wallabag/pull/3861)
|
||||
- Fix read & starred status in Pocket import [#3819](https://github.com/wallabag/wallabag/pull/3819)
|
||||
- Fix broken 2 factor auth logo image [#3869](https://github.com/wallabag/wallabag/pull/3869)
|
||||
- Fix CORS for API [#3882](https://github.com/wallabag/wallabag/pull/3882)
|
||||
- Add support of expect parameter to change return object when deleting entry [#3887](https://github.com/wallabag/wallabag/pull/3887)
|
||||
- epub export: fix missing cover image, only for exports of one article [#3886](https://github.com/wallabag/wallabag/pull/3886)
|
||||
- Allow optional --ignore-root-warning [#3885](https://github.com/wallabag/wallabag/pull/3885)
|
||||
- material: fix left padding of content on medium screens [#3893](https://github.com/wallabag/wallabag/pull/3893)
|
||||
- material: hide creation date from card actions on specific sizes [#3894](https://github.com/wallabag/wallabag/pull/3894)
|
||||
|
||||
## [2.3.6](https://github.com/wallabag/wallabag/tree/2.3.6)
|
||||
[Full Changelog](https://github.com/wallabag/wallabag/compare/2.3.5...2.3.6)
|
||||
|
||||
### Fixes
|
||||
|
||||
- Jump to 2.3.6-dev and update release process [#3814](https://github.com/wallabag/wallabag/pull/3814)
|
||||
- Fix tag API leak [#3823](https://github.com/wallabag/wallabag/pull/3823)
|
||||
- Validate imported entry to avoid error on import [#3816](https://github.com/wallabag/wallabag/pull/3816)
|
||||
- Fix incorrect reading time calculation for entries with CJK characters [#3820](https://github.com/wallabag/wallabag/pull/3820)
|
||||
- EntriesExport/epub: replace epub identifier with unique urn [#3827](https://github.com/wallabag/wallabag/pull/3827)
|
||||
- Fix settings field inverted [#3833](https://github.com/wallabag/wallabag/pull/3833)
|
||||
- Cast client id to avoid PG error [#3831](https://github.com/wallabag/wallabag/pull/3831)
|
||||
- Rework of EPUB/PDF exports [#3826](https://github.com/wallabag/wallabag/pull/3826)
|
||||
|
||||
## [2.3.5](https://github.com/wallabag/wallabag/tree/2.3.5)
|
||||
[Full Changelog](https://github.com/wallabag/wallabag/compare/2.3.4...2.3.5)
|
||||
|
||||
### Fixes
|
||||
|
||||
- Jump to 2.3.5-dev and update release process [#3778](https://github.com/wallabag/wallabag/pull/3778)
|
||||
- Remove preview picture from single entry view page [#3765](https://github.com/wallabag/wallabag/pull/3765)
|
||||
- Fix Android app login issue [#3784](https://github.com/wallabag/wallabag/pull/3784)
|
||||
- material: fix missing thumbnail on list view [#3782](https://github.com/wallabag/wallabag/pull/3782)
|
||||
- material: decrease size of tags on list view [#3783](https://github.com/wallabag/wallabag/pull/3783)
|
||||
- build: upgrade yarn dependencies, update prod assets [#3781](https://github.com/wallabag/wallabag/pull/3781)
|
||||
- No more dev for guzzle-site-authenticator [#3810](https://github.com/wallabag/wallabag/pull/3810)
|
||||
|
||||
## [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
53
GNUmakefile
Executable 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
54
Makefile
Executable file → Normal 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 $@
|
||||
|
||||
@ -13,7 +13,7 @@ If you do not have your own server, consider [the wallabag.it hosting solution](
|
||||

|
||||
|
||||
# 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
|
||||
[](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
|
||||
|
||||
@ -4,14 +4,17 @@ A release is mostly a git tag of http://github.com/wallabag/wallabag, following
|
||||
|
||||
### Steps to release
|
||||
|
||||
During this documentation, we assume the release is `$LAST_WALLABAG_RELEASE`.
|
||||
During this documentation, we assume the release is `$LAST_WALLABAG_RELEASE` (like 2.3.4).
|
||||
|
||||
#### Files to edit
|
||||
#### Prepare the release
|
||||
|
||||
- `app/config/wallabag.yml` (`wallabag_core.version`)
|
||||
- `CHANGELOG.md`
|
||||
- Update these files with new information
|
||||
- `app/config/wallabag.yml` (`wallabag_core.version`)
|
||||
- `CHANGELOG.md`
|
||||
- Create a PR named "Prepare $LAST_WALLABAG_RELEASE release".
|
||||
- Wait for test to be ok, merge it.
|
||||
|
||||
#### Create release on GitHub
|
||||
#### Create a new release on GitHub
|
||||
|
||||
- Run these commands to create the tag:
|
||||
|
||||
@ -26,8 +29,8 @@ SYMFONY_ENV=prod composer up --no-dev
|
||||
|
||||
```diff
|
||||
script:
|
||||
- - travis_wait bash composer install -o --no-interaction --no-progress --prefer-dist
|
||||
+ - travis_wait composer update --no-interaction --no-progress
|
||||
- - travis_wait bash composer install -o --no-interaction --no-progress --prefer-dist
|
||||
+ - travis_wait bash composer update -o --no-interaction --no-progress --prefer-dist
|
||||
```
|
||||
|
||||
- Then continue with these commands:
|
||||
@ -42,16 +45,15 @@ git push origin release-$LAST_WALLABAG_RELEASE
|
||||
- Run these command to create the package:
|
||||
|
||||
```
|
||||
make release master /tmp wllbg-release prod
|
||||
make release VERSION=$LAST_WALLABAG_RELEASE
|
||||
```
|
||||
|
||||
- [Create the new release on GitHub](https://github.com/wallabag/wallabag/releases/new). You have to upload on this page the package.
|
||||
- Delete the `release-$LAST_WALLABAG_RELEASE` branch and close the pull request (**DO NOT MERGE IT**).
|
||||
- [Create the new release on GitHub](https://github.com/wallabag/wallabag/releases/new) by targetting the `release-$LAST_WALLABAG_RELEASE` branch. You have to upload the package (generated previously).
|
||||
- Close the previously created pull request (**DO NOT MERGE IT**) and delete the `release-$LAST_WALLABAG_RELEASE` branch.
|
||||
- Update the URL shortener (used on `wllbg.org` to generate links like `https://wllbg.org/latest-v2-package` or `http://wllbg.org/latest-v2`)
|
||||
- Update [the downloads page](https://github.com/wallabag/wallabag.org/blob/master/content/pages/download.md) on the website (MD5 sum, release date)
|
||||
- Update Dockerfile https://github.com/wallabag/docker (and create a new tag)
|
||||
- Update wallabag.org website (downloads, releases and new blog post)
|
||||
- Put the next patch version suffixed with `-dev` in `app/config/config.yml` (`wallabag_core.version`)
|
||||
- Update wallabag.org website (downloads, MD5 sum, releases and new blog post)
|
||||
- Put the next patch version suffixed with `-dev` in `app/config/wallabag.yml` (`wallabag_core.version`)
|
||||
- Drink a :beer:!
|
||||
|
||||
### `composer.lock`
|
||||
|
||||
@ -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') . '
|
||||
|
||||
@ -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 |
1
app/Resources/static/themes/_global/img/logo-square.svg
Normal file
1
app/Resources/static/themes/_global/img/logo-square.svg
Normal 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 |
@ -221,6 +221,12 @@ ol li:last-child {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
iframe,
|
||||
video {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
mark {
|
||||
padding: 0 0.2em;
|
||||
}
|
||||
|
||||
@ -18,6 +18,24 @@ main {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@mixin mixin-reading-time {
|
||||
.reading-time {
|
||||
display: inline-flex;
|
||||
vertical-align: middle;
|
||||
|
||||
.card-reading-time,
|
||||
.card-created-at {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
span {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
.card-content .card-title,
|
||||
.card-reveal .card-title {
|
||||
@ -98,14 +116,7 @@ main {
|
||||
margin-right: 5px !important;
|
||||
}
|
||||
|
||||
.reading-time {
|
||||
display: inline-flex;
|
||||
vertical-align: middle;
|
||||
|
||||
span {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
@include mixin-reading-time;
|
||||
}
|
||||
|
||||
.card-image {
|
||||
@ -116,13 +127,6 @@ main {
|
||||
height: 13.5em;
|
||||
}
|
||||
|
||||
.card-image .preview,
|
||||
.card-fullimage .preview {
|
||||
height: 14em;
|
||||
background: no-repeat 50%/cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
&.sw {
|
||||
max-width: 370px;
|
||||
margin-left: auto;
|
||||
@ -137,6 +141,19 @@ a.original:not(.waves-effect) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.card .card-image .preview,
|
||||
.card .card-fullimage .preview,
|
||||
.card-stacked .preview {
|
||||
height: 100%;
|
||||
background: no-repeat 50%/cover;
|
||||
background-color: #efefef;
|
||||
display: block;
|
||||
|
||||
&--default {
|
||||
background-size: contain;
|
||||
}
|
||||
}
|
||||
|
||||
.card-entry-labels li,
|
||||
.card-tag-labels li {
|
||||
margin: 10px 10px 10px auto;
|
||||
@ -205,24 +222,29 @@ a.original:not(.waves-effect) {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.preview {
|
||||
.card-preview {
|
||||
max-width: 100px;
|
||||
height: auto;
|
||||
max-height: 50px;
|
||||
margin-right: 10px;
|
||||
flex: 1;
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
div.metadata {
|
||||
overflow: hidden;
|
||||
height: 1.5em;
|
||||
display: flex;
|
||||
|
||||
ul.tags {
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.chip {
|
||||
background-color: $blueAccentColor;
|
||||
padding: 0 15px 0 10px;
|
||||
margin: auto 2px;
|
||||
padding: 0 7px;
|
||||
margin: auto 1px;
|
||||
border-radius: 6px;
|
||||
line-height: 22px;
|
||||
height: 22px;
|
||||
|
||||
a,
|
||||
i {
|
||||
@ -236,6 +258,16 @@ a.original:not(.waves-effect) {
|
||||
padding-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
@include mixin-reading-time {
|
||||
padding: 0 5px;
|
||||
flex-wrap: wrap;
|
||||
margin-left: auto;
|
||||
|
||||
i.material-icons {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div.card-content {
|
||||
@ -269,9 +301,3 @@ a.original:not(.waves-effect) {
|
||||
.settings .div_tabs {
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 992px) {
|
||||
.card-tag-labels li {
|
||||
max-width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,6 +24,10 @@ main,
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.typo-logo {
|
||||
max-width: 150px;
|
||||
}
|
||||
|
||||
#main {
|
||||
flex: 1 0 auto;
|
||||
|
||||
|
||||
@ -12,6 +12,16 @@
|
||||
.pagination {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.card-tag-labels li {
|
||||
max-width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 993px) {
|
||||
body.entry main #content {
|
||||
padding-left: 70px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 992px) {
|
||||
@ -163,4 +173,17 @@
|
||||
.row .col {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.card-stacked div.metadata .reading-time {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 310px),
|
||||
screen and (min-width: 601px) and (max-width: 660px),
|
||||
screen and (min-width: 993px) and (max-width: 1050px),
|
||||
screen and (min-width: 1201px) and (max-width: 1250px) {
|
||||
.card .card-action .reading-time .card-created-at {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
@ -148,18 +147,18 @@ nelmio_cors:
|
||||
paths:
|
||||
'^/api/':
|
||||
allow_origin: ['*']
|
||||
allow_headers: ['X-Custom-Auth']
|
||||
allow_headers: ['Authorization','content-type']
|
||||
allow_methods: ['POST', 'PUT', 'PATCH','GET', 'DELETE']
|
||||
max_age: 3600
|
||||
'^/oauth/':
|
||||
allow_origin: ['*']
|
||||
allow_headers: ['X-Custom-Auth']
|
||||
allow_headers: ['Authorization','content-type']
|
||||
allow_methods: ['POST', 'PUT', 'GET', 'DELETE']
|
||||
max_age: 3600
|
||||
'^/':
|
||||
#origin_regex: true
|
||||
allow_origin: ['^http://localhost:[0-9]+']
|
||||
allow_headers: ['X-Custom-Auth']
|
||||
allow_origin: ['*']
|
||||
allow_headers: ['Authorization','content-type']
|
||||
allow_methods: ['POST', 'PUT', 'GET', 'DELETE']
|
||||
max_age: 3600
|
||||
hosts: ['^api\.']
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
wallabag_core:
|
||||
version: 2.3.3
|
||||
version: 2.3.8
|
||||
paypal_url: "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9UBA65LG3FX9Y&lc=gb"
|
||||
languages:
|
||||
en: 'English'
|
||||
|
||||
@ -43,6 +43,7 @@
|
||||
"ext-iconv": "*",
|
||||
"ext-tokenizer": "*",
|
||||
"ext-pdo": "*",
|
||||
"ext-tidy": "*",
|
||||
"symfony/symfony": "~3.3.13",
|
||||
"doctrine/orm": "^2.5.12",
|
||||
"doctrine/doctrine-bundle": "^1.8.0",
|
||||
@ -83,15 +84,16 @@
|
||||
"javibravo/simpleue": "^2.0",
|
||||
"symfony/dom-crawler": "^3.3.13",
|
||||
"friendsofsymfony/jsrouting-bundle": "^1.6.3",
|
||||
"bdunogier/guzzle-site-authenticator": "^1.0.0@dev",
|
||||
"bdunogier/guzzle-site-authenticator": "^1.0.0",
|
||||
"defuse/php-encryption": "^2.1",
|
||||
"html2text/html2text": "^4.1"
|
||||
"html2text/html2text": "^4.1",
|
||||
"sulu/symfony-intl-fix": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/doctrine-fixtures-bundle": "~2.2",
|
||||
"doctrine/data-fixtures": "~1.1",
|
||||
"sensio/generator-bundle": "^3.0",
|
||||
"symfony/phpunit-bridge": "^3.3",
|
||||
"symfony/phpunit-bridge": "^4.2",
|
||||
"friendsofphp/php-cs-fixer": "~2.0",
|
||||
"m6web/redis-mock": "^2.0",
|
||||
"dama/doctrine-test-bundle": "^4.0"
|
||||
@ -124,7 +126,11 @@
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Wallabag\\": "src/Wallabag/" },
|
||||
"classmap": [ "app/AppKernel.php", "app/AppCache.php" ]
|
||||
"classmap": [ "app/AppKernel.php", "app/AppCache.php" ],
|
||||
"exclude-from-classmap": [
|
||||
"vendor/symfony/intl/Locale.php",
|
||||
"vendor/symfony/symfony/src/Symfony/Component/Intl/Locale.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": { "Tests\\": "tests/" }
|
||||
|
||||
7748
composer.lock
generated
Normal file
7748
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
|
||||
|
||||
@ -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",
|
||||
|
||||
10
scripts/dev.sh
Normal file → Executable file
10
scripts/dev.sh
Normal file → Executable file
@ -3,10 +3,14 @@
|
||||
# eg: `sh dev.sh`
|
||||
|
||||
COMPOSER_COMMAND='composer'
|
||||
REQUIRE_FILE='scripts/require.sh'
|
||||
|
||||
DIR="${BASH_SOURCE}"
|
||||
if [ ! -d "$DIR" ]; then DIR="$PWD/scripts"; fi
|
||||
. "$DIR/require.sh"
|
||||
if [ ! -f "$REQUIRE_FILE" ]; then
|
||||
echo "Cannot find $REQUIRE_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
. "$REQUIRE_FILE"
|
||||
|
||||
$COMPOSER_COMMAND install
|
||||
php bin/console wallabag:install
|
||||
|
||||
33
scripts/install.sh
Normal file → Executable file
33
scripts/install.sh
Normal file → Executable file
@ -2,13 +2,38 @@
|
||||
# You can execute this file to install wallabag
|
||||
# eg: `sh install.sh prod`
|
||||
|
||||
IGNORE_ROOT_ARG="--ignore-root-warning"
|
||||
IGNORE_ROOT=0
|
||||
|
||||
while :; do
|
||||
case $1 in
|
||||
$IGNORE_ROOT_ARG) IGNORE_ROOT=1
|
||||
;;
|
||||
*[a-zA-Z]) ENV=$1
|
||||
;;
|
||||
*) break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# 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'
|
||||
REQUIRE_FILE='scripts/require.sh'
|
||||
|
||||
DIR="${BASH_SOURCE}"
|
||||
if [ ! -d "$DIR" ]; then DIR="$PWD/scripts"; fi
|
||||
. "$DIR/require.sh"
|
||||
if [ ! -f "$REQUIRE_FILE" ]; then
|
||||
echo "Cannot find $REQUIRE_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
. "$REQUIRE_FILE"
|
||||
|
||||
ENV=$1
|
||||
TAG=$(git describe --tags $(git rev-list --tags --max-count=1))
|
||||
|
||||
git checkout $TAG
|
||||
|
||||
12
scripts/release.sh
Normal file → Executable file
12
scripts/release.sh
Normal file → Executable 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
|
||||
@ -9,12 +9,12 @@ 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 && php bin/console wallabag:install --env=$ENV
|
||||
git clone git@github.com:wallabag/wallabag.git -b release-$VERSION $TMP_FOLDER/$RELEASE_FOLDER/$VERSION
|
||||
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 -n
|
||||
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
0
scripts/require.sh
Normal file → Executable file
32
scripts/update.sh
Normal file → Executable file
32
scripts/update.sh
Normal file → Executable file
@ -2,16 +2,40 @@
|
||||
# You can execute this file to update wallabag
|
||||
# eg: `sh update.sh prod`
|
||||
|
||||
IGNORE_ROOT_ARG="--ignore-root-warning"
|
||||
IGNORE_ROOT=0
|
||||
|
||||
while :; do
|
||||
case $1 in
|
||||
$IGNORE_ROOT_ARG) IGNORE_ROOT=1
|
||||
;;
|
||||
*[a-zA-Z]) ENV=$1
|
||||
;;
|
||||
*) break
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# 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
|
||||
|
||||
COMPOSER_COMMAND='composer'
|
||||
REQUIRE_FILE='scripts/require.sh'
|
||||
|
||||
DIR="${BASH_SOURCE}"
|
||||
if [ ! -d "$DIR" ]; then DIR="$PWD/scripts"; fi
|
||||
. "$DIR/require.sh"
|
||||
if [ ! -f "$REQUIRE_FILE" ]; then
|
||||
echo "Cannot find $REQUIRE_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ENV=$1
|
||||
. "$REQUIRE_FILE"
|
||||
|
||||
rm -rf var/cache/*
|
||||
git fetch origin
|
||||
|
||||
@ -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');
|
||||
|
||||
@ -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')
|
||||
|
||||
@ -9,6 +9,7 @@ use Nelmio\ApiDocBundle\Annotation\ApiDoc;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
use Wallabag\CoreBundle\Entity\Entry;
|
||||
@ -98,24 +99,28 @@ class EntryRestController extends WallabagRestController
|
||||
$isArchived = (null === $request->query->get('archive')) ? null : (bool) $request->query->get('archive');
|
||||
$isStarred = (null === $request->query->get('starred')) ? null : (bool) $request->query->get('starred');
|
||||
$isPublic = (null === $request->query->get('public')) ? null : (bool) $request->query->get('public');
|
||||
$sort = $request->query->get('sort', 'created');
|
||||
$order = $request->query->get('order', 'desc');
|
||||
$sort = strtolower($request->query->get('sort', 'created'));
|
||||
$order = strtolower($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 */
|
||||
$pager = $this->get('wallabag_core.entry_repository')->findEntries(
|
||||
$this->getUser()->getId(),
|
||||
$isArchived,
|
||||
$isStarred,
|
||||
$isPublic,
|
||||
$sort,
|
||||
$order,
|
||||
$since,
|
||||
$tags
|
||||
);
|
||||
try {
|
||||
/** @var \Pagerfanta\Pagerfanta $pager */
|
||||
$pager = $this->get('wallabag_core.entry_repository')->findEntries(
|
||||
$this->getUser()->getId(),
|
||||
$isArchived,
|
||||
$isStarred,
|
||||
$isPublic,
|
||||
$sort,
|
||||
$order,
|
||||
$since,
|
||||
$tags
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
throw new BadRequestHttpException($e->getMessage());
|
||||
}
|
||||
|
||||
$pager->setMaxPerPage($perPage);
|
||||
$pager->setCurrentPage($page);
|
||||
@ -253,7 +258,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 +352,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 +466,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']));
|
||||
}
|
||||
|
||||
@ -565,18 +570,31 @@ class EntryRestController extends WallabagRestController
|
||||
* @ApiDoc(
|
||||
* requirements={
|
||||
* {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
|
||||
* },
|
||||
* parameters={
|
||||
* {"name"="expect", "dataType"="string", "required"=false, "format"="id or entry", "description"="Only returns the id instead of the deleted entry's full entity if 'id' is specified. Default to entry"},
|
||||
* }
|
||||
* )
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function deleteEntriesAction(Entry $entry)
|
||||
public function deleteEntriesAction(Entry $entry, Request $request)
|
||||
{
|
||||
$expect = $request->query->get('expect', 'entry');
|
||||
if (!\in_array($expect, ['id', 'entry'], true)) {
|
||||
throw new BadRequestHttpException(sprintf("expect: 'id' or 'entry' expected, %s given", $expect));
|
||||
}
|
||||
$this->validateAuthentication();
|
||||
$this->validateUserAccess($entry->getUser()->getId());
|
||||
|
||||
// We copy $entry to keep id in returned object
|
||||
$e = $entry;
|
||||
$response = $this->sendResponse([
|
||||
'id' => $entry->getId(),
|
||||
]);
|
||||
// We clone $entry to keep id in returned object
|
||||
if ('entry' === $expect) {
|
||||
$e = clone $entry;
|
||||
$response = $this->sendResponse($e);
|
||||
}
|
||||
|
||||
$em = $this->getDoctrine()->getManager();
|
||||
$em->remove($entry);
|
||||
@ -585,7 +603,7 @@ class EntryRestController extends WallabagRestController
|
||||
// entry deleted, dispatch event about it!
|
||||
$this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry));
|
||||
|
||||
return $this->sendResponse($e);
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -46,12 +46,14 @@ class TagRestController extends WallabagRestController
|
||||
$this->validateAuthentication();
|
||||
$label = $request->get('tag', '');
|
||||
|
||||
$tag = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneByLabel($label);
|
||||
$tags = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findByLabelsAndUser([$label], $this->getUser()->getId());
|
||||
|
||||
if (empty($tag)) {
|
||||
if (empty($tags)) {
|
||||
throw $this->createNotFoundException('Tag not found');
|
||||
}
|
||||
|
||||
$tag = $tags[0];
|
||||
|
||||
$this->getDoctrine()
|
||||
->getRepository('WallabagCoreBundle:Entry')
|
||||
->removeTag($this->getUser()->getId(), $tag);
|
||||
@ -80,15 +82,7 @@ class TagRestController extends WallabagRestController
|
||||
|
||||
$tagsLabels = $request->get('tags', '');
|
||||
|
||||
$tags = [];
|
||||
|
||||
foreach (explode(',', $tagsLabels) as $tagLabel) {
|
||||
$tagEntity = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneByLabel($tagLabel);
|
||||
|
||||
if (!empty($tagEntity)) {
|
||||
$tags[] = $tagEntity;
|
||||
}
|
||||
}
|
||||
$tags = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findByLabelsAndUser(explode(',', $tagsLabels), $this->getUser()->getId());
|
||||
|
||||
if (empty($tags)) {
|
||||
throw $this->createNotFoundException('Tags not found');
|
||||
@ -120,6 +114,12 @@ class TagRestController extends WallabagRestController
|
||||
{
|
||||
$this->validateAuthentication();
|
||||
|
||||
$tagFromDb = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findByLabelsAndUser([$tag->getLabel()], $this->getUser()->getId());
|
||||
|
||||
if (empty($tagFromDb)) {
|
||||
throw $this->createNotFoundException('Tag not found');
|
||||
}
|
||||
|
||||
$this->getDoctrine()
|
||||
->getRepository('WallabagCoreBundle:Entry')
|
||||
->removeTag($this->getUser()->getId(), $tag);
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,7 +11,7 @@ use Wallabag\UserBundle\Entity\User;
|
||||
|
||||
/**
|
||||
* @ORM\Table("oauth2_clients")
|
||||
* @ORM\Entity
|
||||
* @ORM\Entity(repositoryClass="Wallabag\ApiBundle\Repository\ClientRepository")
|
||||
*/
|
||||
class Client extends BaseClient
|
||||
{
|
||||
|
||||
19
src/Wallabag/ApiBundle/Repository/ClientRepository.php
Normal file
19
src/Wallabag/ApiBundle/Repository/ClientRepository.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Wallabag\ApiBundle\Repository;
|
||||
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
|
||||
class ClientRepository extends EntityRepository
|
||||
{
|
||||
public function findOneBy(array $criteria, array $orderBy = null)
|
||||
{
|
||||
if (!empty($criteria['id'])) {
|
||||
// cast client id to be an integer to avoid postgres error:
|
||||
// "invalid input syntax for integer"
|
||||
$criteria['id'] = (int) $criteria['id'];
|
||||
}
|
||||
|
||||
return parent::findOneBy($criteria, $orderBy);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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');
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)'
|
||||
)
|
||||
|
||||
@ -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.');
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,6 +58,7 @@ class ExportController extends Controller
|
||||
$method = ucfirst($category);
|
||||
$methodBuilder = 'getBuilderFor' . $method . 'ByUser';
|
||||
$repository = $this->get('wallabag_core.entry_repository');
|
||||
$title = $method;
|
||||
|
||||
if ('tag_entries' === $category) {
|
||||
$tag = $this->get('wallabag_core.tag_repository')->findOneBySlug($request->query->get('tag'));
|
||||
@ -66,6 +67,8 @@ class ExportController extends Controller
|
||||
$this->getUser()->getId(),
|
||||
$tag->getId()
|
||||
);
|
||||
|
||||
$title = 'Tag ' . $tag->getLabel();
|
||||
} else {
|
||||
$entries = $repository
|
||||
->$methodBuilder($this->getUser()->getId())
|
||||
@ -76,7 +79,7 @@ class ExportController extends Controller
|
||||
try {
|
||||
return $this->get('wallabag_core.helper.entries_export')
|
||||
->setEntries($entries)
|
||||
->updateTitle($method)
|
||||
->updateTitle($title)
|
||||
->updateAuthor($method)
|
||||
->exportAs($format);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -14,97 +14,112 @@ class LoadEntryData extends AbstractFixture implements OrderedFixtureInterface
|
||||
*/
|
||||
public function load(ObjectManager $manager)
|
||||
{
|
||||
$entry1 = new Entry($this->getReference('admin-user'));
|
||||
$entry1->setUrl('http://0.0.0.0/entry1');
|
||||
$entry1->setReadingTime(11);
|
||||
$entry1->setDomainName('domain.io');
|
||||
$entry1->setMimetype('text/html');
|
||||
$entry1->setTitle('test title entry1');
|
||||
$entry1->setContent('This is my content /o/');
|
||||
$entry1->setLanguage('en');
|
||||
$entries = [
|
||||
'entry1' => [
|
||||
'user' => 'admin-user',
|
||||
'url' => 'http://0.0.0.0/entry1',
|
||||
'reading_time' => 11,
|
||||
'domain' => 'domain.io',
|
||||
'mime' => 'text/html',
|
||||
'title' => 'test title entry1',
|
||||
'content' => 'This is my content /o/',
|
||||
'language' => 'en',
|
||||
'tags' => ['foo-tag', 'baz-tag'],
|
||||
],
|
||||
'entry2' => [
|
||||
'user' => 'admin-user',
|
||||
'url' => 'http://0.0.0.0/entry2',
|
||||
'reading_time' => 1,
|
||||
'domain' => 'domain.io',
|
||||
'mime' => 'text/html',
|
||||
'title' => 'test title entry2',
|
||||
'content' => 'This is my content /o/',
|
||||
'origin' => 'ftp://oneftp.tld',
|
||||
'language' => 'fr',
|
||||
],
|
||||
'entry3' => [
|
||||
'user' => 'bob-user',
|
||||
'url' => 'http://0.0.0.0/entry3',
|
||||
'reading_time' => 1,
|
||||
'domain' => 'domain.io',
|
||||
'mime' => 'text/html',
|
||||
'title' => 'test title entry3',
|
||||
'content' => 'This is my content /o/',
|
||||
'language' => 'en',
|
||||
'tags' => ['foo-tag', 'bar-tag', 'bob-tag'],
|
||||
],
|
||||
'entry4' => [
|
||||
'user' => 'admin-user',
|
||||
'url' => 'http://0.0.0.0/entry4',
|
||||
'reading_time' => 12,
|
||||
'domain' => 'domain.io',
|
||||
'mime' => 'text/html',
|
||||
'title' => 'test title entry4',
|
||||
'content' => 'This is my content /o/',
|
||||
'language' => 'en',
|
||||
'tags' => ['foo-tag', 'bar-tag'],
|
||||
],
|
||||
'entry5' => [
|
||||
'user' => 'admin-user',
|
||||
'url' => 'http://0.0.0.0/entry5',
|
||||
'reading_time' => 12,
|
||||
'domain' => 'domain.io',
|
||||
'mime' => 'text/html',
|
||||
'title' => 'test title entry5',
|
||||
'content' => 'This is my content /o/',
|
||||
'language' => 'fr',
|
||||
'starred' => true,
|
||||
'preview' => 'http://0.0.0.0/image.jpg',
|
||||
],
|
||||
'entry6' => [
|
||||
'user' => 'admin-user',
|
||||
'url' => 'http://0.0.0.0/entry6',
|
||||
'reading_time' => 12,
|
||||
'domain' => 'domain.io',
|
||||
'mime' => 'text/html',
|
||||
'title' => 'test title entry6',
|
||||
'content' => 'This is my content /o/',
|
||||
'language' => 'de',
|
||||
'archived' => true,
|
||||
'tags' => ['bar-tag'],
|
||||
],
|
||||
];
|
||||
|
||||
$entry1->addTag($this->getReference('foo-tag'));
|
||||
$entry1->addTag($this->getReference('baz-tag'));
|
||||
foreach ($entries as $reference => $item) {
|
||||
$entry = new Entry($this->getReference($item['user']));
|
||||
$entry->setUrl($item['url']);
|
||||
$entry->setReadingTime($item['reading_time']);
|
||||
$entry->setDomainName($item['domain']);
|
||||
$entry->setMimetype($item['mime']);
|
||||
$entry->setTitle($item['title']);
|
||||
$entry->setContent($item['content']);
|
||||
$entry->setLanguage($item['language']);
|
||||
|
||||
$manager->persist($entry1);
|
||||
if (isset($item['tags'])) {
|
||||
foreach ($item['tags'] as $tag) {
|
||||
$entry->addTag($this->getReference($tag));
|
||||
}
|
||||
}
|
||||
|
||||
$this->addReference('entry1', $entry1);
|
||||
if (isset($item['origin'])) {
|
||||
$entry->setOriginUrl($item['origin']);
|
||||
}
|
||||
|
||||
$entry2 = new Entry($this->getReference('admin-user'));
|
||||
$entry2->setUrl('http://0.0.0.0/entry2');
|
||||
$entry2->setReadingTime(1);
|
||||
$entry2->setDomainName('domain.io');
|
||||
$entry2->setMimetype('text/html');
|
||||
$entry2->setTitle('test title entry2');
|
||||
$entry2->setContent('This is my content /o/');
|
||||
$entry2->setOriginUrl('ftp://oneftp.tld');
|
||||
$entry2->setLanguage('fr');
|
||||
if (isset($item['starred'])) {
|
||||
$entry->setStarred($item['starred']);
|
||||
}
|
||||
|
||||
$manager->persist($entry2);
|
||||
if (isset($item['archived'])) {
|
||||
$entry->setArchived($item['archived']);
|
||||
}
|
||||
|
||||
$this->addReference('entry2', $entry2);
|
||||
if (isset($item['preview'])) {
|
||||
$entry->setPreviewPicture($item['preview']);
|
||||
}
|
||||
|
||||
$entry3 = new Entry($this->getReference('bob-user'));
|
||||
$entry3->setUrl('http://0.0.0.0/entry3');
|
||||
$entry3->setReadingTime(1);
|
||||
$entry3->setDomainName('domain.io');
|
||||
$entry3->setMimetype('text/html');
|
||||
$entry3->setTitle('test title entry3');
|
||||
$entry3->setContent('This is my content /o/');
|
||||
$entry3->setLanguage('en');
|
||||
|
||||
$entry3->addTag($this->getReference('foo-tag'));
|
||||
$entry3->addTag($this->getReference('bar-tag'));
|
||||
|
||||
$manager->persist($entry3);
|
||||
|
||||
$this->addReference('entry3', $entry3);
|
||||
|
||||
$entry4 = new Entry($this->getReference('admin-user'));
|
||||
$entry4->setUrl('http://0.0.0.0/entry4');
|
||||
$entry4->setReadingTime(12);
|
||||
$entry4->setDomainName('domain.io');
|
||||
$entry4->setMimetype('text/html');
|
||||
$entry4->setTitle('test title entry4');
|
||||
$entry4->setContent('This is my content /o/');
|
||||
$entry4->setLanguage('en');
|
||||
|
||||
$entry4->addTag($this->getReference('foo-tag'));
|
||||
$entry4->addTag($this->getReference('bar-tag'));
|
||||
|
||||
$manager->persist($entry4);
|
||||
|
||||
$this->addReference('entry4', $entry4);
|
||||
|
||||
$entry5 = new Entry($this->getReference('admin-user'));
|
||||
$entry5->setUrl('http://0.0.0.0/entry5');
|
||||
$entry5->setReadingTime(12);
|
||||
$entry5->setDomainName('domain.io');
|
||||
$entry5->setMimetype('text/html');
|
||||
$entry5->setTitle('test title entry5');
|
||||
$entry5->setContent('This is my content /o/');
|
||||
$entry5->setStarred(true);
|
||||
$entry5->setLanguage('fr');
|
||||
$entry5->setPreviewPicture('http://0.0.0.0/image.jpg');
|
||||
|
||||
$manager->persist($entry5);
|
||||
|
||||
$this->addReference('entry5', $entry5);
|
||||
|
||||
$entry6 = new Entry($this->getReference('admin-user'));
|
||||
$entry6->setUrl('http://0.0.0.0/entry6');
|
||||
$entry6->setReadingTime(12);
|
||||
$entry6->setDomainName('domain.io');
|
||||
$entry6->setMimetype('text/html');
|
||||
$entry6->setTitle('test title entry6');
|
||||
$entry6->setContent('This is my content /o/');
|
||||
$entry6->setArchived(true);
|
||||
$entry6->setLanguage('de');
|
||||
$entry6->addTag($this->getReference('bar-tag'));
|
||||
|
||||
$manager->persist($entry6);
|
||||
|
||||
$this->addReference('entry6', $entry6);
|
||||
$manager->persist($entry);
|
||||
$this->addReference($reference, $entry);
|
||||
}
|
||||
|
||||
$manager->flush();
|
||||
}
|
||||
|
||||
@ -5,19 +5,38 @@ namespace Wallabag\CoreBundle\DataFixtures\ORM;
|
||||
use Doctrine\Common\DataFixtures\AbstractFixture;
|
||||
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
|
||||
use Doctrine\Common\Persistence\ObjectManager;
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Wallabag\CoreBundle\Entity\SiteCredential;
|
||||
|
||||
class LoadSiteCredentialData extends AbstractFixture implements OrderedFixtureInterface
|
||||
class LoadSiteCredentialData extends AbstractFixture implements OrderedFixtureInterface, ContainerAwareInterface
|
||||
{
|
||||
/**
|
||||
* @var ContainerInterface
|
||||
*/
|
||||
private $container;
|
||||
|
||||
public function setContainer(ContainerInterface $container = null)
|
||||
{
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function load(ObjectManager $manager)
|
||||
{
|
||||
$credential = new SiteCredential($this->getReference('admin-user'));
|
||||
$credential->setHost('example.com');
|
||||
$credential->setUsername('foo');
|
||||
$credential->setPassword('bar');
|
||||
$credential->setHost('.super.com');
|
||||
$credential->setUsername($this->container->get('wallabag_core.helper.crypto_proxy')->crypt('.super'));
|
||||
$credential->setPassword($this->container->get('wallabag_core.helper.crypto_proxy')->crypt('bar'));
|
||||
|
||||
$manager->persist($credential);
|
||||
|
||||
$credential = new SiteCredential($this->getReference('admin-user'));
|
||||
$credential->setHost('paywall.example.com');
|
||||
$credential->setUsername($this->container->get('wallabag_core.helper.crypto_proxy')->crypt('paywall.example'));
|
||||
$credential->setPassword($this->container->get('wallabag_core.helper.crypto_proxy')->crypt('bar'));
|
||||
|
||||
$manager->persist($credential);
|
||||
|
||||
|
||||
@ -14,33 +14,22 @@ class LoadTagData extends AbstractFixture implements OrderedFixtureInterface
|
||||
*/
|
||||
public function load(ObjectManager $manager)
|
||||
{
|
||||
$tag1 = new Tag();
|
||||
$tag1->setLabel('foo bar');
|
||||
$tags = [
|
||||
'foo-bar-tag' => 'foo bar', //tag used for EntryControllerTest
|
||||
'bar-tag' => 'bar',
|
||||
'baz-tag' => 'baz', // tag used for ExportControllerTest
|
||||
'foo-tag' => 'foo',
|
||||
'bob-tag' => 'bob', // tag used for TagRestControllerTest
|
||||
];
|
||||
|
||||
$manager->persist($tag1);
|
||||
foreach ($tags as $reference => $label) {
|
||||
$tag = new Tag();
|
||||
$tag->setLabel($label);
|
||||
|
||||
$this->addReference('foo-bar-tag', $tag1);
|
||||
$manager->persist($tag);
|
||||
|
||||
$tag2 = new Tag();
|
||||
$tag2->setLabel('bar');
|
||||
|
||||
$manager->persist($tag2);
|
||||
|
||||
$this->addReference('bar-tag', $tag2);
|
||||
|
||||
$tag3 = new Tag();
|
||||
$tag3->setLabel('baz');
|
||||
|
||||
$manager->persist($tag3);
|
||||
|
||||
$this->addReference('baz-tag', $tag3);
|
||||
|
||||
$tag4 = new Tag();
|
||||
$tag4->setLabel('foo');
|
||||
|
||||
$manager->persist($tag4);
|
||||
|
||||
$this->addReference('foo-tag', $tag4);
|
||||
$this->addReference($reference, $tag);
|
||||
}
|
||||
|
||||
$manager->flush();
|
||||
}
|
||||
|
||||
@ -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 . '%')));
|
||||
@ -108,7 +108,7 @@ class EntryFilterType extends AbstractType
|
||||
->add('httpStatus', TextFilterType::class, [
|
||||
'apply_filter' => function (QueryInterface $filterQuery, $field, $values) {
|
||||
$value = $values['value'];
|
||||
if (false === array_key_exists($value, Response::$statusTexts)) {
|
||||
if (false === \array_key_exists($value, Response::$statusTexts)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -62,11 +62,24 @@ class GrabySiteConfigBuilder implements SiteConfigBuilder
|
||||
$host = substr($host, 4);
|
||||
}
|
||||
|
||||
$credentials = null;
|
||||
if ($this->currentUser) {
|
||||
$credentials = $this->credentialRepository->findOneByHostAndUser($host, $this->currentUser->getId());
|
||||
if (!$this->currentUser) {
|
||||
$this->logger->debug('Auth: no current user defined.');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$hosts = [$host];
|
||||
// will try to see for a host without the first subdomain (fr.example.org & .example.org)
|
||||
$split = explode('.', $host);
|
||||
|
||||
if (\count($split) > 1) {
|
||||
// remove first subdomain
|
||||
array_shift($split);
|
||||
$hosts[] = '.' . implode('.', $split);
|
||||
}
|
||||
|
||||
$credentials = $this->credentialRepository->findOneByHostsAndUser($hosts, $this->currentUser->getId());
|
||||
|
||||
if (null === $credentials) {
|
||||
$this->logger->debug('Auth: no credentials available for host.', ['host' => $host]);
|
||||
|
||||
@ -107,7 +120,7 @@ class GrabySiteConfigBuilder implements SiteConfigBuilder
|
||||
*/
|
||||
protected function processExtraFields($extraFieldsStrings)
|
||||
{
|
||||
if (!is_array($extraFieldsStrings)) {
|
||||
if (!\is_array($extraFieldsStrings)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
@ -47,12 +47,14 @@ class ContentProxy
|
||||
*/
|
||||
public function updateEntry(Entry $entry, $url, array $content = [], $disableContentUpdate = false)
|
||||
{
|
||||
$this->graby->toggleImgNoReferrer(true);
|
||||
if (!empty($content['html'])) {
|
||||
$content['html'] = $this->graby->cleanupHtml($content['html'], $url);
|
||||
}
|
||||
|
||||
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 +67,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 +94,7 @@ class ContentProxy
|
||||
(new LocaleConstraint())
|
||||
);
|
||||
|
||||
if (0 === count($errors)) {
|
||||
if (0 === \count($errors)) {
|
||||
$entry->setLanguage($value);
|
||||
|
||||
return;
|
||||
@ -107,7 +116,7 @@ class ContentProxy
|
||||
(new UrlConstraint())
|
||||
);
|
||||
|
||||
if (0 === count($errors)) {
|
||||
if (0 === \count($errors)) {
|
||||
$entry->setPreviewPicture($value);
|
||||
|
||||
return;
|
||||
@ -176,6 +185,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 +247,7 @@ class ContentProxy
|
||||
*/
|
||||
private function stockEntry(Entry $entry, array $content)
|
||||
{
|
||||
$entry->setUrl($content['url']);
|
||||
$this->updateOriginUrl($entry, $content['url']);
|
||||
|
||||
$this->setEntryDomainName($entry);
|
||||
|
||||
@ -195,24 +257,23 @@ class ContentProxy
|
||||
$entry->setTitle($content['open_graph']['og_title']);
|
||||
}
|
||||
|
||||
$html = $content['html'];
|
||||
if (false === $html) {
|
||||
$html = $this->fetchingErrorMessage;
|
||||
if (empty($content['html'])) {
|
||||
$content['html'] = $this->fetchingErrorMessage;
|
||||
|
||||
if (!empty($content['open_graph']['og_description'])) {
|
||||
$html .= '<p><i>But we found a short description: </i></p>';
|
||||
$html .= $content['open_graph']['og_description'];
|
||||
$content['html'] .= '<p><i>But we found a short description: </i></p>';
|
||||
$content['html'] .= $content['open_graph']['og_description'];
|
||||
}
|
||||
}
|
||||
|
||||
$entry->setContent($html);
|
||||
$entry->setReadingTime(Utils::getReadingTime($html));
|
||||
$entry->setContent($content['html']);
|
||||
$entry->setReadingTime(Utils::getReadingTime($content['html']));
|
||||
|
||||
if (!empty($content['status'])) {
|
||||
$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.
|
||||
*
|
||||
|
||||
@ -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';
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -45,7 +45,7 @@ class EntriesExport
|
||||
*/
|
||||
public function setEntries($entries)
|
||||
{
|
||||
if (!is_array($entries)) {
|
||||
if (!\is_array($entries)) {
|
||||
$this->language = $entries->getLanguage();
|
||||
$entries = [$entries];
|
||||
}
|
||||
@ -85,7 +85,7 @@ class EntriesExport
|
||||
public function updateAuthor($method)
|
||||
{
|
||||
if ('entry' !== $method) {
|
||||
$this->author = $method . ' authors';
|
||||
$this->author = 'Various authors';
|
||||
|
||||
return $this;
|
||||
}
|
||||
@ -150,8 +150,6 @@ class EntriesExport
|
||||
*/
|
||||
|
||||
$book->setTitle($this->title);
|
||||
// Could also be the ISBN number, prefered for published books, or a UUID.
|
||||
$book->setIdentifier($this->title, EPub::IDENTIFIER_URI);
|
||||
// Not needed, but included for the example, Language is mandatory, but EPub defaults to "en". Use RFC3066 Language codes, such as "en", "da", "fr" etc.
|
||||
$book->setLanguage($this->language);
|
||||
$book->setDescription('Some articles saved on my wallabag');
|
||||
@ -167,12 +165,9 @@ class EntriesExport
|
||||
$book->addDublinCoreMetadata(DublinCore::CONTRIBUTOR, 'PHP');
|
||||
$book->addDublinCoreMetadata(DublinCore::CONTRIBUTOR, 'wallabag');
|
||||
|
||||
/*
|
||||
* Front page
|
||||
*/
|
||||
if (file_exists($this->logoPath)) {
|
||||
$book->setCoverImage('Cover.png', file_get_contents($this->logoPath), 'image/png');
|
||||
}
|
||||
$entryIds = [];
|
||||
$entryCount = \count($this->entries);
|
||||
$i = 0;
|
||||
|
||||
/*
|
||||
* Adding actual entries
|
||||
@ -180,21 +175,48 @@ class EntriesExport
|
||||
|
||||
// set tags as subjects
|
||||
foreach ($this->entries as $entry) {
|
||||
++$i;
|
||||
|
||||
/*
|
||||
* Front page
|
||||
* Set if there's only one entry in the given set
|
||||
*/
|
||||
if (1 === $entryCount && null !== $entry->getPreviewPicture()) {
|
||||
$book->setCoverImage($entry->getPreviewPicture());
|
||||
}
|
||||
|
||||
foreach ($entry->getTags() as $tag) {
|
||||
$book->setSubject($tag->getLabel());
|
||||
}
|
||||
$filename = sha1(sprintf('%s:%s', $entry->getUrl(), $entry->getTitle()));
|
||||
|
||||
// the reader in Kobo Devices doesn't likes special caracters
|
||||
// in filenames, we limit to A-z/0-9
|
||||
$filename = preg_replace('/[^A-Za-z0-9\-]/', '', $entry->getTitle());
|
||||
$publishedBy = $entry->getPublishedBy();
|
||||
$authors = $this->translator->trans('export.unknown');
|
||||
if (!empty($publishedBy)) {
|
||||
$authors = implode(',', $publishedBy);
|
||||
}
|
||||
|
||||
$titlepage = $content_start . '<h1>' . $entry->getTitle() . '</h1>' . $this->getExportInformation('PHPePub') . $bookEnd;
|
||||
$book->addChapter('Title', 'Title.html', $titlepage, true, EPub::EXTERNAL_REF_ADD);
|
||||
$titlepage = $content_start .
|
||||
'<h1>' . $entry->getTitle() . '</h1>' .
|
||||
'<dl>' .
|
||||
'<dt>' . $this->translator->trans('entry.view.published_by') . '</dt><dd>' . $authors . '</dd>' .
|
||||
'<dt>' . $this->translator->trans('entry.metadata.reading_time') . '</dt><dd>' . $this->translator->trans('entry.metadata.reading_time_minutes_short', ['%readingTime%' => $entry->getReadingTime()]) . '</dd>' .
|
||||
'<dt>' . $this->translator->trans('entry.metadata.added_on') . '</dt><dd>' . $entry->getCreatedAt()->format('Y-m-d') . '</dd>' .
|
||||
'<dt>' . $this->translator->trans('entry.metadata.address') . '</dt><dd><a href="' . $entry->getUrl() . '">' . $entry->getUrl() . '</a></dd>' .
|
||||
'</dl>' .
|
||||
$bookEnd;
|
||||
$book->addChapter("Entry {$i} of {$entryCount}", "{$filename}_cover.html", $titlepage, true, EPub::EXTERNAL_REF_ADD);
|
||||
$chapter = $content_start . $entry->getContent() . $bookEnd;
|
||||
$book->addChapter($entry->getTitle(), htmlspecialchars($filename) . '.html', $chapter, true, EPub::EXTERNAL_REF_ADD);
|
||||
|
||||
$entryIds[] = $entry->getId();
|
||||
$book->addChapter($entry->getTitle(), "{$filename}.html", $chapter, true, EPub::EXTERNAL_REF_ADD);
|
||||
}
|
||||
|
||||
$book->buildTOC();
|
||||
$book->addChapter('Notices', 'Cover2.html', $content_start . $this->getExportInformation('PHPePub') . $bookEnd);
|
||||
|
||||
// Could also be the ISBN number, prefered for published books, or a UUID.
|
||||
$hash = sha1(sprintf('%s:%s', $this->wallabagUrl, implode(',', $entryIds)));
|
||||
$book->setIdentifier(sprintf('urn:wallabag:%s', $hash), EPub::IDENTIFIER_URI);
|
||||
|
||||
return Response::create(
|
||||
$book->getBook(),
|
||||
@ -202,7 +224,7 @@ class EntriesExport
|
||||
[
|
||||
'Content-Description' => 'File Transfer',
|
||||
'Content-type' => 'application/epub+zip',
|
||||
'Content-Disposition' => 'attachment; filename="' . $this->title . '.epub"',
|
||||
'Content-Disposition' => 'attachment; filename="' . $this->getSanitizedFilename() . '.epub"',
|
||||
'Content-Transfer-Encoding' => 'binary',
|
||||
]
|
||||
);
|
||||
@ -244,9 +266,6 @@ class EntriesExport
|
||||
}
|
||||
$mobi->setContentProvider($content);
|
||||
|
||||
// the browser inside Kindle Devices doesn't likes special caracters either, we limit to A-z/0-9
|
||||
$this->title = preg_replace('/[^A-Za-z0-9\-]/', '', $this->title);
|
||||
|
||||
return Response::create(
|
||||
$mobi->toString(),
|
||||
200,
|
||||
@ -254,7 +273,7 @@ class EntriesExport
|
||||
'Accept-Ranges' => 'bytes',
|
||||
'Content-Description' => 'File Transfer',
|
||||
'Content-type' => 'application/x-mobipocket-ebook',
|
||||
'Content-Disposition' => 'attachment; filename="' . $this->title . '.mobi"',
|
||||
'Content-Disposition' => 'attachment; filename="' . $this->getSanitizedFilename() . '.mobi"',
|
||||
'Content-Transfer-Encoding' => 'binary',
|
||||
]
|
||||
);
|
||||
@ -278,14 +297,6 @@ class EntriesExport
|
||||
$pdf->SetSubject('Articles via wallabag');
|
||||
$pdf->SetKeywords('wallabag');
|
||||
|
||||
/*
|
||||
* Front page
|
||||
*/
|
||||
$pdf->AddPage();
|
||||
$intro = '<h1>' . $this->title . '</h1>' . $this->getExportInformation('tcpdf');
|
||||
|
||||
$pdf->writeHTMLCell(0, 0, '', '', $intro, 0, 1, 0, true, '', true);
|
||||
|
||||
/*
|
||||
* Adding actual entries
|
||||
*/
|
||||
@ -294,6 +305,22 @@ class EntriesExport
|
||||
$pdf->SetKeywords($tag->getLabel());
|
||||
}
|
||||
|
||||
$publishedBy = $entry->getPublishedBy();
|
||||
$authors = $this->translator->trans('export.unknown');
|
||||
if (!empty($publishedBy)) {
|
||||
$authors = implode(',', $publishedBy);
|
||||
}
|
||||
|
||||
$pdf->addPage();
|
||||
$html = '<h1>' . $entry->getTitle() . '</h1>' .
|
||||
'<dl>' .
|
||||
'<dt>' . $this->translator->trans('entry.view.published_by') . '</dt><dd>' . $authors . '</dd>' .
|
||||
'<dt>' . $this->translator->trans('entry.metadata.reading_time') . '</dt><dd>' . $this->translator->trans('entry.metadata.reading_time_minutes_short', ['%readingTime%' => $entry->getReadingTime()]) . '</dd>' .
|
||||
'<dt>' . $this->translator->trans('entry.metadata.added_on') . '</dt><dd>' . $entry->getCreatedAt()->format('Y-m-d') . '</dd>' .
|
||||
'<dt>' . $this->translator->trans('entry.metadata.address') . '</dt><dd><a href="' . $entry->getUrl() . '">' . $entry->getUrl() . '</a></dd>' .
|
||||
'</dl>';
|
||||
$pdf->writeHTMLCell(0, 0, '', '', $html, 0, 1, 0, true, '', true);
|
||||
|
||||
$pdf->AddPage();
|
||||
$html = '<h1>' . $entry->getTitle() . '</h1>';
|
||||
$html .= $entry->getContent();
|
||||
@ -301,6 +328,14 @@ class EntriesExport
|
||||
$pdf->writeHTMLCell(0, 0, '', '', $html, 0, 1, 0, true, '', true);
|
||||
}
|
||||
|
||||
/*
|
||||
* Last page
|
||||
*/
|
||||
$pdf->AddPage();
|
||||
$html = $this->getExportInformation('tcpdf');
|
||||
|
||||
$pdf->writeHTMLCell(0, 0, '', '', $html, 0, 1, 0, true, '', true);
|
||||
|
||||
// set image scale factor
|
||||
$pdf->setImageScale(PDF_IMAGE_SCALE_RATIO);
|
||||
|
||||
@ -310,7 +345,7 @@ class EntriesExport
|
||||
[
|
||||
'Content-Description' => 'File Transfer',
|
||||
'Content-type' => 'application/pdf',
|
||||
'Content-Disposition' => 'attachment; filename="' . $this->title . '.pdf"',
|
||||
'Content-Disposition' => 'attachment; filename="' . $this->getSanitizedFilename() . '.pdf"',
|
||||
'Content-Transfer-Encoding' => 'binary',
|
||||
]
|
||||
);
|
||||
@ -325,7 +360,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);
|
||||
|
||||
@ -356,7 +391,7 @@ class EntriesExport
|
||||
200,
|
||||
[
|
||||
'Content-type' => 'application/csv',
|
||||
'Content-Disposition' => 'attachment; filename="' . $this->title . '.csv"',
|
||||
'Content-Disposition' => 'attachment; filename="' . $this->getSanitizedFilename() . '.csv"',
|
||||
'Content-Transfer-Encoding' => 'UTF-8',
|
||||
]
|
||||
);
|
||||
@ -374,7 +409,7 @@ class EntriesExport
|
||||
200,
|
||||
[
|
||||
'Content-type' => 'application/json',
|
||||
'Content-Disposition' => 'attachment; filename="' . $this->title . '.json"',
|
||||
'Content-Disposition' => 'attachment; filename="' . $this->getSanitizedFilename() . '.json"',
|
||||
'Content-Transfer-Encoding' => 'UTF-8',
|
||||
]
|
||||
);
|
||||
@ -392,7 +427,7 @@ class EntriesExport
|
||||
200,
|
||||
[
|
||||
'Content-type' => 'application/xml',
|
||||
'Content-Disposition' => 'attachment; filename="' . $this->title . '.xml"',
|
||||
'Content-Disposition' => 'attachment; filename="' . $this->getSanitizedFilename() . '.xml"',
|
||||
'Content-Transfer-Encoding' => 'UTF-8',
|
||||
]
|
||||
);
|
||||
@ -418,7 +453,7 @@ class EntriesExport
|
||||
200,
|
||||
[
|
||||
'Content-type' => 'text/plain',
|
||||
'Content-Disposition' => 'attachment; filename="' . $this->title . '.txt"',
|
||||
'Content-Disposition' => 'attachment; filename="' . $this->getSanitizedFilename() . '.txt"',
|
||||
'Content-Transfer-Encoding' => 'UTF-8',
|
||||
]
|
||||
);
|
||||
@ -461,4 +496,15 @@ class EntriesExport
|
||||
|
||||
return str_replace('%IMAGE%', '', $info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a sanitized version of the title by applying translit iconv
|
||||
* and removing non alphanumeric characters, - and space.
|
||||
*
|
||||
* @return string Sanitized filename
|
||||
*/
|
||||
private function getSanitizedFilename()
|
||||
{
|
||||
return preg_replace('/[^A-Za-z0-9\- \']/', '', iconv('utf-8', 'us-ascii//TRANSLIT', $this->title));
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ class PreparePagerForEntries
|
||||
* @param AdapterInterface $adapter
|
||||
* @param User $user If user isn't logged in, we can force it (like for rss)
|
||||
*
|
||||
* @return null|Pagerfanta
|
||||
* @return Pagerfanta|null
|
||||
*/
|
||||
public function prepare(AdapterInterface $adapter, User $user = null)
|
||||
{
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -129,7 +142,7 @@ class EntryRepository extends EntityRepository
|
||||
*
|
||||
* @return Pagerfanta
|
||||
*/
|
||||
public function findEntries($userId, $isArchived = null, $isStarred = null, $isPublic = null, $sort = 'created', $order = 'ASC', $since = 0, $tags = '')
|
||||
public function findEntries($userId, $isArchived = null, $isStarred = null, $isPublic = null, $sort = 'created', $order = 'asc', $since = 0, $tags = '')
|
||||
{
|
||||
$qb = $this->createQueryBuilder('e')
|
||||
->leftJoin('e.tags', 't')
|
||||
@ -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;
|
||||
@ -172,6 +185,10 @@ class EntryRepository extends EntityRepository
|
||||
}
|
||||
}
|
||||
|
||||
if (!\in_array(strtolower($order), ['asc', 'desc'], true)) {
|
||||
throw new \Exception('Order "' . $order . '" parameter is wrong, allowed: asc or desc');
|
||||
}
|
||||
|
||||
if ('created' === $sort) {
|
||||
$qb->orderBy('e.id', $order);
|
||||
} elseif ('updated' === $sort) {
|
||||
@ -260,7 +277,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 +313,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 +337,7 @@ class EntryRepository extends EntityRepository
|
||||
->getQuery()
|
||||
->getResult();
|
||||
|
||||
if (count($res)) {
|
||||
if (\count($res)) {
|
||||
return current($res);
|
||||
}
|
||||
|
||||
@ -414,7 +431,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 +452,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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,16 +19,16 @@ class SiteCredentialRepository extends \Doctrine\ORM\EntityRepository
|
||||
/**
|
||||
* Retrieve one username/password for the given host and userId.
|
||||
*
|
||||
* @param string $host
|
||||
* @param int $userId
|
||||
* @param array $hosts An array of host to look for
|
||||
* @param int $userId
|
||||
*
|
||||
* @return null|array
|
||||
* @return array|null
|
||||
*/
|
||||
public function findOneByHostAndUser($host, $userId)
|
||||
public function findOneByHostsAndUser($hosts, $userId)
|
||||
{
|
||||
$res = $this->createQueryBuilder('s')
|
||||
->select('s.username', 's.password')
|
||||
->where('s.host = :hostname')->setParameter('hostname', $host)
|
||||
->where('s.host IN (:hosts)')->setParameter('hosts', $hosts)
|
||||
->andWhere('s.user = :userId')->setParameter('userId', $userId)
|
||||
->setMaxResults(1)
|
||||
->getQuery()
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
namespace Wallabag\CoreBundle\Repository;
|
||||
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Wallabag\CoreBundle\Entity\Tag;
|
||||
|
||||
class TagRepository extends EntityRepository
|
||||
@ -30,7 +31,7 @@ class TagRepository extends EntityRepository
|
||||
$query->setResultCacheLifetime($cacheLifeTime);
|
||||
}
|
||||
|
||||
return count($query->getArrayResult());
|
||||
return \count($query->getArrayResult());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -45,12 +46,8 @@ class TagRepository extends EntityRepository
|
||||
*/
|
||||
public function findAllTags($userId)
|
||||
{
|
||||
$ids = $this->createQueryBuilder('t')
|
||||
$ids = $this->getQueryBuilderByUser($userId)
|
||||
->select('t.id')
|
||||
->leftJoin('t.entries', 'e')
|
||||
->where('e.user = :userId')->setParameter('userId', $userId)
|
||||
->groupBy('t.id')
|
||||
->orderBy('t.slug')
|
||||
->getQuery()
|
||||
->getArrayResult();
|
||||
|
||||
@ -71,18 +68,30 @@ class TagRepository extends EntityRepository
|
||||
*/
|
||||
public function findAllFlatTagsWithNbEntries($userId)
|
||||
{
|
||||
return $this->createQueryBuilder('t')
|
||||
return $this->getQueryBuilderByUser($userId)
|
||||
->select('t.id, t.label, t.slug, count(e.id) as nbEntries')
|
||||
->distinct(true)
|
||||
->leftJoin('t.entries', 'e')
|
||||
->where('e.user = :userId')
|
||||
->groupBy('t.id')
|
||||
->orderBy('t.slug')
|
||||
->setParameter('userId', $userId)
|
||||
->getQuery()
|
||||
->getArrayResult();
|
||||
}
|
||||
|
||||
public function findByLabelsAndUser($labels, $userId)
|
||||
{
|
||||
$qb = $this->getQueryBuilderByUser($userId)
|
||||
->select('t.id');
|
||||
|
||||
$ids = $qb->andWhere($qb->expr()->in('t.label', $labels))
|
||||
->getQuery()
|
||||
->getArrayResult();
|
||||
|
||||
$tags = [];
|
||||
foreach ($ids as $id) {
|
||||
$tags[] = $this->find($id);
|
||||
}
|
||||
|
||||
return $tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used only in test case to get a tag for our entry.
|
||||
*
|
||||
@ -101,13 +110,9 @@ class TagRepository extends EntityRepository
|
||||
|
||||
public function findForArchivedArticlesByUser($userId)
|
||||
{
|
||||
$ids = $this->createQueryBuilder('t')
|
||||
$ids = $this->getQueryBuilderByUser($userId)
|
||||
->select('t.id')
|
||||
->leftJoin('t.entries', 'e')
|
||||
->where('e.user = :userId')->setParameter('userId', $userId)
|
||||
->andWhere('e.isArchived = true')
|
||||
->groupBy('t.id')
|
||||
->orderBy('t.slug')
|
||||
->getQuery()
|
||||
->getArrayResult();
|
||||
|
||||
@ -118,4 +123,20 @@ class TagRepository extends EntityRepository
|
||||
|
||||
return $tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a sorted list of tags used by a user.
|
||||
*
|
||||
* @param int $userId
|
||||
*
|
||||
* @return QueryBuilder
|
||||
*/
|
||||
private function getQueryBuilderByUser($userId)
|
||||
{
|
||||
return $this->createQueryBuilder('t')
|
||||
->leftJoin('t.entries', 'e')
|
||||
->where('e.user = :userId')->setParameter('userId', $userId)
|
||||
->groupBy('t.id')
|
||||
->orderBy('t.slug');
|
||||
}
|
||||
}
|
||||
|
||||
@ -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'
|
||||
@ -253,6 +253,11 @@ entry:
|
||||
confirm:
|
||||
# delete: "Are you sure you want to remove that article?"
|
||||
# delete_tag: "Are you sure you want to remove that tag from that article?"
|
||||
metadata:
|
||||
# reading_time: "Estimated reading time"
|
||||
# reading_time_minutes_short: "%readingTime% min"
|
||||
# address: "Address"
|
||||
# added_on: "Added on"
|
||||
|
||||
about:
|
||||
page_title: 'Om'
|
||||
@ -402,6 +407,7 @@ tag:
|
||||
|
||||
# export:
|
||||
# footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>'
|
||||
# unknown: 'Unknown'
|
||||
|
||||
import:
|
||||
# page_title: 'Import'
|
||||
@ -544,7 +550,7 @@ site_credential:
|
||||
# create_new_one: Create a new credential
|
||||
# form:
|
||||
# username_label: 'Username'
|
||||
# host_label: 'Host'
|
||||
# host_label: 'Host (subdomain.example.org, .example.org, etc.)'
|
||||
# password_label: 'Password'
|
||||
# save: Save
|
||||
# delete: Delete
|
||||
|
||||
@ -253,6 +253,11 @@ entry:
|
||||
confirm:
|
||||
delete: 'Bist du sicher, dass du diesen Artikel löschen möchtest?'
|
||||
delete_tag: 'Bist du sicher, dass du diesen Tag vom Artikel entfernen möchtest?'
|
||||
metadata:
|
||||
# reading_time: "Estimated reading time"
|
||||
# reading_time_minutes_short: "%readingTime% min"
|
||||
# address: "Address"
|
||||
# added_on: "Added on"
|
||||
|
||||
about:
|
||||
page_title: 'Über'
|
||||
@ -402,6 +407,7 @@ tag:
|
||||
|
||||
export:
|
||||
footer_template: '<div style="text-align:center;"><p>Generiert von wallabag mit Hilfe von %method%</p><p>Bitte öffne <a href="https://github.com/wallabag/wallabag/issues">ein Ticket</a> wenn du ein Problem mit der Darstellung von diesem E-Book auf deinem Gerät hast.</p></div>'
|
||||
# unknown: 'Unknown'
|
||||
|
||||
import:
|
||||
page_title: 'Importieren'
|
||||
@ -544,7 +550,7 @@ site_credential:
|
||||
create_new_one: 'Einen neuen Seitenzugang anlegen'
|
||||
form:
|
||||
username_label: 'Benutzername'
|
||||
host_label: 'Host'
|
||||
host_label: 'Host (subdomain.example.org, .example.org, etc.)'
|
||||
password_label: 'Passwort'
|
||||
save: 'Speichern'
|
||||
delete: 'Löschen'
|
||||
|
||||
@ -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'
|
||||
@ -253,6 +253,11 @@ entry:
|
||||
confirm:
|
||||
delete: "Are you sure you want to remove that article?"
|
||||
delete_tag: "Are you sure you want to remove that tag from that article?"
|
||||
metadata:
|
||||
reading_time: "Estimated reading time"
|
||||
reading_time_minutes_short: "%readingTime% min"
|
||||
address: "Address"
|
||||
added_on: "Added on"
|
||||
|
||||
about:
|
||||
page_title: 'About'
|
||||
@ -402,6 +407,7 @@ tag:
|
||||
|
||||
export:
|
||||
footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>'
|
||||
unknown: 'Unknown'
|
||||
|
||||
import:
|
||||
page_title: 'Import'
|
||||
@ -544,7 +550,7 @@ site_credential:
|
||||
create_new_one: Create a new credential
|
||||
form:
|
||||
username_label: 'Username'
|
||||
host_label: 'Host'
|
||||
host_label: 'Host (subdomain.example.org, .example.org, etc.)'
|
||||
password_label: 'Password'
|
||||
save: Save
|
||||
delete: Delete
|
||||
|
||||
@ -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'
|
||||
@ -253,6 +253,11 @@ entry:
|
||||
confirm:
|
||||
# delete: "Are you sure you want to remove that article?"
|
||||
# delete_tag: "Are you sure you want to remove that tag from that article?"
|
||||
metadata:
|
||||
# reading_time: "Estimated reading time"
|
||||
# reading_time_minutes_short: "%readingTime% min"
|
||||
# address: "Address"
|
||||
# added_on: "Added on"
|
||||
|
||||
about:
|
||||
page_title: 'Acerca de'
|
||||
@ -402,6 +407,7 @@ tag:
|
||||
|
||||
# export:
|
||||
# footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>'
|
||||
# unknown: 'Unknown'
|
||||
|
||||
import:
|
||||
page_title: 'Importar'
|
||||
@ -544,7 +550,7 @@ site_credential:
|
||||
# create_new_one: Create a new credential
|
||||
# form:
|
||||
# username_label: 'Username'
|
||||
# host_label: 'Host'
|
||||
# host_label: 'Host (subdomain.example.org, .example.org, etc.)'
|
||||
# password_label: 'Password'
|
||||
# save: Save
|
||||
# delete: Delete
|
||||
|
||||
@ -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'
|
||||
@ -253,6 +253,11 @@ entry:
|
||||
confirm:
|
||||
# delete: "Are you sure you want to remove that article?"
|
||||
# delete_tag: "Are you sure you want to remove that tag from that article?"
|
||||
metadata:
|
||||
# reading_time: "Estimated reading time"
|
||||
# reading_time_minutes_short: "%readingTime% min"
|
||||
# address: "Address"
|
||||
# added_on: "Added on"
|
||||
|
||||
about:
|
||||
page_title: 'درباره'
|
||||
@ -402,6 +407,7 @@ tag:
|
||||
|
||||
# export:
|
||||
# footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>'
|
||||
# unknown: 'Unknown'
|
||||
|
||||
import:
|
||||
page_title: 'درونریزی'
|
||||
@ -544,7 +550,7 @@ site_credential:
|
||||
# create_new_one: Create a new credential
|
||||
# form:
|
||||
# username_label: 'Username'
|
||||
# host_label: 'Host'
|
||||
# host_label: 'Host (subdomain.example.org, .example.org, etc.)'
|
||||
# password_label: 'Password'
|
||||
# save: Save
|
||||
# delete: Delete
|
||||
|
||||
@ -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 d’accueil"
|
||||
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 d’accueil"
|
||||
redirect_current_page: "Rester sur la page actuelle"
|
||||
pocket_consumer_key_label: "Clé d’authentification Pocket pour importer les données"
|
||||
android_configuration: "Configurez votre application Android"
|
||||
android_instruction: "Appuyez ici pour préremplir votre application Android"
|
||||
@ -253,6 +253,11 @@ entry:
|
||||
confirm:
|
||||
delete: "Voulez-vous vraiment supprimer cet article ?"
|
||||
delete_tag: "Voulez-vous vraiment supprimer ce tag de cet article ?"
|
||||
metadata:
|
||||
reading_time: "Durée de lecture estimée"
|
||||
reading_time_minutes_short: "%readingTime% min"
|
||||
address: "Adresse"
|
||||
added_on: "Ajouté le"
|
||||
|
||||
about:
|
||||
page_title: "À propos"
|
||||
@ -402,6 +407,7 @@ tag:
|
||||
|
||||
export:
|
||||
footer_template: '<div style="text-align:center;"><p>Généré par wallabag with %method%</p><p>Merci d''ouvrir <a href="https://github.com/wallabag/wallabag/issues">un ticket</a> si vous rencontrez des soucis d''affichage avec ce document sur votre support.</p></div>'
|
||||
unknown: 'Inconnu'
|
||||
|
||||
import:
|
||||
page_title: "Importer"
|
||||
@ -544,7 +550,7 @@ site_credential:
|
||||
create_new_one: Créer un nouvel accès à un site
|
||||
form:
|
||||
username_label: 'Identifiant'
|
||||
host_label: 'Domaine'
|
||||
host_label: 'Domaine (subdomain.example.org, .example.org, etc.)'
|
||||
password_label: 'Mot de passe'
|
||||
save: "Sauvegarder"
|
||||
delete: "Supprimer"
|
||||
|
||||
@ -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"
|
||||
@ -253,6 +253,11 @@ entry:
|
||||
confirm:
|
||||
delete: "Vuoi veramente rimuovere quell'articolo?"
|
||||
delete_tag: "Vuoi veramente rimuovere quell'etichetta da quell'articolo?"
|
||||
metadata:
|
||||
# reading_time: "Estimated reading time"
|
||||
# reading_time_minutes_short: "%readingTime% min"
|
||||
# address: "Address"
|
||||
# added_on: "Added on"
|
||||
|
||||
about:
|
||||
page_title: 'A proposito'
|
||||
@ -402,6 +407,7 @@ tag:
|
||||
|
||||
# export:
|
||||
# footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>'
|
||||
# unknown: 'Unknown'
|
||||
|
||||
import:
|
||||
page_title: 'Importa'
|
||||
@ -544,7 +550,7 @@ site_credential:
|
||||
# create_new_one: Create a new credential
|
||||
# form:
|
||||
# username_label: 'Username'
|
||||
# host_label: 'Host'
|
||||
# host_label: 'Host (subdomain.example.org, .example.org, etc.)'
|
||||
# password_label: 'Password'
|
||||
# save: Save
|
||||
# delete: Delete
|
||||
|
||||
@ -253,6 +253,11 @@ entry:
|
||||
confirm:
|
||||
delete: "Sètz segur de voler suprimir aqueste article ?"
|
||||
delete_tag: "Sètz segur de voler levar aquesta etiqueta de l'article ?"
|
||||
metadata:
|
||||
# reading_time: "Estimated reading time"
|
||||
# reading_time_minutes_short: "%readingTime% min"
|
||||
# address: "Address"
|
||||
# added_on: "Added on"
|
||||
|
||||
about:
|
||||
page_title: 'A prepaus'
|
||||
@ -402,6 +407,7 @@ tag:
|
||||
|
||||
export:
|
||||
footer_template: '<div style="text-align:center;"><p>Produch per wallabag amb %method%</p><p>Mercés de dobrir <a href="https://github.com/wallabag/wallabag/issues">una sollicitacion</a> s’avètz de problèmas amb l’afichatge d’aqueste E-Book sus vòstre periferic.</p></div>'
|
||||
# unknown: 'Unknown'
|
||||
|
||||
import:
|
||||
page_title: 'Importar'
|
||||
@ -544,7 +550,7 @@ site_credential:
|
||||
create_new_one: Crear un novèl identificant
|
||||
form:
|
||||
username_label: "Nom d'utilizaire"
|
||||
host_label: 'Òste'
|
||||
host_label: 'Òste (subdomain.example.org, .example.org, etc.)'
|
||||
password_label: 'Senhal'
|
||||
save: 'Enregistrar'
|
||||
delete: 'Suprimir'
|
||||
|
||||
@ -253,6 +253,11 @@ entry:
|
||||
confirm:
|
||||
delete: "Czy jesteś pewien, że chcesz usunąć ten artykuł?"
|
||||
delete_tag: "Czy jesteś pewien, że chcesz usunąć ten tag, z tego artykułu?"
|
||||
metadata:
|
||||
# reading_time: "Estimated reading time"
|
||||
# reading_time_minutes_short: "%readingTime% min"
|
||||
# address: "Address"
|
||||
# added_on: "Added on"
|
||||
|
||||
about:
|
||||
page_title: 'O nas'
|
||||
@ -401,7 +406,8 @@ tag:
|
||||
placeholder: 'Możesz dodać kilka tagów, oddzielając je przecinkami.'
|
||||
|
||||
export:
|
||||
footer_template: '<div style="text-align:center;"><p>Stworzone przez wallabag z %method%</p><p>Proszę zgłoś <a href="https://github.com/wallabag/wallabag/issues">sprawę</a>, jeżeli masz problem z wyświetleniem tego e-booka na swoim urządzeniu.</p></div>'
|
||||
footer_template: '<div style="text-align:center;"><p>Stworzone przez wallabag z %method%</p><p>Proszę zgłoś <a href="https://github.com/wallabag/wallabag/issues">sprawę</a>, jeżeli masz problem z wyświetleniem tego e-booka na swoim urządzeniu.</p></div>'
|
||||
# unknown: 'Unknown'
|
||||
|
||||
import:
|
||||
page_title: 'Import'
|
||||
@ -544,7 +550,7 @@ site_credential:
|
||||
create_new_one: Stwórz nowe poświadczenie
|
||||
form:
|
||||
username_label: 'Nazwa użytkownika'
|
||||
host_label: 'Host'
|
||||
host_label: 'Host (subdomain.example.org, .example.org, etc.)'
|
||||
password_label: 'Hasło'
|
||||
save: Zapisz
|
||||
delete: Usuń
|
||||
|
||||
@ -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'
|
||||
@ -253,6 +253,11 @@ entry:
|
||||
confirm:
|
||||
# delete: "Are you sure you want to remove that article?"
|
||||
# delete_tag: "Are you sure you want to remove that tag from that article?"
|
||||
metadata:
|
||||
# reading_time: "Estimated reading time"
|
||||
# reading_time_minutes_short: "%readingTime% min"
|
||||
# address: "Address"
|
||||
# added_on: "Added on"
|
||||
|
||||
about:
|
||||
page_title: 'Sobre'
|
||||
@ -402,6 +407,7 @@ tag:
|
||||
|
||||
# export:
|
||||
# footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>'
|
||||
# unknown: 'Unknown'
|
||||
|
||||
import:
|
||||
page_title: 'Importar'
|
||||
@ -544,7 +550,7 @@ site_credential:
|
||||
# create_new_one: Create a new credential
|
||||
form:
|
||||
# username_label: 'Username'
|
||||
# host_label: 'Host'
|
||||
# host_label: 'Host (subdomain.example.org, .example.org, etc.)'
|
||||
# password_label: 'Password'
|
||||
save: 'Salvar'
|
||||
delete: 'Apagar'
|
||||
|
||||
@ -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'
|
||||
@ -253,6 +253,11 @@ entry:
|
||||
confirm:
|
||||
# delete: "Are you sure you want to remove that article?"
|
||||
# delete_tag: "Are you sure you want to remove that tag from that article?"
|
||||
metadata:
|
||||
# reading_time: "Estimated reading time"
|
||||
# reading_time_minutes_short: "%readingTime% min"
|
||||
# address: "Address"
|
||||
# added_on: "Added on"
|
||||
|
||||
about:
|
||||
page_title: 'Despre'
|
||||
@ -402,6 +407,7 @@ tag:
|
||||
|
||||
# export:
|
||||
# footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>'
|
||||
# unknown: 'Unknown'
|
||||
|
||||
import:
|
||||
# page_title: 'Import'
|
||||
@ -544,7 +550,7 @@ site_credential:
|
||||
# create_new_one: Create a new credential
|
||||
# form:
|
||||
# username_label: 'Username'
|
||||
# host_label: 'Host'
|
||||
# host_label: 'Host (subdomain.example.org, .example.org, etc.)'
|
||||
# password_label: 'Password'
|
||||
# save: Save
|
||||
# delete: Delete
|
||||
|
||||
@ -241,6 +241,11 @@ entry:
|
||||
save_label: 'Сохранить'
|
||||
public:
|
||||
shared_by_wallabag: "Запись была опубликована <a href='%wallabag_instance%'>wallabag</a>"
|
||||
metadata:
|
||||
# reading_time: "Estimated reading time"
|
||||
# reading_time_minutes_short: "%readingTime% min"
|
||||
# address: "Address"
|
||||
# added_on: "Added on"
|
||||
|
||||
about:
|
||||
page_title: 'О'
|
||||
@ -388,6 +393,10 @@ tag:
|
||||
add: 'Добавить'
|
||||
placeholder: 'Вы можете добавить несколько тегов, разделенных запятой.'
|
||||
|
||||
# export:
|
||||
# footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>'
|
||||
# unknown: 'Unknown'
|
||||
|
||||
import:
|
||||
page_title: 'Импорт'
|
||||
page_description: 'Добро пожаловать в импортер wallabag. Выберите сервис, из которого вы хотите перенести данные.'
|
||||
|
||||
@ -251,6 +251,11 @@ entry:
|
||||
confirm:
|
||||
delete: "คุณแน่ใจหรือไม่ว่าคุณต้องการลบบทความนี้?"
|
||||
delete_tag: "คุณแน่ใจหรือไม่ว่าคุณต้องการลบแท็กจากบทความนี้?"
|
||||
metadata:
|
||||
# reading_time: "Estimated reading time"
|
||||
# reading_time_minutes_short: "%readingTime% min"
|
||||
# address: "Address"
|
||||
# added_on: "Added on"
|
||||
|
||||
about:
|
||||
page_title: 'เกี่ยวกับ'
|
||||
@ -400,6 +405,7 @@ tag:
|
||||
|
||||
export:
|
||||
footer_template: '<div style="text-align:center;"><p>ผลิตโดย wallabag กับ %method%</p><p>ให้ทำการเปิด <a href="https://github.com/wallabag/wallabag/issues">ฉบับนี้</a> ถ้าคุณมีข้อบกพร่องif you have trouble with the display of this E-Book on your device.</p></div>'
|
||||
# unknown: 'Unknown'
|
||||
|
||||
import:
|
||||
page_title: 'นำข้อมูลเช้า'
|
||||
@ -542,7 +548,7 @@ site_credential:
|
||||
create_new_one: สร้างข้อมูลส่วนตัวใหม่
|
||||
form:
|
||||
username_label: 'ชื่อผู้ใช้'
|
||||
host_label: 'โฮส'
|
||||
host_label: 'โฮส (subdomain.example.org, .example.org, etc.)'
|
||||
password_label: 'รหัสผ่าน'
|
||||
save: บันทึก
|
||||
delete: ลบ
|
||||
|
||||
@ -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ığı'
|
||||
@ -251,6 +251,11 @@ entry:
|
||||
confirm:
|
||||
# delete: "Are you sure you want to remove that article?"
|
||||
# delete_tag: "Are you sure you want to remove that tag from that article?"
|
||||
metadata:
|
||||
# reading_time: "Estimated reading time"
|
||||
# reading_time_minutes_short: "%readingTime% min"
|
||||
# address: "Address"
|
||||
# added_on: "Added on"
|
||||
|
||||
about:
|
||||
page_title: 'Hakkımızda'
|
||||
@ -400,6 +405,7 @@ tag:
|
||||
|
||||
# export:
|
||||
# footer_template: '<div style="text-align:center;"><p>Produced by wallabag with %method%</p><p>Please open <a href="https://github.com/wallabag/wallabag/issues">an issue</a> if you have trouble with the display of this E-Book on your device.</p></div>'
|
||||
# unknown: 'Unknown'
|
||||
|
||||
import:
|
||||
page_title: 'İçe Aktar'
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
{% block head %}
|
||||
<meta name="viewport" content="initial-scale=1.0">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="referrer" content="strict-origin-when-cross-origin">
|
||||
<!--[if IE]>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=10">
|
||||
<![endif]-->
|
||||
@ -38,6 +39,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 %}
|
||||
|
||||
@ -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>
|
||||
@ -99,8 +99,8 @@
|
||||
{% if craue_setting('export_epub') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'epub', 'tag' : currentTag }) }}">EPUB</a></li>{% endif %}
|
||||
{% if craue_setting('export_mobi') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'mobi', 'tag' : currentTag }) }}">MOBI</a></li>{% endif %}
|
||||
{% if craue_setting('export_pdf') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'pdf', 'tag' : currentTag }) }}">PDF</a></li>{% endif %}
|
||||
{% if craue_setting('export_csv') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'json', 'tag' : currentTag }) }}">JSON</a></li>{% endif %}
|
||||
{% if craue_setting('export_json') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'csv', 'tag' : currentTag }) }}">CSV</a></li>{% endif %}
|
||||
{% if craue_setting('export_json') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'json', 'tag' : currentTag }) }}">JSON</a></li>{% endif %}
|
||||
{% if craue_setting('export_csv') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'csv', 'tag' : currentTag }) }}">CSV</a></li>{% endif %}
|
||||
{% if craue_setting('export_txt') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'txt', 'tag' : currentTag }) }}">TXT</a></li>{% endif %}
|
||||
{% if craue_setting('export_xml') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'xml', 'tag' : currentTag }) }}">XML</a></li>{% endif %}
|
||||
</ul>
|
||||
|
||||
@ -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,18 +27,18 @@
|
||||
<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 }}&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 }}&title={{ entry.title|url_encode }}&tags={{ entry.tags|join(',')|url_encode }}{% if craue_setting('shaarli_share_origin_url') %}&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&address={{ entry.url|url_encode }}&title={{ entry.title|url_encode }}&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 }}¬es=&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 }}&title={{entry.title|url_encode}}&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 }}&title={{ entry.title|url_encode }}&tags={{ entry.tags|join(',')|url_encode }}{% if craue_setting('shaarli_share_origin_url') %}&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&address={{ entry.url|url_encode }}&title={{ entry.title|url_encode }}&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 }}¬es=&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 }}&title={{entry.title|url_encode}}&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 %}
|
||||
{% if craue_setting('export_pdf') %}<li><a href="{{ path('export_entry', { 'id': entry.id, 'format': 'pdf' }) }}" title="Generate PDF file">PDF</a></li>{% endif %}
|
||||
<li><a href="mailto:hello@wallabag.org?subject=Wrong%20display%20in%20wallabag&body={{ entry.url|url_encode }}" title="{{ 'entry.view.left_menu.problem.label'|trans }}" class="tool bad-display icon icon-delete"><span>{{ 'entry.view.left_menu.problem.label'|trans }}</span></a></li>
|
||||
<li><a href="mailto:siteconfig@wallabag.org?subject=Wrong%20display%20in%20wallabag&body={{ entry.url|url_encode }}" title="{{ 'entry.view.left_menu.problem.label'|trans }}" class="tool bad-display icon icon-delete"><span>{{ 'entry.view.left_menu.problem.label'|trans }}</span></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@ -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 %}
|
||||
@ -96,9 +96,6 @@
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
{% if entry.previewPicture is not null %}
|
||||
<div><img class="preview" src="{{ entry.previewPicture }}" alt="{{ entry.title|e|raw }}" /></div>
|
||||
{% endif %}
|
||||
<article>
|
||||
{{ entry.content | raw }}
|
||||
</article>
|
||||
|
||||
@ -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,11 +27,8 @@
|
||||
<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') }}" />
|
||||
{% endif %}
|
||||
</header>
|
||||
<article class="block">
|
||||
{{ entry.content | raw }}
|
||||
|
||||
@ -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>
|
||||
|
||||
|
||||
@ -8,8 +8,11 @@
|
||||
|
||||
<div class="{{ subClass|default('original grey-text') }}">
|
||||
<a href="{{ entry.url|e }}" target="_blank" title="{{ entry.domainName|removeWww }}" class="tool grey-text">{{ entry.domainName|removeWww }}</a>
|
||||
{% if withTags is defined %}
|
||||
{% if withMetadata is defined %}
|
||||
{% include "@WallabagCore/themes/material/Entry/_tags.html.twig" with {'tags': entry.tags | slice(0, 3), 'entryId': entry.id, 'listClass': ' hide-on-med-and-down'} only %}
|
||||
<div class="reading-time grey-text">
|
||||
<div class="card-reading-time">{% include "@WallabagCore/themes/material/Entry/_reading_time.html.twig" with {'entry': entry} only %}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
<div class="card-action">
|
||||
<span class="reading-time grey-text">
|
||||
{% include "@WallabagCore/themes/material/Entry/_reading_time.html.twig" with {'entry': entry} only %}
|
||||
<i class="material-icons hide-on-med-and-down" title="{{ 'entry.view.created_at'|trans }}">today</i>
|
||||
<span class="hide-on-med-and-down"> {{ entry.createdAt|date('Y-m-d') }}</span>
|
||||
</span>
|
||||
<div class="reading-time grey-text">
|
||||
<div class="card-reading-time">{% include "@WallabagCore/themes/material/Entry/_reading_time.html.twig" with {'entry': entry} only %}</div>
|
||||
<div class="card-created-at">
|
||||
<i class="material-icons" title="{{ 'entry.view.created_at'|trans }}">today</i>
|
||||
<span> {{ entry.createdAt|date('Y-m-d') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="tools right">
|
||||
<li>
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
<div class="card-stacked">
|
||||
<div class="preview">
|
||||
{% if entry.previewPicture is not null %}
|
||||
<a href="{{ path('view', { 'id': entry.id }) }}">
|
||||
<img src="{{ entry.previewPicture }}" />
|
||||
</a>
|
||||
{% endif %}
|
||||
<div class="card-preview">
|
||||
<a href="{{ path('view', { 'id': entry.id }) }}">
|
||||
{% 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, 'withTags': true, 'subClass': 'metadata'} only %}
|
||||
{% include "@WallabagCore/themes/material/Entry/Card/_content.html.twig" with {'entry': entry, 'withMetadata': true, 'subClass': 'metadata'} only %}
|
||||
<ul class="tools-list hide-on-small-only">
|
||||
<li>
|
||||
<a title="{{ 'entry.list.toogle_as_read'|trans }}" class="tool grey-text" href="{{ path('archive_entry', { 'id': entry.id }) }}"><i class="material-icons">{% if entry.isArchived == 0 %}done{% else %}unarchive{% endif %}</i></a>
|
||||
|
||||
@ -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>
|
||||
@ -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 %}
|
||||
|
||||
@ -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>
|
||||
@ -70,8 +68,8 @@
|
||||
{% if craue_setting('export_epub') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'epub', 'tag' : currentTag }) }}">EPUB</a></li>{% endif %}
|
||||
{% if craue_setting('export_mobi') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'mobi', 'tag' : currentTag }) }}">MOBI</a></li>{% endif %}
|
||||
{% if craue_setting('export_pdf') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'pdf', 'tag' : currentTag }) }}">PDF</a></li>{% endif %}
|
||||
{% if craue_setting('export_csv') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'json', 'tag' : currentTag }) }}">JSON</a></li>{% endif %}
|
||||
{% if craue_setting('export_json') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'csv', 'tag' : currentTag }) }}">CSV</a></li>{% endif %}
|
||||
{% if craue_setting('export_json') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'json', 'tag' : currentTag }) }}">JSON</a></li>{% endif %}
|
||||
{% if craue_setting('export_csv') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'csv', 'tag' : currentTag }) }}">CSV</a></li>{% endif %}
|
||||
{% if craue_setting('export_txt') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'txt', 'tag' : currentTag }) }}">TXT</a></li>{% endif %}
|
||||
{% if craue_setting('export_xml') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'xml', 'tag' : currentTag }) }}">XML</a></li>{% endif %}
|
||||
</ul>
|
||||
|
||||
@ -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 }}&title={{ entry.title|striptags|url_encode }}&tags={{ entry.tags|join(',')|striptags|url_encode }}{% if craue_setting('shaarli_share_origin_url') %}&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 }}&title={{ entry.title|striptags|url_encode }}&tags={{ entry.tags|join(',')|striptags|url_encode }}{% if craue_setting('shaarli_share_origin_url') %}&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&address={{ entry.url|url_encode }}&title={{ entry.title|striptags|url_encode }}&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&address={{ entry.url|url_encode }}&title={{ entry.title|striptags|url_encode }}&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 }}&title={{ entry.title|striptags|url_encode }}&notes=&v=1&noui=1&jump=doclose" target="_blank" class="tool icon-image diaspora" title="diaspora">
|
||||
<a href="{{ craue_setting('diaspora_url') }}/bookmarklet?url={{ entry.url|url_encode }}&title={{ entry.title|striptags|url_encode }}&notes=&v=1&noui=1&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 }}&title={{entry.title|striptags|url_encode}}&v=6" target="_blank" class="tool icon-image unmark" title="unmark">
|
||||
<a href="{{ craue_setting('unmark_url') }}/mark/add?url={{ entry.url|url_encode }}&title={{entry.title|striptags|url_encode}}&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 }}&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 }}&title={{ entry.title|striptags|url_encode }}" target="_blank" rel="noopener" title="carrot" class="tool icon-image carrot">
|
||||
<span>Carrot</span>
|
||||
</a>
|
||||
</li>
|
||||
@ -209,7 +209,7 @@
|
||||
</li>
|
||||
|
||||
<li class="bold">
|
||||
<a class="waves-effect collapsible-header" href="mailto:hello@wallabag.org?subject=Wrong%20display%20in%20wallabag&body={{ entry.url|url_encode }}" title="{{ 'entry.view.left_menu.problem.description'|trans }}">
|
||||
<a class="waves-effect collapsible-header" href="mailto:siteconfig@wallabag.org?subject=Wrong%20display%20in%20wallabag&body={{ entry.url|url_encode }}" title="{{ 'entry.view.left_menu.problem.description'|trans }}">
|
||||
<i class="material-icons small">error</i>
|
||||
<span>{{ 'entry.view.left_menu.problem.label'|trans }}</span>
|
||||
</a>
|
||||
@ -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>
|
||||
@ -275,10 +275,6 @@
|
||||
{{ render(controller( "WallabagCoreBundle:Tag:addTagForm", { 'id': entry.id } )) }}
|
||||
</div>
|
||||
|
||||
{% if entry.previewPicture is not null %}
|
||||
<div><img class="preview" src="{{ entry.previewPicture }}" alt="{{ entry.title|striptags|default('entry.default_title'|trans)|raw }}" /></div>
|
||||
{% endif %}
|
||||
|
||||
</aside>
|
||||
<article>
|
||||
{{ entry.content | raw }}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -20,15 +20,14 @@ class Utils
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given text, we calculate reading time for an article
|
||||
* based on 200 words per minute.
|
||||
* For a given text, we calculate reading time for an article based on 200 words per minute.
|
||||
*
|
||||
* @param $text
|
||||
* @param string $text
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
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}\']+|(\p{Han}|\p{Hiragana}|\p{Katakana}|\p{Hangul}){1,2})~u', strip_tags($text))) / 200);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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')));
|
||||
}
|
||||
|
||||
|
||||
@ -52,6 +52,13 @@ abstract class AbstractConsumer
|
||||
|
||||
$this->import->setUser($user);
|
||||
|
||||
if (false === $this->import->validateEntry($storedEntry)) {
|
||||
$this->logger->warning('Entry is invalid', ['entry' => $storedEntry]);
|
||||
|
||||
// return true to skip message
|
||||
return true;
|
||||
}
|
||||
|
||||
$entry = $this->import->parseEntry($storedEntry);
|
||||
|
||||
if (null === $entry) {
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -118,6 +118,15 @@ abstract class AbstractImport implements ImportInterface
|
||||
*/
|
||||
abstract public function parseEntry(array $importedEntry);
|
||||
|
||||
/**
|
||||
* Validate that an entry is valid (like has some required keys, etc.).
|
||||
*
|
||||
* @param array $importedEntry
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function validateEntry(array $importedEntry);
|
||||
|
||||
/**
|
||||
* Fetch content from the ContentProxy (using graby).
|
||||
* If it fails return the given entry to be saved in all case (to avoid user to loose the content).
|
||||
@ -141,9 +150,9 @@ abstract class AbstractImport implements ImportInterface
|
||||
/**
|
||||
* Parse and insert all given entries.
|
||||
*
|
||||
* @param $entries
|
||||
* @param array $entries
|
||||
*/
|
||||
protected function parseEntries($entries)
|
||||
protected function parseEntries(array $entries)
|
||||
{
|
||||
$i = 1;
|
||||
$entryToBeFlushed = [];
|
||||
@ -153,6 +162,10 @@ abstract class AbstractImport implements ImportInterface
|
||||
$importedEntry = $this->setEntryAsRead($importedEntry);
|
||||
}
|
||||
|
||||
if (false === $this->validateEntry($importedEntry)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$entry = $this->parseEntry($importedEntry);
|
||||
|
||||
if (null === $entry) {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -89,7 +89,7 @@ abstract class BrowserImport extends AbstractImport
|
||||
return;
|
||||
}
|
||||
|
||||
if (array_key_exists('children', $importedEntry)) {
|
||||
if (\array_key_exists('children', $importedEntry)) {
|
||||
if ($this->producer) {
|
||||
$this->parseEntriesForProducer($importedEntry['children']);
|
||||
|
||||
@ -101,11 +101,11 @@ abstract class BrowserImport extends AbstractImport
|
||||
return;
|
||||
}
|
||||
|
||||
if (!array_key_exists('uri', $importedEntry) && !array_key_exists('url', $importedEntry)) {
|
||||
if (!\array_key_exists('uri', $importedEntry) && !\array_key_exists('url', $importedEntry)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$url = array_key_exists('uri', $importedEntry) ? $importedEntry['uri'] : $importedEntry['url'];
|
||||
$url = \array_key_exists('uri', $importedEntry) ? $importedEntry['uri'] : $importedEntry['url'];
|
||||
|
||||
$existingEntry = $this->em
|
||||
->getRepository('WallabagCoreBundle:Entry')
|
||||
@ -126,7 +126,7 @@ abstract class BrowserImport extends AbstractImport
|
||||
// update entry with content (in case fetching failed, the given entry will be return)
|
||||
$this->fetchContent($entry, $data['url'], $data);
|
||||
|
||||
if (array_key_exists('tags', $data)) {
|
||||
if (\array_key_exists('tags', $data)) {
|
||||
$this->tagsAssigner->assignTagsToEntry(
|
||||
$entry,
|
||||
$data['tags']
|
||||
@ -149,9 +149,9 @@ abstract class BrowserImport extends AbstractImport
|
||||
/**
|
||||
* Parse and insert all given entries.
|
||||
*
|
||||
* @param $entries
|
||||
* @param array $entries
|
||||
*/
|
||||
protected function parseEntries($entries)
|
||||
protected function parseEntries(array $entries)
|
||||
{
|
||||
$i = 1;
|
||||
$entryToBeFlushed = [];
|
||||
|
||||
@ -30,6 +30,18 @@ class ChromeImport extends BrowserImport
|
||||
return 'import.chrome.description';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateEntry(array $importedEntry)
|
||||
{
|
||||
if (empty($importedEntry['url'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@ -45,7 +57,7 @@ class ChromeImport extends BrowserImport
|
||||
'created_at' => substr($entry['date_added'], 0, 10),
|
||||
];
|
||||
|
||||
if (array_key_exists('tags', $entry) && '' !== $entry['tags']) {
|
||||
if (\array_key_exists('tags', $entry) && '' !== $entry['tags']) {
|
||||
$data['tags'] = $entry['tags'];
|
||||
}
|
||||
|
||||
|
||||
@ -30,6 +30,18 @@ class FirefoxImport extends BrowserImport
|
||||
return 'import.firefox.description';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function validateEntry(array $importedEntry)
|
||||
{
|
||||
if (empty($importedEntry['uri'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@ -45,7 +57,7 @@ class FirefoxImport extends BrowserImport
|
||||
'created_at' => substr($entry['dateAdded'], 0, 10),
|
||||
];
|
||||
|
||||
if (array_key_exists('tags', $entry) && '' !== $entry['tags']) {
|
||||
if (\array_key_exists('tags', $entry) && '' !== $entry['tags']) {
|
||||
$data['tags'] = $entry['tags'];
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user