Merge remote-tracking branch 'origin/master' into 2.5.0

This commit is contained in:
Jeremy Benoist 2022-05-13 13:50:50 +02:00
commit 4947ea6758
No known key found for this signature in database
GPG key ID: BCA73962457ACC3C
106 changed files with 2100 additions and 1461 deletions

View file

@ -6,11 +6,15 @@
- Clone the repository
- Ensure your Docker daemon is running
- Launch `docker-compose up`
- Copy `docker/php/env.example` to `docker/php/env` and customize
- Launch `docker-compose run --rm php composer install` to bootstrap php dependencies
- Launch `docker-compose run --rm php bin/console wallabag:install` to bootstrap your installation
- Launch `docker-compose run --rm php yarn install` to bootstrap dependencies for the frontend
- Launch `docker-compose run --rm php yarn build:dev` to build assets for the frontend
- Launch `docker-compose up -d` to start the stack
You'll then have:
- a web server (nginx)
- a PHP daemon (using FPM)
- a PHP daemon with standalone web server
- a Redis database (to handle imports)
- a SQLite database to store articles
@ -20,7 +24,7 @@ If you want to test using an other database than SQLite, uncomment the `postgres
### Using your own PHP server
- Ensure you are running PHP > 7.1.
- Ensure you are running PHP >= 7.4.
- Clone the repository
- Launch `composer install`
- If you got some errors, fix them (they might be related to some missing PHP extension from your machine)

View file

@ -34,3 +34,10 @@ updates:
- dependency-name: nelmio/api-doc-bundle
versions:
- "> 2.13.4"
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: weekly
time: "04:00"
timezone: Europe/Paris
open-pull-requests-limit: 10

BIN
.github/images/screenshot.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 837 KiB

View file

@ -7,6 +7,9 @@ on:
- master
- 2.*
permissions:
contents: read
jobs:
js:
name: "Building assets"
@ -14,10 +17,10 @@ jobs:
steps:
- name: "Checkout"
uses: "actions/checkout@v2"
uses: "actions/checkout@v3"
- name: "Install Node"
uses: actions/setup-node@v2
uses: actions/setup-node@v3
with:
node-version: "12"

View file

@ -7,6 +7,9 @@ on:
- master
- 2.*
permissions:
contents: read
jobs:
coding-standards:
name: "CS Fixer & PHPStan"
@ -14,14 +17,14 @@ jobs:
steps:
- name: "Checkout"
uses: "actions/checkout@v2"
uses: "actions/checkout@v3"
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
with:
coverage: "none"
php-version: "7.4"
tools: cs2pr, pecl, composer:v2
tools: cs2pr, pecl, composer:2.2
extensions: pdo, pdo_mysql, pdo_sqlite, pdo_pgsql, curl, imagick, pgsql, gd, tidy
ini-values: "date.timezone=Europe/Paris"
env:

View file

@ -29,8 +29,6 @@ jobs:
fail-fast: false
matrix:
php:
- "7.2"
- "7.3"
- "7.4"
- "8.0"
- "8.1"
@ -41,7 +39,7 @@ jobs:
steps:
- name: "Checkout"
uses: "actions/checkout@v2"
uses: "actions/checkout@v3"
with:
fetch-depth: 2
@ -50,7 +48,7 @@ jobs:
with:
php-version: "${{ matrix.php }}"
coverage: none
tools: pecl, composer:v2
tools: pecl, composer:2.2
extensions: json, pdo, pdo_mysql, pdo_sqlite, pdo_pgsql, curl, imagick, pgsql, gd, tidy
ini-values: "date.timezone=Europe/Paris"
@ -58,7 +56,7 @@ jobs:
if: "${{ matrix.database == 'mysql' }}"
run: |
sudo systemctl start mysql.service
sudo mysql -u root -proot -e "CREATE DATABASE wallabag_test"
sudo mysql -u root -proot -h 127.0.0.1 -e "CREATE DATABASE wallabag_test"
- name: "Setup PostgreSQL"
if: "${{ matrix.database == 'pgsql' }}"

View file

@ -7,6 +7,9 @@ on:
- master
- 2.*
permissions:
contents: read
jobs:
translations:
name: "Translations"
@ -19,14 +22,14 @@ jobs:
steps:
- name: "Checkout"
uses: "actions/checkout@v2"
uses: "actions/checkout@v3"
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
with:
coverage: "none"
php-version: "${{ matrix.php }}"
tools: pecl, composer:v2
tools: pecl, composer:2.2
extensions: pdo, pdo_mysql, pdo_sqlite, pdo_pgsql, curl, imagick, pgsql, gd, tidy
ini-values: "date.timezone=Europe/Paris"
env:

8
.gitignore vendored
View file

@ -15,6 +15,7 @@
!/bin/symfony_requirements
.php_cs.cache
.phpunit.result.cache
phpunit.xml
# Parameters
/app/config/parameters.yml
@ -35,16 +36,15 @@ web/uploads/
/build
/coverage
# Development
docker/php/env
# Composer PHAR
/composer.phar
# Data for wallabag
data/db/wallabag*.sqlite
# Docker container logs and data
docker/logs/
docker/data/
# assets stuff
node_modules/
bin

View file

@ -1,3 +1,3 @@
wallabag is mainly developed by [Nicolas Lœuillet](https://github.com/nicosomb), [@j0k3r](https://github.com/j0k3r) and [@tcitworld](https://github.com/tcitworld) under the MIT License.
wallabag is mainly developed by [Nicolas Lœuillet](https://github.com/nicosomb), [@j0k3r](https://github.com/j0k3r), [@tcitworld](https://github.com/tcitworld) and [@Kdecherf](https://github.com/Kdecherf) under the MIT License.
Thank you [to others contributors](https://github.com/wallabag/wallabag/graphs/contributors).

View file

@ -46,7 +46,7 @@ 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
XDEBUG_MODE=off bin/simple-phpunit -v
release: ## Create a package. Need a VERSION parameter (eg: `make release VERSION=master`).
ifndef VERSION

View file

@ -1,20 +1,32 @@
<img src="https://raw.githubusercontent.com/wallabag/logo/master/_default/typo-horizontal/png/sm/logo-typo-horizontal-black-no-bg-no-border-sm.png" align="right" />
# wallabag
![CI](https://github.com/wallabag/wallabag/workflows/CI/badge.svg)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/wallabag/wallabag/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/wallabag/wallabag/?branch=master)
[![Gitter](https://badges.gitter.im/gitterHQ/gitter.svg)](https://gitter.im/wallabag/wallabag)
[![Donation Status](https://img.shields.io/liberapay/goal/wallabag.svg?logo=liberapay)](https://liberapay.com/wallabag/donate)
[![Translation status](https://hosted.weblate.org/widgets/wallabag/-/svg-badge.svg)](https://hosted.weblate.org/engage/wallabag/?utm_source=widget)
![License](https://img.shields.io/github/license/wallabag/wallabag)
# What is wallabag?
wallabag is a self-hostable PHP application allowing you to not miss any content anymore.
Click, save and read it when you can. It extracts content so that you can read it when you have time.
wallabag is a web application allowing you to save web pages for later reading.
Click, save and read it when you want. It extracts content so that you won't be distracted by pop-ups and cie.
More information on our website: [wallabag.org](https://wallabag.org).
You can install it on your own server, or you can create an account on [wallabag.it](https://wallabag.it).
If you do not have your own server, consider [the wallabag.it hosting solution](https://wallabag.it).
![wallabag](./.github/images/screenshot.png)
* Website: [wallabag.org](https://wallabag.org)
* Android app: [wallabag/android-app](https://github.com/wallabag/android-app)
* iOS app: [wallabag/ios-app](https://github.com/wallabag/ios-app)
* Browser extension: [wallabag/wallabagger](https://github.com/wallabag/wallabagger)
## Documentation
The documentation is available at https://doc.wallabag.org.
You can contribute to it through its dedicated repository, available here: https://github.com/wallabag/doc.
## Installation
# Install wallabag
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:
@ -26,16 +38,24 @@ cd wallabag && make install
Now, [configure a virtual host](https://doc.wallabag.org/en/admin/installation/virtualhosts.html) to use your wallabag.
# Run on YunoHost
[![Install Wallabag with YunoHost](https://install-app.yunohost.org/install-with-yunohost.png)](https://install-app.yunohost.org/?app=wallabag2)
### Other methods
Wallabag app for [YunoHost](https://yunohost.org). See [here](https://github.com/YunoHost-Apps/wallabag2_ynh)
Refer to the [installation documentation](https://doc.wallabag.org/en/admin/installation/installation.html) for other installation methods.
# Translate wallabag
## Translation
[wallabag](https://hosted.weblate.org/projects/wallabag/) is being translated using [Weblate](https://weblate.org/), a web tool designed to ease translating for both developers and translators. Feel free to help us [translate wallabag](https://hosted.weblate.org/projects/wallabag/)!
This project uses [Weblate](https://weblate.org/) for translation.
# License
Feel free to help us [translating wallabag](https://hosted.weblate.org/projects/wallabag/).
## Contributing
To learn more about developing wallabag, please refer to the [contribution guide](./.github/CONTRIBUTING.md).
Content extraction relies on [Graby](https://github.com/j0k3r/graby), [php-readability](https://github.com/j0k3r/php-readability) and [ftr-site-config](https://github.com/fivefilters/ftr-site-config).
## License
Copyright © 2013-current Nicolas Lœuillet <nicolas@loeuillet.org>
This work is free. You can redistribute it and/or modify it under the
terms of the MIT License. See the COPYING file for more details.
terms of the MIT License. See the [COPYING.md](./COPYING.md) file for more details.

View file

@ -38,7 +38,7 @@ be locally specified in `composer.lock`:
```json
"config": {
"platform": {
"php": "7.1.3",
"php": "7.4.29",
"ext-something": "4.0"
}
}

View file

@ -6,16 +6,6 @@ main {
#content {
padding: 0 0.5rem;
}
ul.row {
margin: 0.4rem 0 0;
padding: 0 0.75rem;
}
}
.data .card .card-body {
height: 19em;
overflow: hidden;
}
@mixin mixin-reading-time {
@ -37,6 +27,11 @@ main {
}
.card {
.card-content {
padding-bottom: 12px;
flex-grow: 1;
}
.card-content .card-title,
.card-reveal .card-title {
line-height: 22.8px;
@ -95,7 +90,7 @@ main {
}
.card-action {
padding: 10px 5px 10px 15px;
padding: 10px 10px 10px 15px;
ul.links {
margin: 0;
@ -112,8 +107,9 @@ main {
color: #fff;
}
ul.tools li a.tool {
margin-right: 5px !important;
.tool {
display: flex;
margin-right: 0 !important;
}
@include mixin-reading-time;
@ -134,6 +130,12 @@ main {
}
}
.card-body {
display: flex;
flex-grow: 1;
flex-direction: column;
}
a.original:not(.waves-effect) {
text-overflow: ellipsis;
white-space: nowrap;
@ -311,3 +313,23 @@ a.original:not(.waves-effect) {
.settings .div_tabs {
padding-bottom: 15px;
}
.entries-row {
display: grid;
margin: 0.4rem 0 0;
padding: 0 0.75rem;
gap: 20px;
}
.entry-card {
display: flex;
margin: 0;
height: 100%;
flex-direction: column;
}
.tools {
display: flex;
gap: 10px;
}

View file

@ -3,7 +3,7 @@
* ========================================================================== */
.mass-buttons {
margin: 5px;
margin: 10px 5px 10px 20px;
#selectAll {
position: relative;
@ -17,12 +17,13 @@
button {
i {
font-size: 12px;
font-size: 15px;
}
height: 24px;
line-height: 24px;
padding: 0 0.5rem;
margin-right: 0.75rem;
}
}
@ -38,8 +39,13 @@
}
}
.entries {
list-style: none;
}
.collection {
margin: 15px 15px 0;
margin: 5px 15px 0;
padding: 0;
.collection-item {
padding: 7px;

View file

@ -2,6 +2,12 @@
Media queries
========================================================================== */
@media only screen and (min-width: 450px) {
.entries-row {
grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
}
}
@media only screen and (min-width: 992px) {
nav,
body:not(.entry):not(.login) main,
@ -180,15 +186,6 @@
}
}
@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;
}
}
@media only print {
body {
display: block;

View file

@ -41,6 +41,18 @@ homepage:
fos_user:
resource: "@FOSUserBundle/Resources/config/routing/all.xml"
fos_user_security_login:
path: /login
defaults:
_controller: Wallabag\UserBundle\Controller\SecurityController::loginAction
methods: [GET, POST]
fos_user_registration_register:
path: /register
defaults:
_controller: Wallabag\UserBundle\Controller\RegistrationController::registerAction
methods: [GET, POST]
fos_oauth_server_token:
resource: "@FOSOAuthServerBundle/Resources/config/routing/token.xml"

View file

@ -1,6 +1,6 @@
parameters:
test_database_driver: pdo_mysql
test_database_host: localhost
test_database_host: 127.0.0.1
test_database_port: 3306
test_database_name: wallabag_test
test_database_user: root

View file

@ -22,6 +22,7 @@ wallabag_core:
uk: 'Українська'
hr: 'Hrvatski'
cs: 'Čeština'
el: 'Ελληνικά'
items_on_page: 12
theme: material
language: '%locale%'

View file

@ -33,13 +33,15 @@
"issues": "https://github.com/wallabag/wallabag/issues"
},
"require": {
"php": ">=7.2.5",
"php": ">=7.4",
"composer": "< 2.3",
"ext-ctype": "*",
"ext-curl": "*",
"ext-dom": "*",
"ext-gd": "*",
"ext-hash": "*",
"ext-iconv": "*",
"ext-intl": "*",
"ext-json": "*",
"ext-mbstring": "*",
"ext-pcre": "*",
@ -95,7 +97,7 @@
"tecnickcom/tcpdf": "^6.3.0",
"twig/extensions": "^1.5",
"wallabag/php-mobi": "~1.0",
"wallabag/phpepub": "^4.0.7.2",
"wallabag/phpepub": "^4.0.10",
"willdurand/hateoas-bundle": "~2.1"
},
"require-dev": {
@ -162,7 +164,7 @@
"config": {
"bin-dir": "bin",
"platform": {
"php": "7.2.5"
"php": "7.4.29"
},
"sort-packages": true,
"allow-plugins": {

1186
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,52 +1,64 @@
version: '2'
version: '3.8'
services:
nginx:
image: nginx:alpine
ports:
- "8000:80"
volumes:
- ./docker/nginx/nginx.conf:/nginx.conf
- ./docker/logs/nginx:/var/log/nginx
- .:/var/www/html
links:
- php:php
command: nginx -c /nginx.conf
php:
build:
context: docker/php
args:
# Set here your timezone using one of this: https://php.net/manual/en/timezones.php
timezone: 'Europe/Monaco'
ports:
- "9000:9000"
# Use target 'rootless' if you run rootless Docker
target: default
volumes:
- .:/var/www/html
#links:
# - "postgres:rdbms"
# - "mariadb:rdbms"
# Volumes for data-related folders
- assets:/var/www/html/web/assets
- data:/var/www/html/data
- cache:/var/www/.cache
# Uncomment the following volume if you run rootless Docker
# A limitation blocks permissions on root docker, see
# https://github.com/moby/moby/issues/40881
# - type: tmpfs
# target: /var/www/html/var/cache
# Override third-party libraries for dev
# - ../graby-site-config:/var/www/html/vendor/j0k3r/graby-site-config
# - ../php-readability:/var/www/html/vendor/j0k3r/php-readability
# - ../graby:/var/www/html/vendor/j0k3r/graby
# - ../HTMLawed:/var/www/html/vendor/fossar/htmlawed
# - ../PHPePub:/var/www/html/vendor/wallabag/phpepub
# - ../guzzle-site-authenticator:/var/www/html/vendor/bdunogier/guzzle-site-authenticator
env_file:
# Copy docker/php/env.example to docker/php/env and customize
- ./docker/php/env
# Comment non-used DBMS lines
# If all DBMS are commented out, sqlite will be used as default
# - ./docker/postgres/env
# Uncomment the database engine you want, it will use sqlite if both are commented
# - ./docker/mariadb/env
# - ./docker/postgres/env
# Uncomment to enable Xdebug
# - ./docker/php/xdebug
ports:
- 8000:8000
# Uncomment to permit Xdebug remote session
# - 9000:9000
depends_on:
# - mariadb
# - postgres
- redis
# postgres:
# image: postgres:11-alpine
# ports:
# - "5432:5432"
# volumes:
# - ./docker/data/pgsql:/var/lib/postgresql/data
# env_file:
# - ./docker/postgres/env
# image: postgres:11-alpine
# volumes:
# - db-data:/var/lib/postgresql/data
# env_file:
# - ./docker/postgres/env
# healthcheck:
# test: ["CMD-SHELL", "pg_isready -q || exit 1"]
# interval: 10s
# timeout: 3s
# retries: 3
# mariadb:
# image: mariadb:10
# ports:
# - "3306:3306"
# volumes:
# - ./docker/data/mariadb:/var/lib/mysql
# - db-data:/var/lib/mysql
# env_file:
# - ./docker/mariadb/env
@ -56,6 +68,19 @@ services:
# - "15672:15672"
redis:
image: redis:4-alpine
ports:
- "6379:6379"
image: redis:6-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 3
volumes:
db-data:
driver: local
assets:
driver: local
data:
driver: local
cache:
driver: local

View file

@ -2,9 +2,9 @@ MYSQL_ROOT_PASSWORD=wallaroot
MYSQL_USER=wallabag
MYSQL_PASSWORD=wallapass
MYSQL_DATABASE=wallabag
SYMFONY__ENV__DATABASE_DRIVER=pdo_mysql
SYMFONY__ENV__DATABASE_HOST=rdbms
SYMFONY__ENV__DATABASE_PORT=3306
SYMFONY__ENV__DATABASE_NAME=wallabag
SYMFONY__ENV__DATABASE_USER=wallabag
SYMFONY__ENV__DATABASE_PASSWORD=wallapass
DATABASE_DRIVER=pdo_mysql
DATABASE_HOST=mysql
DATABASE_PORT=3306
DATABASE_NAME=wallabag
DATABASE_USER=wallabag
DATABASE_PASSWORD=wallapass

View file

@ -1,89 +0,0 @@
user nginx;
worker_processes 1;
pid /var/run/nginx.pid;
events {
worker_connections 2048;
multi_accept on;
use epoll;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
server_tokens off;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 15;
types_hash_max_size 2048;
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log off;
error_log off;
gzip on;
gzip_disable "msie6";
open_file_cache max=100;
upstream php-upstream {
server php:9000;
}
server {
#server_name domain.tld www.domain.tld;
root /var/www/html/web;
location / {
# try to serve file directly, fallback to app.php
try_files $uri /app.php$is_args$args;
}
# DEV
# This rule should only be placed on your development environment
# In production, don't include this and don't deploy app_dev.php or config.php
location ~ ^/(app_dev|config)\.php(/|$) {
fastcgi_pass php-upstream;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
# When you are using symlinks to link the document root to the
# current version of your application, you should pass the real
# application path instead of the path to the symlink to PHP
# FPM.
# Otherwise, PHP's OPcache may not properly detect changes to
# your PHP files (see https://github.com/zendtech/ZendOptimizerPlus/issues/126
# for more information).
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
}
# PROD
location ~ ^/app\.php(/|$) {
fastcgi_pass php-upstream;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
# When you are using symlinks to link the document root to the
# current version of your application, you should pass the real
# application path instead of the path to the symlink to PHP
# FPM.
# Otherwise, PHP's OPcache may not properly detect changes to
# your PHP files (see https://github.com/zendtech/ZendOptimizerPlus/issues/126
# for more information).
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
# Prevents URIs that include the front controller. This will 404:
# http://domain.tld/app.php/some-path
# Remove the internal directive to allow URIs like this
internal;
}
error_log /var/log/nginx/project_error.log;
access_log /var/log/nginx/project_access.log;
}
}
daemon off;

View file

@ -1,8 +1,19 @@
FROM php:7.2-fpm
FROM php:7.4-fpm AS rootless
# Default timezone. To change it, use the argument in the docker-compose.yml file
ARG timezone='Europe/Paris'
ARG memorylimit='512M'
ARG DEBIAN_FRONTEND=noninteractive
ARG NODE_VERSION=14
RUN apt-get update \
&& apt-get install -y \
ca-certificates \
curl \
gnupg \
lsb-release \
openssl \
software-properties-common
RUN curl 'https://deb.nodesource.com/gpgkey/nodesource.gpg.key' | apt-key add - \
&& echo "deb https://deb.nodesource.com/node_${NODE_VERSION}.x $(lsb_release -cs) main" > /etc/apt/sources.list.d/nodesource.list
RUN apt-get update && apt-get install -y \
libmcrypt-dev \
@ -11,30 +22,77 @@ RUN apt-get update && apt-get install -y \
libxml2-dev \
libpng-dev \
libjpeg-dev \
libwebp-dev \
libsqlite3-dev \
imagemagick \
libmagickwand-dev \
libtidy-dev \
git
RUN docker-php-ext-install \
iconv \
mbstring \
libonig-dev \
libzip-dev \
libfreetype6-dev \
zlib1g-dev \
git \
build-essential \
nodejs
RUN docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp
RUN docker-php-ext-install -j "$(nproc)" \
bcmath \
gd \
gettext \
iconv \
intl \
mbstring \
opcache \
pdo \
pdo_mysql \
pdo_pgsql \
pdo_sqlite \
sockets \
tidy \
bcmath \
zip
RUN printf "\n" | pecl install imagick && docker-php-ext-enable imagick
RUN pecl install redis; \
pecl install imagick; \
pecl install xdebug; \
docker-php-ext-enable \
redis \
imagick \
xdebug \
;
RUN echo "date.timezone="$timezone > /usr/local/etc/php/conf.d/date_timezone.ini
RUN echo "memory_limit ="$memorylimit > /usr/local/etc/php/conf.d/memory_limit.ini
RUN npm install -g yarn
RUN usermod -u 1000 www-data
RUN curl -L -o /usr/local/bin/envsubst https://github.com/a8m/envsubst/releases/download/v1.1.0/envsubst-`uname -s`-`uname -m`; \
chmod +x /usr/local/bin/envsubst
CMD ["php-fpm"]
COPY --from=composer:2.2.12 /usr/bin/composer /usr/local/bin/composer
COPY entrypoint.sh /entrypoint.sh
COPY config/ /opt/wallabag/config/
RUN mkdir -p \
/var/www/html/app/config/ \
/var/www/html/var/cache \
/var/www/html/web/assets \
/var/www/html/data \
/var/www/html/data/db \
/var/www/.cache
ENTRYPOINT ["/entrypoint.sh"]
CMD ["php", "bin/console", "--env=dev", "server:run", "0.0.0.0:8000"]
FROM rootless AS default
ARG USER_UID=1000
ARG USER_GID=1000
RUN groupmod -g 1000 www-data ; \
usermod -u ${USER_UID} -g www-data www-data ; \
touch /usr/local/etc/php/conf.d/wallabag-php.ini \
/var/www/.yarnrc ; \
chown -R www-data: /var/www/html \
/usr/local/etc/php/conf.d/wallabag-php.ini \
/var/www/.cache \
/var/www/.yarnrc
USER www-data

View file

@ -0,0 +1,62 @@
parameters:
database_driver: ${DATABASE_DRIVER:-pdo_sqlite}
database_driver_class: ${DATABASE_DRIVER_CLASS:-~}
database_host: ${DATABASE_HOST:-127.0.0.1}
database_port: ${DATABASE_PORT:-~}
database_name: ${DATABASE_NAME:-symfony}
database_user: ${DATABASE_USER:-root}
database_password: ${DATABASE_PASSWORD:-~}
database_path: ${DATABASE_PATH:-"%kernel.root_dir%/data/db/wallabag.sqlite"}
database_table_prefix: wallabag_
database_socket: null
database_charset: ${DATABASE_CHARSET:-utf8}
domain_name: ${DOMAIN_NAME:-https://www.example.com}
server_name: ${SERVER_NAME:-"Your wallabag instance"}
mailer_transport: ${MAILER_TRANSPORT:-smtp}
mailer_user: ${MAILER_USER:-~}
mailer_password: ${MAILER_PASSWORD:-~}
mailer_host: ${MAILER_HOST:-127.0.0.1}
mailer_port: ${MAILER_PORT:-25}
mailer_encryption: ${MAILER_ENCRYPTION:-~}
mailer_auth_mode: ${MAILER_AUTH_MODE:-~}
locale: ${LOCALE:-en}
# A secret key that's used to generate certain security-related tokens
secret: ${SECRET:-~}
# two factor stuff
twofactor_auth: ${TWOFACTOR_AUTH:-true}
twofactor_sender: ${TWOFACTOR_SENDER:-no-reply@wallabag.org}
# fosuser stuff
fosuser_registration: ${FOSUSER_REGISTRATION:-true}
fosuser_confirmation: ${FOSUSER_CONFIRMATION:-true}
fos_oauth_server_access_token_lifetime: 3600
fos_oauth_server_refresh_token_lifetime: 1209600
from_email: ${FROM_EMAIL:-wallabag@example.com}
rss_limit: 50
# RabbitMQ processing
rabbitmq_host: localhost
rabbitmq_port: 5672
rabbitmq_user: guest
rabbitmq_password: guest
rabbitmq_prefetch_count: 10
# Redis processing
redis_scheme: ${REDIS_SCHEME:-tcp}
redis_host: ${REDIS_HOST:-redis}
redis_port: ${REDIS_PORT:-6379}
redis_path: ${REDIS_PATH:-~}
redis_password: ${REDIS_PASSWORD:-~}
# Sentry
sentry_dsn: ${SENTRY_DSN:-~}
session_handler: ${SESSION_HANDLER:-session.handler.native_file}

View file

@ -0,0 +1,10 @@
memory_limit = ${PHP_MEMORY_LIMIT:-512M}
max_execution_time = ${PHP_MAX_EXECUTION_TIME:-60}
date.time = ${PHP_TIMEZONE:-Europe/Paris}
session.save_handler = ${PHP_SESSION_HANDLER:-files}
session.save_path = "${PHP_SESSION_SAVE_PATH:-}"
upload_max_filesize = 10M

6
docker/php/entrypoint.sh Executable file
View file

@ -0,0 +1,6 @@
#!/bin/sh
envsubst < /opt/wallabag/config/wallabag-php.ini > /usr/local/etc/php/conf.d/wallabag-php.ini
envsubst < /opt/wallabag/config/parameters.yml > /var/www/html/app/config/parameters.yml
exec "$@"

View file

@ -1,6 +0,0 @@
SYMFONY__ENV__DATABASE_DRIVER=pdo_sqlite
SYMFONY__ENV__DATABASE_HOST=127.0.0.1
SYMFONY__ENV__DATABASE_PORT=~
SYMFONY__ENV__DATABASE_NAME=symfony
SYMFONY__ENV__DATABASE_USER=root
SYMFONY__ENV__DATABASE_PASSWORD=~

13
docker/php/env.example Normal file
View file

@ -0,0 +1,13 @@
DATABASE_DRIVER=pdo_sqlite
DATABASE_HOST=127.0.0.1
DATABASE_PORT=~
DATABASE_NAME=symfony
DATABASE_USER=root
DATABASE_PASSWORD=~
DATABASE_PATH="%kernel.project_dir%/data/db/wallabag.sqlite"
DOMAIN_NAME=http://localhost:8000
SECRET=ch4n63m31fy0uc4n
PHP_SESSION_SAVE_PATH=tcp://redis:6379?database=2
PHP_SESSION_HANDLER=redis
SESSION_HANDLER=~
TRUSTED_PROXIES=0.0.0.0/0

2
docker/php/xdebug Normal file
View file

@ -0,0 +1,2 @@
XDEBUG_MODE=debug
XDEBUG_TRIGGER=yes

View file

@ -1,9 +1,10 @@
POSTGRES_USER=wallabag
POSTGRES_PASSWORD=wallapass
POSTGRES_DB=wallabag
SYMFONY__ENV__DATABASE_HOST=rdbms
SYMFONY__ENV__DATABASE_PORT=5432
SYMFONY__ENV__DATABASE_NAME=wallabag
SYMFONY__ENV__DATABASE_USER=wallabag
SYMFONY__ENV__DATABASE_PASSWORD=wallapass
SYMFONY__ENV__DATABASE_DRIVER=pdo_pgsql
DATABASE_HOST=postgres
DATABASE_PORT=5432
DATABASE_NAME=wallabag
DATABASE_USER=wallabag
DATABASE_PASSWORD=wallapass
DATABASE_DRIVER=pdo_pgsql
DATABASE_PATH=null

View file

@ -36,48 +36,48 @@
"url": "https://github.com/wallabag/wallabag/issues"
},
"devDependencies": {
"@babel/core": "^7.17.5",
"@babel/core": "^7.17.10",
"@babel/eslint-parser": "^7.17.0",
"@babel/preset-env": "^7.16.11",
"autoprefixer": "^10.4.2",
"babel-loader": "^8.2.3",
"css-loader": "^6.6.0",
"eslint": "^8.10.0",
"@babel/preset-env": "^7.17.10",
"autoprefixer": "^10.4.7",
"babel-loader": "^8.2.5",
"css-loader": "^6.7.1",
"eslint": "^8.15.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-plugin-import": "^2.25.4",
"eslint-plugin-import": "^2.26.0",
"eslint-webpack-plugin": "^3.1.1",
"file-loader": "^6.2.0",
"lato-font": "^3.0.0",
"mini-css-extract-plugin": "^2.5.3",
"mini-css-extract-plugin": "^2.6.0",
"node-sass": "^7.0.1",
"postcss": "^8.4.7",
"postcss": "^8.4.13",
"postcss-loader": "^6.2.1",
"postcss-scss": "^4.0.3",
"sass": "^1.49.9",
"postcss-scss": "^4.0.4",
"sass": "^1.51.0",
"sass-loader": "^12.6.0",
"style-loader": "^3.3.1",
"stylelint": "^14.5.3",
"stylelint": "^14.8.2",
"stylelint-config-standard": "^25.0.0",
"stylelint-scss": "^4.1.0",
"stylelint-webpack-plugin": "^3.1.1",
"stylelint-scss": "^4.2.0",
"stylelint-webpack-plugin": "^3.2.0",
"terser-webpack-plugin": "^5.3.1",
"url-loader": "^4.1.1",
"webpack": "^5.69.1",
"webpack": "^5.72.0",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.7.4",
"webpack-manifest-plugin": "^4.1.1",
"webpack-dev-server": "^4.9.0",
"webpack-manifest-plugin": "^5.0.0",
"webpack-merge": "^5.7.3"
},
"dependencies": {
"annotator": "wallabag/annotator#master",
"clipboard": "^2.0.10",
"clipboard": "^2.0.11",
"hammerjs": "^2.0.8",
"highlight.js": "^11.4.0",
"highlight.js": "^11.5.1",
"icomoon-free-npm": "^0.0.0",
"jquery": "^3.6.0",
"jquery.cookie": "^1.4.1",
"jr-qrcode": "^1.0.7",
"material-design-icons-iconfont": "^6.4.2",
"material-design-icons-iconfont": "^6.7.0",
"materialize-css": "^0.98.1",
"mathjax": "^3.1.2",
"mousetrap": "^1.6.0",

View file

@ -0,0 +1,32 @@
<?php
namespace Wallabag\ApiBundle\Controller;
use JMS\Serializer\SerializationContext;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use Symfony\Component\HttpFoundation\JsonResponse;
class ConfigRestController extends WallabagRestController
{
/**
* Retrieve configuration for current user.
*
* @ApiDoc()
*
* @return JsonResponse
*/
public function getConfigAction()
{
$this->validateAuthentication();
$json = $this->get('jms_serializer')->serialize(
$this->getUser()->getConfig(),
'json',
SerializationContext::create()->setGroups(['config_api'])
);
return (new JsonResponse())
->setJson($json)
->setStatusCode(JsonResponse::HTTP_OK);
}
}

View file

@ -32,3 +32,8 @@ user:
type: rest
resource: "WallabagApiBundle:UserRest"
name_prefix: api_
config:
type: rest
resource: "WallabagApiBundle:ConfigRest"
name_prefix: api_

View file

@ -41,7 +41,7 @@ class TagAllCommand extends ContainerAwareCommand
$entries = $tagger->tagAllForUser($user);
$io->text('Persist entries... ');
$io->text('Persist ' . \count($entries) . ' entries... ');
$em = $this->getDoctrine()->getManager();
foreach ($entries as $entry) {

View file

@ -43,6 +43,16 @@ class ConfigController extends Controller
$configForm->handleRequest($request);
if ($configForm->isSubmitted() && $configForm->isValid()) {
// force theme to material to avoid using baggy
if ('baggy' === $config->getTheme()) {
$config->setTheme('material');
$this->addFlash(
'notice',
'Baggy is deprecated, forced to Material theme.'
);
}
$em->persist($config);
$em->flush();

View file

@ -8,7 +8,9 @@ use Pagerfanta\Exception\OutOfRangeCurrentPageException;
use Pagerfanta\Pagerfanta;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Wallabag\CoreBundle\Entity\Tag;
@ -88,8 +90,19 @@ class FeedController extends Controller
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function showTagsFeedAction(User $user, Tag $tag, $page)
public function showTagsFeedAction(Request $request, User $user, Tag $tag, $page)
{
$sort = $request->query->get('sort', 'created');
$sorts = [
'created' => 'createdAt',
'updated' => 'updatedAt',
];
if (!isset($sorts[$sort])) {
throw new BadRequestHttpException(sprintf('Sort "%s" is not available.', $sort));
}
$url = $this->generateUrl(
'tag_feed',
[
@ -102,7 +115,8 @@ class FeedController extends Controller
$entriesByTag = $this->get('wallabag_core.entry_repository')->findAllByTagId(
$user->getId(),
$tag->getId()
$tag->getId(),
$sorts[$sort]
);
$pagerAdapter = new ArrayAdapter($entriesByTag);
@ -137,11 +151,28 @@ class FeedController extends Controller
'domainName' => $this->getParameter('domain_name'),
'version' => $this->getParameter('wallabag_core.version'),
'tag' => $tag->getSlug(),
'updated' => $this->prepareFeedUpdatedDate($entries, $sort),
],
new Response('', 200, ['Content-Type' => 'application/atom+xml'])
);
}
private function prepareFeedUpdatedDate(Pagerfanta $entries, $sort = 'created')
{
$currentPageResults = $entries->getCurrentPageResults();
if (isset($currentPageResults[0])) {
$firstEntry = $currentPageResults[0];
if ('created' === $sort) {
return $firstEntry->getCreatedAt();
}
return $firstEntry->getUpdatedAt();
}
return null;
}
/**
* Global method to retrieve entries depending on the given type
* It returns the response to be send.
@ -202,6 +233,7 @@ class FeedController extends Controller
'user' => $user->getUsername(),
'domainName' => $this->getParameter('domain_name'),
'version' => $this->getParameter('wallabag_core.version'),
'updated' => $this->prepareFeedUpdatedDate($entries),
],
new Response('', 200, ['Content-Type' => 'application/atom+xml'])
);

View file

@ -2,6 +2,7 @@
namespace Wallabag\CoreBundle\Controller;
use Doctrine\ORM\QueryBuilder;
use Pagerfanta\Adapter\ArrayAdapter;
use Pagerfanta\Exception\OutOfRangeCurrentPageException;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
@ -190,4 +191,35 @@ class TagController extends Controller
return $this->redirect($redirectUrl);
}
/**
* Tag search results with the current search term.
*
* @Route("/tag/search/{filter}", name="tag_this_search")
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function tagThisSearchAction($filter, Request $request)
{
$currentRoute = $request->query->has('currentRoute') ? $request->query->get('currentRoute') : '';
/** @var QueryBuilder $qb */
$qb = $this->get('wallabag_core.entry_repository')->getBuilderForSearchByUser($this->getUser()->getId(), $filter, $currentRoute);
$em = $this->getDoctrine()->getManager();
$entries = $qb->getQuery()->getResult();
foreach ($entries as $entry) {
$this->get('wallabag_core.tags_assigner')->assignTagsToEntry(
$entry,
$filter
);
$em->persist($entry);
}
$em->flush();
return $this->redirect($this->get('wallabag_core.helper.redirect')->to($request->headers->get('referer'), '', true));
}
}

View file

@ -45,7 +45,7 @@ class ConfigFixtures extends Fixture implements DependentFixtureInterface
$emptyConfig = new Config($this->getReference('empty-user'));
$emptyConfig->setTheme('material');
$emptyConfig->setItemsPerPage(10);
$emptyConfig->setReadingSpeed(200);
$emptyConfig->setReadingSpeed(100);
$emptyConfig->setLanguage('en');
$emptyConfig->setPocketConsumerKey(null);
$emptyConfig->setActionMarkAsRead(0);

View file

@ -43,6 +43,20 @@ class TaggingRuleFixtures extends Fixture implements DependentFixtureInterface
$manager->persist($tr4);
$tr5 = new TaggingRule();
$tr5->setRule('readingTime <= 5');
$tr5->setTags(['shortread']);
$tr5->setConfig($this->getReference('empty-config'));
$manager->persist($tr5);
$tr6 = new TaggingRule();
$tr6->setRule('readingTime > 5');
$tr6->setTags(['longread']);
$tr6->setConfig($this->getReference('empty-config'));
$manager->persist($tr6);
$manager->flush();
}

View file

@ -4,6 +4,7 @@ namespace Wallabag\CoreBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
use Wallabag\UserBundle\Entity\User;
@ -29,6 +30,8 @@ class Config
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*
* @Groups({"config_api"})
*/
private $id;
@ -50,6 +53,8 @@ class Config
* maxMessage = "validator.item_per_page_too_high"
* )
* @ORM\Column(name="items_per_page", type="integer", nullable=false)
*
* @Groups({"config_api"})
*/
private $itemsPerPage;
@ -58,6 +63,8 @@ class Config
*
* @Assert\NotBlank()
* @ORM\Column(name="language", type="string", nullable=false)
*
* @Groups({"config_api"})
*/
private $language;
@ -65,6 +72,8 @@ class Config
* @var string
*
* @ORM\Column(name="feed_token", type="string", nullable=true)
*
* @Groups({"config_api"})
*/
private $feedToken;
@ -77,6 +86,8 @@ class Config
* max = 100000,
* maxMessage = "validator.feed_limit_too_high"
* )
*
* @Groups({"config_api"})
*/
private $feedLimit;
@ -84,6 +95,8 @@ class Config
* @var float
*
* @ORM\Column(name="reading_speed", type="float", nullable=true)
*
* @Groups({"config_api"})
*/
private $readingSpeed;
@ -98,6 +111,8 @@ class Config
* @var int
*
* @ORM\Column(name="action_mark_as_read", type="integer", nullable=true, options={"default" = 0})
*
* @Groups({"config_api"})
*/
private $actionMarkAsRead;
@ -105,6 +120,8 @@ class Config
* @var int
*
* @ORM\Column(name="list_mode", type="integer", nullable=true)
*
* @Groups({"config_api"})
*/
private $listMode;

View file

@ -24,7 +24,13 @@ class ConfigType extends AbstractType
$this->themes = array_combine(
$themes,
array_map(function ($s) {
return ucwords(strtolower(str_replace('-', ' ', $s)));
$cleanTheme = ucwords(strtolower(str_replace('-', ' ', $s)));
if ('Baggy' === $cleanTheme) {
$cleanTheme = 'Baggy (DEPRECATED)';
}
return $cleanTheme;
}, $themes)
);

View file

@ -236,7 +236,9 @@ class ContentProxy
return $rawText;
}
return iconv('UTF-8', 'UTF-8//IGNORE', $rawText);
mb_substitute_character('none');
return mb_convert_encoding($rawText, 'UTF-8', 'UTF-8');
}
/**

View file

@ -188,6 +188,10 @@ class DownloadImages
imagesavealpha($im, true);
imagepng($im, $localPath, ceil(self::REGENERATE_PICTURES_QUALITY / 100 * 9));
$this->logger->debug('DownloadImages: Re-creating png');
break;
case 'webp':
imagewebp($im, $localPath, self::REGENERATE_PICTURES_QUALITY);
$this->logger->debug('DownloadImages: Re-creating webp');
}
imagedestroy($im);
@ -333,6 +337,7 @@ class DownloadImages
'jpeg' => "\xFF\xD8\xFF",
'gif' => 'GIF',
'png' => "\x89\x50\x4e\x47\x0d\x0a",
'webp' => "\x52\x49\x46\x46",
];
$bytes = substr((string) $res->getBody(), 0, 8);
@ -346,7 +351,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', 'webp'], true)) {
$this->logger->error('DownloadImages: Processed image with not allowed extension. Skipping: ' . $imagePath);
return false;

View file

@ -161,8 +161,8 @@ class EntriesExport
*/
$book->setTitle($this->title);
// 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);
// EPub specification requires BCP47-compliant languages, thus we replace _ with -
$book->setLanguage(str_replace('_', '-', $this->language));
$book->setDescription('Some articles saved on my wallabag');
$book->setAuthor($this->author, $this->author);
@ -527,6 +527,8 @@ class EntriesExport
*/
private function getSanitizedFilename()
{
return preg_replace('/[^A-Za-z0-9\- \']/', '', iconv('utf-8', 'us-ascii//TRANSLIT', $this->title));
$transliterator = \Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', \Transliterator::FORWARD);
return preg_replace('/[^A-Za-z0-9\- \']/', '', $transliterator->transliterate($this->title));
}
}

View file

@ -35,8 +35,10 @@ class RuleBasedTagger
{
$rules = $this->getRulesForUser($entry->getUser());
$clonedEntry = $this->fixEntry($entry);
foreach ($rules as $rule) {
if (!$this->rulerz->satisfies($entry, $rule->getRule())) {
if (!$this->rulerz->satisfies($clonedEntry, $rule->getRule())) {
continue;
}
@ -61,14 +63,22 @@ class RuleBasedTagger
public function tagAllForUser(User $user)
{
$rules = $this->getRulesForUser($user);
$entries = [];
$entriesToUpdate = [];
$tagsCache = [];
foreach ($rules as $rule) {
$qb = $this->entryRepository->getBuilderForAllByUser($user->getId());
$entries = $this->rulerz->filter($qb, $rule->getRule());
$entries = $this->entryRepository
->getBuilderForAllByUser($user->getId())
->getQuery()
->getResult();
foreach ($entries as $entry) {
$clonedEntry = $this->fixEntry($entry);
foreach ($rules as $rule) {
if (!$this->rulerz->satisfies($clonedEntry, $rule->getRule())) {
continue;
}
foreach ($entries as $entry) {
foreach ($rule->getTags() as $label) {
// avoid new tag duplicate by manually caching them
if (!isset($tagsCache[$label])) {
@ -78,11 +88,13 @@ class RuleBasedTagger
$tag = $tagsCache[$label];
$entry->addTag($tag);
$entriesToUpdate[] = $entry;
}
}
}
return $entries;
return $entriesToUpdate;
}
/**
@ -114,4 +126,15 @@ class RuleBasedTagger
{
return $user->getConfig()->getTaggingRules();
}
/**
* Update reading time on the fly to match the proper words per minute from the user.
*/
private function fixEntry(Entry $entry)
{
$clonedEntry = clone $entry;
$clonedEntry->setReadingTime($entry->getReadingTime() / $entry->getUser()->getConfig()->getReadingSpeed() * 200);
return $clonedEntry;
}
}

View file

@ -385,9 +385,9 @@ class EntryRepository extends EntityRepository
*
* @return array
*/
public function findAllByTagId($userId, $tagId)
public function findAllByTagId($userId, $tagId, $sort = 'createdAt')
{
return $this->getSortedQueryBuilderByUser($userId)
return $this->getSortedQueryBuilderByUser($userId, $sort)
->innerJoin('e.tags', 't')
->andWhere('t.id = :tagId')->setParameter('tagId', $tagId)
->getQuery()

View file

@ -163,7 +163,7 @@ config:
meaning: Význam
variables_available_description: 'Pro vytvoření pravidel štítkování mohou být použity následující proměnné a operátory:'
variables_available_title: Které proměnné a operátory mohu pro psaní pravidel použít?
how_to_use_them_description: 'Předpokládejme, že chcete označit nové položky štítkem jako je « <i>krátké čtení</i> », pokud je doba čtení kratší než 3 minuty.<br />V takovém případě byste měli do pole <i>Pravidlo</i> zadat « readingTime &lt;= 3 » a do pole <i>Štítky</i> zadat « <i>krátké čtení</i> ».<br />Několik štítků současně lze přidat jejich oddělením čárkou: « <i>krátké čtení, musím přečíst</i> »<br />Složitá pravidla lze zapsat pomocí předdefinovaných operátorů: if « <i>readingTime &gt;= 5 AND domainName = "github.com"</i> » pak označit štítkem jako « <i>dlouhé čtení, GitHub</i> »'
how_to_use_them_description: 'Předpokládejme, že chcete označit nové položky štítkem jako je « <i>krátké čtení</i> », pokud je doba čtení kratší než 3 minuty.<br />V takovém případě byste měli do pole <i>Pravidlo</i> zadat « readingTime &lt;= 3 » a do pole <i>Štítky</i> zadat « <i>krátké čtení</i> ».<br />Několik štítků současně lze přidat jejich oddělením čárkou: « <i>krátké čtení, musím přečíst</i> »<br />Složitá pravidla lze zapsat pomocí předdefinovaných operátorů: if « <i>readingTime &gt;= 5 AND domainName = "www.php.net"</i> » pak označit štítkem jako « <i>dlouhé čtení, php</i> »'
how_to_use_them_title: Jak je použiji?
tagging_rules_definition_description: Jsou to pravidla používaná programem wallabag k automatickému označování nových položek štítkem. <br />Pokaždé, když je přidána nová položky, všechna pravidla štítkování budou použita k přidání štítků, které jste nakonfigurovali, čímž vám ušetří potíže s ručním označováním vašich položek.
tagging_rules_definition_title: Co znamenají „pravidla štítkování“?

View file

@ -150,7 +150,7 @@ config:
tagging_rules_definition_title: Was bedeuten die „Tagging-Regeln“?
tagging_rules_definition_description: Dies sind Regeln von wallabag, um neu hinzugefügte Einträge automatisch zu taggen.<br />Jedes Mal, wenn ein neuer Eintrag hinzugefügt wird, werden die Tagging-Regeln angewandt. Dies erleichtert dir die Arbeit, deine Einträge manuell zu kategorisieren.
how_to_use_them_title: Wie nutze ich sie?
how_to_use_them_description: 'Nehmen wir an, du möchtest deine Einträge als „<i>schnell lesbar</i>“ markiert, wenn die Lesezeit kürzer als drei Minuten ist.<br />In diesem Fall solltest du „readingTime &lt;= 3“ in das Feld <i>Regel</i> und „<i>schnell lesbar</i>“ in das Feld <i>Tags</i> schreiben.<br />Mehrere Tags können gleichzeitig hinzugefügt werden, indem sie durch ein Komma getrennt werden: „<i>schnell lesbar, interessant</i>“.<br />Komplexe Regeln können durch vordefinierte Operatoren geschrieben werden: wenn „<i>readingTime &gt;= 5 AND domainName = "github.com"</i>“ dann tagge als „<i>länger lesen, GitHub</i>“'
how_to_use_them_description: 'Nehmen wir an, du möchtest deine Einträge als „<i>schnell lesbar</i>“ markiert, wenn die Lesezeit kürzer als drei Minuten ist.<br />In diesem Fall solltest du „readingTime &lt;= 3“ in das Feld <i>Regel</i> und „<i>schnell lesbar</i>“ in das Feld <i>Tags</i> schreiben.<br />Mehrere Tags können gleichzeitig hinzugefügt werden, indem sie durch ein Komma getrennt werden: „<i>schnell lesbar, interessant</i>“.<br />Komplexe Regeln können durch vordefinierte Operatoren geschrieben werden: wenn „<i>readingTime &gt;= 5 AND domainName = "www.php.net"</i>“ dann tagge als „<i>länger lesen, php</i>“'
variables_available_title: Welche Variablen und Operatoren kann ich benutzen, um Regeln zu schreiben?
variables_available_description: 'Die folgenden Variablen und Operatoren können genutzt werden, um Tagging-Regeln zu erstellen:'
meaning: Bedeutung

View file

@ -158,7 +158,7 @@ config:
tagging_rules_definition_title: What does “tagging rules” mean?
tagging_rules_definition_description: They are rules used by wallabag to automatically tag new entries.<br />Each time a new entry is added, all the tagging rules will be used to add the tags you configured, thus saving you the trouble of manually classifying your entries.
how_to_use_them_title: How do I use them?
how_to_use_them_description: 'Let us assume you want to tag new entries as « <i>short reading</i> » when the reading time is under 3 minutes.<br />In that case, you should put « readingTime &lt;= 3 » in the <i>Rule</i> field and « <i>short reading</i> » in the <i>Tags</i> field.<br />Several tags can added simultaneously by separating them with a comma: « <i>short reading, must read</i> »<br />Complex rules can be written by using predefined operators: if « <i>readingTime &gt;= 5 AND domainName = "github.com"</i> » then tag as « <i>long reading, GitHub </i> »'
how_to_use_them_description: 'Let us assume you want to tag new entries as « <i>short reading</i> » when the reading time is under 3 minutes.<br />In that case, you should put « readingTime &lt;= 3 » in the <i>Rule</i> field and « <i>short reading</i> » in the <i>Tags</i> field.<br />Several tags can added simultaneously by separating them with a comma: « <i>short reading, must read</i> »<br />Complex rules can be written by using predefined operators: if « <i>readingTime &gt;= 5 AND domainName = "www.php.net"</i> » then tag as « <i>long reading, php</i> »'
variables_available_title: Which variables and operators can I use to write rules?
variables_available_description: 'The following variables and operators can be used to create tagging rules:'
meaning: Meaning
@ -241,6 +241,7 @@ entry:
delete: Delete
export_title: Export
show_same_domain: Show articles with the same domain
assign_search_tag: Assign this search as a tag to each result
filters:
title: Filters
status_label: Status

View file

@ -157,7 +157,7 @@ config:
tagging_rules_definition_title: '¿Qué significa «reglas de etiquetado»?'
tagging_rules_definition_description: 'Son las reglas usadas por wallabag para etiquetar automáticamente los nuevos artículos.<br />Cada vez que un artículo es añadido, todas las reglas de etiquetado automático serán usadas para etiquetarlo, ayudándote a clasificar automáticamente tus artículos.'
how_to_use_them_title: '¿Cómo se utilizan?'
how_to_use_them_description: 'Supongamos que quiere etiquetar los artículos nuevos como « <i>lectura corta</i> » cuando el tiempo de lectura sea menos de 3 minutos.<br /> En este caso, debe poner « readingTime &lt;= 3 » en el campo <i>Regla</i> y « <i>lectura corta</i> » en el campo <i>Etiquetas</i>.<br />Se pueden añadir varias etiquetas al mismo tiempo separadas por comas: « <i>lectura corta, lectura obligada</i> »<br />Se pueden escribir reglas complejas utilizando los operadores predefinidos: si « <i>readingTime &gt;= 5 AND domainName = "github.com"</i> » entonces etiqueta como « <i>lectura larga, GitHub </i> »'
how_to_use_them_description: 'Supongamos que quiere etiquetar los artículos nuevos como « <i>lectura corta</i> » cuando el tiempo de lectura sea menos de 3 minutos.<br /> En este caso, debe poner « readingTime &lt;= 3 » en el campo <i>Regla</i> y « <i>lectura corta</i> » en el campo <i>Etiquetas</i>.<br />Se pueden añadir varias etiquetas al mismo tiempo separadas por comas: « <i>lectura corta, lectura obligada</i> »<br />Se pueden escribir reglas complejas utilizando los operadores predefinidos: si « <i>readingTime &gt;= 5 AND domainName = "www.php.net"</i> » entonces etiqueta como « <i>lectura larga, php</i> »'
variables_available_title: '¿Qué variables y operadores se pueden utilizar para escribir las reglas?'
variables_available_description: 'Las siguientes variables y operadores se pueden utilizar para crear reglas de etiquetado:'
meaning: 'Significado'

View file

@ -158,7 +158,7 @@ config:
tagging_rules_definition_title: Que signifient les règles d'étiquetage automatiques ?
tagging_rules_definition_description: Ce sont des règles utilisées par wallabag pour classer automatiquement vos nouveaux articles.<br />À chaque fois quun nouvel article est ajouté, toutes les règles d'étiquetage automatiques seront utilisées afin dajouter les tags que vous avez configurés, vous épargnant ainsi leffort de classifier vos articles manuellement.
how_to_use_them_title: Comment les utiliser ?
how_to_use_them_description: 'Imaginons que voulez attribuer aux nouveaux articles l''étiquette « <i>lecture courte</i> » lorsque le temps de lecture est inférieur à 3 minutes.<br />Dans ce cas, vous devriez mettre « readingTime &lt;= 3 » dans le champ <i>Règle</i> et « <i>lecture courte</i> » dans le champ <i>Tag</i>.<br />Plusieurs tags peuvent être ajoutés simultanément en les séparant par des virgules : « <i>lecture courte, à lire</i> »<br />Des règles complexes peuvent être créées en utilisant des opérateurs prédéfinis: si « <i>readingTime &gt;= 5 AND domainName = "github.com"</i> » alors attribuer les étiquettes « <i>lecture longue, GitHub</i> »'
how_to_use_them_description: 'Imaginons que voulez attribuer aux nouveaux articles l''étiquette « <i>lecture courte</i> » lorsque le temps de lecture est inférieur à 3 minutes.<br />Dans ce cas, vous devriez mettre « readingTime &lt;= 3 » dans le champ <i>Règle</i> et « <i>lecture courte</i> » dans le champ <i>Tag</i>.<br />Plusieurs tags peuvent être ajoutés simultanément en les séparant par des virgules : « <i>lecture courte, à lire</i> »<br />Des règles complexes peuvent être créées en utilisant des opérateurs prédéfinis: si « <i>readingTime &gt;= 5 AND domainName = "www.php.net"</i> » alors attribuer les étiquettes « <i>lecture longue, php</i> »'
variables_available_title: Quelles variables et opérateurs puis-je utiliser pour écrire des règles ?
variables_available_description: 'Les variables et opérateurs suivants peuvent être utilisés pour écrire des règles d''étiquetage automatiques :'
meaning: Signification

View file

@ -203,7 +203,7 @@ config:
title: PMF
form_rules:
faq:
how_to_use_them_description: 'Demos por feito que queres etiquetar as novas entradas con « <i>lectura rápida</i> » cando o tempo de lectura non supera os 3 minutos.<br />Neste caso, tes que poñer « TempodeLectura &lt;= 3 » no campo da <i>Regra</i> e « <i>lectura rápida</i> » no campo da <i>Etiqueta</i> .<br />Podes engadir varias etiquetas ó mesmo tempo separándoas cunha vírgula: « <i>lectura rápida, teño que ler</i> »<br />Pódense establecer regras complexas usando os operadores predefinidos: se o « <i>readingTime &gt;= 5 AND domainName = "github.com"</i> » entón etiquetar como « <i>lectura longa, GitHub </i> »'
how_to_use_them_description: 'Demos por feito que queres etiquetar as novas entradas con « <i>lectura rápida</i> » cando o tempo de lectura non supera os 3 minutos.<br />Neste caso, tes que poñer « TempodeLectura &lt;= 3 » no campo da <i>Regra</i> e « <i>lectura rápida</i> » no campo da <i>Etiqueta</i> .<br />Podes engadir varias etiquetas ó mesmo tempo separándoas cunha vírgula: « <i>lectura rápida, teño que ler</i> »<br />Pódense establecer regras complexas usando os operadores predefinidos: se o « <i>readingTime &gt;= 5 AND domainName = "www.php.net"</i> » entón etiquetar como « <i>lectura longa, php</i> »'
operator_description:
notmatches: 'Comproba que o <i>obxecto</i> non contén a <i>busca</i> (non dif. maiúsculas).<br />Exemplo: <code>título non contén "saúde"</code>'
matches: 'Comproba que un <i>obxecto</i> concorda coa <i>busca</i> (dif. maius.-minus.).<br />Exemplo: <code>título contén "saúde"</code>'
@ -312,7 +312,7 @@ config:
table_method: Método
googleTwoFactor_label: Usando app OTP (usa unha app como andOTP, FreeOTP, Authy ou Google Authenticator, para obter o código único)
emailTwoFactor_label: Utilizando email (recibirás código por email)
login_label: Conexión (non se pode cambiar
login_label: Identificador (non se pode cambiar)
email_label: Email
name_label: Nome
two_factor_description: Se activas o segundo factor de autenticación recibirás un email cun código en cada nova conexión que aínda non verificases.
@ -391,11 +391,11 @@ security:
login:
cancel: Cancelar
password: Contrasinal
username: Nome de usuaria
register: Abre unha conta
submit: Conectar
username: Identificador
register: Crea unha conta
submit: Acceder
forgot_password: Esqueceches o contrasinal?
keep_logged_in: Manterme conectada
keep_logged_in: Manter a sesión
page_title: Benvida a wallabag!
import:
wallabag_v2:
@ -530,7 +530,7 @@ developer:
paragraph_7: Esta chamada devolverá todas as entradas do teu usuario.
paragraph_6: 'O access_token é útil para chamar ao punto de acceso da API. Por exemplo:'
paragraph_5: 'A API responderá con algo semellante a:'
paragraph_4: 'Agora, crea o token (substitúe client_id, client_secret, username e password cos valores axeitados):'
paragraph_4: 'Agora, crea o token (substitúe client_id, client_secret, identificador e contrasinal cos valores axeitados):'
paragraph_3: Para crear este token, tes que <a href="%link%">crear un novo cliente</a>.
paragraph_2: Precisas un token para comunicarte entre a túa app de terceiros e a API de wallabag.
paragraph_1: As seguintes ordes fan uso da <a href="https://github.com/jkbrzt/httpie">biblioteca HTTPie</a>. Comproba que está instalada no teu sistema antes de utilizala.
@ -660,7 +660,7 @@ site_credential:
save: Gardar
password_label: Contrasinal
host_label: Servidor (subdominio.exemplo.org, .exemplo.org, etc.)
username_label: Conectar
username_label: Identificador
list:
create_new_one: Crear unha nova credencial
no: Non
@ -673,7 +673,7 @@ site_credential:
page_title: Xestión das credenciais do sitio
user:
search:
placeholder: Filtrar por nome de usuaria ou email
placeholder: Filtrar por identificador ou email
form:
back_to_list: Volver á lista
delete_confirm: Tes a certeza?
@ -681,14 +681,14 @@ user:
save: Gardar
twofactor_google_label: Segundo factor de autenticación por app OTP
twofactor_email_label: Segundo factor de autenticación por email
last_login_label: Última conexión
last_login_label: Último acceso
enabled_label: Activado
email_label: Email
plain_password_label: ????
repeat_new_password_label: Repetir o novo contrasinal
password_label: Contrasinal
name_label: Nome
username_label: Nome de usuaria
username_label: Identificador
list:
create_new_one: Crear nova usuaria
no: Non

View file

@ -176,7 +176,7 @@ config:
title: ČPP
meaning: Značenje
variables_available_title: Koje varijable i operateri mogu koristiti za pisanje pravila?
how_to_use_them_description: 'Pretpostavimo da želiš označiti nove zapise kao <i>kratko čitanje</i> kad je vrijeme čitanja manje od 3 minute.<br />U tom slučaju, postavi „readingTime &lt;= 3” u polju <i>Pravilo</i> i <i>kratko čitanje</i> u polju <i>Oznake</i>.<br />Moguće je dodati više oznaka istovremeno, odvajajući ih zarezom: „<i>kratko čitanje, moram čitati</i>”<br />Složena pravila mogu se napisati pomoću unaprijed definiranih operatora: ako „<i>readingTime &gt;= 5 AND domainName = "github.com"</i>”, onda označi kao „<i>dugo čitanje, GitHub </i>”'
how_to_use_them_description: 'Pretpostavimo da želiš označiti nove zapise kao <i>kratko čitanje</i> kad je vrijeme čitanja manje od 3 minute.<br />U tom slučaju, postavi „readingTime &lt;= 3” u polju <i>Pravilo</i> i <i>kratko čitanje</i> u polju <i>Oznake</i>.<br />Moguće je dodati više oznaka istovremeno, odvajajući ih zarezom: „<i>kratko čitanje, moram čitati</i>”<br />Složena pravila mogu se napisati pomoću unaprijed definiranih operatora: ako „<i>readingTime &gt;= 5 AND domainName = "www.php.net"</i>”, onda označi kao „<i>dugo čitanje, php</i>”'
delete_rule_label: ukloni
edit_rule_label: uredi
if_label: ako

View file

@ -129,7 +129,7 @@ config:
tagging_rules_definition_title: A „címkézési szabályok” mit jelent?
tagging_rules_definition_description: Ezek olyan szabályok amiket a Wallabag használ arra, hogy automatikusan felcímkézze az új bejegyzéseket.<br />Minden egyes alkalommal, amikor egy újabb bejegyzés hozzáadásra kerül, minden címkézési szabály fel lesz használva a beállított címkék hozzáadására, így mentve meg attól a problémától, hogy kézzel osztályozza a bejegyzéseit.
how_to_use_them_title: Hogyan használhatom ezeket?
how_to_use_them_description: 'Tételezzük fel, hogy szeretné az új bejegyzéseket úgy címkézni mint « <i>rövid olvasnivaló</i> », ha az elolvasási idejük 3 perc alatt van.<br />Ebben az esetben ezt írja be a <i>Szabály</i> mezőbe: « readingTime &lt;= 3 », a <i>Címkék</i> mezőbe pedig: « <i>rövid olvasnivaló</i> ».<br />Több címke is hozzáadható egyszerre, ha vesszővel elválasztja őket: « <i>rövid olvasnivaló, el kell olvasni</i> »<br />Összetett szabályok írhatók előre definiált operátorok használatával: ha « <i>readingTime &gt;= 5 AND domainName = "github.com"</i> » akkor címkézd meg mint « <i>hosszú olvasnivaló, GitHub</i> »'
how_to_use_them_description: 'Tételezzük fel, hogy szeretné az új bejegyzéseket úgy címkézni mint « <i>rövid olvasnivaló</i> », ha az elolvasási idejük 3 perc alatt van.<br />Ebben az esetben ezt írja be a <i>Szabály</i> mezőbe: « readingTime &lt;= 3 », a <i>Címkék</i> mezőbe pedig: « <i>rövid olvasnivaló</i> ».<br />Több címke is hozzáadható egyszerre, ha vesszővel elválasztja őket: « <i>rövid olvasnivaló, el kell olvasni</i> »<br />Összetett szabályok írhatók előre definiált operátorok használatával: ha « <i>readingTime &gt;= 5 AND domainName = "www.php.net"</i> » akkor címkézd meg mint « <i>hosszú olvasnivaló, php</i> »'
variables_available_title: Milyen változókat és operátorokat használhatok a szabályok írásához?
variables_available_description: 'A következő változók és operátorok használhatók címkézési szabályok létrehozásához:'
meaning: Jelentés

View file

@ -140,7 +140,7 @@ config:
tagging_rules_definition_title: Cosa significa «regole di etichettatura»?
tagging_rules_definition_description: Sono regole utilizzate da wallabag per etichettare automaticamente i contenuti.<br />Ogni volta che viene aggiunto un contenuto, tutte le regole di etichettatura vengono utilizzate per aggiungere le etichette configurate, risparmiandoti il lavoro di classificare i contenuti manualmente.
how_to_use_them_title: Come si usano?
how_to_use_them_description: 'Diciamo che vuoi etichettare un contenuto come « <i>lettura veloce</i> » quando il tempo di lettura è inferiore ai 3 minuti.<br />In questo case, devi mettere « readingTime &lt;= 3 » nel campo <i>Regola</i> e « <i>lettura veloce</i> » nel campo <i>Etichette</i>.<br />Molte etichette si possono aggiungere contemporanemente separandole con una virgola: « <i>lettura veloce, da leggere</i> »<br />Regole complesse possono essere scritte utilizzando gli operatori predefiniti: se « <i>readingTime &gt;= 5 AND domainName = "github.com"</i> » allora etichetta « <i>lettura lunga, GitHub </i> »'
how_to_use_them_description: 'Diciamo che vuoi etichettare un contenuto come « <i>lettura veloce</i> » quando il tempo di lettura è inferiore ai 3 minuti.<br />In questo case, devi mettere « readingTime &lt;= 3 » nel campo <i>Regola</i> e « <i>lettura veloce</i> » nel campo <i>Etichette</i>.<br />Molte etichette si possono aggiungere contemporanemente separandole con una virgola: « <i>lettura veloce, da leggere</i> »<br />Regole complesse possono essere scritte utilizzando gli operatori predefiniti: se « <i>readingTime &gt;= 5 AND domainName = "www.php.net"</i> » allora etichetta « <i>lettura lunga, php</i> »'
variables_available_title: Quali operatori e variabili posso utilizzare per scrivere delle regole?
variables_available_description: 'I seguenti operatori e variabili posso essere utilizzati per scrivere regole di etichettatura:'
meaning: Significato

View file

@ -165,7 +165,7 @@ config:
notmatches: <i>対象</i>が<i>検索</i>と一致しないことをテストする(大文字小文字の区別なし)。<br />例:<code>title notmatches "football"</code>
matches: <i>対象</i>が<i>検索</i>と一致するかテストする(大文字小文字の区別なし)。<br />例:<code>title matches "football"</code>
tagging_rules_definition_description: wallabag ではルールを使用することで新しい記事へ自動的にタグ付けする事ができます。<br />新しい記事が追加されるたびに、あなたが設定したすべてのタグ付けルールを使用してタグが追加されるので、記事を手動で分類する手間が省けます。
how_to_use_them_description: 'あなたは読了時間が3分以下の時に新しい記事へ « <i>短い文章</i> » のタグ付けをしたいとします。<br />この場合、<i>ルール</i>に « readingTime &lt;= 3 »と<i>タグ</i>に « <i>短い文章</i> » を設定します。<br />コンマで区切ることにより複数のタグを追加することができます: « <i>短い文章, 必読</i> »<br />演算子を使用することにより複雑なルールを書くことができます: もし « <i>readingTime &gt;= 5 AND domainName = "github.com"</i> »ならばタグ付け « <i>長い文章, GitHub </i> »'
how_to_use_them_description: 'あなたは読了時間が3分以下の時に新しい記事へ « <i>短い文章</i> » のタグ付けをしたいとします。<br />この場合、<i>ルール</i>に « readingTime &lt;= 3 »と<i>タグ</i>に « <i>短い文章</i> » を設定します。<br />コンマで区切ることにより複数のタグを追加することができます: « <i>短い文章, 必読</i> »<br />演算子を使用することにより複雑なルールを書くことができます: もし « <i>readingTime &gt;= 5 AND domainName = "www.php.net"</i> »ならばタグ付け « <i>長い文章, php</i> »'
delete_rule_label: 削除
export: エクスポート
import_submit: インポート

View file

@ -400,7 +400,7 @@ config:
title: 문서 제목
label: 변수
tagging_rules_definition_description: wallabag 에서 자동으로 새 문서에 태그를 지정하는 데 사용하는 규칙입니다. <br /> 태그 지정 규칙이 새 문서가 추가 될 때마다 태그를 구성하는 데 사용되므로, 수동으로 분류하는 수고를 덜 수 있습니다.
how_to_use_them_description: '읽기 시간이 3 분 미만인 경우 «<i> 짧은 읽기 </i>» 와 같은 새 기사에 태그를 지정한다고 가정합니다. <br />이 경우 <i> 규칙</i> 을 입력해야합니다. <i>태그</i> 필드에 « readingTime &lt;= 3 » 을 입력하고 « <i>짧은 읽기</i>» 를 입력하십시오. <br /> 여러 태그를 쉼표로구분하여 한 번에 추가 할 수 있습니다: « <i> 짧은 읽기, 반드시 읽기</i> » <br /> 사전 정의 된 연산자를 사용하여 복잡한 규칙을 작성할 수 있습니다: 만약 « <i>readingTime &gt;= 5 AND domainName = "github .com"</i> » 다음으로 태그 «<i> 긴 읽기, GitHub </i>»'
how_to_use_them_description: '읽기 시간이 3 분 미만인 경우 «<i> 짧은 읽기 </i>» 와 같은 새 기사에 태그를 지정한다고 가정합니다. <br />이 경우 <i> 규칙</i> 을 입력해야합니다. <i>태그</i> 필드에 « readingTime &lt;= 3 » 을 입력하고 « <i>짧은 읽기</i>» 를 입력하십시오. <br /> 여러 태그를 쉼표로구분하여 한 번에 추가 할 수 있습니다: « <i> 짧은 읽기, 반드시 읽기</i> » <br /> 사전 정의 된 연산자를 사용하여 복잡한 규칙을 작성할 수 있습니다: 만약 « <i>readingTime &gt;= 5 AND domainName = "www.php.net"</i> » 다음으로 태그 «<i> 긴 읽기, php</i>»'
operator_description:
and: 하나의 규칙 그리고 다른 규칙
or: 하나의 규칙 또는 다른 규칙

View file

@ -102,7 +102,7 @@ config:
how_to_use_them_title: Hoe gebruik is ze?
tagging_rules_definition_title: Wat betekend "label regels"?
title: FAQ
how_to_use_them_description: 'Laten we aannemen dat u nieuwe vermeldingen wilt labelen als « <i>korte lezing</i> » wanneer de leestijd minder dan 3 minuten bedraagt.<br />In dat geval moet u « readingTime &lt;= 3 » in het <i>Regelveld</i> en « <i>korte lezing</i> » in het <i>Labels</i> veld.<br />Meerdere labels kunnen tegelijkertijd worden toegevoegd door ze te scheiden met een komma:« <i>korte lezing, belangrijk</i> »<br />Complexe regels kunnen worden geschreven met behulp van vooraf gedefinieerde operators: if « <i>readingTime &gt;= 5 AND domainName = "github.com"</i> » label dan als« <i>lange lezing, GitHub </i> »'
how_to_use_them_description: 'Laten we aannemen dat u nieuwe vermeldingen wilt labelen als « <i>korte lezing</i> » wanneer de leestijd minder dan 3 minuten bedraagt.<br />In dat geval moet u « readingTime &lt;= 3 » in het <i>Regelveld</i> en « <i>korte lezing</i> » in het <i>Labels</i> veld.<br />Meerdere labels kunnen tegelijkertijd worden toegevoegd door ze te scheiden met een komma:« <i>korte lezing, belangrijk</i> »<br />Complexe regels kunnen worden geschreven met behulp van vooraf gedefinieerde operators: if « <i>readingTime &gt;= 5 AND domainName = "www.php.net"</i> » label dan als« <i>lange lezing, php</i> »'
tags_label: Labels
rule_label: Regel
edit_rule_label: wijzig

View file

@ -136,7 +136,7 @@ config:
tagging_rules_definition_title: Qué significa las règlas d'etiquetas automaticas?
tagging_rules_definition_description: Son de règlas utilizadas per wallabag per classar automaticament vòstres novèls articles.<br />Cada còp qu'un novèl article es apondut, totas las règlas d'etiquetas automaticas seràn utilizadas per ajustar d'etiquetas qu'avètz configuradas, en vos esparnhant l'esfòrç de classificar vòstres articles manualament.
how_to_use_them_title: Cossí las utilizar?
how_to_use_them_description: Imaginem que volètz atribuir als novèls article l'etiqueta « <i>lectura corta</i> » quand lo temps per legir es inferior a 3 minutas.<br />Dins aquel cas, deuriatz metre « readingTime &lt;= 3 » dins lo camp <i>Règla</i> e « <i>lectura corta</i> » dins lo camp <i>Etiqueta</i>.<br />Mai d'una etiquetas pòdon èsser apondudas simultanèament ne las separant amb de virgulas: « <i>lectura corta, per ligir</i> »<br />De règlas complèxas pòdon èsser creadas n'emplegant d'operators predefinits:se « <i>readingTime &gt;= 5 AND domainName = "github.com"</i> » alara atribuir las etiquetas « <i>lectura longa, github </i> »
how_to_use_them_description: Imaginem que volètz atribuir als novèls article l'etiqueta « <i>lectura corta</i> » quand lo temps per legir es inferior a 3 minutas.<br />Dins aquel cas, deuriatz metre « readingTime &lt;= 3 » dins lo camp <i>Règla</i> e « <i>lectura corta</i> » dins lo camp <i>Etiqueta</i>.<br />Mai d'una etiquetas pòdon èsser apondudas simultanèament ne las separant amb de virgulas: « <i>lectura corta, per ligir</i> »<br />De règlas complèxas pòdon èsser creadas n'emplegant d'operators predefinits:se « <i>readingTime &gt;= 5 AND domainName = "www.php.net"</i> » alara atribuir las etiquetas « <i>lectura longa, php</i> »
variables_available_title: Quinas variablas e operators pòdi utilizar per escriure de règlas?
variables_available_description: "Las variablas e operators seguents pòdon èsser utilizats per escriure de règlas d'etiquetas automaticas:"
meaning: Significacion

View file

@ -150,7 +150,7 @@ config:
tagging_rules_definition_title: Co oznaczają « reguły tagowania » ?
tagging_rules_definition_description: Istnieją reguły używane przez wallabag służące do automatycznego tagowania nowych wpisów.<br />Za każdym razem kiedy dodasz nowy wpis, zostaną użyte wszystkie skonfigurowane przez ciebie reguły. Dzięki temu unikniesz konieczności ręcznego ich klasyfikowania.
how_to_use_them_title: Jak ich użyć?
how_to_use_them_description: 'Załóżmy, że chcesz otagować nowe wpisy jako « <i>krótki tekst</i> » jeżeli czas czytania wynosi mniej niż 3 minuty.<br />W tym przypadku powinieneś umieścić « czasCzytania &lt;= 3 » w polu <i>Reguła</i> i « <i>krótki tekst</i> » w polu <i>Tags</i> .<br />Wiele tagów może zostać dodanych jednocześnie rozdzielając je przecinkami: « <i>do szybkiego przeczytania, koniecznie przeczytać</i> »<br />Kompleksowe reguły mogą być napisane przy użyciu operatorów: jeżeli « <i>czasCzytania &gt;= 5 I nazwaDomeny = "github.com"</i> » wtedy otaguj jako « <i>dłuższy tekst, GitHub </i> »'
how_to_use_them_description: 'Załóżmy, że chcesz otagować nowe wpisy jako « <i>krótki tekst</i> » jeżeli czas czytania wynosi mniej niż 3 minuty.<br />W tym przypadku powinieneś umieścić « czasCzytania &lt;= 3 » w polu <i>Reguła</i> i « <i>krótki tekst</i> » w polu <i>Tags</i> .<br />Wiele tagów może zostać dodanych jednocześnie rozdzielając je przecinkami: « <i>do szybkiego przeczytania, koniecznie przeczytać</i> »<br />Kompleksowe reguły mogą być napisane przy użyciu operatorów: jeżeli « <i>czasCzytania &gt;= 5 I nazwaDomeny = "www.php.net"</i> » wtedy otaguj jako « <i>dłuższy tekst, php</i> »'
variables_available_title: Jakich zmiennych i operatorów mogę użyć przy pisaniu reguł?
variables_available_description: 'Następujące zmienne i operatory mogą być użyte przy tworzeniu reguł tagowania:'
meaning: Znaczenie

View file

@ -111,7 +111,7 @@ config:
tagging_rules_definition_title: 'O que as « regras de tags » significam?'
tagging_rules_definition_description: 'São regras usadas pelo Wallabag para automaticamente adicionar tags em novos artigos.<br />Cada vez que um novo artigo é adicionado, todas as regras de tags podem ser usadas para adicionar as tags que você configurou, ajudando-o com o problema de classificar manualmente seus artigos.'
how_to_use_them_title: 'Como eu as utilizo?'
how_to_use_them_description: 'Vamos dizer que você deseja adicionar a tag « <i>leitura rápida</i> » quando o tempo de leitura for menor que 3 minutos.<br />Neste caso, você deve « readingTime &lt;= 3 » no campo <i>Regra</i> e « <i>leitura rápida</i> » no campo <i>Tags</i>.<br />Diversas tags podem ser adicionadas simultâneamente separando-as com vírgula: « <i>leitura rápida, precisa ser lido</i> »<br />Regras complexas podem ser escritas usando os seguintes operadores pré-definidos: if « <i>readingTime &gt;= 5 AND domainName = "github.com"</i> » então adicione a tag « <i>leitura longa, github </i> »'
how_to_use_them_description: 'Vamos dizer que você deseja adicionar a tag « <i>leitura rápida</i> » quando o tempo de leitura for menor que 3 minutos.<br />Neste caso, você deve « readingTime &lt;= 3 » no campo <i>Regra</i> e « <i>leitura rápida</i> » no campo <i>Tags</i>.<br />Diversas tags podem ser adicionadas simultâneamente separando-as com vírgula: « <i>leitura rápida, precisa ser lido</i> »<br />Regras complexas podem ser escritas usando os seguintes operadores pré-definidos: if « <i>readingTime &gt;= 5 AND domainName = "www.php.net"</i> » então adicione a tag « <i>leitura longa, php</i> »'
variables_available_title: 'Quais variáveis e operadores eu posso usar para escrever regras?'
variables_available_description: 'As seguintes variáveis e operadores podem ser usados para criar regras de tags:'
meaning: ''

View file

@ -150,7 +150,7 @@ config:
tagging_rules_definition_title: 'Что значит "правила тегирования"?'
tagging_rules_definition_description: 'Правила, по которым wallabag автоматически добавит теги для новых записей.<br />Каждый раз, при добавлении новых записей, будут проставляться теги к записям, согласно настроенным правилам тегирования, это избавит Вас от необходимости проставлять теги для каждой записи вручную.'
how_to_use_them_title: 'Как мне их использовать?'
how_to_use_them_description: 'Предположим, вы хотите пометить новые записи как "<i>короткая</i>", когда на чтение уйдет меньше 3 минут.<br />В этом случае, установите " readingTime &lt;= 3 " в поле <i>Правила</i> и "<i>короткая</i>" в поле <i>Теги</i>.<br />Несколько тегов могут добавляться одновременно, разделяя их запятой: "<i>короткая, прочитать обязательно</i>" <br />Сложные правила могут быть записаны с использованием предопределенных операторов: если "<i>readingTime &gt;= 5 AND domainName = \"github.com\"</i> " тогда тег будет "<i>долго читать, GitHub </i>"'
how_to_use_them_description: 'Предположим, вы хотите пометить новые записи как "<i>короткая</i>", когда на чтение уйдет меньше 3 минут.<br />В этом случае, установите " readingTime &lt;= 3 " в поле <i>Правила</i> и "<i>короткая</i>" в поле <i>Теги</i>.<br />Несколько тегов могут добавляться одновременно, разделяя их запятой: "<i>короткая, прочитать обязательно</i>" <br />Сложные правила могут быть записаны с использованием предопределенных операторов: если "<i>readingTime &gt;= 5 AND domainName = \"www.php.net\"</i> " тогда тег будет "<i>долго читать, php</i>"'
variables_available_title: 'Какие переменные и операторы я могу использовать для написания правил?'
variables_available_description: 'Следующие переменные и операторы могут использоваться для создания правил тегов:'
meaning: 'Смысл'

View file

@ -124,7 +124,7 @@ config:
tagging_rules_definition_title: 'ข้อบังคับการแท็กคืออะไร?'
tagging_rules_definition_description: 'การใช้ข้อบังคับโดย Wallabag ไปแท็กรายการใหม่อัตโนมัติ <br />แต่ละช่วงรายการใหม่จะเป็นการเพิ่ม, การแท็กข้อบังคับทั้งหมดจะใช้การเพิ่มแท็กที่คุณกำหนดค่า, ดังนั้นการบันทึกของคุณจะเป็นปัญหาในการจัดหมวดหมู่ของรายการของคุณ'
how_to_use_them_title: 'ฉันจะใช้ได้อย่างไร?'
how_to_use_them_description: 'การสมมติที่คุณต้องการแท็กรายการใหม่ไปยัง « <i>การอ่านแบบสั้น</i> » เมื่ออ่านในช่วง 3 นาที <br />ในกรณีนี้, คุณควรใส่ « readingTime &lt;= 3 » ภายใน <i>ข้อบังคับ</i> ของพื่นที่และ « <i>การอ่านแบบสั้น</i> » ภายใน <i>แท็ก</i> ของพื้นที่<br />ในหลายแท็กสามารถเพิ่มได้พร้อมกันโดยแบ่งกับ comma: « <i>การอ่านแบบสั้น, ต้องอ่าน</i> »<br />ข้อบังคับที่ซับซ้อนสามารถเขียนโดยการใช้การดำเนินการที่กำหนดไว้ก่อน: ถ้า « <i>readingTime &gt;= 5 AND domainName = "github.com"</i> » ดังนั้นแท็กไปยัง « <i>การอ่านแบบยาว, github </i> »'
how_to_use_them_description: 'การสมมติที่คุณต้องการแท็กรายการใหม่ไปยัง « <i>การอ่านแบบสั้น</i> » เมื่ออ่านในช่วง 3 นาที <br />ในกรณีนี้, คุณควรใส่ « readingTime &lt;= 3 » ภายใน <i>ข้อบังคับ</i> ของพื่นที่และ « <i>การอ่านแบบสั้น</i> » ภายใน <i>แท็ก</i> ของพื้นที่<br />ในหลายแท็กสามารถเพิ่มได้พร้อมกันโดยแบ่งกับ comma: « <i>การอ่านแบบสั้น, ต้องอ่าน</i> »<br />ข้อบังคับที่ซับซ้อนสามารถเขียนโดยการใช้การดำเนินการที่กำหนดไว้ก่อน: ถ้า « <i>readingTime &gt;= 5 AND domainName = "www.php.net"</i> » ดังนั้นแท็กไปยัง « <i>การอ่านแบบยาว, php</i> »'
variables_available_title: 'ตัวแปรและตัวดำเนินการสามารถให้ใช้การเขียนข้อบังคับได้ที่ไหน?'
variables_available_description: 'การติดตามตัวแปรและตัวดำเนินการสามารถใช้การสร้างข้อบังคับแท็ก:'
meaning: 'ความหมาย'

View file

@ -164,7 +164,7 @@ config:
matches: '<i>konu</i>nun <i>arama</i> kriterine (büyük küçük harf duyarsız) eşleştiğini test eder.<br />Örnek: <code>title matches "futbol"</code>'
notmatches: '<i>konu</i>nun <i>arama</i> kriterine (büyük küçük harf duyarsız) eşleşmediğini test eder.<br />Örnek: <code>title nonmatches "futbol"</code>'
tagging_rules_definition_description: Bunlar wallabag'in yeni makaleleri otomatik olarak etiketleyebilmesi için tanımlanmış kurallardır.<br />Her yeni makale eklendiğinde, ayarladığınız tüm etiket kuralları kullanılarak makaleniz etiketlenecektir. Bu sayede her bir makaleyi tek tek etiketlemekle uğraşmanız gerekmeyecek.
how_to_use_them_description: 'Örneğin, 3 dakikadan kısa okuma süresi olan yeni makaleleri « <i>kısa okumalar</i>» etiketi ile etiketlemek istiyorsunuz diyelim.<br />Bu durumda, <i>Kural</i> alanına « readingTime &lt; = 3 » değeri, ve de <i>Etiketler</i> alanına da <i>kısa okumalar</i> değerini girmelisiniz.<br /> Eğer birden fazla etiket tanımlamak istiyorsanız arasına virgül koyabilirsiniz: « <i>kısa okumalar, mutlaka oku</i> »<br />Daha kompleks kurallar önden tanımlanmış operatörlerle girilebilir: Eğer « <i> readingTime &gt; = AND domainName = "github.com"</i> » ise, o zaman « <i>uzun okumalar, GitHub</i> » gibi girebilirsiniz'
how_to_use_them_description: 'Örneğin, 3 dakikadan kısa okuma süresi olan yeni makaleleri « <i>kısa okumalar</i>» etiketi ile etiketlemek istiyorsunuz diyelim.<br />Bu durumda, <i>Kural</i> alanına « readingTime &lt; = 3 » değeri, ve de <i>Etiketler</i> alanına da <i>kısa okumalar</i> değerini girmelisiniz.<br /> Eğer birden fazla etiket tanımlamak istiyorsanız arasına virgül koyabilirsiniz: « <i>kısa okumalar, mutlaka oku</i> »<br />Daha kompleks kurallar önden tanımlanmış operatörlerle girilebilir: Eğer « <i> readingTime &gt; = AND domainName = "www.php.net"</i> » ise, o zaman « <i>uzun okumalar, php</i> » gibi girebilirsiniz'
if_label: eğer
then_tag_as_label: ise, şu şekilde etiketle
delete_rule_label: sil

View file

@ -160,7 +160,7 @@ config:
tagging_rules_definition_title: '“标签规则”是什么意思?'
tagging_rules_definition_description: '它们是 wallabag 用来给新项目自动打上标签的规则。<br />每当一个新项目被添加进来,所有标签规则都会作用于这个项目,为它打上你配置好的标签,免去你手动分类的麻烦。'
how_to_use_them_title: '我该怎么使用它们?'
how_to_use_them_description: '假设你想要将一个阅读时间短于 3 分钟的新项目标记为“ <i>短阅读</i> ”。<br /> 你应该在 <i>规则</i> 区域输入“readingTime &lt;= 3”并在 <i>标签</i> 区域输入“<i>短阅读</i>”。<br /> 可以同时添加数个标签,只需要用半角逗号来隔开它们,如:“<i>短阅读, 必读</i>”<br /> 可以使用预定义的操作符来编写复杂的规则,如:如果“ <i>readingTime &gt;= 5 AND domainName ="github.com"</i>”则标记为“<i>长阅读, github</i>”'
how_to_use_them_description: '假设你想要将一个阅读时间短于 3 分钟的新项目标记为“ <i>短阅读</i> ”。<br /> 你应该在 <i>规则</i> 区域输入“readingTime &lt;= 3”并在 <i>标签</i> 区域输入“<i>短阅读</i>”。<br /> 可以同时添加数个标签,只需要用半角逗号来隔开它们,如:“<i>短阅读, 必读</i>”<br /> 可以使用预定义的操作符来编写复杂的规则,如:如果“ <i>readingTime &gt;= 5 AND domainName ="www.php.net"</i>”则标记为“<i>长阅读, php</i>”'
variables_available_title: '我可以使用哪些变量和操作符来编写规则?'
variables_available_description: '可以使用以下变量和操作符来编写标签规则:'
meaning: '含义'
@ -253,6 +253,7 @@ entry:
toogle_as_star: '添加到收藏'
delete: '删除'
export_title: '导出'
assign_search_tag: 将此搜索作为标签分配给每个结果
filters:
title: '筛选器'
status_label: '状态'

View file

@ -57,6 +57,11 @@
{% endblock %}
{% block messages %}
<div style="margin-top: 10px; color: #e01a15; border-left: 20px #e01a15 solid; padding-left: 10px; border-bottom: 6px #e01a15 solid; border-bottom-left-radius: 10px;">
<h3>⚠️ You are using the Baggy theme which is now deprecated.</h3>
<p>It will be removed in the next version. You can use the Material theme by <a href="{{ path('config') }}">updating the theme config</a>.</p>
</div>
{% for flashMessage in app.session.flashbag.get('notice') %}
<div class="messages success">
<a href="#" class="closeMessage">×</a>

View file

@ -11,8 +11,8 @@
<title>wallabag — {{type}} {{ tag }} feed</title>
<subtitle type="html">Atom feed for entries tagged with {{ tag }}</subtitle>
{% endif %}
{% if entries | length > 0 %}
<updated>{{ (entries | first).createdAt | date('c') }}</updated> {# Indicates the last time the feed was modified in a significant way. #}
{% if updated %}
<updated>{{ updated | date('c') }}</updated> {# Indicates the last time the feed was modified in a significant way. #}
{% endif %}
<link rel="self" type="application/atom+xml" href="{{ app.request.uri }}"/>
{% if entries.hasPreviousPage %}

View file

@ -9,10 +9,16 @@
<ul class="tools right">
<li>
<a title="{{ 'entry.list.show_same_domain'|trans }}" class="tool grey-text" href="{{ path('same_domain', { 'id': entry.id }) }}"><i class="material-icons">language</i></a>
<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>
<a title="{{ 'entry.list.toogle_as_star'|trans }}" class="tool grey-text" href="{{ path('star_entry', { 'id': entry.id }) }}"><i class="material-icons">{% if entry.isStarred == 0 %}star_border{% else %}star{% endif %}</i></a>
<a title="{{ 'entry.list.delete'|trans }}" onclick="return confirm('{{ 'entry.confirm.delete'|trans|escape('js') }}')" class="tool grey-text delete" href="{{ path('delete_entry', { 'id': entry.id }) }}"><i class="material-icons">delete</i></a>
<a title="{{ 'entry.list.show_same_domain'|trans }}" class="tool grey-text" href="{{ path('same_domain', { 'id': entry.id }) }}" data-action="same_domain" data-entry-id="{{ entry.id }}"><i class="material-icons">language</i></a>
</li>
<li>
<a title="{{ 'entry.list.toogle_as_read'|trans }}" class="tool grey-text" href="{{ path('archive_entry', { 'id': entry.id }) }}" data-action="archived" data-entry-id="{{ entry.id }}"><i class="material-icons">{% if entry.isArchived == 0 %}done{% else %}unarchive{% endif %}</i></a>
</li>
<li>
<a title="{{ 'entry.list.toogle_as_star'|trans }}" class="tool grey-text" href="{{ path('star_entry', { 'id': entry.id }) }}" data-action="star" data-entry-id="{{ entry.id }}"><i class="material-icons">{% if entry.isStarred == 0 %}star_border{% else %}star{% endif %}</i></a>
</li>
<li>
<a title="{{ 'entry.list.delete'|trans }}" data-action-confirm="{{ 'entry.confirm.delete'|trans }}" class="tool grey-text delete" href="{{ path('delete_entry', { 'id': entry.id }) }}" data-action="delete" data-entry-id="{{ entry.id }}"><i class="material-icons">delete</i></a>
</li>
</ul>
</div>

View file

@ -1,4 +1,4 @@
<div class="card{% if currentRoute in routes and entry.isArchived %} archived{% endif %}">
<div class="card entry-card{% if currentRoute in routes and entry.isArchived %} archived{% endif %}">
<div class="card-body">
<div class="card-fullimage">
<ul class="card-entry-labels">

View file

@ -1,4 +1,4 @@
<div class="card{% if currentRoute in routes and entry.isArchived %} archived{% endif %}">
<div class="card entry-card{% if currentRoute in routes and entry.isArchived %} archived{% endif %}">
<div class="card-body">
<div class="card-image waves-effect waves-block waves-light">
<ul class="card-entry-labels">

View file

@ -34,14 +34,14 @@
{% include "@WallabagCore/themes/common/Entry/_feed_link.html.twig" %}
{% endif %}
</div>
{% if currentRoute == 'search' %}<div><a href="{{ path('tag_this_search', {'filter': searchTerm, 'currentRoute': app.request.get('currentRoute') }) }}" title="{{ 'entry.list.assign_search_tag'|trans }}">{{ 'entry.list.assign_search_tag'|trans }}</a></div>{% endif %}
{% if entries.getNbPages > 1 %}
{{ pagerfanta(entries, 'twitter_bootstrap_translated', {'proximity': 1}) }}
{% endif %}
</div>
<ul class="{% if listMode == 1 %}collection{% else %}row data{% endif %}">
<li class="mass-buttons">
{% if listMode == 1 %}
<div class="mass-buttons">
{% if entries.count > 0 and listMode == 1 %}
<span>
<input id="selectAll" type="checkbox" data-toggle="[data-js='entry-checkbox']" data-js="checkboxes-toggle" />&nbsp;
@ -53,10 +53,13 @@
<button class="btn cyan darken-1" type="submit" name="delete" onclick="return confirm('{{ 'entry.confirm.delete_entries'|trans|escape('js') }}')" title="{{ 'entry.list.delete'|trans }}"><i class="material-icons">delete</i></button>
</span>
{% endif %}
</li>
</div>
{% endif %}
<ol class="entries {% if listMode == 1 %}collection{% else %}row entries-row data{% endif %}">
{% for entry in entries %}
<li id="entry-{{ entry.id|e }}" class="entry col {% if listMode == 0 %}l3 m6{% else %}collection-item{% endif %} s12">
<li id="entry-{{ entry.id|e }}" class="{% if listMode != 0 %}col collection-item{% endif %} s12" data-entry-id="{{ entry.id|e }}" data-test="entry">
{% if listMode == 1 %}
{% include "@WallabagCore/themes/material/Entry/_card_list.html.twig" with {'entry': entry, 'currentRoute': currentRoute, 'routes': entriesWithArchivedClassRoutes} only %}
{% elseif not entry.previewPicture is null and entry.mimetype starts with 'image/' %}
@ -66,7 +69,7 @@
{% endif %}
</li>
{% endfor %}
</ul>
</ol>
</form>
{% if entries.getNbPages > 1 %}

View file

@ -6,8 +6,4 @@ use Symfony\Component\HttpKernel\Bundle\Bundle;
class WallabagUserBundle extends Bundle
{
public function getParent()
{
return 'FOSUserBundle';
}
}

View file

@ -0,0 +1,46 @@
<?php
namespace Tests\Wallabag\ApiBundle\Controller;
use Tests\Wallabag\ApiBundle\WallabagApiTestCase;
class ConfigRestControllerTest extends WallabagApiTestCase
{
public function testGetConfig()
{
$this->client->request('GET', '/api/config.json');
$this->assertSame(200, $this->client->getResponse()->getStatusCode());
$config = json_decode($this->client->getResponse()->getContent(), true);
$this->assertArrayHasKey('id', $config);
$this->assertArrayHasKey('items_per_page', $config);
$this->assertArrayHasKey('language', $config);
$this->assertArrayHasKey('reading_speed', $config);
$this->assertArrayHasKey('action_mark_as_read', $config);
$this->assertArrayHasKey('list_mode', $config);
$this->assertSame(200.0, $config['reading_speed']);
$this->assertSame('en', $config['language']);
$this->assertCount(6, $config);
$this->assertSame('application/json', $this->client->getResponse()->headers->get('Content-Type'));
}
public function testGetConfigWithoutAuthentication()
{
$client = static::createClient();
$client->request('GET', '/api/config.json');
$this->assertSame(401, $client->getResponse()->getStatusCode());
$config = json_decode($client->getResponse()->getContent(), true);
$this->assertArrayHasKey('error', $config);
$this->assertArrayHasKey('error_description', $config);
$this->assertSame('access_denied', $config['error']);
$this->assertSame('application/json', $client->getResponse()->headers->get('Content-Type'));
}
}

View file

@ -15,6 +15,7 @@ class EntryControllerTest extends WallabagCoreTestCase
const AN_URL_CONTAINING_AN_ARTICLE_WITH_IMAGE = 'https://www.lemonde.fr/judo/article/2017/11/11/judo-la-decima-de-teddy-riner_5213605_1556020.html';
public $downloadImagesEnabled = false;
public $url = 'https://www.lemonde.fr/pixels/article/2019/06/18/ce-qu-il-faut-savoir-sur-le-libra-la-cryptomonnaie-de-facebook_5477887_4408996.html';
private $entryDataTestAttribute = '[data-test="entry"]';
/**
* @after
@ -101,14 +102,14 @@ class EntryControllerTest extends WallabagCoreTestCase
$crawler = $client->request('GET', '/');
$this->assertCount(4, $crawler->filter('li.entry'));
$this->assertCount(4, $crawler->filter($this->entryDataTestAttribute));
// Good URL
$client->request('GET', '/bookmarklet', ['url' => $this->url]);
$this->assertSame(302, $client->getResponse()->getStatusCode());
$client->followRedirect();
$crawler = $client->request('GET', '/');
$this->assertCount(5, $crawler->filter('li.entry'));
$this->assertCount(5, $crawler->filter($this->entryDataTestAttribute));
$em = $client->getContainer()
->get('doctrine.orm.entity_manager');
@ -176,6 +177,42 @@ class EntryControllerTest extends WallabagCoreTestCase
$client->getContainer()->get('craue_config')->set('store_article_headers', 0);
}
/**
* @group NetworkCalls
*/
public function testPostNewOkWithTaggingRules()
{
$this->logInAs('empty');
$client = $this->getClient();
$crawler = $client->request('GET', '/new');
$this->assertSame(200, $client->getResponse()->getStatusCode());
$form = $crawler->filter('form[name=entry]')->form();
$data = [
'entry[url]' => $this->url,
];
$client->submit($form, $data);
$this->assertSame(302, $client->getResponse()->getStatusCode());
$content = $client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findByUrlAndUserId($this->url, $this->getLoggedInUserId());
$tags = $content->getTagsLabel();
/*
* Without the custom reading speed of `empty` user, it'll be inversed
*/
$this->assertContains('longread', $tags);
$this->assertNotContains('shortread', $tags);
}
/**
* @group NetworkCalls
*/
@ -734,7 +771,7 @@ class EntryControllerTest extends WallabagCoreTestCase
$crawler = $client->submit($form, $data);
$this->assertCount(1, $crawler->filter('li.entry'));
$this->assertCount(1, $crawler->filter($this->entryDataTestAttribute));
}
public function testFilterOnReadingTimeWithNegativeValue()
@ -754,7 +791,7 @@ class EntryControllerTest extends WallabagCoreTestCase
$crawler = $client->submit($form, $data);
// forcing negative value results in no entry displayed
$this->assertCount(0, $crawler->filter('li.entry'));
$this->assertCount(0, $crawler->filter($this->entryDataTestAttribute));
}
public function testFilterOnReadingTimeOnlyUpper()
@ -763,7 +800,7 @@ class EntryControllerTest extends WallabagCoreTestCase
$client = $this->getClient();
$crawler = $client->request('GET', '/all/list');
$this->assertCount(5, $crawler->filter('li.entry'));
$this->assertCount(5, $crawler->filter($this->entryDataTestAttribute));
$entry = new Entry($this->getLoggedInUser());
$entry->setUrl($this->url);
@ -772,7 +809,7 @@ class EntryControllerTest extends WallabagCoreTestCase
$this->getEntityManager()->flush();
$crawler = $client->request('GET', '/all/list');
$this->assertCount(6, $crawler->filter('li.entry'));
$this->assertCount(6, $crawler->filter($this->entryDataTestAttribute));
$form = $crawler->filter('button[id=submit-filter]')->form();
@ -782,7 +819,7 @@ class EntryControllerTest extends WallabagCoreTestCase
$crawler = $client->submit($form, $data);
$this->assertCount(5, $crawler->filter('li.entry'));
$this->assertCount(5, $crawler->filter($this->entryDataTestAttribute));
}
public function testFilterOnReadingTimeOnlyLower()
@ -800,7 +837,7 @@ class EntryControllerTest extends WallabagCoreTestCase
$crawler = $client->submit($form, $data);
$this->assertCount(0, $crawler->filter('li.entry'));
$this->assertCount(0, $crawler->filter($this->entryDataTestAttribute));
$entry = new Entry($this->getLoggedInUser());
$entry->setUrl($this->url);
@ -809,7 +846,7 @@ class EntryControllerTest extends WallabagCoreTestCase
$this->getEntityManager()->flush();
$crawler = $client->submit($form, $data);
$this->assertCount(1, $crawler->filter('li.entry'));
$this->assertCount(1, $crawler->filter($this->entryDataTestAttribute));
}
public function testFilterOnUnreadStatus()
@ -827,7 +864,7 @@ class EntryControllerTest extends WallabagCoreTestCase
$crawler = $client->submit($form, $data);
$this->assertCount(4, $crawler->filter('li.entry'));
$this->assertCount(4, $crawler->filter($this->entryDataTestAttribute));
$entry = new Entry($this->getLoggedInUser());
$entry->setUrl($this->url);
@ -837,7 +874,7 @@ class EntryControllerTest extends WallabagCoreTestCase
$crawler = $client->submit($form, $data);
$this->assertCount(5, $crawler->filter('li.entry'));
$this->assertCount(5, $crawler->filter($this->entryDataTestAttribute));
}
public function testFilterOnCreationDate()
@ -868,7 +905,7 @@ class EntryControllerTest extends WallabagCoreTestCase
$crawler = $client->submit($form, $data);
$this->assertCount(4, $crawler->filter('li.entry'));
$this->assertCount(4, $crawler->filter($this->entryDataTestAttribute));
$data = [
'entry_filter[createdAt][left_date]' => $today->format('Y-m-d'),
@ -877,7 +914,7 @@ class EntryControllerTest extends WallabagCoreTestCase
$crawler = $client->submit($form, $data);
$this->assertCount(4, $crawler->filter('li.entry'));
$this->assertCount(4, $crawler->filter($this->entryDataTestAttribute));
$data = [
'entry_filter[createdAt][left_date]' => '1970-01-01',
@ -886,7 +923,7 @@ class EntryControllerTest extends WallabagCoreTestCase
$crawler = $client->submit($form, $data);
$this->assertCount(0, $crawler->filter('li.entry'));
$this->assertCount(0, $crawler->filter($this->entryDataTestAttribute));
}
public function testFilterOnAnnotatedStatus()
@ -968,7 +1005,7 @@ class EntryControllerTest extends WallabagCoreTestCase
];
$crawler = $client->submit($form, $data);
$this->assertCount(4, $crawler->filter('li.entry'));
$this->assertCount(4, $crawler->filter($this->entryDataTestAttribute));
$crawler = $client->request('GET', '/unread/list');
$form = $crawler->filter('button[id=submit-filter]')->form();
@ -977,7 +1014,7 @@ class EntryControllerTest extends WallabagCoreTestCase
];
$crawler = $client->submit($form, $data);
$this->assertCount(4, $crawler->filter('li.entry'));
$this->assertCount(4, $crawler->filter($this->entryDataTestAttribute));
$form = $crawler->filter('button[id=submit-filter]')->form();
$data = [
@ -985,7 +1022,7 @@ class EntryControllerTest extends WallabagCoreTestCase
];
$crawler = $client->submit($form, $data);
$this->assertCount(0, $crawler->filter('li.entry'));
$this->assertCount(0, $crawler->filter($this->entryDataTestAttribute));
}
public function testFilterOnStatus()
@ -1000,14 +1037,14 @@ class EntryControllerTest extends WallabagCoreTestCase
$form['entry_filter[isUnread]']->untick();
$crawler = $client->submit($form);
$this->assertCount(1, $crawler->filter('li.entry'));
$this->assertCount(1, $crawler->filter($this->entryDataTestAttribute));
$form = $crawler->filter('button[id=submit-filter]')->form();
$form['entry_filter[isArchived]']->untick();
$form['entry_filter[isStarred]']->tick();
$crawler = $client->submit($form);
$this->assertCount(1, $crawler->filter('li.entry'));
$this->assertCount(1, $crawler->filter($this->entryDataTestAttribute));
}
public function testFilterPreselectedStatus()
@ -1044,7 +1081,7 @@ class EntryControllerTest extends WallabagCoreTestCase
$form['entry_filter[isPublic]']->tick();
$crawler = $client->submit($form);
$this->assertCount(0, $crawler->filter('li.entry'));
$this->assertCount(0, $crawler->filter($this->entryDataTestAttribute));
}
public function testPreviewPictureFilter()
@ -1057,7 +1094,7 @@ class EntryControllerTest extends WallabagCoreTestCase
$form['entry_filter[previewPicture]']->tick();
$crawler = $client->submit($form);
$this->assertCount(1, $crawler->filter('li.entry'));
$this->assertCount(1, $crawler->filter($this->entryDataTestAttribute));
}
public function testFilterOnLanguage()
@ -1078,7 +1115,7 @@ class EntryControllerTest extends WallabagCoreTestCase
];
$crawler = $client->submit($form, $data);
$this->assertCount(3, $crawler->filter('li.entry'));
$this->assertCount(3, $crawler->filter($this->entryDataTestAttribute));
$form = $crawler->filter('button[id=submit-filter]')->form();
$data = [
@ -1086,7 +1123,7 @@ class EntryControllerTest extends WallabagCoreTestCase
];
$crawler = $client->submit($form, $data);
$this->assertCount(2, $crawler->filter('li.entry'));
$this->assertCount(2, $crawler->filter($this->entryDataTestAttribute));
}
public function testShareEntryPublicly()
@ -1289,7 +1326,7 @@ class EntryControllerTest extends WallabagCoreTestCase
$crawler = $client->submit($form, $data);
$this->assertCount(1, $crawler->filter('li.entry'));
$this->assertCount(1, $crawler->filter($this->entryDataTestAttribute));
$entry = new Entry($this->getLoggedInUser());
$entry->setUrl($this->url);
@ -1312,7 +1349,7 @@ class EntryControllerTest extends WallabagCoreTestCase
$crawler = $client->submit($form, $data);
$this->assertCount(2, $crawler->filter('li.entry'));
$this->assertCount(2, $crawler->filter($this->entryDataTestAttribute));
$crawler = $client->request('GET', '/all/list');
$form = $crawler->filter('button[id=submit-filter]')->form();
@ -1323,7 +1360,7 @@ class EntryControllerTest extends WallabagCoreTestCase
$crawler = $client->submit($form, $data);
$this->assertCount(8, $crawler->filter('li.entry'));
$this->assertCount(8, $crawler->filter($this->entryDataTestAttribute));
}
public function testSearch()
@ -1347,7 +1384,7 @@ class EntryControllerTest extends WallabagCoreTestCase
$crawler = $client->submit($form, $data);
$this->assertCount(4, $crawler->filter('li.entry'));
$this->assertCount(4, $crawler->filter($this->entryDataTestAttribute));
// Search on starred list
$crawler = $client->request('GET', '/starred/list');
@ -1366,7 +1403,7 @@ class EntryControllerTest extends WallabagCoreTestCase
$crawler = $client->submit($form, $data);
$this->assertCount(1, $crawler->filter('li.entry'));
$this->assertCount(1, $crawler->filter($this->entryDataTestAttribute));
$crawler = $client->request('GET', '/archive/list');
@ -1385,7 +1422,7 @@ class EntryControllerTest extends WallabagCoreTestCase
$crawler = $client->submit($form, $data);
$this->assertCount(1, $crawler->filter('li.entry'));
$this->assertCount(1, $crawler->filter($this->entryDataTestAttribute));
$client->request('GET', '/delete/' . $entry->getId());
// test on list of all articles
@ -1398,7 +1435,7 @@ class EntryControllerTest extends WallabagCoreTestCase
$crawler = $client->submit($form, $data);
$this->assertCount(0, $crawler->filter('li.entry'));
$this->assertCount(0, $crawler->filter($this->entryDataTestAttribute));
// test url search on list of all articles
$entry = new Entry($this->getLoggedInUser());
@ -1417,7 +1454,7 @@ class EntryControllerTest extends WallabagCoreTestCase
$crawler = $client->submit($form, $data);
$this->assertCount(1, $crawler->filter('li.entry'));
$this->assertCount(1, $crawler->filter($this->entryDataTestAttribute));
// same as previous test but for case-sensitivity
$crawler = $client->request('GET', '/all/list');
@ -1429,7 +1466,7 @@ class EntryControllerTest extends WallabagCoreTestCase
$crawler = $client->submit($form, $data);
$this->assertCount(1, $crawler->filter('li.entry'));
$this->assertCount(1, $crawler->filter($this->entryDataTestAttribute));
}
public function dataForLanguage()

View file

@ -347,6 +347,8 @@ class ExportControllerTest extends WallabagCoreTestCase
private function getSanitizedFilename($title)
{
return preg_replace('/[^A-Za-z0-9\- \']/', '', iconv('utf-8', 'us-ascii//TRANSLIT', $title));
$transliterator = \Transliterator::createFromRules(':: Any-Latin; :: Latin-ASCII; :: NFD; :: [:Nonspacing Mark:] Remove; :: NFC;', \Transliterator::FORWARD);
return preg_replace('/[^A-Za-z0-9\- \']/', '', $transliterator->transliterate($title));
}
}

View file

@ -213,15 +213,62 @@ class FeedControllerTest extends WallabagCoreTestCase
$config->setFeedToken('SUPERTOKEN');
$config->setFeedLimit(null);
$em->persist($config);
$entry1 = $em
->getRepository('WallabagCoreBundle:Entry')
->find(1)
;
$entry4 = $em
->getRepository('WallabagCoreBundle:Entry')
->find(4)
;
$now = new \DateTimeImmutable('now');
$day1 = $now->modify('-8 days');
$day2 = $now->modify('-6 days');
$day3 = $now->modify('-4 days');
$day4 = $now->modify('-2 days');
$entry1->setCreatedAt($day1);
$entry4->setCreatedAt($day2);
$property = (new \ReflectionObject($entry1))->getProperty('updatedAt');
$property->setAccessible(true);
$property->setValue($entry1, $day4);
$property = (new \ReflectionObject($entry4))->getProperty('updatedAt');
$property->setAccessible(true);
$property->setValue($entry4, $day3);
$em->flush();
$client = $this->getClient();
$client->request('GET', '/feed/admin/SUPERTOKEN/tags/foo');
// tag foo - without sort
$crawler = $client->request('GET', '/feed/admin/SUPERTOKEN/tags/foo');
$this->assertSame(200, $client->getResponse()->getStatusCode());
$this->assertSame('test title entry4', $crawler->filterXPath('//feed/entry[1]/title')->text());
$this->assertSame('test title entry1', $crawler->filterXPath('//feed/entry[2]/title')->text());
$this->validateDom($client->getResponse()->getContent(), 'tag', 2, 'foo');
// tag foo - with sort created
$crawler = $client->request('GET', '/feed/admin/SUPERTOKEN/tags/foo?sort=created');
$this->assertSame(200, $client->getResponse()->getStatusCode());
$this->assertSame('test title entry4', $crawler->filterXPath('//feed/entry[1]/title')->text());
$this->assertSame('test title entry1', $crawler->filterXPath('//feed/entry[2]/title')->text());
// tag foo - with sort updated
$crawler = $client->request('GET', '/feed/admin/SUPERTOKEN/tags/foo?sort=updated');
$this->assertSame(200, $client->getResponse()->getStatusCode());
$this->assertSame('test title entry1', $crawler->filterXPath('//feed/entry[1]/title')->text());
$this->assertSame('test title entry4', $crawler->filterXPath('//feed/entry[2]/title')->text());
// tag foo - with invalid sort
$client->request('GET', '/feed/admin/SUPERTOKEN/tags/foo?sort=invalid');
$this->assertSame(400, $client->getResponse()->getStatusCode());
// tag foo/3000
$client->request('GET', '/feed/admin/SUPERTOKEN/tags/foo/3000');
$this->assertSame(302, $client->getResponse()->getStatusCode());
}

View file

@ -476,4 +476,35 @@ class TagControllerTest extends WallabagCoreTestCase
$this->assertNotFalse(array_search('cache', $tags, true), 'Tag cache is assigned to the entry');
$this->assertNotFalse(array_search('caché', $tags, true), 'Tag caché is assigned to the entry');
}
public function testAssignTagsOnSearchResults()
{
$this->logInAs('admin');
$client = $this->getClient();
// Search on unread list
$crawler = $client->request('GET', '/unread/list');
$form = $crawler->filter('form[name=search]')->form();
$data = [
'search_entry[term]' => 'title',
];
$crawler = $client->submit($form, $data);
$client->click($crawler->selectLink('entry.list.assign_search_tag')->link());
$client->followRedirect();
$entries = $client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->getBuilderForSearchByUser($this->getLoggedInUserId(), 'title', 'unread')
->getQuery()->getResult();
foreach ($entries as $entry) {
$tags = $entry->getTagsLabel();
$this->assertContains('title', $tags);
}
}
}

View file

@ -65,6 +65,7 @@ class DownloadImagesTest extends TestCase
['image/jpeg', 'jpeg'],
['image/png', 'png'],
['image/gif', 'gif'],
['image/webp', 'webp'],
];
}

View file

@ -202,10 +202,29 @@ class RuleBasedTaggerTest extends TestCase
->method('satisfies')
->willReturn(true);
$this->rulerz
->method('filter')
$query = $this->getMockBuilder('Doctrine\ORM\AbstractQuery')
->disableOriginalConstructor()
->getMock();
$query
->expects($this->once())
->method('getResult')
->willReturn([new Entry($user), new Entry($user)]);
$qb = $this->getMockBuilder('Doctrine\ORM\QueryBuilder')
->disableOriginalConstructor()
->getMock();
$qb
->expects($this->once())
->method('getQuery')
->willReturn($query);
$this->entryRepository
->expects($this->once())
->method('getBuilderForAllByUser')
->willReturn($qb);
$entries = $this->tagger->tagAllForUser($user);
$this->assertCount(2, $entries);
@ -222,6 +241,7 @@ class RuleBasedTaggerTest extends TestCase
{
$user = new User();
$config = new Config($user);
$config->setReadingSpeed(200);
$user->setConfig($config);

View file

@ -114,13 +114,13 @@ class ChromeControllerTest extends WallabagCoreTestCase
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findByUrlAndUserId(
'https://www.usinenouvelle.com/article/la-multiplication-des-chefs-de-projet-est-une-catastrophe-manageriale-majeure-affirme-le-sociologue-francois-dupuy.N307730',
'https://www.20minutes.fr/sport/3256363-20220321-tournoi-vi-nations-trophee-gagne-xv-france-fini-fond-seine',
$this->getLoggedInUserId()
);
$this->assertInstanceOf('Wallabag\CoreBundle\Entity\Entry', $content);
$this->assertNotEmpty($content->getPreviewPicture(), 'Preview picture for http://www.usinenouvelle.com is ok');
$this->assertNotEmpty($content->getLanguage(), 'Language for http://www.usinenouvelle.com is ok');
$this->assertNotEmpty($content->getPreviewPicture(), 'Preview picture for https://www.20minutes.fr is ok');
$this->assertNotEmpty($content->getLanguage(), 'Language for https://www.20minutes.fr is ok');
$this->assertCount(1, $content->getTags());
$createdAt = $content->getCreatedAt();

View file

@ -1,38 +1,36 @@
{
"checksum": "f3aa0e9c0edad632a246f7e98ec64918",
"roots": {
"bookmark_bar": {
"children": [
{
"date_added": "13118850929335823",
"id": "6",
"name": "\"La multiplication des chefs de projet est une catastrophe managériale majeure\", affirme le sociologue François Dupuy - Ressources humaines",
"type": "url",
"url": "http://www.usinenouvelle.com/article/la-multiplication-des-chefs-de-projet-est-une-catastrophe-manageriale-majeure-affirme-le-sociologue-francois-dupuy.N307730"
}
],
"date_added": "13118829474385693",
"date_modified": "13118850929335823",
"id": "1",
"name": "Barre de favoris",
"type": "folder"
},
"other": {
"children": [],
"date_added": "13118829474385701",
"date_modified": "0",
"id": "2",
"name": "Autres favoris",
"type": "folder"
},
"synced": {
"children": [],
"date_added": "13118829474385702",
"date_modified": "0",
"id": "3",
"name": "Favoris sur mobile",
"type": "folder"
}
},
"version": 1
"checksum": "f3aa0e9c0edad632a246f7e98ec64918",
"roots": {
"bookmark_bar": {
"children": [ {
"date_added": "13118850929335823",
"id": "6",
"name": "Tournoi des VI Nations : Le trophée gagné par le XV de France a-t-il fini au fond de la Seine ?",
"type": "url",
"url": "https://www.20minutes.fr/sport/3256363-20220321-tournoi-vi-nations-trophee-gagne-xv-france-fini-fond-seine"
} ],
"date_added": "13118829474385693",
"date_modified": "13118850929335823",
"id": "1",
"name": "Barre de favoris",
"type": "folder"
},
"other": {
"children": [ ],
"date_added": "13118829474385701",
"date_modified": "0",
"id": "2",
"name": "Autres favoris",
"type": "folder"
},
"synced": {
"children": [ ],
"date_added": "13118829474385702",
"date_modified": "0",
"id": "3",
"name": "Favoris sur mobile",
"type": "folder"
}
},
"version": 1
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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