From: Jeremy Benoist Date: Mon, 26 Jun 2017 08:05:48 +0000 (+0200) Subject: Merge remote-tracking branch 'origin/master' into 2.3 X-Git-Tag: 2.3.0~31^2~47 X-Git-Url: https://git.immae.eu/?p=github%2Fwallabag%2Fwallabag.git;a=commitdiff_plain;h=42708d1121fef12c84487247b170eb03083d5ffc;hp=53e1892eb61dc839d5cb58c9d2af466a779bc68e Merge remote-tracking branch 'origin/master' into 2.3 --- diff --git a/.babelrc b/.babelrc new file mode 100644 index 00000000..cd38bcca --- /dev/null +++ b/.babelrc @@ -0,0 +1,5 @@ +{ + "presets": [ + ["env", {"modules": false}] + ] +} diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 96854460..5d703325 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -3,7 +3,7 @@ ### Issue details Please provide issue details here. -Remember, this is _not_ a place to ask questions. For that, go to http://gitter.im/wallabag/wallabag. +Remember, this is _not_ a place to ask questions. For that, go to https://community.wallabag.org/ (forum) or http://gitter.im/wallabag/wallabag (chat). ### Environment diff --git a/.gitignore b/.gitignore index 0bcbabb8..8fd762fa 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ web/bundles/* !web/bundles/wallabagcore /web/assets/images/* !web/assets/images/.gitkeep +web/bundles/wallabagcore/*.dev.js # Build /app/build @@ -55,3 +56,4 @@ app/Resources/build/ # Test-generated files admin-export.json specialexport.json +/data/site-credentials-secret-key.txt diff --git a/.travis.yml b/.travis.yml index 29ca7dc3..e6cf96cb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,11 +20,10 @@ cache: - vendor - $HOME/.composer/cache - node_modules - - $HOME/.cache/bower - $HOME/.npm + - $HOME/.yarn-cache php: - - 5.5 - 5.6 - 7.0 - 7.1 @@ -60,9 +59,9 @@ before_script: - if [[ $DB = pgsql ]]; then psql -c 'create database wallabag_test;' -U postgres; fi; install: - - if [[ $ASSETS = build ]]; then source ~/.nvm/nvm.sh && nvm install 6.7; fi; - - if [[ $ASSETS = build ]]; then npm install -g npm@latest; fi; - - if [[ $ASSETS = build ]]; then npm install; fi; + - if [[ $ASSETS = build ]]; then source ~/.nvm/nvm.sh && nvm install 6.10; fi; + - if [[ $ASSETS = build ]]; then npm install -g yarn@latest; fi; + - if [[ $ASSETS = build ]]; then yarn install; fi; before_install: - if [[ $TRAVIS_REPO_SLUG = wallabag/wallabag ]]; then cp .composer-auth.json ~/.composer/auth.json; fi; @@ -70,9 +69,18 @@ before_install: script: - travis_wait bash composer install -o --no-interaction --no-progress --prefer-dist - ant prepare-$DB + + - echo "travis_fold:start:migrations" + - php bin/console doctrine:migrations:migrate --no-interaction --env=test + - echo "travis_fold:end:migrations" + + - echo "travis_fold:start:fixtures" + - php bin/console doctrine:fixtures:load --no-interaction --env=test + - echo "travis_fold:end:fixtures" + - if [[ $VALIDATE_TRANSLATION_FILE = '' ]]; then ./bin/simple-phpunit -v ; fi; - if [[ $CS_FIXER = run ]]; then php bin/php-cs-fixer fix src/ --verbose --dry-run ; fi; - if [[ $VALIDATE_TRANSLATION_FILE = run ]]; then php bin/console lint:yaml src/Wallabag/CoreBundle/Resources/translations -v ; fi; - if [[ $VALIDATE_TRANSLATION_FILE = run ]]; then php bin/console lint:yaml app/Resources/CraueConfigBundle/translations -v ; fi; - if [[ $VALIDATE_TRANSLATION_FILE = run ]]; then php bin/console lint:yaml src/Wallabag/UserBundle/Resources/translations -v ; fi; - - if [[ $ASSETS = build ]]; then ./node_modules/grunt-cli/bin/grunt tests; fi; + - if [[ $ASSETS = build ]]; then yarn run build:prod; fi; diff --git a/Gruntfile.js b/Gruntfile.js deleted file mode 100644 index 72473b21..00000000 --- a/Gruntfile.js +++ /dev/null @@ -1,230 +0,0 @@ -module.exports = function (grunt) { - require('load-grunt-tasks')(grunt); - - grunt.initConfig({ - appDir: 'app/Resources/static', - buildDir: 'app/Resources/build', - modulesDir: 'node_modules', - releaseDir: 'web/bundles/wallabagcore', - - postcss: { - material: { - options: { - processors: [ - require('pixrem')(), - require('autoprefixer')({ browsers: 'last 2 versions' }), - require('cssnano')(), - ], - }, - src: '<%= buildDir %>/material.css', - dest: '<%= releaseDir %>/themes/material/css/style.min.css', - }, - baggy: { - options: { - processors: [ - require('pixrem')(), - require('autoprefixer')({ browsers: 'last 2 versions' }), - require('cssnano')(), - ], - }, - src: '<%= buildDir %>/baggy.css', - dest: '<%= releaseDir %>/themes/baggy/css/style.min.css', - }, - }, - concat: { - options: { - separator: ';', - }, - cssMaterial: { - src: [ - 'node_modules/materialize-css/bin/materialize.css', - '<%= appDir %>/themes/material/css/*.css', - ], - dest: '<%= buildDir %>/material.css', - }, - cssBaggy: { - src: [ - '<%= appDir %>/themes/baggy/css/*.css', - ], - dest: '<%= buildDir %>/baggy.css', - }, - }, - browserify: { - dist: { - files: { - '<%= buildDir %>/material.browser.js': ['<%= appDir %>/themes/material/js/init.js'], - '<%= buildDir %>/baggy.browser.js': ['<%= appDir %>/themes/baggy/js/init.js'] - } - }, - options: { - sourceType: "module", - transform: [ - ["babelify", { - presets: ["es2015"] - }], ["browserify-shim", { - "jquery": { - "exports": "$" - }, - "materialize": "materialize", - "jquery-ui": { - "depends": "jquery", - "exports": null - } - }] - ], - browserifyOptions: { - browser: { - "jQuery": "./node_modules/jquery/dist/jquery.js", - "jquery.tinydot": "./node_modules/jquery.tinydot/src/jquery.tinydot.js", - "jquery.ui": "./node_modules/jquery-ui-browserify/dist/jquery-ui.js" - } - } - } - - }, - uglify: { - material: { - files: { - '<%= releaseDir %>/themes/material/js/material.min.js': - ['<%= buildDir %>/material.browser.js'], - } - }, - baggy: { - files: { - '<%= releaseDir %>/themes/baggy/js/baggy.min.js': - ['<%= buildDir %>/baggy.browser.js'], - } - }, - }, - copy: { - pickerjs: { - expand: true, - cwd: '<%= modulesDir %>/pickadate/lib', - src: 'picker.js', - dest: '<%= buildDir %>', - }, - annotator: { - expand: true, - cwd: '<%= modulesDir %>/annotator/pkg', - src: 'annotator.min.js', - dest: '<%= buildDir %>/themes/_global/js/', - }, - baggyfonts: { - files: [ - { - expand: true, - cwd: '<%= modulesDir %>/icomoon-free-npm/Font', - src: 'IcoMoon-Free.ttf', - dest: '<%= releaseDir %>/themes/baggy/fonts/', - }, - { - expand: true, - cwd: '<%= modulesDir %>/ptsans-npm-webfont/fonts', - src: 'ptsansbold.woff', - dest: '<%= releaseDir %>/themes/baggy/fonts/', - }, - { - expand: true, - cwd: '<%= modulesDir %>/material-design-icons-iconfont/dist/fonts/', - src: ['MaterialIcons-Regular.eot', 'MaterialIcons-Regular.woff2', 'MaterialIcons-Regular.woff', 'MaterialIcons-Regular.ttf'], - dest: '<%= releaseDir %>/themes/baggy/fonts/', - }, - ], - }, - materialfonts: { - files: [ - { - expand: true, - overwrite: true, - cwd: '<%= modulesDir %>/icomoon-free-npm/Font', - src: 'IcoMoon-Free.ttf', - dest: '<%= releaseDir %>/themes/material/fonts', - }, - { - expand: true, - overwrite: true, - cwd: '<%= modulesDir %>/roboto-fontface/fonts/Roboto', - src: '*', - dest: '<%= releaseDir %>/themes/material/font/roboto', - }, - { - expand: true, - overwrite: true, - cwd: '<%= modulesDir %>/material-design-icons-iconfont/dist/fonts/', - src: ['MaterialIcons-Regular.eot', 'MaterialIcons-Regular.woff2', 'MaterialIcons-Regular.woff', 'MaterialIcons-Regular.ttf'], - dest: '<%= releaseDir %>/themes/material/fonts/', - }, - ], - }, - }, - symlink: { - pics: { - files: [ - { - expand: true, - overwrite: true, - cwd: '<%= appDir %>/themes/_global/', - src: 'img', - dest: '<%= releaseDir %>/themes/_global/', - }, - ], - }, - }, - clean: { - css: { - src: ['<%= buildDir %>/**/*.css'], - }, - js: { - src: ['<%= buildDir %>/**/*.js', '<%= buildDir %>/**/*.map'], - }, - all: { - src: ['./<%= buildDir %>'], - }, - release: { - src: ['./<%= releaseDir %>/*'], - } - }, - eslint: { - target: ['<%= appDir %>/themes/material/js/init.js', '<%= appDir %>/themes/baggy/js/init.js'] - }, - stylelint: { - target: ['<%= appDir %>/themes/material/css/*.css', '<%= appDir %>/themes/baggy/css/*.css'] - }, - watch: { - files: ['<%= appDir %>/**/*.css', '<%= appDir %>/**/*.js'], - tasks: ['css', 'js'] - } - }); - - grunt.registerTask( - 'fonts', - 'Install fonts', - ['copy:baggyfonts', 'copy:materialfonts'] - ); - - grunt.registerTask( - 'js', - 'Build and install js files', - ['clean:js', 'copy:pickerjs', 'browserify', 'uglify'] - ); - - grunt.registerTask( - 'default', - 'Build and install everything', - ['clean', 'copy:pickerjs', 'concat', 'browserify', 'uglify', 'postcss', 'copy', 'symlink'] - ); - - grunt.registerTask( - 'css', - 'Compiles the stylesheets.', - ['clean:css', 'concat:cssMaterial', 'concat:cssBaggy', 'postcss'] - ); - - grunt.registerTask( - 'tests', - 'Test css and js style conformity', - ['eslint', 'stylelint', 'default'] - ), - - grunt.loadNpmTasks('grunt-contrib-watch'); -}; diff --git a/app/AppKernel.php b/app/AppKernel.php index c8382d5f..c50783a6 100644 --- a/app/AppKernel.php +++ b/app/AppKernel.php @@ -48,6 +48,10 @@ class AppKernel extends Kernel $bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle(); $bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle(); $bundles[] = new Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle(); + + if ('test' === $this->getEnvironment()) { + $bundles[] = new DAMA\DoctrineTestBundle\DAMADoctrineTestBundle(); + } } return $bundles; @@ -71,5 +75,14 @@ class AppKernel extends Kernel public function registerContainerConfiguration(LoaderInterface $loader) { $loader->load($this->getRootDir().'/config/config_'.$this->getEnvironment().'.yml'); + $loader->load(function ($container) { + if ($container->getParameter('use_webpack_dev_server')) { + $container->loadFromExtension('framework', [ + 'assets' => [ + 'base_url' => 'http://localhost:8080/', + ], + ]); + } + }); } } diff --git a/app/DoctrineMigrations/Version20160410190541.php b/app/DoctrineMigrations/Version20160410190541.php index 6294d842..5e5cae35 100644 --- a/app/DoctrineMigrations/Version20160410190541.php +++ b/app/DoctrineMigrations/Version20160410190541.php @@ -8,7 +8,7 @@ use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** - * Added foreign keys for account resetting + * Added foreign keys for account resetting. */ class Version20160410190541 extends AbstractMigration implements ContainerAwareInterface { diff --git a/app/DoctrineMigrations/Version20160812120952.php b/app/DoctrineMigrations/Version20160812120952.php index bd6e8d63..13272267 100644 --- a/app/DoctrineMigrations/Version20160812120952.php +++ b/app/DoctrineMigrations/Version20160812120952.php @@ -8,7 +8,7 @@ use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** - * Added name field on wallabag_oauth2_clients + * Added name field on wallabag_oauth2_clients. */ class Version20160812120952 extends AbstractMigration implements ContainerAwareInterface { diff --git a/app/DoctrineMigrations/Version20160911214952.php b/app/DoctrineMigrations/Version20160911214952.php index edef81ed..4eae46e7 100644 --- a/app/DoctrineMigrations/Version20160911214952.php +++ b/app/DoctrineMigrations/Version20160911214952.php @@ -8,7 +8,7 @@ use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** - * Added settings for RabbitMQ and Redis imports + * Added settings for RabbitMQ and Redis imports. */ class Version20160911214952 extends AbstractMigration implements ContainerAwareInterface { diff --git a/app/DoctrineMigrations/Version20160916201049.php b/app/DoctrineMigrations/Version20160916201049.php index 9f8e77e7..ff34c894 100644 --- a/app/DoctrineMigrations/Version20160916201049.php +++ b/app/DoctrineMigrations/Version20160916201049.php @@ -8,7 +8,7 @@ use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** - * Added pocket_consumer_key field on wallabag_config + * Added pocket_consumer_key field on wallabag_config. */ class Version20160916201049 extends AbstractMigration implements ContainerAwareInterface { diff --git a/app/DoctrineMigrations/Version20161001072726.php b/app/DoctrineMigrations/Version20161001072726.php index f247c236..ad761541 100644 --- a/app/DoctrineMigrations/Version20161001072726.php +++ b/app/DoctrineMigrations/Version20161001072726.php @@ -9,7 +9,7 @@ use Symfony\Component\DependencyInjection\ContainerInterface; use Doctrine\DBAL\Migrations\SkipMigrationException; /** - * Added pocket_consumer_key field on wallabag_config + * Added pocket_consumer_key field on wallabag_config. */ class Version20161001072726 extends AbstractMigration implements ContainerAwareInterface { diff --git a/app/DoctrineMigrations/Version20161022134138.php b/app/DoctrineMigrations/Version20161022134138.php index c71166a0..39949e7d 100644 --- a/app/DoctrineMigrations/Version20161022134138.php +++ b/app/DoctrineMigrations/Version20161022134138.php @@ -8,7 +8,7 @@ use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** - * Converted database to utf8mb4 encoding (for MySQL only) + * Converted database to utf8mb4 encoding (for MySQL only). */ class Version20161022134138 extends AbstractMigration implements ContainerAwareInterface { diff --git a/app/DoctrineMigrations/Version20161024212538.php b/app/DoctrineMigrations/Version20161024212538.php index ecb872d1..b2f6aaf8 100644 --- a/app/DoctrineMigrations/Version20161024212538.php +++ b/app/DoctrineMigrations/Version20161024212538.php @@ -8,7 +8,7 @@ use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** - * Added user_id column on oauth2_clients to prevent users to delete API clients from other users + * Added user_id column on oauth2_clients to prevent users to delete API clients from other users. */ class Version20161024212538 extends AbstractMigration implements ContainerAwareInterface { diff --git a/app/DoctrineMigrations/Version20161031132655.php b/app/DoctrineMigrations/Version20161031132655.php index 83b97ca9..ef846412 100644 --- a/app/DoctrineMigrations/Version20161031132655.php +++ b/app/DoctrineMigrations/Version20161031132655.php @@ -8,7 +8,7 @@ use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** - * Added the internal setting to enable/disable downloading pictures + * Added the internal setting to enable/disable downloading pictures. */ class Version20161031132655 extends AbstractMigration implements ContainerAwareInterface { diff --git a/app/DoctrineMigrations/Version20161104073720.php b/app/DoctrineMigrations/Version20161104073720.php index fb8f5fa1..0e05f02e 100644 --- a/app/DoctrineMigrations/Version20161104073720.php +++ b/app/DoctrineMigrations/Version20161104073720.php @@ -8,7 +8,7 @@ use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** - * Added created_at index on entry table + * Added created_at index on entry table. */ class Version20161104073720 extends AbstractMigration implements ContainerAwareInterface { diff --git a/app/DoctrineMigrations/Version20161106113822.php b/app/DoctrineMigrations/Version20161106113822.php index de3702a4..facc14f4 100644 --- a/app/DoctrineMigrations/Version20161106113822.php +++ b/app/DoctrineMigrations/Version20161106113822.php @@ -8,7 +8,7 @@ use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** - * Added action_mark_as_read field on config table + * Added action_mark_as_read field on config table. */ class Version20161106113822 extends AbstractMigration implements ContainerAwareInterface { diff --git a/app/DoctrineMigrations/Version20161117071626.php b/app/DoctrineMigrations/Version20161117071626.php index 8daa2142..e779eacf 100644 --- a/app/DoctrineMigrations/Version20161117071626.php +++ b/app/DoctrineMigrations/Version20161117071626.php @@ -8,7 +8,7 @@ use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** - * Added the internal setting to share articles to unmark.it + * Added the internal setting to share articles to unmark.it. */ class Version20161117071626 extends AbstractMigration implements ContainerAwareInterface { diff --git a/app/DoctrineMigrations/Version20161214094402.php b/app/DoctrineMigrations/Version20161214094402.php index db125f76..8ca32b09 100644 --- a/app/DoctrineMigrations/Version20161214094402.php +++ b/app/DoctrineMigrations/Version20161214094402.php @@ -8,7 +8,7 @@ use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** - * Renamed uuid to uid in entry table + * Renamed uuid to uid in entry table. */ class Version20161214094402 extends AbstractMigration implements ContainerAwareInterface { diff --git a/app/DoctrineMigrations/Version20161214094403.php b/app/DoctrineMigrations/Version20161214094403.php index 5948b5fa..c7326f95 100644 --- a/app/DoctrineMigrations/Version20161214094403.php +++ b/app/DoctrineMigrations/Version20161214094403.php @@ -8,7 +8,7 @@ use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** - * Added index on wallabag_entry.uid + * Added index on wallabag_entry.uid. */ class Version20161214094403 extends AbstractMigration implements ContainerAwareInterface { diff --git a/app/DoctrineMigrations/Version20170127093841.php b/app/DoctrineMigrations/Version20170127093841.php index 20c79479..5bfd9670 100644 --- a/app/DoctrineMigrations/Version20170127093841.php +++ b/app/DoctrineMigrations/Version20170127093841.php @@ -8,7 +8,7 @@ use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** - * Added indexes on wallabag_entry.is_starred and wallabag_entry.is_archived + * Added indexes on wallabag_entry.is_starred and wallabag_entry.is_archived. */ class Version20170127093841 extends AbstractMigration implements ContainerAwareInterface { diff --git a/app/DoctrineMigrations/Version20170327194233.php b/app/DoctrineMigrations/Version20170327194233.php new file mode 100644 index 00000000..e1466b2f --- /dev/null +++ b/app/DoctrineMigrations/Version20170327194233.php @@ -0,0 +1,54 @@ +container = $container; + } + + private function getTable($tableName) + { + return $this->container->getParameter('database_table_prefix').$tableName; + } + + /** + * @param Schema $schema + */ + public function up(Schema $schema) + { + $scuttle = $this->container + ->get('doctrine.orm.default_entity_manager') + ->getConnection() + ->fetchArray('SELECT * FROM '.$this->getTable('craue_config_setting')." WHERE name = 'share_scuttle'"); + + $this->skipIf(false !== $scuttle, 'It seems that you already played this migration.'); + + $this->addSql('INSERT INTO '.$this->getTable('craue_config_setting')." (name, value, section) VALUES ('share_scuttle', '1', 'entry')"); + $this->addSql('INSERT INTO '.$this->getTable('craue_config_setting')." (name, value, section) VALUES ('scuttle_url', 'http://scuttle.org', 'entry')"); + } + + /** + * @param Schema $schema + */ + public function down(Schema $schema) + { + $this->addSql('DELETE FROM '.$this->getTable('craue_config_setting')." WHERE name = 'share_scuttle';"); + $this->addSql('DELETE FROM '.$this->getTable('craue_config_setting')." WHERE name = 'scuttle_url';"); + } +} diff --git a/app/DoctrineMigrations/Version20170405182620.php b/app/DoctrineMigrations/Version20170405182620.php new file mode 100644 index 00000000..3ef9633f --- /dev/null +++ b/app/DoctrineMigrations/Version20170405182620.php @@ -0,0 +1,65 @@ +container = $container; + } + + private function getTable($tableName) + { + return $this->container->getParameter('database_table_prefix').$tableName; + } + + /** + * @param Schema $schema + */ + public function up(Schema $schema) + { + $entryTable = $schema->getTable($this->getTable('entry')); + + $this->skipIf($entryTable->hasColumn('published_at'), 'It seems that you already played this migration.'); + + $entryTable->addColumn('published_at', 'datetime', [ + 'notnull' => false, + ]); + + $this->skipIf($entryTable->hasColumn('published_by'), 'It seems that you already played this migration.'); + + $entryTable->addColumn('published_by', 'text', [ + 'notnull' => false, + ]); + } + + /** + * @param Schema $schema + */ + public function down(Schema $schema) + { + $entryTable = $schema->getTable($this->getTable('entry')); + + $this->skipIf(!$entryTable->hasColumn('published_at'), 'It seems that you already played this migration.'); + + $entryTable->dropColumn('published_at'); + + $this->skipIf(!$entryTable->hasColumn('published_by'), 'It seems that you already played this migration.'); + + $entryTable->dropColumn('published_by'); + } +} diff --git a/app/DoctrineMigrations/Version20170407200919.php b/app/DoctrineMigrations/Version20170407200919.php new file mode 100644 index 00000000..4b9d475a --- /dev/null +++ b/app/DoctrineMigrations/Version20170407200919.php @@ -0,0 +1,51 @@ +container = $container; + } + + private function getTable($tableName) + { + return $this->container->getParameter('database_table_prefix').$tableName; + } + + /** + * @param Schema $schema + */ + public function up(Schema $schema) + { + $entryTable = $schema->getTable($this->getTable('entry')); + $this->skipIf(!$entryTable->hasColumn('is_public'), 'It seems that you already played this migration.'); + + $entryTable->dropColumn('is_public'); + } + + /** + * @param Schema $schema + */ + public function down(Schema $schema) + { + $entryTable = $schema->getTable($this->getTable('entry')); + $this->skipIf($entryTable->hasColumn('is_public'), 'It seems that you already played this migration.'); + + $entryTable->addColumn('is_public', 'boolean', ['notnull' => false, 'default' => 0]); + } +} diff --git a/app/DoctrineMigrations/Version20170420134133.php b/app/DoctrineMigrations/Version20170420134133.php new file mode 100644 index 00000000..b1ab7bcb --- /dev/null +++ b/app/DoctrineMigrations/Version20170420134133.php @@ -0,0 +1,52 @@ +container = $container; + } + + private function getTable($tableName) + { + return $this->container->getParameter('database_table_prefix').$tableName; + } + + /** + * @param Schema $schema + */ + public function up(Schema $schema) + { + $this->addSql('DELETE FROM '.$this->getTable('craue_config_setting')." WHERE name = 'download_pictures';"); + } + + /** + * @param Schema $schema + */ + public function down(Schema $schema) + { + $downloadPictures = $this->container + ->get('doctrine.orm.default_entity_manager') + ->getConnection() + ->fetchArray('SELECT * FROM '.$this->getTable('craue_config_setting')." WHERE name = 'download_pictures'"); + + $this->skipIf(false !== $downloadPictures, 'It seems that you already played this migration.'); + + $this->addSql('INSERT INTO '.$this->getTable('craue_config_setting')." (name, value, section) VALUES ('download_pictures', '1', 'entry')"); + } +} diff --git a/app/DoctrineMigrations/Version20170501115751.php b/app/DoctrineMigrations/Version20170501115751.php new file mode 100644 index 00000000..7f068eb8 --- /dev/null +++ b/app/DoctrineMigrations/Version20170501115751.php @@ -0,0 +1,61 @@ +container = $container; + } + + private function getTable($tableName) + { + return $this->container->getParameter('database_table_prefix').$tableName; + } + + /** + * @param Schema $schema + */ + public function up(Schema $schema) + { + $this->skipIf($schema->hasTable($this->getTable('site_credential')), 'It seems that you already played this migration.'); + + $table = $schema->createTable($this->getTable('site_credential')); + $table->addColumn('id', 'integer', ['autoincrement' => true]); + $table->addColumn('user_id', 'integer'); + $table->addColumn('host', 'string', ['length' => 255]); + $table->addColumn('username', 'text'); + $table->addColumn('password', 'text'); + $table->addColumn('createdAt', 'datetime'); + $table->addIndex(['user_id'], 'idx_user'); + $table->setPrimaryKey(['id']); + $table->addForeignKeyConstraint($this->getTable('user'), ['user_id'], ['id'], [], 'fk_user'); + + if ('postgresql' === $this->connection->getDatabasePlatform()->getName()) { + $schema->dropSequence('site_credential_id_seq'); + $schema->createSequence('site_credential_id_seq'); + } + } + + /** + * @param Schema $schema + */ + public function down(Schema $schema) + { + $schema->dropTable($this->getTable('site_credential')); + } +} diff --git a/app/DoctrineMigrations/Version20170510082609.php b/app/DoctrineMigrations/Version20170510082609.php new file mode 100644 index 00000000..a99af2d2 --- /dev/null +++ b/app/DoctrineMigrations/Version20170510082609.php @@ -0,0 +1,60 @@ +container = $container; + } + + private function getTable($tableName) + { + return $this->container->getParameter('database_table_prefix').$tableName; + } + + /** + * @param Schema $schema + */ + public function up(Schema $schema) + { + $this->skipIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'This migration only apply to MySQL'); + + foreach ($this->fields as $field) { + $this->addSql('ALTER TABLE '.$this->getTable('user').' CHANGE '.$field.' '.$field.' VARCHAR(180) NOT NULL;'); + } + } + + /** + * @param Schema $schema + */ + public function down(Schema $schema) + { + $this->skipIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'This migration only apply to MySQL'); + + foreach ($this->fields as $field) { + $this->addSql('ALTER TABLE '.$this->getTable('user').' CHANGE '.$field.' '.$field.' VARCHAR(255) NOT NULL;'); + } + } +} diff --git a/app/DoctrineMigrations/Version20170511115400.php b/app/DoctrineMigrations/Version20170511115400.php new file mode 100644 index 00000000..64ee9e0a --- /dev/null +++ b/app/DoctrineMigrations/Version20170511115400.php @@ -0,0 +1,55 @@ +container = $container; + } + + private function getTable($tableName) + { + return $this->container->getParameter('database_table_prefix').$tableName; + } + + /** + * @param Schema $schema + */ + public function up(Schema $schema) + { + $entryTable = $schema->getTable($this->getTable('entry')); + + $this->skipIf($entryTable->hasColumn('headers'), 'It seems that you already played this migration.'); + + $entryTable->addColumn('headers', 'text', [ + 'notnull' => false, + ]); + } + + /** + * @param Schema $schema + */ + public function down(Schema $schema) + { + $entryTable = $schema->getTable($this->getTable('entry')); + + $this->skipIf(!$entryTable->hasColumn('headers'), 'It seems that you already played this migration.'); + + $entryTable->dropColumn('headers'); + } +} diff --git a/app/DoctrineMigrations/Version20170511211659.php b/app/DoctrineMigrations/Version20170511211659.php new file mode 100644 index 00000000..f2d5cf5e --- /dev/null +++ b/app/DoctrineMigrations/Version20170511211659.php @@ -0,0 +1,100 @@ +container = $container; + } + + private function getTable($tableName) + { + return $this->container->getParameter('database_table_prefix') . $tableName; + } + + public function up(Schema $schema) + { + $tableName = $this->getTable('annotation'); + + switch ($this->connection->getDatabasePlatform()->getName()) { + case 'sqlite': + $this->addSql(<<addSql('DROP TABLE ' . $tableName); + $this->addSql(<<addSql(<<addSql('DROP TABLE __temp__wallabag_annotation'); + break; + + case 'mysql': + $this->addSql('ALTER TABLE '.$tableName.' MODIFY quote TEXT NOT NULL'); + break; + + case 'postgresql': + $this->addSql('ALTER TABLE '.$tableName.' ALTER COLUMN quote TYPE TEXT'); + break; + } + } + + public function down(Schema $schema) + { + $tableName = $this->getTable('annotation'); + + switch ($this->connection->getDatabasePlatform()->getName()) { + case 'sqlite': + throw new SkipMigrationException('Too complex ...'); + break; + + case 'mysql': + $this->addSql('ALTER TABLE '.$tableName.' MODIFY quote VARCHAR(255) NOT NULL'); + break; + + case 'postgresql': + $this->addSql('ALTER TABLE '.$tableName.' ALTER COLUMN quote TYPE VARCHAR(255)'); + break; + } + } +} diff --git a/app/DoctrineMigrations/Version20170602075214.php b/app/DoctrineMigrations/Version20170602075214.php new file mode 100644 index 00000000..451d16ba --- /dev/null +++ b/app/DoctrineMigrations/Version20170602075214.php @@ -0,0 +1,52 @@ +container = $container; + } + + private function getTable($tableName) + { + return $this->container->getParameter('database_table_prefix').$tableName; + } + + /** + * @param Schema $schema + */ + public function up(Schema $schema) + { + $apiUserRegistration = $this->container + ->get('doctrine.orm.default_entity_manager') + ->getConnection() + ->fetchArray('SELECT * FROM '.$this->getTable('craue_config_setting')." WHERE name = 'api_user_registration'"); + + $this->skipIf(false !== $apiUserRegistration, 'It seems that you already played this migration.'); + + $this->addSql('INSERT INTO '.$this->getTable('craue_config_setting')." (name, value, section) VALUES ('api_user_registration', '0', 'api')"); + } + + /** + * @param Schema $schema + */ + public function down(Schema $schema) + { + $this->addSql('DELETE FROM '.$this->getTable('craue_config_setting')." WHERE name = 'api_user_registration';"); + } +} diff --git a/app/DoctrineMigrations/Version20170606155640.php b/app/DoctrineMigrations/Version20170606155640.php new file mode 100644 index 00000000..e9b50428 --- /dev/null +++ b/app/DoctrineMigrations/Version20170606155640.php @@ -0,0 +1,53 @@ +container = $container; + } + + private function getTable($tableName) + { + return $this->container->getParameter('database_table_prefix').$tableName; + } + + /** + * @param Schema $schema + */ + public function up(Schema $schema) + { + $apiUserRegistration = $this->container + ->get('doctrine.orm.default_entity_manager') + ->getConnection() + ->fetchArray('SELECT * FROM '.$this->getTable('craue_config_setting')." WHERE name = 'wallabag_url'"); + + $this->skipIf(false === $apiUserRegistration, 'It seems that you already played this migration.'); + + $this->addSql('DELETE FROM '.$this->getTable('craue_config_setting')." WHERE name = 'wallabag_url'"); + } + + /** + * @param Schema $schema + */ + public function down(Schema $schema) + { + $this->addSql('INSERT INTO '.$this->getTable('craue_config_setting')." (name, value, section) VALUES ('wallabag_url', 'wallabag.me', 'misc')"); + } +} diff --git a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.da.yml b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.da.yml index c65463db..5475e571 100644 --- a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.da.yml +++ b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.da.yml @@ -15,11 +15,11 @@ shaarli_url: Shaarli-URL, hvis tjenesten er aktiv share_diaspora: Aktiver deling til Diaspora share_mail: Aktiver deling med email share_shaarli: Aktiver deling gennem Shaarli +share_scuttle: Aktiver deling gennem Scuttle share_twitter: Aktiver deling gennem Twitter share_unmark: Aktiver deling gennem Unmark.it show_printlink: Vis et link til print-indhold wallabag_support_url: Support-URL for wallabag -wallabag_url: URL for *sin* wallabag-installation entry: "artikel" export: "eksport" import: "import" @@ -33,3 +33,4 @@ demo_mode_username: "Demobruger" # share_public: Allow public url for entries # download_images_enabled: Download images locally # restricted_access: Enable authentication for websites with paywall +# api_user_registration: Enable user to be registered using the API diff --git a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.de.yml b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.de.yml index bc378147..f57db303 100644 --- a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.de.yml +++ b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.de.yml @@ -15,11 +15,11 @@ shaarli_url: Shaarli-URL, sofern der Service aktiviert ist share_diaspora: Teilen zu Diaspora aktiveren share_mail: Teilen via E-Mail aktiveren share_shaarli: Teilen zu Shaarli aktiveren +share_scuttle: Teilen zu Scuttle aktiveren share_twitter: Teilen zu Twitter aktiveren share_unmark: Teilen zu Unmark.it aktiveren show_printlink: Link anzeigen, um den Inhalt auszudrucken wallabag_support_url: Support-URL für wallabag -wallabag_url: URL von *deiner* wallabag-Instanz entry: "Artikel" export: "Export" import: "Import" @@ -33,3 +33,4 @@ demo_mode_username: "Test-Benutzer" share_public: Erlaube eine öffentliche URL für Einträge # download_images_enabled: Download images locally # restricted_access: Enable authentication for websites with paywall +# api_user_registration: Enable user to be registered using the API diff --git a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.en.yml b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.en.yml index 52cb8e20..1e4f4668 100644 --- a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.en.yml +++ b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.en.yml @@ -15,11 +15,11 @@ shaarli_url: Shaarli URL, if the service is enabled share_diaspora: Enable share to Diaspora share_mail: Enable share by email share_shaarli: Enable share to Shaarli +share_scuttle: Enable share to Scuttle share_twitter: Enable share to Twitter share_unmark: Enable share to Unmark.it show_printlink: Display a link to print content wallabag_support_url: Support URL for wallabag -wallabag_url: URL of *your* wallabag instance entry: "article" export: "export" import: "import" @@ -33,3 +33,4 @@ demo_mode_username: "Demo user" share_public: Allow public url for entries download_images_enabled: Download images locally restricted_access: Enable authentication for websites with paywall +api_user_registration: Enable user to be registered using the API diff --git a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.es.yml b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.es.yml index c338836d..46ed4040 100644 --- a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.es.yml +++ b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.es.yml @@ -15,11 +15,11 @@ shaarli_url: URL de Shaarli, si el servicio está activado share_diaspora: Activar compartir con Diaspora share_mail: Activar compartir con Email share_shaarli: Activar compartir con Shaarli +share_scuttle: Activar compartir con Scuttle share_twitter: Activar compartir con Twitter share_unmark: Activar compartir con Unmark.it show_printlink: Mostrar un enlace para imprimir contenido wallabag_support_url: URL de soporte de wallabag -wallabag_url: URL de *tu* instancia de wallabag entry: "artículo" export: "exportar" import: "importar" @@ -33,3 +33,4 @@ demo_mode_username: "Nombre de usuario demo" share_public: Permitir URL pública para los artículos download_images_enabled: Descargar imágenes localmente restricted_access: Activar autenticación para websites con paywall +# api_user_registration: Enable user to be registered using the API diff --git a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.fa.yml b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.fa.yml index 7a341e0b..f8da4acf 100644 --- a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.fa.yml +++ b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.fa.yml @@ -15,11 +15,11 @@ shaarli_url: نشانی Shaarli، اگر فعال بود share_diaspora: فعال‌سازی هم‌رسانی به Diaspora share_mail: فعال‌سازی هم‌رسانی با ایمیل share_shaarli: فعال‌سازی هم‌رسانی به Shaarli +share_scuttle: فعال‌سازی هم‌رسانی به Scuttle share_twitter: فعال‌سازی هم‌رسانی به Twitter share_unmark: فعال‌سازی هم‌رسانی به Unmark.it show_printlink: نمایش پیوندی برای چاپ مطلب wallabag_support_url: نشانی صفحهٔ پشتیبانی wallabag -wallabag_url: نشانی صفحهٔ wallabag *شما* entry: "مقاله" export: "برون‌سپاری" import: "درون‌ریزی" @@ -33,3 +33,4 @@ modify_settings: "اعمال" # share_public: Allow public url for entries # download_images_enabled: Download images locally # restricted_access: Enable authentication for websites with paywall +# api_user_registration: Enable user to be registered using the API diff --git a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.fr.yml b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.fr.yml index f5c886d6..a79409b4 100644 --- a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.fr.yml +++ b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.fr.yml @@ -15,11 +15,11 @@ shaarli_url: URL de Shaarli, si le service Shaarli est activé share_diaspora: Activer le partage vers Diaspora share_mail: Activer le partage par email share_shaarli: Activer le partage vers Shaarli +share_scuttle: Activer le partage vers Scuttle share_twitter: Activer le partage vers Twitter share_unmark: Activer le partage vers Unmark.it show_printlink: Afficher un lien pour imprimer wallabag_support_url: URL de support de wallabag -wallabag_url: URL de *votre* instance de wallabag entry: "article" export: "export" import: "import" @@ -33,3 +33,4 @@ demo_mode_username: "Utilisateur de la démo" share_public: Autoriser une URL publique pour les articles download_images_enabled: Télécharger les images en local restricted_access: Activer l'authentification pour les articles derrière un paywall +api_user_registration: Activer la création de compte depuis l'API diff --git a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.it.yml b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.it.yml index 88a1b4f6..dd4def4b 100644 --- a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.it.yml +++ b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.it.yml @@ -15,11 +15,11 @@ shaarli_url: Shaarli URL, se il servizio è abilitato share_diaspora: Abilita la condivisione con Diaspora share_mail: Abilita la condivisione per email share_shaarli: Abilita la condivisione con Shaarli +share_scuttle: Abilita la condivisione con Scuttle share_twitter: Abilita la condivisione con Twitter share_unmark: Abilita la condivisione con Unmark.it show_printlink: Mostra un collegamento per stampare il contenuto wallabag_support_url: URL di supporto per wallabag -wallabag_url: URL della *tua* installazione di wallabag entry: "contenuto" export: "esporta" import: "importa" @@ -33,3 +33,4 @@ demo_mode_username: "Utente Demo" # share_public: Allow public url for entries # download_images_enabled: Download images locally # restricted_access: Enable authentication for websites with paywall +api_user_registration: Abilita la registrazione dell'utente attraverso l'API diff --git a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.oc.yml b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.oc.yml index 04aaf0e8..66d9ae26 100644 --- a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.oc.yml +++ b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.oc.yml @@ -15,11 +15,11 @@ shaarli_url: URL de Shaarli, se lo servici Shaarli es activat share_diaspora: Activar lo partatge cap a Diaspora share_mail: Activar lo partatge per corrièl share_shaarli: Activar lo partatge cap a Shaarli +share_scuttle: Activar lo partatge cap a Scuttle share_twitter: Activar lo partatge cap a Twitter share_unmark: Activar lo partatge cap a Unmark.it show_printlink: Afichar un ligam per imprimir wallabag_support_url: URL d'assisténcia de wallabag -wallabag_url: URL de *vòstra* instància de wallabag entry: "article" export: "expòrt" import: "impòrt" @@ -33,3 +33,4 @@ demo_mode_username: "Utilizaire de la demostracion" share_public: Autorizar una URL publica pels articles download_images_enabled: Telecargar los imatges en local restricted_access: Activar l'autenticacion pels sites amb peatge +# api_user_registration: Enable user to be registered using the API diff --git a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.pl.yml b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.pl.yml index 1203e159..04ad4fd3 100644 --- a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.pl.yml +++ b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.pl.yml @@ -15,11 +15,11 @@ shaarli_url: Adress URL Shaarli, jeżeli usługa jest włączona share_diaspora: Włącz udostępnianie dla Diaspora share_mail: Włącz udostępnianie przez email share_shaarli: Włącz udostępnianie dla Shaarli +share_scuttle: Włącz udostępnianie dla Scuttle share_twitter: Włącz udostępnianie dla Twitter share_unmark: Włącz udostępnianie dla Unmark.it show_printlink: Pokaż link do wydrukowania zawartości wallabag_support_url: Adres URL wsparcia dla wallabag -wallabag_url: Adres *twojej* instacji wallabag entry: "artykuł" export: "eksport" import: "import" @@ -33,3 +33,4 @@ demo_mode_username: "Użytkownik Demonstracyjny" share_public: Zezwalaj na publiczny adres url dla wpisow download_images_enabled: Pobierz obrazy lokalnie restricted_access: Włącz autoryzację dla stron z paywallem +api_user_registration: Włącz rejestrację użytkownika przy użyciu API diff --git a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.pt.yml b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.pt.yml index 1edde87a..77e22b37 100644 --- a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.pt.yml +++ b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.pt.yml @@ -15,11 +15,11 @@ shaarli_url: URL Shaarli, se o serviço está habilitado share_diaspora: Habilitar compartilhamento para o Diaspora share_mail: Habilitar compartilhamento por e-mail share_shaarli: Habilitar compartilhamento para o Shaarli +share_scuttle: Habilitar compartilhamento para o Scuttle share_twitter: Habilitar compartilhamento para o Twitter share_unmark: Habilitar compartilhamento para o Unmark.it show_printlink: Mostrar um link para imprimir o conteúdo wallabag_support_url: URL de Suporte do wallabag -wallabag_url: URL de *sua* instância do wallabag entry: "artigo" export: "exportar" import: "importar" @@ -33,3 +33,4 @@ demo_mode_username: "Usuário demo" # share_public: Allow public url for entries # download_images_enabled: Download images locally # restricted_access: Enable authentication for websites with paywall +# api_user_registration: Enable user to be registered using the API diff --git a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.ro.yml b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.ro.yml index f0c935d3..1b4bfb5b 100644 --- a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.ro.yml +++ b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.ro.yml @@ -15,11 +15,11 @@ shaarli_url: Shaarli URL, dacă serviciul este permis share_diaspora: Permite share către Diaspora share_mail: Permite share prin email share_shaarli: Permite share către Shaarli +share_scuttle: Permite share către Scuttle share_twitter: Permite share către Twitter share_unmark: Permite share către Unmark.it show_printlink: Afișează un link pentru a printa content-ul wallabag_support_url: URL-ul de suport pentru wallabag -wallabag_url: URL-ul instanței tale wallabag entry: "alticol" export: "exportă" import: "importă" @@ -33,3 +33,4 @@ modify_settings: "aplică" # share_public: Allow public url for entries # download_images_enabled: Download images locally # restricted_access: Enable authentication for websites with paywall +# api_user_registration: Enable user to be registered using the API diff --git a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.tr.yml b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.tr.yml index eb40fc5e..1579366d 100644 --- a/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.tr.yml +++ b/app/Resources/CraueConfigBundle/translations/CraueConfigBundle.tr.yml @@ -15,6 +15,7 @@ # share_diaspora: Enable share to Diaspora # share_mail: Enable share by email # share_shaarli: Enable share to Shaarli +# share_scuttle: Enable share to Scuttle # share_twitter: Enable share to Twitter # share_unmark: Enable share to Unmark.it # show_printlink: Display a link to print content @@ -33,3 +34,4 @@ # share_public: Allow public url for entries # download_images_enabled: Download images locally # restricted_access: Enable authentication for websites with paywall +# api_user_registration: Enable user to be registered using the API diff --git a/app/Resources/CraueConfigBundle/views/Settings/modify.html.twig b/app/Resources/CraueConfigBundle/views/Settings/modify.html.twig index c4e735ee..db153270 100644 --- a/app/Resources/CraueConfigBundle/views/Settings/modify.html.twig +++ b/app/Resources/CraueConfigBundle/views/Settings/modify.html.twig @@ -20,12 +20,10 @@ {% for section in sections | craue_sortSections %}
- {% for setting in form.settings if setting.section.vars.value == section %} - {{ form_row(setting.name) }} - {{ form_row(setting.section) }} - {{ form_row(setting.value, { - 'label': setting.name.vars.value | trans({}, 'CraueConfigBundle'), - }) }} + {% for setting in form.settings if setting.vars.value.section == section %} + {{ form_row(setting.value, { + 'label': setting.vars.value.name | trans({}, 'CraueConfigBundle'), + }) }} {% endfor %}
{% endfor %} @@ -35,8 +33,7 @@ {{ 'modify_settings' | trans({}, 'CraueConfigBundle') }} - {{ form_rest(form) }} - {{ form_end(form) }} + {{ form_widget(form._token) }} diff --git a/app/Resources/static/themes/_global/global.scss b/app/Resources/static/themes/_global/global.scss new file mode 100644 index 00000000..0e877efb --- /dev/null +++ b/app/Resources/static/themes/_global/global.scss @@ -0,0 +1,13 @@ +/* Rules for sizing the icon. */ +.material-icons.md-18 { font-size: 18px; } +.material-icons.md-24 { font-size: 24px; } +.material-icons.md-36 { font-size: 36px; } +.material-icons.md-48 { font-size: 48px; } + +/* Rules for using icons as black on a light background. */ +.material-icons.md-dark { color: rgba(0, 0, 0, 0.54); } +.material-icons.md-dark.md-inactive { color: rgba(0, 0, 0, 0.26); } + +/* Rules for using icons as white on a dark background. */ +.material-icons.md-light { color: rgba(255, 255, 255, 1); } +.material-icons.md-light.md-inactive { color: rgba(255, 255, 255, 0.3); } diff --git a/app/Resources/static/themes/_global/img/icons/Diaspora-asterisk.svg b/app/Resources/static/themes/_global/img/icons/Diaspora-asterisk.svg new file mode 100644 index 00000000..91764b60 --- /dev/null +++ b/app/Resources/static/themes/_global/img/icons/Diaspora-asterisk.svg @@ -0,0 +1,334 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/app/Resources/static/themes/_global/img/icons/scuttle.png b/app/Resources/static/themes/_global/img/icons/scuttle.png new file mode 100644 index 00000000..3b8eb264 Binary files /dev/null and b/app/Resources/static/themes/_global/img/icons/scuttle.png differ diff --git a/app/Resources/static/themes/_global/index.js b/app/Resources/static/themes/_global/index.js new file mode 100644 index 00000000..3ec26488 --- /dev/null +++ b/app/Resources/static/themes/_global/index.js @@ -0,0 +1,71 @@ +/* jQuery */ +import $ from 'jquery'; + +/* Annotations */ +import annotator from 'annotator'; + +/* Fonts */ +import 'material-design-icons-iconfont/dist/material-design-icons.css'; +import 'lato-font/css/lato-font.css'; +import './global.scss'; + +/* Shortcuts*/ +import './js/shortcuts/entry'; +import './js/shortcuts/main'; + +import { savePercent, retrievePercent } from './js/tools'; + + +/* ========================================================================== + Annotations & Remember position + ========================================================================== */ + +$(document).ready(() => { + if ($('article').length) { + const app = new annotator.App(); + + app.include(annotator.ui.main, { + element: document.querySelector('article'), + }); + + const authorization = { + permits() { return true; }, + }; + app.registry.registerUtility(authorization, 'authorizationPolicy'); + + const x = JSON.parse($('#annotationroutes').html()); + app.include(annotator.storage.http, $.extend({}, x, { + onError(msg, xhr) { + if (!Object.prototype.hasOwnProperty.call(xhr, 'responseJSON')) { + annotator.notification.banner('An error occurred', 'error'); + return; + } + $.each(xhr.responseJSON.children, (k, v) => { + if (v.errors) { + $.each(v.errors, (n, errorText) => { + annotator.notification.banner(errorText, 'error'); + }); + } + }); + }, + })); + + app.start().then(() => { + app.annotations.load({ entry: x.entryId }); + }); + + $(window).scroll(() => { + const scrollTop = $(window).scrollTop(); + const docHeight = $(document).height(); + const scrollPercent = (scrollTop) / (docHeight); + const scrollPercentRounded = Math.round(scrollPercent * 100) / 100; + savePercent(x.entryId, scrollPercentRounded); + }); + + retrievePercent(x.entryId); + + $(window).resize(() => { + retrievePercent(x.entryId); + }); + } +}); diff --git a/app/Resources/static/themes/_global/js/tools.js b/app/Resources/static/themes/_global/js/tools.js index cee84fa8..774f4539 100644 --- a/app/Resources/static/themes/_global/js/tools.js +++ b/app/Resources/static/themes/_global/js/tools.js @@ -31,25 +31,4 @@ function retrievePercent(id) { return true; } -function initFilters() { - // no display if filters not available - if ($('div').is('#filters')) { - $('#button_filters').show(); - $('.js-filters-action').sideNav({ edge: 'right' }); - $('#clear_form_filters').on('click', () => { - $('#filters input').val(''); - $('#filters :checked').removeAttr('checked'); - return false; - }); - } -} - -function initExport() { - // no display if export not available - if ($('div').is('#export')) { - $('#button_export').show(); - $('.js-export-action').sideNav({ edge: 'right' }); - } -} - -export { savePercent, retrievePercent, initFilters, initExport }; +export { savePercent, retrievePercent }; diff --git a/app/Resources/static/themes/_global/share.js b/app/Resources/static/themes/_global/share.js new file mode 100644 index 00000000..d7078616 --- /dev/null +++ b/app/Resources/static/themes/_global/share.js @@ -0,0 +1 @@ +import './share.scss'; diff --git a/app/Resources/static/themes/_global/share.scss b/app/Resources/static/themes/_global/share.scss new file mode 100644 index 00000000..5a853324 --- /dev/null +++ b/app/Resources/static/themes/_global/share.scss @@ -0,0 +1,407 @@ +/* -------------------------- Reset ------------------- */ + +html, +body, +div, +span, +applet, +object, +iframe, +h1, +h2, +h3, +h4, +h5, +h6, +p, +blockquote, +pre, +a, +abbr, +acronym, +address, +big, +cite, +code, +del, +dfn, +em, +img, +ins, +kbd, +q, +s, +samp, +small, +strike, +strong, +sub, +sup, +tt, +b, +u, +i, +dl, +dt, +dd, +ol, +ul, +li, +fieldset, +form, +label, +legend, +table, +caption, +tbody, +tfoot, +thead, +tr, +th, +td, +article, +aside, +canvas, +details, +embed, +figure, +figcaption, +footer, +header, +hgroup, +menu, +nav, +output, +section, +summary, +time, +mark, +audio, +video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + vertical-align: baseline; +} + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +menu, +nav, +section { + display: block; +} + +body { + line-height: 1; +} + +blockquote, +q { + quotes: none; +} + +blockquote::before, +blockquote::after, +q::before, +q::after { + content: ''; + content: none; +} + +table { + border-collapse: collapse; + border-spacing: 0; +} + +/* -------------------------- General properties ------------------- */ + +body { + background-color: white; + color: #444; + font-family: Georgia; + line-height: 1.7; + -ms-content-zooming: none; + margin-bottom: 64px; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + font-weight: 600; + margin: 0.2em 0; +} + +article h1, +article h2, +article h3, +article h4, +article h5, +article h6 { + text-align: left !important; + line-height: 1.3; +} + +h1 { + font-size: 1.4em; +} + +h2 { + font-size: 1.3em; +} + +h3, +h4 { + font-size: 1.2em; +} + +h5, +h6 { + font-size: 1.1em; +} + +p { + margin-bottom: 0.75em; +} + +b, +strong { + font-weight: bold; +} + +i, +em { + font-style: italic; +} + +a { + color: #444; + text-decoration: underline; +} + +a:active, +a:hover { + outline: 0; +} + +p, +ul, +ol, +dl { + margin: 0 0 1.75em; +} + +ul, +ol { + padding-left: 1.25em; +} + +li { + padding-bottom: 0.2em; + line-height: 1.6; +} + +li p:last-child, +li li:last-child { + margin-bottom: -0.2em; +} + +ul li:last-child, +ol li:last-child { + padding-bottom: 0; +} + +mark { + padding: 0 0.2em; +} + +mark a { + text-decoration: none; +} + +blockquote { + font-style: italic; + border-left: 0.25em solid black; + margin-left: -20px; + padding-left: 17px; + margin-bottom: 0.5em; + margin-top: 0.5em; +} + +blockquote cite { + text-transform: uppercase; + font-size: 0.8em; + font-style: normal; +} + +blockquote cite::before { + content: "—"; + margin-right: 0.5em; +} + +img { + display: block; + height: auto; + margin-bottom: 0.5em; + max-width: 100%; +} + +figure { + margin: 0; +} + +figure figcaption { + display: block; + margin-top: 0.3em; + font-style: italic; + font-size: 0.8em; +} + +button { + display: none !important; +} + +hr { + display: block; + height: 1px; + border: solid #666; + border-width: 1px 0 0; + margin: 1.6em 0; + padding: 0; +} + +small { + font-size: 0.7em; +} + +dl { + margin: 1.6em 0; +} + +dl dt { + float: left; + width: 11.25em; + overflow: hidden; + clear: left; + text-align: right; + -ms-text-overflow: ellipsis; + -o-text-overflow: ellipsis; + text-overflow: ellipsis; + white-space: nowrap; + font-weight: bold; + margin-bottom: 1em; +} + +dl dd { + margin-left: 12.5em; + margin-bottom: 1em; +} + +pre { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + margin: 4em 0; + border: 0.0625em solid #efefef; + width: 100%; + padding: 1em; + font-family: Consolas, monospace; + white-space: pre; + overflow: auto; +} + +pre code { + font-size: 0.8em; + line-height: 1.6em; + white-space: pre-wrap; + background: transparent; + border: none; + padding: 0; + vertical-align: inherit; +} + +code { + padding: 0.125em 0.375em; + margin: 0 0.2em; + font-family: Consolas, monospace; + font-size: 0.8em; + white-space: pre; + border: 1px solid lightgray; + overflow: auto; +} + +audio, +video { + max-width: 43.75em; +} + +::selection, +mark { + background: #666; + color: white; +} + +table { + border-collapse: collapse; + margin-bottom: 2em; + width: 100%; +} + +th, +td { + padding: 0.25em; + text-align: left; +} + +thead tr { + text-transform: uppercase; + font-size: 0.85em; + letter-spacing: 1px; + font-family: "Segoe UI", sans-serif; + font-weight: 600; +} + +tbody tr:nth-child(2n+1) { + background: rgba(0, 0, 0, 0.1); +} + +tbody { + border: solid #999; + border-width: 1px 0; +} + +article { + padding: 0 1em; +} + +/* --------------------- Responsive design ------------------------- */ + +@media (min-width: 720px) { + blockquote { + margin-left: -1.4375em; + padding-left: 1.25em; + } + + article { + margin: 0 auto; + max-width: 43.75em; + padding: 0 1.25em; + } + + header { + max-width: 43.75em; + margin: 0 auto; + margin-top: 32px; + text-align: center; + } +} diff --git a/app/Resources/static/themes/baggy/css/article.scss b/app/Resources/static/themes/baggy/css/article.scss new file mode 100644 index 00000000..9094ad55 --- /dev/null +++ b/app/Resources/static/themes/baggy/css/article.scss @@ -0,0 +1,165 @@ + +#article { + width: 70%; + margin-bottom: 3em; + text-align: justify; + + .tags { + margin-bottom: 1em; + } + + i { + font-style: normal; + } + + h1 { + text-align: left; + } + + h2::after { + content: none; + } + + h2, + h3, + h4 { + text-transform: none; + } +} + +blockquote { + border: 1px solid #999; + background-color: #fff; + padding: 1em; + margin: 0; +} + +.topPosF { + position: fixed; + right: 20%; + bottom: 2em; + font-size: 1.5em; +} + +#article_toolbar { + margin-bottom: 1em; + + li { + display: inline-block; + margin: 3px auto; + } + + a { + background-color: #000; + padding: 0.3em 0.5em 0.2em; + color: #fff; + text-decoration: none; + + &:hover, + &:focus { + background-color: #999; + } + } +} + +#nav-btn-add-tag { + cursor: pointer; +} + +.shaarli::before { + content: "*"; +} + +.return { + text-decoration: none; + margin-top: 1em; + display: block; +} + +.return::before { + margin-right: 0.5em; +} + +.notags { + font-style: italic; + color: #999; +} + +.icon-rss { + background-color: #000; + color: #fff; + padding: 0.2em 0.5em; + + &::before { + position: relative; + top: 2px; + } +} + +.list-tags { + li { + margin-bottom: 0.5em; + } + + .icon-rss:hover, + .icon-rss:focus { + background-color: #fff; + color: #000; + text-decoration: none; + } + + a { + text-decoration: none; + + &:hover, + &:focus { + text-decoration: underline; + } + } +} + +pre code { + font-family: "Courier New", Courier, monospace; +} + +#filters { + position: fixed; + width: 20%; + height: 100%; + top: 0; + right: 0; + background-color: #fff; + padding: 30px 30px 15px 15px; + border-left: 1px #333 solid; + z-index: 12; + min-width: 300px; + + form .filter-group { + margin: 5px; + } +} + +#download-form { + position: fixed; + width: 10%; + height: 100%; + top: 0; + right: 0; + background-color: #fff; + padding: 30px 30px 15px 15px; + border-left: 1px #333 solid; + z-index: 12; + min-width: 200px; + + li { + display: block; + padding: 0.5em 2em 0.5em 1em; + color: #fff; + position: relative; + text-transform: uppercase; + text-decoration: none; + font-weight: 400; + font-family: PT Sans, sans-serif; + transition: all 0.5s ease; + } +} diff --git a/app/Resources/static/themes/baggy/css/font.css b/app/Resources/static/themes/baggy/css/font.css deleted file mode 100755 index 47edcb83..00000000 --- a/app/Resources/static/themes/baggy/css/font.css +++ /dev/null @@ -1,6 +0,0 @@ -@font-face { - font-family: "PT Sans"; - font-style: normal; - font-weight: 700; - src: local("PT Sans Bold"), local("PTSans-Bold"), url("../fonts/ptsansbold.woff") format("woff"); -} diff --git a/app/Resources/static/themes/baggy/css/guide.scss b/app/Resources/static/themes/baggy/css/guide.scss new file mode 100644 index 00000000..afb47c4a --- /dev/null +++ b/app/Resources/static/themes/baggy/css/guide.scss @@ -0,0 +1,263 @@ + +::selection { + color: #fff; + background-color: #000; +} + +.desktopHide { + display: none; +} + +.logo { + position: fixed; + z-index: 20; + top: 0.4em; + left: 0.6em; +} + +h2, +h3, +h4 { + font-family: "PT Sans", sans-serif; + text-transform: uppercase; +} + +p, +li, +label { + color: #666; +} + +a { + color: #000; + font-weight: bold; + + &.nostyle { + text-decoration: none; + } + + &:hover, + &:focus { + text-decoration: none; + } +} + +form fieldset { + border: 0; + padding: 0; + margin: 0; +} + +form input[type="text"], +form input[type="number"], +select, +form input[type="password"], +form input[type="url"], +form input[type="email"] { + border: 1px solid #999; + padding: 0.5em 1em; + min-width: 12em; + color: #666; +} + +@media screen and (-webkit-min-device-pixel-ratio: 0) { + select { + -webkit-appearance: none; + border-radius: 0; + background: #fff url("../../_global/img/bg-select.png") no-repeat right center; + } +} + +.inline { + .row { + display: inline-block; + margin-right: 0.5em; + } + + label { + min-width: 6em; + } +} + +fieldset label { + display: inline-block; + min-width: 12.5em; + color: #666; +} + +label { + margin-right: 0.5em; +} + +form .row { + margin-bottom: 0.5em; +} + +form button, +input[type="submit"] { + cursor: pointer; + background-color: #000; + color: #fff; + padding: 0.5em 1em; + display: inline-block; + border: 1px solid #000; +} + +form button:hover, +form button:focus, +input[type="submit"]:hover, +input[type="submit"]:focus { + background-color: #fff; + color: #000; + -webkit-transition: all 0.5s ease; + -moz-transition: all 0.5s ease; + -ms-transition: all 0.5s ease; + -o-transition: all 0.5s ease; + transition: all 0.5s ease; +} + +#bookmarklet { + cursor: move; +} + +h2::after { + content: ""; + height: 4px; + width: 20%; + background-color: #000; + display: block; +} + +.links { + padding: 0; + margin: 0; + + li { + list-style: none; + margin: 0; + padding: 0; + } +} + +#links { + position: fixed; + top: 0; + width: 10em; + left: 0; + text-align: right; + background-color: #333; + padding-top: 9.5em; + height: 100%; + box-shadow: inset -4px 0 20px rgba(0, 0, 0, 0.6); + z-index: 15; + + > li > a { + display: block; + padding: 0.5em 2em 0.5em 1em; + color: #fff; + position: relative; + text-transform: uppercase; + text-decoration: none; + font-weight: normal; + font-family: "PT Sans", sans-serif; + transition: all 0.5s ease; + + &:hover, + &:focus { + background-color: #999; + color: #000; + } + } + + .current::after { + content: ""; + width: 0; + height: 0; + position: absolute; + border: 10px solid transparent; + border-right-color: #eee; + right: 0; + top: 50%; + margin-top: -10px; + } + + li:last-child { + position: fixed; + bottom: 1em; + width: 10em; + + a::before { + font-size: 1.2em; + position: relative; + top: 2px; + } + } +} + +#main { + margin-left: 12em; + position: relative; + z-index: 10; + padding-right: 5%; + padding-bottom: 1em; +} + +#sort { + padding: 0; + list-style-type: none; + opacity: 0.5; + display: inline-block; + + li { + display: inline; + font-size: 0.9em; + + & + li { + margin-left: 10px; + } + } + + a { + padding: 2px 2px 0; + vertical-align: middle; + } + + img { + vertical-align: baseline; + + :hover { + cursor: pointer; + } + } +} + +#display-mode { + float: right; + margin-top: 10px; + margin-bottom: 10px; + opacity: 0.5; +} + +#listmode { + width: 16px; + display: inline-block; + text-decoration: none; + + &.tablemode { + background: url("../../_global/img/table.png") no-repeat bottom; + } + + .listmode { + background: url("../../_global/img/list.png") no-repeat bottom; + } +} + +#warning_message { + position: fixed; + background-color: #ff6347; + z-index: 1000; + bottom: 0; + left: 0; + width: 100%; + color: #000; +} diff --git a/app/Resources/static/themes/baggy/css/index.scss b/app/Resources/static/themes/baggy/css/index.scss new file mode 100644 index 00000000..e7a11963 --- /dev/null +++ b/app/Resources/static/themes/baggy/css/index.scss @@ -0,0 +1,13 @@ +/* Style */ +@import 'guide'; +@import 'layout'; +@import 'article'; +@import 'pictos'; +@import 'login'; +@import 'save'; +@import 'messages'; + +/* Tools */ +@import 'media_queries'; +@import 'print'; +@import 'ratatouille'; diff --git a/app/Resources/static/themes/baggy/css/layout.scss b/app/Resources/static/themes/baggy/css/layout.scss new file mode 100644 index 00000000..cb14e62d --- /dev/null +++ b/app/Resources/static/themes/baggy/css/layout.scss @@ -0,0 +1,300 @@ +#content { + margin-top: 2em; + min-height: 30em; +} + +footer { + text-align: right; + position: relative; + bottom: 0; + right: 5em; + color: #999; + font-size: 0.8em; + font-style: italic; + z-index: 20; + + a { + color: #999; + font-weight: normal; + } +} + +.list-entries { + letter-spacing: -5px; +} + +.listmode.entry { + width: 100%; + height: inherit; +} + +.card-entry-tags { + max-height: 2em; + overflow-y: hidden; + padding: 0; + margin: 0; +} + +.card-entry-tags li, +.card-entry-tags span { + display: inline-block; + margin: 0 5px; + padding: 5px 12px; + background-color: rgba(0, 0, 0, 0.6); + border-radius: 3px; + max-height: 2em; + overflow: hidden; + text-overflow: ellipsis; +} + +.card-entry-tags a, +.card-entry-labels a { + text-decoration: none; + font-weight: normal; + color: #fff; +} + +.nav-panel-add-tag { + margin-top: 10px; +} + +.list-entries + .results { + margin-bottom: 2em; +} + +.reading-time, +.created-at { + color: #999; + font-style: italic; + font-weight: normal; + font-size: 0.9em; +} + +.estimatedTime small { + position: relative; + top: -1px; +} + +.entry { + background-color: #fff; + letter-spacing: normal; + box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3); + display: inline-block; + width: 32%; + margin-bottom: 1.5em; + vertical-align: top; + margin-right: 1%; + position: relative; + overflow: hidden; + padding: 1.5em 0 3em; + height: 440px; + + img.preview { + width: 100%; + object-fit: cover; + height: 100%; + } + + &::before { + content: ""; + width: 0; + height: 0; + border: 10px solid transparent; + border-bottom-color: #000; + position: absolute; + bottom: 0.7em; + z-index: 10; + right: 1.5em; + transition: all 0.5s ease; + } + + &::after { + content: ""; + position: absolute; + height: 7px; + width: 100%; + bottom: 0; + left: 0; + background-color: #000; + transition: all 0.5s ease; + } + + &:hover { + box-shadow: 0 3px 10px rgba(0, 0, 0, 1); + + &::after { + height: 40px; + } + + &::before { + bottom: 2.3em; + } + + h2 a { + color: #666; + } + + .tools { + bottom: 0; + } + } + + h2 { + text-transform: none; + margin-bottom: 0; + line-height: 1.2; + margin-left: 5px; + } + + &::after { + content: none; + } + + a { + display: block; + text-decoration: none; + color: #000; + word-wrap: break-word; + transition: all 0.5s ease; + } + + p { + color: #666; + font-size: 0.9em; + line-height: 1.7; + margin: 5px 5px auto; + } + + h2 a::first-letter { + text-transform: uppercase; + } + + .tools { + position: absolute; + bottom: -40px; + left: 0; + background: #000; + width: 100%; + z-index: 10; + padding-right: 0.5em; + text-align: right; + transition: all 0.5s ease; + + a { + color: #666; + text-decoration: none; + display: block; + padding: 0.4em; + + &:hover { + color: #fff; + } + } + + li { + display: inline-block; + margin-top: 10px; + } + + li:first-child { + float: left; + font-size: 0.9em; + max-width: calc(100% - 40px * 4); + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + max-height: 2em; + margin-left: 10px; + } + } + + .card-entry-labels { + position: absolute; + top: 100px; + left: -1em; + z-index: 90; + max-width: 50%; + padding-left: 0; + + li { + margin: 10px 10px 10px auto; + padding: 5px 12px 5px 25px; + background-color: rgba(0, 0, 0, 0.6); + border-radius: 0 3px 3px 0; + color: #fff; + cursor: default; + max-height: 2em; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + + a { + color: #fff; + } + } + } +} + +.entry:nth-child(3n+1) { + margin-left: 0; +} + +.results { + letter-spacing: -5px; + padding: 0 0 0.5em; + + > * { + display: inline-block; + vertical-align: top; + letter-spacing: normal; + width: 50%; + text-align: right; + } +} + +div.pagination ul { + text-align: right; +} + +.nb-results { + text-align: left; + font-style: italic; + color: #999; + display: inline-flex; +} + +div.pagination ul { + a { + color: #999; + text-decoration: none; + + &:hover, + &:focus { + text-decoration: underline; + } + } + + > * { + display: inline-block; + margin-left: 0.5em; + } + + .prev.disabled, + .next.disabled { + display: none; + } + + .current { + height: 25px; + padding: 4px 8px; + border: 1px solid #d5d5d5; + text-decoration: none; + font-weight: bold; + color: #000; + background-color: #ccc; + } +} + +.hide { + display: none; +} diff --git a/app/Resources/static/themes/baggy/css/login.scss b/app/Resources/static/themes/baggy/css/login.scss new file mode 100644 index 00000000..312df670 --- /dev/null +++ b/app/Resources/static/themes/baggy/css/login.scss @@ -0,0 +1,26 @@ +.login { + background-color: #333; + + #main { + padding: 0; + margin: 0; + } + + form { + background-color: #fff; + padding: 1.5em; + box-shadow: 0 1px 8px rgba(0, 0, 0, 0.9); + width: 20em; + position: absolute; + top: 8em; + left: 50%; + margin-left: -10em; + } + + .logo { + position: absolute; + top: 2em; + left: 50%; + margin-left: -55px; + } +} diff --git a/app/Resources/static/themes/baggy/css/main.css b/app/Resources/static/themes/baggy/css/main.css index e16846ea..f82c6bee 100755 --- a/app/Resources/static/themes/baggy/css/main.css +++ b/app/Resources/static/themes/baggy/css/main.css @@ -912,6 +912,14 @@ a.add-to-wallabag-link-after::after { content: "\e953"; } +.icon-pencil2::before { + content: "\e906"; +} + +.icon-users::before { + content: "\e972"; +} + .icon-time::before { content: "\e952"; } @@ -947,6 +955,11 @@ a.add-to-wallabag-link-after::after { background-image: url("../../_global/img/icons/shaarli.png"); } +/* scuttle */ +.icon-image--scuttle { + background-image: url("../../_global/img/icons/scuttle.png"); +} + /* ========================================================================== Icon selected ========================================================================== */ @@ -1063,6 +1076,10 @@ blockquote { content: "*"; } +.scuttle::before { + content: "*"; +} + .return { text-decoration: none; margin-top: 1em; diff --git a/app/Resources/static/themes/baggy/css/media_queries.scss b/app/Resources/static/themes/baggy/css/media_queries.scss new file mode 100755 index 00000000..c33db0b3 --- /dev/null +++ b/app/Resources/static/themes/baggy/css/media_queries.scss @@ -0,0 +1,172 @@ + +@media screen and (max-width: 1050px) { + .entry { + width: 49%; + } + + .entry:nth-child(3n+1) { + margin-left: 1.5%; + } + + .entry:nth-child(2n+1) { + margin-left: 0; + } +} + +@media screen and (max-width: 900px) { + #article { + width: 80%; + } + + .topPosF { + right: 2.5em; + } +} + +@media screen and (max-width: 700px) { + .entry { + width: 100%; + margin-left: 0; + } + + #display-mode { + display: none; + } +} + +@media screen and (max-height: 770px) { + .menu.users, + .menu.internal, + .menu.developer { + display: none; + } +} + +@media screen and (max-width: 500px) { + .entry { + width: 100%; + margin-left: 0; + } + + body > header { + background-color: #333; + position: fixed; + top: 0; + width: 100%; + height: 3em; + z-index: 11; + } + + #links li:last-child { + position: static; + width: auto; + } + + #links li:last-child a::before { + content: none; + } + + .logo { + width: 1.25em; + height: 1.25em; + left: 0; + top: 0; + } + + .login > header { + position: static; + } + + .login form { + width: 100%; + position: static; + margin-left: 0; + } + + .login .logo { + height: auto; + top: 0.5em; + width: 75px; + margin-left: -37.5px; + } + + .desktopHide { + display: block; + position: fixed; + z-index: 20; + top: 0; + right: 0; + border: 0; + width: 2.5em; + height: 2.5em; + cursor: pointer; + background-color: #999; + font-size: 1.2em; + } + + .desktopHide:hover, + .desktopHide:focus { + background-color: #fff; + } + + #links { + display: none; + width: 100%; + height: auto; + padding-top: 3em; + } + + #links.menu--open { + display: block; + } + + footer { + position: static; + margin-right: 3em; + } + + #main { + margin-left: 1.5em; + padding-right: 1.5em; + position: static; + margin-top: 3em; + } + + .card-entry-labels { + display: none; + } + + #article_toolbar .topPosF { + display: none; + } + + #article { + width: 100%; + } + + #article h1 { + font-size: 1.5em; + } + + #article_toolbar a { + padding: 0.3em 0.4em 0.2em; + } + + #display-mode { + display: none; + } + + .popup-form, + #bagit-form, + #search-form { + left: 0; + width: 100%; + border-left: none; + } + + .popup-form form, + #bagit-form form, + #search-form form { + width: 100%; + } +} diff --git a/app/Resources/static/themes/baggy/css/messages.css b/app/Resources/static/themes/baggy/css/messages.css deleted file mode 100755 index bfaf1448..00000000 --- a/app/Resources/static/themes/baggy/css/messages.css +++ /dev/null @@ -1,19 +0,0 @@ -.messages.error.install { - border: 1px solid #c42608; - color: #c00 !important; - background: #fff0ef; - text-align: left; -} - -.messages.notice.install { - border: 1px solid #ebcd41; - color: #000; - background: #fffcd3; - text-align: left; -} - -.messages.success.install { - border: 1px solid #6dc70c; - background: #e0fbcc !important; - text-align: left; -} diff --git a/app/Resources/static/themes/baggy/css/messages.scss b/app/Resources/static/themes/baggy/css/messages.scss new file mode 100755 index 00000000..a388419e --- /dev/null +++ b/app/Resources/static/themes/baggy/css/messages.scss @@ -0,0 +1,50 @@ +/* ========================================================================== + Messages + ========================================================================== */ + +.messages { + text-align: left; + width: 60%; + margin: auto 17%; + + > * { + display: inline-block; + } + + .install { + text-align: left; + + &.error { + border: 1px solid #c42608; + color: #c00 !important; + background: #fff0ef; + } + + &.notice { + border: 1px solid #ebcd41; + color: #000; + background: #fffcd3; + } + + &.success { + border: 1px solid #6dc70c; + background: #e0fbcc !important; + } + } +} + +.warning { + font-weight: bold; + display: block; + width: 100%; +} + +.more-info { + font-size: 0.85em; + line-height: 1.5; + color: #aaa; + + a { + color: #aaa; + } +} diff --git a/app/Resources/static/themes/baggy/css/pictos.scss b/app/Resources/static/themes/baggy/css/pictos.scss new file mode 100644 index 00000000..2ff01937 --- /dev/null +++ b/app/Resources/static/themes/baggy/css/pictos.scss @@ -0,0 +1,210 @@ +/* ========================================================================== + Pictos + ========================================================================== */ + +@font-face { + font-family: icomoon; + src: url('~icomoon-free-npm/Font/IcoMoon-Free.ttf'); + font-weight: normal; + font-style: normal; +} + +.material-icons { + font-family: 'Material Icons'; + font-weight: normal; + font-style: normal; + font-size: 1em; /* Preferred icon size */ + width: 1em; + height: 1em; + display: inline-block; + line-height: 1; + text-transform: none; + letter-spacing: normal; + word-wrap: normal; + white-space: nowrap; + direction: ltr; + + /* Support for all WebKit browsers. */ + -webkit-font-smoothing: antialiased; + + /* Support for Safari and Chrome. */ + text-rendering: optimizeLegibility; + + /* Support for Firefox. */ + -moz-osx-font-smoothing: grayscale; + + /* Support for IE. */ + font-feature-settings: 'liga'; + + .md-18 { font-size: 18px; } + .md-24 { font-size: 24px; } + .md-36 { font-size: 36px; } + .md-48 { font-size: 48px; } + + .vertical-align-middle { + vertical-align: middle !important; + } +} + +.icon span, +.icon-image span { + position: absolute; + top: -9999px; +} + +[class^="icon-"]::before, +[class*=" icon-"]::before { + font-family: icomoon; + speak: none; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + line-height: 1; + + /* Enable Ligatures ================ */ + letter-spacing: 0; + -webkit-font-feature-settings: "liga"; + -moz-font-feature-settings: "liga=1"; + -moz-font-feature-settings: "liga"; + -ms-font-feature-settings: "liga" 1; + -o-font-feature-settings: "liga"; + font-feature-settings: "liga"; + + /* Better Font Rendering =========== */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.icon-flattr::before { + content: "\ead4"; +} + +.icon-mail::before { + content: "\ea86"; +} + +.icon-up-open::before { + content: "\e80b"; +} + +.icon-star::before { + content: "\e9d9"; +} + +.icon-check::before { + content: "\ea10"; +} + +.icon-link::before { + content: "\e9cb"; +} + +.icon-reply::before { + content: "\e806"; +} + +.icon-menu::before { + content: "\e9bd"; +} + +.icon-clock::before { + content: "\e803"; +} + +.icon-twitter::before { + content: "\ea96"; +} + +.icon-down-open::before { + content: "\e809"; +} + +.icon-trash::before { + content: "\e9ac"; +} + +.icon-delete::before { + content: "\ea0d"; +} + +.icon-power::before { + content: "\ea14"; +} + +.icon-arrow-up-thick::before { + content: "\ea3a"; +} + +.icon-rss::before { + content: "\e808"; +} + +.icon-print::before { + content: "\e954"; +} + +.icon-reload::before { + content: "\ea2e"; +} + +.icon-price-tags::before { + content: "\e936"; +} + +.icon-eye::before { + content: "\e9ce"; +} + +.icon-no-eye::before { + content: "\e9d1"; +} + +.icon-calendar::before { + content: "\e953"; +} + +.icon-time::before { + content: "\e952"; +} + +/* .icon-image class, for image-based icons + ========================================================================== */ + +.icon-image { + background: no-repeat center/80%; + padding-right: 1em !important; + padding-left: 1em !important; +} + +/* Carrot (http://carrot.org) */ +.icon-image--carrot { + background-image: url("../../_global/img/icons/carrot-icon--white.png"); +} + +/* Diaspora */ +.icon-image--diaspora { + background-image: url("../../_global/img/icons/Diaspora-asterisk.svg"); +} + +/* Unmark.it */ +.icon-image--unmark { + background-image: url("../../_global/img/icons/unmark-icon--black.png"); +} + +/* shaarli */ +.icon-image--shaarli { + background-image: url("../../_global/img/icons/shaarli.png"); +} + +/* ========================================================================== + Icon selected + ========================================================================== */ + +.icon-star.fav::before { + color: #fff; +} + +.icon-check.archive::before { + color: #fff; +} diff --git a/app/Resources/static/themes/baggy/css/print.css b/app/Resources/static/themes/baggy/css/print.scss similarity index 91% rename from app/Resources/static/themes/baggy/css/print.css rename to app/Resources/static/themes/baggy/css/print.scss index f7f6a8ad..a63f62e9 100755 --- a/app/Resources/static/themes/baggy/css/print.css +++ b/app/Resources/static/themes/baggy/css/print.scss @@ -17,7 +17,7 @@ /* ### Content ### */ /* Hide useless blocks */ - body > header, + body > .logo, #article_toolbar, #links, #sort, @@ -53,11 +53,8 @@ #main { width: 100%; - padding: 0; margin: 0; - margin-left: 0; - padding-right: 0; - padding-bottom: 0; + padding: 0; } #article { diff --git a/app/Resources/static/themes/baggy/css/ratatouille.css b/app/Resources/static/themes/baggy/css/ratatouille.scss similarity index 100% rename from app/Resources/static/themes/baggy/css/ratatouille.css rename to app/Resources/static/themes/baggy/css/ratatouille.scss diff --git a/app/Resources/static/themes/baggy/css/save.scss b/app/Resources/static/themes/baggy/css/save.scss new file mode 100644 index 00000000..ade77b40 --- /dev/null +++ b/app/Resources/static/themes/baggy/css/save.scss @@ -0,0 +1,115 @@ +/* ========================================================================== + "save a link" related styles + ========================================================================== */ + +.popup-form { + background: rgba(0, 0, 0, 0.5); + position: absolute; + top: 0; + left: 10em; + z-index: 20; + height: 100%; + width: 100%; + margin: 0; + margin-top: -30% !important; + padding: 2em; + display: none; + border-left: 1px #eee solid; + + form { + background-color: #fff; + position: absolute; + top: 0; + left: 0; + z-index: 20; + border: 10px solid #000; + width: 400px; + height: 200px; + padding: 2em; + } +} + +#bagit-form-form .addurl { + margin-left: 0; +} + +.closeMessage, +.close-button { + background-color: #000; + color: #fff; + font-size: 1.2em; + line-height: 1.6; + width: 1.6em; + height: 1.6em; + text-align: center; + text-decoration: none; + + &:hover, + &:focus { + background-color: #999; + color: #000; + } +} + +.close-button--popup { + display: inline-block; + position: absolute; + top: 0; + right: 0; + font-size: 1.4em; +} + +.active-current { + background-color: #999; + + &::after { + content: ""; + width: 0; + height: 0; + position: absolute; + border: 10px solid transparent; + border-right-color: #eee; + right: 0; + top: 50%; + margin-top: -10px; + } +} + +.opacity03 { + opacity: 0.3; +} + +.add-to-wallabag-link-after { + background-color: #000; + color: #fff; + padding: 0 3px 2px; +} + +a.add-to-wallabag-link-after { + visibility: hidden; + position: absolute; + opacity: 0; + transition-duration: 2s; + transition-timing-function: ease-out; +} + +#article article a:hover + a.add-to-wallabag-link-after, +a.add-to-wallabag-link-after:hover { + opacity: 1; + visibility: visible; + transition-duration: 0.3s; + transition-timing-function: ease-in; +} + +a.add-to-wallabag-link-after::after { + content: "w"; +} + +#add-link-result { + font-weight: bold; + font-size: 0.9em; +} + +.btn-clickable { + cursor: pointer; +} diff --git a/app/Resources/static/themes/baggy/font/icomoon.eot b/app/Resources/static/themes/baggy/font/icomoon.eot deleted file mode 100644 index 248758fb..00000000 Binary files a/app/Resources/static/themes/baggy/font/icomoon.eot and /dev/null differ diff --git a/app/Resources/static/themes/baggy/font/icomoon.svg b/app/Resources/static/themes/baggy/font/icomoon.svg deleted file mode 100644 index b1b9c92a..00000000 --- a/app/Resources/static/themes/baggy/font/icomoon.svg +++ /dev/null @@ -1,501 +0,0 @@ - - - -Generated by IcoMoon - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/Resources/static/themes/baggy/font/icomoon.ttf b/app/Resources/static/themes/baggy/font/icomoon.ttf deleted file mode 100644 index c03770c6..00000000 Binary files a/app/Resources/static/themes/baggy/font/icomoon.ttf and /dev/null differ diff --git a/app/Resources/static/themes/baggy/font/icomoon.woff b/app/Resources/static/themes/baggy/font/icomoon.woff deleted file mode 100644 index 121e0bdd..00000000 Binary files a/app/Resources/static/themes/baggy/font/icomoon.woff and /dev/null differ diff --git a/app/Resources/static/themes/baggy/js/init.js b/app/Resources/static/themes/baggy/index.js similarity index 68% rename from app/Resources/static/themes/baggy/js/init.js rename to app/Resources/static/themes/baggy/index.js index 05360a28..39ad49aa 100755 --- a/app/Resources/static/themes/baggy/js/init.js +++ b/app/Resources/static/themes/baggy/index.js @@ -1,31 +1,19 @@ -/* jQuery */ import $ from 'jquery'; -/* eslint-disable no-unused-vars */ -/* jquery has default scope */ -import cookie from 'jquery.cookie'; -import ui from 'jquery-ui-browserify'; -/* eslint-enable no-unused-vars */ - -/* Annotations */ -import annotator from 'annotator'; +/* Global imports */ +import '../_global/index'; /* Shortcuts */ -import './shortcuts/main'; -import './shortcuts/entry'; -import '../../_global/js/shortcuts/main'; -import '../../_global/js/shortcuts/entry'; +import './js/shortcuts/main'; +import './js/shortcuts/entry'; /* Tools */ -import { savePercent, retrievePercent } from '../../_global/js/tools'; -import toggleSaveLinkForm from './uiTools'; - -global.jquery = $; +import toggleSaveLinkForm from './js/uiTools'; -$.fn.ready(() => { - const $listmode = $('#listmode'); - const $listentries = $('#list-entries'); +/* Theme style */ +import './css/index.scss'; +$(document).ready(() => { /* ========================================================================== Menu ========================================================================== */ @@ -38,45 +26,12 @@ $.fn.ready(() => { } }); - /* ========================================================================== - List mode or Table Mode - ========================================================================== */ - - $listmode.click(() => { - if ($.cookie('listmode') === 1) { - // Cookie - $.removeCookie('listmode'); - - $listentries.removeClass('listmode'); - $listmode.removeClass('tablemode'); - $listmode.addClass('listmode'); - } else { - // Cookie - $.cookie('listmode', 1, { expires: 365 }); - - $listentries.addClass('listmode'); - $listmode.removeClass('listmode'); - $listmode.addClass('tablemode'); - } - }); - - /* ========================================================================== - Cookie listmode - ========================================================================== */ - - if ($.cookie('listmode') === 1) { - $listentries.addClass('listmode'); - $listmode.removeClass('listmode'); - $listmode.addClass('tablemode'); - } - /* ========================================================================== Add tag panel ========================================================================== */ - $('#nav-btn-add-tag').on('click', () => { - $('.nav-panel-add-tag').toggle(100); + $('.baggy-add-tag').toggle(100); $('.nav-panel-menu').addClass('hidden'); $('#tag_label').focus(); return false; @@ -95,39 +50,6 @@ $.fn.ready(() => { }); } - /* ========================================================================== - Annotations & Remember position - ========================================================================== */ - - if ($('article').length) { - const app = new annotator.App(); - - app.include(annotator.ui.main, { - element: document.querySelector('article'), - }); - - const x = JSON.parse($('#annotationroutes').html()); - app.include(annotator.storage.http, x); - - app.start().then(() => { - app.annotations.load({ entry: x.entryId }); - }); - - $(window).scroll(() => { - const scrollTop = $(window).scrollTop(); - const docHeight = $(document).height(); - const scrollPercent = (scrollTop) / (docHeight); - const scrollPercentRounded = Math.round(scrollPercent * 100) / 100; - savePercent(x.entryId, scrollPercentRounded); - }); - - retrievePercent(x.entryId); - - $(window).resize(() => { - retrievePercent(x.entryId); - }); - } - /** * Close window after adding entry if popup */ @@ -136,6 +58,23 @@ $.fn.ready(() => { window.close(); } + /** + if ($('article').size() > 0) { + const waypoint = new Waypoint({ + element: $('.wallabag-title').get(0), + handler: (direction) => { + console.log(direction); + if (direction === 'down') { + $('aside.tags').fadeIn('slow'); + } else { + $('aside.tags').fadeOut('slow'); + } + }, + offset: 250, + }); + } + */ + /** * Tags autocomplete */ @@ -283,25 +222,24 @@ $.fn.ready(() => { toggleBagit(); }); - const $bagitFormForm = $('#bagit-form-form'); + const bagitFormForm = $('#bagit-form-form'); /* ========================================================================== bag it link and close button ========================================================================== */ // send 'bag it link' form request via ajax - $bagitFormForm.submit((event) => { + bagitFormForm.submit((event) => { $('body').css('cursor', 'wait'); $('#add-link-result').empty(); $.ajax({ - type: $bagitFormForm.attr('method'), - url: $bagitFormForm.attr('action'), - data: $bagitFormForm.serialize(), + type: bagitFormForm.attr('method'), + url: bagitFormForm.attr('action'), + data: bagitFormForm.serialize(), success: function success() { $('#add-link-result').html('Done!'); - $('#plainurl').val(''); - $('#plainurl').blur(''); + $('#plainurl').val('').blur(''); $('body').css('cursor', 'auto'); }, error: function error() { @@ -318,8 +256,7 @@ $.fn.ready(() => { ========================================================================== */ $('article a[href^="http"]').after( - () => `' + () => ``, ); $('.add-to-wallabag-link-after').click((event) => { diff --git a/app/Resources/static/themes/baggy/js/shortcuts/main.js b/app/Resources/static/themes/baggy/js/shortcuts/main.js index aed09aee..43ebf3be 100644 --- a/app/Resources/static/themes/baggy/js/shortcuts/main.js +++ b/app/Resources/static/themes/baggy/js/shortcuts/main.js @@ -1,3 +1,6 @@ +import $ from 'jquery'; +import Mousetrap from 'mousetrap'; + $(document).ready(() => { Mousetrap.bind('s', () => { $('#search').trigger('click'); diff --git a/app/Resources/static/themes/material/css/article.scss b/app/Resources/static/themes/material/css/article.scss new file mode 100644 index 00000000..27ff5221 --- /dev/null +++ b/app/Resources/static/themes/material/css/article.scss @@ -0,0 +1,190 @@ +/* ========================================================================== + Article + ========================================================================== */ + +#article { + font-size: 20px; + margin: 0 auto; + max-width: 45em; + + article { + color: #424242; + font-size: 18px; + line-height: 1.7em; + + h1, + h2, + h3, + h4, + h5, + h6 { + color: #212121; + + strong { + font-weight: 500; + } + } + + h6 { + font-size: 1.2rem; + } + + h5 { + font-size: 1.6rem; + } + + h4 { + font-size: 1.9rem; + } + + h3 { + font-size: 2.2rem; + } + + h2 { + font-size: 2.5rem; + } + + h1 { + font-size: 2.7rem; + } + + a { + border-bottom: 1px dotted #03a9f4; + text-decoration: none; + } + + a:hover { + border-bottom-style: solid; + } + + ul { + padding-left: 30px; + } + + ul, + ul li { + list-style-type: disc; + } + + blockquote { + font-style: italic; + } + + strong { + font-weight: bold; + } + } + + img, + figure { + max-width: 100%; + height: auto; + } + + pre { + box-sizing: border-box; + margin: 0 0 1.75em; + border: #e3f2fd 1px solid; + width: 100%; + padding: 10px; + font-family: monospace; + font-size: 0.8em; + white-space: pre; + overflow: auto; + background: #f5f5f5; + border-radius: 3px; + } + + > header > h1 { + font-size: 2em; + margin: 2.1rem 0 0.68rem; + } + + aside { + .tools { + display: flex; + flex-flow: row wrap; + + .stats { + font-size: 0.8em; + margin: 8px 15px 5px; + + li { + display: inline-flex; + vertical-align: middle; + margin: 0 5px; + } + + a { + color: #000; + text-decoration: none; + } + } + + .tags { + float: right; + margin: 5px 15px 10px; + } + } + + .chip { + background-color: $blueAccentColor; + padding: 0 15px 0 10px; + margin: auto 2px; + border-radius: 6px; + + a, + i { + color: #fff; + } + + i.material-icons { + float: right; + font-size: 20px; + line-height: 32px; + padding-left: 8px; + } + } + } +} + +.reader-mode { + width: 70px !important; + transition: width 0.2s ease; + + .collapsible-body { + height: 0; + overflow: hidden; + } + + span { + opacity: 0; + transition: opacity 0.2s ease; + } + + &:hover { + width: 260px !important; + + .collapsible-body { + height: auto; + + li a i.material-icons { + margin: auto 5px auto -8px; + } + } + + span { + opacity: 1; + } + } +} + +.progress { + position: fixed; + top: 0; + width: 100%; + height: 3px; + margin: 0; + z-index: 9999; +} diff --git a/app/Resources/static/themes/material/css/cards.scss b/app/Resources/static/themes/material/css/cards.scss new file mode 100644 index 00000000..3edbe673 --- /dev/null +++ b/app/Resources/static/themes/material/css/cards.scss @@ -0,0 +1,245 @@ +/* ========================================================================== + Cards + ========================================================================== */ + +main { + #content { + padding: 0 0.5rem; + } + + ul.row { + padding: 0 0.75rem; + } +} + +.data .card .card-body { + height: 19em; + overflow: hidden; +} + +.card { + .card-content .card-title, + .card-reveal .card-title { + line-height: 22.8px; + max-height: 80px; + font-size: 19px; + font-family: roberto, "Helvetica Neue", Helvetica, Arial, sans-serif; + color: #313131; + } + + .card-stacked .card-content .card-title { + display: inline-block; + } + + .card-content .activator, + .card-reveal .activator { + cursor: pointer; + font-family: "Material Icons"; + } + + .card-content i.right, + .card-reveal i.right { + margin-left: 0; + } + + .card-content .original { + line-height: 24px; + font-size: 15px; + } + + .card-entry-labels { + position: absolute; + top: 10px; + z-index: 90; + max-width: 50%; + } + + .card-entry-labels-hidden { + margin: 2.5px auto; + } + + .card-entry-labels-hidden li { + display: inline-block; + background-color: $blueAccentColor; + margin: 0 5px; + padding: 5px 12px; + border-radius: 3px; + color: #fff; + max-height: 2em; + max-width: calc(100% - 15px); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .card-entry-labels-hidden li { + display: inline-block; + background-color: $blueAccentColor; + margin: 0 5px; + padding: 5px 12px; + border-radius: 3px; + color: #fff; + max-height: 2em; + max-width: calc(100% - 15px); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .card-content .estimatedTime { + margin-bottom: 10px; + } + + .card-action { + padding: 10px 5px 10px 15px; + + ul.links { + margin: 0; + font-size: 24px; + line-height: 24px; + } + + a { + color: #fff; + margin: 0; + } + + a:hover { + color: #fff; + } + + ul.tools li a.tool { + margin-right: 5px !important; + } + + .reading-time { + display: inline-flex; + vertical-align: middle; + + span { + margin-right: 5px; + } + } + } + + .card-image { + height: 10em; + } + + .card-fullimage { + height: 13.5em; + } + + .card-image .preview, + .card-fullimage .preview { + height: 14em; + background: no-repeat 50%/cover; + } + + &.sw { + max-width: 370px; + margin-left: auto; + margin-right: auto; + } +} + +a.original:not(.waves-effect) { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + display: block; +} + +.card-entry-labels li, +.card-tag-labels li { + margin: 10px 10px 10px auto; + padding: 5px 12px 5px 16px !important; + background-color: $blueAccentColor; + border-radius: 0 3px 3px 0; + color: #fff; + cursor: default; + max-height: 2em; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.card-entry-tags a, +.card-entry-labels a, +.card-tag-labels a, +.card-entry-labels-hidden a, +#list .chip a { + text-decoration: none; + font-weight: normal; + color: #fff; +} + +.card-stacked { + display: flex; + flex-flow: row wrap; + + &:hover ul.tools-list { + display: inline; + text-align: right; + } + + .preview { + max-width: 100px; + height: auto; + margin-right: 10px; + flex: 1; + + img { + max-width: 100%; + max-height: 100%; + } + } + + div.metadata { + .chip { + background-color: $blueAccentColor; + padding: 0 15px 0 10px; + margin: auto 2px; + border-radius: 6px; + + a, + i { + color: #fff; + } + + i.material-icons { + float: right; + font-size: 20px; + line-height: 32px; + padding-left: 8px; + } + } + } + + div.card-content { + flex: 4; + } + + ul.tools-list { + flex: 1; + display: none; + flex-basis: 5em; + align-self: flex-end; + float: right; + max-width: 6em; + } +} + +#content .collection .collection-item { + min-height: 65px; + height: auto; +} + +.quickstart .card .card-action a, +.quickstart .card .card-action a:hover { + color: #fff !important; +} + +.settings .div_tabs { + padding-bottom: 15px; +} diff --git a/app/Resources/static/themes/material/css/entries.scss b/app/Resources/static/themes/material/css/entries.scss new file mode 100644 index 00000000..c667be27 --- /dev/null +++ b/app/Resources/static/themes/material/css/entries.scss @@ -0,0 +1,96 @@ +/* ========================================================================== + * Entries + * ========================================================================== */ + +.collection { + margin: 15px 15px 0; + + .collection-item { + padding: 7px; + height: 65px; + } +} + +.results { + height: 1em; + + .nb-results, + .pagination { + margin: 15px 15px 0; + } + + .nb-results { + display: inline-flex; + } + + a { + color: #444; + } +} + +.pagination { + float: right; + + ul { + margin: 0 !important; + + .prev.disabled, + .next.disabled { + display: none; + } + } + + li { + padding: 0; + } + + a { + padding: 0 10px; + height: 30px; + display: block; + } + + .disabled { + margin-right: 10px; + margin-left: 10px; + } + + li.active span { + padding: 0 10px; + height: 30px; + display: block; + color: #fff; + } +} + +.page-footer .footer-copyright { + min-width: 50px; + height: auto !important; + line-height: 1em !important; + + p { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + display: block; + } +} + +.hidden { + display: none; +} + +.picker__date-display { + display: none; +} + +footer { + &.page-footer { + margin-top: 10px; + padding-top: 0; + } + + .row { + margin-bottom: 10px; + } +} diff --git a/app/Resources/static/themes/material/css/filters.scss b/app/Resources/static/themes/material/css/filters.scss new file mode 100644 index 00000000..299dad3d --- /dev/null +++ b/app/Resources/static/themes/material/css/filters.scss @@ -0,0 +1,15 @@ +/* ========================================================================== + * Filters slider + * ========================================================================== */ + +#filters { + button { + padding: 0; + width: 100%; + } + + div.with-checkbox { + height: 3rem; + margin-top: 0; + } +} diff --git a/app/Resources/static/themes/material/css/fonts.scss b/app/Resources/static/themes/material/css/fonts.scss new file mode 100644 index 00000000..743f1a84 --- /dev/null +++ b/app/Resources/static/themes/material/css/fonts.scss @@ -0,0 +1,13 @@ +/* ========================================================================== + * Fonts + * ========================================================================== */ + +/** + * Icomoon + */ +@font-face { + font-family: icomoon; + src: url("~icomoon-free-npm/Font/IcoMoon-Free.ttf"); + font-weight: normal; + font-style: normal; +} diff --git a/app/Resources/static/themes/material/css/icons.scss b/app/Resources/static/themes/material/css/icons.scss new file mode 100644 index 00000000..e7f215c0 --- /dev/null +++ b/app/Resources/static/themes/material/css/icons.scss @@ -0,0 +1,189 @@ +/* ========================================================================== + * Icons + * ========================================================================== */ + +/** + * + * Material icons + * + */ +.material-icons { + font-family: 'Material Icons'; + font-weight: normal; + font-style: normal; + font-size: 24px; /* Preferred icon size */ + width: 1em; + height: 1em; + display: inline-block; + line-height: 1; + text-transform: none; + letter-spacing: normal; + word-wrap: normal; + white-space: nowrap; + direction: ltr; + + /* Support for all WebKit browsers. */ + -webkit-font-smoothing: antialiased; + + /* Support for Safari and Chrome. */ + text-rendering: optimizeLegibility; + + /* Support for Firefox. */ + -moz-osx-font-smoothing: grayscale; + + /* Support for IE. */ + font-feature-settings: 'liga'; + + .md-18 { + font-size: 18px; + } + + .md-24 { + font-size: 24px; + } + + .md-36 { + font-size: 36px; + } + + .md-48 { + font-size: 48px; + } + + .md-dark { + color: rgba(0, 0, 0, 0.54); + + .md-inactive { + color: rgba(0, 0, 0, 0.26); + } + } + + .md-light { + color: rgba(255, 255, 255, 1); + + .md-inactive { + color: rgba(255, 255, 255, 0.3); + } + } +} + +/** + * + * Icomoon icons + * + */ +[class^="icon-"]::before, +[class*=" icon-"]::before { + font-family: icomoon; + speak: none; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + line-height: 1; + background-size: 24px; + + /* Enable Ligatures ================ */ + letter-spacing: 0; + font-feature-settings: "liga"; +} + +.icon-eye::before { + content: "\e9ce"; +} + +.icon-no-eye::before { + content: "\e9d1"; +} + +.icon-calendar::before { + content: "\e953"; +} + +.icon-mail::before { + content: "\ea86"; +} + +.icon-time::before { + content: "\e952"; +} + +a.icon-image { + background-repeat: no-repeat; + padding-right: 0.4em !important; + padding-left: 0 !important; + margin-left: 25px; + + &::before { + content: ""; + display: block; + width: 24px; + height: 24px; + float: left; + margin: 7px 1.5px 0 0; + } + + &.carrot::before { + background: url("../../_global/img/icons/carrot-icon--black.png") no-repeat center/90%; + } + + &.diaspora::before { + background: url("../../_global/img/icons/diaspora-icon--black.png") no-repeat center/80%; + } + + &.unmark::before { + background: url("../../_global/img/icons/unmark-icon--black.png") no-repeat center/80%; + } + + &.shaarli::before { + background: url("../../_global/img/icons/shaarli.png") no-repeat center/80%; + } + + &.scuttle::before { + background: url("../../_global/img/icons/scuttle.png") no-repeat center/80%; + } +} + +.icon-google-plus2::before { + content: "\ea89"; +} + +.icon-facebook2::before { + content: "\ea8d"; +} + +.icon-twitter::before { + content: "\ea96"; +} + +.icon-apple::before { + content: "\eabf"; +} + +.icon-android::before { + content: "\eac1"; +} + +.icon-chrome::before { + content: "\eae5"; +} + +.icon-firefox::before { + content: "\eae6"; +} + +.icon-link::before { + content: "\e9cb"; +} + +footer [class^="icon-"], +footer [class*=" icon-"] { + font-size: 2em; + transition: text-shadow 0.2s ease; + padding-right: 10px; +} + +footer [class^="icon-"]:hover, +footer [class*=" icon-"]:hover { + text-shadow: 0 0 10px rgba(0, 0, 0, 0.3); +} diff --git a/app/Resources/static/themes/material/css/index.scss b/app/Resources/static/themes/material/css/index.scss new file mode 100644 index 00000000..285a6504 --- /dev/null +++ b/app/Resources/static/themes/material/css/index.scss @@ -0,0 +1,17 @@ +@import 'variables'; + +/* Style */ +@import 'article'; +@import 'cards'; +@import 'entries'; +@import 'filters'; +@import 'layout'; +@import 'nav'; +@import 'sidenav'; +@import 'various'; + +/* Tools */ +@import 'fonts'; +@import 'icons'; +@import 'print'; +@import 'media_queries'; diff --git a/app/Resources/static/themes/material/css/layout.scss b/app/Resources/static/themes/material/css/layout.scss new file mode 100755 index 00000000..cfdbf2b3 --- /dev/null +++ b/app/Resources/static/themes/material/css/layout.scss @@ -0,0 +1,44 @@ +/* ========================================================================== + Layout + ========================================================================== */ + +body { + display: flex; + min-height: 100vh; + flex-direction: column; + background: #fafafa; + + &.login main { + padding: 0; + min-height: 100vh; + } +} + +.border-bottom { + border-bottom: 1px solid #ddd; +} + +main, +#content, +.valign-wrapper { + height: 100%; +} + +#main { + flex: 1 0 auto; + + .logo { + a { + height: 100pt; + } + + img { + height: 100pt; + width: 100pt; + } + + &:hover { + background: transparent; + } + } +} diff --git a/app/Resources/static/themes/material/css/main.css b/app/Resources/static/themes/material/css/main.css deleted file mode 100755 index ee4ad4e0..00000000 --- a/app/Resources/static/themes/material/css/main.css +++ /dev/null @@ -1,1019 +0,0 @@ -/* ========================================================================== - Sommaire - - 0 = Common - 1 = Nav - 2 = Side-nav - 3 = Filters slider - 4 = Cards - 5 = Article - 6 = Media queries - 7 = Font - 8 = Others - - ========================================================================== */ - -/* ========================================================================== - 0 = Common - ========================================================================== */ - -/** - * - * Material icons - * - */ - -@font-face { - font-family: 'Material Icons'; - font-style: normal; - font-weight: 400; - src: url(../fonts/MaterialIcons-Regular.eot); - - /* For IE6-8 */ - src: local("Material Icons"), local("MaterialIcons-Regular"), url(../fonts/MaterialIcons-Regular.woff2) format("woff2"), url(../fonts/MaterialIcons-Regular.woff) format("woff"), url(../fonts/MaterialIcons-Regular.ttf) format("truetype"); -} - -.material-icons { - font-family: 'Material Icons'; - font-weight: normal; - font-style: normal; - font-size: 24px; /* Preferred icon size */ - width: 1em; - height: 1em; - display: inline-block; - line-height: 1; - text-transform: none; - letter-spacing: normal; - word-wrap: normal; - white-space: nowrap; - direction: ltr; - - /* Support for all WebKit browsers. */ - -webkit-font-smoothing: antialiased; - - /* Support for Safari and Chrome. */ - text-rendering: optimizeLegibility; - - /* Support for Firefox. */ - -moz-osx-font-smoothing: grayscale; - - /* Support for IE. */ - font-feature-settings: 'liga'; -} - -/* Rules for sizing the icon. */ -.material-icons.md-18 { font-size: 18px; } -.material-icons.md-24 { font-size: 24px; } -.material-icons.md-36 { font-size: 36px; } -.material-icons.md-48 { font-size: 48px; } - -/* Rules for using icons as black on a light background. */ -.material-icons.md-dark { color: rgba(0, 0, 0, 0.54); } -.material-icons.md-dark.md-inactive { color: rgba(0, 0, 0, 0.26); } - -/* Rules for using icons as white on a dark background. */ -.material-icons.md-light { color: rgba(255, 255, 255, 1); } -.material-icons.md-light.md-inactive { color: rgba(255, 255, 255, 0.3); } - -/** - * - * Icomoon icons - * - */ - -@font-face { - font-family: icomoon; - src: url("../fonts/IcoMoon-Free.ttf"); - font-weight: normal; - font-style: normal; -} - -[class^="icon-"]::before, -[class*=" icon-"]::before { - font-family: icomoon; - speak: none; - font-style: normal; - font-weight: normal; - font-variant: normal; - text-transform: none; - line-height: 1; - background-size: 24px; - - /* Enable Ligatures ================ */ - letter-spacing: 0; - -webkit-font-feature-settings: "liga"; - -moz-font-feature-settings: "liga=1"; - -moz-font-feature-settings: "liga"; - -ms-font-feature-settings: "liga" 1; - -o-font-feature-settings: "liga"; - font-feature-settings: "liga"; - - /* Better Font Rendering =========== */ - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -.icon-image { - background-size: 16px; - background-repeat: no-repeat; - padding-right: 1em !important; - padding-left: 1em !important; -} - -.icon-eye::before { - content: "\e9ce"; -} - -.icon-no-eye::before { - content: "\e9d1"; -} - -.icon-calendar::before { - content: "\e953"; -} - -.icon-mail::before { - content: "\ea86"; -} - -.icon-time::before { - content: "\e952"; -} - -/* Carrot (http://carrot.org) */ -.icon-image--carrot { - background-image: url("../../_global/img/icons/carrot-icon--black.png"); -} - -/* Diaspora */ -.icon-image--diaspora { - background-image: url("../../_global/img/icons/diaspora-icon--black.png"); -} - -/* Unmark.it */ -.icon-image--unmark { - background-image: url("../../_global/img/icons/unmark-icon--black.png"); -} - -/* Shaarli */ -.icon-image--shaarli { - background-image: url("../../_global/img/icons/shaarli.png"); -} - -body { - display: flex; - min-height: 100vh; - flex-direction: column; - background: #fafafa; -} - -body.login main { - padding: 0; - min-height: 100vh; -} - -.border-bottom { - border-bottom: 1px solid #ddd; -} - -nav, -main, -footer { - padding-left: 240px; -} - -main, -#content, -.valign-wrapper { - height: 100%; -} - -#main { - flex: 1 0 auto; -} - -.results { - height: 1em; -} - -.results .nb-results, -.results .pagination { - margin: 15px; - margin-bottom: 0; -} - -.results .nb-results { - display: inline-flex; -} - -.results a { - color: #444; -} - -.pagination { - float: right; -} - -.pagination ul { - margin: 0 !important; -} - -.pagination li { - padding: 0; -} - -.pagination a { - padding: 0 10px; - height: 30px; - display: block; -} - -.pagination .disabled { - margin-right: 10px; - margin-left: 10px; -} - -div.pagination ul .prev.disabled, -div.pagination ul .next.disabled { - display: none; -} - -.pagination li.active span { - padding: 0 10px; - height: 30px; - display: block; - color: #fff; -} - -.page-footer .footer-copyright { - min-width: 50px; - height: auto !important; - line-height: 1em !important; -} - -.page-footer .footer-copyright p { - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - display: block; -} - -.hidden { - display: none; -} - -.picker__date-display { - display: none; -} - -footer.page-footer { - margin-top: 10px; - padding-top: 0; -} - -footer .row { - margin-bottom: 10px; -} - -/* ========================================================================== - 1 = Nav - ========================================================================== */ - -nav input { - color: #aaa; -} - -nav { - height: auto; -} - -.nav-wrapper { - display: flex; - flex-wrap: wrap; - justify-content: space-between; - min-height: 64px; -} - -.nav-wrapper .button-collapse { - padding: 0 15px; -} - -.nav-input { - display: none; -} - -.nav-panel-buttom { - display: flex; - flex-grow: 1; - justify-content: flex-end; -} - -.nav-panel-buttom li { - max-height: 64px; -} - -.nav-panels { - transition: background 0.2s ease; -} - -.nav-panel-add .add, -.nav-panel-search .search, -.nav-panels .close { - color: #444 !important; -} - -.nav-panels .action { - padding-left: 0.75rem; - font-size: 2.1rem; - white-space: nowrap; -} - -.nav-panels .input-field input { - display: block; - line-height: inherit; - padding-left: 4rem !important; - width: calc(100% - 8rem); -} - -.nav-panels .input-field input:focus { - background-color: #fff; - border: 0; - box-shadow: none; - color: #444; -} - -.input-field.nav-panel-add label, -.input-field.nav-panel-search label { - left: 1rem; -} - -.input-field.nav-panel-add .close, -.input-field.nav-panel-search .close { - position: absolute; - top: 0; - right: 1rem; - color: transparent; - cursor: pointer; - font-size: 2rem; - transition: 0.3s color; -} - -#button_filters { - display: none; -} - -#button_export { - display: none; -} - -.input-field.nav-panel-add, -.input-field.nav-panel-add form, -.input-field.nav-panel-search, -.input-field.nav-panel-search form { - display: flex; - flex: 1; -} - -/* ========================================================================== - 2 = Side-nav - ========================================================================== */ - -.side-nav.fixed a { - font-size: 13px; - line-height: 44px; - height: 44px; -} - -.side-nav .collapsible-header, -.side-nav.fixed .collapsible-header { - height: 45px; - line-height: 44px; - padding: 0 20px; -} - -.bold > a { - font-weight: bold; -} - -.side-nav > li.logo { - line-height: 0; - text-align: center; -} - -#main .logo a { - height: 100pt; -} - -#main .logo img { - height: 100pt; - width: 100pt; -} - -#main .logo:hover { - background: transparent; -} - -.side-nav li { - padding: 0; -} - -.side-nav a { - margin: 0 1rem; -} - -span.numberItems { - float: right; -} - -nav ul a:hover { - background-color: initial; -} - -/* ========================================================================== - * 3 = Filters slider - * ========================================================================== */ - -#filters button { - padding: 0; - width: 100%; -} - -.side-nav.fixed.right-aligned { - right: -250px; - left: auto !important; -} - -#filters div.with-checkbox { - height: 3rem; - margin-top: 0; -} - -/* ========================================================================== - 4 = Cards - ========================================================================== */ - -main #content { - padding: 0 0.5rem; -} - -main ul.row { - padding: 0 0.75rem; -} - -.data .card .card-body { - height: 19em; - overflow: hidden; -} - -.card .card-content .card-title, -.card .card-reveal .card-title { - line-height: 22.8px; - max-height: 80px; - font-size: 19px; - font-family: roberto, "Helvetica Neue", Helvetica, Arial, sans-serif; - color: #313131; -} - -.card .card-content .activator, -.card .card-reveal .activator { - cursor: pointer; - font-family: "Material Icons"; -} - -.card .card-content i.right, -.card .card-reveal i.right { - margin-left: 0; -} - -.card .card-content .original { - line-height: 24px; - font-size: 15px; -} - -a.original { - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - display: block; -} - -.card .card-entry-labels { - position: absolute; - top: 10px; - z-index: 90; - max-width: 50%; -} - -.card .card-entry-labels li, -.card-tag-labels li { - margin: 10px 10px 10px auto; - padding: 5px 12px 5px 16px !important; - background-color: rgba(0, 151, 167, 0.85); - border-radius: 0 3px 3px 0; - color: #fff; - cursor: default; - max-height: 2em; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.card .card-entry-labels-hidden { - margin: 2.5px auto; -} - -.card .card-entry-labels-hidden li { - display: inline-block; - background-color: rgba(0, 151, 167, 0.85); - margin: 0 5px; - padding: 5px 12px; - border-radius: 3px; - color: #fff; - max-height: 2em; - max-width: calc(100% - 15px); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.card .card-entry-labels-hidden li:first-child { - margin-left: 0; -} - -.card-entry-tags a, -.card-entry-labels a, -.card-tag-labels a, -.card-entry-labels-hidden a, -#list .chip a { - text-decoration: none; - font-weight: normal; - color: #fff; -} - -.card .card-content .estimatedTime { - margin-bottom: 10px; -} - -.card .card-action { - padding: 10px 5px 10px 15px; -} - -.card .card-action ul.links { - margin: 0; - font-size: 24px; - line-height: 24px; -} - -.card .card-action ul.tools li a.tool { - margin-right: 5px !important; -} - -.card-stacked:hover ul.tools-list { - display: block; -} - -.card-stacked ul.tools-list { - display: none; -} - -.card .card-action a { - color: #fff; - margin: 0; -} - -.card .card-action a:hover { - color: #fff; -} - -.card .card-action .reading-time { - display: inline-flex; - vertical-align: middle; -} - -.quickstart .card .card-action a, -.quickstart .card .card-action a:hover { - color: #fff !important; -} - -.settings .div_tabs { - padding-bottom: 15px; -} - -.card.sw { - max-width: 370px; - margin-left: auto; - margin-right: auto; -} - -.card .card-image { - height: 10em; -} - -.card .card-fullimage { - height: 13.5em; -} - -.card .card-image .preview, -.card .card-fullimage .preview { - height: 14em; - background-size: cover; - background-repeat: no-repeat; - background-position: 50%; -} - -/* ========================================================================== - 5 = Article - ========================================================================== */ - -#article { - font-size: 20px; - margin: 0 auto; - max-width: 45em; -} - -#article article { - color: #424242; - font-size: 18px; - line-height: 1.7em; -} - -#article article h1, -#article article h2, -#article article h3, -#article article h4, -#article article h5, -#article article h6 { - color: #212121; -} - -#article article h1 strong, -#article article h2 strong, -#article article h3 strong, -#article article h4 strong, -#article article h5 strong, -#article article h6 strong { - font-weight: 500; -} - -#article article h6 { - font-size: 1.2rem; -} - -#article article h5 { - font-size: 1.6rem; -} - -#article article h4 { - font-size: 1.9rem; -} - -#article article h3 { - font-size: 2.2rem; -} - -#article article h2 { - font-size: 2.5rem; -} - -#article article h1 { - font-size: 2.7rem; -} - -#article img, -#article figure { - max-width: 100%; - height: auto; -} - -#article article a { - border-bottom: 1px dotted #03a9f4; - text-decoration: none; -} - -#article article a:hover { - border-bottom-style: solid; -} - -#article article ul { - padding-left: 30px; -} - -#article article ul, -#article article ul li { - list-style-type: disc; -} - -#article article blockquote { - font-style: italic; -} - -#article article strong { - font-weight: bold; -} - -#article article pre { - box-sizing: border-box; - margin: 0 0 1.75em; - border: #e3f2fd 1px solid; - width: 100%; - padding: 10px; - font-family: monospace; - font-size: 0.8em; - white-space: pre; - overflow: auto; - background: #f5f5f5; - border-radius: 3px; -} - -#article > header > h1 { - font-size: 2em; - margin: 2.1rem 0 0.68rem; -} - -.reader-mode { - width: 95px !important; - transition: width 0.2s ease; -} - -.reader-mode:hover { - width: 240px !important; -} - -.reader-mode .collapsible-body { - height: 0; - overflow: hidden; -} - -.reader-mode:hover .collapsible-body { - height: auto; -} - -.reader-mode span { - opacity: 0; - transition: opacity 0.2s ease; -} - -.reader-mode:hover span { - opacity: 1; -} - -.progress { - position: fixed; - top: 0; - width: 100%; - height: 3px; - margin: 0; - z-index: 9999; -} - -#article aside .tools { - font-size: 0.8em; - display: flex; - flex-flow: row wrap; - margin: 0 auto; -} - -article aside .tools li { - display: inline-flex; - vertical-align: middle; -} - -#article aside .tools a { - color: #000; - text-decoration: none; -} - -#article aside #list { - float: right; - margin: 0 15px 10px; -} - -#article aside .chip { - background-color: rgba(0, 151, 167, 0.85); - padding: 0 15px 0 10px; - margin: auto 2px; -} - -#article aside .chip a, -#article aside .chip i { - color: #fff; -} - -/* ========================================================================== - 6 = Media queries - ========================================================================== */ - -@media only screen and (max-width: 992px) { - header, - main, - footer { - padding-left: 0; - } - - nav, - main, - footer { - padding-left: 0; - } - - .pagination { - width: auto; - } - - .nav-panels .action { - padding-right: 0.75rem; - } - - .nav-panel-buttom { - justify-content: space-around; - } - - #article { - max-width: 35em; - margin-left: auto; - margin-right: auto; - font-size: 18px; - } - - #article > header > h1 { - font-size: 1.33em; - } - - .reader-mode { - width: 240px !important; - } - - .reader-mode span { - opacity: 1; - } - - .tabs { - display: inline-block; - height: auto; - } - - .tab { - min-width: 100%; - } - - .indicator { - display: none; - } - - .pagination li.prev, - .pagination li.next { - width: auto; - } - - .drag-target + .drag-target { - height: 50%; - } - - .drag-target + .drag-target + .drag-target { - top: 50%; - } -} - -@media only screen and (min-width: 1200px) and (max-width: 1650px) { - .row .col.l3 { - width: 33.33333%; - margin-left: 0; - } -} - -@media only screen and (min-width: 993px) and (max-width: 1200px) { - .row .col.l1 { - width: 25%; - margin-left: 0; - } - - .row .col.l2 { - width: 33.33333%; - margin-left: 0; - } - - .row .col.l3 { - width: 41.66667%; - margin-left: 0; - } - - .row .col.l4 { - width: 50%; - margin-left: 0; - } - - .row .col.l5 { - width: 58.33333%; - margin-left: 0; - } - - .row .col.l6 { - width: 66.66667%; - margin-left: 0; - } - - .row .col.l7 { - width: 75%; - margin-left: 0; - } - - .row .col.l8 { - width: 83.33333%; - margin-left: 0; - } - - .row .col.l9 { - width: 91.66667%; - margin-left: 0; - } - - .row .col.l10 { - width: 100%; - margin-left: 0; - } -} - -@media only screen and (max-width: 350px) { - .nb-results { - display: none; - } - - main ul.row { - padding: 0; - } - - .row .col { - padding: 0; - } -} - -/* ========================================================================== - 7 = Font - ========================================================================== */ - -.icon-google-plus2::before { - content: "\ea89"; -} - -.icon-facebook2::before { - content: "\ea8d"; -} - -.icon-twitter::before { - content: "\ea96"; -} - -.icon-apple::before { - content: "\eabf"; -} - -.icon-android::before { - content: "\eac1"; -} - -.icon-chrome::before { - content: "\eae5"; -} - -.icon-firefox::before { - content: "\eae6"; -} - -.icon-link::before { - content: "\e9cb"; -} - -footer [class^="icon-"], -footer [class*=" icon-"] { - font-size: 2em; - transition: text-shadow 0.2s ease; - padding-right: 10px; -} - -footer [class^="icon-"]:hover, -footer [class*=" icon-"]:hover { - text-shadow: 0 0 10px rgba(0, 0, 0, 0.3); -} - -/* ========================================================================== - 8 = Others - ========================================================================== */ - -/* force height on non-input field in the settings page */ -div.settings div.input-field div, -div.settings div.input-field ul { - margin-top: 40px; -} - -/* but avoid to kill all file input */ -div.settings div.file-field div { - margin-top: inherit; -} - -.input-field label.active { - font-size: 1rem; -} - -nav .input-field input { - margin: 0; -} diff --git a/app/Resources/static/themes/material/css/media_queries.scss b/app/Resources/static/themes/material/css/media_queries.scss new file mode 100644 index 00000000..08c2d8ab --- /dev/null +++ b/app/Resources/static/themes/material/css/media_queries.scss @@ -0,0 +1,152 @@ +/* ========================================================================== + Media queries + ========================================================================== */ + +@media only screen and (min-width: 992px) { + nav, + body:not(.entry) main, + footer { + padding-left: 240px; + } +} + +@media only screen and (max-width: 992px) { + header, + main, + footer, + nav { + padding-left: 0; + } + + .pagination { + width: auto; + } + + .nav-panels .action { + padding-right: 0.75rem; + } + + .nav-panel-buttom { + justify-content: space-around; + } + + #article { + max-width: 35em; + margin-left: auto; + margin-right: auto; + font-size: 18px; + + > header > h1 { + font-size: 1.33em; + } + } + + .reader-mode { + width: 240px !important; + + span { + opacity: 1; + } + } + + .tabs { + display: inline-block; + height: auto; + } + + .tab { + min-width: 100%; + } + + .indicator { + display: none; + } + + .pagination li.prev, + .pagination li.next { + width: auto; + } + + .drag-target + .drag-target { + height: 50%; + } + + .drag-target + .drag-target + .drag-target { + top: 50%; + } +} + +@media only screen and (min-width: 1200px) and (max-width: 1650px) { + .row .col.l3 { + width: 33.33333%; + margin-left: 0; + } +} + +@media only screen and (min-width: 993px) and (max-width: 1200px) { + .row { + .col.l1 { + width: 25%; + margin-left: 0; + } + + .col.l2 { + width: 33.33333%; + margin-left: 0; + } + + .col.l3 { + width: 41.66667%; + margin-left: 0; + } + + .col.l4 { + width: 50%; + margin-left: 0; + } + + .col.l5 { + width: 58.33333%; + margin-left: 0; + } + + .col.l6 { + width: 66.66667%; + margin-left: 0; + } + + .col.l7 { + width: 75%; + margin-left: 0; + } + + .col.l8 { + width: 83.33333%; + margin-left: 0; + } + + .col.l9 { + width: 91.66667%; + margin-left: 0; + } + + .col.l10 { + width: 100%; + margin-left: 0; + } + } +} + +@media only screen and (max-width: 350px) { + .nb-results { + display: none; + } + + main ul.row { + padding: 0; + } + + .row .col { + padding: 0; + } +} diff --git a/app/Resources/static/themes/material/css/nav.scss b/app/Resources/static/themes/material/css/nav.scss new file mode 100644 index 00000000..1a25a5be --- /dev/null +++ b/app/Resources/static/themes/material/css/nav.scss @@ -0,0 +1,106 @@ + +/* ========================================================================== + Nav + ========================================================================== */ + +nav { + height: auto; + + input { + color: #aaa; + } + + ul a:hover { + background-color: initial; + } +} + +.nav-wrapper { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + min-height: 64px; + + .button-collapse { + padding: 0 15px; + } +} + +.nav-input { + display: none; +} + +.nav-panel-buttom { + display: flex; + flex-grow: 1; + justify-content: flex-end; + + li { + max-height: 64px; + } +} + +.nav-panel-add .add, +.nav-panel-search .search, +.nav-panels .close { + color: #444 !important; +} + +.nav-panels { + transition: background 0.2s ease; + + .action { + padding-left: 0.75rem; + font-size: 2.1rem; + white-space: nowrap; + } + + .input-field input { + display: block; + line-height: inherit; + padding-left: 4rem !important; + width: calc(100% - 8rem); + height: 4.1rem; + } + + .input-field input:focus { + background-color: #fff; + border: 0; + box-shadow: none; + color: #444; + } +} + +.input-field { + &.nav-panel-add label, + &.nav-panel-search label { + left: 1rem; + } + + &.nav-panel-add .close, + &.nav-panel-search .close { + position: absolute; + top: 0; + right: 1rem; + color: transparent; + cursor: pointer; + font-size: 2rem; + transition: 0.3s color; + } + + &.nav-panel-add, + &.nav-panel-add form, + &.nav-panel-search, + &.nav-panel-search form { + display: flex; + flex: 1; + } +} + +#button_filters { + display: none; +} + +#button_export { + display: none; +} diff --git a/app/Resources/static/themes/material/css/print.css b/app/Resources/static/themes/material/css/print.scss similarity index 94% rename from app/Resources/static/themes/material/css/print.css rename to app/Resources/static/themes/material/css/print.scss index 98505aae..967a8c2b 100755 --- a/app/Resources/static/themes/material/css/print.css +++ b/app/Resources/static/themes/material/css/print.scss @@ -64,11 +64,8 @@ #main { width: 100%; - padding: 0; margin: 0; - margin-left: 0; - padding-right: 0; - padding-bottom: 0; + padding: 0; } #article { diff --git a/app/Resources/static/themes/material/css/sidenav.scss b/app/Resources/static/themes/material/css/sidenav.scss new file mode 100644 index 00000000..416dc1c7 --- /dev/null +++ b/app/Resources/static/themes/material/css/sidenav.scss @@ -0,0 +1,45 @@ +/* ========================================================================== + Side-nav + ========================================================================== */ + +.side-nav { + width: 240px; + + li { + padding: 0; + + &.logo > a:hover { + background: initial; + } + } + + a { + margin: 0; + } + + &.fixed a { + font-size: 13px; + line-height: 44px; + height: 44px; + } + + .collapsible-header, + &.fixed .collapsible-header { + height: 45px; + line-height: 44px; + padding: 0 20px; + } + + > li.logo { + line-height: 0; + text-align: center; + } +} + +.bold > a { + font-weight: bold; +} + +span.numberItems { + float: right; +} diff --git a/app/Resources/static/themes/material/css/variables.scss b/app/Resources/static/themes/material/css/variables.scss new file mode 100644 index 00000000..25e22aca --- /dev/null +++ b/app/Resources/static/themes/material/css/variables.scss @@ -0,0 +1,5 @@ +/* ========================================================================== + Variables + ========================================================================== */ + +$blueAccentColor: rgba(0, 151, 167, 0.85); diff --git a/app/Resources/static/themes/material/css/various.scss b/app/Resources/static/themes/material/css/various.scss new file mode 100644 index 00000000..7daf40ec --- /dev/null +++ b/app/Resources/static/themes/material/css/various.scss @@ -0,0 +1,32 @@ +/* ========================================================================== + * Various + * ========================================================================== */ + +div.settings div.file-field { + /* force height on non-input field in the settings page */ + div, + ul { + margin-top: 40px; + } + + /* but avoid to kill all file input */ + div { + margin-top: inherit; + } +} + +.input-field label.active { + font-size: 1rem; +} + +nav .input-field input { + margin: 0; +} + +.tabs { + display: flex; +} + +.tab { + flex: 1; +} diff --git a/app/Resources/static/themes/material/js/init.js b/app/Resources/static/themes/material/index.js similarity index 57% rename from app/Resources/static/themes/material/js/init.js rename to app/Resources/static/themes/material/index.js index 0b2832c0..d6afbb8a 100755 --- a/app/Resources/static/themes/material/js/init.js +++ b/app/Resources/static/themes/material/index.js @@ -1,21 +1,21 @@ -/* jQuery */ import $ from 'jquery'; -/* Annotations */ -import annotator from 'annotator'; +/* Materialize imports */ +import 'materialize-css/dist/css/materialize.css'; +import 'materialize-css/dist/js/materialize'; + +/* Global imports */ +import '../_global/index'; /* Tools */ -import { savePercent, retrievePercent, initFilters, initExport } from '../../_global/js/tools'; +import { initExport, initFilters } from './js/tools'; /* Import shortcuts */ -import './shortcuts/main'; -import './shortcuts/entry'; -import '../../_global/js/shortcuts/main'; -import '../../_global/js/shortcuts/entry'; - -require('materialize'); // eslint-disable-line +import './js/shortcuts/main'; +import './js/shortcuts/entry'; -global.jQuery = $; +/* Theme style */ +import './css/index.scss'; $(document).ready(() => { // sideNav @@ -30,6 +30,7 @@ $(document).ready(() => { formatSubmit: 'dd/mm/yyyy', hiddenName: true, format: 'dd/mm/yyyy', + container: 'body', }); initFilters(); initExport(); @@ -74,37 +75,4 @@ $(document).ready(() => { const scrollPercent = (s / (d - c)) * 100; $('.progress .determinate').css('width', `${scrollPercent}%`); }); - -/* ========================================================================== - Annotations & Remember position - ========================================================================== */ - - if ($('article').length) { - const app = new annotator.App(); - const x = JSON.parse($('#annotationroutes').html()); - - app.include(annotator.ui.main, { - element: document.querySelector('article'), - }); - - app.include(annotator.storage.http, x); - - app.start().then(() => { - app.annotations.load({ entry: x.entryId }); - }); - - $(window).scroll(() => { - const scrollTop = $(window).scrollTop(); - const docHeight = $(document).height(); - const scrollPercent = (scrollTop) / (docHeight); - const scrollPercentRounded = Math.round(scrollPercent * 100) / 100; - savePercent(x.entryId, scrollPercentRounded); - }); - - retrievePercent(x.entryId); - - $(window).resize(() => { - retrievePercent(x.entryId); - }); - } }); diff --git a/app/Resources/static/themes/material/js/shortcuts/main.js b/app/Resources/static/themes/material/js/shortcuts/main.js index 0a2d2a69..042b423c 100644 --- a/app/Resources/static/themes/material/js/shortcuts/main.js +++ b/app/Resources/static/themes/material/js/shortcuts/main.js @@ -23,6 +23,16 @@ $(document).ready(() => { return; } + /* Show nothing on login/register page */ + if ($('#username').length > 0 || $('#fos_user_registration_form_username').length > 0) { + return; + } + + /* Show nothing on login/register page */ + if ($('#username').length > 0 || $('#fos_user_registration_form_username').length > 0) { + return; + } + /* Focus current card */ toggleFocus(card); diff --git a/app/Resources/static/themes/material/js/tools.js b/app/Resources/static/themes/material/js/tools.js new file mode 100644 index 00000000..39398fd8 --- /dev/null +++ b/app/Resources/static/themes/material/js/tools.js @@ -0,0 +1,24 @@ +import $ from 'jquery'; + +function initFilters() { + // no display if filters not available + if ($('div').is('#filters')) { + $('#button_filters').show(); + $('.js-filters-action').sideNav({ edge: 'right' }); + $('#clear_form_filters').on('click', () => { + $('#filters input').val(''); + $('#filters :checked').removeAttr('checked'); + return false; + }); + } +} + +function initExport() { + // no display if export not available + if ($('div').is('#export')) { + $('#button_export').show(); + $('.js-export-action').sideNav({ edge: 'right' }); + } +} + +export { initExport, initFilters }; diff --git a/app/config/config.yml b/app/config/config.yml index 77cda052..2bc5e3b3 100644 --- a/app/config/config.yml +++ b/app/config/config.yml @@ -2,6 +2,12 @@ imports: - { resource: parameters.yml } - { resource: security.yml } - { resource: services.yml } + - { resource: wallabag.yml } + +parameters: + # Allows to use the live reload feature for changes in assets + use_webpack_dev_server: false + craue_config.cache_adapter.class: Craue\ConfigBundle\CacheAdapter\SymfonyCacheComponentAdapter framework: #esi: ~ @@ -29,40 +35,6 @@ framework: http_method_override: true assets: ~ -wallabag_core: - version: 2.2.3 - paypal_url: "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9UBA65LG3FX9Y&lc=gb" - languages: - en: 'English' - fr: 'Français' - de: 'Deutsch' - tr: 'Türkçe' - fa: 'فارسی' - ro: 'Română' - pl: 'Polish' - da: 'Dansk' - es: 'Español' - oc: 'Occitan' - it: 'Italiano' - pt: 'Português' - items_on_page: 12 - theme: material - language: '%locale%' - rss_limit: 50 - reading_speed: 1 - cache_lifetime: 10 - action_mark_as_read: 1 - list_mode: 1 - fetching_error_message: | - wallabag can't retrieve contents for this article. Please troubleshoot this issue. - -wallabag_user: - registration_enabled: "%fosuser_registration%" - -wallabag_import: - allow_mimetypes: ['application/octet-stream', 'application/json', 'text/plain', 'text/csv'] - resource_dir: "%kernel.root_dir%/../web/uploads/import" - # Twig Configuration twig: debug: "%kernel.debug%" diff --git a/app/config/config_prod.yml b/app/config/config_prod.yml index 5a4dd69e..65b02d66 100644 --- a/app/config/config_prod.yml +++ b/app/config/config_prod.yml @@ -1,9 +1,9 @@ imports: - { resource: config.yml } -#framework: -# cache: -# system: cache.adapter.apcu +framework: + assets: + # json_manifest_path: '%kernel.root_dir%/../web/bundles/wallabagcore/manifest.json' #doctrine: # orm: diff --git a/app/config/config_test.yml b/app/config/config_test.yml index f5e2c25e..c620c359 100644 --- a/app/config/config_test.yml +++ b/app/config/config_test.yml @@ -29,7 +29,8 @@ doctrine: user: "%test_database_user%" password: "%test_database_password%" charset: "%test_database_charset%" - path: "%test_database_path%" + path: "%env(TEST_DATABASE_PATH)%" + orm: metadata_cache_driver: type: service diff --git a/app/config/parameters.yml.dist b/app/config/parameters.yml.dist index 97f51ed1..b3fe11c8 100644 --- a/app/config/parameters.yml.dist +++ b/app/config/parameters.yml.dist @@ -10,17 +10,20 @@ parameters: # database_user: %env.database_user% # database_password: %env.database_password% - database_driver: pdo_sqlite + database_driver: pdo_mysql database_host: 127.0.0.1 database_port: ~ - database_name: symfony + database_name: wallabag database_user: root database_password: ~ - database_path: "%kernel.root_dir%/../data/db/wallabag.sqlite" + # For SQLite, database_path should be "%kernel.root_dir%/../data/db/wallabag.sqlite" + database_path: null database_table_prefix: wallabag_ database_socket: null - # with MySQL, use "utf8mb4" if you got problem with content with emojis - database_charset: utf8 + # with PostgreSQL and SQLite, you must set "utf8" + database_charset: utf8mb4 + + domain_name: https://your-wallabag-url-instance.com mailer_transport: smtp mailer_host: 127.0.0.1 @@ -57,6 +60,3 @@ parameters: redis_port: 6379 redis_path: null redis_password: null - - # sites credentials - sites_credentials: {} diff --git a/app/config/parameters_test.yml b/app/config/parameters_test.yml index 5f2e25bb..010785e6 100644 --- a/app/config/parameters_test.yml +++ b/app/config/parameters_test.yml @@ -5,5 +5,6 @@ parameters: test_database_name: null test_database_user: null test_database_password: null - test_database_path: '%kernel.root_dir%/../data/db/wallabag_test.sqlite' + test_database_path: "%env(TEST_DATABASE_PATH)%" + env(TEST_DATABASE_PATH): "%kernel.root_dir%/../data/db/wallabag_test.sqlite" test_database_charset: utf8 diff --git a/app/config/security.yml b/app/config/security.yml index efb00a53..e14a0bd1 100644 --- a/app/config/security.yml +++ b/app/config/security.yml @@ -56,10 +56,12 @@ security: access_control: - { path: ^/api/doc, roles: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/api/version, roles: IS_AUTHENTICATED_ANONYMOUSLY } + - { path: ^/api/user, roles: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY } - { path: /(unread|starred|archive).xml$, roles: IS_AUTHENTICATED_ANONYMOUSLY } + - { path: /tags/(.*).xml$, roles: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/share, roles: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/settings, roles: ROLE_SUPER_ADMIN } - { path: ^/annotations, roles: ROLE_USER } diff --git a/app/config/services.yml b/app/config/services.yml index 9a1ce80b..7b85d846 100644 --- a/app/config/services.yml +++ b/app/config/services.yml @@ -42,3 +42,11 @@ services: arguments: ["@session"] tags: - { name: kernel.event_listener, event: security.interactive_login, method: onInteractiveLogin } + + craue_config_cache_provider: + class: Symfony\Component\Cache\Adapter\FilesystemAdapter + public: false + arguments: + - 'craue_config' + - 0 + - '%kernel.cache_dir%' diff --git a/app/config/tests/parameters_test.mysql.yml b/app/config/tests/parameters_test.mysql.yml index bca2d466..36b227fb 100644 --- a/app/config/tests/parameters_test.mysql.yml +++ b/app/config/tests/parameters_test.mysql.yml @@ -6,4 +6,5 @@ parameters: test_database_user: root test_database_password: ~ test_database_path: ~ + env(TEST_DATABASE_PATH): ~ test_database_charset: utf8mb4 diff --git a/app/config/tests/parameters_test.pgsql.yml b/app/config/tests/parameters_test.pgsql.yml index 3e18d4a0..60f51df6 100644 --- a/app/config/tests/parameters_test.pgsql.yml +++ b/app/config/tests/parameters_test.pgsql.yml @@ -6,4 +6,5 @@ parameters: test_database_user: travis test_database_password: ~ test_database_path: ~ + env(TEST_DATABASE_PATH): ~ test_database_charset: utf8 diff --git a/app/config/tests/parameters_test.sqlite.yml b/app/config/tests/parameters_test.sqlite.yml index b8a5f41a..5c731bf5 100644 --- a/app/config/tests/parameters_test.sqlite.yml +++ b/app/config/tests/parameters_test.sqlite.yml @@ -5,5 +5,8 @@ parameters: test_database_name: ~ test_database_user: ~ test_database_password: ~ - test_database_path: "%kernel.root_dir%/../data/db/wallabag_test.sqlite" + # Using an environnement variable in order to avoid the error "attempt to write a readonly database" + # when the schema is dropped then recreate + test_database_path: "%env(TEST_DATABASE_PATH)%" + env(TEST_DATABASE_PATH): "%kernel.root_dir%/../data/db/wallabag_test.sqlite" test_database_charset: utf8 diff --git a/app/config/wallabag.yml b/app/config/wallabag.yml new file mode 100644 index 00000000..b45934e4 --- /dev/null +++ b/app/config/wallabag.yml @@ -0,0 +1,161 @@ +wallabag_core: + version: 2.2.3 + paypal_url: "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9UBA65LG3FX9Y&lc=gb" + languages: + en: 'English' + fr: 'Français' + de: 'Deutsch' + tr: 'Türkçe' + fa: 'فارسی' + ro: 'Română' + pl: 'Polish' + da: 'Dansk' + es: 'Español' + oc: 'Occitan' + it: 'Italiano' + pt: 'Português' + items_on_page: 12 + theme: material + language: '%locale%' + rss_limit: 50 + reading_speed: 1 + cache_lifetime: 10 + action_mark_as_read: 1 + list_mode: 0 + fetching_error_message_title: 'No title found' + fetching_error_message: | + wallabag can't retrieve contents for this article. Please troubleshoot this issue. + api_limit_mass_actions: 10 + encryption_key_path: "%kernel.root_dir%/../data/site-credentials-secret-key.txt" + default_internal_settings: + - + name: share_public + value: 1 + section: entry + - + name: carrot + value: 1 + section: entry + - + name: share_diaspora + value: 1 + section: entry + - + name: diaspora_url + value: http://diasporapod.com + section: entry + - + name: share_unmark + value: 1 + section: entry + - + name: unmark_url + value: https://unmark.it + section: entry + - + name: share_shaarli + value: 1 + section: entry + - + name: share_scuttle + value: 1 + section: entry + - + name: shaarli_url + value: http://myshaarli.com + section: entry + - + name: scuttle_url + value: http://scuttle.org + section: entry + - + name: share_mail + value: 1 + section: entry + - + name: share_twitter + value: 1 + section: entry + - + name: show_printlink + value: 1 + section: entry + - + name: restricted_access + value: 0 + section: entry + - + name: export_epub + value: 1 + section: export + - + name: export_mobi + value: 1 + section: export + - + name: export_pdf + value: 1 + section: export + - + name: export_csv + value: 1 + section: export + - + name: export_json + value: 1 + section: export + - + name: export_txt + value: 1 + section: export + - + name: export_xml + value: 1 + section: export + - + name: import_with_redis + value: 0 + section: import + - + name: import_with_rabbitmq + value: 0 + section: import + - + name: piwik_enabled + value: 0 + section: analytics + - + name: piwik_host + value: v2.wallabag.org + section: analytics + - + name: piwik_site_id + value: 1 + section: analytics + - + name: demo_mode_enabled + value: 0 + section: misc + - + name: demo_mode_username + value: wallabag + section: misc + - + name: download_images_enabled + value: 0 + section: misc + - + name: wallabag_support_url + value: https://www.wallabag.org/pages/support.html + section: misc + - + name: api_user_registration + value: 0 + section: api + +wallabag_user: + registration_enabled: "%fosuser_registration%" + +wallabag_import: + allow_mimetypes: ['application/octet-stream', 'application/json', 'text/plain', 'text/csv'] + resource_dir: "%kernel.root_dir%/../web/uploads/import" diff --git a/app/config/webpack/common.js b/app/config/webpack/common.js new file mode 100644 index 00000000..c497689a --- /dev/null +++ b/app/config/webpack/common.js @@ -0,0 +1,40 @@ +const path = require('path'); +const webpack = require('webpack'); +const StyleLintPlugin = require('stylelint-webpack-plugin'); + +const rootDir = path.resolve(__dirname, '../../../'); + +module.exports = function () { + return { + entry: { + material: path.join(rootDir, './app/Resources/static/themes/material/index.js'), + baggy: path.join(rootDir, './app/Resources/static/themes/baggy/index.js'), + public: path.join(rootDir, './app/Resources/static/themes/_global/share.js'), + }, + output: { + filename: '[name].js', + path: path.resolve(rootDir, 'web/bundles/wallabagcore'), + publicPath: '/bundles/wallabagcore/', + }, + plugins: [ + new webpack.ProvidePlugin({ + $: 'jquery', + jQuery: 'jquery', + 'window.$': 'jquery', + 'window.jQuery': 'jquery', + }), + new StyleLintPlugin({ + configFile: '.stylelintrc', + failOnError: false, + quiet: false, + context: 'app/Resources/static/themes', + files: '**/*.scss', + }), + ], + resolve: { + alias: { + jquery: path.join(rootDir, 'node_modules/jquery/dist/jquery.js'), + }, + }, + }; +}; diff --git a/app/config/webpack/dev.js b/app/config/webpack/dev.js new file mode 100644 index 00000000..b6551152 --- /dev/null +++ b/app/config/webpack/dev.js @@ -0,0 +1,61 @@ +const webpackMerge = require('webpack-merge'); +const webpack = require('webpack'); +const commonConfig = require('./common.js'); + +module.exports = function () { + return webpackMerge(commonConfig(), { + devtool: 'eval-source-map', + output: { + filename: '[name].dev.js', + }, + + devServer: { + hot: true, + // enable HMR on the server + + contentBase: './web', + // match the output path + }, + plugins: [ + new webpack.HotModuleReplacementPlugin(), + ], + module: { + rules: [ + { + enforce: 'pre', + test: /\.js$/, + loader: 'eslint-loader', + exclude: /node_modules/, + }, + { + test: /\.js$/, + exclude: /(node_modules)/, + use: { + loader: 'babel-loader', + options: { + presets: ['env'], + }, + }, + }, + { + test: /\.(s)?css$/, + use: [ + 'style-loader', + { + loader: 'css-loader', + options: { + importLoaders: 1, + }, + }, + 'postcss-loader', + 'sass-loader', + ], + }, + { + test: /\.(jpg|png|gif|svg|eot|ttf|woff|woff2)$/, + use: 'url-loader', + }, + ], + }, + }); +}; diff --git a/app/config/webpack/prod.js b/app/config/webpack/prod.js new file mode 100644 index 00000000..44961cc5 --- /dev/null +++ b/app/config/webpack/prod.js @@ -0,0 +1,99 @@ +const webpack = require('webpack'); +const webpackMerge = require('webpack-merge'); +const ExtractTextPlugin = require('extract-text-webpack-plugin'); +const ManifestPlugin = require('webpack-manifest-plugin'); + +const commonConfig = require('./common.js'); + +module.exports = function () { + return webpackMerge(commonConfig(), { + output: { + filename: '[name].js', + }, + devtool: 'source-map', + plugins: [ + new webpack.DefinePlugin({ + 'process.env': { + 'NODE_ENV': JSON.stringify('production'), + }, + }), + new webpack.optimize.UglifyJsPlugin({ + beautify: false, + mangle: { + screw_ie8: true, + keep_fnames: true, + }, + compress: { + screw_ie8: true, + warnings: false, + }, + comments: false, + }), + new ExtractTextPlugin('[name].css'), + new ManifestPlugin({ + fileName: 'manifest.json', + }), + ], + module: { + rules: [ + { + enforce: 'pre', + test: /\.js$/, + loader: 'eslint-loader', + exclude: /node_modules/, + }, + { + test: /\.js$/, + exclude: /(node_modules)/, + use: { + loader: 'babel-loader', + options: { + presets: ['env'], + }, + }, + }, + { + test: /\.(s)?css$/, + use: ExtractTextPlugin.extract({ + fallback: 'style-loader', + use: [ + { + loader: 'css-loader', + options: { + importLoaders: 1, + minimize: { + discardComments: { + removeAll: true, + }, + core: true, + minifyFontValues: true, + }, + }, + }, + 'postcss-loader', + 'sass-loader', + ], + }), + }, + { + test: /\.(jpg|png|gif|svg)$/, + use: { + loader: 'file-loader', + options: { + name: 'img/[name].[ext]', + }, + }, + }, + { + test: /\.(eot|ttf|woff|woff2)$/, + use: { + loader: 'file-loader', + options: { + name: 'fonts/[name].[ext]', + }, + }, + }, + ], + }, + }); +}; diff --git a/build.xml b/build.xml index b5727e62..b9fe6136 100644 --- a/build.xml +++ b/build.xml @@ -62,18 +62,6 @@ - - - - - - - - - - - - @@ -103,18 +91,6 @@ - - - - - - - - - - - - @@ -149,18 +125,6 @@ - - - - - - - - - - - - diff --git a/composer.json b/composer.json index 9336e801..7428f688 100644 --- a/composer.json +++ b/composer.json @@ -28,7 +28,7 @@ "issues": "https://github.com/wallabag/wallabag/issues" }, "require": { - "php": ">=5.5.9", + "php": ">=5.6.0", "ext-pcre": "*", "ext-dom": "*", "ext-curl": "*", @@ -61,11 +61,10 @@ "wallabag/tcpdf": "^6.2", "simplepie/simplepie": "~1.3.1", "willdurand/hateoas-bundle": "~1.0", - "htmlawed/htmlawed": "~1.1.19", "liip/theme-bundle": "~1.1", "lexik/form-filter-bundle": "~5.0", - "j0k3r/graby": "~1.0", - "friendsofsymfony/user-bundle": "2.0.x-dev", + "j0k3r/graby": "^1.0", + "friendsofsymfony/user-bundle": "^2.0", "friendsofsymfony/oauth-server-bundle": "^1.5", "stof/doctrine-extensions-bundle": "^1.2", "scheb/two-factor-bundle": "~2.0", @@ -74,8 +73,8 @@ "kphoen/rulerz-bundle": "~0.13", "guzzlehttp/guzzle": "^5.3.1", "doctrine/doctrine-migrations-bundle": "^1.0", - "paragonie/random_compat": "~1.0", - "craue/config-bundle": "~1.4", + "paragonie/random_compat": "~2.0", + "craue/config-bundle": "~2.0", "mnapoli/piwik-twig-extension": "^1.0", "ocramius/proxy-manager": "1.*", "white-october/pagerfanta-bundle": "^1.0", @@ -84,15 +83,17 @@ "javibravo/simpleue": "^1.0", "symfony/dom-crawler": "^3.1", "friendsofsymfony/jsrouting-bundle": "^1.6", - "bdunogier/guzzle-site-authenticator": "1.0.0-beta1" + "bdunogier/guzzle-site-authenticator": "^1.0.0@dev", + "defuse/php-encryption": "^2.1" }, "require-dev": { "doctrine/doctrine-fixtures-bundle": "~2.2", "doctrine/data-fixtures": "~1.1.1", "sensio/generator-bundle": "^3.0", - "symfony/phpunit-bridge": "^3.0", + "symfony/phpunit-bridge": "^3.3", "friendsofphp/php-cs-fixer": "~1.9", - "m6web/redis-mock": "^2.0" + "m6web/redis-mock": "^2.0", + "dama/doctrine-test-bundle": "^1.0" }, "scripts": { "post-cmd": [ @@ -130,7 +131,7 @@ "config": { "bin-dir": "bin", "platform": { - "php": "5.5.9" + "php": "5.6.0" } }, "minimum-stability": "dev", diff --git a/docs/de/conf.py b/docs/de/conf.py deleted file mode 100644 index cc9dcdf8..00000000 --- a/docs/de/conf.py +++ /dev/null @@ -1,55 +0,0 @@ -# -*- coding: utf-8 -*- -# -# wallabag documentation build configuration file, created by -# sphinx-quickstart on Fri Oct 16 06:47:23 2015. - -import sys -import os - -extensions = [] -templates_path = ['_templates'] -source_suffix = '.rst' -master_doc = 'index' -project = u'wallabag-fr' -copyright = u'2013-2016, Nicolas Lœuillet - MIT Licence' -version = '2.1.0' -release = version -exclude_patterns = ['_build'] -pygments_style = 'sphinx' -html_theme = 'default' -html_static_path = ['_static'] -htmlhelp_basename = 'wallabagdedoc' - -latex_elements = { -} - -latex_documents = [ - ('index', 'wallabag-de.tex', u'wallabag Documentation', - u'Nicolas Lœuillet', 'manual'), -] - -man_pages = [ - ('index', 'wallabagde', u'wallabag Documentation', - [u'Nicolas Lœuillet'], 1) -] - -texinfo_documents = [ - ('index', 'wallabag', u'wallabag Documentation', - u'Nicolas Lœuillet', 'wallabag', 'wallabag is an opensource read-it-later.', - 'Miscellaneous'), -] - -##### Guzzle sphinx theme - -import guzzle_sphinx_theme -html_translator_class = 'guzzle_sphinx_theme.HTMLTranslator' -html_theme_path = guzzle_sphinx_theme.html_theme_path() -html_theme = 'guzzle_sphinx_theme' - -# Custom sidebar templates, maps document names to template names. -html_sidebars = { - '**': ['logo-text.html', 'globaltoc.html', 'searchbox.html'] -} - -# Register the theme as an extension to generate a sitemap.xml -extensions.append("guzzle_sphinx_theme") diff --git a/docs/de/developer/api.rst b/docs/de/developer/api.rst deleted file mode 100644 index 7a18afde..00000000 --- a/docs/de/developer/api.rst +++ /dev/null @@ -1,272 +0,0 @@ -API Dokumentation -================= - -Dank dieser Dokumentation werden wir sehen, wie wir mit der wallabag API interagieren. - -Voraussetzungen ---------------- - -* wallabag frisch (oder nicht) installiert auf http://localhost:8000 -* ``httpie`` installiert auf deinem Computer (`siehe Projektwebsite `__). Beachte, dass du die Kommandos auch mit curl oder wget nutzen kannst. -* alle API Methoden sind hier dokumentiert http://localhost:8000/api/doc - -Einen neuen API Client erstellen --------------------------------- - -In deinem wallabag Account, kannst du einen neuen API Client unter dieser URL http://localhost:8000/developer/client/create erstellen. - -Gib dazu nur die Umleitungs-URL deiner Appliaktion an und erstelle deinen Client. Wenn deine Applikation eine Desktopapplikation ist, trage die URL, die dir am besten passt, ein. - -Du bekommst Informationen wie diese: - -:: - - Client ID: - - 1_3o53gl30vhgk0c8ks4cocww08o84448osgo40wgw4gwkoo8skc - - Client secret: - - 636ocbqo978ckw0gsw4gcwwocg8044sco0w8w84cws48ggogs4 - - -Einen Aktualisierungstoken erhalten ------------------------------------ - -Für jeden API Aufruf brauchst du einen Token. Lass uns einen erstellen mit diesem Kommando (ersetze ``client_id``, ``client_secret``, ``username`` und ``password`` mit ihren Werten): - -:: - - http POST http://localhost:8000/oauth/v2/token \ - grant_type=password \ - client_id=1_3o53gl30vhgk0c8ks4cocww08o84448osgo40wgw4gwkoo8skc \ - client_secret=636ocbqo978ckw0gsw4gcwwocg8044sco0w8w84cws48ggogs4 \ - username=wallabag \ - password=wallabag - -Du bekommst folgendes zurück: - -:: - - HTTP/1.1 200 OK - Cache-Control: no-store, private - Connection: close - Content-Type: application/json - Date: Tue, 05 Apr 2016 08:44:33 GMT - Host: localhost:8000 - Pragma: no-cache - X-Debug-Token: 19c8e0 - X-Debug-Token-Link: /_profiler/19c8e0 - X-Powered-By: PHP/7.0.4 - - { - "access_token": "ZGJmNTA2MDdmYTdmNWFiZjcxOWY3MWYyYzkyZDdlNWIzOTU4NWY3NTU1MDFjOTdhMTk2MGI3YjY1ZmI2NzM5MA", - "expires_in": 3600, - "refresh_token": "OTNlZGE5OTJjNWQwYzc2NDI5ZGE5MDg3ZTNjNmNkYTY0ZWZhZDVhNDBkZTc1ZTNiMmQ0MjQ0OThlNTFjNTQyMQ", - "scope": null, - "token_type": "bearer" - } - -Wir werden mit dem ``access_token`` Wert in unseren nächsten Aufrufen arbeiten. - -cURL Beispiel: - -:: - - curl -s "https://localhost:8000/oauth/v2/token?grant_type=password&client_id=1_3o53gl30vhgk0c8ks4cocww08o84448osgo40wgw4gwkoo8skc&client_secret=636ocbqo978ckw0gsw4gcwwocg8044sco0w8w84cws48ggogs4&username=wallabag&password=wallabag" - -Existierende Einträge erhalten ------------------------------- - -Dokumentation für diese Methode: http://localhost:8000/api/doc#get--api-entries.{_format} - -Da wir auf einer neuen wallabag Installation arbeiten, bekommen wir keine Ergebnisse mit diesem Kommando: - -:: - - http GET http://localhost:8000/api/entries.json \ - "Authorization:Bearer ZGJmNTA2MDdmYTdmNWFiZjcxOWY3MWYyYzkyZDdlNWIzOTU4NWY3NTU1MDFjOTdhMTk2MGI3YjY1ZmI2NzM5MA" - -gibt zurück: - -:: - - HTTP/1.1 200 OK - 0: application/json - Cache-Control: no-cache - Connection: close - Content-Type: application/json - Date: Tue, 05 Apr 2016 08:51:32 GMT - Host: localhost:8000 - Set-Cookie: PHPSESSID=nrogm748md610ovhu6j70c3q63; path=/; HttpOnly - X-Debug-Token: 4fbbc4 - X-Debug-Token-Link: /_profiler/4fbbc4 - X-Powered-By: PHP/7.0.4 - - { - "_embedded": { - "items": [] - }, - "_links": { - "first": { - "href": "http://localhost:8000/api/entries?page=1&perPage=30" - }, - "last": { - "href": "http://localhost:8000/api/entries?page=1&perPage=30" - }, - "self": { - "href": "http://localhost:8000/api/entries?page=1&perPage=30" - } - }, - "limit": 30, - "page": 1, - "pages": 1, - "total": 0 - } - -Das Array ``items`` ist leer. - -cURL Beispiel: - -:: - - curl --get "https://localhost:8000/api/entries.html?access_token=ZGJmNTA2MDdmYTdmNWFiZjcxOWY3MWYyYzkyZDdlNWIzOTU4NWY3NTU1MDFjOTdhMTk2MGI3YjY1ZmI2NzM5MA" - -Deinen ersten Eintrag hinzufügen --------------------------------- - -Dokumentation für diese Methode: http://localhost:8000/api/doc#post--api-entries.{_format} - -:: - - http POST http://localhost:8000/api/entries.json \ - "Authorization:Bearer ZGJmNTA2MDdmYTdmNWFiZjcxOWY3MWYyYzkyZDdlNWIzOTU4NWY3NTU1MDFjOTdhMTk2MGI3YjY1ZmI2NzM5MA" \ - url="http://www.numerama.com/tech/160115-le-pocket-libre-wallabag-fait-le-plein-de-fonctionnalites.html" - -gibt zurück: - -:: - - HTTP/1.1 200 OK - 0: application/json - Cache-Control: no-cache - Connection: close - Content-Type: application/json - Date: Tue, 05 Apr 2016 09:07:54 GMT - Host: localhost:8000 - Set-Cookie: PHPSESSID=bjie40ck72kp2pst3i71gf43a4; path=/; HttpOnly - X-Debug-Token: e01c51 - X-Debug-Token-Link: /_profiler/e01c51 - X-Powered-By: PHP/7.0.4 - - { - "_links": { - "self": { - "href": "/api/entries/1" - } - }, - "content": "

Fonctionnant sur le même principe que Pocket, Instapaper ou Readability, le logiciel Wallabag permet de mémoriser des articles pour les lire plus tard. Sa nouvelle version apporte une multitude de nouvelles fonctionnalités.

Si vous utilisez Firefox comme navigateur web, vous avez peut-être constaté l’arrivée d’une fonctionnalité intitulée Pocket. Disponible autrefois sous la forme d’un module complémentaire, et sous un autre nom (Read it Later), elle est depuis le mois de juin 2015 directement incluse au sein de Firefox.

\n

Concrètement, Pocket sert à garder en mémoire des contenus que vous croisez au fil de la navigation, comme des articles de presse ou des vidéos, afin de pouvoir les consulter plus tard. Pocket fonctionne un peu comme un système de favoris, mais en bien plus élaboré grâce à ses options supplémentaires.

\n

Mais Pocket fait polémique, car il s’agit d’un projet propriétaire qui est intégré dans un logiciel libre. C’est pour cette raison que des utilisateurs ont choisi de se tourner vers d’autres solutions, comme Wallabag, qui est l’équivalent libre de Pocket et d’autres systèmes du même genre, comme Instapaper et Readability.

\n

Et justement, Wallabag évolue. C’est ce dimanche que la version 2.0.0 du logiciel a été publiée par l’équipe en  charge de son développement et celle-ci contient de nombreux changements par rapport aux moutures précédentes (la documentation est traduite en français), lui permettant d’apparaître comme une alternative à Pocket, Instapaper et Readability.

\n

\"homepage\"

\n

Parmi les principaux changements que l’on peut retenir avec cette nouvelle version, notons la possibilité d’écrire des annotations dans les articles mémorisés, de filtrer les contenus selon divers critères (temps de lecture, nom de domaine, date de création, statut…), d’assigner des mots-clés aux entrées, de modifier le titre des articles, le support des flux RSS ou encore le support de plusieurs langues dont le français.

\n

D’autres options sont également à signaler, comme l’aperçu d’un article mémorisé (si l’option est disponible), un guide de démarrage rapide pour les débutants, un outil d’export dans divers formats (PDF, JSON, EPUB, MOBI, XML, CSV et TXT) et, surtout, la possibilité de migrer vers Wallabag depuis Pocket, afin de convaincre les usagers de se lancer.

\n \n \n

Articles liés

\n
\n
\n", - "created_at": "2016-04-05T09:07:54+0000", - "domain_name": "www.numerama.com", - "id": 1, - "is_archived": 0, - "is_starred": 0, - "language": "fr-FR", - "mimetype": "text/html", - "preview_picture": "http://www.numerama.com/content/uploads/2016/04/post-it.jpg", - "reading_time": 2, - "tags": [], - "title": "Le Pocket libre Wallabag fait le plein de fonctionnalités - Tech - Numerama", - "updated_at": "2016-04-05T09:07:54+0000", - "url": "http://www.numerama.com/tech/160115-le-pocket-libre-wallabag-fait-le-plein-de-fonctionnalites.html", - "user_email": "", - "user_id": 1, - "user_name": "wallabag" - } - -Wenn du jetzt das vorherige Kommando (siehe **Existierende Einträge erhalten**), wirst du Daten erhalten. - -cURL Beispiel: - -:: - - curl "https://localhost:8000/api/entries.html?access_token=ZGJmNTA2MDdmYTdmNWFiZjcxOWY3MWYyYzkyZDdlNWIzOTU4NWY3NTU1MDFjOTdhMTk2MGI3YjY1ZmI2NzM5MA&url=http://www.numerama.com/tech/160115-le-pocket-libre-wallabag-fait-le-plein-de-fonctionnalites.html" - -Eintrag löschen ------------------ - -Dokumentation für diese Methode: http://localhost:8000/api/doc#delete--api-entries-{entry}.{_format} - -:: - - http DELETE http://localhost:8000/api/entries/1.json \ - "Authorization:Bearer ZGJmNTA2MDdmYTdmNWFiZjcxOWY3MWYyYzkyZDdlNWIzOTU4NWY3NTU1MDFjOTdhMTk2MGI3YjY1ZmI2NzM5MA" - -gibt zurück: - -:: - - HTTP/1.1 200 OK - 0: application/json - Cache-Control: no-cache - Connection: close - Content-Type: application/json - Date: Tue, 05 Apr 2016 09:19:07 GMT - Host: localhost:8000 - Set-Cookie: PHPSESSID=jopgnfvmuc9a62b27sqm6iulr6; path=/; HttpOnly - X-Debug-Token: 887cef - X-Debug-Token-Link: /_profiler/887cef - X-Powered-By: PHP/7.0.4 - - { - "_links": { - "self": { - "href": "/api/entries/" - } - }, - "annotations": [], - "content": "

Fonctionnant sur le même principe que Pocket, Instapaper ou Readability, le logiciel Wallabag permet de mémoriser des articles pour les lire plus tard. Sa nouvelle version apporte une multitude de nouvelles fonctionnalités.

Si vous utilisez Firefox comme navigateur web, vous avez peut-être constaté l’arrivée d’une fonctionnalité intitulée Pocket. Disponible autrefois sous la forme d’un module complémentaire, et sous un autre nom (Read it Later), elle est depuis le mois de juin 2015 directement incluse au sein de Firefox.

\n

Concrètement, Pocket sert à garder en mémoire des contenus que vous croisez au fil de la navigation, comme des articles de presse ou des vidéos, afin de pouvoir les consulter plus tard. Pocket fonctionne un peu comme un système de favoris, mais en bien plus élaboré grâce à ses options supplémentaires.

\n

Mais Pocket fait polémique, car il s’agit d’un projet propriétaire qui est intégré dans un logiciel libre. C’est pour cette raison que des utilisateurs ont choisi de se tourner vers d’autres solutions, comme Wallabag, qui est l’équivalent libre de Pocket et d’autres systèmes du même genre, comme Instapaper et Readability.

\n

Et justement, Wallabag évolue. C’est ce dimanche que la version 2.0.0 du logiciel a été publiée par l’équipe en  charge de son développement et celle-ci contient de nombreux changements par rapport aux moutures précédentes (la documentation est traduite en français), lui permettant d’apparaître comme une alternative à Pocket, Instapaper et Readability.

\n

\"homepage\"

\n

Parmi les principaux changements que l’on peut retenir avec cette nouvelle version, notons la possibilité d’écrire des annotations dans les articles mémorisés, de filtrer les contenus selon divers critères (temps de lecture, nom de domaine, date de création, statut…), d’assigner des mots-clés aux entrées, de modifier le titre des articles, le support des flux RSS ou encore le support de plusieurs langues dont le français.

\n

D’autres options sont également à signaler, comme l’aperçu d’un article mémorisé (si l’option est disponible), un guide de démarrage rapide pour les débutants, un outil d’export dans divers formats (PDF, JSON, EPUB, MOBI, XML, CSV et TXT) et, surtout, la possibilité de migrer vers Wallabag depuis Pocket, afin de convaincre les usagers de se lancer.

\n \n \n

Articles liés

\n
\n
\n", - "created_at": "2016-04-05T09:07:54+0000", - "domain_name": "www.numerama.com", - "is_archived": 0, - "is_starred": 0, - "language": "fr-FR", - "mimetype": "text/html", - "preview_picture": "http://www.numerama.com/content/uploads/2016/04/post-it.jpg", - "reading_time": 2, - "tags": [], - "title": "Le Pocket libre Wallabag fait le plein de fonctionnalités - Tech - Numerama", - "updated_at": "2016-04-05T09:07:54+0000", - "url": "http://www.numerama.com/tech/160115-le-pocket-libre-wallabag-fait-le-plein-de-fonctionnalites.html", - "user_email": "", - "user_id": 1, - "user_name": "wallabag" - } - -Und wenn du die existierenden Einträge nun listen willst (siehe **Existierende Einträge erhalten**), ist das Array wieder leer. - -cURL Beispiel: - -:: - - curl --request DELETE "https://localhost:8000/api/entries/1.html?access_token=ZGJmNTA2MDdmYTdmNWFiZjcxOWY3MWYyYzkyZDdlNWIzOTU4NWY3NTU1MDFjOTdhMTk2MGI3YjY1ZmI2NzM5MA" - -Andere Methoden ---------------- - -Wir werden nicht für jede API Methode Beispiele schreiben. - -Wirf einen Blick in die Liste http://localhost:8000/api/doc, um alle Methode kennenzulernen. - - -Drittanbieter Ressourcen ------------------------- - -Einige Applikationen oder Bibliotheken nutzen unsere API. Hier ist eine nicht abschließende Aufzählung von ihnen: - -- `Java wrapper for the wallabag API `_ von Dmitriy Bogdanov. -- `.NET library for the wallabag v2 API `_ von Julian Oster. -- `Python API for wallabag `_ von FoxMaSk, für sein Projekt `Trigger Happy `_. -- `A plugin `_ entworfen für `Tiny Tiny RSS `_, das die wallabag v2 API nutzt. Von Josh Panter. -- `Golang wrapper for the wallabag API `_ von Strubbl, für seine Projekte `wallabag-stats Graph `_ und das Kommandozeilentool `wallabag-add-article `_. -- Tool, um automatisiert wallabag Artikel auf den lokalen PC oder ebook reader zu laden `wallabako `_ von anarcat. diff --git a/docs/de/developer/asynchronous.rst b/docs/de/developer/asynchronous.rst deleted file mode 100644 index 742dd3e5..00000000 --- a/docs/de/developer/asynchronous.rst +++ /dev/null @@ -1,159 +0,0 @@ -Asynchrone Aufgaben -=================== - -Um große asynchrone Aufgaben zu starten (etwa für große Importe), können wir RabbitMQ oder Redis nutzen. - -Installation von RabbitMQ für asynchrone Aufgaben -------------------------------------------------- - -Voraussetzungen -^^^^^^^^^^^^^^^ - -Du musst RabbitMQ auf deinem Server installiert haben. - -Installation -^^^^^^^^^^^^ - -.. code:: bash - - wget https://www.rabbitmq.com/rabbitmq-signing-key-public.asc - apt-key add rabbitmq-signing-key-public.asc - apt-get update - apt-get install rabbitmq-server - -Konfiguration und Start -^^^^^^^^^^^^^^^^^^^^^^^ - -.. code:: bash - - rabbitmq-plugins enable rabbitmq_management # (useful to have a web interface, available at http://localhost:15672/ (guest/guest) - rabbitmq-server -detached - -RabbitMQ stoppen -^^^^^^^^^^^^^^^^ - -.. code:: bash - - rabbitmqctl stop - -RabbitMQ für wallabag konfigurieren -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Bearbeite deine ``app/config/parameters.yml``-Datei, um die RabbitMQ-Parameter zu ändern. Die Standardwerte sollten in Ordnung sein: - -.. code:: yaml - - rabbitmq_host: localhost - rabbitmq_port: 5672 - rabbitmq_user: guest - rabbitmq_password: guest - rabbitmq_prefetch_count: 10 # lesen http://www.rabbitmq.com/consumer-prefetch.html - -RabbitMQ in wallabag aktivieren -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -In den internen Einstellungen, aktiviere RabbitMQ im Import-Abschnitt mit dem Wert 1. - -Starte den RabbitMQ-Consumer -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Abhängig davon, über welchen Service du importieren möchtest, musst du den entsprechenden (oder mehrere) Cronjob aktivieren: - -.. code:: bash - - # für den Pocket-Import - bin/console rabbitmq:consumer -e=prod import_pocket -w - - # für den Readability-Import - bin/console rabbitmq:consumer -e=prod import_readability -w - - # für den Instapaper-Import - bin/console rabbitmq:consumer -e=prod import_instapaper -w - - # für den wallabag v1-Import - bin/console rabbitmq:consumer -e=prod import_wallabag_v1 -w - - # für den wallabag v2-Import - bin/console rabbitmq:consumer -e=prod import_wallabag_v2 -w - - # für den Firefox-Import - bin/console rabbitmq:consumer -e=prod import_firefox -w - - # für den Chrome-Import - bin/console rabbitmq:consumer -e=prod import_chrome -w - -Redis für asynchrone Aufgaben installieren ------------------------------------------- - -Um große asynchrone Aufgaben zu starten (etwa für große Importe), können wir auch Redis nutzen. - -Voraussetzungen -^^^^^^^^^^^^^^^ - -Du musst Redis auf deinem Server installiert haben. - -Installation -^^^^^^^^^^^^ - -.. code:: bash - - apt-get install redis-server - -Start -^^^^^ - -Der Server kann bereits nach der Installation laufen, falls nicht, kannst du ihn wie folgt starten: - -.. code:: bash - - redis-server - - -Redis für wallabag konfigurieren -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Bearbeite deine ``app/config/parameters.yml``-Datei, um die Redis-Parameter zu ändern. Die Standardwerte sollten in Ordnung sein: - -.. code:: yaml - - redis_host: localhost - redis_port: 6379 - -Redis in wallabag aktivieren -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -In den internen Einstellungen, aktiviere Redis im Import-Abschnitt mit dem Wert 1. - -Starten des Redis-Consumer -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Abhängig davon, über welchen Service du importieren möchtest, musst du den entsprechenden (oder mehrere) Cronjob aktivieren: - -.. code:: bash - - # für den Pocket-Import - bin/console wallabag:import:redis-worker -e=prod pocket -vv >> /path/to/wallabag/var/logs/redis-pocket.log - - # für den Readability-Import - bin/console wallabag:import:redis-worker -e=prod readability -vv >> /path/to/wallabag/var/logs/redis-readability.log - - # für den Instapaper-Import - bin/console wallabag:import:redis-worker -e=prod instapaper -vv >> /path/to/wallabag/var/logs/redis-instapaper.log - - # für den wallabag v1-Import - bin/console wallabag:import:redis-worker -e=prod wallabag_v1 -vv >> /path/to/wallabag/var/logs/redis-wallabag_v1.log - - # für den wallabag v2-Import - bin/console wallabag:import:redis-worker -e=prod wallabag_v2 -vv >> /path/to/wallabag/var/logs/redis-wallabag_v2.log - - # für den Firefox-Import - bin/console wallabag:import:redis-worker -e=prod firefox -vv >> /path/to/wallabag/var/logs/redis-firefox.log - - # für den Chrome-Import - bin/console wallabag:import:redis-worker -e=prod chrome -vv >> /path/to/wallabag/var/logs/redis-chrome.log - -Wenn du den Import nur für einige Artikel nutzen willst, kannst du die Nummer festlegen (hier: 12) und der Consumer wird nach dem zwölften Artikel aufhören: - -.. code:: bash - - bin/console wallabag:import:redis-worker -e=prod pocket -vv --maxIterations=12 diff --git a/docs/de/developer/docker.rst b/docs/de/developer/docker.rst deleted file mode 100644 index 9948fe8c..00000000 --- a/docs/de/developer/docker.rst +++ /dev/null @@ -1,57 +0,0 @@ -Lasse wallabag in docker-compose laufen -======================================= - -Um deine eigene Entwicklungsinstanz von wallabag laufen zu lassen, -möchtest du vielleicht die vorkonfigurierten docker compose Dateien -nutzen. - -Voraussetzungen ---------------- - -Stelle sicher `Docker -`__ und `Docker -Compose `__ auf deinem -System verfügbar und aktuell zu haben. - -Wechsel des DBMS ----------------- - -Standardmäßig startet wallabag mit einer SQLite Datenbank. -Da wallabag Unterstützung für Postgresql und MySQL bietet, gibt es -auch docker Container für diese. - -In der ``docker-compose.yml`` kommentierst du für das gewählte DBMS -aus: - -- die Container Definition (``postgres`` oder ``mariadb`` root - Level Block) -- den Container Link in dem ``php`` Container -- die Container Umgebungsdatei in dem ``php`` Container - -Um mit Symfony Kommandos auf deinem Host auszuführen (wie z.B. -``wallabag:install``), sollst du außerdem: - -- die richtige Umgebungsdatei auf deiner Kommandozeile einlesen, - sodass Variablen wie ``SYMFONY__ENV__DATABASE_HOST`` existieren -- eine Zeile ``127.0.0.1 rdbms`` in deiner ``hosts`` Datei auf dem - System erstellen - -wallabag laufen lassen ----------------------- - -#. Forke und klone das Projekt -#. Bearbeite ``app/config/parameters.yml`` um ``database_*`` - Eigenschaften mit den kommentierten zu ersetzen (mit Werten - mit ``env.`` Präfix) -#. ``composer install`` die Projektabhängigkeiten -#. ``php bin/console wallabag:install``, um das Schema zu erstellen -#. ``docker-compose up`` um die Container laufen zu lassen -#. Schließlich öffne http://localhost:8080/, um dein frisch - installiertes wallabag zu finden. - -In den verschiedenen Schritten wirst du vielleicht in verschiendene -Probleme laufen wie UNIX Berechtigungsprobleme, falschen Pfaden im -generierten Cache, etc.… -Operationen wie das Löschen der Cachedateien oder das Ändern der -Dateibesitzer können öfter gebraucht werden, darum habe keine Angst -sie anzupassen. diff --git a/docs/de/developer/documentation.rst b/docs/de/developer/documentation.rst deleted file mode 100644 index 41e19363..00000000 --- a/docs/de/developer/documentation.rst +++ /dev/null @@ -1,10 +0,0 @@ -Wirke an dieser Dokumentation mit -================================= - -Quellen der Dokumentation sind hier zu finden https://github.com/wallabag/wallabag/tree/master/docs - -Wir nutzen `ReadTheDocs `__, um sie zu generieren. - -Seiten werden in `reStructuredText `__ geschrieben. Du kannst Onlinetools wie http://rst.aaroniles.net/ oder http://rst.ninjs.org/ nutzen, um eine Vorschau deiner Artikel zu betrachten. - -Wenn du eine neue Seite erstellst, vergiss nicht die `index.rst `__ zu bearbeiten, um dort einen Link für die Seitenleiste hinzuzufügen. diff --git a/docs/de/developer/paywall.rst b/docs/de/developer/paywall.rst deleted file mode 100644 index 365027b4..00000000 --- a/docs/de/developer/paywall.rst +++ /dev/null @@ -1,56 +0,0 @@ -Articles behind a paywall -========================= - -wallabag can fetch articles from websites which use a paywall system. - -Enable paywall authentication ------------------------------ - -In internal settings, in the **Article** section, enable authentication for websites with paywall (with the value 1). - -Configure credentials in wallabag ---------------------------------- - -Edit your ``app/config/parameters.yml`` file to edit credentials for each website with paywall. Here is an example for some french websites: - -.. code:: yaml - - sites_credentials: - mediapart.fr: {username: "myMediapartLogin", password: "mypassword"} - arretsurimages.net: {username: "myASILogin", password: "mypassword"} - -.. note:: - - These credentials will be shared between each user of your wallabag instance. - -Parsing configuration files ---------------------------- - -.. note:: - - Read `this part of the documentation `_ to understand the configuration files. - -Each parsing configuration file needs to be improved by adding ``requires_login``, ``login_uri``, -``login_username_field``, ``login_password_field`` and ``not_logged_in_xpath``. - -Be careful, the login form must be in the page content when wallabag loads it. It's impossible for wallabag to be authenticated -on a website where the login form is loaded after the page (by ajax for example). - -``login_uri`` is the action URL of the form (``action`` attribute in the form). -``login_username_field`` is the ``name`` attribute of the login field. -``login_password_field`` is the ``name`` attribute of the password field. - -For example: - -.. code:: - - title://div[@id="titrage-contenu"]/h1[@class="title"] - body: //div[@class="contenu-html"]/div[@class="page-pane"] - - requires_login: yes - - login_uri: http://www.arretsurimages.net/forum/login.php - login_username_field: username - login_password_field: password - - not_logged_in_xpath: //body[@class="not-logged-in"] diff --git a/docs/de/developer/testsuite.rst b/docs/de/developer/testsuite.rst deleted file mode 100644 index b2b16cdc..00000000 --- a/docs/de/developer/testsuite.rst +++ /dev/null @@ -1,10 +0,0 @@ -Testsuite -========= - -To ensure wallabag development quality, we wrote tests with `PHPUnit `_. - -If you contribute to the project (by translating the application, by fixing bugs or by adding a new feature), please write your own tests. - -To launch wallabag testsuite, you need to install `ant `_. - -Then, execute this command ``make test``. diff --git a/docs/de/developer/translate.rst b/docs/de/developer/translate.rst deleted file mode 100644 index 10544e31..00000000 --- a/docs/de/developer/translate.rst +++ /dev/null @@ -1,57 +0,0 @@ -Übersetze wallabag -================== - -wallabag Webapplikation ------------------------ - -Übersetzungsdateien -~~~~~~~~~~~~~~~~~~~ - -.. note:: - - Da wallabag hauptsächlich von einem französischem Team entwickelt wird, betrachte - die französische Übersetzung als die aktuellste und kopiere sie, um deine eigene Übersetzung zu starten. - -Du kannst die Übersetzungsdateien hier finden: https://github.com/wallabag/wallabag/tree/master/src/Wallabag/CoreBundle/Resources/translations. - -Du musst die ``messages.CODE.yml`` und ``validators.CODE.yml`` erstellen, wobei CODE -der ISO 639-1 Code deiner Sprache ist (`siehe Wikipedia `__). - -Andere Dateien zum Übersetzen: - -- https://github.com/wallabag/wallabag/tree/master/app/Resources/CraueConfigBundle/translations. -- https://github.com/wallabag/wallabag/tree/master/src/Wallabag/UserBundle/Resources/translations. - -Du musst die ``THE_TRANSLATION_FILE.CODE.yml`` Dateien erstellen. - -Konfigurationsdatei -~~~~~~~~~~~~~~~~~~~ - -Du musst die `app/config/config.yml `__ bearbeiten, -um deine Sprache auf der Konfigurationsseite in wallabag anzuzeigen (um Nutzern zu erlauben zu dieser neuen Übersetzung zu wechseln). - -Unter dem Abschnitt ``wallabag_core.languages`` musst du eine neue Zeile mit deiner Übersetzung hinzufügen. Zum Beispiel: - -:: - - wallabag_core: - ... - languages: - en: 'English' - fr: 'Français' - - -Für die erste Spalte (``en``, ``fr``, etc.) musst du den ISO 639-1 Code deiner Sprache hinzufügen (siehe oben). - -Für die zweite Spalte trägst du den Namen deiner Sprache ein. Nur den. - -wallabag Dokumentation ----------------------- - -.. note:: - - Im Gegensatz zur Webapplikation ist die Hauptsprache für die Dokumentation Englisch. - -Documentationsdateien sind hier gespeichert: https://github.com/wallabag/wallabag/tree/master/docs - -Du musst die Ordnerstruktur des Ordners ``en`` beachten, wenn du deine eigene Übersetzung startest. diff --git a/docs/de/index.rst b/docs/de/index.rst deleted file mode 100644 index 6e725fbe..00000000 --- a/docs/de/index.rst +++ /dev/null @@ -1,53 +0,0 @@ -wallabag Dokumentation -====================== - -.. image:: ../img/wallabag.png - :alt: wallabag Logo - :align: center - -**wallabag** ist eine Read-it-later Applikation: es speichert Websites, -indem es nur den Inhalt behält. Elemente wie Navigation oder Werbung werden gelöscht. - -Die Hauptdokumentation für diese Applikation ist in einigen Abschnitten organisiert: - -* :ref:`user-docs` -* :ref:`dev-docs` - -Die Dokumentation ist in anderen Sprachen verfügbar : - -* `Documentation in english `_ -* `Documentation en français `_ - -.. _user-docs: - -.. toctree:: - :maxdepth: 2 - :caption: Nutzerdokumentation - - user/installation - user/upgrade - user/configuration - user/import - user/create_account - user/articles - user/errors_during_fetching - user/filters - user/tags - user/configuring_mobile - user/android - user/parameters - user/backup - user/faq - -.. _dev-docs: - -.. toctree:: - :maxdepth: 2 - :caption: Entwicklerdokumentation - - developer/api - developer/docker - developer/paywall - developer/documentation - developer/translate - developer/asynchronous diff --git a/docs/de/requirements.txt b/docs/de/requirements.txt deleted file mode 100644 index 06fc8973..00000000 --- a/docs/de/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -Sphinx>=1.3.0,<1.4.0 -guzzle_sphinx_theme>=0.7.0,<0.8.0 diff --git a/docs/de/user/android.rst b/docs/de/user/android.rst deleted file mode 100644 index 31509438..00000000 --- a/docs/de/user/android.rst +++ /dev/null @@ -1,102 +0,0 @@ -Android App -=========== - - -Zweck dieses Dokuments ----------------------- - -Dieses Dokument beschreibt wie du deine Android App einrichtest, damit sie mit deiner Wallabaginstanz zusammenarbeitet. Es gibt hierbei keinen Unterschied im Vorgang - egal ob du Wallabag v1 oder v2 einsetzt. - - -Schritte, um dein App zu einzurichten -------------------------------------- - -Wenn du das erste Mal die App startest, siehst du den Willkommensbildschirm, wo du angewiesen wirst zunächst deine App mit deiner Wallabaginstanz einzurichten. - -.. image:: ../../img/user/android_welcome_screen.de.png - :alt: Willkommensbildschirm - :align: center - -Bestätige nur diese Nachricht und du wirst zum Einstellungsbildschirm weitergeleitet. - -.. image:: ../../img/user/android_configuration_screen.de.png - :alt: Einstellungsbildschirm - :align: center - -Trage deine Wallabagdaten ein. Du musst deine Wallabagadresse eintragen. **Es ist wichtig, dass die URL nicht mit einem Schrägstrich endet.** Füge auch deine Wallabagzugangsdaten in das Nutzer- und Passwortfeld ein. - -.. image:: ../../img/user/android_configuration_filled_in.de.png - :alt: Eingetragene Einstellungen - :align: center - -Nachdem du deine Daten eingetragen hast, drücke den Button Verbindung testen und warte auf das Fertigstellen des Tests. - -.. image:: ../../img/user/android_configuration_connection_test.de.png - :alt: Verbindungstest mit deinen Wallabagdaten - :align: center - -Der Verbindungstest sollte mit Erfolg ausgehen. Falls nicht, musst du zunächst deine Daten korrigieren bevor du zum nächsten Schritt gehst. - -.. image:: ../../img/user/android_configuration_connection_test_success.de.png - :alt: Verbindungstest war erfolgreich - :align: center - -Nach dem Verbindungstest erfolgreich war, kannst du den Button zum Feedzugangsdaten abholen drücken. Die App versucht nun sich bei deiner Wallabaginstanz einzuloggen und die Nutzer ID und den dazugehörigen Token für die Feeds zu laden. - -.. image:: ../../img/user/android_configuration_get_feed_credentials.de.png - :alt: Feedzugangsdaten abholen - :align: center - -Wenn der Prozess des Abholens deiner Feedzugangsdaten erfolgreich beendet wurde, siehst du eine Toastnachricht, dass die User ID und der Token automatisch in das Formular eingetragen wurden. - -.. image:: ../../img/user/android_configuration_feed_credentials_automatically_filled_in.de.png - :alt: Feedzugangsdaten erfolgreich abgeholt - :align: center - -Jetzt scrollst du bis zum unteren Rand des Einstellungsbildschirms. Natürlich kannst du die Einstellungen dort deinen Wünschen anpassen. Schließe die Einrichtung mit dem Drücken des Speicherbuttons ab. - -.. image:: ../../img/user/android_configuration_scroll_bottom.de.png - :alt: unterer Rand des Einstellungsbildschirms - :align: center - -Nachdem du den Speicherbutton gedrückt hast, kommst du in den folgenden Bildschirm. Die App schlägt vor, eine initiale Synchronisation der Artikelfeeds durchzuführen. Hier ist es empfohlen, dies zu bestätigen und Ja zu drücken. - -.. image:: ../../img/user/android_configuration_saved_feed_update.de.png - :alt: Einstellung erstmals gespeichert - :align: center - -Schließlich nach der ersten erfolgreichen Synchronisation, wird dir die Liste der ungelesenen Artikel präsentiert. - -.. image:: ../../img/user/android_unread_feed_synced.de.png - :alt: Gefüllte Artikellist da Feeds erfolgreich synchronisiert sind - :align: center - - - -Bekannte Limitierungen ---------------------- - -Zwei-Faktor-Authentifizierung (2FA) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Zur Zeit unterstützt die App keine Zwei-Faktor Authentifizierung. Du solltest sie deaktivieren damit die App funktioniert. - - -Begrenzte Anzahl Artikel mit Wallabag v2 -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In deiner Wallabaginstanz kannst du einstellen, wie viele Artikel Teil deiner RSS Feeds sind. Diese Option existierte in Wallabag v1 nicht, wo immer alle Artikel Teil des Feeds waren. Also wenn du die Anzahl der Artikel, die in der App angezeigt werden sollen, größer einstellst als die Anzahl Artikel in deinem Feed, wirst du nur die Anzahl an Artikel deines Feeds sehen. - - -SSL/TLS Verschlüsselung -~~~~~~~~~~~~~~~~~~~~~~~ - -Wenn du deine Wallabaginstanz per HTTPS erreichen kannst, solltest du das so konfigurieren. Besonders dann, wenn deine HTTP URL nach HTTPS umleitet. Im Moment kann die App mit dieser Weiterleitung nicht korrekt umgehen. - - -Referenzen ----------- - -- `Quellcode der Android-App `_ -- `Android-App auf F-Droid `_ -- `Android-App auf Google Play `_ \ No newline at end of file diff --git a/docs/de/user/articles.rst b/docs/de/user/articles.rst deleted file mode 100644 index 07cfa06c..00000000 --- a/docs/de/user/articles.rst +++ /dev/null @@ -1,112 +0,0 @@ -Artikel -======= - -Speichere deinen ersten Artikel -------------------------------- - -Die Hauptzweck von wallabag ist es, Artikel aus dem Web zu speichern. Es gibt viele Wege, dieses Ziel zu erreichen. -Wenn du denkst, dass ein Artikel falsch angezeigt wird, kannst du `diese Dokumentation lesen `_. - -Über ein Bookmarklet -^^^^^^^^^^^^^^^^^^^^ - -Auf der ``Howto``-Seite hast du einen ``Bookmarklet``-Tab. Ziehe das ``bag it!`` in die Lesezeichenleiste deines Browsers. -Nun kannst du jedes Mal, wenn du einen Artikel speichern willst, auf den ``bag it!``-Link klicken, dann ist der Artikel gespeichert. - -Der klassische Weg -^^^^^^^^^^^^^^^^^^ - -In der oberen Leiste deines Fensters hast du auf der rechten Seite drei Symbole. Mit dem ersten Symbole, einem Plus-Zeichen, kannst du auf einfache Weise einen neuen Artikel speichern. - -.. image:: ../../img/user/topbar.png - :alt: Top bar - :align: center - -Klicke darauf, um ein neues Feld anzuzeigen, füge die Artikel-URL ein und drücke Enter. Dann wird der Artikel gespeichert. - -Über ein Browser-Addon -^^^^^^^^^^^^^^^^^^^^^^ - -Firefox -""""""" - -Du kannst das `Firefox-Addon hier herunterladen`_. - -Chrome -"""""" - -Du kannst das `Chrome-Addon hier herunterladen`_. - -Über das Smartphone -^^^^^^^^^^^^^^^^^^^ - -Android -""""""" - -Du kannst die `Android-App hier herunterladen`_. - -Windows 10 in general -""""""""""""""""""""" - -Du kannst die `Windows-App hier herunterladen`_. - -Artikel herunterladen ---------------------- - -Du kannst jeden Artikel in verschiedenen Formaten herunterladen: ePUB, MOBI, PDF, XML, JSON, CSV. - -Klicke in der Artikelansicht auf dieses Symbol in der Seitenleiste: - -.. image:: ../../img/user/download_article.png - :alt: Artikel herunterladen - :align: center - -Du kannst sogar eine ganze Kategorie (Ungelesen, Favoriten, Archiv) in diesen Formaten herunterladen: -Beispielsweise kannst du auf der **Ungelesen**-Ansicht auf dieses Symbol in der oberen Leiste klicken: - -.. image:: ../../img/user/download_articles.png - :alt: Artikel herunterladen - :align: center - -Artikel teilen --------------- - -Wenn du einen Artikel liest, kannst du ihn auch teilen. Klicke dazu einfach auf den Teilen-Button: - -.. image:: ../../img/user/share.png - :alt: Artikel teilen - :align: center - -Nun kannst du den Artikel teilen: - -- über eine öffentliche URL (es wird eine reduzierte Ansicht des Artikels zurückgegeben) -- über einen Tweet -- in deine Shaarli -- mit einem Beitrag auf Diaspora* -- an Carrot -- mit einer E-Mail - -Artikel-Anmerkungen -------------------- - -In jedem Artikel, den du liest, kannst du Anmerkungen hinzufügen. Es ist einfacher mit ein paar Bilder erklärt. - -Wähle den Teil des Artikels aus, den du kommentieren willst und klicke auf den Bleistift: - -.. image:: ../../img/user/annotations_1.png - :alt: Wähle den Text - :align: center - -Schreibe deinen Kommentar: - -.. image:: ../../img/user/annotations_2.png - :alt: Schreibe deinen Kommentar - :align: center - -Der Text ist nun hervorgehoben und du kannst deine Anmerkung lesen, wenn du den Mauspfeil darüber fährst. - -.. image:: ../../img/user/annotations_3.png - :alt: lese deine Anmerkung - :align: center - -Du kannst so viele Anmerkungen erstellen wie du möchtest. diff --git a/docs/de/user/backup.rst b/docs/de/user/backup.rst deleted file mode 100644 index 521e17ad..00000000 --- a/docs/de/user/backup.rst +++ /dev/null @@ -1,25 +0,0 @@ -wallabag sichern -================ -Da es manchmal vorkommen kann, dass dir ein Fehler mit deiner wallabag unterläuft und du Daten verlierst oder deine wallabag auf einen anderen Server verschieben willst, ist eine Sicherung der Daten sicher ratsam. -Dieser Artikel beschreibt, was du für die Sicherung benötigst. - -Grundlegende Einstellungen --------------------------- -wallabag speichert grundlegende Parameter (etwa der SMTP-Server oder das Datenbank-Backend) in der Datei `app/config/parameters.yml`. - -Datenbank ---------- -Da wallabag verschiedene Datenbank-Typen unterstützt, hängt der Weg der Sicherung von dem verwendeten Typ ab. Daher verweisen wir an dieser Stelle auf die entsprechenden Dokumentationen: - -Hier sind einige Beispiele: - -- MySQL: http://dev.mysql.com/doc/refman/5.7/en/backup-methods.html -- PostgreSQL: https://www.postgresql.org/docs/current/static/backup.html - -SQLite -~~~~~~ -Um die SQLite-Datenbank zu sichern, ist es lediglich notwendig, das Verzeichnis `data/db` aus dem wallabag-Installations-Ordner zu kopieren. - -Bilder ------- -Die Bilder, die von wallabag empfangen worden, sind unter `web/assets/images` gespeichert (der Bilder-Speicher wird in wallabag 2.2 implementiert). diff --git a/docs/de/user/configuration.rst b/docs/de/user/configuration.rst deleted file mode 100644 index c0c04bde..00000000 --- a/docs/de/user/configuration.rst +++ /dev/null @@ -1,130 +0,0 @@ -Konfiguration -============= - -Nun, da du eingeloggt bist, ist es Zeit, deinen Account so zu konfigurieren, -wie du möchtest. - -Klicke auf ``Konfiguration`` im Menü. Du hast fünf Karteireiter: ``Einstellungen``, -``RSS``, ``Benutzer-Informationen``, ``Kennwort`` und ``Tagging-Regeln``. - -Einstellungen -------------- - -Theme -~~~~~ - -wallabag ist anpassbar. Du kannst dein bevorzugtes Theme hier auswählen. Das Standardtheme -ist ``Material``, es ist das Theme, dass in den Dokumentationsbildschirmfotos genutzt wird. - -Artikel pro Seite -~~~~~~~~~~~~~~~~~ - -Du kannst die Anzahl der dargestellten Artikel pro Seite ändern. - -Lesegeschwindigkeit -~~~~~~~~~~~~~~~~~~~ - -wallabag berechnet die Lesezeit für jeden Artikel. Du kannst hier definieren, dank dieser Liste, ob du -ein schneller oder langsamer Leser bist. wallabag wird die Lesezeit für jeden Artikel neu berechnen. - -Wohin möchtest du weitergeleitet werden, nach dem ein Artikel als gelesen markiert wurde? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Jedes Mal, wenn du eine Aktion ausführst (nach dem Markieren eines Artikels als gelesen oder Favorit, nach dem Löschen eines Artikels oder dem Entfernen eines Tag von einem Eintrag), kannst du weitergeleitet werden: - -- zur Homepage -- zur aktuellen Seite - -Sprache -~~~~~~~ - -Du kannst die Sprache von der wallabag Benutzeroberfläche ändern. - -RSS ---- - -wallabag stellt RSS Feeds für jeden Artikelstatus bereit: ungelesen, Favoriten und Archiv. - -Als erstes musst du einen persönlciehn Token erstellen: Klicke auf ``Token generieren``. -Es ist möglich deinen Token zu ändern, indem du auf ``Token zurücksetzen`` klickst. - -Jetzt hast du drei Links, einen für jeden Status: Füge sie in deinem liebsten Feedreader hinzu. - -Du kannst auch definieren wie viele Artikel du in deinem RSS Feed (Standardwert: 50) haben willst. - -There is also a pagination available for these feeds. You can add ``?page=2`` to jump to the second page. -The pagination follow `the RFC `_ about that, which means you'll find the ``next``, ``previous`` & ``last`` page link inside the `` tag of each RSS feed. - -Benutzer-Informationen ----------------------- - -Du kannst deinen Namen ändern, deine E-Mail-Adresse und die Zwei-Faktor-Authentifizierung aktivieren. - -Zwei-Faktor-Authentifizierung (2FA) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Die Zwei-Faktor-Authentifizierung (2FA) dient dem Identitätsnachweis eines Nutzers mittels der - Kombination zweier verschiedener und insbesondere unabhängiger Komponenten (Faktoren). - - https://de.wikipedia.org/wiki/Zwei-Faktor-Authentifizierung - -**Warnung:** Das Aktivieren von 2FA über das Konfigurations-Interface ist nur möglich, wenn vorher in der `app/config/parameters.yml` die `twofactor_auth`-Eigenschaft auf `true` gesetzt wurde (nach der Konfiguration das Leeren des Cache mit `php bin/console cache:clear -e=prod` nicht vergessen). - -Wenn du 2FA aktivierst, erhälst du jedes Mal, wenn du dich bei wallabag einloggen willst, einen Code per -Mail. Du musst den Code in das folgende Formular eingeben. - -.. image:: ../../img/user/2FA_form.png - :alt: Zwei-Faktor-Authentifizierung - :align: center - -Wenn du nicht jedes Mal, wenn du dich einloggen willst, einen Code zugesendet bekommen möchtest, kannst du -die Checkbox ``Ich bin an einem persönlichen Computer`` anhaken: wallabag wird sich an dich für 15 Tage -erinnern. - -Passwort --------- - -Du kannst dein Passwort hier ändern (8 Zeichen Minimum). - -Tagging-Regeln --------------- - -Wenn du automatisch einen Tag zu einem neuen Artikel zuweisen lassen möchtest, ist dieser Teil der -Konfiguration, was du suchst. - -Was ist mit Tagging-Regeln gemeint? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Dies sind Regeln, die von wallabag genutzt werden, um neue Artikel automatisch zu taggen -Jedes Mal, wenn ein neuer Artikel hinzugefügt wird, werden alle Tagging-Regeln genutzt, um deine -konfigurierten Tags hinzuzufügen, folglich um dir den Aufwand zu sparen, die Artikel manuell einzuteilen. - -Wie benutze ich sie? -~~~~~~~~~~~~~~~~~~~~ - -Nehmen wir an, du möchtest neuen Artikeln einen Tag *schnell gelesen*, wenn du die Lesezeit kleiner als -3 Minuten ist. -In diesem Fall solltest du in das Regelfeld "readingTime <= 3" eintragen und *schnell gelesen* in das Tags-Feld. -Mehrere Tags können gleichzeitig hinzugefügt werden, wenn man sie mit einem Komma trennt: -*schnell gelesen, Pflichtlektüre*. -Komplexe Regeln können mit vordefinierten Operatoren geschrieben werden: -Wenn *readingTime >= 5 AND domainName = "github.com"*, dann tagge als *lange zu lesen, github*. - -Welche Variablen und Operatoren kann ich zum Regeln schreiben nutzen? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Die folgenden Variablen und Operatoren können genutzt werden, um Tagging-Regeln zu erstellen (sei vorsichtig, denn bei einigen Werten musst du Anführungszeichen hinzufügen, z.B. ``language = "de"``): - -=========== ============================================== ======== ========== -Variable Bedeutung Operator Bedeutung ------------ ---------------------------------------------- -------- ---------- -title Titel des Artikels <= Kleiner gleich als… -url URL des Artikels < Kleiner als… -isArchived Ob der Artikel archiviert ist oder nicht => Größer gleich als… -isStarred Ob der Artikel favorisiert ist oder nicht > Größer als… -content Inhalt des Eintrags = Gleich zu… -language Sprache des Eintrags != Nicht gleich zu… -mimetype MIME-Typ des Eintrags OR Eine Regel oder die andere -readingTime Die geschätzte Lesezeit in Minuten AND Eine Regel und die andere -domainName Der Domain-Name des Eintrags matches Testet, dass ein Feld einer Suche (unabhängig von Groß- und Kleinschreibung) übereinstimmt. Z.B.: title matches "Fußball" -=========== ============================================== ======== ========== diff --git a/docs/de/user/configuring_mobile.rst b/docs/de/user/configuring_mobile.rst deleted file mode 100644 index c80320c9..00000000 --- a/docs/de/user/configuring_mobile.rst +++ /dev/null @@ -1,12 +0,0 @@ -Konfiguration mobiler Apps für wallabag -======================================= - -Schritte zum Konfigurieren der App ----------------------------------- - -- Als Erstes erstellst du einen *neuen Client* in dem Abschnitt ``API Client Management``. Der Name deiner App ist dabei nicht so wichtig wie die ``Client-ID`` und das ``Client-Secret``. Schreib diese zwei Werte auf. -- Wenn du eine mobile App herunterlädst, wird sie dich nach der Server-Adresse deiner wallabag-Instanz fragen. Zum Beispiel ist es ``https://app.wallabag.it`` bei wallabag.it. -- Sie wird dich auch nach den zuvor genannten ``Client-ID`` und ``Client-Secret`` fragen. Bitte trage diese in die Textfelder ein, wenn du danach gefragt wirst. -- Zuletzt musst du deinen ``Benutzernamen`` und ``Passwort`` bereitstellen. Diese Zugangsdaten sind die gleichen, die du zum Login bei wallabag nutzt. - -Bitte schaue auch auf die `Android `_ Seite und dort insbesondere den Abschnitt *Bekannte Limitierungen*. diff --git a/docs/de/user/create_account.rst b/docs/de/user/create_account.rst deleted file mode 100644 index 14a7d006..00000000 --- a/docs/de/user/create_account.rst +++ /dev/null @@ -1,41 +0,0 @@ -Account erstellen -================= - -Registrierungsformular ----------------------- - -Klicke auf der Loginseite auf den ``Registrieren`` Button- - -.. image:: ../../img/user/registration_form.png - :alt: Registrierungsformular - :align: center - -Du musst das Formular ausfüllen. Bitte stelle sicher, dass du eine gültige -E-Mail-Adresse eingibst, wir werden dir eine Aktivierungsmail schicken. - -.. image:: ../../img/user/sent_email.png - :alt: E-Mail wurde gesendet, um Account zu aktivieren - :align: center - -Überprüfge deinen Posteingang, du hast jetzt eine neue Mail mit einem Link wie diesen -``http://wallabag/register/confirm/Ba19wokGovN-DdBQNfg4YgRkUQWRP4-k2g0Bk-hBTX4``. -Klicke auf den Link, um deinen Account zu aktivieren. - -Dein Account ist nun aktiviert. - -.. image:: ../../img/user/activated_account.png - :alt: Willkommen! - :align: center - -Anmeldung ---------- - -Glückwunsch, dein Account ist nun aktiviert! -Um dich auf wallabag anzumelden, fülle das Formular auf der Login-Seite aus. - -Wenn du auf einem persönlichen Computer bist und angemeldet bleiben möchtest, -kannst du ein Häkchen nach ``angemeldet bleiben`` setzen, wallabag wird sich ein Jahr an dich erinnern. - -.. image:: ../../img/user/login_form.png - :alt: Login form - :align: center \ No newline at end of file diff --git a/docs/de/user/errors_during_fetching.rst b/docs/de/user/errors_during_fetching.rst deleted file mode 100644 index 29621559..00000000 --- a/docs/de/user/errors_during_fetching.rst +++ /dev/null @@ -1,28 +0,0 @@ -Fehler während des Artikelladens -================================ - -Warum schlägt das Laden eines Artikels fehl? --------------------------------------------- - -Das kann verschiedene Ursachen haben: - -- Netzwerkprobleme -- wallabag kann den Inhalt aufgrund der Websitestruktur nicht laden - -Wie kann ich helfen das zu beheben? ------------------------------------ - -- `indem du uns eine Mail mit der URL des Artikels sendest `_ -- indem du versuchst das Laden des Artikels durch Erstellen einer Datei für den Artikel - selbst zu beheben - Du kannst `dieses Tool `__ nutzen. - -Wie kann ich versuchen, einen Artikel erneut zu laden? ------------------------------------------------------- - -Wenn wallabag beim Laden eines Artikels fehlschlägt, kannst du auf den erneut laden Button -klicken (der dritte in dem unteren Bild). - -.. image:: ../../img/user/refetch.png - :alt: Inhalt neu laden - :align: center diff --git a/docs/de/user/faq.rst b/docs/de/user/faq.rst deleted file mode 100644 index c14cb3ef..00000000 --- a/docs/de/user/faq.rst +++ /dev/null @@ -1,52 +0,0 @@ -Häufig gestellte Fragen -========================== - -Während der Installation sehe ich den Fehler ``Error Output: sh: 1: @post-cmd: not found`` ------------------------------------------------------------------------------------------- - -Es scheint, dass du ein Problem bei deiner ``composer`` Installation hast. Versuche es zu deinstallieren und neu zu installieren. - -`Lies die Dokumentation über composer, um zu erfahren wie es installiert wird -`__. - -Ich kann das Registrierungsformular nicht validieren ----------------------------------------------------- - -Stelle sicher, dass alle Felder ausgefüllt sind: - -* valide E-Mail-Adresse -* das gleiche Passwort in zwei Feldern - -Ich erhalte meine Aktivierungsmail nicht ----------------------------------------- - -Bist du sicher, dass deine eingegebene E-Mail-Adresse korrekt war? Hast du deinen Spamordner überprüft? - -Wenn du dann immer noch nicht deine Aktivierungsmail siehst, stelle bitte sicher, dass du einen MTA -korrekt installiert und eingerichtet hast. Prüfe, dass deine Firewallregel existiert, z.B. für firewalld: - -:: - firewall-cmd --permanent --add-service=smtp - firewall-cmd --reload - -Schließlich, falls du SELinux aktiviert hast, setze folgende Regel: - -``setsebool -P httpd_can_sendmail 1`` - -Wenn ich den Aktivierungslink klicke, bekomme ich die Nachricht ``Der Nutzer mit dem Bestätigungstoken "DtrOPfbQeVkWf6N" existiert nicht`` ------------------------------------------------------------------------------------------------------------------------------------------- - -Du hast deinen Account schon aktiviert oder die URL der Aktivierungsmail ist falsch. - -Ich habe mein Passwort vergessen --------------------------------- - -Du kannst dein Passwort zurücksetzen, indem du auf den Link ``Kennwort vergessen?`` auf der Loginseite klickst. Fülle dann das Formular mit deiner E-Mail-Adresse oder deinem Nutzernamen aus -und du wirst eine E-Mail zum Passwort zurücksetzen erhalten. - -Ich erhalte den Fehler ``failed to load external entity``, wenn ich wallabag installiere ----------------------------------------------------------------------------------------- - -Wie `hier `_ beschrieben, bearbeite bitte deine Datei ``web/app.php`` und füge ihr diese Zeile ``libxml_disable_entity_loader(false);`` in Zeile 5 hinzu. - -Dies ist ein Doctrine / PHP Fehler - nichts, woran wir etwas ändern können. diff --git a/docs/de/user/filters.rst b/docs/de/user/filters.rst deleted file mode 100644 index 94b82b24..00000000 --- a/docs/de/user/filters.rst +++ /dev/null @@ -1,54 +0,0 @@ -Filter -====== - -Um Artikel leichter zu erreichen, kannst du sie filtern. -Klicke auf das dritte Symbol in der oberen Leiste. - -.. image:: ../../img/user/topbar.png - :alt: Obere Leiste - :align: center - -Alle diese Filter können kombiniert werden. - -.. image:: ../../img/user/filters.png - :alt: Kombiniere alle Filter - :align: center - -Status ------- - -Nutze die Checkboxen, um einen archivierten oder favorisierten Artikel zu finden. - -Vorschaubild ---------------- - -Hake diesen Filter an, wenn du Artikel mit einem Vorschaubild erhalten willst. - -Sprache -------- - -wallabag (via graby) kann die Artikelsprache erkennen. Es ist einfach für dich, Artikel -in einer bestimmten Sprache zu filtern. - -HTTP status ------------ - -You can retrieve the articles by filtering by their HTTP status code: 200, 404, 500, etc. - -Lesezeit --------- - -wallabag schätzt wieviel Zeit du brauchst, um einen Artikel zu lesen. Mit diesem Filter -kannst du zum Beispiel Artikel mit einer Lesezeit zwischen 2 und 5 Minuten finden. - -Domainname ----------- - -Dank dieses Filters, kannst ud Artikel von der gleichen Domain finden. -Zum Beispiel tippst du in diesem Feld ``bbc.co.uk``, um Artikel dieser Website zu finden. - -Erstellungsdatum ----------------- - -Wenn du Artikel speicherst, merkt sich wallabag das aktuelle Datum. So praktisch, um Aritkel, die -zwischen dem 1. und 31. Januar geschrieben sind, zu finden. diff --git a/docs/de/user/import.rst b/docs/de/user/import.rst deleted file mode 100644 index 399a1b98..00000000 --- a/docs/de/user/import.rst +++ /dev/null @@ -1,152 +0,0 @@ -Migration von einem Drittanbieter -================================= - -In wallabag 2.x kannst du Daten von folgenden Anbietern importieren: - -- Pocket <#id1>`_ -- Readability <#id2>`_ -- Instapaper <#id4>`_ -- wallabag 1.x <#id6>`_ -- wallabag 2.x <#id7>`_ - -Wir haben zusätzlich `ein Skript für die Migration per Kommandozeile <#import-via-command-line-interface-cli>`_ geschrieben. - -Da Importe eine Menge Zeit in Anspruch nehmen können, haben wir auch ein asynchrones Aufgabensystem entwickelt. `Du kannst die Dokumentation hier lesen `_ (für Experten). - -Pocket ------- - -Erstelle eine neue Applikation in Pocket -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Um deine Daten von Pocket zu importieren, nutzen wir die Pocket API. Du musst -eine neue Applikation auf ihrer Entwicklerwebsite erstellen, um fortzufahren. - -* Erstelle eine neue Applikation `auf der Entwicklerwebsite `_ -* Fülle die erforderlichen Felder aus: Name, Beschreibung, Berechtigungen (nur **abrufen**), Plattform - (**web**), akzeptiere die Nutzungsbedingungen und reiche deine neue Applikation ein - -Pocket wird dir einen **Consumer Key** geben (z.B. `49961-985e4b92fe21fe4c78d682c1`). -Du musst den ``pocket_consumer_key`` in dem Abschnitt ``Import`` in dem ``Interne Einstellungen`` Menü -konfigurieren. - -Jetzt ist alles in Ordnung, um von Pocket zu migrieren. - -Importiere deine Daten in wallabag 2.x -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Klicke auf den ``Importieren`` Link im Menü, auf ``Inhalte importieren`` in dem Pocketabschnitt und -dann auf ``Verbinde mit Pocket und importieren Daten``. - -Du musst wallabag erlauben, mit deinem Pocketaccount zu interagieren. -Deine Daten werden importiert. Datenimport kann ein sehr anspruchsvoller Prozess für deinen Server -sein (wir müssen daran arbeiten, um diesen Import zu verbessern). - -Von Readability ----------------- - -Exportiere deine Readability Daten -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Auf der Seite Tools (`https://www.readability.com/tools/ `_), klicke auf "Exportiere deine Daten" in dem Abschnitt "Daten Export". Du wirst eine E-Mail empfangen, um eine JSON Datei herunterladen zu können (Datei endet aber nicht auf .json). - -Importiere deine Daten in wallabag 2.x -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Klicke auf den ``Importieren`` Link im Menü, auf ``Importiere Inhalte`` in dem Readability Abschnitt und wähle dann deine JSON Datei aus und lade sie hoch. - -Deine Daten werden importiert. Der Datenimport can ein beanspruchender Prozess für deinen Server sein. - -Von Pinboard -------------- - -Exportiere deine Pinboard Daten -~~~~~~~~~~~~~~~~~~~~~~~~~ - -Auf der Seite Backup (`https://pinboard.in/settings/backup `_), klicke auf "JSON" in dem Abschnitt "Lesezeichen". Eine JSON Datei wird heruntergeladen (z.B. ``pinboard_export``). - -Importiere deine Daten in wallabag 2.x -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Klicke auf den ``Importieren`` Link im Menü, auf ``Importiere Inhalte`` in dem Pinboard Abschnitt und wähle dann deine JSON Datei aus und lade sie hoch. - -Deine Daten werden importiert. Der Datenimport can ein beanspruchender Prozess für deinen Server sein. - -Von Instapaper ---------------- - -Exportiere deine Instapaper Daten -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Auf der Seite Einstellungen (`https://www.instapaper.com/user `_), klicke auf "Download .CSV Datei" in dem Abschnitt "Export". Eine CSV Datei wird heruntergeladen (z.B. ``instapaper-export.csv``). - -Importiere deine Daten in wallabag 2.x -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Klicke auf den ``Importieren`` Link im Menü, auf ``Importiere Inhalte`` in dem Instapaper Abschnitt und wähle dann deine JSON Datei aus und lade sie hoch. - -Deine Daten werden importiert. Der Datenimport can ein beanspruchender Prozess für deinen Server sein. - -wallabag 1.x ------------- - -Wenn du in der Vergangenheit wallabag 1.x genutzt hast, musst du deine Daten exportieren, bevor du auf wallabag 2.x umsteigst, da sich viel an der Anwendung und der Datenbank geändert hast. In deiner alten wallabag-installation kannst du deine Daten exportieren, indem du die Konfigurationsseite auf der alten wallabag-Instanz öffnest. - -.. image:: ../../img/user/export_v1.png - :alt: Export aus wallabag 1.x - :align: center - -.. note:: - Wenn du mehrere Accounts auf der gleichen wallabag-Instanz hast, muss jeder Nutzer seine Daten aus 1.x exportieren und in 2.x importieren. - -.. note:: - Falls während des Exports oder des Imports Probleme auftreten sollten, scheue dich nicht, den `Support zu kontaktieren `__. - -Wenn du dann die JSON-Datei mit deinen Einträgen heruntergeladen hast, kannst du `wallabag 2 über die Standard-Prozedur installieren `__. - -Nach dem Erstellen des Benutzeraccounts auf deiner neuen "wallabag 2.x"-Instanz, navigiere auf den Import-Bereich und wähle `Aus wallabag v1 importieren`. Wähle deine JSON-Datei und lade sie hoch. - -.. image:: ../../img/user/import_wallabagv1.png - :alt: Import aus wallabag v1 - :align: center - -wallabag 2.x ------------- - -Gehe auf der alten wallabag-Instanz, die du vorher genutzt hast, auf `Alle Artikel` und exportiere diese dann als JSON. - -.. image:: ../../img/user/export_v2.png - :alt: Export aus wallabag v2 - :align: center - -Nach dem Erstellen des Benutzeraccounts auf deiner neuen "wallabag 2.x"-Instanz, navigiere auf den Import-Bereich und wähle `Aus wallabag v2 importieren`. Wähle deine JSON-Datei und lade sie hoch. - -.. note:: - Falls während des Exports oder des Imports Probleme auftreten sollten, scheue dich nicht, den `Support zu kontaktieren `__. - -Import über die Kommandozeile (CLI) ------------------------------------ - -Falls du auf deinem Server Zugriff auf die Kommandozeile hast, kannst du den folgenden Befehl ausführen, um deine Daten aus wallabag v1 zu importieren: - -:: - - bin/console wallabag:import 1 ~/Downloads/wallabag-export-1-2016-04-05.json --env=prod - -Bitte ersetze die Werte: - -* ``1`` ist die Benutzer-ID in der Datenbank (die ID des ersten Benutzers ist immer 1) -* ``~/Downloads/wallabag-export-1-2016-04-05.json`` ist der Pfad zu deiner wallabag v1-Exportdatei - -Wenn du alle Artikel als gelesen markieren möchtest, kannst du die ``--markAsRead``-Option hinzufügen. - -Um eine wallabag 2.x-Datei zu importieren, musst du die Option ``--importer=v2`` hinzufügen. - -Als Ergebnis wirst du so etwas erhalten: - -:: - - Start : 05-04-2016 11:36:07 --- - 403 imported - 0 already saved - End : 05-04-2016 11:36:09 --- diff --git a/docs/de/user/installation.rst b/docs/de/user/installation.rst deleted file mode 100644 index 0de6b6de..00000000 --- a/docs/de/user/installation.rst +++ /dev/null @@ -1,341 +0,0 @@ -Installation von wallabag -========================= - -Voraussetzungen ---------------- - -wallabag ist kompatibel mit PHP >= 5.5, inkl. PHP 7. - -.. note:: - - To install wallabag easily, we create a ``Makefile``, so you need to have the ``make`` tool. - -wallabag nutzt eine große Anzahl an Bibliotheken, um zu funktionieren. Diese Bibliotheken müssen mit einem Tool namens Composer installiert werden. Du musst es installieren sofern du es bisher noch nicht gemacht hast. - -Composer installieren: - -:: - - curl -s https://getcomposer.org/installer | php - -Du kannst eine spezifische Anleitung `hier `__ finden. - -Du benötigst die folgenden Extensions damit wallabag funktioniert. Einige von diesen sind vielleicht schon in deiner Version von PHP aktiviert, somit musst du eventuell -nicht alle folgenden Pakete installieren. - -- php-session -- php-ctype -- php-dom -- php-hash -- php-simplexml -- php-json -- php-gd -- php-mbstring -- php-xml -- php-tidy -- php-iconv -- php-curl -- php-gettext -- php-tokenizer -- php-bcmath - -wallabag nutzt PDO, um sich mit der Datenbank zu verbinden, darum benötigst du eines der folgenden Komponenten: - -- pdo_mysql -- pdo_sqlite -- pdo_pgsql - -und dessen dazugehörigen Datenbankserver. - -Installation ------------- - -Auf einem dedizierten Webserver (empfohlener Weg) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Um wallabag selbst zu installieren, musst du die folgenden Kommandos ausführen: - -:: - - git clone https://github.com/wallabag/wallabag.git - cd wallabag && make install - -Um PHPs eingebauten Server zu starten und zu testen, ob alles korrekt installiert wurde, kannst du folgendes Kommando ausführen: - -:: - - make run - -und wallabag unter http://deineserverip:8000 erreichen. - -.. tip:: - - Um Parameter mit Umgebungsvariable zu definieren, musst du die Variable mit dem ``SYMFONY__`` Präfix setzen. Zum Beispiel ``SYMFONY__DATABASE_DRIVER``. Du kannst einen Blick die `Symfony Dokumentation `__ werfen. - -Auf einem geteilten Webhosting -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Wir stellen ein Paket inkl. aller Abhängigkeiten bereit. -Die Standardkonfiguration nutzt SQLite für die Datenbank. Wenn du diese Einstellung ändern willst, ändere bitte ``app/config/parameters.yml``. - -Wir haben bereits einen Nutzer erstellt: Login und Passwort sind ``wallabag``. - -.. caution:: Mit diesem Paket überprüft wallabag nicht die von der Applikation gebrauchten Exentions (diese Tests werden während ``composer install`` durchgeführt wenn du einen dedizierten Webserver hast, siehe oben). - -Führe dieses Kommando aus, um das neueste Paket herunterzuladen und zu entpacken: - -.. code-block:: bash - - wget https://wllbg.org/latest-v2-package && tar xvf latest-v2-package - -Du findest die `md5 Hashsumme des neuesten Pakets auf unserer Website `_. - -Jetzt lies die Dokumentation, um einen Virtualhost zu erstellen, dann greife auf dein wallabag zu. -Wenn du die Datenbankkonfiguration eingestellt hast, MySQL oder PostgreSQL zu nutzen, musst du einen Nutzer über das folgende Kommando erstellen ``php bin/console wallabag:install --env=prod``. - -Installation mit Docker -~~~~~~~~~~~~~~~~~~~~~~~ - -Wir stellen ein Docker Image zu Verfügung, um wallabag einfach zu installieren. Schaue in unser Repository in unserem `Docker Hub `__, um mehr Informationen zu erhalten. - -Kommando, um den Container zu starten -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: bash - - docker pull wallabag/wallabag - -Virtualhosts ------------- - -Konfiguration von Apache -~~~~~~~~~~~~~~~~~~~~~~~~ - -Vergiss nicht, die *rewrite* mod von Apache zu aktivieren. - -.. code-block:: bash - - a2enmod rewrite && systemctl reload apache2 - -Angenommen du willst wallabag in das Verzeichnis ``/var/www/wallabag`` installieren und du willst PHP als Apache Modul nutzen, dann ist hier ein vhost für wallabag: - -:: - - - ServerName domain.tld - ServerAlias www.domain.tld - - DocumentRoot /var/www/wallabag/web - - AllowOverride None - Order Allow,Deny - Allow from All - - - Options -MultiViews - RewriteEngine On - RewriteCond %{REQUEST_FILENAME} !-f - RewriteRule ^(.*)$ app.php [QSA,L] - - - - # uncomment the following lines if you install assets as symlinks - # or run into problems when compiling LESS/Sass/CoffeScript assets - # - # Options FollowSymlinks - # - - # optionally disable the RewriteEngine for the asset directories - # which will allow apache to simply reply with a 404 when files are - # not found instead of passing the request into the full symfony stack - - - RewriteEngine Off - - - ErrorLog /var/log/apache2/wallabag_error.log - CustomLog /var/log/apache2/wallabag_access.log combined - - -Nach dem du Apache neugeladen oder neugestartet hast, solltest du nun wallabag unter http://domain.tld erreichen. - -Konfiguration von Nginx -~~~~~~~~~~~~~~~~~~~~~~~ - -Angenommen du willst wallabag in das Verzeichnis ``/var/www/wallabag`` installieren, dann ist hier ein Rezept für wallabag: - -:: - - server { - server_name domain.tld www.domain.tld; - root /var/www/wallabag/web; - - location / { - # try to serve file directly, fallback to app.php - try_files $uri /app.php$is_args$args; - } - location ~ ^/app\.php(/|$) { - fastcgi_pass unix:/var/run/php5-fpm.sock; - 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; - } - - # return 404 for all other php files not matching the front controller - # this prevents access to other php files you don't want to be accessible. - location ~ \.php$ { - return 404; - } - - error_log /var/log/nginx/wallabag_error.log; - access_log /var/log/nginx/wallabag_access.log; - } - -Nach dem Neuladen oder Neustarten von nginx solltest du nun wallabag unter http://domain.tld erreichen. - -.. tip:: - - Wenn du eine große Datei in wallabag importieren willst, solltest du diese Zeile zu deiner nginx Konfiguration hinzufügen ``client_max_body_size XM; # allows file uploads up to X megabytes``. - -Konfiguration von lighttpd -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Angenommen du willst wallabag in das Verzeichnis ``/var/www/wallabag`` installieren, dann ist hier ein Rezept für wallabag (bearbeite deine ``lighttpd.conf`` und füge die Konfiguration dort ein): - -:: - - server.modules = ( - "mod_fastcgi", - "mod_access", - "mod_alias", - "mod_compress", - "mod_redirect", - "mod_rewrite", - ) - server.document-root = "/var/www/wallabag/web" - server.upload-dirs = ( "/var/cache/lighttpd/uploads" ) - server.errorlog = "/var/log/lighttpd/error.log" - server.pid-file = "/var/run/lighttpd.pid" - server.username = "www-data" - server.groupname = "www-data" - server.port = 80 - server.follow-symlink = "enable" - index-file.names = ( "index.php", "index.html", "index.lighttpd.html") - url.access-deny = ( "~", ".inc" ) - static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" ) - compress.cache-dir = "/var/cache/lighttpd/compress/" - compress.filetype = ( "application/javascript", "text/css", "text/html", "text/plain" ) - include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port - include_shell "/usr/share/lighttpd/create-mime.assign.pl" - include_shell "/usr/share/lighttpd/include-conf-enabled.pl" - dir-listing.activate = "disable" - - url.rewrite-if-not-file = ( - "^/([^?]*)(?:\?(.*))?" => "/app.php?$1&$2", - "^/([^?]*)" => "/app.php?=$1", - ) - -Rechte, um das Projektverzeichnis zu betreten ---------------------------------------------- - -Testumgebung -~~~~~~~~~~~~ - -Wenn wir nur wallabag testen wollen, führen wir nur das Kommando ``php bin/console server:run --env=prod`` aus, um unsere wallabag Instanz zu starten und alles wird geschmeidig laufen, weil der Nutzer, der das Projekt gestartet hat, den aktuellen Ordner ohne Probleme betreten kann. - -Produktionsumgebung -~~~~~~~~~~~~~~~~~~~ - -Sobald wir Apache oder Nginx nutzen, um unsere wallabag Instanz zu erreichen, und nicht das Kommando ``php bin/console server:run --env=prod`` nutzen, sollten wir dafür sorgen, die Rechte vernünftig zu vergeben, um die Ordner des Projektes zu schützen. - -Um dies zu machen, muss der Ordner, bekannt als ``DocumentRoot`` (bei Apache) oder ``root`` (bei Nginx), von dem Apache-/Nginx-Nutzer zugänglich sein. Sein Name ist meist ``www-data``, ``apache`` oder ``nobody`` (abhängig vom genutzten Linuxsystem). - -Der Ordner ``/var/www/wallabag/web`` musst dem letztgenannten zugänglich sein. Aber dies könnte nicht genug sein, wenn wir nur auf diesen Ordner achten, weil wir eine leere Seite sehen könnten oder einen Fehler 500, wenn wir die Homepage des Projekt öffnen. - -Dies kommt daher, dass wir die gleichen Rechte dem Ordner ``/var/www/wallabag/var`` geben müssen, so wie wir es für den Ordner ``/var/www/wallabag/web`` gemacht haben. Somit beheben wir das Problem mit dem folgenden Kommando: - -.. code-block:: bash - - chown -R www-data:www-data /var/www/wallabag/var - -Es muss analog für die folgenden Ordner ausgeführt werden - -* /var/www/wallabag/bin/ -* /var/www/wallabag/app/config/ -* /var/www/wallabag/vendor/ -* /var/www/wallabag/data/ - -durch Eingabe der Kommandos - -.. code-block:: bash - - chown -R www-data:www-data /var/www/wallabag/bin - chown -R www-data:www-data /var/www/wallabag/app/config - chown -R www-data:www-data /var/www/wallabag/vendor - chown -R www-data:www-data /var/www/wallabag/data/ - -ansonsten wirst du früher oder später folgenden Fehlermeldung sehen: - -.. code-block:: bash - - Unable to write to the "bin" directory. - file_put_contents(app/config/parameters.yml): failed to open stream: Permission denied - file_put_contents(/.../wallabag/vendor/autoload.php): failed to open stream: Permission denied - -Zusätzliche Regeln für SELinux -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Wenn SELinux in deinem System aktiviert ist, wirst du zusätzliche Kontexte konfigurieren müssen damit wallabag ordentlich funktioniert. Um zu testen, ob SELinux aktiviert ist, führe einfach folgendes aus: - -``getenforce`` - -Dies wird ``Enforcing`` ausgeben, wenn SELinux aktiviert ist. Einen neuen Kontext zu erstellen, erfordert die folgende Syntax: - -``semanage fcontext -a -t `` - -Zum Beispiel: - -``semanage fcontext -a -t httpd_sys_content_t "/var/www/wallabag(/.*)?"`` - -Dies wird rekursiv den httpd_sys_content_t Kontext auf das wallabag Verzeichnis und alle darunterliegenden Dateien und Ordner anwenden. Die folgenden Regeln werden gebraucht: - -+-----------------------------------+----------------------------+ -| Vollständiger Pfad | Kontext | -+===================================+============================+ -| /var/www/wallabag(/.*)? | ``httpd_sys_content_t`` | -+-----------------------------------+----------------------------+ -| /var/www/wallabag/data(/.*)? | ``httpd_sys_rw_content_t`` | -+-----------------------------------+----------------------------+ -| /var/www/wallabag/var/logs(/.*)? | ``httpd_log_t`` | -+-----------------------------------+----------------------------+ -| /var/www/wallabag/var/cache(/.*)? | ``httpd_cache_t`` | -+-----------------------------------+----------------------------+ - -Nach dem diese Kontexte erstellt wurden, tippe das folgende, um deine Regeln anzuwenden: - -``restorecon -R -v /var/www/wallabag`` - -Du kannst deine Kontexte in einem Verzeichnis überprüfen, indem du ``ls -lZ`` tippst und alle deine aktuellen Regeln mit ``semanage fcontext -l -C`` überprüfst. - -Wenn du das vorkonfigurierte latest-v2-package installierst, dann ist eine weitere Regel während der Installation nötig: - -``semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/wallabag/var"`` - -Nachdem du erfolgreich dein wallabag erreichst und die Installation fertiggestellt hast, kann dieser Kontext entfernt werden: - -:: - - semanage fcontext -d -t httpd_sys_rw_content_t "/var/www/wallabag/var" - retorecon -R -v /var/www/wallabag/var diff --git a/docs/de/user/parameters.rst b/docs/de/user/parameters.rst deleted file mode 100644 index 8d8f9206..00000000 --- a/docs/de/user/parameters.rst +++ /dev/null @@ -1,95 +0,0 @@ -Was bedeuten die Parameter? -=========================== - -Standardeinstellungen der `parameters.yml` ------------------------------------------- - -Dies ist die letzte standardisierte Version der `app/config/parameters.yml`-Datei. Stelle sicher, dass sich deine mit dieser ähnelt. -Wenn du nicht weißt, welchen Wert du setzen sollst, belasse es bei dem Standardwert. - -.. code-block:: yml - - parameters: - database_driver: pdo_sqlite - database_host: 127.0.0.1 - database_port: null - database_name: symfony - database_user: root - database_password: null - database_path: '%kernel.root_dir%/../data/db/wallabag.sqlite' - database_table_prefix: wallabag_ - database_socket: null - mailer_transport: smtp - mailer_host: 127.0.0.1 - mailer_user: null - mailer_password: null - locale: en - secret: ovmpmAWXRCabNlMgzlzFXDYmCFfzGv - twofactor_auth: true - twofactor_sender: no-reply@wallabag.org - fosuser_registration: true - fosuser_confirmation: true - from_email: no-reply@wallabag.org - rss_limit: 50 - rabbitmq_host: localhost - rabbitmq_port: 5672 - rabbitmq_user: guest - rabbitmq_password: guest - redis_scheme: tcp - redis_host: localhost - redis_port: 6379 - redis_path: null - redis_password: null - -Bedeutung von jedem Parameter ------------------------------ - -.. csv-table:: Datenbankparameter - :header: "Name", "Standardwert", "Beschreibung" - - "database_driver", "pdo_sqlite", "Sollte pdo_sqlite oder pdo_mysql oder pdo_pgsql sein" - "database_host", "127.0.0.1", "Hostadresse deiner Datenbank (normalerweise localhost oder 127.0.0.1)" - "database_port", "~", "Port deiner Datenbank (Du kannst ``~`` stehen lassen, um den Standardport zu nutzen)" - "database_name", "symfony", "Benenne deine Datenbank" - "database_user", "root", "Benutzer, der Schreibrecht in der Datenbank hat" - "database_password", "~", "Passwort des Benutzers" - "database_path", "``""%kernel.root_dir%/../data/db/wallabag.sqlite""``", "nur für SQLite, definiere, wo die Datenbankdatei abgelegt werden soll. Lass den Parameter leer für andere Datenbanktypen." - "database_table_prefix", "wallabag_", "alle wallabag Tabellen erhalten diesen Präfix im Namen. Du kannst einen ``_`` dafür im Präfix nutzen, um das zu verdeutlichen." - "database_socket", "null", "Wenn deine Datenbank einen Socket statt TCP nutzt, schreibe hier den Pfad zum Socket hin (andere Verbindungsparameter werden dann ignoriert." - -.. csv-table:: Konfiguration, um mit wallabag E-Mails senden zu können - :header: "Name", "Standardwert", "Beschreibung" - - "mailer_transport", "smtp", "Die exakte Transportmethode, um E-Mails zuzustellen. Gültige Werte sind: smtp, gmail, mail, sendmail, null (was das Mailen deaktivert)" - "mailer_host", "127.0.0.1", "Der Host, zu dem sich verbunden wird, wenn SMTP als Transport genutzt wird." - "mailer_user", "~", "Der Benutzername, wenn SMTP als Transport genutzt wird." - "mailer_password", "~", "Das Passwort, wenn SMTP als Transport genutzt wird." - -.. csv-table:: Andere wallabag Optionen - :header: "Name", "Standardwert", "Beschreibung" - - "locale", "en", "Standardsprache deiner wallabag Instanz (wie z.B. en, fr, es, etc.)" - "secret", "ovmpmAWXRCabNlMgzlzFXDYmCFfzGv", "Dieser String sollte einzigartig für deine Applikation sein und er wird genutzt, um sicherheitsrelevanten Operationen mehr Entropie hinzuzufügen." - "twofactor_auth", "true", "true, um Zwei-Faktor-Authentifizierung zu aktivieren" - "twofactor_sender", "no-reply@wallabag.org", "E-Mail-Adresse des Senders der Mails mit dem Code für die Zwei-Faktor-Authentifizierung" - "fosuser_registration", "true", "true, um die Registrierung für jedermann zu aktivieren" - "fosuser_confirmation", "true", "true, um eine Bestätigungsmail für jede Registrierung zu senden" - "from_email", "no-reply@wallabag.org", "E-Mail-Adresse, die im Absenderfeld jeder Mail genutzt wird" - "rss_limit", "50", "Artikellimit für RSS Feeds" - -.. csv-table:: RabbitMQ Konfiguration - :header: "Name", "Standardwert", "Beschreibung" - - "rabbitmq_host", "localhost", "Host deines RabbitMQ" - "rabbitmq_port", "5672", "Port deines RabbitMQ" - "rabbitmq_user", "guest", "Benutzer, der die Queue lesen kann" - "rabbitmq_password", "guest", "Passwort dieses Benutzers" - -.. csv-table:: Redis Konfiguration - :header: "Name", "Standardwert", "Beschreibung" - - "redis_scheme", "tcp", "Bestimmt das Protokoll, dass genutzt wird, um mit Redis zu kommunizieren. Gültige Werte sind: tcp, unix, http" - "redis_host", "localhost", "IP oder Hostname des Zielservers (ignoriert bei Unix Schema)" - "redis_port", "6379", "TCP/IP Port des Zielservers (ignoriert bei Unix Schema)" - "redis_path", "null", "Pfad zur Unix Domain Socket Datei, wenn Redis Unix Domain Sockets nutzt" - "redis_password", "null", "Kennwort, welches in der Redis-Server-Konfiguration definiert ist (Parameter `requirepass` in `redis.conf`)" diff --git a/docs/de/user/query-upgrade-21-22.rst b/docs/de/user/query-upgrade-21-22.rst deleted file mode 100644 index fa9835a8..00000000 --- a/docs/de/user/query-upgrade-21-22.rst +++ /dev/null @@ -1,984 +0,0 @@ -Migration 20161001072726 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_entry_tag DROP FOREIGN KEY FK_C9F0DD7CBA364942 - ALTER TABLE wallabag_entry_tag DROP FOREIGN KEY FK_C9F0DD7CBAD26311 - ALTER TABLE wallabag_entry_tag ADD CONSTRAINT FK_entry_tag_entry FOREIGN KEY (entry_id) REFERENCES wallabag_entry (id) ON DELETE CASCADE - ALTER TABLE wallabag_entry_tag ADD CONSTRAINT FK_entry_tag_tag FOREIGN KEY (tag_id) REFERENCES wallabag_tag (id) ON DELETE CASCADE - ALTER TABLE wallabag_annotation DROP FOREIGN KEY FK_A7AED006BA364942 - ALTER TABLE wallabag_annotation ADD CONSTRAINT FK_annotation_entry FOREIGN KEY (entry_id) REFERENCES wallabag_entry (id) ON DELETE CASCADE - -Migration down -"""""""""""""" - -We didn't write down migration for ``20161001072726``. - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_entry_tag DROP CONSTRAINT fk_c9f0dd7cba364942 - ALTER TABLE wallabag_entry_tag DROP CONSTRAINT fk_c9f0dd7cbad26311 - ALTER TABLE wallabag_entry_tag ADD CONSTRAINT FK_entry_tag_entry FOREIGN KEY (entry_id) REFERENCES wallabag_entry (id) ON DELETE CASCADE - ALTER TABLE wallabag_entry_tag ADD CONSTRAINT FK_entry_tag_tag FOREIGN KEY (tag_id) REFERENCES wallabag_tag (id) ON DELETE CASCADE - ALTER TABLE wallabag_annotation DROP CONSTRAINT fk_a7aed006ba364942 - ALTER TABLE wallabag_annotation ADD CONSTRAINT FK_annotation_entry FOREIGN KEY (entry_id) REFERENCES wallabag_entry (id) ON DELETE CASCADE - -Migration down -"""""""""""""" - -We didn't write down migration for ``20161001072726``. - -SQLite -^^^^^^ - -This migration can only be executed safely on MySQL or PostgreSQL. - -Migration 20161022134138 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER DATABASE wallabag CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; - ALTER TABLE wallabag_user CHANGE confirmation_token confirmation_token VARCHAR(180) DEFAULT NULL; - ALTER TABLE wallabag_user CHANGE salt salt VARCHAR(180) NOT NULL; - ALTER TABLE wallabag_user CHANGE password password VARCHAR(180) NOT NULL; - ALTER TABLE wallabag_annotation CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - ALTER TABLE wallabag_entry CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - ALTER TABLE wallabag_tag CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - ALTER TABLE wallabag_user CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - ALTER TABLE wallabag_annotation CHANGE `text` `text` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - ALTER TABLE wallabag_annotation CHANGE `quote` `quote` VARCHAR(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - ALTER TABLE wallabag_entry CHANGE `title` `title` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - ALTER TABLE wallabag_entry CHANGE `content` `content` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - ALTER TABLE wallabag_tag CHANGE `label` `label` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - ALTER TABLE wallabag_user CHANGE `name` `name` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER DATABASE wallabag CHARACTER SET = utf8 COLLATE = utf8_unicode_ci; - ALTER TABLE wallabag_annotation CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci; - ALTER TABLE wallabag_entry CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci; - ALTER TABLE wallabag_tag CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci; - ALTER TABLE wallabag_user CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci; - ALTER TABLE wallabag_annotation CHANGE `text` `text` longtext CHARACTER SET utf8 COLLATE utf8_unicode_ci; - ALTER TABLE wallabag_annotation CHANGE `quote` `quote` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci; - ALTER TABLE wallabag_entry CHANGE `title` `title` longtext CHARACTER SET utf8 COLLATE utf8_unicode_ci; - ALTER TABLE wallabag_entry CHANGE `content` `content` longtext CHARACTER SET utf8 COLLATE utf8_unicode_ci; - ALTER TABLE wallabag_tag CHANGE `label` `label` longtext CHARACTER SET utf8 COLLATE utf8_unicode_ci; - ALTER TABLE wallabag_user CHANGE `name` `name` longtext CHARACTER SET utf8 COLLATE utf8_unicode_ci; - -PostgreSQL and SQLite -^^^^^^^^^^^^^^^^^^^^^ - -This migration only apply to MySQL. - -Migration 20161024212538 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_oauth2_clients ADD user_id INT NOT NULL - ALTER TABLE wallabag_oauth2_clients ADD CONSTRAINT IDX_user_oauth_client FOREIGN KEY (user_id) REFERENCES wallabag_user (id) ON DELETE CASCADE - CREATE INDEX IDX_635D765EA76ED395 ON wallabag_oauth2_clients (user_id) - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_oauth2_clients DROP FOREIGN KEY IDX_user_oauth_client - ALTER TABLE wallabag_oauth2_clients DROP user_id - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_oauth2_clients ADD user_id INT DEFAULT NULL - ALTER TABLE wallabag_oauth2_clients ADD CONSTRAINT IDX_user_oauth_client FOREIGN KEY (user_id) REFERENCES wallabag_user (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE - CREATE INDEX IDX_635D765EA76ED395 ON wallabag_oauth2_clients (user_id) - - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_oauth2_clients DROP CONSTRAINT idx_user_oauth_client - ALTER TABLE wallabag_oauth2_clients DROP user_id - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - CREATE TEMPORARY TABLE __temp__wallabag_oauth2_clients AS SELECT id, random_id, redirect_uris, secret, allowed_grant_types, name FROM wallabag_oauth2_clients - DROP TABLE wallabag_oauth2_clients - CREATE TABLE wallabag_oauth2_clients (id INTEGER NOT NULL, user_id INTEGER DEFAULT NULL, random_id VARCHAR(255) NOT NULL COLLATE BINARY, redirect_uris CLOB NOT NULL COLLATE BINARY, secret VARCHAR(255) NOT NULL COLLATE BINARY, allowed_grant_types CLOB NOT NULL COLLATE BINARY, name CLOB DEFAULT NULL COLLATE BINARY, PRIMARY KEY(id), CONSTRAINT IDX_user_oauth_client FOREIGN KEY (user_id) REFERENCES wallabag_user (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE) - INSERT INTO wallabag_oauth2_clients (id, random_id, redirect_uris, secret, allowed_grant_types, name) SELECT id, random_id, redirect_uris, secret, allowed_grant_types, name FROM __temp__wallabag_oauth2_clients - DROP TABLE __temp__wallabag_oauth2_clients - CREATE INDEX IDX_635D765EA76ED395 ON wallabag_oauth2_clients (user_id) - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX IDX_635D765EA76ED395 - CREATE TEMPORARY TABLE __temp__wallabag_oauth2_clients AS SELECT id, random_id, redirect_uris, secret, allowed_grant_types, name FROM wallabag_oauth2_clients - DROP TABLE wallabag_oauth2_clients - CREATE TABLE wallabag_oauth2_clients (id INTEGER NOT NULL, random_id VARCHAR(255) NOT NULL COLLATE BINARY, redirect_uris CLOB NOT NULL COLLATE BINARY, secret VARCHAR(255) NOT NULL COLLATE BINARY, allowed_grant_types CLOB NOT NULL COLLATE BINARY, name CLOB DEFAULT NULL COLLATE BINARY, PRIMARY KEY(id)) - INSERT INTO wallabag_oauth2_clients (id, random_id, redirect_uris, secret, allowed_grant_types, name) SELECT id, random_id, redirect_uris, secret, allowed_grant_types, name FROM __temp__wallabag_oauth2_clients - DROP TABLE __temp__wallabag_oauth2_clients - -Migration 20161031132655 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('download_images_enabled', 0, 'misc') - -Migration down -"""""""""""""" - -.. code-block:: sql - - DELETE FROM wallabag_craue_config_setting WHERE name = 'download_images_enabled'; - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('download_images_enabled', 0, 'misc') - -Migration down -"""""""""""""" - -.. code-block:: sql - - DELETE FROM wallabag_craue_config_setting WHERE name = 'download_images_enabled'; - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('download_images_enabled', 0, 'misc') - -Migration down -"""""""""""""" - -.. code-block:: sql - - DELETE FROM wallabag_craue_config_setting WHERE name = 'download_images_enabled'; - -Migration 20161104073720 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - CREATE INDEX IDX_entry_created_at ON wallabag_entry (created_at) - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX IDX_entry_created_at ON wallabag_entry - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - CREATE INDEX IDX_entry_created_at ON wallabag_entry (created_at) - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX idx_entry_created_at - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - DROP INDEX created_at_idx - DROP INDEX IDX_F4D18282A76ED395 - CREATE TEMPORARY TABLE __temp__wallabag_entry AS SELECT id, user_id, uuid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public FROM wallabag_entry - DROP TABLE wallabag_entry - CREATE TABLE wallabag_entry (id INTEGER NOT NULL, user_id INTEGER DEFAULT NULL, uuid CLOB DEFAULT NULL COLLATE BINARY, title CLOB DEFAULT NULL COLLATE BINARY, url CLOB DEFAULT NULL COLLATE BINARY, is_archived BOOLEAN NOT NULL, is_starred BOOLEAN NOT NULL, content CLOB DEFAULT NULL COLLATE BINARY, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, mimetype CLOB DEFAULT NULL COLLATE BINARY, language CLOB DEFAULT NULL COLLATE BINARY, reading_time INTEGER DEFAULT NULL, domain_name CLOB DEFAULT NULL COLLATE BINARY, preview_picture CLOB DEFAULT NULL COLLATE BINARY, is_public BOOLEAN DEFAULT '0', PRIMARY KEY(id)) - INSERT INTO wallabag_entry (id, user_id, uuid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public) SELECT id, user_id, uuid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public FROM __temp__wallabag_entry - DROP TABLE __temp__wallabag_entry - CREATE INDEX created_at_idx ON wallabag_entry (created_at) - CREATE INDEX IDX_F4D18282A76ED395 ON wallabag_entry (user_id) - CREATE INDEX IDX_entry_created_at ON wallabag_entry (created_at) - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX IDX_entry_created_at - DROP INDEX IDX_F4D18282A76ED395 - DROP INDEX created_at_idx - CREATE TEMPORARY TABLE __temp__wallabag_entry AS SELECT id, user_id, uuid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public FROM wallabag_entry - DROP TABLE wallabag_entry - CREATE TABLE wallabag_entry (id INTEGER NOT NULL, user_id INTEGER DEFAULT NULL, uuid CLOB DEFAULT NULL COLLATE BINARY, title CLOB DEFAULT NULL COLLATE BINARY, url CLOB DEFAULT NULL COLLATE BINARY, is_archived BOOLEAN NOT NULL, is_starred BOOLEAN NOT NULL, content CLOB DEFAULT NULL COLLATE BINARY, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, mimetype CLOB DEFAULT NULL COLLATE BINARY, language CLOB DEFAULT NULL COLLATE BINARY, reading_time INTEGER DEFAULT NULL, domain_name CLOB DEFAULT NULL COLLATE BINARY, preview_picture CLOB DEFAULT NULL COLLATE BINARY, is_public BOOLEAN DEFAULT '0', PRIMARY KEY(id)) - INSERT INTO wallabag_entry (id, user_id, uuid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public) SELECT id, user_id, uuid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public FROM __temp__wallabag_entry - DROP TABLE __temp__wallabag_entry - CREATE INDEX IDX_F4D18282A76ED395 ON wallabag_entry (user_id) - CREATE INDEX created_at_idx ON wallabag_entry (created_at) - -Migration 20161106113822 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_config ADD action_mark_as_read INT DEFAULT 0 - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_config DROP action_mark_as_read - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_config ADD action_mark_as_read INT DEFAULT 0 - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_config DROP action_mark_as_read - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_config ADD COLUMN action_mark_as_read INTEGER DEFAULT 0 - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX UNIQ_87E64C53A76ED395 - CREATE TEMPORARY TABLE __temp__wallabag_config AS SELECT id, user_id, theme, items_per_page, language, rss_token, rss_limit, reading_speed, pocket_consumer_key FROM wallabag_config - DROP TABLE wallabag_config - CREATE TABLE wallabag_config (id INTEGER NOT NULL, user_id INTEGER DEFAULT NULL, theme VARCHAR(255) NOT NULL COLLATE BINARY, items_per_page INTEGER NOT NULL, language VARCHAR(255) NOT NULL COLLATE BINARY, rss_token VARCHAR(255) DEFAULT NULL COLLATE BINARY, rss_limit INTEGER DEFAULT NULL, reading_speed DOUBLE PRECISION DEFAULT NULL, pocket_consumer_key VARCHAR(255) DEFAULT NULL COLLATE BINARY, PRIMARY KEY(id)) - INSERT INTO wallabag_config (id, user_id, theme, items_per_page, language, rss_token, rss_limit, reading_speed, pocket_consumer_key) SELECT id, user_id, theme, items_per_page, language, rss_token, rss_limit, reading_speed, pocket_consumer_key FROM __temp__wallabag_config - DROP TABLE __temp__wallabag_config - CREATE UNIQUE INDEX UNIQ_87E64C53A76ED395 ON wallabag_config (user_id) - -Migration 20161117071626 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('share_unmark', 0, 'entry') - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('unmark_url', 'https://unmark.it', 'entry') - -Migration down -"""""""""""""" - -.. code-block:: sql - - DELETE FROM wallabag_craue_config_setting WHERE name = 'share_unmark'; - DELETE FROM wallabag_craue_config_setting WHERE name = 'unmark_url'; - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('share_unmark', 0, 'entry') - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('unmark_url', 'https://unmark.it', 'entry') - -Migration down -"""""""""""""" - -.. code-block:: sql - - DELETE FROM wallabag_craue_config_setting WHERE name = 'share_unmark'; - DELETE FROM wallabag_craue_config_setting WHERE name = 'unmark_url'; - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('share_unmark', 0, 'entry') - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('unmark_url', 'https://unmark.it', 'entry') - -Migration down -"""""""""""""" - -.. code-block:: sql - - DELETE FROM wallabag_craue_config_setting WHERE name = 'share_unmark'; - DELETE FROM wallabag_craue_config_setting WHERE name = 'unmark_url'; - -Migration 20161118134328 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_entry ADD http_status VARCHAR(3) DEFAULT NULL - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_entry DROP http_status - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_entry ADD http_status VARCHAR(3) DEFAULT NULL - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_entry DROP http_status - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_entry ADD COLUMN http_status VARCHAR(3) DEFAULT NULL - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX created_at_idx - DROP INDEX IDX_F4D18282A76ED395 - CREATE TEMPORARY TABLE __temp__wallabag_entry AS SELECT id, user_id, uuid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public FROM wallabag_entry - DROP TABLE wallabag_entry - CREATE TABLE wallabag_entry (id INTEGER NOT NULL, user_id INTEGER DEFAULT NULL, uuid CLOB DEFAULT NULL COLLATE BINARY, title CLOB DEFAULT NULL COLLATE BINARY, url CLOB DEFAULT NULL COLLATE BINARY, is_archived BOOLEAN NOT NULL, is_starred BOOLEAN NOT NULL, content CLOB DEFAULT NULL COLLATE BINARY, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, mimetype CLOB DEFAULT NULL COLLATE BINARY, language CLOB DEFAULT NULL COLLATE BINARY, reading_time INTEGER DEFAULT NULL, domain_name CLOB DEFAULT NULL COLLATE BINARY, preview_picture CLOB DEFAULT NULL COLLATE BINARY, is_public BOOLEAN DEFAULT '0', PRIMARY KEY(id)) - INSERT INTO wallabag_entry (id, user_id, uuid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public) SELECT id, user_id, uuid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public FROM __temp__wallabag_entry - DROP TABLE __temp__wallabag_entry - CREATE INDEX created_at_idx ON wallabag_entry (created_at) - CREATE INDEX IDX_F4D18282A76ED395 ON wallabag_entry (user_id) - -Migration 20161122144743 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('restricted_access', 0, 'entry') - -Migration down -"""""""""""""" - -.. code-block:: sql - - DELETE FROM wallabag_craue_config_setting WHERE name = 'restricted_access'; - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('restricted_access', 0, 'entry') - -Migration down -"""""""""""""" - -.. code-block:: sql - - DELETE FROM wallabag_craue_config_setting WHERE name = 'restricted_access'; - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('restricted_access', 0, 'entry') - -Migration down -"""""""""""""" - -.. code-block:: sql - - DELETE FROM wallabag_craue_config_setting WHERE name = 'restricted_access'; - -Migration 20161122203647 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_user DROP expired, DROP credentials_expired - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_user ADD expired SMALLINT DEFAULT NULL, ADD credentials_expired SMALLINT DEFAULT NULL - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_user DROP expired - ALTER TABLE wallabag_user DROP credentials_expired - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_user ADD expired SMALLINT DEFAULT NULL - ALTER TABLE wallabag_user ADD credentials_expired SMALLINT DEFAULT NULL - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - DROP INDEX UNIQ_1D63E7E5C05FB297 - DROP INDEX UNIQ_1D63E7E5A0D96FBF - DROP INDEX UNIQ_1D63E7E592FC23A8 - CREATE TEMPORARY TABLE __temp__wallabag_user AS SELECT id, username, username_canonical, email, email_canonical, enabled, salt, password, last_login, locked, expires_at, confirmation_token, password_requested_at, roles, credentials_expire_at, name, created_at, updated_at, authCode, twoFactorAuthentication, trusted FROM wallabag_user - DROP TABLE wallabag_user - CREATE TABLE wallabag_user (id INTEGER NOT NULL, username VARCHAR(180) NOT NULL COLLATE BINARY, username_canonical VARCHAR(180) NOT NULL COLLATE BINARY, email VARCHAR(180) NOT NULL COLLATE BINARY, email_canonical VARCHAR(180) NOT NULL COLLATE BINARY, enabled BOOLEAN NOT NULL, salt VARCHAR(255) NOT NULL COLLATE BINARY, password VARCHAR(255) NOT NULL COLLATE BINARY, last_login DATETIME DEFAULT NULL, locked BOOLEAN NOT NULL, expires_at DATETIME DEFAULT NULL, confirmation_token VARCHAR(180) DEFAULT NULL COLLATE BINARY, password_requested_at DATETIME DEFAULT NULL, roles CLOB NOT NULL COLLATE BINARY, credentials_expire_at DATETIME DEFAULT NULL, name CLOB DEFAULT NULL COLLATE BINARY, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, authCode INTEGER DEFAULT NULL, twoFactorAuthentication BOOLEAN NOT NULL, trusted CLOB DEFAULT NULL COLLATE BINARY, PRIMARY KEY(id)) - INSERT INTO wallabag_user (id, username, username_canonical, email, email_canonical, enabled, salt, password, last_login, locked, expires_at, confirmation_token, password_requested_at, roles, credentials_expire_at, name, created_at, updated_at, authCode, twoFactorAuthentication, trusted) SELECT id, username, username_canonical, email, email_canonical, enabled, salt, password, last_login, locked, expires_at, confirmation_token, password_requested_at, roles, credentials_expire_at, name, created_at, updated_at, authCode, twoFactorAuthentication, trusted FROM __temp__wallabag_user - DROP TABLE __temp__wallabag_user - CREATE UNIQUE INDEX UNIQ_1D63E7E5C05FB297 ON wallabag_user (confirmation_token) - CREATE UNIQUE INDEX UNIQ_1D63E7E5A0D96FBF ON wallabag_user (email_canonical) - CREATE UNIQUE INDEX UNIQ_1D63E7E592FC23A8 ON wallabag_user (username_canonical) - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_user ADD COLUMN expired SMALLINT DEFAULT NULL - ALTER TABLE wallabag_user ADD COLUMN credentials_expired SMALLINT DEFAULT NULL - -Migration 20161128084725 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_config ADD list_mode INT DEFAULT NULL - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_config DROP list_mode - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_config ADD list_mode INT DEFAULT NULL - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_config DROP list_mode - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_config ADD COLUMN list_mode INTEGER DEFAULT NULL - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX UNIQ_87E64C53A76ED395 - CREATE TEMPORARY TABLE __temp__wallabag_config AS SELECT id, user_id, theme, items_per_page, language, rss_token, rss_limit, reading_speed, pocket_consumer_key FROM wallabag_config - DROP TABLE wallabag_config - CREATE TABLE wallabag_config (id INTEGER NOT NULL, user_id INTEGER DEFAULT NULL, theme VARCHAR(255) NOT NULL COLLATE BINARY, items_per_page INTEGER NOT NULL, language VARCHAR(255) NOT NULL COLLATE BINARY, rss_token VARCHAR(255) DEFAULT NULL COLLATE BINARY, rss_limit INTEGER DEFAULT NULL, reading_speed DOUBLE PRECISION DEFAULT NULL, pocket_consumer_key VARCHAR(255) DEFAULT NULL COLLATE BINARY, PRIMARY KEY(id)) - INSERT INTO wallabag_config (id, user_id, theme, items_per_page, language, rss_token, rss_limit, reading_speed, pocket_consumer_key) SELECT id, user_id, theme, items_per_page, language, rss_token, rss_limit, reading_speed, pocket_consumer_key FROM __temp__wallabag_config - DROP TABLE __temp__wallabag_config - CREATE UNIQUE INDEX UNIQ_87E64C53A76ED395 ON wallabag_config (user_id) - -Migration 20161128131503 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_user DROP locked, DROP credentials_expire_at, DROP expires_at - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_user ADD locked SMALLINT DEFAULT NULL, ADD credentials_expire_at DATETIME DEFAULT NULL, ADD expires_at DATETIME DEFAULT NULL - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_user DROP locked - ALTER TABLE wallabag_user DROP credentials_expire_at - ALTER TABLE wallabag_user DROP expires_at - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_user ADD locked SMALLINT DEFAULT NULL - ALTER TABLE wallabag_user ADD credentials_expire_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL - ALTER TABLE wallabag_user ADD expires_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_user ADD COLUMN locked SMALLINT DEFAULT NULL - ALTER TABLE wallabag_user ADD COLUMN credentials_expire_at DATETIME DEFAULT NULL - ALTER TABLE wallabag_user ADD COLUMN expires_at DATETIME DEFAULT NULL - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX UNIQ_1D63E7E592FC23A8 - DROP INDEX UNIQ_1D63E7E5A0D96FBF - DROP INDEX UNIQ_1D63E7E5C05FB297 - CREATE TEMPORARY TABLE __temp__wallabag_user AS SELECT id, username, username_canonical, email, email_canonical, enabled, salt, password, last_login, confirmation_token, password_requested_at, roles, name, created_at, updated_at, authCode, twoFactorAuthentication, trusted, expired, credentials_expired FROM wallabag_user - DROP TABLE wallabag_user - CREATE TABLE wallabag_user (id INTEGER NOT NULL, username VARCHAR(180) NOT NULL COLLATE BINARY, username_canonical VARCHAR(180) NOT NULL COLLATE BINARY, email VARCHAR(180) NOT NULL COLLATE BINARY, email_canonical VARCHAR(180) NOT NULL COLLATE BINARY, enabled BOOLEAN NOT NULL, salt VARCHAR(255) NOT NULL COLLATE BINARY, password VARCHAR(255) NOT NULL COLLATE BINARY, last_login DATETIME DEFAULT NULL, confirmation_token VARCHAR(180) DEFAULT NULL COLLATE BINARY, password_requested_at DATETIME DEFAULT NULL, roles CLOB NOT NULL COLLATE BINARY, name CLOB DEFAULT NULL COLLATE BINARY, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, authCode INTEGER DEFAULT NULL, twoFactorAuthentication BOOLEAN NOT NULL, trusted CLOB DEFAULT NULL COLLATE BINARY, expired SMALLINT DEFAULT NULL, credentials_expired SMALLINT DEFAULT NULL, PRIMARY KEY(id)) - INSERT INTO wallabag_user (id, username, username_canonical, email, email_canonical, enabled, salt, password, last_login, confirmation_token, password_requested_at, roles, name, created_at, updated_at, authCode, twoFactorAuthentication, trusted, expired, credentials_expired) SELECT id, username, username_canonical, email, email_canonical, enabled, salt, password, last_login, confirmation_token, password_requested_at, roles, name, created_at, updated_at, authCode, twoFactorAuthentication, trusted, expired, credentials_expired FROM __temp__wallabag_user - DROP TABLE __temp__wallabag_user - CREATE UNIQUE INDEX UNIQ_1D63E7E592FC23A8 ON wallabag_user (username_canonical) - CREATE UNIQUE INDEX UNIQ_1D63E7E5A0D96FBF ON wallabag_user (email_canonical) - CREATE UNIQUE INDEX UNIQ_1D63E7E5C05FB297 ON wallabag_user (confirmation_token) - -Migration 20161214094402 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_entry CHANGE uuid uid VARCHAR(23) - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_entry CHANGE uid uuid VARCHAR(23) - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_entry RENAME uuid TO uid - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_entry RENAME uid TO uuid - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - CREATE TABLE __temp__wallabag_entry ( - id INTEGER NOT NULL, - user_id INTEGER DEFAULT NULL, - uid VARCHAR(23) DEFAULT NULL, - title CLOB DEFAULT NULL, - url CLOB DEFAULT NULL, - is_archived BOOLEAN NOT NULL, - is_starred BOOLEAN NOT NULL, - content CLOB DEFAULT NULL, - created_at DATETIME NOT NULL, - updated_at DATETIME NOT NULL, - mimetype CLOB DEFAULT NULL, - language CLOB DEFAULT NULL, - reading_time INTEGER DEFAULT NULL, - domain_name CLOB DEFAULT NULL, - preview_picture CLOB DEFAULT NULL, - is_public BOOLEAN DEFAULT '0', - http_status VARCHAR(3) DEFAULT NULL, - PRIMARY KEY(id) - ); - INSERT INTO __temp__wallabag_entry SELECT id,user_id,uuid,title,url,is_archived,is_starred,content,created_at,updated_at,mimetype,language,reading_time,domain_name,preview_picture,is_public,http_status FROM wallabag_entry; - DROP TABLE wallabag_entry; - ALTER TABLE __temp__wallabag_entry RENAME TO wallabag_entry - CREATE INDEX uid ON wallabag_entry (uid) - CREATE INDEX created_at ON wallabag_entry (created_at) - CREATE INDEX IDX_F4D18282A76ED395 ON wallabag_entry (user_id) - - -Migration down -"""""""""""""" - -.. code-block:: sql - - CREATE TABLE __temp__wallabag_entry ( - id INTEGER NOT NULL, - user_id INTEGER DEFAULT NULL, - uuid VARCHAR(23) DEFAULT NULL, - title CLOB DEFAULT NULL, - url CLOB DEFAULT NULL, - is_archived BOOLEAN NOT NULL, - is_starred BOOLEAN NOT NULL, - content CLOB DEFAULT NULL, - created_at DATETIME NOT NULL, - updated_at DATETIME NOT NULL, - mimetype CLOB DEFAULT NULL, - language CLOB DEFAULT NULL, - reading_time INTEGER DEFAULT NULL, - domain_name CLOB DEFAULT NULL, - preview_picture CLOB DEFAULT NULL, - is_public BOOLEAN DEFAULT '0', - http_status VARCHAR(3) DEFAULT NULL, - PRIMARY KEY(id) - ); - INSERT INTO __temp__wallabag_entry SELECT id,user_id,uid,title,url,is_archived,is_starred,content,created_at,updated_at,mimetype,language,reading_time,domain_name,preview_picture,is_public,http_status FROM wallabag_entry; - DROP TABLE wallabag_entry; - ALTER TABLE __temp__wallabag_entry RENAME TO wallabag_entry - CREATE INDEX uid ON wallabag_entry (uid) - CREATE INDEX created_at ON wallabag_entry (created_at) - CREATE INDEX IDX_F4D18282A76ED395 ON wallabag_entry (user_id) - -Migration 20161214094403 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - CREATE INDEX IDX_entry_uid ON wallabag_entry (uid) - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX IDX_entry_uid ON wallabag_entry - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - CREATE INDEX IDX_entry_uid ON wallabag_entry (uid) - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX idx_entry_uid - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - DROP INDEX IDX_F4D18282A76ED395 - DROP INDEX created_at_idx - CREATE TEMPORARY TABLE __temp__wallabag_entry AS SELECT id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public FROM wallabag_entry - DROP TABLE wallabag_entry - CREATE TABLE wallabag_entry (id INTEGER NOT NULL, user_id INTEGER DEFAULT NULL, uid CLOB DEFAULT NULL COLLATE BINARY, title CLOB DEFAULT NULL COLLATE BINARY, url CLOB DEFAULT NULL COLLATE BINARY, is_archived BOOLEAN NOT NULL, is_starred BOOLEAN NOT NULL, content CLOB DEFAULT NULL COLLATE BINARY, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, mimetype CLOB DEFAULT NULL COLLATE BINARY, language CLOB DEFAULT NULL COLLATE BINARY, reading_time INTEGER DEFAULT NULL, domain_name CLOB DEFAULT NULL COLLATE BINARY, preview_picture CLOB DEFAULT NULL COLLATE BINARY, is_public BOOLEAN DEFAULT '0', PRIMARY KEY(id)) - INSERT INTO wallabag_entry (id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public) SELECT id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public FROM __temp__wallabag_entry - DROP TABLE __temp__wallabag_entry - CREATE INDEX IDX_F4D18282A76ED395 ON wallabag_entry (user_id) - CREATE INDEX created_at_idx ON wallabag_entry (created_at) - CREATE INDEX IDX_entry_uid ON wallabag_entry (uid) - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX IDX_entry_uid - DROP INDEX created_at_idx - DROP INDEX IDX_F4D18282A76ED395 - CREATE TEMPORARY TABLE __temp__wallabag_entry AS SELECT id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public FROM wallabag_entry - DROP TABLE wallabag_entry - CREATE TABLE wallabag_entry (id INTEGER NOT NULL, user_id INTEGER DEFAULT NULL, uid CLOB DEFAULT NULL COLLATE BINARY, title CLOB DEFAULT NULL COLLATE BINARY, url CLOB DEFAULT NULL COLLATE BINARY, is_archived BOOLEAN NOT NULL, is_starred BOOLEAN NOT NULL, content CLOB DEFAULT NULL COLLATE BINARY, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, mimetype CLOB DEFAULT NULL COLLATE BINARY, language CLOB DEFAULT NULL COLLATE BINARY, reading_time INTEGER DEFAULT NULL, domain_name CLOB DEFAULT NULL COLLATE BINARY, preview_picture CLOB DEFAULT NULL COLLATE BINARY, is_public BOOLEAN DEFAULT '0', PRIMARY KEY(id)) - INSERT INTO wallabag_entry (id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public) SELECT id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public FROM __temp__wallabag_entry - DROP TABLE __temp__wallabag_entry - CREATE INDEX created_at_idx ON wallabag_entry (created_at) - CREATE INDEX IDX_F4D18282A76ED395 ON wallabag_entry (user_id) - -Migration 20170127093841 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - CREATE INDEX IDX_entry_starred ON wallabag_entry (is_starred) - CREATE INDEX IDX_entry_archived ON wallabag_entry (is_archived) - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX IDX_entry_starred ON wallabag_entry - DROP INDEX IDX_entry_archived ON wallabag_entry - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - CREATE INDEX IDX_entry_starred ON wallabag_entry (is_starred) - CREATE INDEX IDX_entry_archived ON wallabag_entry (is_archived) - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX IDX_entry_starred - DROP INDEX IDX_entry_archived - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - DROP INDEX uid - DROP INDEX created_at - DROP INDEX IDX_F4D18282A76ED395 - CREATE TEMPORARY TABLE __temp__wallabag_entry AS SELECT id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public, http_status FROM wallabag_entry - DROP TABLE wallabag_entry - CREATE TABLE wallabag_entry (id INTEGER NOT NULL, user_id INTEGER DEFAULT NULL, uid VARCHAR(23) DEFAULT NULL COLLATE BINARY, title CLOB DEFAULT NULL COLLATE BINARY, url CLOB DEFAULT NULL COLLATE BINARY, is_archived BOOLEAN NOT NULL, is_starred BOOLEAN NOT NULL, content CLOB DEFAULT NULL COLLATE BINARY, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, mimetype CLOB DEFAULT NULL COLLATE BINARY, language CLOB DEFAULT NULL COLLATE BINARY, reading_time INTEGER DEFAULT NULL, domain_name CLOB DEFAULT NULL COLLATE BINARY, preview_picture CLOB DEFAULT NULL COLLATE BINARY, is_public BOOLEAN DEFAULT '0', http_status VARCHAR(3) DEFAULT NULL COLLATE BINARY, PRIMARY KEY(id)) - INSERT INTO wallabag_entry (id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public, http_status) SELECT id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public, http_status FROM __temp__wallabag_entry - DROP TABLE __temp__wallabag_entry - CREATE INDEX uid ON wallabag_entry (uid) - CREATE INDEX created_at ON wallabag_entry (created_at) - CREATE INDEX IDX_F4D18282A76ED395 ON wallabag_entry (user_id) - CREATE INDEX IDX_entry_starred ON wallabag_entry (is_starred) - CREATE INDEX IDX_entry_archived ON wallabag_entry (is_archived) - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX IDX_entry_archived - DROP INDEX IDX_entry_starred - DROP INDEX IDX_F4D18282A76ED395 - DROP INDEX created_at - DROP INDEX uid - CREATE TEMPORARY TABLE __temp__wallabag_entry AS SELECT id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public, http_status FROM wallabag_entry - DROP TABLE wallabag_entry - CREATE TABLE wallabag_entry (id INTEGER NOT NULL, user_id INTEGER DEFAULT NULL, uid VARCHAR(23) DEFAULT NULL COLLATE BINARY, title CLOB DEFAULT NULL COLLATE BINARY, url CLOB DEFAULT NULL COLLATE BINARY, is_archived BOOLEAN NOT NULL, is_starred BOOLEAN NOT NULL, content CLOB DEFAULT NULL COLLATE BINARY, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, mimetype CLOB DEFAULT NULL COLLATE BINARY, language CLOB DEFAULT NULL COLLATE BINARY, reading_time INTEGER DEFAULT NULL, domain_name CLOB DEFAULT NULL COLLATE BINARY, preview_picture CLOB DEFAULT NULL COLLATE BINARY, is_public BOOLEAN DEFAULT '0', http_status VARCHAR(3) DEFAULT NULL COLLATE BINARY, PRIMARY KEY(id)) - INSERT INTO wallabag_entry (id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public, http_status) SELECT id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public, http_status FROM __temp__wallabag_entry - DROP TABLE __temp__wallabag_entry - CREATE INDEX IDX_F4D18282A76ED395 ON wallabag_entry (user_id) - CREATE INDEX created_at ON wallabag_entry (created_at) - CREATE INDEX uid ON wallabag_entry (uid) diff --git a/docs/de/user/tags.rst b/docs/de/user/tags.rst deleted file mode 100644 index 8ddc0f40..00000000 --- a/docs/de/user/tags.rst +++ /dev/null @@ -1,2 +0,0 @@ -Tags -==== \ No newline at end of file diff --git a/docs/de/user/upgrade.rst b/docs/de/user/upgrade.rst deleted file mode 100644 index 76faa4e2..00000000 --- a/docs/de/user/upgrade.rst +++ /dev/null @@ -1,140 +0,0 @@ -wallabag-Installation aktualisieren -=================================== - -Du wirst hier mehrere Wege finden, um deine wallabag zu aktualisieren: - -- `von 2.1.x zu 2.2.x <#upgrade-von-2-1-x-zu-2-2-x>`_ -- `von 2.0.x zu 2.1.1 <#upgrade-von-2-0-x-zu-2-1-1>`_ -- `von 1.x zu 2.x <#upgrade-von-1-x>`_ - -Upgrade von 2.1.x zu 2.2.x --------------------------- - -Upgrade auf einem dedizierten Webserver -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -**Von 2.1.x:** - -:: - - make update - php bin/console doctrine:migrations:migrate --no-interaction -e=prod - -**Von 2.2.0:** - -:: - - make update - -Erklärungen über die Datenbankmigration -""""""""""""""""""""""""""""""""""""""" - -Während des Updates migrieren wir die Datenbank. - -Alle Datenbankmigrationen sind im Verzeichnis ``app/DoctrineMigrations`` gespeichert. Jede von ihnen kann einzeln ausgeführt werden: -``bin/console doctrine:migrations:execute 20161001072726 --env=prod``. - -Dies ist die Migrationsliste von 2.1.x auf 2.2.0: - -* ``20161001072726``: Fremdschlüssel für das Zurücksetzen des Kontos hinzugefügt -* ``20161022134138``: Datenbank zum ``utf8mb4``-Encoding ändern (nur für MySQL) -* ``20161024212538``: ``user_id``-Spalte zu ``oauth2_clients`` hinzugefügt, um Benutzer davon abzuhalten, API-Clients anderer Nutzer zu löschen -* ``20161031132655``: Interne Einstellung für das (de-)aktivieren vom Bilder-Download hinzugefügt -* ``20161104073720``: ``created_at``-Index zur ``entry``-Tabelle hinzugefügt -* ``20161106113822``: ``action_mark_as_read``-Feld zur ``config``-Tabelle hinzugefügt -* ``20161117071626``: Interne Einstellung zum Teilen mit unmark.it hinzugefügt -* ``20161118134328``: ``http_status``-Feld zur ``entry``-Tabelle hinzugefügt -* ``20161122144743``: Interne Einstellung für das (de-)aktivieren zum Holen von Artikeln mit einer Paywall hinzugefügt -* ``20161122203647``: ``expired``- und ``credentials_expired``-Feld aus der ``user``-Tabelle entfernt -* ``20161128084725``: added ``list_mode`` field on ``config`` table -* ``20161128131503``: dropped ``locked``, ``credentials_expire_at`` and ``expires_at`` fields on ``user`` table -* ``20161214094402``: renamed ``uuid`` to ``uid`` on ``entry`` table -* ``20161214094403``: added ``uid`` index on ``entry`` table -* ``20170127093841``: added ``is_starred`` and ``is_archived`` indexes on ``entry`` table - -Upgrade auf einem Shared Hosting -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Sichere deine ``app/config/parameters.yml``-Datei. - -Lade das letzte Release von wallabag herunter: - -.. code-block:: bash - - wget https://wllbg.org/latest-v2-package && tar xvf latest-v2-package - -Du findest den `aktuellen MD5-Hash auf unserer Webseite `_. - -Extrahiere das Archiv in deinen wallabag-Ordner und ersetze die ``app/config/parameters.yml`` mit deiner. - -Bitte überprüfe, dass deine ``app/config/parameters.yml`` alle notwendigen Parameter enthält. Eine Dokumentation darüber `findest du hier `_. - -Falls du SQLite nutzt, musst du außerdem deinen ``data/``-Ordner in die neue Installation kopieren. - -Leere den ``var/cache``-Ordner. - -Du musst einige SQL-Abfragen durchführen, um deine Datenbank zu aktualisieren. Wir gehen in diesem Fall davon aus, dass das Tabellenpräfix ``wallabag_`` ist. - -`You can find all the queries here `_. - -Upgrade von 2.0.x zu 2.1.1 ---------------------------- - -.. warning:: - - Mache eine Sicherung deines Pocket-Consumer-Key, falls hinzugefügt, da dieser nach dem Upgrade erneut hinzugefügt werden muss. - -Upgrade auf einem dedizierten Webserver -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -:: - - rm -rf var/cache/* - git fetch origin - git fetch --tags - git checkout 2.1.1 --force - SYMFONY_ENV=prod composer install --no-dev -o --prefer-dist - php bin/console doctrine:migrations:migrate --env=prod - php bin/console cache:clear --env=prod - -Upgrade auf einem Shared Hosting -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Sichere deine ``app/config/parameters.yml``-Datei. - -Lade das 2.1.1-Release von wallabag herunter: - -.. code-block:: bash - - wget http://framabag.org/wallabag-release-2.1.1.tar.gz && tar xvf wallabag-release-2.1.1.tar.gz - -(md5 hash of the 2.1.1 package: ``9584a3b60a2b2a4de87f536548caac93``) - -Extrahiere das Archiv in deinen wallabag-Ordner und ersetze die ``app/config/parameters.yml`` mit deiner. - -Bitte überprüfe, dass deine ``app/config/parameters.yml`` alle notwendigen Parameter enthält. Eine Dokumentation darüber `findest du hier `_. - -Falls du SQLite nutzt, musst du außerdem deinen ``data/``-Ordner in die neue Installation kopieren. - -Leere den ``var/cache``-Ordner. - -Du musst einige SQL-Abfragen durchführen, um deine Datenbank zu aktualisieren. Wir gehen in diesem Fall davon aus, dass das Tabellenpräfix ``wallabag_`` ist und eine MySQL-Datenbank verwendet wird: - -.. code-block:: sql - - ALTER TABLE `wallabag_entry` ADD `uuid` LONGTEXT DEFAULT NULL; - INSERT INTO `wallabag_craue_config_setting` (`name`, `value`, `section`) VALUES ('share_public', '1', 'entry'); - ALTER TABLE `wallabag_oauth2_clients` ADD name longtext COLLATE 'utf8_unicode_ci' DEFAULT NULL; - INSERT INTO `wallabag_craue_config_setting` (`name`, `value`, `section`) VALUES ('import_with_redis', '0', 'import'); - INSERT INTO `wallabag_craue_config_setting` (`name`, `value`, `section`) VALUES ('import_with_rabbitmq', '0', 'import'); - ALTER TABLE `wallabag_config` ADD `pocket_consumer_key` VARCHAR(255) DEFAULT NULL; - DELETE FROM `wallabag_craue_config_setting` WHERE `name` = 'pocket_consumer_key'; - -Upgrade von 1.x ---------------- - -Es gibt kein automatisiertes Skript, um wallabag 1.x auf wallabag 2.x zu aktualisieren. Du musst: - -- deine Daten exportieren -- wallabag 2.x installieren (Dokumentation `_ ) -- die Daten in die neue Installation importieren (`Dokumentation `_ ) diff --git a/docs/en/conf.py b/docs/en/conf.py deleted file mode 100644 index 717b35f1..00000000 --- a/docs/en/conf.py +++ /dev/null @@ -1,55 +0,0 @@ -# -*- coding: utf-8 -*- -# -# wallabag documentation build configuration file, created by -# sphinx-quickstart on Fri Oct 16 06:47:23 2015. - -import sys -import os - -extensions = [] -templates_path = ['_templates'] -source_suffix = '.rst' -master_doc = 'index' -project = u'wallabag' -copyright = u'2013-2016, Nicolas Lœuillet - MIT Licence' -version = '2.1.0' -release = version -exclude_patterns = ['_build'] -pygments_style = 'sphinx' -html_theme = 'default' -html_static_path = ['_static'] -htmlhelp_basename = 'wallabagdoc' -latex_elements = { - -} - -latex_documents = [ - ('index', 'wallabag.tex', u'wallabag Documentation', - u'Nicolas Lœuillet', 'manual'), -] - -man_pages = [ - ('index', 'wallabag', u'wallabag Documentation', - [u'Nicolas Lœuillet'], 1) -] - -texinfo_documents = [ - ('index', 'wallabag', u'wallabag Documentation', - u'Nicolas Lœuillet', 'wallabag', 'wallabag is an opensource read-it-later.', - 'Miscellaneous'), -] - -##### Guzzle sphinx theme - -import guzzle_sphinx_theme -html_translator_class = 'guzzle_sphinx_theme.HTMLTranslator' -html_theme_path = guzzle_sphinx_theme.html_theme_path() -html_theme = 'guzzle_sphinx_theme' - -# Custom sidebar templates, maps document names to template names. -html_sidebars = { - '**': ['logo-text.html', 'globaltoc.html', 'searchbox.html'] -} - -# Register the theme as an extension to generate a sitemap.xml -extensions.append("guzzle_sphinx_theme") diff --git a/docs/en/developer/api.rst b/docs/en/developer/api.rst deleted file mode 100644 index 80c96025..00000000 --- a/docs/en/developer/api.rst +++ /dev/null @@ -1,271 +0,0 @@ -API documentation -================= - -Thanks to this documentation, we'll see how to interact with the wallabag API. - -Requirements ------------- - -* wallabag freshly (or not) installed on http://localhost:8000 -* ``httpie`` installed on your computer (`see project website `__). Note that you can also adapt the commands using curl or wget. -* all the API methods are documented here http://localhost:8000/api/doc (on your instance) and `on our example instance `_ - -Creating a new API client -------------------------- - -In your wallabag account, you can create a new API client at this URL http://localhost:8000/developer/client/create. - -Just give the redirect URL of your application and create your client. If your application is a desktop one, put whatever URL suits you the most. - -You get information like this: - -:: - - Client ID: - - 1_3o53gl30vhgk0c8ks4cocww08o84448osgo40wgw4gwkoo8skc - - Client secret: - - 636ocbqo978ckw0gsw4gcwwocg8044sco0w8w84cws48ggogs4 - - -Obtaining a refresh token -------------------------- - -For each API call, you'll need a token. Let's create it with this command (replace ``client_id``, ``client_secret``, ``username`` and ``password`` with their values): - -:: - - http POST http://localhost:8000/oauth/v2/token \ - grant_type=password \ - client_id=1_3o53gl30vhgk0c8ks4cocww08o84448osgo40wgw4gwkoo8skc \ - client_secret=636ocbqo978ckw0gsw4gcwwocg8044sco0w8w84cws48ggogs4 \ - username=wallabag \ - password=wallabag - -You'll have this in return: - -:: - - HTTP/1.1 200 OK - Cache-Control: no-store, private - Connection: close - Content-Type: application/json - Date: Tue, 05 Apr 2016 08:44:33 GMT - Host: localhost:8000 - Pragma: no-cache - X-Debug-Token: 19c8e0 - X-Debug-Token-Link: /_profiler/19c8e0 - X-Powered-By: PHP/7.0.4 - - { - "access_token": "ZGJmNTA2MDdmYTdmNWFiZjcxOWY3MWYyYzkyZDdlNWIzOTU4NWY3NTU1MDFjOTdhMTk2MGI3YjY1ZmI2NzM5MA", - "expires_in": 3600, - "refresh_token": "OTNlZGE5OTJjNWQwYzc2NDI5ZGE5MDg3ZTNjNmNkYTY0ZWZhZDVhNDBkZTc1ZTNiMmQ0MjQ0OThlNTFjNTQyMQ", - "scope": null, - "token_type": "bearer" - } - -We'll work with the ``access_token`` value in our next calls. - -cURL example: - -:: - - curl -s "https://localhost:8000/oauth/v2/token?grant_type=password&client_id=1_3o53gl30vhgk0c8ks4cocww08o84448osgo40wgw4gwkoo8skc&client_secret=636ocbqo978ckw0gsw4gcwwocg8044sco0w8w84cws48ggogs4&username=wallabag&password=wallabag" - -Getting existing entries ------------------------- - -Documentation for this method: http://localhost:8000/api/doc#get--api-entries.{_format} - -As we work on a fresh wallabag installation, we'll have no result with this command: - -:: - - http GET http://localhost:8000/api/entries.json \ - "Authorization:Bearer ZGJmNTA2MDdmYTdmNWFiZjcxOWY3MWYyYzkyZDdlNWIzOTU4NWY3NTU1MDFjOTdhMTk2MGI3YjY1ZmI2NzM5MA" - -returns: - -:: - - HTTP/1.1 200 OK - 0: application/json - Cache-Control: no-cache - Connection: close - Content-Type: application/json - Date: Tue, 05 Apr 2016 08:51:32 GMT - Host: localhost:8000 - Set-Cookie: PHPSESSID=nrogm748md610ovhu6j70c3q63; path=/; HttpOnly - X-Debug-Token: 4fbbc4 - X-Debug-Token-Link: /_profiler/4fbbc4 - X-Powered-By: PHP/7.0.4 - - { - "_embedded": { - "items": [] - }, - "_links": { - "first": { - "href": "http://localhost:8000/api/entries?page=1&perPage=30" - }, - "last": { - "href": "http://localhost:8000/api/entries?page=1&perPage=30" - }, - "self": { - "href": "http://localhost:8000/api/entries?page=1&perPage=30" - } - }, - "limit": 30, - "page": 1, - "pages": 1, - "total": 0 - } - -The ``items`` array is empty. - -cURL example: - -:: - - curl --get "https://localhost:8000/api/entries.html?access_token=ZGJmNTA2MDdmYTdmNWFiZjcxOWY3MWYyYzkyZDdlNWIzOTU4NWY3NTU1MDFjOTdhMTk2MGI3YjY1ZmI2NzM5MA" - -Adding your first entry ------------------------ - -Documentation for this method: http://localhost:8000/api/doc#post--api-entries.{_format} - -:: - - http POST http://localhost:8000/api/entries.json \ - "Authorization:Bearer ZGJmNTA2MDdmYTdmNWFiZjcxOWY3MWYyYzkyZDdlNWIzOTU4NWY3NTU1MDFjOTdhMTk2MGI3YjY1ZmI2NzM5MA" \ - url="http://www.numerama.com/tech/160115-le-pocket-libre-wallabag-fait-le-plein-de-fonctionnalites.html" - -returns - -:: - - HTTP/1.1 200 OK - 0: application/json - Cache-Control: no-cache - Connection: close - Content-Type: application/json - Date: Tue, 05 Apr 2016 09:07:54 GMT - Host: localhost:8000 - Set-Cookie: PHPSESSID=bjie40ck72kp2pst3i71gf43a4; path=/; HttpOnly - X-Debug-Token: e01c51 - X-Debug-Token-Link: /_profiler/e01c51 - X-Powered-By: PHP/7.0.4 - - { - "_links": { - "self": { - "href": "/api/entries/1" - } - }, - "content": "

Fonctionnant sur le même principe que Pocket, Instapaper ou Readability, le logiciel Wallabag permet de mémoriser des articles pour les lire plus tard. Sa nouvelle version apporte une multitude de nouvelles fonctionnalités.

Si vous utilisez Firefox comme navigateur web, vous avez peut-être constaté l’arrivée d’une fonctionnalité intitulée Pocket. Disponible autrefois sous la forme d’un module complémentaire, et sous un autre nom (Read it Later), elle est depuis le mois de juin 2015 directement incluse au sein de Firefox.

\n

Concrètement, Pocket sert à garder en mémoire des contenus que vous croisez au fil de la navigation, comme des articles de presse ou des vidéos, afin de pouvoir les consulter plus tard. Pocket fonctionne un peu comme un système de favoris, mais en bien plus élaboré grâce à ses options supplémentaires.

\n

Mais Pocket fait polémique, car il s’agit d’un projet propriétaire qui est intégré dans un logiciel libre. C’est pour cette raison que des utilisateurs ont choisi de se tourner vers d’autres solutions, comme Wallabag, qui est l’équivalent libre de Pocket et d’autres systèmes du même genre, comme Instapaper et Readability.

\n

Et justement, Wallabag évolue. C’est ce dimanche que la version 2.0.0 du logiciel a été publiée par l’équipe en  charge de son développement et celle-ci contient de nombreux changements par rapport aux moutures précédentes (la documentation est traduite en français), lui permettant d’apparaître comme une alternative à Pocket, Instapaper et Readability.

\n

\"homepage\"

\n

Parmi les principaux changements que l’on peut retenir avec cette nouvelle version, notons la possibilité d’écrire des annotations dans les articles mémorisés, de filtrer les contenus selon divers critères (temps de lecture, nom de domaine, date de création, statut…), d’assigner des mots-clés aux entrées, de modifier le titre des articles, le support des flux RSS ou encore le support de plusieurs langues dont le français.

\n

D’autres options sont également à signaler, comme l’aperçu d’un article mémorisé (si l’option est disponible), un guide de démarrage rapide pour les débutants, un outil d’export dans divers formats (PDF, JSON, EPUB, MOBI, XML, CSV et TXT) et, surtout, la possibilité de migrer vers Wallabag depuis Pocket, afin de convaincre les usagers de se lancer.

\n \n \n

Articles liés

\n
\n
\n", - "created_at": "2016-04-05T09:07:54+0000", - "domain_name": "www.numerama.com", - "id": 1, - "is_archived": 0, - "is_starred": 0, - "language": "fr-FR", - "mimetype": "text/html", - "preview_picture": "http://www.numerama.com/content/uploads/2016/04/post-it.jpg", - "reading_time": 2, - "tags": [], - "title": "Le Pocket libre Wallabag fait le plein de fonctionnalités - Tech - Numerama", - "updated_at": "2016-04-05T09:07:54+0000", - "url": "http://www.numerama.com/tech/160115-le-pocket-libre-wallabag-fait-le-plein-de-fonctionnalites.html", - "user_email": "", - "user_id": 1, - "user_name": "wallabag" - } - -Now, if you execute the previous command (see **Get existing entries**), you'll have data. - -cURL example: - -:: - - curl "https://localhost:8000/api/entries.html?access_token=ZGJmNTA2MDdmYTdmNWFiZjcxOWY3MWYyYzkyZDdlNWIzOTU4NWY3NTU1MDFjOTdhMTk2MGI3YjY1ZmI2NzM5MA&url=http://www.numerama.com/tech/160115-le-pocket-libre-wallabag-fait-le-plein-de-fonctionnalites.html" - -Deleting an entry ------------------ - -Documentation for this method: http://localhost:8000/api/doc#delete--api-entries-{entry}.{_format} - -:: - - http DELETE http://localhost:8000/api/entries/1.json \ - "Authorization:Bearer ZGJmNTA2MDdmYTdmNWFiZjcxOWY3MWYyYzkyZDdlNWIzOTU4NWY3NTU1MDFjOTdhMTk2MGI3YjY1ZmI2NzM5MA" - -returns - -:: - - HTTP/1.1 200 OK - 0: application/json - Cache-Control: no-cache - Connection: close - Content-Type: application/json - Date: Tue, 05 Apr 2016 09:19:07 GMT - Host: localhost:8000 - Set-Cookie: PHPSESSID=jopgnfvmuc9a62b27sqm6iulr6; path=/; HttpOnly - X-Debug-Token: 887cef - X-Debug-Token-Link: /_profiler/887cef - X-Powered-By: PHP/7.0.4 - - { - "_links": { - "self": { - "href": "/api/entries/" - } - }, - "annotations": [], - "content": "

Fonctionnant sur le même principe que Pocket, Instapaper ou Readability, le logiciel Wallabag permet de mémoriser des articles pour les lire plus tard. Sa nouvelle version apporte une multitude de nouvelles fonctionnalités.

Si vous utilisez Firefox comme navigateur web, vous avez peut-être constaté l’arrivée d’une fonctionnalité intitulée Pocket. Disponible autrefois sous la forme d’un module complémentaire, et sous un autre nom (Read it Later), elle est depuis le mois de juin 2015 directement incluse au sein de Firefox.

\n

Concrètement, Pocket sert à garder en mémoire des contenus que vous croisez au fil de la navigation, comme des articles de presse ou des vidéos, afin de pouvoir les consulter plus tard. Pocket fonctionne un peu comme un système de favoris, mais en bien plus élaboré grâce à ses options supplémentaires.

\n

Mais Pocket fait polémique, car il s’agit d’un projet propriétaire qui est intégré dans un logiciel libre. C’est pour cette raison que des utilisateurs ont choisi de se tourner vers d’autres solutions, comme Wallabag, qui est l’équivalent libre de Pocket et d’autres systèmes du même genre, comme Instapaper et Readability.

\n

Et justement, Wallabag évolue. C’est ce dimanche que la version 2.0.0 du logiciel a été publiée par l’équipe en  charge de son développement et celle-ci contient de nombreux changements par rapport aux moutures précédentes (la documentation est traduite en français), lui permettant d’apparaître comme une alternative à Pocket, Instapaper et Readability.

\n

\"homepage\"

\n

Parmi les principaux changements que l’on peut retenir avec cette nouvelle version, notons la possibilité d’écrire des annotations dans les articles mémorisés, de filtrer les contenus selon divers critères (temps de lecture, nom de domaine, date de création, statut…), d’assigner des mots-clés aux entrées, de modifier le titre des articles, le support des flux RSS ou encore le support de plusieurs langues dont le français.

\n

D’autres options sont également à signaler, comme l’aperçu d’un article mémorisé (si l’option est disponible), un guide de démarrage rapide pour les débutants, un outil d’export dans divers formats (PDF, JSON, EPUB, MOBI, XML, CSV et TXT) et, surtout, la possibilité de migrer vers Wallabag depuis Pocket, afin de convaincre les usagers de se lancer.

\n \n \n

Articles liés

\n
\n
\n", - "created_at": "2016-04-05T09:07:54+0000", - "domain_name": "www.numerama.com", - "is_archived": 0, - "is_starred": 0, - "language": "fr-FR", - "mimetype": "text/html", - "preview_picture": "http://www.numerama.com/content/uploads/2016/04/post-it.jpg", - "reading_time": 2, - "tags": [], - "title": "Le Pocket libre Wallabag fait le plein de fonctionnalités - Tech - Numerama", - "updated_at": "2016-04-05T09:07:54+0000", - "url": "http://www.numerama.com/tech/160115-le-pocket-libre-wallabag-fait-le-plein-de-fonctionnalites.html", - "user_email": "", - "user_id": 1, - "user_name": "wallabag" - } - -And if you want to list the existing entries (see **Get existing entries**), the array is empty. - -cURL example: - -:: - - curl --request DELETE "https://localhost:8000/api/entries/1.html?access_token=ZGJmNTA2MDdmYTdmNWFiZjcxOWY3MWYyYzkyZDdlNWIzOTU4NWY3NTU1MDFjOTdhMTk2MGI3YjY1ZmI2NzM5MA" - -Other methods -------------- - -We won't write samples for each API method. - -Have a look on the listing here: http://localhost:8000/api/doc to know each method. - -Third party resources ---------------- - -Some applications or libraries use our API. Here is a non-exhaustive list of them: - -- `Java wrapper for the wallabag API `_ by Dmitriy Bogdanov. -- `.NET library for the wallabag v2 API `_ by Julian Oster. -- `Python API for wallabag `_ by FoxMaSk, for his project `Trigger Happy `_. -- `A plugin `_ designed for `Tiny Tiny RSS `_ that makes use of the wallabag v2 API. By Josh Panter. -- `Golang wrapper for the wallabag API `_ by Strubbl, for his projects `wallabag-stats graph `_ and the command line tool `wallabag-add-article `_. -- Tool to automatically download Wallabag articles into your local computer or Kobo ebook reader `wallabako `_ by anarcat. diff --git a/docs/en/developer/asynchronous.rst b/docs/en/developer/asynchronous.rst deleted file mode 100644 index 2e409e4a..00000000 --- a/docs/en/developer/asynchronous.rst +++ /dev/null @@ -1,160 +0,0 @@ -Asynchronous tasks -================== - -In order to launch asynchronous tasks (useful for huge imports for example), we can use RabbitMQ or Redis. - -Install RabbitMQ for asynchronous tasks ---------------------------------------- - -Requirements -^^^^^^^^^^^^ - -You need to have RabbitMQ installed on your server. - -Installation -^^^^^^^^^^^^ - -.. code:: bash - - wget https://www.rabbitmq.com/rabbitmq-signing-key-public.asc - apt-key add rabbitmq-signing-key-public.asc - apt-get update - apt-get install rabbitmq-server - -Configuration and launch -^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code:: bash - - rabbitmq-plugins enable rabbitmq_management # (useful to have a web interface, available at http://localhost:15672/ (guest/guest) - rabbitmq-server -detached - -Stop RabbitMQ -^^^^^^^^^^^^^ - -.. code:: bash - - rabbitmqctl stop - - -Configure RabbitMQ in wallabag -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Edit your ``app/config/parameters.yml`` file to edit RabbitMQ configuration. The default one should be ok: - -.. code:: yaml - - rabbitmq_host: localhost - rabbitmq_port: 5672 - rabbitmq_user: guest - rabbitmq_password: guest - rabbitmq_prefetch_count: 10 # read http://www.rabbitmq.com/consumer-prefetch.html - -Enable RabbitMQ in wallabag -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -In internal settings, in the **Import** section, enable RabbitMQ (with the value 1). - -Launch RabbitMQ consumer -^^^^^^^^^^^^^^^^^^^^^^^^ - -Depending on which service you want to import from you need to enable one (or many if you want to support many) cron job: - -.. code:: bash - - # for Pocket import - bin/console rabbitmq:consumer -e=prod import_pocket -w - - # for Readability import - bin/console rabbitmq:consumer -e=prod import_readability -w - - # for Instapaper import - bin/console rabbitmq:consumer -e=prod import_instapaper -w - - # for wallabag v1 import - bin/console rabbitmq:consumer -e=prod import_wallabag_v1 -w - - # for wallabag v2 import - bin/console rabbitmq:consumer -e=prod import_wallabag_v2 -w - - # for Firefox import - bin/console rabbitmq:consumer -e=prod import_firefox -w - - # for Chrome import - bin/console rabbitmq:consumer -e=prod import_chrome -w - -Install Redis for asynchronous tasks ------------------------------------- - -In order to launch asynchronous tasks (useful for huge imports for example), we can use Redis. - -Requirements -^^^^^^^^^^^^ - -You need to have Redis installed on your server. - -Installation -^^^^^^^^^^^^ - -.. code:: bash - - apt-get install redis-server - -Launch -^^^^^^ - -The server might be already running after installing, if not you can launch it using: - -.. code:: bash - - redis-server - - -Configure Redis in wallabag -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Edit your ``app/config/parameters.yml`` file to edit Redis configuration. The default one should be ok: - -.. code:: yaml - - redis_host: localhost - redis_port: 6379 - -Enable Redis in wallabag -^^^^^^^^^^^^^^^^^^^^^^^^ - -In internal settings, in the **Import** section, enable Redis (with the value 1). - -Launch Redis consumer -^^^^^^^^^^^^^^^^^^^^^ - -Depending on which service you want to import from you need to enable one (or many if you want to support many) cron job: - -.. code:: bash - - # for Pocket import - bin/console wallabag:import:redis-worker -e=prod pocket -vv >> /path/to/wallabag/var/logs/redis-pocket.log - - # for Readability import - bin/console wallabag:import:redis-worker -e=prod readability -vv >> /path/to/wallabag/var/logs/redis-readability.log - - # for Instapaper import - bin/console wallabag:import:redis-worker -e=prod instapaper -vv >> /path/to/wallabag/var/logs/redis-instapaper.log - - # for wallabag v1 import - bin/console wallabag:import:redis-worker -e=prod wallabag_v1 -vv >> /path/to/wallabag/var/logs/redis-wallabag_v1.log - - # for wallabag v2 import - bin/console wallabag:import:redis-worker -e=prod wallabag_v2 -vv >> /path/to/wallabag/var/logs/redis-wallabag_v2.log - - # for Firefox import - bin/console wallabag:import:redis-worker -e=prod firefox -vv >> /path/to/wallabag/var/logs/redis-firefox.log - - # for Chrome import - bin/console wallabag:import:redis-worker -e=prod chrome -vv >> /path/to/wallabag/var/logs/redis-chrome.log - -If you want to launch the import only for some messages and not all, you can specify this number (here 12) and the worker will stop right after the 12th message : - -.. code:: bash - - bin/console wallabag:import:redis-worker -e=prod pocket -vv --maxIterations=12 diff --git a/docs/en/developer/docker.rst b/docs/en/developer/docker.rst deleted file mode 100644 index 5e4f2ce6..00000000 --- a/docs/en/developer/docker.rst +++ /dev/null @@ -1,51 +0,0 @@ -Run wallabag in docker-compose -============================== - -In order to run your own development instance of wallabag, you may -want to use the pre-configured docker compose files. - -Requirements ------------- - -Make sure to have `Docker -`__ and `Docker -Compose `__ availables on -your system and up to date. - -Switch DBMS ------------ - -By default, wallabag will start with a SQLite database. -Since wallabag provides support for Postgresql and MySQL, docker -containers are also available for these ones. - -In ``docker-compose.yml``, for the chosen DBMS uncomment: - -- the container definition (``postgres`` or ``mariadb`` root level - block) -- the container link in the ``php`` container -- the container env file in the ``php`` container - -In order to keep running Symfony commands on your host (such as -``wallabag:install``), you also should: - -- source the proper env files on your command line, so variables - like ``SYMFONY__ENV__DATABASE_HOST`` will exist. -- create a ``127.0.0.1 rdbms`` on your system ``hosts`` file - -Run wallabag ------------- - -#. Fork and clone the project -#. Edit ``app/config/parameters.yml`` to replace ``database_*`` - properties with commented ones (with values prefixed by ``env.``) -#. ``composer install`` the project dependencies -#. ``php bin/console wallabag:install`` to create the schema -#. ``docker-compose up`` to run the containers -#. Finally, browse to http://localhost:8080/ to find your freshly - installed wallabag. - -At various step, you'll probably run into UNIX permission problems, -bad paths in generated cache, etc… -Operations like removing cache files or changing files owners might -be frequently required, so don't be afraid ! diff --git a/docs/en/developer/documentation.rst b/docs/en/developer/documentation.rst deleted file mode 100644 index ab206479..00000000 --- a/docs/en/developer/documentation.rst +++ /dev/null @@ -1,12 +0,0 @@ -Contribute to this documentation -================================ - -Sources of our documentation are here https://github.com/wallabag/wallabag/tree/master/docs - -We use `ReadTheDocs -`__ to generate it. - -Pages are written in `Restructured Text -`__ format. You can use online tools like http://rst.aaroniles.net/ or http://rst.ninjs.org/ to preview your articles. - -If you create a new page, don't forget to edit the `index.rst `__ file to add a link in the sidebar. \ No newline at end of file diff --git a/docs/en/developer/paywall.rst b/docs/en/developer/paywall.rst deleted file mode 100644 index 153afa6f..00000000 --- a/docs/en/developer/paywall.rst +++ /dev/null @@ -1,65 +0,0 @@ -Articles behind a paywall -========================= - -wallabag can fetch articles from websites which use a paywall system. - -Enable paywall authentication ------------------------------ - -In internal settings, as a wallabag administrator, in the **Article** section, enable authentication for websites with paywall (with the value 1). - -Configure credentials in wallabag ---------------------------------- - -Edit your ``app/config/parameters.yml`` file to edit credentials for each website with paywall. For example, under Ubuntu: - -``sudo -u www-data nano /var/www/html/wallabag/app/config/parameters.yml`` - -Here is an example for some french websites (be careful: don't use the "tab" key, only spaces): - -.. code:: yaml - - sites_credentials: - mediapart.fr: {username: "myMediapartLogin", password: "mypassword"} - arretsurimages.net: {username: "myASILogin", password: "mypassword"} - -.. note:: - - These credentials will be shared between each user of your wallabag instance. - -Parsing configuration files ---------------------------- - -.. note:: - - Read `this part of the documentation `_ to understand the configuration files, which are located under ``vendor/j0k3r/graby-site-config/``. For most of the websites, this file is already configured: the following instructions are only for the websites that are not configured yet. - -Each parsing configuration file needs to be improved by adding ``requires_login``, ``login_uri``, -``login_username_field``, ``login_password_field`` and ``not_logged_in_xpath``. - -Be careful, the login form must be in the page content when wallabag loads it. It's impossible for wallabag to be authenticated -on a website where the login form is loaded after the page (by ajax for example). - -``login_uri`` is the action URL of the form (``action`` attribute in the form). -``login_username_field`` is the ``name`` attribute of the login field. -``login_password_field`` is the ``name`` attribute of the password field. - -For example: - -.. code:: - - title://div[@id="titrage-contenu"]/h1[@class="title"] - body: //div[@class="contenu-html"]/div[@class="page-pane"] - - requires_login: yes - - login_uri: http://www.arretsurimages.net/forum/login.php - login_username_field: username - login_password_field: password - - not_logged_in_xpath: //body[@class="not-logged-in"] - -Last step: clear the cache --------------------------- - -It's necessary to clear the wallabag cache with the following command (here under Ubuntu): ``sudo -u www-data php /var/www/html/wallabag/bin/console cache:clear -e=prod`` diff --git a/docs/en/developer/testsuite.rst b/docs/en/developer/testsuite.rst deleted file mode 100644 index b2b16cdc..00000000 --- a/docs/en/developer/testsuite.rst +++ /dev/null @@ -1,10 +0,0 @@ -Testsuite -========= - -To ensure wallabag development quality, we wrote tests with `PHPUnit `_. - -If you contribute to the project (by translating the application, by fixing bugs or by adding a new feature), please write your own tests. - -To launch wallabag testsuite, you need to install `ant `_. - -Then, execute this command ``make test``. diff --git a/docs/en/developer/translate.rst b/docs/en/developer/translate.rst deleted file mode 100644 index 1e5d5009..00000000 --- a/docs/en/developer/translate.rst +++ /dev/null @@ -1,60 +0,0 @@ -Translate wallabag -================== - -wallabag web application ------------------------- - -Translation files -~~~~~~~~~~~~~~~~~ - -.. note:: - - As wallabag is mainly developed by a French team, please consider that french - translation is the most updated one and please copy it to create your own translation. - -You can find translation files here: https://github.com/wallabag/wallabag/tree/master/src/Wallabag/CoreBundle/Resources/translations. - -You have to create ``messages.CODE.yml`` and ``validators.CODE.yml``, where CODE -is the ISO 639-1 code of your language (`see wikipedia `__). - -Other files to translate: - -- https://github.com/wallabag/wallabag/tree/master/app/Resources/CraueConfigBundle/translations. -- https://github.com/wallabag/wallabag/tree/master/src/Wallabag/UserBundle/Resources/translations. - -You have to create ``THE_TRANSLATION_FILE.CODE.yml`` files. - -Configuration file -~~~~~~~~~~~~~~~~~~ - -You have to edit `app/config/config.yml -`__ to display -your language on Configuration page of wallabag (to allow users to switch to this new translation). - -Under the ``wallabag_core.languages`` section, you have to add a new line with -your translation. For example: - -:: - - wallabag_core: - ... - languages: - en: 'English' - fr: 'Français' - - -For the first column (``en``, ``fr``, etc.), you have to add the ISO 639-1 code -of your language (see above). - -For the second column, it's the name of your language. Just that. - -wallabag documentation ----------------------- - -.. note:: - - Contrary to the web application, the main language for documentation is english. - -Documentation files are stored here: https://github.com/wallabag/wallabag/tree/master/docs - -You need to respect the ``en`` folder structure when you create your own translation. diff --git a/docs/en/index.rst b/docs/en/index.rst deleted file mode 100644 index 89f61d6d..00000000 --- a/docs/en/index.rst +++ /dev/null @@ -1,53 +0,0 @@ -wallabag documentation -====================== - -.. image:: ../img/wallabag.png - :alt: wallabag logo - :align: center - -**wallabag** is a read-it-later application: it saves a web page by -keeping content only. Elements like navigation or ads are deleted. - -The main documentation for this application is organized into a couple sections: - -* :ref:`user-docs` -* :ref:`dev-docs` - -The documentation is available in other languages: - -* `Documentation en français `_ -* `Dokumentation in Deutsch `_ - -.. _user-docs: - -.. toctree:: - :maxdepth: 2 - :caption: User documentation - - user/installation - user/upgrade - user/configuration - user/import - user/create_account - user/articles - user/errors_during_fetching - user/filters - user/tags - user/configuring_mobile - user/android - user/parameters - user/backup - user/faq - -.. _dev-docs: - -.. toctree:: - :maxdepth: 2 - :caption: Developer documentation - - developer/api - developer/docker - developer/paywall - developer/documentation - developer/translate - developer/asynchronous diff --git a/docs/en/requirements.txt b/docs/en/requirements.txt deleted file mode 100644 index 06fc8973..00000000 --- a/docs/en/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -Sphinx>=1.3.0,<1.4.0 -guzzle_sphinx_theme>=0.7.0,<0.8.0 diff --git a/docs/en/user/android.rst b/docs/en/user/android.rst deleted file mode 100644 index e286bb3b..00000000 --- a/docs/en/user/android.rst +++ /dev/null @@ -1,95 +0,0 @@ -Android application -=================== - -Purpose of this document ------------------------- - -This document describes how you can setup your Android application to work with your wallabag instance. There is no difference in this procedure for wallabag v1 or v2. - -Steps to configure your app ---------------------------- - -When you first start the app, you see the welcome screen, where you are adviced to configure the app for your wallabag instance at first. - -.. image:: ../../img/user/android_welcome_screen.en.png - :alt: Welcome screen - :align: center - -Just confirm that message and you get redirected to the settings screen. - -.. image:: ../../img/user/android_configuration_screen.en.png - :alt: Settings screen - :align: center - -Fill in your wallabag data. You need to enter your wallabag address. **It is important that this URL does not end with a slash**. Also add your wallabag credentials to the user name and password field. - -.. image:: ../../img/user/android_configuration_filled_in.en.png - :alt: Filled in settings - :align: center - -After you have filled in your data, push the button Connection test and wait for the test to finish. - -.. image:: ../../img/user/android_configuration_connection_test.en.png - :alt: Connection test with your wallabag data - :align: center - -The connection test should finish with success. If not, you need to fix this first until you proceed. - -.. image:: ../../img/user/android_configuration_connection_test_success.en.png - :alt: Connection test successful - :align: center - -After the connection test was successful, you can push the button to get your feed credentials. The app now tries to login to your wallabag instance and get the user id and the corresponding token for the feeds. - -.. image:: ../../img/user/android_configuration_get_feed_credentials.en.png - :alt: Getting the feed credentials - :align: center - -When the process of getting your feed credentials finishes with success you see a toast message that the user id and the token were automatically filled in to the form. - -.. image:: ../../img/user/android_configuration_feed_credentials_automatically_filled_in.en.png - :alt: Getting feed credentials successful - :align: center - -Now you need to scroll to the bottom of the settings menu. Of course you can adjust the given settings to your needs. Finish the configuration of your app with pushing the save button. - -.. image:: ../../img/user/android_configuration_scroll_bottom.en.png - :alt: Bottom of the settings screen - :align: center - -After hitting the save button, you get the following screen. The app proposes to initiate a synchronization process to update your feeds of articles. It is recommended to acknowledge this action and press Yes. - -.. image:: ../../img/user/android_configuration_saved_feed_update.en.png - :alt: Settings saved the first time - :align: center - -Finally after the synchronization finished successfully, you are presented to the list of unread articles. - -.. image:: ../../img/user/android_unread_feed_synced.en.png - :alt: Filled article list cause feeds successfully synchronized - :align: center - -Known limitations ------------------ - -Two factor authentication (2FA) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Currently the Android application does not support two-factor authentication. You should disable that to get the application working. - -Limited amount of articles with wallabag v2 -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In your wallabag web instance you can configure how many items are part of the RSS feed. This option did not exist in wallabag v1, where all articles were part of the feed. So if you set the amount of articles being displayed greater than the number of items being content of your RSS feed, you will only see the number of items in your RSS feed. - -SSL/TLS encryption -~~~~~~~~~~~~~~~~~~ - -If you can reach your wallabag web instance via HTTPS, you should use that. Especially if your HTTP URL redirects you to the HTTPS one. Currently, the app cannot handle that redirect properly. - -References ----------- - -- `Source code of the Android application `_ -- `Android Application on F-Droid `_ -- `Android Application on Google Play `_ diff --git a/docs/en/user/articles.rst b/docs/en/user/articles.rst deleted file mode 100644 index 16b3b0d2..00000000 --- a/docs/en/user/articles.rst +++ /dev/null @@ -1,116 +0,0 @@ -Articles -======== - -Save your first article ------------------------ - -The main purpose of wallabag is to save web articles. You have many ways to do it. If you think that the article is wrong displayed, `you can read this documentation `_. - -By using a bookmarklet -^^^^^^^^^^^^^^^^^^^^^^ - -On the ``Howto`` page, you have a ``Bookmarklet`` tab. Drag and drop the ``bag it!`` -link to your bookmarks bar of your browser. - -Now, each time you're reading an article on the web and you want to save it, -click on the ``bag it!`` link in your bookmarks bar. The article is saved. - -By using the classic form -^^^^^^^^^^^^^^^^^^^^^^^^^ - -In the top bar of your screen, you have 3 icons. With the first one, a plus sign, -you can easily save a new article. - -.. image:: ../../img/user/topbar.png - :alt: Top bar - :align: center - -Click on it to display a new field, paste the article URL inside and press your -``Return`` key. The article is saved. - -By using a browser add-on -^^^^^^^^^^^^^^^^^^^^^^^^^ - -Firefox -""""""" - -You can download the `Firefox addon here `_. - -Chrome -"""""" - -You can download the `Chrome addon here `_. - -By using your smarphone application -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Android -""""""" - -You can download the `Android application here `_. - -Windows Phone -""""""""""""" - -You can downlaod the `Windows Phone application here `_. - -Download your articles ----------------------- - -You can download each article in several formats: ePUB, MOBI, PDF, XML, JSON, CSV. - -On the article view, click on this icon, in the sidebar: - -.. image:: ../../img/user/download_article.png - :alt: download article - :align: center - -You can also download a full category (unread, starred, archive) in these formats. -For example, on **Unread** view, click on this icon in the top bar: - -.. image:: ../../img/user/download_articles.png - :alt: download articles - :align: center - -Share your articles -------------------- - -When you're reading an article, you can share it. Just click on the share button: - -.. image:: ../../img/user/share.png - :alt: share article - :align: center - -Now, you can share the article: - -- with a public URL (you'll have a light view of the article) -- with a tweet -- into your Shaarli -- with a post in Diaspora* -- to Carrot -- with an email - -Annotate your articles ----------------------- - -In each article you read, you can write annotations. It's easier to understand with some pictures. - -Select the part of the article that you want to annotate and click on the pencil: - -.. image:: ../../img/user/annotations_1.png - :alt: Select your text - :align: center - -Then, write your annotation: - -.. image:: ../../img/user/annotations_2.png - :alt: Write your annotation - :align: center - -The text is now highlighted and you can read your annotation if you move the mouse cursor over it. - -.. image:: ../../img/user/annotations_3.png - :alt: Read your annotation - :align: center - -You can create as many annotations as you wish. diff --git a/docs/en/user/backup.rst b/docs/en/user/backup.rst deleted file mode 100644 index f8b480a3..00000000 --- a/docs/en/user/backup.rst +++ /dev/null @@ -1,26 +0,0 @@ -Backup wallabag -=============== - -Because sometimes you may do a mistake with your wallabag and lose data or in case you need to move your wallabag to another server you want to backup your data. -This articles describes what you need to backup. - -Basic settings --------------- -wallabag stores some basic parameters (like SMTP server or database backend) in the file `app/config/parameters.yml`. - -Database --------- -As wallabag supports different kinds of database, the way to perform the backup depends on the database you use, so you need to refer to the vendor documentation. - -Here's some examples: - -- MySQL: http://dev.mysql.com/doc/refman/5.7/en/backup-methods.html -- PostgreSQL: https://www.postgresql.org/docs/current/static/backup.html - -SQLite -~~~~~~ -To backup the SQLite database, you just need to copy the directory `data/db` from the wallabag application directory. - -Images ------- -The images retrieved by wallabag are stored under `web/assets/images` (the images storage will be implemented in wallabag 2.2). diff --git a/docs/en/user/configuration.rst b/docs/en/user/configuration.rst deleted file mode 100644 index bba12cb9..00000000 --- a/docs/en/user/configuration.rst +++ /dev/null @@ -1,131 +0,0 @@ -Configuration -============= - -Now you're logged in, it's time to configure your account as you want. - -Click on ``Config`` menu. You have five tabs: ``Settings``, ``RSS``, -``User information``, ``Password`` and ``Tagging rules``. - -Settings --------- - -Theme -~~~~~ - -wallabag is customizable. You can choose your prefered theme here. The default theme is -``Material``, it's the theme used in the documentation screenshots. - -Items per page -~~~~~~~~~~~~~~ - -You can change the number of articles displayed on each page. - -Reading speed -~~~~~~~~~~~~~ - -wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are -a fast or a slow reader. wallabag will recalculate the reading time for each article. - -Where do you want to be redirected after mark an article as read? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Each time you'll do some actions (after marking an article as read/favorite, -after deleting an article, after removing a tag from an entry), you can be redirected: - -- To the homepage -- To the current page - -Language -~~~~~~~~ - -You can change the language of wallabag interface. - -RSS ---- - -wallabag provides RSS feeds for each article status: unread, starred and archive. - -Firstly, you need to create a personal token: click on ``Create your token``. -It's possible to change your token by clicking on ``Reset your token``. - -Now you have three links, one for each status: add them into your favourite RSS reader. - -You can also define how many articles you want in each RSS feed (default value: 50). - -There is also a pagination available for these feeds. You can add ``?page=2`` to jump to the second page. -The pagination follow `the RFC `_ about that, which means you'll find the ``next``, ``previous`` & ``last`` page link inside the `` tag of each RSS feed. - -User information ----------------- - -You can change your name, your email address and enable ``Two factor authentication``. - -If the wallabag instance has more than one enabled user, you can delete your account here. **Take care, we delete all your data**. - -Two factor authentication (2FA) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Two-factor authentication (also known as 2FA) is a technology patented in 1984 - that provides identification of users by means of the combination of two different components. - - https://en.wikipedia.org/wiki/Two-factor_authentication - -**Warning**: enabling 2FA from the configuration interface is only possible if it has been authorized before in `app/config/parameters.yml` by setting the *twofactor_auth* property to `true` (do not forget to run `php bin/console cache:clear -e=prod` after modification). - -If you enable 2FA, each time you want to login to wallabag, you'll receive -a code by email. You have to put this code on the following form. - -.. image:: ../../img/user/2FA_form.png - :alt: Two factor authentication - :align: center - -If you don't want to receive a code each time you want to login, you can check -the ``I'm on a trusted computer`` checkbox: wallabag will remember you for 15 days. - -Password --------- - -You can change your password here (8 characters minimum). - -Tagging rules -------------- - -If you want to automatically assign a tag to new articles, this part -of the configuration is for you. - -What does « tagging rules » mean? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -They are rules used by wallabag to automatically tag new entries. -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 to manually classify your entries. - -How do I use them? -~~~~~~~~~~~~~~~~~~ - -Let assume you want to tag new entries as *« short reading »* when -the reading time is inferior to 3 minutes. -In that case, you should put « readingTime <= 3 » in the **Rule** field -and *« short reading »* in the **Tags** field. -Several tags can added simultaneously by separating them by a comma: *« short reading, must read »*. -Complex rules can be written by using predefined operators: -if *« readingTime >= 5 AND domainName = "github.com" »* then tag as *« long reading, github »*. - -Which variables and operators can I use to write rules? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The following variables and operators can be used to create tagging rules (be careful, for some values, you need to add quotes, for example ``language = "en"``): - -=========== ============================================== ======== ========== -Variable Meaning Operator Meaning ------------ ---------------------------------------------- -------- ---------- -title Title of the entry <= Less than… -url URL of the entry < Strictly less than… -isArchived Whether the entry is archived or not => Greater than… -isStarred Whether the entry is starred or not > Strictly greater than… -content The entry's content = Equal to… -language The entry's language != Not equal to… -mimetype The entry's mime-type OR One rule or another -readingTime The estimated entry's reading time, in minutes AND One rule and another -domainName The domain name of the entry matches Tests that a subject is matches a search (case-insensitive). Example: title matches "football" -=========== ============================================== ======== ========== diff --git a/docs/en/user/configuring_mobile.rst b/docs/en/user/configuring_mobile.rst deleted file mode 100644 index 7229aed6..00000000 --- a/docs/en/user/configuring_mobile.rst +++ /dev/null @@ -1,13 +0,0 @@ -Configuring mobile apps to work with wallabag -============================================= - -Steps to configure the app --------------------------- - -- First *create a new client* in the ``API clients management`` section. The name of your app doesn't matter as much as the ``Client ID`` and the ``Client secret``. Write these two values down. -- When you download a mobile app, it's going to ask for the server address of the hosted wallabag instance. For example, with wallabag.it that is: ``https://app.wallabag.it``. -- It's also going to ask for the aforementioned ``Client ID`` and ``secret``. Please insert those in the text fields, when asked. -- Lastly you need to provide your ``username`` and ``password``. These are the same credentials you use to login in to wallabag. - -Please also have a look at `Android `_ page. Especially the section on *known limitations*. - diff --git a/docs/en/user/create_account.rst b/docs/en/user/create_account.rst deleted file mode 100644 index 8c43867d..00000000 --- a/docs/en/user/create_account.rst +++ /dev/null @@ -1,42 +0,0 @@ -Create an account and authentication -==================================== - -Register --------- - -On the login page, click on ``Register`` button. - -.. image:: ../../img/user/registration_form.png - :alt: Registration form - :align: center - -You have to fill the form. Please sure to type a valid email address, -we'll send you an activation email. - -.. image:: ../../img/user/sent_email.png - :alt: Email was sent to activate account - :align: center - -Check your inbox, you now have a new mail with a link like this -``http://wallabag/register/confirm/Ba19wokGovN-DdBQNfg4YgRkUQWRP4-k2g0Bk-hBTX4``. -Click on it to activate your account. - -Your account is now activated. - -.. image:: ../../img/user/activated_account.png - :alt: Welcome on board! - :align: center - -Login ------ - -Your account is now enabled, congratulations! - -To login to wallabag, fill the form on login page. - -If you are on your personal computer and you want to stay connected, -you can check the ``Keep me logged in`` checkbox: wallabag will remember you for one year. - -.. image:: ../../img/user/login_form.png - :alt: Login form - :align: center diff --git a/docs/en/user/errors_during_fetching.rst b/docs/en/user/errors_during_fetching.rst deleted file mode 100644 index c5e18d3b..00000000 --- a/docs/en/user/errors_during_fetching.rst +++ /dev/null @@ -1,37 +0,0 @@ -Errors during fetching articles -=============================== - -Why does the fetch of an article fail? --------------------------------------- - -There may be several reasons: - -- network problem -- wallabag can't fetch content due to the website structure - -How can I help to fix that? ---------------------------- - -You can try to fix this problem by yourself (so we can be focused on improving wallabag internally instead of writing siteconfig :) ). - -You can try to see if it works here: `http://f43.me/feed/test `_ (it uses almost the same system as wallabag to retrieve content). - -If it works here and not on wallabag, it means there is something internally in wallabag that breaks the parser (hard to fix: please open an issue about it). - -If it doesn't works, try to extract a site config using: `http://siteconfig.fivefilters.org/ `_ (select which part of the content is actually the content). You can `read this documentation before `_. - -You can test it on **f43.me** website: click on **Want to try a custom siteconfig?** and put the generated file from siteconfig.fivefilters.org. - -Repeat until you have something ok. - -Then you can submit a pull request to `https://github.com/fivefilters/ftr-site-config `_ which is the global repo for siteconfig files. - -How can I try to re-fetch this article? ---------------------------------------- - -If wallabag failed when fetching an article, you can click on the reload button -(the third on the below picture). - -.. image:: ../../img/user/refetch.png - :alt: Refetch content - :align: center diff --git a/docs/en/user/faq.rst b/docs/en/user/faq.rst deleted file mode 100644 index 0f995ce5..00000000 --- a/docs/en/user/faq.rst +++ /dev/null @@ -1,55 +0,0 @@ -Frequently Asked Questions -========================== - -During the installation, I got the error ``Error Output: sh: 1: @post-cmd: not found`` --------------------------------------------------------------------------------------- - -It seems you have a problem with your ``composer`` installation. Try to uninstall and reinstall it. - -`Read the documentation about composer to know how to install it -`__. - -I can't validate the registration form --------------------------------------- - -Ensure that all fields are properly filled: - -* valid email address -* same passwords in two fields - -I'm not receiving my activation email -------------------------------------- - -Are you sure your email address was correct? Did you check your spam folder? - -If you still don't see the activation email, please ensure that you have -installed and properly configured a mail transfer agent. Be sure to include a -firewall rule for SMTP. E.g., if using firewalld: - -:: - - firewall-cmd --permanent --add-service=smtp - firewall-cmd --reload - -Lastly, if you have SELinux enabled, set the following rule: - -``setsebool -P httpd_can_sendmail 1`` - -When I click on the activation link, I've got this message: ``The user with confirmation token "DtrOPfbQeVkWf6N" does not exist``. ----------------------------------------------------------------------------------------------------------------------------------- - -You already enabled your account or the URL of the activation email is wrong. - -I forgot my password --------------------- - -You can reset your password by clicking on ``Forgot your password?`` link, -on the login page. Then, fill the form with your email address or your username, -you'll receive an email to reset your password. - -I've got the ``failed to load external entity`` error when I try to install wallabag ------------------------------------------------------------------------------------- - -As described `here `_, please edit your ``web/app.php`` file and add this line: ``libxml_disable_entity_loader(false);`` on line 5. - -This is a Doctrine / PHP bug, nothing we can do about it. diff --git a/docs/en/user/filters.rst b/docs/en/user/filters.rst deleted file mode 100644 index aae8a749..00000000 --- a/docs/en/user/filters.rst +++ /dev/null @@ -1,54 +0,0 @@ -Retrieve your articles thanks to filters -======================================== - -To retrieve articles easily, you can use filters. -Click on the third icon in the top bar. - -.. image:: ../../img/user/topbar.png - :alt: Top bar - :align: center - -All these filters can be combined. - -.. image:: ../../img/user/filters.png - :alt: Combine all filters - :align: center - -Status ------- - -Use these checkboxes to find archived or starred articles. - -Preview picture ---------------- - -Check this filter if you want to retrieve articles with a preview picture. - -Language --------- - -wallabag (via graby) can detect article language. It's easy to you to retrieve articles -written in a specific language. - -HTTP status ------------ - -You can retrieve the articles by filtering by their HTTP status code: 200, 404, 500, etc. - -Reading time ------------- - -wallabag estimates how many time you need to read an article. With this filter, -you can for example find the articles with a reading time between 2 and 5 minutes. - -Domain name ------------ - -Thanks to this filter, you can retrieve the articles from the same domain name. -For example, in this field, type ``bbc.co.uk`` to retrieve the articles of this website. - -Creation date -------------- - -When you save an article, wallabag stored the current date. So handful to retrieve articles written -between 1st and 31th January for example. diff --git a/docs/en/user/import.rst b/docs/en/user/import.rst deleted file mode 100644 index f420a131..00000000 --- a/docs/en/user/import.rst +++ /dev/null @@ -1,154 +0,0 @@ -Migrate from ... -================ - -In wallabag 2.x, you can import data from: - -- `Pocket <#id1>`_ -- `Readability <#id2>`_ -- `Instapaper <#id4>`_ -- `wallabag 1.x <#id6>`_ -- `wallabag 2.x <#id7>`_ - -We also developed `a script to execute migrations via command-line interface <#import-via-command-line-interface-cli>`_. - -Because imports can take ages, we developed an asynchronous tasks system. `You can read the documentation here `_ (for experts). - -Pocket ------- - -Create a new application on Pocket -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To import your data from Pocket, we use the Pocket API. You need to create -a new application on their developer website to continue. - -* Create a new application `on the developer website `_ -* Fill in the required fields: application name, application description, - permissions (only **retrieve**), platform (**web**), accept the terms of service - and submit your new application - -Pocket will give you a **Consumer Key** (for example, `49961-985e4b92fe21fe4c78d682c1`). -You need to configure the ``pocket_consumer_key`` in the ``Config`` menu. - -Now, all is fine to migrate from Pocket. - -Import your data into wallabag 2.x -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Click on ``Import`` link in the menu, on ``Import contents`` in Pocket section -and then on ``Connect to Pocket and import data``. - -You need to authorize wallabag to interact with your Pocket account. -Your data will be imported. Data import can be a demanding process for your server. - -Readability ------------ - -Export your Readability data -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -On the tools (`https://www.readability.com/tools/ `_) page, click on "Export your data" in the "Data Export" section. You will received an email to download a json (which does not end with .json in fact). - -Import your data into wallabag 2.x -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Click on ``Import`` link in the menu, on ``Import contents`` in Readability section -and then select your json file and upload it. - -Your data will be imported. Data import can be a demanding process for your server. - -From Pinboard -------------- - -Export your Pinboard data -~~~~~~~~~~~~~~~~~~~~~~~~~ - -On the backup (`https://pinboard.in/settings/backup `_) page, click on "JSON" in the "Bookmarks" section. A JSON file will be downloaded (like ``pinboard_export``). - -Import your data into wallabag 2.x -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Click on ``Import`` link in the menu, on ``Import contents`` in Pinboard section -and then select your json file and upload it. - -Your data will be imported. Data import can be a demanding process for your server. - -From Instapaper ---------------- - -Export your Instapaper data -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -On the settings (`https://www.instapaper.com/user `_) page, click on "Download .CSV file" in the "Export" section. A CSV file will be downloaded (like ``instapaper-export.csv``). - -Import your data into wallabag 2.x -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Click on ``Import`` link in the menu, on ``Import contents`` in Instapaper section -and then select your CSV file and upload it. - -Your data will be imported. Data import can be a demanding process for your server. - -wallabag 1.x ------------- - -If you were using wallabag v1.x, you need to export your data before migrating to wallabag v2.x, because the application and its database changed a lot. In your old wallabag installation, you can export your data, which can be done on the Config page of your old wallabag installation. - -.. image:: ../../img/user/export_v1.png - :alt: Exporting from wallabag v1 - :align: center - -.. note:: - If you have multiple accounts on the same instance of wallabag, each user must export from v1 and import into v2 its data. - -.. note:: - If you encounter issues during the export or the import, don't hesitate to `ask for support `__. - -When you have retrieved the json file containing your entries, you can install wallabag v2 if needed by following `the standard procedure `__. - -After creating an user account on your new wallabag v2 instance, you must head over to the `Import` section and select `Import from wallabag v1`. Select your json file and upload it. - -.. image:: ../../img/user/import_wallabagv1.png - :alt: Import from wallabag v1 - :align: center - -wallabag 2.x ------------- - -From the previous wallabag instance on which you were before, go to `All articles`, then export these articles as json. - -.. image:: ../../img/user/export_v2.png - :alt: Export depuis wallabag v2 - :align: center - -From your new wallabag instance, create your user account and click on the link in the menu to proceed to import. Choose import from wallabag v2 and select your json file to upload it. - -.. note:: - If you encounter issues during the export or the import, don't hesitate to `ask for support `__. - -Import via command-line interface (CLI) ---------------------------------------- - -If you have a CLI access on your web server, you can execute this command to import your wallabag v1 export: - -:: - - bin/console wallabag:import 1 ~/Downloads/wallabag-export-1-2016-04-05.json --env=prod - -Please replace values: - -* ``1`` is the user identifier in database (The ID of the first user created on wallabag is 1) -* ``~/Downloads/wallabag-export-1-2016-04-05.json`` is the path of your wallabag v1 export - -If you want to mark all these entries as read, you can add the ``--markAsRead`` option. - -To import a wallabag v2 file, you need to add the option ``--importer=v2``. - -You'll have this in return: - -:: - - Start : 05-04-2016 11:36:07 --- - 403 imported - 0 already saved - End : 05-04-2016 11:36:09 --- diff --git a/docs/en/user/installation.rst b/docs/en/user/installation.rst deleted file mode 100644 index 2b730b83..00000000 --- a/docs/en/user/installation.rst +++ /dev/null @@ -1,357 +0,0 @@ -Install wallabag -================ - -Requirements ------------- - -wallabag is compatible with PHP >= 5.5, including PHP 7. - -.. note:: - - To install wallabag easily, we provide a ``Makefile``, so you need to have the ``make`` tool. - -wallabag uses a large number of PHP libraries in order to function. These libraries must be installed with a tool called Composer. You need to install it if you have not already done so and be sure to use the 1.2 version (if you already have Composer, run a ``composer selfupdate``). - -Install Composer: - -:: - - curl -s https://getcomposer.org/installer | php - -You can find specific instructions `here `__. - -You'll also need the following extensions for wallabag to work. Some of these may already activated in your version of PHP, so you may not have to install all corresponding packages. - -- php-session -- php-ctype -- php-dom -- php-hash -- php-simplexml -- php-json -- php-gd -- php-mbstring -- php-xml -- php-tidy -- php-iconv -- php-curl -- php-gettext -- php-tokenizer -- php-bcmath - -wallabag uses PDO to connect to the database, so you'll need one of the following: - -- pdo_mysql -- pdo_sqlite -- pdo_pgsql - -and its corresponding database server. - -Installation ------------- - -On a dedicated web server (recommended way) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To install wallabag itself, you must run the following commands: - -:: - - git clone https://github.com/wallabag/wallabag.git - cd wallabag && make install - -To start PHP's build-in server and test if everything did install correctly, you can do: - -:: - - make run - -And access wallabag at http://yourserverip:8000 - -.. tip:: - - To define parameters with environment variables, you have to set these variables with ``SYMFONY__`` prefix. For example, ``SYMFONY__DATABASE_DRIVER``. You can have a look at `Symfony documentation `__. - -On a shared hosting -~~~~~~~~~~~~~~~~~~~ - -We provide a package with all dependencies inside. -The default configuration uses SQLite for the database. If you want to change these settings, please edit ``app/config/parameters.yml``. - -We already created a user: login and password are ``wallabag``. - -.. caution:: With this package, wallabag doesn't check for mandatory extensions used in the application (theses checks are made during ``composer install`` when you have a dedicated web server, see above). - -Execute this command to download and extract the latest package: - -.. code-block:: bash - - wget https://wllbg.org/latest-v2-package && tar xvf latest-v2-package - -You will find the `md5 hash of the latest package on our website `_. - -Now, read the following documentation to create your virtual host, then access your wallabag. -If you changed the database configuration to use MySQL or PostgreSQL, you need to create a user via this command ``php bin/console wallabag:install --env=prod``. - -Installation with Docker -~~~~~~~~~~~~~~~~~~~~~~~~ - -We provide you a Docker image to install wallabag easily. Have a look at our repository on `Docker Hub `__ for more information. - -Command to launch container -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: bash - - docker pull wallabag/wallabag - -Virtual hosts -------------- - -Configuration on Apache -~~~~~~~~~~~~~~~~~~~~~~~ - -Do not forget to active the *rewrite* mod of Apache - -.. code-block:: bash - - a2enmod rewrite && systemctl reload apache2 - -Assuming you install wallabag in the ``/var/www/wallabag`` folder and that you want to use PHP as an Apache module, here's a vhost for wallabag: - -:: - - - ServerName domain.tld - ServerAlias www.domain.tld - - DocumentRoot /var/www/wallabag/web - - AllowOverride None - Order Allow,Deny - Allow from All - - - Options -MultiViews - RewriteEngine On - RewriteCond %{REQUEST_FILENAME} !-f - RewriteRule ^(.*)$ app.php [QSA,L] - - - - # uncomment the following lines if you install assets as symlinks - # or run into problems when compiling LESS/Sass/CoffeScript assets - # - # Options FollowSymlinks - # - - # optionally disable the RewriteEngine for the asset directories - # which will allow apache to simply reply with a 404 when files are - # not found instead of passing the request into the full symfony stack - - - RewriteEngine Off - - - ErrorLog /var/log/apache2/wallabag_error.log - CustomLog /var/log/apache2/wallabag_access.log combined - - - -.. tip:: Note for Apache 2.4, in the section `` you have to replace the directives : - -:: - - AllowOverride None - Order Allow,Deny - Allow from All - - -by - -:: - - Require All granted - - -After reloading or restarting Apache, you should now be able to access wallabag at http://domain.tld. - -Configuration on Nginx -~~~~~~~~~~~~~~~~~~~~~~ - -Assuming you installed wallabag in the ``/var/www/wallabag`` folder, here's the recipe for wallabag : - -:: - - server { - server_name domain.tld www.domain.tld; - root /var/www/wallabag/web; - - location / { - # try to serve file directly, fallback to app.php - try_files $uri /app.php$is_args$args; - } - location ~ ^/app\.php(/|$) { - fastcgi_pass unix:/var/run/php5-fpm.sock; - 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; - } - - # return 404 for all other php files not matching the front controller - # this prevents access to other php files you don't want to be accessible. - location ~ \.php$ { - return 404; - } - - error_log /var/log/nginx/wallabag_error.log; - access_log /var/log/nginx/wallabag_access.log; - } - -After reloading or restarting nginx, you should now be able to access wallabag at http://domain.tld. - -.. tip:: - - When you want to import large files into wallabag, you need to add this line in your nginx configuration ``client_max_body_size XM; # allows file uploads up to X megabytes``. - -Configuration on lighttpd -~~~~~~~~~~~~~~~~~~~~~~~~~ - -Assuming you install wallabag in the ``/var/www/wallabag`` folder, here's the recipe for wallabag (edit your ``lighttpd.conf`` file and paste this configuration into it): - -:: - - server.modules = ( - "mod_fastcgi", - "mod_access", - "mod_alias", - "mod_compress", - "mod_redirect", - "mod_rewrite", - ) - server.document-root = "/var/www/wallabag/web" - server.upload-dirs = ( "/var/cache/lighttpd/uploads" ) - server.errorlog = "/var/log/lighttpd/error.log" - server.pid-file = "/var/run/lighttpd.pid" - server.username = "www-data" - server.groupname = "www-data" - server.port = 80 - server.follow-symlink = "enable" - index-file.names = ( "index.php", "index.html", "index.lighttpd.html") - url.access-deny = ( "~", ".inc" ) - static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" ) - compress.cache-dir = "/var/cache/lighttpd/compress/" - compress.filetype = ( "application/javascript", "text/css", "text/html", "text/plain" ) - include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port - include_shell "/usr/share/lighttpd/create-mime.assign.pl" - include_shell "/usr/share/lighttpd/include-conf-enabled.pl" - dir-listing.activate = "disable" - - url.rewrite-if-not-file = ( - "^/([^?]*)(?:\?(.*))?" => "/app.php?$1&$2", - "^/([^?]*)" => "/app.php?=$1", - ) - -Rights access to the folders of the project -------------------------------------------- - -Test environment -~~~~~~~~~~~~~~~~ - -When we just want to test wallabag, we just run the command ``make run`` to start our wallabag instance and everything will go smoothly because the user who started the project can access to the current folder naturally, without any problem. - -Production environment -~~~~~~~~~~~~~~~~~~~~~~ - -As soon as we use Apache or Nginx to access to our wallabag instance, and not from the command ``make run`` to start it, we should take care to grant the good rights on the good folders to keep safe all the folders of the project. - -To do so, the folder name, known as ``DocumentRoot`` (for apache) or ``root`` (for Nginx), has to be absolutely accessible by the Apache/Nginx user. Its name is generally ``www-data``, ``apache`` or ``nobody`` (depending on linux system used). - -So the folder ``/var/www/wallabag/web`` has to be accessible by this last one. But this may not be enough if we just care about this folder, because we could meet a blank page or get an error 500 when trying to access to the homepage of the project. - -This is due to the fact that we will need to grant the same rights access on the folder ``/var/www/wallabag/var`` like those we gave on the folder ``/var/www/wallabag/web``. Thus, we fix this problem with the following command: - -.. code-block:: bash - - chown -R www-data:www-data /var/www/wallabag/var - -It has to be the same for the following folders - -* /var/www/wallabag/bin/ -* /var/www/wallabag/app/config/ -* /var/www/wallabag/vendor/ -* /var/www/wallabag/data/ - -by entering - -.. code-block:: bash - - chown -R www-data:www-data /var/www/wallabag/bin - chown -R www-data:www-data /var/www/wallabag/app/config - chown -R www-data:www-data /var/www/wallabag/vendor - chown -R www-data:www-data /var/www/wallabag/data/ - -otherwise, sooner or later you will see these error messages: - -.. code-block:: bash - - Unable to write to the "bin" directory. - file_put_contents(app/config/parameters.yml): failed to open stream: Permission denied - file_put_contents(/.../wallabag/vendor/autoload.php): failed to open stream: Permission denied - -Additional rules for SELinux -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If SELinux is enabled on your system, you will need to configure additional contexts in order for wallabag to function properly. To check if SELinux is enabled, simply enter the following: - -``getenforce`` - -This will return ``Enforcing`` if SELinux is enabled. Creating a new context involves the following syntax: - -``semanage fcontext -a -t `` - -For example: - -``semanage fcontext -a -t httpd_sys_content_t "/var/www/wallabag(/.*)?"`` - -This will recursively apply the httpd_sys_content_t context to the wallabag directory and all underlying files and folders. The following rules are needed: - -+-----------------------------------+----------------------------+ -| Full path | Context | -+===================================+============================+ -| /var/www/wallabag(/.*)? | ``httpd_sys_content_t`` | -+-----------------------------------+----------------------------+ -| /var/www/wallabag/data(/.*)? | ``httpd_sys_rw_content_t`` | -+-----------------------------------+----------------------------+ -| /var/www/wallabag/var/logs(/.*)? | ``httpd_log_t`` | -+-----------------------------------+----------------------------+ -| /var/www/wallabag/var/cache(/.*)? | ``httpd_cache_t`` | -+-----------------------------------+----------------------------+ - -After creating these contexts, enter the following in order to apply your rules: - -``restorecon -R -v /var/www/wallabag`` - -You can check contexts in a directory by typing ``ls -lZ`` and you can see all of your current rules with ``semanage fcontext -l -C``. - -If you're installing the preconfigured latest-v2-package, then an additional rule is needed during the initial setup: - -``semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/wallabag/var"`` - -After you successfully access your wallabag and complete the initial setup, this context can be removed: - -:: - - semanage fcontext -d -t httpd_sys_rw_content_t "/var/www/wallabag/var" - retorecon -R -v /var/www/wallabag/var diff --git a/docs/en/user/parameters.rst b/docs/en/user/parameters.rst deleted file mode 100644 index d8a209e7..00000000 --- a/docs/en/user/parameters.rst +++ /dev/null @@ -1,96 +0,0 @@ -What is the meaning of the parameters? -====================================== - -Default `parameters.yml` file ------------------------------ - -Here is the last version of the default `app/config/parameters.yml` file. Be sure that yours respects this one. -If you don't know which value you need to set, please leave the default one. - -.. code-block:: yml - - parameters: - database_driver: pdo_sqlite - database_host: 127.0.0.1 - database_port: null - database_name: symfony - database_user: root - database_password: null - database_path: '%kernel.root_dir%/../data/db/wallabag.sqlite' - database_table_prefix: wallabag_ - database_socket: null - mailer_transport: smtp - mailer_host: 127.0.0.1 - mailer_user: null - mailer_password: null - locale: en - secret: ovmpmAWXRCabNlMgzlzFXDYmCFfzGv - twofactor_auth: true - twofactor_sender: no-reply@wallabag.org - fosuser_registration: true - fosuser_confirmation: true - from_email: no-reply@wallabag.org - rss_limit: 50 - rabbitmq_host: localhost - rabbitmq_port: 5672 - rabbitmq_user: guest - rabbitmq_password: guest - redis_scheme: tcp - redis_host: localhost - redis_port: 6379 - redis_path: null - redis_password: null - -Meaning of each parameter -------------------------- - -.. csv-table:: Database parameters - :header: "name", "default", "description" - - "database_driver", "pdo_sqlite", "Should be pdo_sqlite or pdo_mysql or pdo_pgsql" - "database_host", "127.0.0.1", "host of your database (usually localhost or 127.0.0.1)" - "database_port", "~", "port of your database (you can leave ``~`` to use the default one)" - "database_name", "symfony", "name of your database" - "database_user", "root", "user that can write to this database" - "database_password", "~", "password of that user" - "database_path", "``""%kernel.root_dir%/../data/db/wallabag.sqlite""``", "only for SQLite, define where to put the database file. Leave it empty for other database" - "database_table_prefix", "wallabag_", "all wallabag's tables will be prefixed with that string. You can include a ``_`` for clarity" - "database_socket", "null", "If your database is using a socket instead of tcp, put the path of the socket (other connection parameters will then be ignored)" - "database_charset", "utf8mb4", "For PostgreSQL & SQLite you should use utf8, for MySQL use utf8mb4 which handle emoji" - -.. csv-table:: Configuration to send emails from wallabag - :header: "name", "default", "description" - - "mailer_transport", "smtp", "The exact transport method to use to deliver emails. Valid values are: smtp, gmail, mail, sendmail, null (which will disable the mailer)" - "mailer_host", "127.0.0.1", "The host to connect to when using smtp as the transport." - "mailer_user", "~", "The username when using smtp as the transport." - "mailer_password", "~", "The password when using smtp as the transport." - -.. csv-table:: Other wallabag's option - :header: "name", "default", "description" - - "locale", "en", "Default language of your wallabag instance (like en, fr, es, etc.)" - "secret", "ovmpmAWXRCabNlMgzlzFXDYmCFfzGv", "This is a string that should be unique to your application and it's commonly used to add more entropy to security related operations." - "twofactor_auth", "true", "true to enable Two factor authentication" - "twofactor_sender", "no-reply@wallabag.org", "email of the email sender to receive the two factor code" - "fosuser_registration", "true", "true to enable public registration" - "fosuser_confirmation", "true", "true to send a confirmation by email for each registration" - "from_email", "no-reply@wallabag.org", "email address used in From: field in each email" - "rss_limit", "50", "limit for RSS feeds" - -.. csv-table:: RabbitMQ configuration - :header: "name", "default", "description" - - "rabbitmq_host", "localhost", "Host of your RabbitMQ" - "rabbitmq_port", "5672", "Port of your RabbitMQ" - "rabbitmq_user", "guest", "User that can read queues" - "rabbitmq_password", "guest", "Password of that user" - -.. csv-table:: Redis configuration - :header: "name", "default", "description" - - "redis_scheme", "tcp", "Specifies the protocol used to communicate with an instance of Redis. Valid values are: tcp, unix, http" - "redis_host", "localhost", "IP or hostname of the target server (ignored for unix scheme)" - "redis_port", "6379", "TCP/IP port of the target server (ignored for unix scheme)" - "redis_path", "null", "Path of the UNIX domain socket file used when connecting to Redis using UNIX domain sockets" - "redis_password", "null", "Password defined in the Redis server configuration (parameter `requirepass` in `redis.conf`)" diff --git a/docs/en/user/query-upgrade-21-22.rst b/docs/en/user/query-upgrade-21-22.rst deleted file mode 100644 index fa9835a8..00000000 --- a/docs/en/user/query-upgrade-21-22.rst +++ /dev/null @@ -1,984 +0,0 @@ -Migration 20161001072726 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_entry_tag DROP FOREIGN KEY FK_C9F0DD7CBA364942 - ALTER TABLE wallabag_entry_tag DROP FOREIGN KEY FK_C9F0DD7CBAD26311 - ALTER TABLE wallabag_entry_tag ADD CONSTRAINT FK_entry_tag_entry FOREIGN KEY (entry_id) REFERENCES wallabag_entry (id) ON DELETE CASCADE - ALTER TABLE wallabag_entry_tag ADD CONSTRAINT FK_entry_tag_tag FOREIGN KEY (tag_id) REFERENCES wallabag_tag (id) ON DELETE CASCADE - ALTER TABLE wallabag_annotation DROP FOREIGN KEY FK_A7AED006BA364942 - ALTER TABLE wallabag_annotation ADD CONSTRAINT FK_annotation_entry FOREIGN KEY (entry_id) REFERENCES wallabag_entry (id) ON DELETE CASCADE - -Migration down -"""""""""""""" - -We didn't write down migration for ``20161001072726``. - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_entry_tag DROP CONSTRAINT fk_c9f0dd7cba364942 - ALTER TABLE wallabag_entry_tag DROP CONSTRAINT fk_c9f0dd7cbad26311 - ALTER TABLE wallabag_entry_tag ADD CONSTRAINT FK_entry_tag_entry FOREIGN KEY (entry_id) REFERENCES wallabag_entry (id) ON DELETE CASCADE - ALTER TABLE wallabag_entry_tag ADD CONSTRAINT FK_entry_tag_tag FOREIGN KEY (tag_id) REFERENCES wallabag_tag (id) ON DELETE CASCADE - ALTER TABLE wallabag_annotation DROP CONSTRAINT fk_a7aed006ba364942 - ALTER TABLE wallabag_annotation ADD CONSTRAINT FK_annotation_entry FOREIGN KEY (entry_id) REFERENCES wallabag_entry (id) ON DELETE CASCADE - -Migration down -"""""""""""""" - -We didn't write down migration for ``20161001072726``. - -SQLite -^^^^^^ - -This migration can only be executed safely on MySQL or PostgreSQL. - -Migration 20161022134138 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER DATABASE wallabag CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; - ALTER TABLE wallabag_user CHANGE confirmation_token confirmation_token VARCHAR(180) DEFAULT NULL; - ALTER TABLE wallabag_user CHANGE salt salt VARCHAR(180) NOT NULL; - ALTER TABLE wallabag_user CHANGE password password VARCHAR(180) NOT NULL; - ALTER TABLE wallabag_annotation CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - ALTER TABLE wallabag_entry CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - ALTER TABLE wallabag_tag CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - ALTER TABLE wallabag_user CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - ALTER TABLE wallabag_annotation CHANGE `text` `text` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - ALTER TABLE wallabag_annotation CHANGE `quote` `quote` VARCHAR(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - ALTER TABLE wallabag_entry CHANGE `title` `title` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - ALTER TABLE wallabag_entry CHANGE `content` `content` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - ALTER TABLE wallabag_tag CHANGE `label` `label` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - ALTER TABLE wallabag_user CHANGE `name` `name` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER DATABASE wallabag CHARACTER SET = utf8 COLLATE = utf8_unicode_ci; - ALTER TABLE wallabag_annotation CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci; - ALTER TABLE wallabag_entry CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci; - ALTER TABLE wallabag_tag CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci; - ALTER TABLE wallabag_user CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci; - ALTER TABLE wallabag_annotation CHANGE `text` `text` longtext CHARACTER SET utf8 COLLATE utf8_unicode_ci; - ALTER TABLE wallabag_annotation CHANGE `quote` `quote` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci; - ALTER TABLE wallabag_entry CHANGE `title` `title` longtext CHARACTER SET utf8 COLLATE utf8_unicode_ci; - ALTER TABLE wallabag_entry CHANGE `content` `content` longtext CHARACTER SET utf8 COLLATE utf8_unicode_ci; - ALTER TABLE wallabag_tag CHANGE `label` `label` longtext CHARACTER SET utf8 COLLATE utf8_unicode_ci; - ALTER TABLE wallabag_user CHANGE `name` `name` longtext CHARACTER SET utf8 COLLATE utf8_unicode_ci; - -PostgreSQL and SQLite -^^^^^^^^^^^^^^^^^^^^^ - -This migration only apply to MySQL. - -Migration 20161024212538 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_oauth2_clients ADD user_id INT NOT NULL - ALTER TABLE wallabag_oauth2_clients ADD CONSTRAINT IDX_user_oauth_client FOREIGN KEY (user_id) REFERENCES wallabag_user (id) ON DELETE CASCADE - CREATE INDEX IDX_635D765EA76ED395 ON wallabag_oauth2_clients (user_id) - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_oauth2_clients DROP FOREIGN KEY IDX_user_oauth_client - ALTER TABLE wallabag_oauth2_clients DROP user_id - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_oauth2_clients ADD user_id INT DEFAULT NULL - ALTER TABLE wallabag_oauth2_clients ADD CONSTRAINT IDX_user_oauth_client FOREIGN KEY (user_id) REFERENCES wallabag_user (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE - CREATE INDEX IDX_635D765EA76ED395 ON wallabag_oauth2_clients (user_id) - - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_oauth2_clients DROP CONSTRAINT idx_user_oauth_client - ALTER TABLE wallabag_oauth2_clients DROP user_id - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - CREATE TEMPORARY TABLE __temp__wallabag_oauth2_clients AS SELECT id, random_id, redirect_uris, secret, allowed_grant_types, name FROM wallabag_oauth2_clients - DROP TABLE wallabag_oauth2_clients - CREATE TABLE wallabag_oauth2_clients (id INTEGER NOT NULL, user_id INTEGER DEFAULT NULL, random_id VARCHAR(255) NOT NULL COLLATE BINARY, redirect_uris CLOB NOT NULL COLLATE BINARY, secret VARCHAR(255) NOT NULL COLLATE BINARY, allowed_grant_types CLOB NOT NULL COLLATE BINARY, name CLOB DEFAULT NULL COLLATE BINARY, PRIMARY KEY(id), CONSTRAINT IDX_user_oauth_client FOREIGN KEY (user_id) REFERENCES wallabag_user (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE) - INSERT INTO wallabag_oauth2_clients (id, random_id, redirect_uris, secret, allowed_grant_types, name) SELECT id, random_id, redirect_uris, secret, allowed_grant_types, name FROM __temp__wallabag_oauth2_clients - DROP TABLE __temp__wallabag_oauth2_clients - CREATE INDEX IDX_635D765EA76ED395 ON wallabag_oauth2_clients (user_id) - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX IDX_635D765EA76ED395 - CREATE TEMPORARY TABLE __temp__wallabag_oauth2_clients AS SELECT id, random_id, redirect_uris, secret, allowed_grant_types, name FROM wallabag_oauth2_clients - DROP TABLE wallabag_oauth2_clients - CREATE TABLE wallabag_oauth2_clients (id INTEGER NOT NULL, random_id VARCHAR(255) NOT NULL COLLATE BINARY, redirect_uris CLOB NOT NULL COLLATE BINARY, secret VARCHAR(255) NOT NULL COLLATE BINARY, allowed_grant_types CLOB NOT NULL COLLATE BINARY, name CLOB DEFAULT NULL COLLATE BINARY, PRIMARY KEY(id)) - INSERT INTO wallabag_oauth2_clients (id, random_id, redirect_uris, secret, allowed_grant_types, name) SELECT id, random_id, redirect_uris, secret, allowed_grant_types, name FROM __temp__wallabag_oauth2_clients - DROP TABLE __temp__wallabag_oauth2_clients - -Migration 20161031132655 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('download_images_enabled', 0, 'misc') - -Migration down -"""""""""""""" - -.. code-block:: sql - - DELETE FROM wallabag_craue_config_setting WHERE name = 'download_images_enabled'; - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('download_images_enabled', 0, 'misc') - -Migration down -"""""""""""""" - -.. code-block:: sql - - DELETE FROM wallabag_craue_config_setting WHERE name = 'download_images_enabled'; - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('download_images_enabled', 0, 'misc') - -Migration down -"""""""""""""" - -.. code-block:: sql - - DELETE FROM wallabag_craue_config_setting WHERE name = 'download_images_enabled'; - -Migration 20161104073720 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - CREATE INDEX IDX_entry_created_at ON wallabag_entry (created_at) - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX IDX_entry_created_at ON wallabag_entry - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - CREATE INDEX IDX_entry_created_at ON wallabag_entry (created_at) - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX idx_entry_created_at - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - DROP INDEX created_at_idx - DROP INDEX IDX_F4D18282A76ED395 - CREATE TEMPORARY TABLE __temp__wallabag_entry AS SELECT id, user_id, uuid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public FROM wallabag_entry - DROP TABLE wallabag_entry - CREATE TABLE wallabag_entry (id INTEGER NOT NULL, user_id INTEGER DEFAULT NULL, uuid CLOB DEFAULT NULL COLLATE BINARY, title CLOB DEFAULT NULL COLLATE BINARY, url CLOB DEFAULT NULL COLLATE BINARY, is_archived BOOLEAN NOT NULL, is_starred BOOLEAN NOT NULL, content CLOB DEFAULT NULL COLLATE BINARY, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, mimetype CLOB DEFAULT NULL COLLATE BINARY, language CLOB DEFAULT NULL COLLATE BINARY, reading_time INTEGER DEFAULT NULL, domain_name CLOB DEFAULT NULL COLLATE BINARY, preview_picture CLOB DEFAULT NULL COLLATE BINARY, is_public BOOLEAN DEFAULT '0', PRIMARY KEY(id)) - INSERT INTO wallabag_entry (id, user_id, uuid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public) SELECT id, user_id, uuid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public FROM __temp__wallabag_entry - DROP TABLE __temp__wallabag_entry - CREATE INDEX created_at_idx ON wallabag_entry (created_at) - CREATE INDEX IDX_F4D18282A76ED395 ON wallabag_entry (user_id) - CREATE INDEX IDX_entry_created_at ON wallabag_entry (created_at) - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX IDX_entry_created_at - DROP INDEX IDX_F4D18282A76ED395 - DROP INDEX created_at_idx - CREATE TEMPORARY TABLE __temp__wallabag_entry AS SELECT id, user_id, uuid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public FROM wallabag_entry - DROP TABLE wallabag_entry - CREATE TABLE wallabag_entry (id INTEGER NOT NULL, user_id INTEGER DEFAULT NULL, uuid CLOB DEFAULT NULL COLLATE BINARY, title CLOB DEFAULT NULL COLLATE BINARY, url CLOB DEFAULT NULL COLLATE BINARY, is_archived BOOLEAN NOT NULL, is_starred BOOLEAN NOT NULL, content CLOB DEFAULT NULL COLLATE BINARY, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, mimetype CLOB DEFAULT NULL COLLATE BINARY, language CLOB DEFAULT NULL COLLATE BINARY, reading_time INTEGER DEFAULT NULL, domain_name CLOB DEFAULT NULL COLLATE BINARY, preview_picture CLOB DEFAULT NULL COLLATE BINARY, is_public BOOLEAN DEFAULT '0', PRIMARY KEY(id)) - INSERT INTO wallabag_entry (id, user_id, uuid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public) SELECT id, user_id, uuid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public FROM __temp__wallabag_entry - DROP TABLE __temp__wallabag_entry - CREATE INDEX IDX_F4D18282A76ED395 ON wallabag_entry (user_id) - CREATE INDEX created_at_idx ON wallabag_entry (created_at) - -Migration 20161106113822 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_config ADD action_mark_as_read INT DEFAULT 0 - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_config DROP action_mark_as_read - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_config ADD action_mark_as_read INT DEFAULT 0 - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_config DROP action_mark_as_read - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_config ADD COLUMN action_mark_as_read INTEGER DEFAULT 0 - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX UNIQ_87E64C53A76ED395 - CREATE TEMPORARY TABLE __temp__wallabag_config AS SELECT id, user_id, theme, items_per_page, language, rss_token, rss_limit, reading_speed, pocket_consumer_key FROM wallabag_config - DROP TABLE wallabag_config - CREATE TABLE wallabag_config (id INTEGER NOT NULL, user_id INTEGER DEFAULT NULL, theme VARCHAR(255) NOT NULL COLLATE BINARY, items_per_page INTEGER NOT NULL, language VARCHAR(255) NOT NULL COLLATE BINARY, rss_token VARCHAR(255) DEFAULT NULL COLLATE BINARY, rss_limit INTEGER DEFAULT NULL, reading_speed DOUBLE PRECISION DEFAULT NULL, pocket_consumer_key VARCHAR(255) DEFAULT NULL COLLATE BINARY, PRIMARY KEY(id)) - INSERT INTO wallabag_config (id, user_id, theme, items_per_page, language, rss_token, rss_limit, reading_speed, pocket_consumer_key) SELECT id, user_id, theme, items_per_page, language, rss_token, rss_limit, reading_speed, pocket_consumer_key FROM __temp__wallabag_config - DROP TABLE __temp__wallabag_config - CREATE UNIQUE INDEX UNIQ_87E64C53A76ED395 ON wallabag_config (user_id) - -Migration 20161117071626 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('share_unmark', 0, 'entry') - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('unmark_url', 'https://unmark.it', 'entry') - -Migration down -"""""""""""""" - -.. code-block:: sql - - DELETE FROM wallabag_craue_config_setting WHERE name = 'share_unmark'; - DELETE FROM wallabag_craue_config_setting WHERE name = 'unmark_url'; - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('share_unmark', 0, 'entry') - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('unmark_url', 'https://unmark.it', 'entry') - -Migration down -"""""""""""""" - -.. code-block:: sql - - DELETE FROM wallabag_craue_config_setting WHERE name = 'share_unmark'; - DELETE FROM wallabag_craue_config_setting WHERE name = 'unmark_url'; - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('share_unmark', 0, 'entry') - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('unmark_url', 'https://unmark.it', 'entry') - -Migration down -"""""""""""""" - -.. code-block:: sql - - DELETE FROM wallabag_craue_config_setting WHERE name = 'share_unmark'; - DELETE FROM wallabag_craue_config_setting WHERE name = 'unmark_url'; - -Migration 20161118134328 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_entry ADD http_status VARCHAR(3) DEFAULT NULL - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_entry DROP http_status - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_entry ADD http_status VARCHAR(3) DEFAULT NULL - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_entry DROP http_status - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_entry ADD COLUMN http_status VARCHAR(3) DEFAULT NULL - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX created_at_idx - DROP INDEX IDX_F4D18282A76ED395 - CREATE TEMPORARY TABLE __temp__wallabag_entry AS SELECT id, user_id, uuid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public FROM wallabag_entry - DROP TABLE wallabag_entry - CREATE TABLE wallabag_entry (id INTEGER NOT NULL, user_id INTEGER DEFAULT NULL, uuid CLOB DEFAULT NULL COLLATE BINARY, title CLOB DEFAULT NULL COLLATE BINARY, url CLOB DEFAULT NULL COLLATE BINARY, is_archived BOOLEAN NOT NULL, is_starred BOOLEAN NOT NULL, content CLOB DEFAULT NULL COLLATE BINARY, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, mimetype CLOB DEFAULT NULL COLLATE BINARY, language CLOB DEFAULT NULL COLLATE BINARY, reading_time INTEGER DEFAULT NULL, domain_name CLOB DEFAULT NULL COLLATE BINARY, preview_picture CLOB DEFAULT NULL COLLATE BINARY, is_public BOOLEAN DEFAULT '0', PRIMARY KEY(id)) - INSERT INTO wallabag_entry (id, user_id, uuid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public) SELECT id, user_id, uuid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public FROM __temp__wallabag_entry - DROP TABLE __temp__wallabag_entry - CREATE INDEX created_at_idx ON wallabag_entry (created_at) - CREATE INDEX IDX_F4D18282A76ED395 ON wallabag_entry (user_id) - -Migration 20161122144743 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('restricted_access', 0, 'entry') - -Migration down -"""""""""""""" - -.. code-block:: sql - - DELETE FROM wallabag_craue_config_setting WHERE name = 'restricted_access'; - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('restricted_access', 0, 'entry') - -Migration down -"""""""""""""" - -.. code-block:: sql - - DELETE FROM wallabag_craue_config_setting WHERE name = 'restricted_access'; - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('restricted_access', 0, 'entry') - -Migration down -"""""""""""""" - -.. code-block:: sql - - DELETE FROM wallabag_craue_config_setting WHERE name = 'restricted_access'; - -Migration 20161122203647 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_user DROP expired, DROP credentials_expired - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_user ADD expired SMALLINT DEFAULT NULL, ADD credentials_expired SMALLINT DEFAULT NULL - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_user DROP expired - ALTER TABLE wallabag_user DROP credentials_expired - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_user ADD expired SMALLINT DEFAULT NULL - ALTER TABLE wallabag_user ADD credentials_expired SMALLINT DEFAULT NULL - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - DROP INDEX UNIQ_1D63E7E5C05FB297 - DROP INDEX UNIQ_1D63E7E5A0D96FBF - DROP INDEX UNIQ_1D63E7E592FC23A8 - CREATE TEMPORARY TABLE __temp__wallabag_user AS SELECT id, username, username_canonical, email, email_canonical, enabled, salt, password, last_login, locked, expires_at, confirmation_token, password_requested_at, roles, credentials_expire_at, name, created_at, updated_at, authCode, twoFactorAuthentication, trusted FROM wallabag_user - DROP TABLE wallabag_user - CREATE TABLE wallabag_user (id INTEGER NOT NULL, username VARCHAR(180) NOT NULL COLLATE BINARY, username_canonical VARCHAR(180) NOT NULL COLLATE BINARY, email VARCHAR(180) NOT NULL COLLATE BINARY, email_canonical VARCHAR(180) NOT NULL COLLATE BINARY, enabled BOOLEAN NOT NULL, salt VARCHAR(255) NOT NULL COLLATE BINARY, password VARCHAR(255) NOT NULL COLLATE BINARY, last_login DATETIME DEFAULT NULL, locked BOOLEAN NOT NULL, expires_at DATETIME DEFAULT NULL, confirmation_token VARCHAR(180) DEFAULT NULL COLLATE BINARY, password_requested_at DATETIME DEFAULT NULL, roles CLOB NOT NULL COLLATE BINARY, credentials_expire_at DATETIME DEFAULT NULL, name CLOB DEFAULT NULL COLLATE BINARY, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, authCode INTEGER DEFAULT NULL, twoFactorAuthentication BOOLEAN NOT NULL, trusted CLOB DEFAULT NULL COLLATE BINARY, PRIMARY KEY(id)) - INSERT INTO wallabag_user (id, username, username_canonical, email, email_canonical, enabled, salt, password, last_login, locked, expires_at, confirmation_token, password_requested_at, roles, credentials_expire_at, name, created_at, updated_at, authCode, twoFactorAuthentication, trusted) SELECT id, username, username_canonical, email, email_canonical, enabled, salt, password, last_login, locked, expires_at, confirmation_token, password_requested_at, roles, credentials_expire_at, name, created_at, updated_at, authCode, twoFactorAuthentication, trusted FROM __temp__wallabag_user - DROP TABLE __temp__wallabag_user - CREATE UNIQUE INDEX UNIQ_1D63E7E5C05FB297 ON wallabag_user (confirmation_token) - CREATE UNIQUE INDEX UNIQ_1D63E7E5A0D96FBF ON wallabag_user (email_canonical) - CREATE UNIQUE INDEX UNIQ_1D63E7E592FC23A8 ON wallabag_user (username_canonical) - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_user ADD COLUMN expired SMALLINT DEFAULT NULL - ALTER TABLE wallabag_user ADD COLUMN credentials_expired SMALLINT DEFAULT NULL - -Migration 20161128084725 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_config ADD list_mode INT DEFAULT NULL - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_config DROP list_mode - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_config ADD list_mode INT DEFAULT NULL - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_config DROP list_mode - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_config ADD COLUMN list_mode INTEGER DEFAULT NULL - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX UNIQ_87E64C53A76ED395 - CREATE TEMPORARY TABLE __temp__wallabag_config AS SELECT id, user_id, theme, items_per_page, language, rss_token, rss_limit, reading_speed, pocket_consumer_key FROM wallabag_config - DROP TABLE wallabag_config - CREATE TABLE wallabag_config (id INTEGER NOT NULL, user_id INTEGER DEFAULT NULL, theme VARCHAR(255) NOT NULL COLLATE BINARY, items_per_page INTEGER NOT NULL, language VARCHAR(255) NOT NULL COLLATE BINARY, rss_token VARCHAR(255) DEFAULT NULL COLLATE BINARY, rss_limit INTEGER DEFAULT NULL, reading_speed DOUBLE PRECISION DEFAULT NULL, pocket_consumer_key VARCHAR(255) DEFAULT NULL COLLATE BINARY, PRIMARY KEY(id)) - INSERT INTO wallabag_config (id, user_id, theme, items_per_page, language, rss_token, rss_limit, reading_speed, pocket_consumer_key) SELECT id, user_id, theme, items_per_page, language, rss_token, rss_limit, reading_speed, pocket_consumer_key FROM __temp__wallabag_config - DROP TABLE __temp__wallabag_config - CREATE UNIQUE INDEX UNIQ_87E64C53A76ED395 ON wallabag_config (user_id) - -Migration 20161128131503 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_user DROP locked, DROP credentials_expire_at, DROP expires_at - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_user ADD locked SMALLINT DEFAULT NULL, ADD credentials_expire_at DATETIME DEFAULT NULL, ADD expires_at DATETIME DEFAULT NULL - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_user DROP locked - ALTER TABLE wallabag_user DROP credentials_expire_at - ALTER TABLE wallabag_user DROP expires_at - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_user ADD locked SMALLINT DEFAULT NULL - ALTER TABLE wallabag_user ADD credentials_expire_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL - ALTER TABLE wallabag_user ADD expires_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_user ADD COLUMN locked SMALLINT DEFAULT NULL - ALTER TABLE wallabag_user ADD COLUMN credentials_expire_at DATETIME DEFAULT NULL - ALTER TABLE wallabag_user ADD COLUMN expires_at DATETIME DEFAULT NULL - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX UNIQ_1D63E7E592FC23A8 - DROP INDEX UNIQ_1D63E7E5A0D96FBF - DROP INDEX UNIQ_1D63E7E5C05FB297 - CREATE TEMPORARY TABLE __temp__wallabag_user AS SELECT id, username, username_canonical, email, email_canonical, enabled, salt, password, last_login, confirmation_token, password_requested_at, roles, name, created_at, updated_at, authCode, twoFactorAuthentication, trusted, expired, credentials_expired FROM wallabag_user - DROP TABLE wallabag_user - CREATE TABLE wallabag_user (id INTEGER NOT NULL, username VARCHAR(180) NOT NULL COLLATE BINARY, username_canonical VARCHAR(180) NOT NULL COLLATE BINARY, email VARCHAR(180) NOT NULL COLLATE BINARY, email_canonical VARCHAR(180) NOT NULL COLLATE BINARY, enabled BOOLEAN NOT NULL, salt VARCHAR(255) NOT NULL COLLATE BINARY, password VARCHAR(255) NOT NULL COLLATE BINARY, last_login DATETIME DEFAULT NULL, confirmation_token VARCHAR(180) DEFAULT NULL COLLATE BINARY, password_requested_at DATETIME DEFAULT NULL, roles CLOB NOT NULL COLLATE BINARY, name CLOB DEFAULT NULL COLLATE BINARY, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, authCode INTEGER DEFAULT NULL, twoFactorAuthentication BOOLEAN NOT NULL, trusted CLOB DEFAULT NULL COLLATE BINARY, expired SMALLINT DEFAULT NULL, credentials_expired SMALLINT DEFAULT NULL, PRIMARY KEY(id)) - INSERT INTO wallabag_user (id, username, username_canonical, email, email_canonical, enabled, salt, password, last_login, confirmation_token, password_requested_at, roles, name, created_at, updated_at, authCode, twoFactorAuthentication, trusted, expired, credentials_expired) SELECT id, username, username_canonical, email, email_canonical, enabled, salt, password, last_login, confirmation_token, password_requested_at, roles, name, created_at, updated_at, authCode, twoFactorAuthentication, trusted, expired, credentials_expired FROM __temp__wallabag_user - DROP TABLE __temp__wallabag_user - CREATE UNIQUE INDEX UNIQ_1D63E7E592FC23A8 ON wallabag_user (username_canonical) - CREATE UNIQUE INDEX UNIQ_1D63E7E5A0D96FBF ON wallabag_user (email_canonical) - CREATE UNIQUE INDEX UNIQ_1D63E7E5C05FB297 ON wallabag_user (confirmation_token) - -Migration 20161214094402 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_entry CHANGE uuid uid VARCHAR(23) - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_entry CHANGE uid uuid VARCHAR(23) - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_entry RENAME uuid TO uid - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_entry RENAME uid TO uuid - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - CREATE TABLE __temp__wallabag_entry ( - id INTEGER NOT NULL, - user_id INTEGER DEFAULT NULL, - uid VARCHAR(23) DEFAULT NULL, - title CLOB DEFAULT NULL, - url CLOB DEFAULT NULL, - is_archived BOOLEAN NOT NULL, - is_starred BOOLEAN NOT NULL, - content CLOB DEFAULT NULL, - created_at DATETIME NOT NULL, - updated_at DATETIME NOT NULL, - mimetype CLOB DEFAULT NULL, - language CLOB DEFAULT NULL, - reading_time INTEGER DEFAULT NULL, - domain_name CLOB DEFAULT NULL, - preview_picture CLOB DEFAULT NULL, - is_public BOOLEAN DEFAULT '0', - http_status VARCHAR(3) DEFAULT NULL, - PRIMARY KEY(id) - ); - INSERT INTO __temp__wallabag_entry SELECT id,user_id,uuid,title,url,is_archived,is_starred,content,created_at,updated_at,mimetype,language,reading_time,domain_name,preview_picture,is_public,http_status FROM wallabag_entry; - DROP TABLE wallabag_entry; - ALTER TABLE __temp__wallabag_entry RENAME TO wallabag_entry - CREATE INDEX uid ON wallabag_entry (uid) - CREATE INDEX created_at ON wallabag_entry (created_at) - CREATE INDEX IDX_F4D18282A76ED395 ON wallabag_entry (user_id) - - -Migration down -"""""""""""""" - -.. code-block:: sql - - CREATE TABLE __temp__wallabag_entry ( - id INTEGER NOT NULL, - user_id INTEGER DEFAULT NULL, - uuid VARCHAR(23) DEFAULT NULL, - title CLOB DEFAULT NULL, - url CLOB DEFAULT NULL, - is_archived BOOLEAN NOT NULL, - is_starred BOOLEAN NOT NULL, - content CLOB DEFAULT NULL, - created_at DATETIME NOT NULL, - updated_at DATETIME NOT NULL, - mimetype CLOB DEFAULT NULL, - language CLOB DEFAULT NULL, - reading_time INTEGER DEFAULT NULL, - domain_name CLOB DEFAULT NULL, - preview_picture CLOB DEFAULT NULL, - is_public BOOLEAN DEFAULT '0', - http_status VARCHAR(3) DEFAULT NULL, - PRIMARY KEY(id) - ); - INSERT INTO __temp__wallabag_entry SELECT id,user_id,uid,title,url,is_archived,is_starred,content,created_at,updated_at,mimetype,language,reading_time,domain_name,preview_picture,is_public,http_status FROM wallabag_entry; - DROP TABLE wallabag_entry; - ALTER TABLE __temp__wallabag_entry RENAME TO wallabag_entry - CREATE INDEX uid ON wallabag_entry (uid) - CREATE INDEX created_at ON wallabag_entry (created_at) - CREATE INDEX IDX_F4D18282A76ED395 ON wallabag_entry (user_id) - -Migration 20161214094403 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - CREATE INDEX IDX_entry_uid ON wallabag_entry (uid) - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX IDX_entry_uid ON wallabag_entry - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - CREATE INDEX IDX_entry_uid ON wallabag_entry (uid) - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX idx_entry_uid - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - DROP INDEX IDX_F4D18282A76ED395 - DROP INDEX created_at_idx - CREATE TEMPORARY TABLE __temp__wallabag_entry AS SELECT id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public FROM wallabag_entry - DROP TABLE wallabag_entry - CREATE TABLE wallabag_entry (id INTEGER NOT NULL, user_id INTEGER DEFAULT NULL, uid CLOB DEFAULT NULL COLLATE BINARY, title CLOB DEFAULT NULL COLLATE BINARY, url CLOB DEFAULT NULL COLLATE BINARY, is_archived BOOLEAN NOT NULL, is_starred BOOLEAN NOT NULL, content CLOB DEFAULT NULL COLLATE BINARY, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, mimetype CLOB DEFAULT NULL COLLATE BINARY, language CLOB DEFAULT NULL COLLATE BINARY, reading_time INTEGER DEFAULT NULL, domain_name CLOB DEFAULT NULL COLLATE BINARY, preview_picture CLOB DEFAULT NULL COLLATE BINARY, is_public BOOLEAN DEFAULT '0', PRIMARY KEY(id)) - INSERT INTO wallabag_entry (id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public) SELECT id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public FROM __temp__wallabag_entry - DROP TABLE __temp__wallabag_entry - CREATE INDEX IDX_F4D18282A76ED395 ON wallabag_entry (user_id) - CREATE INDEX created_at_idx ON wallabag_entry (created_at) - CREATE INDEX IDX_entry_uid ON wallabag_entry (uid) - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX IDX_entry_uid - DROP INDEX created_at_idx - DROP INDEX IDX_F4D18282A76ED395 - CREATE TEMPORARY TABLE __temp__wallabag_entry AS SELECT id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public FROM wallabag_entry - DROP TABLE wallabag_entry - CREATE TABLE wallabag_entry (id INTEGER NOT NULL, user_id INTEGER DEFAULT NULL, uid CLOB DEFAULT NULL COLLATE BINARY, title CLOB DEFAULT NULL COLLATE BINARY, url CLOB DEFAULT NULL COLLATE BINARY, is_archived BOOLEAN NOT NULL, is_starred BOOLEAN NOT NULL, content CLOB DEFAULT NULL COLLATE BINARY, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, mimetype CLOB DEFAULT NULL COLLATE BINARY, language CLOB DEFAULT NULL COLLATE BINARY, reading_time INTEGER DEFAULT NULL, domain_name CLOB DEFAULT NULL COLLATE BINARY, preview_picture CLOB DEFAULT NULL COLLATE BINARY, is_public BOOLEAN DEFAULT '0', PRIMARY KEY(id)) - INSERT INTO wallabag_entry (id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public) SELECT id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public FROM __temp__wallabag_entry - DROP TABLE __temp__wallabag_entry - CREATE INDEX created_at_idx ON wallabag_entry (created_at) - CREATE INDEX IDX_F4D18282A76ED395 ON wallabag_entry (user_id) - -Migration 20170127093841 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - CREATE INDEX IDX_entry_starred ON wallabag_entry (is_starred) - CREATE INDEX IDX_entry_archived ON wallabag_entry (is_archived) - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX IDX_entry_starred ON wallabag_entry - DROP INDEX IDX_entry_archived ON wallabag_entry - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - CREATE INDEX IDX_entry_starred ON wallabag_entry (is_starred) - CREATE INDEX IDX_entry_archived ON wallabag_entry (is_archived) - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX IDX_entry_starred - DROP INDEX IDX_entry_archived - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - DROP INDEX uid - DROP INDEX created_at - DROP INDEX IDX_F4D18282A76ED395 - CREATE TEMPORARY TABLE __temp__wallabag_entry AS SELECT id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public, http_status FROM wallabag_entry - DROP TABLE wallabag_entry - CREATE TABLE wallabag_entry (id INTEGER NOT NULL, user_id INTEGER DEFAULT NULL, uid VARCHAR(23) DEFAULT NULL COLLATE BINARY, title CLOB DEFAULT NULL COLLATE BINARY, url CLOB DEFAULT NULL COLLATE BINARY, is_archived BOOLEAN NOT NULL, is_starred BOOLEAN NOT NULL, content CLOB DEFAULT NULL COLLATE BINARY, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, mimetype CLOB DEFAULT NULL COLLATE BINARY, language CLOB DEFAULT NULL COLLATE BINARY, reading_time INTEGER DEFAULT NULL, domain_name CLOB DEFAULT NULL COLLATE BINARY, preview_picture CLOB DEFAULT NULL COLLATE BINARY, is_public BOOLEAN DEFAULT '0', http_status VARCHAR(3) DEFAULT NULL COLLATE BINARY, PRIMARY KEY(id)) - INSERT INTO wallabag_entry (id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public, http_status) SELECT id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public, http_status FROM __temp__wallabag_entry - DROP TABLE __temp__wallabag_entry - CREATE INDEX uid ON wallabag_entry (uid) - CREATE INDEX created_at ON wallabag_entry (created_at) - CREATE INDEX IDX_F4D18282A76ED395 ON wallabag_entry (user_id) - CREATE INDEX IDX_entry_starred ON wallabag_entry (is_starred) - CREATE INDEX IDX_entry_archived ON wallabag_entry (is_archived) - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX IDX_entry_archived - DROP INDEX IDX_entry_starred - DROP INDEX IDX_F4D18282A76ED395 - DROP INDEX created_at - DROP INDEX uid - CREATE TEMPORARY TABLE __temp__wallabag_entry AS SELECT id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public, http_status FROM wallabag_entry - DROP TABLE wallabag_entry - CREATE TABLE wallabag_entry (id INTEGER NOT NULL, user_id INTEGER DEFAULT NULL, uid VARCHAR(23) DEFAULT NULL COLLATE BINARY, title CLOB DEFAULT NULL COLLATE BINARY, url CLOB DEFAULT NULL COLLATE BINARY, is_archived BOOLEAN NOT NULL, is_starred BOOLEAN NOT NULL, content CLOB DEFAULT NULL COLLATE BINARY, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, mimetype CLOB DEFAULT NULL COLLATE BINARY, language CLOB DEFAULT NULL COLLATE BINARY, reading_time INTEGER DEFAULT NULL, domain_name CLOB DEFAULT NULL COLLATE BINARY, preview_picture CLOB DEFAULT NULL COLLATE BINARY, is_public BOOLEAN DEFAULT '0', http_status VARCHAR(3) DEFAULT NULL COLLATE BINARY, PRIMARY KEY(id)) - INSERT INTO wallabag_entry (id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public, http_status) SELECT id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public, http_status FROM __temp__wallabag_entry - DROP TABLE __temp__wallabag_entry - CREATE INDEX IDX_F4D18282A76ED395 ON wallabag_entry (user_id) - CREATE INDEX created_at ON wallabag_entry (created_at) - CREATE INDEX uid ON wallabag_entry (uid) diff --git a/docs/en/user/tags.rst b/docs/en/user/tags.rst deleted file mode 100644 index 8ddc0f40..00000000 --- a/docs/en/user/tags.rst +++ /dev/null @@ -1,2 +0,0 @@ -Tags -==== \ No newline at end of file diff --git a/docs/en/user/upgrade.rst b/docs/en/user/upgrade.rst deleted file mode 100644 index 46b490c3..00000000 --- a/docs/en/user/upgrade.rst +++ /dev/null @@ -1,148 +0,0 @@ -================================== -Upgrade your wallabag installation -================================== - -You will find here different ways to upgrade your wallabag: - -- `from 2.1.x to 2.2.x <#upgrading-from-2-1-x-to-2-2-x>`_ -- `from 2.0.x to 2.1.1 <#upgrade-from-2-0-x-to-2-1-1>`_ -- `from 1.x to 2.x <#from-wallabag-1-x>`_ - -***************************** -Upgrading from 2.1.x to 2.2.x -***************************** - -Upgrade on a dedicated web server -================================= - -**From 2.1.x:** - -:: - - make update - php bin/console doctrine:migrations:migrate --no-interaction -e=prod - -**From 2.2.0:** - -:: - - make update - -Explanations about database migrations --------------------------------------- - -During the update, we execute database migrations. - -All the database migrations are stored in ``app/DoctrineMigrations``. You can execute each migration individually: -``bin/console doctrine:migrations:execute 20161001072726 --env=prod``. - -You can also cancel each migration individually: ``bin/console doctrine:migrations:execute 20161001072726 --down --env=prod``. - -Here is the migrations list for 2.1.x to 2.2.0 release: - -* ``20161001072726``: added foreign keys for account resetting -* ``20161022134138``: converted database to ``utf8mb4`` encoding (for MySQL only) -* ``20161024212538``: added ``user_id`` column on ``oauth2_clients`` to prevent users to delete API clients from other users -* ``20161031132655``: added the internal setting to enable/disable downloading pictures -* ``20161104073720``: added ``created_at`` index on ``entry`` table -* ``20161106113822``: added ``action_mark_as_read`` field on ``config`` table -* ``20161117071626``: added the internal setting to share articles to unmark.it -* ``20161118134328``: added ``http_status`` field on ``entry`` table -* ``20161122144743``: added the internal setting to enable/disable fetching articles with paywall -* ``20161122203647``: dropped ``expired`` and ``credentials_expired`` fields on ``user`` table -* ``20161128084725``: added ``list_mode`` field on ``config`` table -* ``20161128131503``: dropped ``locked``, ``credentials_expire_at`` and ``expires_at`` fields on ``user`` table -* ``20161214094402``: renamed ``uuid`` to ``uid`` on ``entry`` table -* ``20161214094403``: added ``uid`` index on ``entry`` table -* ``20170127093841``: added ``is_starred`` and ``is_archived`` indexes on ``entry`` table - -Upgrade on a shared hosting -=========================== - -Backup your ``app/config/parameters.yml`` file. - -Download the last release of wallabag: - -.. code-block:: bash - - wget https://wllbg.org/latest-v2-package && tar xvf latest-v2-package - -You will find the `md5 hash of the latest package on our website `_. - -Extract the archive in your wallabag folder and replace ``app/config/parameters.yml`` with yours. - -Please check that your ``app/config/parameters.yml`` contains all the required parameters. You can find `here a documentation about parameters `_. - -If you use SQLite, you must also copy your ``data/`` folder inside the new installation. - -Empty ``var/cache`` folder. - -You must run some SQL queries to upgrade your database. We assume that the table prefix is ``wallabag_``. Don't forgete to backup your database before migrating. - -You may encounter issues with indexes names: if so, please change queries with the correct index name. - -`You can find all the queries here `_. - -*************************** -Upgrade from 2.0.x to 2.1.1 -*************************** - -.. warning:: - - Before this migration, if you configured the Pocket import by adding your consumer key in Internal settings, please do a backup of it: you'll have to add it into the Config page after the upgrade. - -Upgrade on a dedicated web server -================================= - -:: - - rm -rf var/cache/* - git fetch origin - git fetch --tags - git checkout 2.1.1 --force - SYMFONY_ENV=prod composer install --no-dev -o --prefer-dist - php bin/console doctrine:migrations:migrate --env=prod - php bin/console cache:clear --env=prod - -Upgrade on a shared hosting -=========================== - -Backup your ``app/config/parameters.yml`` file. - -Download the 2.1.1 release of wallabag: - -.. code-block:: bash - - wget http://framabag.org/wallabag-release-2.1.1.tar.gz && tar xvf wallabag-release-2.1.1.tar.gz - -(md5 hash of the 2.1.1 package: ``9584a3b60a2b2a4de87f536548caac93``) - -Extract the archive in your wallabag folder and replace ``app/config/parameters.yml`` with yours. - -Please check that your ``app/config/parameters.yml`` contains all the required parameters. You can find `here a documentation about parameters `_. - -If you use SQLite, you must also copy your ``data/`` folder inside the new installation. - -Empty ``var/cache`` folder. - -You must run some SQL queries to upgrade your database. We assume that the table prefix is ``wallabag_`` and the database server is a MySQL one: - -.. code-block:: sql - - ALTER TABLE `wallabag_entry` ADD `uuid` LONGTEXT DEFAULT NULL; - INSERT INTO `wallabag_craue_config_setting` (`name`, `value`, `section`) VALUES ('share_public', '1', 'entry'); - ALTER TABLE `wallabag_oauth2_clients` ADD name longtext COLLATE 'utf8_unicode_ci' DEFAULT NULL; - INSERT INTO `wallabag_craue_config_setting` (`name`, `value`, `section`) VALUES ('import_with_redis', '0', 'import'); - INSERT INTO `wallabag_craue_config_setting` (`name`, `value`, `section`) VALUES ('import_with_rabbitmq', '0', 'import'); - ALTER TABLE `wallabag_config` ADD `pocket_consumer_key` VARCHAR(255) DEFAULT NULL; - DELETE FROM `wallabag_craue_config_setting` WHERE `name` = 'pocket_consumer_key'; - -***************** -From wallabag 1.x -***************** - -There is no automatic script to update from wallabag 1.x to wallabag 2.x. You need to: - -- export your data -- install wallabag 2.x (`read the installation documentation `_ ) -- import data in this fresh installation (`read the import documentation `_ ) diff --git a/docs/fr/conf.py b/docs/fr/conf.py deleted file mode 100644 index 49a57e2d..00000000 --- a/docs/fr/conf.py +++ /dev/null @@ -1,55 +0,0 @@ -# -*- coding: utf-8 -*- -# -# wallabag documentation build configuration file, created by -# sphinx-quickstart on Fri Oct 16 06:47:23 2015. - -import sys -import os - -extensions = [] -templates_path = ['_templates'] -source_suffix = '.rst' -master_doc = 'index' -project = u'wallabag-fr' -copyright = u'2013-2016, Nicolas Lœuillet - MIT Licence' -version = '2.1.0' -release = version -exclude_patterns = ['_build'] -pygments_style = 'sphinx' -html_theme = 'default' -html_static_path = ['_static'] -htmlhelp_basename = 'wallabagfrdoc' - -latex_elements = { -} - -latex_documents = [ - ('index', 'wallabag-fr.tex', u'wallabag Documentation', - u'Nicolas Lœuillet', 'manual'), -] - -man_pages = [ - ('index', 'wallabagfr', u'wallabag Documentation', - [u'Nicolas Lœuillet'], 1) -] - -texinfo_documents = [ - ('index', 'wallabag', u'wallabag Documentation', - u'Nicolas Lœuillet', 'wallabag', 'wallabag is an opensource read-it-later.', - 'Miscellaneous'), -] - -##### Guzzle sphinx theme - -import guzzle_sphinx_theme -html_translator_class = 'guzzle_sphinx_theme.HTMLTranslator' -html_theme_path = guzzle_sphinx_theme.html_theme_path() -html_theme = 'guzzle_sphinx_theme' - -# Custom sidebar templates, maps document names to template names. -html_sidebars = { - '**': ['logo-text.html', 'globaltoc.html', 'searchbox.html'] -} - -# Register the theme as an extension to generate a sitemap.xml -extensions.append("guzzle_sphinx_theme") diff --git a/docs/fr/developer/api.rst b/docs/fr/developer/api.rst deleted file mode 100644 index e7bb264f..00000000 --- a/docs/fr/developer/api.rst +++ /dev/null @@ -1,271 +0,0 @@ -Documentation de l'API -====================== - -Grâce à cette documentation, nous allons voir comment interagir avec l'API de wallabag. - -Pré-requis ----------- - -* wallabag fraichement installé et disponible à http://localhost:8000 -* ``httpie`` installé sur votre ordinateur (`voir le site du projet `__). Vous pouvez également adapter les commandes en utilisant curl ou wget. -* toutes les méthodes de l'API documentées ici http://localhost:8000/api/doc - -Créer un nouveau client d'API ------------------------------ - -Depuis votre wallabag, vous pouvez créer un nouveau client d'API à cette URL http://localhost:8000/developer/client/create. - -Vous devez renseigner l'URL de redirection de votre application et créer votre client. Si votre application est une application desktop, renseignez l'URL que vous souhaitez. - -Vous obtiendrez les informations suivantes : - -:: - - Client ID: - - 1_3o53gl30vhgk0c8ks4cocww08o84448osgo40wgw4gwkoo8skc - - Client secret: - - 636ocbqo978ckw0gsw4gcwwocg8044sco0w8w84cws48ggogs4 - - -Créer un jeton --------------- - -Pour chaque appel d'API, vous aurez besoin d'un jeton. Créons-le avec la commande suivante (remplacez ``client_id``, ``client_secret``, ``username`` and ``password`` par leur valeur): - -:: - - http POST http://localhost:8000/oauth/v2/token \ - grant_type=password \ - client_id=1_3o53gl30vhgk0c8ks4cocww08o84448osgo40wgw4gwkoo8skc \ - client_secret=636ocbqo978ckw0gsw4gcwwocg8044sco0w8w84cws48ggogs4 \ - username=wallabag \ - password=wallabag - -Vous obtiendrez : - -:: - - HTTP/1.1 200 OK - Cache-Control: no-store, private - Connection: close - Content-Type: application/json - Date: Tue, 05 Apr 2016 08:44:33 GMT - Host: localhost:8000 - Pragma: no-cache - X-Debug-Token: 19c8e0 - X-Debug-Token-Link: /_profiler/19c8e0 - X-Powered-By: PHP/7.0.4 - - { - "access_token": "ZGJmNTA2MDdmYTdmNWFiZjcxOWY3MWYyYzkyZDdlNWIzOTU4NWY3NTU1MDFjOTdhMTk2MGI3YjY1ZmI2NzM5MA", - "expires_in": 3600, - "refresh_token": "OTNlZGE5OTJjNWQwYzc2NDI5ZGE5MDg3ZTNjNmNkYTY0ZWZhZDVhNDBkZTc1ZTNiMmQ0MjQ0OThlNTFjNTQyMQ", - "scope": null, - "token_type": "bearer" - } - -Nous allons utiliser la valeur de ``access_token`` dans nos prochains appels. - -Exemple cURL : - -:: - - curl -s "https://localhost:8000/oauth/v2/token?grant_type=password&client_id=1_3o53gl30vhgk0c8ks4cocww08o84448osgo40wgw4gwkoo8skc&client_secret=636ocbqo978ckw0gsw4gcwwocg8044sco0w8w84cws48ggogs4&username=wallabag&password=wallabag" - -Récupérer les articles existants --------------------------------- - -Documentation pour cette méthode : http://localhost:8000/api/doc#get--api-entries.{_format} - -Comme nous venons tout juste d'installer wallabag, nous n'aurons aucun résultat avec cette commande : - -:: - - http GET http://localhost:8000/api/entries.json \ - "Authorization:Bearer ZGJmNTA2MDdmYTdmNWFiZjcxOWY3MWYyYzkyZDdlNWIzOTU4NWY3NTU1MDFjOTdhMTk2MGI3YjY1ZmI2NzM5MA" - -retournera : - -:: - - HTTP/1.1 200 OK - 0: application/json - Cache-Control: no-cache - Connection: close - Content-Type: application/json - Date: Tue, 05 Apr 2016 08:51:32 GMT - Host: localhost:8000 - Set-Cookie: PHPSESSID=nrogm748md610ovhu6j70c3q63; path=/; HttpOnly - X-Debug-Token: 4fbbc4 - X-Debug-Token-Link: /_profiler/4fbbc4 - X-Powered-By: PHP/7.0.4 - - { - "_embedded": { - "items": [] - }, - "_links": { - "first": { - "href": "http://localhost:8000/api/entries?page=1&perPage=30" - }, - "last": { - "href": "http://localhost:8000/api/entries?page=1&perPage=30" - }, - "self": { - "href": "http://localhost:8000/api/entries?page=1&perPage=30" - } - }, - "limit": 30, - "page": 1, - "pages": 1, - "total": 0 - } - -Le tableau ``items`` est vide. - -Exemple cURL : - -:: - - curl --get "https://localhost:8000/api/entries.html?access_token=ZGJmNTA2MDdmYTdmNWFiZjcxOWY3MWYyYzkyZDdlNWIzOTU4NWY3NTU1MDFjOTdhMTk2MGI3YjY1ZmI2NzM5MA" - -Créer votre premier article ---------------------------- - -Documentation pour cette méthode : http://localhost:8000/api/doc#post--api-entries.{_format} - -:: - - http POST http://localhost:8000/api/entries.json \ - "Authorization:Bearer ZGJmNTA2MDdmYTdmNWFiZjcxOWY3MWYyYzkyZDdlNWIzOTU4NWY3NTU1MDFjOTdhMTk2MGI3YjY1ZmI2NzM5MA" \ - url="http://www.numerama.com/tech/160115-le-pocket-libre-wallabag-fait-le-plein-de-fonctionnalites.html" - -retournera : - -:: - - HTTP/1.1 200 OK - 0: application/json - Cache-Control: no-cache - Connection: close - Content-Type: application/json - Date: Tue, 05 Apr 2016 09:07:54 GMT - Host: localhost:8000 - Set-Cookie: PHPSESSID=bjie40ck72kp2pst3i71gf43a4; path=/; HttpOnly - X-Debug-Token: e01c51 - X-Debug-Token-Link: /_profiler/e01c51 - X-Powered-By: PHP/7.0.4 - - { - "_links": { - "self": { - "href": "/api/entries/1" - } - }, - "content": "

Fonctionnant sur le même principe que Pocket, Instapaper ou Readability, le logiciel Wallabag permet de mémoriser des articles pour les lire plus tard. Sa nouvelle version apporte une multitude de nouvelles fonctionnalités.

Si vous utilisez Firefox comme navigateur web, vous avez peut-être constaté l’arrivée d’une fonctionnalité intitulée Pocket. Disponible autrefois sous la forme d’un module complémentaire, et sous un autre nom (Read it Later), elle est depuis le mois de juin 2015 directement incluse au sein de Firefox.

\n

Concrètement, Pocket sert à garder en mémoire des contenus que vous croisez au fil de la navigation, comme des articles de presse ou des vidéos, afin de pouvoir les consulter plus tard. Pocket fonctionne un peu comme un système de favoris, mais en bien plus élaboré grâce à ses options supplémentaires.

\n

Mais Pocket fait polémique, car il s’agit d’un projet propriétaire qui est intégré dans un logiciel libre. C’est pour cette raison que des utilisateurs ont choisi de se tourner vers d’autres solutions, comme Wallabag, qui est l’équivalent libre de Pocket et d’autres systèmes du même genre, comme Instapaper et Readability.

\n

Et justement, Wallabag évolue. C’est ce dimanche que la version 2.0.0 du logiciel a été publiée par l’équipe en  charge de son développement et celle-ci contient de nombreux changements par rapport aux moutures précédentes (la documentation est traduite en français), lui permettant d’apparaître comme une alternative à Pocket, Instapaper et Readability.

\n

\"homepage\"

\n

Parmi les principaux changements que l’on peut retenir avec cette nouvelle version, notons la possibilité d’écrire des annotations dans les articles mémorisés, de filtrer les contenus selon divers critères (temps de lecture, nom de domaine, date de création, statut…), d’assigner des mots-clés aux entrées, de modifier le titre des articles, le support des flux RSS ou encore le support de plusieurs langues dont le français.

\n

D’autres options sont également à signaler, comme l’aperçu d’un article mémorisé (si l’option est disponible), un guide de démarrage rapide pour les débutants, un outil d’export dans divers formats (PDF, JSON, EPUB, MOBI, XML, CSV et TXT) et, surtout, la possibilité de migrer vers Wallabag depuis Pocket, afin de convaincre les usagers de se lancer.

\n \n \n

Articles liés

\n
\n
\n", - "created_at": "2016-04-05T09:07:54+0000", - "domain_name": "www.numerama.com", - "id": 1, - "is_archived": 0, - "is_starred": 0, - "language": "fr-FR", - "mimetype": "text/html", - "preview_picture": "http://www.numerama.com/content/uploads/2016/04/post-it.jpg", - "reading_time": 2, - "tags": [], - "title": "Le Pocket libre Wallabag fait le plein de fonctionnalités - Tech - Numerama", - "updated_at": "2016-04-05T09:07:54+0000", - "url": "http://www.numerama.com/tech/160115-le-pocket-libre-wallabag-fait-le-plein-de-fonctionnalites.html", - "user_email": "", - "user_id": 1, - "user_name": "wallabag" - } - -Maintenant, si vous exécutez la précédente commande (voir **Récupérer les articles existants**), vous obtiendrez quelque chose. - -Exemple cURL : - -:: - - curl "https://localhost:8000/api/entries.html?access_token=ZGJmNTA2MDdmYTdmNWFiZjcxOWY3MWYyYzkyZDdlNWIzOTU4NWY3NTU1MDFjOTdhMTk2MGI3YjY1ZmI2NzM5MA&url=http://www.numerama.com/tech/160115-le-pocket-libre-wallabag-fait-le-plein-de-fonctionnalites.html" - -Supprimer un article --------------------- - -Documentation pour cette méthode : http://localhost:8000/api/doc#delete--api-entries-{entry}.{_format} - -:: - - http DELETE http://localhost:8000/api/entries/1.json \ - "Authorization:Bearer ZGJmNTA2MDdmYTdmNWFiZjcxOWY3MWYyYzkyZDdlNWIzOTU4NWY3NTU1MDFjOTdhMTk2MGI3YjY1ZmI2NzM5MA" - -retournera : - -:: - - HTTP/1.1 200 OK - 0: application/json - Cache-Control: no-cache - Connection: close - Content-Type: application/json - Date: Tue, 05 Apr 2016 09:19:07 GMT - Host: localhost:8000 - Set-Cookie: PHPSESSID=jopgnfvmuc9a62b27sqm6iulr6; path=/; HttpOnly - X-Debug-Token: 887cef - X-Debug-Token-Link: /_profiler/887cef - X-Powered-By: PHP/7.0.4 - - { - "_links": { - "self": { - "href": "/api/entries/" - } - }, - "annotations": [], - "content": "

Fonctionnant sur le même principe que Pocket, Instapaper ou Readability, le logiciel Wallabag permet de mémoriser des articles pour les lire plus tard. Sa nouvelle version apporte une multitude de nouvelles fonctionnalités.

Si vous utilisez Firefox comme navigateur web, vous avez peut-être constaté l’arrivée d’une fonctionnalité intitulée Pocket. Disponible autrefois sous la forme d’un module complémentaire, et sous un autre nom (Read it Later), elle est depuis le mois de juin 2015 directement incluse au sein de Firefox.

\n

Concrètement, Pocket sert à garder en mémoire des contenus que vous croisez au fil de la navigation, comme des articles de presse ou des vidéos, afin de pouvoir les consulter plus tard. Pocket fonctionne un peu comme un système de favoris, mais en bien plus élaboré grâce à ses options supplémentaires.

\n

Mais Pocket fait polémique, car il s’agit d’un projet propriétaire qui est intégré dans un logiciel libre. C’est pour cette raison que des utilisateurs ont choisi de se tourner vers d’autres solutions, comme Wallabag, qui est l’équivalent libre de Pocket et d’autres systèmes du même genre, comme Instapaper et Readability.

\n

Et justement, Wallabag évolue. C’est ce dimanche que la version 2.0.0 du logiciel a été publiée par l’équipe en  charge de son développement et celle-ci contient de nombreux changements par rapport aux moutures précédentes (la documentation est traduite en français), lui permettant d’apparaître comme une alternative à Pocket, Instapaper et Readability.

\n

\"homepage\"

\n

Parmi les principaux changements que l’on peut retenir avec cette nouvelle version, notons la possibilité d’écrire des annotations dans les articles mémorisés, de filtrer les contenus selon divers critères (temps de lecture, nom de domaine, date de création, statut…), d’assigner des mots-clés aux entrées, de modifier le titre des articles, le support des flux RSS ou encore le support de plusieurs langues dont le français.

\n

D’autres options sont également à signaler, comme l’aperçu d’un article mémorisé (si l’option est disponible), un guide de démarrage rapide pour les débutants, un outil d’export dans divers formats (PDF, JSON, EPUB, MOBI, XML, CSV et TXT) et, surtout, la possibilité de migrer vers Wallabag depuis Pocket, afin de convaincre les usagers de se lancer.

\n \n \n

Articles liés

\n
\n
\n", - "created_at": "2016-04-05T09:07:54+0000", - "domain_name": "www.numerama.com", - "is_archived": 0, - "is_starred": 0, - "language": "fr-FR", - "mimetype": "text/html", - "preview_picture": "http://www.numerama.com/content/uploads/2016/04/post-it.jpg", - "reading_time": 2, - "tags": [], - "title": "Le Pocket libre Wallabag fait le plein de fonctionnalités - Tech - Numerama", - "updated_at": "2016-04-05T09:07:54+0000", - "url": "http://www.numerama.com/tech/160115-le-pocket-libre-wallabag-fait-le-plein-de-fonctionnalites.html", - "user_email": "", - "user_id": 1, - "user_name": "wallabag" - } - -Et si vous voulez voir la liste des articles existants (voir **Récupérer les articles existants**), le tableau sera vide. - -Exemple cURL : - -:: - - curl --request DELETE "https://localhost:8000/api/entries/1.html?access_token=ZGJmNTA2MDdmYTdmNWFiZjcxOWY3MWYyYzkyZDdlNWIzOTU4NWY3NTU1MDFjOTdhMTk2MGI3YjY1ZmI2NzM5MA" - -Autres méthodes ---------------- - -Nous n'écrirons pas d'exemples pour toutes les méthodes de l'API. - -Jetez un œil à la liste complète ici http://localhost:8000/api/doc pour connaitre chaque méthode. - -Ressources tierces ------------------- - -Certaines applications ou bibliothèques utilisent notre API. En voici une liste non exhaustive : - -- `Java wrapper for the wallabag API `_ par Dmitriy Bogdanov. -- `.NET library for the wallabag v2 API `_ par Julian Oster. -- `Python API for wallabag `_ par FoxMaSk, pour son projet `Trigger Happy `_. -- `Un plugin `_ conçu pour `Tiny Tiny RSS `_ qui utilise l'API wallabag v2. Par Josh Panter. -- `Golang wrapper for the wallabag API `_ par Strubbl, pour son projets `wallabag-stats graphe `_ et l'outil de ligne de commande `wallabag-add-article `_. -- Tool to automatically download Wallabag articles into your local computer or Kobo ebook reader `wallabako `_ par anarcat. diff --git a/docs/fr/developer/asynchronous.rst b/docs/fr/developer/asynchronous.rst deleted file mode 100644 index ff22daea..00000000 --- a/docs/fr/developer/asynchronous.rst +++ /dev/null @@ -1,160 +0,0 @@ -Tâches asynchrones -================== - -Pour lancer des tâches asynchrones (utile pour des imports importants par exemple), nous pouvons utiliser RabbitMQ ou Redis. - -Installer RabbitMQ pour des tâches asynchrones ----------------------------------------------- - -Pour lancer des tâches asynchrones (utile pour des imports importants par exemple), nous pouvons utiliser RabbitMQ. - -Pré-requis -^^^^^^^^^^ - -Vous devez installer RabbitMQ sur votre serveur. - -Installation -^^^^^^^^^^^^ - -.. code:: bash - - wget https://www.rabbitmq.com/rabbitmq-signing-key-public.asc - apt-key add rabbitmq-signing-key-public.asc - apt-get update - apt-get install rabbitmq-server - -Configuration et démarrage -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code:: bash - - rabbitmq-plugins enable rabbitmq_management # (useful to have a web interface, available at http://localhost:15672/ (guest/guest) - rabbitmq-server -detached - -Arrêter RabbitMQ -^^^^^^^^^^^^^^^^ - -.. code:: bash - - rabbitmqctl stop - -Configurer RabbitMQ dans wallabag -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Modifiez votre fichier ``app/config/parameters.yml`` pour éditer la configuration RabbitMQ. Celle par défaut devrait convenir : - -.. code:: yaml - - rabbitmq_host: localhost - rabbitmq_port: 5672 - rabbitmq_user: guest - rabbitmq_password: guest - rabbitmq_prefetch_count: 10 # lire http://www.rabbitmq.com/consumer-prefetch.html - -Activer RabbitMQ dans wallabag -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Dans les paramètres internes, section **Import**, activez RabbitMQ (avec la valeur 1). - -Démarrer les clients RabbitMQ -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -En fonction du service dont vous souhaitez importer vos données, vous devez activer un (ou plusieurs si vous souhaitez en supporter plusieurs) cron job : - -.. code:: bash - - # for Pocket import - bin/console rabbitmq:consumer -e=prod import_pocket -w - - # for Readability import - bin/console rabbitmq:consumer -e=prod import_readability -w - - # for Instapaper import - bin/console rabbitmq:consumer -e=prod import_instapaper -w - - # for wallabag v1 import - bin/console rabbitmq:consumer -e=prod import_wallabag_v1 -w - - # for wallabag v2 import - bin/console rabbitmq:consumer -e=prod import_wallabag_v2 -w - - # for Firefox import - bin/console rabbitmq:consumer -e=prod import_firefox -w - - # for Chrome import - bin/console rabbitmq:consumer -e=prod import_chrome -w - -Installer Redis pour des tâches asynchrones -------------------------------------------- - -Pour lancer des tâches asynchrones (utile pour des imports importants par exemple), nous pouvons utiliser Redis. - -Pré-requis -^^^^^^^^^^ - -Vous devez installer Redis sur votre serveur. - -Installation -^^^^^^^^^^^^ - -.. code:: bash - - apt-get install redis-server - -Démarrage -^^^^^^^^^ - -Le serveur devrait déjà être démarré après l'installation. Si ce n'est pas le cas, vous pouvez le démarrer ainsi : - -.. code:: bash - - redis-server - -Configurer Redis dans wallabag -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Modifiez votre fichier ``app/config/parameters.yml`` pour éditer la configuration Redis. Celle par défaut devrait convenir : - -.. code:: yaml - - redis_host: localhost - redis_port: 6379 - -Activer Redis dans wallabag -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Dans les paramètres internes, section **Import**, activez Redis (avec la valeur 1). - -Démarrer les clients Redis -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -En fonction du service dont vous souhaitez importer vos données, vous devez activer un (ou plusieurs si vous souhaitez en supporter plusieurs) cron job : - -.. code:: bash - - # for Pocket import - bin/console wallabag:import:redis-worker -e=prod pocket -vv >> /path/to/wallabag/var/logs/redis-pocket.log - - # for Readability import - bin/console wallabag:import:redis-worker -e=prod readability -vv >> /path/to/wallabag/var/logs/redis-readability.log - - # for Instapaper import - bin/console wallabag:import:redis-worker -e=prod instapaper -vv >> /path/to/wallabag/var/logs/redis-instapaper.log - - # for wallabag v1 import - bin/console wallabag:import:redis-worker -e=prod wallabag_v1 -vv >> /path/to/wallabag/var/logs/redis-wallabag_v1.log - - # for wallabag v2 import - bin/console wallabag:import:redis-worker -e=prod wallabag_v2 -vv >> /path/to/wallabag/var/logs/redis-wallabag_v2.log - - # for Firefox import - bin/console wallabag:import:redis-worker -e=prod firefox -vv >> /path/to/wallabag/var/logs/redis-firefox.log - - # for Chrome import - bin/console wallabag:import:redis-worker -e=prod chrome -vv >> /path/to/wallabag/var/logs/redis-chrome.log - -Si vous souhaitez démarrer l'import pour quelques messages uniquement, vous pouvez spécifier cette valeur en paramètre (ici 12) et le client va s'arrêter après le 12ème message : - -.. code:: bash - - bin/console wallabag:import:redis-worker -e=prod pocket -vv --maxIterations=12 diff --git a/docs/fr/developer/docker.rst b/docs/fr/developer/docker.rst deleted file mode 100644 index 84724ed3..00000000 --- a/docs/fr/developer/docker.rst +++ /dev/null @@ -1,50 +0,0 @@ -Exécuter wallabag avec docker-compose -===================================== - -Pour faire tourner votre propre instance de développement de wallabag, vous pouvez -utiliser les fichiers docker pré-configurés. - -Pré-requis ----------- - -Soyez sur d'avoir `Docker -`__ et `Docker -Compose `__ installés et à jour sur votre -système. - -Changer de SGBD ---------------- - -Par défaut, wallabag fonctionne avec une base de données SQLite. -Depuis que wallabag supporte Postgresql et MySQL, les conteneurs Docker sont -aussi disponibles pour ces SGBD. - -Dans ``docker-compose.yml``, en fonction de votre SGBD, décommentez : - -- la définition du conteneur (le block racine ``postgres`` ou ``mariadb``) -- le conteneur ``links`` dans le conteneur ``php`` -- le conteneur ``env_file`` dans le conteneur ``php`` - -Pour que les commandes Symfony (par exemple ``wallabag:install``) continuent de -fonctionner sur votre système, vous devez aussi : - -- charger le bon fichier d'environnement dans votre ligne de commandes (``source``), -pour que les variables comme ``SYMFONY__ENV__DATABASE_HOST`` existent. -- ajouter une ligne ``127.0.0.1 rdbms`` dans votre fichier ``hosts`` - -Exécuter wallabag ------------------ - -#. Forker et cloner le projet -#. Editer ``app/config/parameters.yml`` pour remplacer les propriétés ``database_*`` - par les lignes commentées (celles avec des valeurs préfixées par ``env.``) -#. ``composer install`` pour installer les dépendances -#. ``php bin/console wallabag:install`` pour créer le schéma de la BDD -#. ``docker-compose up`` pour démarrer les conteneurs -#. Enfin, se rendre sur http://localhost:8080/ pour accéder à une installation - tout propre de wallabag. - -Il est possible de rencontrer des problèmes de droits UNIX, de mauvais chemins -dans les fichiers de cache, etc… -Les opérations comme vider le cache ou restaurer les permissions des fichiers -peuvent être fréquemment nécessaires, n'ayez crainte ! diff --git a/docs/fr/developer/documentation.rst b/docs/fr/developer/documentation.rst deleted file mode 100644 index 0fbe5626..00000000 --- a/docs/fr/developer/documentation.rst +++ /dev/null @@ -1,12 +0,0 @@ -Contribuer à cette documentation -================================ - -Les sources de notre documentation sont ici https://github.com/wallabag/wallabag/tree/master/docs - -Nous utilisons `ReadTheDocs -`__ pour la générer. - -Les pages sont écrites au format `Restructured Text -`__. Vous pouvez utiliser des outils en ligne comme http://rst.aaroniles.net/ ou http://rst.ninjs.org/ pour prévisualiser vos articles. - -Si vous créez une nouvelle page, n'oubliez pas d'éditer le fichier `index.rst `__ pour ajouter un lien dans la barre latérale. \ No newline at end of file diff --git a/docs/fr/developer/paywall.rst b/docs/fr/developer/paywall.rst deleted file mode 100644 index ddba839a..00000000 --- a/docs/fr/developer/paywall.rst +++ /dev/null @@ -1,65 +0,0 @@ -Articles derrière un paywall -============================ - -wallabag peut récupérer le contenu des articles des sites qui utilisent un système de paiement. - -Activer l'authentification pour les paywall -------------------------------------------- - -Dans les paramètres internes, en tant qu'administrateur de wallabag, section **Article**, activez l'authentification pour les articles derrière un paywall (avec la valeur 1). - -Configurer les accès dans wallabag ----------------------------------- - -Éditez le fichier ``app/config/parameters.yml`` pour modifier les accès aux sites avec paywall. Par exemple, sous Ubuntu : - -``sudo -u www-data nano /var/www/html/wallabag/app/config/parameters.yml`` - -Voici un exemple pour certains sites (attention, ne pas utiliser la touche "tab", seulement des espaces) : - -.. code:: yaml - - sites_credentials: - mediapart.fr: {username: "myMediapartLogin", password: "mypassword"} - arretsurimages.net: {username: "myASILogin", password: "mypassword"} - -.. note:: - - Ces accès seront partagés entre chaque utilisateur de votre instance wallabag. - -Fichiers de configuration pour parser les articles --------------------------------------------------- - -.. note:: - - Lisez `cette documentation `_ pour en savoir plus sur ces fichiers de configuration, qui se trouvent dans le répertoire ``vendor/j0k3r/graby-site-config/``. Pour la majorité des sites, ce fichier est déjà configuré : les instructions qui suivent concernent seulement les sites non encore configurés. - -Chaque fichier de configuration doit être enrichi en ajoutant ``requires_login``, ``login_uri``, -``login_username_field``, ``login_password_field`` et ``not_logged_in_xpath``. - -Attention, le formulaire de connexion doit se trouver dans le contenu de la page lors du chargement de celle-ci. -Il sera impossible pour wallabag de se connecter à un site dont le formulaire de connexion est chargé après coup (en ajax par exemple). - -``login_uri`` correspond à l'URL à laquelle le formulaire est soumis (attribut ``action`` du formulaire). -``login_username_field`` correspond à l'attribut ``name`` du champ de l'identifiant. -``login_password_field`` correspond à l'attribut ``name`` du champ du mot de passe. - -Par exemple : - -.. code:: - - title://div[@id="titrage-contenu"]/h1[@class="title"] - body: //div[@class="contenu-html"]/div[@class="page-pane"] - - requires_login: yes - - login_uri: http://www.arretsurimages.net/forum/login.php - login_username_field: username - login_password_field: password - - not_logged_in_xpath: //body[@class="not-logged-in"] - -Dernière étape : nettoyer le cache ----------------------------------- - -Il est nécessaire de nettoyer le cache de wallabag avec la commande suivante (ici sous Ubuntu) : ``sudo -u www-data php /var/www/html/wallabag/bin/console cache:clear -e=prod`` diff --git a/docs/fr/developer/testsuite.rst b/docs/fr/developer/testsuite.rst deleted file mode 100644 index bd5c68c6..00000000 --- a/docs/fr/developer/testsuite.rst +++ /dev/null @@ -1,10 +0,0 @@ -Suite de tests -============== - -Pour assurer la qualité du développement de wallabag, nous avons écrit des tests avec `PHPUnit `_. - -Si vous contribuez au projet (en traduisant l'application, en corrigeant des bugs ou en ajoutant une nouvelle fonctionnalité), merci d'écrire vos propres tests. - -Pour lancer la suite de tests de wallabag, vous devez installer `ant `_. - -Ensuite, exécutez la commande ``make test``. diff --git a/docs/fr/developer/translate.rst b/docs/fr/developer/translate.rst deleted file mode 100644 index 870d1c20..00000000 --- a/docs/fr/developer/translate.rst +++ /dev/null @@ -1,62 +0,0 @@ -Traduire wallabag -================= - -L'application web ------------------ - -Fichiers de traductions -~~~~~~~~~~~~~~~~~~~~~~~ - -.. note:: - - Comme wallabag est principalement dévelopée par une équipe française, c'est - cette traduction qui est considérée comme la plus récente. Merci de vous baser - sur celle-ci pour créer votre traduction. - -Les principaux fichiers de traduction se trouvent ici : https://github.com/wallabag/wallabag/tree/master/src/Wallabag/CoreBundle/Resources/translations. - -Vous devez créer les fichiers ``messages.CODE.yml`` et ``validators.CODE.yml``, -où CODE est le code ISO 639-1 de votre langue (`cf wikipedia `__). - -Autres fichiers à traduire : - -- https://github.com/wallabag/wallabag/tree/master/app/Resources/CraueConfigBundle/translations. -- https://github.com/wallabag/wallabag/tree/master/src/Wallabag/UserBundle/Resources/translations. - -Vous devez créer les fichiers ``LE_FICHIER_DE_TRADUCTION.CODE.yml``. - -Fichier de configuration -~~~~~~~~~~~~~~~~~~~~~~~~ - -Vous devez éditer `app/config/config.yml -`__ pour -afficher votre langue dans la page Configuration de wallabag (pour permettre aux -utilisateurs de choisir cette nouvelle traduction). - -Dans la section ``wallabag_core.languages``, vous devez ajouter une nouvelle ligne -avec votre traduction. Par exemple : - -:: - - wallabag_core: - ... - languages: - en: 'English' - fr: 'Français' - - -Pour la première colonne (``en``, ``fr``, etc.), vous devez ajouter le code ISO 639-1 -de votre langue (voir ci-dessus). - -Pour la seconde colonne, c'est juste le nom de votre langue. - -Documentation de wallabag -------------------------- - -.. note:: - - Contrairement à l'application, la langue principale de la documentation est l'anglais - -Les fichiers de documentation se trouvent ici : https://github.com/wallabag/wallabag/tree/master/docs - -Vous devez respecter la structure du dossier ``en`` quand vous crééz votre traduction. diff --git a/docs/fr/index.rst b/docs/fr/index.rst deleted file mode 100644 index 99aaa35a..00000000 --- a/docs/fr/index.rst +++ /dev/null @@ -1,54 +0,0 @@ -Documentation de wallabag -========================= - -.. image:: ../img/wallabag.png - :alt: wallabag logo - :align: center - -**wallabag** est une application de lecture différée : elle permet -simplement d’archiver une page web en ne conservant que le contenu. Les -éléments superflus (menu, publicité, etc.) sont supprimés. - -La documentation principale de cette application est découpée en plusieurs sections : - -* :ref:`user-docs` -* :ref:`dev-docs` - -La documentation est disponible dans d'autres langues : - -* `Documentation in english `_ -* `Deutsch Dokumentation `_ - -.. _user-docs: - -.. toctree:: - :maxdepth: 2 - :caption: Documentation utilisateur - - user/installation - user/upgrade - user/configuration - user/import - user/create_account - user/articles - user/errors_during_fetching - user/filters - user/tags - user/configuring_mobile - user/android - user/parameters - user/backup - user/faq - -.. _dev-docs: - -.. toctree:: - :maxdepth: 2 - :caption: Documentation développeur - - developer/api - developer/docker - developer/paywall - developer/documentation - developer/translate - developer/asynchronous diff --git a/docs/fr/requirements.txt b/docs/fr/requirements.txt deleted file mode 100644 index 06fc8973..00000000 --- a/docs/fr/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -Sphinx>=1.3.0,<1.4.0 -guzzle_sphinx_theme>=0.7.0,<0.8.0 diff --git a/docs/fr/user/android.rst b/docs/fr/user/android.rst deleted file mode 100644 index 91501eea..00000000 --- a/docs/fr/user/android.rst +++ /dev/null @@ -1,96 +0,0 @@ -Application Android -=================== - -But de ce document ------------------- - -Ce document explique comment configurer votre application Android pour qu'elle fonctionne avec votre instance de wallabag. Il n'y a pas de différence dans cette procédure entre wallabag v1 et wallabag v2. - -Étapes pour configurer votre application ----------------------------------------- - -Quand vous démarrez l'application pour la première fois, vous voyez le message de bienvenue, où il vous est d'abord conseillé de configurer l'application avec votre instance de wallabag. - -.. image:: ../../img/user/android_welcome_screen.en.png - :alt: Écran de bienvenue - :align: center - -Vous devez confirmer le message et vous serez redirigé vers l'écran de configuration. - -.. image:: ../../img/user/android_configuration_screen.en.png - :alt: Écran de configuration - :align: center - -Saisissez vos données wallabag. Vous devez entrer l'adresse de votre instance de wallabag. **Il ne faut pas que cette adresse se termine par un slash**. Ajoutez également vos identifiants wallabag dans les champs correspondants. - -.. image:: ../../img/user/android_configuration_filled_in.en.png - :alt: Paramètres remplis - :align: center - -Après cet écran, appuyez sur le bouton de test de connexion et attendez que le test se termine. - -.. image:: ../../img/user/android_configuration_connection_test.en.png - :alt: Test de connexion - :align: center - -Le test de connexion devrait se terminer avec succès. Si ce n'est pas le cas, vous devez résoudre ça avant de continuer. - -.. image:: ../../img/user/android_configuration_connection_test_success.en.png - :alt: Test de connexion réussi - :align: center - -Après le test de connexion réussi, vous pouvez cliquer sur le bouton pour récupérer vos informations de flux (feed credentials). L'application essaie maintenant de se connecter à wallabag pour récupérer votre identifiant et votre jeton pour les flux RSS. - -.. image:: ../../img/user/android_configuration_get_feed_credentials.en.png - :alt: Récupération des informations de flux - :align: center - -Quand le processus est terminé avec succès, vous verrez une notification comme quoi l'identifiant et le jeton ont été remplis correctement. - -.. image:: ../../img/user/android_configuration_feed_credentials_automatically_filled_in.en.png - :alt: Récupération des informations correcte - :align: center - -Maintenant, vous devez naviguer jusqu'en bas de l'écran des paramètres. Bien sur, vous pouvez régler les paramètres comme vous le souhaitez. Enregistrez la configuration. - -.. image:: ../../img/user/android_configuration_scroll_bottom.en.png - :alt: Bottom of the settings screen - :align: center - -Après avoir enregistré les paramètres, vous vous retrouvez face à l'écran suivant. L'application vous propose de démarrer une synchronisation pour récupérer vos articles. Il est recommandé de confirmer cette action. - -.. image:: ../../img/user/android_configuration_saved_feed_update.en.png - :alt: Settings saved the first time - :align: center - -Une fois la synchronisation terminée avec succès, vous pouvez lire vos articles. - -.. image:: ../../img/user/android_unread_feed_synced.en.png - :alt: Filled article list cause feeds successfully synchronized - :align: center - -Limitations connues -------------------- - -Double authentification (2FA) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Actuellement, l'application Android ne supporte la double authentification. Vous devez la désactiver pour que l'application fonctionne correctement. - -Limiter le nombre d'articles avec wallabag v2 -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Dans votre instance de wallabag, vous pouvez configurer combien d'articles se trouvent dans les flux RSS. Cette option n'existe pas dans wallabag v1, où tous les articles se retrouvent donc dans le flux RSS. So if you set the amount of articles being displayed greater than the number of items being content of your RSS feed, you will only see the number of items in your RSS feed. - -Cryptage SSL/TLS -~~~~~~~~~~~~~~~~ - -Si vous souhaitez accéder à votre instance de wallabag via HTTPS, vous devez le définir dans les paramètres. Surtout si votre URL HTTP redirige vers l'URL HTTPS. Actuellement, l'application ne gère pas cette redirection correctement. - -Références ----------- - -- `Code source de l'application Android `_ -- `Télécharger l'application Android sur F-Droid `_ -- `Télécharger l'application Android sur Google Play `_ - diff --git a/docs/fr/user/articles.rst b/docs/fr/user/articles.rst deleted file mode 100644 index fb5b3837..00000000 --- a/docs/fr/user/articles.rst +++ /dev/null @@ -1,123 +0,0 @@ -Articles -======== - -Sauvegardez votre premier article ---------------------------------- - -La fonctionnalité principale de wallabag est de sauvegarder des articles. -Vous avez plusieurs manières de le faire. - -.. note:: - - Un guide de démarrage s'affichera dans l'application jusqu'à ce que vous - enregistriez votre premier article. - -En utilisant le bookmarklet -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Sur la page ``Aide``, vous avez un onglet ``Bookmarklet``. Glissez/déposez le lien ``bag it!`` -dans votre barre de favoris de votre navigateur. - -Maintennat, à chaque fois que vous lisez un article et que vous souhaitez le sauvegarder, -cliquez sur le lien ``bag it!`` dans votre barre de favoris. L'article est enregistré. - -En utilisant le formulaire classique -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Dans la barre haut de wallabag, vous avez trois icônes. Avec la première icône, -un signe plus, vous pouvez facilement ajouter un nouvel article. - -.. image:: ../../img/user/topbar.png - :alt: Barre supérieure - :align: center - -Cliquez dessus pour afficher un nouveau champ, collez-y l'URL de l'article et appuyez -sur la touche ``Entrée``. L'article est enregistré. - -En utilisant l'extension de votre navigateur -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Firefox -""""""" - -Vous pouvez télécharger `l'extension Firefox ici `_. - -Chrome -"""""" - -Vous pouvez télécharger `l'extension Chrome ici `_. - -En utilisant l'application de votre smartphone -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Android -""""""" - -Vous pouvez télécharger `l'application Android ici `_. - -Windows Phone -~~~~~~~~~~~~~ - -Vous pouvez télécharger `l'application Windows Phone ici `_. - -Téléchargez vos articles ------------------------- - -Vous pouvez télécharger chaque article dans plusieurs formats : ePUB, MOBI, PDF, XML, JSON, CSV. - -Lorsque vous lisez un article, cliquez sur cette icône dans la barre latérale : - -.. image:: ../../img/user/download_article.png - :alt: Télécharger l'article - :align: center - -Vous pouvez aussi télécharger une catégorie (non lus, favoris, lus) dans ces formats. -Par exemple, dans la vue **Non lus**, cliquez sur cette icône dans la barre supérieure : - -.. image:: ../../img/user/download_articles.png - :alt: Télécharger l'article - :align: center - -Partagez vos articles ---------------------- - -Quand vous lisez un article, vous pouvez le partager. Cliquez sur le bouton de partage : - -.. image:: ../../img/user/share.png - :alt: partager un article - :align: center - -Vous pouvez maintenant le partager : - -- avec une URL publique (vous obtiendrez une vue allégée de l'article) -- avec un tweet -- dans votre Shaarli -- avec un message dans Diaspora* -- sur Carrot -- avec un email - -Annotez vos articles --------------------- - -Sur chaque article que vous lisez, vous pouvez écrire des annotations. Puisqu'une image vaut mieux qu'un long discours, -voici ce que ça donne. - -Sélectionnez la zone du texte que vous souhaitez annoter et cliquez sur le crayon : - -.. image:: ../../img/user/annotations_1.png - :alt: Sélectionnez votre texte - :align: center - -Ensuite, écrivez votre annotation : - -.. image:: ../../img/user/annotations_2.png - :alt: Écrivez votre annotation - :align: center - -Le texte est maintenant surligné et vous pouvez lire le annotation en le survolant avec votre souris. - -.. image:: ../../img/user/annotations_3.png - :alt: Lisez votre annotation - :align: center - -Vous pouvez créer autant de annotations que vous le souhaitez. diff --git a/docs/fr/user/backup.rst b/docs/fr/user/backup.rst deleted file mode 100644 index f8cf87ee..00000000 --- a/docs/fr/user/backup.rst +++ /dev/null @@ -1,26 +0,0 @@ -Sauvegarde de wallabag -====================== - -Parce que des fois vous faites des erreurs avec votre installation de wallabag et vous perdez des données ou parce que vous souhaitez migrer votre installation sur un autre serveur, vous souhaitez faire une sauvegarde de vos données. -Cette documentation décrit ce que vous devez sauvegarder. - -Configuration -------------- -wallabag stocke quelques paramètres (comme la configuration SMTP ou les infos de bases de données) dans le fichier `app/config/parameters.yml`. - -Base de données ---------------- -Comme wallabag supporte différentes sortes de bases de données, la manière de sauvegarder dépend du type de base de données que vous utilisez, donc vous devez vous en référez à la documentation correspondante. - -Quelques exemples : - -- MySQL: http://dev.mysql.com/doc/refman/5.7/en/backup-methods.html -- PostgreSQL: https://www.postgresql.org/docs/current/static/backup.html - -SQLite -~~~~~~ -Pour sauvegarder une base SQLite, vous devez juste copier le répertoire `data/db` de votre installation wallabag. - -Images ------- -Les images sauvegardées par wallabag sont stockées dans `web/assets/images` (le stockage des images sera implémenté dans wallabag 2.2). diff --git a/docs/fr/user/configuration.rst b/docs/fr/user/configuration.rst deleted file mode 100644 index 772000c3..00000000 --- a/docs/fr/user/configuration.rst +++ /dev/null @@ -1,133 +0,0 @@ -Configuration -============= - -Maintenant que vous êtes connecté, il est temps de configurer votre compte. - -Cliquez sur le menu ``Configuration``. Vous avez accès à 5 onglets : -``Paramètres``, ``RSS``, ``Mon compte``, ``Mot de passe`` and ``Règles de tag automatiques``. - -Paramètres ----------- - -Thème -~~~~~ - -L'affichage de wallabag est personnalisable. C'est ici que vous choisissez le thème -que vous préférez. Le thème par défaut est ``Material``, c'est celui -qui est utilisé dans les captures d'écran de la documentation. - -Nombre d'articles par page -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Vous pouvez définir le nombre d'articles affichés sur chaque page. - -Vitesse de lecture -~~~~~~~~~~~~~~~~~~ - -wallabag calcule une durée de lecture pour chaque article. Vous pouvez définir ici, grâce à cette liste déroulante, si vous lisez plus ou moins vite. wallabag recalculera la durée de lecture de chaque article. - -Où souhaitez-vous être redirigé après avoir marqué un article comme lu ? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Chaque fois que vous ferez certaines actions (après avoir marqué un article comme lu / comme favori, -après avoir supprimé un article, après avoir retiré un tag d'un article), vous pouvez être redirigé : - -- sur la page d'accueil -- sur la page courante - -Langue -~~~~~~ - -Vous pouvez définir la langue de l'interface de wallabag. - -RSS ---- - -wallabag propose un flux RSS pour chaque statut d'article : non lus, favoris et lus. - -Tout d'abord, vous devez vous créer un jeton personnel : cliquez sur ``Créez votre jeton``. -Il est possible de regénérer votre jeton en cliquant sur ``Réinitialisez votre jeton``. - -Vous avez maintenant trois liens, un par statut : ajoutez-les dans votre agrégateur de flux RSS préféré. - -Vous pouvez aussi définir combien d'articles vous souhaitez dans vos flux RSS -(50 est la valeur par défaut). - -Une pagination est aussi disponible pour ces flux. Il suffit de rajouter ``?page=2`` pour aller à la seconde page, par exemple. -Cette pagination suit `la RFC `_, ce qui signifie que vous trouverez la page suivante (``next``), précédente (``previous``) et la dernière (``last``) dans la balise `` de chaque flux RSS. - -Mon compte ----------- - -Vous pouvez ici modifier votre nom, votre adresse email et activer la ``Double authentification``. - -Si l'instance de wallabag compte plus d'un utilisateur actif, vous pouvez supprimer ici votre compte. **Attention, nous supprimons toutes vos données**. - -Double authentification (2FA) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - L'authentification à deux facteurs (également appelée 2FA) est une technologie brevetée en 1984 - qui fournit l'identification des utilisateurs au moyen de la combinaison de deux composants différents . - - https://fr.wikipedia.org/wiki/Authentification_forte - -**Attention**: l'activation de la 2FA depuis l'interface de configuration n'est possible que si elle a au préalable été autorisée dans `app/config/parameters.yml` en passant la propriété *twofactor_auth* à `true` (n'oubliez pas d'exécuter `php bin/console cache:clear -e=prod` après modification). - -Si vous activez 2FA, à chaque tentative de connexion à wallabag, vous recevrez -un code par email. Vous devez renseigner ce code dans le formulaire suivant : - -.. image:: ../../img/user/2FA_form.png - :alt: Authentification à deux facteurs - :align: center - -Si vous ne souhaitez pas recevoir un code à chaque fois que vous vous connectez, -vous pouvez cocher la case ``Je suis sur un ordinateur de confiance`` : wallabag -se souviendra de vous pour 15 jours. - -Mot de passe ------------- - -Vous pouvez changer de mot de passe ici (8 caractères minimum). - -Règles de tag automatiques --------------------------- - -Si vous voulez automatiquement assigner un tag à de nouveaux articles en fonction de -certains critères, cette partie de la configuration est pour vous. - -Que veut dire « règles de tag automatiques » ? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Ce sont des règles utilisées par wallabag pour automatiquement assigner un tag -à un nouvel article. -À chaque fois que vous ajoutez un nouvel article, toutes les règles sont utilisées pour ajouter -les tags que vous avez configurés, vous épargnant ainsi la peine de classer manuellement vos articles. - -Comment les utiliser ? -~~~~~~~~~~~~~~~~~~~~~~ - -Admettons que vous voulez ajouter comme tag *« lecture rapide »* quand le temps de lecture -d'un article est inférieur à 3 minutes. -Dans ce cas, vous devez ajouter « readingTime <= 3 » dans le champ **Règle** et *« lecture rapide »* dans le champ **Tags**. -Plusieurs tags peuvent être ajoutés en même temps en les séparant par une virgule : *« lecture rapide, à lire »*. -Des règles complexes peuvent être écrites en utilisant les opérateurs pré-définis : -if *« readingTime >= 5 AND domainName = "github.com" »* then tag as *« long reading, github »*. - -Quels variables et opérateurs puis-je utiliser pour écrire mes règles ? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Les variables et opérateurs suivants peuvent être utilisés lors de la création de vos règles (attention, pour certaines valeurs, vous devez ajouter des guillemets, par exemple ``language = "en"``) : - -=========== ============================================== ========== ========== -Variable Sens Opérateur Sens ------------ ---------------------------------------------- ---------- ---------- -title Titre de l'article <= Inférieur ou égal à … -url URL de l'article < Strictement inférieur à … -isArchived Si l'article est archivé ou non => Supérieur ou égal à … -isStarred Si l'article est en favori ou non > Strictement supérieur à … -content Le contenu de l'article = Égal à … -language La langue de l'article != Différent de … -mimetype The type MIME de l'article OR Telle règle ou telle autre règle -readingTime Le temps de lecture de l'article, en minutes AND Telle règle et telle règle -domainName Le nom de domaine de l'article matches Contient telle chaîne de caractère (insensible à la casse). Exemple : title matches "football" -=========== ============================================== ========== ========== diff --git a/docs/fr/user/configuring_mobile.rst b/docs/fr/user/configuring_mobile.rst deleted file mode 100644 index 4036c354..00000000 --- a/docs/fr/user/configuring_mobile.rst +++ /dev/null @@ -1,12 +0,0 @@ -Configurer les applications mobile pour wallabag -================================================ - -Étapes pour configurer les applications ---------------------------------------- - -- Tout d'abord, *créez un nouveau client* dans la section ``Gestion des clients API``. Le nom de votre application importe peu. Ce qui nous intéresse, ce sont l'``ID Client`` et la ``clé secrète``. Notez bien ces deux valeurs. -- Quand vous installez une application smartphone, on va vous demander de renseigner l'adresse de votre serveur de wallabag. Par exemple, pour wallabag.it, c'est ``https://app.wallabag.it``. -- Les deux valeurs notées précédemment vous seront également demandées. Insérez-les dans les champs correspondants. -- Enfin, vous devez renseigner votre ``nom d'utilisateur`` et votre ``mot de passe``. Ce sont les mêmes valeurs que lorsque vous vous connectez à wallabag. - -Vous pouvez également regarder du côté de la page `Android `_. Particulièrement la section sur les *limitations connues*. diff --git a/docs/fr/user/create_account.rst b/docs/fr/user/create_account.rst deleted file mode 100644 index 9c18e094..00000000 --- a/docs/fr/user/create_account.rst +++ /dev/null @@ -1,42 +0,0 @@ -Se créer un compte et se connecter -================================== - -Se créer un compte ------------------- - -Sur la page de connexion, cliquez sur le bouton ``Créer un compte``. - -.. image:: ../../img/user/registration_form.png - :alt: Formulaire de création de compte - :align: center - -Vous devez renseigner le formulaire. Faites attention de bien renseigner une adresse -email valide, nous allons vous envoyer un email d'activation. - -.. image:: ../../img/user/sent_email.png - :alt: Un email a été envoyé pour activer votre compte - :align: center - -Vérifiez votre boite de réception, vous avez un nouvel email avec un lien comme celui-ci -``http://wallabag/register/confirm/Ba19wokGovN-DdBQNfg4YgRkUQWRP4-k2g0Bk-hBTX4``. -Cliquez dessus pour activer votre compte. - -Votre compte est maintenant actif. - -.. image:: ../../img/user/activated_account.png - :alt: Bienvenue à bord ! - :align: center - -Se connecter ------------- - -Votre compte est maintenant actif, félicitations ! - -Pour vous connecter à wallabag, remplissez le formulaire de connexion. - -Si vous êtes sur un ordinateur de confiance et que vous souhaitez rester connecté -vous pouvez cocher la case ``Restez connecté`` : wallabag se souviendra de vous pour un an. - -.. image:: ../../img/user/login_form.png - :alt: Formulaire de connexion - :align: center diff --git a/docs/fr/user/errors_during_fetching.rst b/docs/fr/user/errors_during_fetching.rst deleted file mode 100644 index 13cbde1d..00000000 --- a/docs/fr/user/errors_during_fetching.rst +++ /dev/null @@ -1,37 +0,0 @@ -Erreur durant la récupération des articles -========================================== - -Pourquoi la récupération des articles échoue ? ----------------------------------------------- - -Il peut y avoir plusieurs raisons : - -- problème de connexion internet -- wallabag ne peut pas récupérer le contenu à cause de la structure du site web - -Comment puis-je aider pour réparer ça ? ---------------------------------------- - -Vous pouvez essayer de résoudre ce problème vous même (comme ça, nous restons concentrés pour améliorer wallabag au lieu d'écrire ces fichiers de configuration :) ). - -Vous pouvez essayer de voir si ça fonctionne ici : `http://f43.me/feed/test `_ (ce site utilise principalement la même manière de fonctionner que wallabag pour récupérer les articles). - -Si ça fonctionne ici et pas sur wallabag, c'est qu'il y a un souci avec wallabag qui casse le parser (difficile à résoudre : merci d'ouvrir un nouveau ticket à ce sujet). - -Si ça ne fonctionne pas, vous pouvez essayer de créer un fichier de configuration en utilisant : `http://siteconfig.fivefilters.org/ `_ (sélectionnez les parties du contenu qui correspondent à ce que vous souhaitez garder). Vous pouvez `lire cette documentation avant `_. - -Vous pouvez tester ce fichier sur le site **f43.me** : cliquez sur **Want to try a custom siteconfig?** et insérez le fichier généré depuis siteconfig.fivefilters.org. - -Répétez cette opération jusqu'à avoir quelque chose qui vous convienne. - -Ensuite, vous pouvez créer une pull request ici `https://github.com/fivefilters/ftr-site-config `_, qui est le projet principal pour stocker les fichiers de configuration. - -Comment puis-je réessayer de récupérer le contenu ? ---------------------------------------------------- - -Si wallabag échoue en récupérant l'article, vous pouvez cliquer sur le bouton suivant -(le troisième sur l'image ci-dessous). - -.. image:: ../../img/user/refetch.png - :alt: Réessayer de récupérer le contenu - :align: center diff --git a/docs/fr/user/faq.rst b/docs/fr/user/faq.rst deleted file mode 100644 index 49aa94ba..00000000 --- a/docs/fr/user/faq.rst +++ /dev/null @@ -1,42 +0,0 @@ -Foire Aux Questions -=================== - -Durant l'installation, je rencontre cette erreur ``Error Output: sh: 1: @post-cmd: not found`` ----------------------------------------------------------------------------------------------- - -Il semblerait que vous ayiez un problème avec votre installation de ``composer``. Essayez de le désinstaller puis de le réinstaller. - -`Vous pouvez lire la documentation de composer pour savoir comment l'installer -`__. - -Je ne peux pas valider le formulaire de création de compte ----------------------------------------------------------- - -Soyez sur d'avoir bien renseigné tous les champs : - -* une adresse email valide -* le même mot de passe dans les deux champs - -Je n'ai pas reçu mon email d'activation ---------------------------------------- - -Êtes-vous sur d'avoir renseigné votre bonne adresse ? Avez-vous vérifié le dossier de spams ? - -Quand je clique sur le lien d'activation, j'ai ce message : ``The user with confirmation token "DtrOPfbQeVkWf6N" does not exist``. ----------------------------------------------------------------------------------------------------------------------------------- - -Vous avez déjà activé votre compte ou l'URL d'activation n'est pas correcte. - -J'ai oublié mon mot de passe ----------------------------- - -Vous pouvez réinitialiser votre mot de passe en cliquant sur ``Mot de passe oublié ?``, -sur la page de connexion. Ensuite, renseignez votre adresse email ou votre nom d'utilisateur, -un email vous sera envoyé. - -J'ai l'erreur ``failed to load external entity`` quand j'essaie d'installer wallabag ------------------------------------------------------------------------------------- - -Comme décrit `ici `_, modifiez le fichier ``web/app.php`` et ajoutez la ligne ``libxml_disable_entity_loader(false);`` à la ligne 5. - -C'est un bug lié à PHP et Doctrine, nous ne pouvons rien faire de notre côté. diff --git a/docs/fr/user/filters.rst b/docs/fr/user/filters.rst deleted file mode 100644 index 5807bdbd..00000000 --- a/docs/fr/user/filters.rst +++ /dev/null @@ -1,55 +0,0 @@ -Retrouver des articles grâce aux filtres -======================================== - -Pour retrouver plus facilement vos articles, vous pouvez utiliser les filtres. -Cliquez sur la troisième icône de la barre supérieure. - -.. image:: ../../img/user/topbar.png - :alt: Barre supérieure - :align: center - -Tous ces filtres peuvent être combinés. - -.. image:: ../../img/user/filters.png - :alt: Combine all filters - :align: center - -Statut ------- - -Utilisez ces cases à cocher pour retrouver les articles lus ou mis en favori. - -Image de prévisualisation -------------------------- - -Cochez ce filtre si vous voulez retrouver les articles avec une image de prévisualisation. - -Langage -------- - -wallabag (via graby) peut détecter la langue dans laquelle l'article est écrit. -C'est ainsi facile pour vous de retrouver des articles écrits dans une langue spécifique. - -Statut HTTP ------------ - -Vous pouvez retrouver des articles en filtrant par leur code HTTP : 200, 404, 500, etc. - -Temps de lecture ----------------- - -wallabag estime combien de temps vous avez besoin pour lire un article. -Avec ce filtre, vous pouvez par exemple retrouver les articles qui ont une estimation -entre 2 et 5 minutes. - -Nom de domaine --------------- - -Grâce à ce filtre, vous pouvez retrouver les articles venant d'un même nom de domaine. -Par exemple, dans ce champ, saisissez ``lemonde.fr`` pour retrouver les articles de ce site. - -Date de création ----------------- - -Quand vous ajoutez un article, wallabag stocke la date courante. -C'est très pratique pour retrouver les articles ajoutés entre le 1er et le 31 janvier par exemple. diff --git a/docs/fr/user/import.rst b/docs/fr/user/import.rst deleted file mode 100644 index 9a2dda8f..00000000 --- a/docs/fr/user/import.rst +++ /dev/null @@ -1,155 +0,0 @@ -Migrer depuis ... -================= - -Dans wallabag 2.x, vous pouvez importer des données depuis : - -- `Pocket <#id1>`_ -- `Readability <#id2>`_ -- `Instapaper <#id4>`_ -- `wallabag 1.x <#id6>`_ -- `wallabag 2.x <#id7>`_ - -Nous avons aussi développé `un script pour exécuter des migrations via la ligne de commande <#import-via-la-ligne-de-commande-cli>`_. - -Puisque les imports peuvent gourmands en ressource, nous avons mis en place un système de tâche asynchrone. `Vous trouverez la documentation ici `_ (niveau expert). - -Pocket ------- - -Créer une nouvelle application dans Pocket -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Pour importer vos données depuis Pocket, nous utilisons l'API de Pocket. -Vous devez créer une nouvelle application sur leur site dédié aux développeurs pour continuer. - -* Créez une nouvelle application `sur leur site Développeurs `_ -* Remplissez les champs requis : nom de l'application, description de l'application, - permissions (seulement **retrieve**), la plateforme (**web**), acceptez les - termes d'utilisation du service et soumettez votre application - -Pocket vous fournira une **Consumer Key** (par exemple, `49961-985e4b92fe21fe4c78d682c1`). -Vous devez configurer la ``pocket_consumer_key`` dans le menu ``Configuration``. - -Maintenant, tout est bien configuré pour migrer depuis Pocket. - -Importez vos données dans wallabag 2.x -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Cliquez sur le lien ``Importer`` dans le menu, sur ``Importer les contenus`` dans -la section Pocket puis sur ``Se connecter à Pocket et importer les données``. - -Vous devez autoriser wallabag à se connecter à votre compte Pocket. -Vos données vont être importées. L'import de données est une action qui peut être couteuse -pour votre serveur. - -Readability ------------ - -Exportez vos données de Readability -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Sur la page des outils (`https://www.readability.com/tools/ `_), cliquez sur "Export your data" dans la section "Data Export". Vous allez recevoir un email avec un lien pour télécharger le json. - -Importez vos données dans wallabag 2.x -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Cliquez sur le lien ``Importer`` dans le menu, sur ``Importer les contenus`` dans -la section Readability et ensuite sélectionnez votre fichier json pour l'uploader. - -Vos données vont être importées. L'import de données est une action qui peut être couteuse pour votre serveur. - -Depuis Pinboard ---------------- - -Exportez vos données de Pinboard -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Sur la page « Backup » (`https://pinboard.in/settings/backup `_), cliquez sur « JSON » dans la section « Bookmarks ». Un fichier json (sans extension) sera téléchargé (``pinboard_export``). - -Importez vos données dans wallabag 2.x -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Cliquez sur le lien ``Importer`` dans le menu, sur ``Importer les contenus`` dans -la section Pinboard et ensuite sélectionnez votre fichier json pour l'uploader. - -Vos données vont être importées. L'import de données est une action qui peut être couteuse pour votre serveur. - -Depuis Instapaper ------------------ - -Exportez vos données de Instapaper -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Sur la page des paramètres (`https://www.instapaper.com/user `_), cliquez sur "Download .CSV file" dans la section "Export". Un fichier CSV se téléchargera (``instapaper-export.csv``). - -Importez vos données dans wallabag 2.x -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Cliquez sur le lien ``Importer`` dans le menu, sur ``Importer les contenus`` dans -la section Instapaper et ensuite sélectionnez votre fichier CSV pour l'uploader. - -Vos données vont être importées. L'import de données est une action qui peut être couteuse pour votre serveur. - -wallabag 1.x ------------- - -Si vous utilisiez wallabag v1.x, vous devez exporter vos données avant de migrer à wallabag v2.x, à cause du changement complet de l'application et de sa base de données. Sur votre ancienne instance de wallabag v1, vous pouvez exporter vos données en allant sur la page de configuration de l'application. - -.. image:: ../../img/user/export_v1.png - :alt: Export depuis wallabag v1 - :align: center - -.. note:: - Si vous avez plusieurs comptes sur la même instance de wallabag, chaque utilisateur doit exporter ses données depuis wallabag v1 et les importer dans la v2. - -.. note:: - S'il vous arrive des problèmes durant l'export ou l'import, n'hésitez pas à `demander de l'aide `_. - -Une fois que vous avez récupéré le fichier json contenant vos données, vous pouvez installer wallabag v2 si c'est nécessaire en suivant `la procédure standard `_. - -Une fois que vous avez créé un compte utilisateur sur votre nouvelle instance de wallabag v2, rendez-vous dans la section `Import`. Vous devez choisir l'import depuis wallabag v1 puis sélectionner votre fichier json récupéré précédemment. - -.. image:: ../../img/user/import_wallabagv1.png - :alt: Import depuis wallabag v1 - :align: center - -wallabag 2.x ------------- - -Depuis l'instance sur laquelle vous étiez, rendez-vous dans la section `Tous les articles`, puis exportez ces articles au format json. - -.. image:: ../../img/user/export_v2.png - :alt: Export depuis wallabag v2 - :align: center - -Depuis votre nouvelle instance de wallabag, créez votre compte utilisateur puis cliquez sur le lien dans le menu pour accéder à l'import. Choisissez l'import depuis wallabag v2 puis sélectionnez votre fichier json pour l'uploader. - -.. note:: - S'il vous arrive des problèmes durant l'export ou l'import, n'hésitez pas à `demander de l'aide `_. - -Import via la ligne de commande (CLI) --------------------------------------= 5.5, PHP 7 inclus. - -.. note:: - - Pour installer wallabag facilement, nous avons créé un ``Makefile``, vous avez donc besoin d'avoir installé l'outil ``make``. - -wallabag utilise un grand nombre de bibliothèques PHP pour fonctionner. Ces bibliothèques doivent être installées à l'aide d'un outil nommé Composer. Vous devez l'installer si ce n'est déjà fait et vous assurer que vous utilisez bien la version 1.2 (si vous avez déjà Composer, faite un ``composer selfupdate``). - -Installation de Composer : - -:: - - curl -s https://getcomposer.org/installer | php - -Vous pouvez trouver des instructions spécifiques `ici (en anglais) `__. - -Vous aurez besoin des extensions suivantes pour que wallabag fonctionne. Il est possible que certaines de ces extensions soient déjà activées dans votre version de PHP, donc vous n'avez pas forcément besoin d'installer tous les paquets correspondants. - -- php-session -- php-ctype -- php-dom -- php-hash -- php-simplexml -- php-json -- php-gd -- php-mbstring -- php-xml -- php-tidy -- php-iconv -- php-curl -- php-gettext -- php-tokenizer -- php-bcmath - -wallabag utilise PDO afin de se connecter à une base de données, donc vous aurez besoin d'une extension et d'un système de bases de données parmi : - -- pdo_mysql -- pdo_sqlite -- pdo_pgsql - -Installation ------------- - -Sur un serveur dédié (méthode conseillée) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Pour installer wallabag, vous devez exécuter ces commandes : - -:: - - git clone https://github.com/wallabag/wallabag.git - cd wallabag && make install - -Pour démarrer le serveur interne à php et vérifier que tout s'est installé correctement, vous pouvez exécuter : - -:: - - make run - -Et accéder wallabag à l'adresse http://lipdevotreserveur:8000 - -.. tip:: - Pour définir des paramètres via des variables d'environnement, vous pouvez les spécifier avec le préfixe ``SYMFONY__``. Par exemple, ``SYMFONY__DATABASE_DRIVER``. Vous pouvez lire la `documentation Symfony `__ pour en savoir plus. - -Sur un serveur mutualisé -~~~~~~~~~~~~~~~~~~~~~~~~ - -Nous mettons à votre disposition une archive avec toutes les dépendances à l'intérieur. -La configuration par défaut utilise SQLite pour la base de données. Si vous souhaitez changer ces paramètres, vous devez modifier le fichier ``app/config/parameters.yml``. - -Nous avons déjà créé un utilisateur : le login et le mot de passe sont ``wallabag``. - -.. caution:: Avec cette archive, wallabag ne vérifie pas si les extensions obligatoires sont présentes sur votre serveur pour bien fonctionner (ces vérifications sont faites durant le ``composer install`` quand vous avez un serveur dédié, voir ci-dessus). - -Exécutez cette commande pour télécharger et décompresser l'archive : - -.. code-block:: bash - - wget https://wllbg.org/latest-v2-package && tar xvf latest-v2-package - -Vous trouverez `le hash md5 du dernier package sur notre site `_. - -Maintenant, lisez la documentation ci-dessous pour crééer un virtual host. Accédez ensuite à votre installation de wallabag. -Si vous avez changé la configuration pour modifier le type de stockage (MySQL ou PostgreSQL), vous devrez vous créer un utilisateur via la commande ``php bin/console wallabag:install --env=prod``. - -Installation avec Docker -~~~~~~~~~~~~~~~~~~~~~~~~ - -Nous vous proposons une image Docker pour installer wallabag facilement. Allez voir du côté de `Docker Hub `__ pour plus d'informations. - -Commande pour démarrer le containeur -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. code-block:: bash - - docker pull wallabag/wallabag - -Virtual hosts -------------- - -Configuration avec Apache -~~~~~~~~~~~~~~~~~~~~~~~~~ - -N'oubliez pas d'activer le mod *rewrite* de Apache - -.. code-block:: bash - - a2enmod rewrite && systemctl reload apache2 - -En imaginant que vous vouliez installer wallabag dans le dossier ``/var/www/wallabag`` et que vous utilisiez PHP comme un module Apache, voici un vhost pour wallabag : - -:: - - - ServerName domain.tld - ServerAlias www.domain.tld - - DocumentRoot /var/www/wallabag/web - - AllowOverride None - Order Allow,Deny - Allow from All - - - Options -MultiViews - RewriteEngine On - RewriteCond %{REQUEST_FILENAME} !-f - RewriteRule ^(.*)$ app.php [QSA,L] - - - - # uncomment the following lines if you install assets as symlinks - # or run into problems when compiling LESS/Sass/CoffeScript assets - # - # Options FollowSymlinks - # - - # optionally disable the RewriteEngine for the asset directories - # which will allow apache to simply reply with a 404 when files are - # not found instead of passing the request into the full symfony stack - - - RewriteEngine Off - - - ErrorLog /var/log/apache2/wallabag_error.log - CustomLog /var/log/apache2/wallabag_access.log combined - - -.. tip:: Pour Apache 2.4, dans la section `` vous devez remplacer les directives suivantes : - -:: - - AllowOverride None - Order Allow,Deny - Allow from All - - -par - -:: - - Require All granted - - - - -Après que vous ayez rechargé/redémarré Apache, vous devriez pouvoir avoir accès à wallabag à l'adresse http://domain.tld. - -Configuration avec Nginx -~~~~~~~~~~~~~~~~~~~~~~~~ - -En imaginant que vous vouliez installer wallabag dans le dossier ``/var/www/wallabag``, voici un fichier de configuration Nginx pour wallabag : - -:: - - server { - server_name domain.tld www.domain.tld; - root /var/www/wallabag/web; - - location / { - # try to serve file directly, fallback to app.php - try_files $uri /app.php$is_args$args; - } - location ~ ^/app\.php(/|$) { - fastcgi_pass unix:/var/run/php5-fpm.sock; - 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; - } - - # return 404 for all other php files not matching the front controller - # this prevents access to other php files you don't want to be accessible. - location ~ \.php$ { - return 404; - } - - error_log /var/log/nginx/wallabag_error.log; - access_log /var/log/nginx/wallabag_access.log; - } - -Après que vous ayez rechargé/redémarré Nginx, vous devriez pouvoir avoir accès à wallabag à l'adresse http://domain.tld. - -.. tip:: - - Si vous voulez importer un fichier important dans wallabag, vous devez ajouter cette ligne dans votre configuration nginx ``client_max_body_size XM; # allows file uploads up to X megabytes``. - -Configuration avec lighttpd -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -En imaginant que vous vouliez installer wallabag dans le dossier ``/var/www/wallabag``, voici un fichier de configuration pour wallabag (éditez votre fichier ``lighttpd.conf`` collez-y cette configuration) : - -:: - - server.modules = ( - "mod_fastcgi", - "mod_access", - "mod_alias", - "mod_compress", - "mod_redirect", - "mod_rewrite", - ) - server.document-root = "/var/www/wallabag/web" - server.upload-dirs = ( "/var/cache/lighttpd/uploads" ) - server.errorlog = "/var/log/lighttpd/error.log" - server.pid-file = "/var/run/lighttpd.pid" - server.username = "www-data" - server.groupname = "www-data" - server.port = 80 - server.follow-symlink = "enable" - index-file.names = ( "index.php", "index.html", "index.lighttpd.html") - url.access-deny = ( "~", ".inc" ) - static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" ) - compress.cache-dir = "/var/cache/lighttpd/compress/" - compress.filetype = ( "application/javascript", "text/css", "text/html", "text/plain" ) - include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port - include_shell "/usr/share/lighttpd/create-mime.assign.pl" - include_shell "/usr/share/lighttpd/include-conf-enabled.pl" - dir-listing.activate = "disable" - - url.rewrite-if-not-file = ( - "^/([^?])(?:\?(.))?" => "/app.php?$1&$2", - "^/([^?]*)" => "/app.php?=$1", - ) - -Droits d'accès aux dossiers du projet -------------------------------------- - -Environnement de test -~~~~~~~~~~~~~~~~~~~~~ - -Quand nous souhaitons juste tester wallabag, nous lançons simplement la commande ``php bin/console server:run --env=prod`` pour démarrer l'instance wallabag et tout se passe correctement car l'utilisateur qui a démarré le projet a accès naturellement au repertoire courant, tout va bien. - -Environnement de production -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Dès lors que nous utilisons Apache ou Nginx pour accéder à notre instance wallabag, et non plus la commande ``php bin/console server:run --env=prod`` pour la démarrer, il faut prendre garde à octroyer les bons droits aux bons dossiers afin de préserver la sécurité de l'ensemble des fichiers fournis par le projet. - -Aussi, le dossier, connu sous le nom de ``DocumentRoot`` (pour apache) ou ``root`` (pour Nginx), doit être impérativement accessible par l'utilisateur de Apache ou Nginx. Le nom de cet utilisateur est généralement ``www-data``, ``apache`` ou ``nobody`` (selon les systèmes linux utilisés). - -Donc le dossier ``/var/www/wallabag/web`` doit être accessible par ce dernier. Mais cela ne suffira pas si nous nous contentons de ce dossier, et nous pourrions avoir, au mieux une page blanche en accédant à la page d'accueil du projet, au pire une erreur 500. - -Cela est dû au fait qu'il faut aussi octroyer les mêmes droits d'accès au dossier ``/var/www/wallabag/var`` que ceux octroyés au dossier ``/var/www/wallabag/web``. Ainsi, on règle le problème par la commande suivante : - -.. code-block:: bash - - chown -R www-data:www-data /var/www/wallabag/var - -Il en est de même pour les dossiers suivants : - -* /var/www/wallabag/bin/ -* /var/www/wallabag/app/config/ -* /var/www/wallabag/vendor/ - -en tapant - -.. code-block:: bash - - chown -R www-data:www-data /var/www/wallabag/bin - chown -R www-data:www-data /var/www/wallabag/app/config - chown -R www-data:www-data /var/www/wallabag/vendor - -sinon lors de la mise à jour vous finirez par rencontrer les erreurs suivantes : - - -.. code-block:: bash - - Unable to write to the "bin" directory. - file_put_contents(app/config/parameters.yml): failed to open stream: Permission denied - file_put_contents(/.../wallabag/vendor/autoload.php): failed to open stream: Permission denied diff --git a/docs/fr/user/parameters.rst b/docs/fr/user/parameters.rst deleted file mode 100644 index b2e33524..00000000 --- a/docs/fr/user/parameters.rst +++ /dev/null @@ -1,95 +0,0 @@ -À quoi servent les paramètres ? -=============================== - -Fichier `parameters.yml` par défaut ------------------------------------ - -Voici la dernière version par défaut du fichier `app/config/parameters.yml`. Soyez sur que le votre respecte celui-ci. -Si vous ne savez pas quelle valeur vous devez mettre, laissez celle par défaut. - -.. code-block:: yml - - parameters: - database_driver: pdo_sqlite - database_host: 127.0.0.1 - database_port: null - database_name: symfony - database_user: root - database_password: null - database_path: '%kernel.root_dir%/../data/db/wallabag.sqlite' - database_table_prefix: wallabag_ - database_socket: null - mailer_transport: smtp - mailer_host: 127.0.0.1 - mailer_user: null - mailer_password: null - locale: en - secret: ovmpmAWXRCabNlMgzlzFXDYmCFfzGv - twofactor_auth: true - twofactor_sender: no-reply@wallabag.org - fosuser_registration: true - fosuser_confirmation: true - from_email: no-reply@wallabag.org - rss_limit: 50 - rabbitmq_host: localhost - rabbitmq_port: 5672 - rabbitmq_user: guest - rabbitmq_password: guest - redis_scheme: tcp - redis_host: localhost - redis_port: 6379 - redis_path: null - redis_password: null - -Meaning of each parameter -------------------------- - -.. csv-table:: Paramètres de base de données - :header: "name", "default", "description" - - "database_driver", "pdo_sqlite", "Doit être pdo_sqlite ou pdo_mysql ou pdo_pgsql" - "database_host", "127.0.0.1", "Hôte de votre base de données (généralement localhost ou 127.0.0.1)" - "database_port", "~", "Port de votre base de données (vous pouvez laisser ``~`` pour utiliser celui par défaut)" - "database_name", "symfony", "Nom de votre base de données" - "database_user", "root", "Utilisateur de votre base de données" - "database_password", "~", "Mot de passe de cet utilisateur" - "database_path", "``""%kernel.root_dir%/../data/db/wallabag.sqlite""``", "Uniquement pour SQLite. Chemin du fichier de base de données. Laissez vide pour les autres bases de données." - "database_table_prefix", "wallabag_", "Toutes les tables de wallabag seront préfixées par cette chaine. Vous pouvez ajouter un ``_`` pour plus de clarté" - "database_socket", "null", "Si votre base de données utilise un socket plutôt que tcp, spécifiez le chemin du socket (les autres paramètres de connexion seront alors ignorés)" - -.. csv-table:: Configuration pour envoyer des emails depuis wallabag - :header: "name", "default", "description" - - "mailer_transport", "smtp", "Méthode de transport exacte utilisée pour envoyer des emails. Les valeurs correctes sont : smtp, gmail, mail, sendmail, null (ce qui désactivera l'envoi des emails)" - "mailer_host", "127.0.0.1", "Hôte sur lequel se connecter quand on utilise smtp comme transport." - "mailer_user", "~", "Utilisateur smtp." - "mailer_password", "~", "Mot de passe de cet utilisateur." - -.. csv-table:: Autres options de wallabag - :header: "name", "default", "description" - - "locale", "en", "Langue par défaut de votre instance wallabag (comme en, fr, es, etc.)" - "secret", "ovmpmAWXRCabNlMgzlzFXDYmCFfzGv", "C'est une chaine qui doit être unique à votre application et qui est couramment utilisée pour ajouter plus d'entropie aux opérations relatives à la sécurité." - "twofactor_auth", "true", "true pour activer l'authentification à deux facteurs" - "twofactor_sender", "no-reply@wallabag.org", "Email de l'expéditeur du code de l'authentification à deux facteurs" - "fosuser_registration", "true", "true pour activer l'inscription publique" - "fosuser_confirmation", "true", "true pour envoyer un email de confirmation pour chaque création de compte" - "from_email", "no-reply@wallabag.org", "Email de l'expéditeur pour chaque email envoyé" - "rss_limit", "50", "Limite pour les flux RSS" - -.. csv-table:: Configuration RabbitMQ - :header: "name", "default", "description" - - "rabbitmq_host", "localhost", "Hôte de votre instance RabbitMQ" - "rabbitmq_port", "5672", "Port de votre instance RabbitMQ" - "rabbitmq_user", "guest", "Utilisateur de votre instance RabbitMQ" - "rabbitmq_password", "guest", "Mot de passe de cet utilisateur" - -.. csv-table:: Configuration Redis - :header: "name", "default", "description" - - "redis_scheme", "tcp", "Définit le protocole utilisé pour commuiquer avec l'instance Redis. Les valeurs correctes sont : tcp, unix, http" - "redis_host", "localhost", "IP ou hôte du serveur cible (ignoré pour un schéma unix)" - "redis_port", "6379", "Port TCP/IP du serveur cible (ignoré pour un schéma unix)" - "redis_path", "null", "Chemin du fichier de socket du domaine UNIX utilisé quand on se connecte à Redis en utilisant les sockets du domaine UNIX" - "redis_password", "null", "Mot de passe défini dans la configuration serveur de Redis (paramètre `requirepass` dans `redis.conf`)" diff --git a/docs/fr/user/query-upgrade-21-22.rst b/docs/fr/user/query-upgrade-21-22.rst deleted file mode 100644 index fa9835a8..00000000 --- a/docs/fr/user/query-upgrade-21-22.rst +++ /dev/null @@ -1,984 +0,0 @@ -Migration 20161001072726 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_entry_tag DROP FOREIGN KEY FK_C9F0DD7CBA364942 - ALTER TABLE wallabag_entry_tag DROP FOREIGN KEY FK_C9F0DD7CBAD26311 - ALTER TABLE wallabag_entry_tag ADD CONSTRAINT FK_entry_tag_entry FOREIGN KEY (entry_id) REFERENCES wallabag_entry (id) ON DELETE CASCADE - ALTER TABLE wallabag_entry_tag ADD CONSTRAINT FK_entry_tag_tag FOREIGN KEY (tag_id) REFERENCES wallabag_tag (id) ON DELETE CASCADE - ALTER TABLE wallabag_annotation DROP FOREIGN KEY FK_A7AED006BA364942 - ALTER TABLE wallabag_annotation ADD CONSTRAINT FK_annotation_entry FOREIGN KEY (entry_id) REFERENCES wallabag_entry (id) ON DELETE CASCADE - -Migration down -"""""""""""""" - -We didn't write down migration for ``20161001072726``. - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_entry_tag DROP CONSTRAINT fk_c9f0dd7cba364942 - ALTER TABLE wallabag_entry_tag DROP CONSTRAINT fk_c9f0dd7cbad26311 - ALTER TABLE wallabag_entry_tag ADD CONSTRAINT FK_entry_tag_entry FOREIGN KEY (entry_id) REFERENCES wallabag_entry (id) ON DELETE CASCADE - ALTER TABLE wallabag_entry_tag ADD CONSTRAINT FK_entry_tag_tag FOREIGN KEY (tag_id) REFERENCES wallabag_tag (id) ON DELETE CASCADE - ALTER TABLE wallabag_annotation DROP CONSTRAINT fk_a7aed006ba364942 - ALTER TABLE wallabag_annotation ADD CONSTRAINT FK_annotation_entry FOREIGN KEY (entry_id) REFERENCES wallabag_entry (id) ON DELETE CASCADE - -Migration down -"""""""""""""" - -We didn't write down migration for ``20161001072726``. - -SQLite -^^^^^^ - -This migration can only be executed safely on MySQL or PostgreSQL. - -Migration 20161022134138 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER DATABASE wallabag CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; - ALTER TABLE wallabag_user CHANGE confirmation_token confirmation_token VARCHAR(180) DEFAULT NULL; - ALTER TABLE wallabag_user CHANGE salt salt VARCHAR(180) NOT NULL; - ALTER TABLE wallabag_user CHANGE password password VARCHAR(180) NOT NULL; - ALTER TABLE wallabag_annotation CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - ALTER TABLE wallabag_entry CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - ALTER TABLE wallabag_tag CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - ALTER TABLE wallabag_user CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - ALTER TABLE wallabag_annotation CHANGE `text` `text` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - ALTER TABLE wallabag_annotation CHANGE `quote` `quote` VARCHAR(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - ALTER TABLE wallabag_entry CHANGE `title` `title` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - ALTER TABLE wallabag_entry CHANGE `content` `content` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - ALTER TABLE wallabag_tag CHANGE `label` `label` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - ALTER TABLE wallabag_user CHANGE `name` `name` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER DATABASE wallabag CHARACTER SET = utf8 COLLATE = utf8_unicode_ci; - ALTER TABLE wallabag_annotation CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci; - ALTER TABLE wallabag_entry CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci; - ALTER TABLE wallabag_tag CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci; - ALTER TABLE wallabag_user CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci; - ALTER TABLE wallabag_annotation CHANGE `text` `text` longtext CHARACTER SET utf8 COLLATE utf8_unicode_ci; - ALTER TABLE wallabag_annotation CHANGE `quote` `quote` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci; - ALTER TABLE wallabag_entry CHANGE `title` `title` longtext CHARACTER SET utf8 COLLATE utf8_unicode_ci; - ALTER TABLE wallabag_entry CHANGE `content` `content` longtext CHARACTER SET utf8 COLLATE utf8_unicode_ci; - ALTER TABLE wallabag_tag CHANGE `label` `label` longtext CHARACTER SET utf8 COLLATE utf8_unicode_ci; - ALTER TABLE wallabag_user CHANGE `name` `name` longtext CHARACTER SET utf8 COLLATE utf8_unicode_ci; - -PostgreSQL and SQLite -^^^^^^^^^^^^^^^^^^^^^ - -This migration only apply to MySQL. - -Migration 20161024212538 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_oauth2_clients ADD user_id INT NOT NULL - ALTER TABLE wallabag_oauth2_clients ADD CONSTRAINT IDX_user_oauth_client FOREIGN KEY (user_id) REFERENCES wallabag_user (id) ON DELETE CASCADE - CREATE INDEX IDX_635D765EA76ED395 ON wallabag_oauth2_clients (user_id) - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_oauth2_clients DROP FOREIGN KEY IDX_user_oauth_client - ALTER TABLE wallabag_oauth2_clients DROP user_id - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_oauth2_clients ADD user_id INT DEFAULT NULL - ALTER TABLE wallabag_oauth2_clients ADD CONSTRAINT IDX_user_oauth_client FOREIGN KEY (user_id) REFERENCES wallabag_user (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE - CREATE INDEX IDX_635D765EA76ED395 ON wallabag_oauth2_clients (user_id) - - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_oauth2_clients DROP CONSTRAINT idx_user_oauth_client - ALTER TABLE wallabag_oauth2_clients DROP user_id - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - CREATE TEMPORARY TABLE __temp__wallabag_oauth2_clients AS SELECT id, random_id, redirect_uris, secret, allowed_grant_types, name FROM wallabag_oauth2_clients - DROP TABLE wallabag_oauth2_clients - CREATE TABLE wallabag_oauth2_clients (id INTEGER NOT NULL, user_id INTEGER DEFAULT NULL, random_id VARCHAR(255) NOT NULL COLLATE BINARY, redirect_uris CLOB NOT NULL COLLATE BINARY, secret VARCHAR(255) NOT NULL COLLATE BINARY, allowed_grant_types CLOB NOT NULL COLLATE BINARY, name CLOB DEFAULT NULL COLLATE BINARY, PRIMARY KEY(id), CONSTRAINT IDX_user_oauth_client FOREIGN KEY (user_id) REFERENCES wallabag_user (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE) - INSERT INTO wallabag_oauth2_clients (id, random_id, redirect_uris, secret, allowed_grant_types, name) SELECT id, random_id, redirect_uris, secret, allowed_grant_types, name FROM __temp__wallabag_oauth2_clients - DROP TABLE __temp__wallabag_oauth2_clients - CREATE INDEX IDX_635D765EA76ED395 ON wallabag_oauth2_clients (user_id) - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX IDX_635D765EA76ED395 - CREATE TEMPORARY TABLE __temp__wallabag_oauth2_clients AS SELECT id, random_id, redirect_uris, secret, allowed_grant_types, name FROM wallabag_oauth2_clients - DROP TABLE wallabag_oauth2_clients - CREATE TABLE wallabag_oauth2_clients (id INTEGER NOT NULL, random_id VARCHAR(255) NOT NULL COLLATE BINARY, redirect_uris CLOB NOT NULL COLLATE BINARY, secret VARCHAR(255) NOT NULL COLLATE BINARY, allowed_grant_types CLOB NOT NULL COLLATE BINARY, name CLOB DEFAULT NULL COLLATE BINARY, PRIMARY KEY(id)) - INSERT INTO wallabag_oauth2_clients (id, random_id, redirect_uris, secret, allowed_grant_types, name) SELECT id, random_id, redirect_uris, secret, allowed_grant_types, name FROM __temp__wallabag_oauth2_clients - DROP TABLE __temp__wallabag_oauth2_clients - -Migration 20161031132655 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('download_images_enabled', 0, 'misc') - -Migration down -"""""""""""""" - -.. code-block:: sql - - DELETE FROM wallabag_craue_config_setting WHERE name = 'download_images_enabled'; - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('download_images_enabled', 0, 'misc') - -Migration down -"""""""""""""" - -.. code-block:: sql - - DELETE FROM wallabag_craue_config_setting WHERE name = 'download_images_enabled'; - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('download_images_enabled', 0, 'misc') - -Migration down -"""""""""""""" - -.. code-block:: sql - - DELETE FROM wallabag_craue_config_setting WHERE name = 'download_images_enabled'; - -Migration 20161104073720 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - CREATE INDEX IDX_entry_created_at ON wallabag_entry (created_at) - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX IDX_entry_created_at ON wallabag_entry - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - CREATE INDEX IDX_entry_created_at ON wallabag_entry (created_at) - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX idx_entry_created_at - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - DROP INDEX created_at_idx - DROP INDEX IDX_F4D18282A76ED395 - CREATE TEMPORARY TABLE __temp__wallabag_entry AS SELECT id, user_id, uuid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public FROM wallabag_entry - DROP TABLE wallabag_entry - CREATE TABLE wallabag_entry (id INTEGER NOT NULL, user_id INTEGER DEFAULT NULL, uuid CLOB DEFAULT NULL COLLATE BINARY, title CLOB DEFAULT NULL COLLATE BINARY, url CLOB DEFAULT NULL COLLATE BINARY, is_archived BOOLEAN NOT NULL, is_starred BOOLEAN NOT NULL, content CLOB DEFAULT NULL COLLATE BINARY, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, mimetype CLOB DEFAULT NULL COLLATE BINARY, language CLOB DEFAULT NULL COLLATE BINARY, reading_time INTEGER DEFAULT NULL, domain_name CLOB DEFAULT NULL COLLATE BINARY, preview_picture CLOB DEFAULT NULL COLLATE BINARY, is_public BOOLEAN DEFAULT '0', PRIMARY KEY(id)) - INSERT INTO wallabag_entry (id, user_id, uuid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public) SELECT id, user_id, uuid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public FROM __temp__wallabag_entry - DROP TABLE __temp__wallabag_entry - CREATE INDEX created_at_idx ON wallabag_entry (created_at) - CREATE INDEX IDX_F4D18282A76ED395 ON wallabag_entry (user_id) - CREATE INDEX IDX_entry_created_at ON wallabag_entry (created_at) - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX IDX_entry_created_at - DROP INDEX IDX_F4D18282A76ED395 - DROP INDEX created_at_idx - CREATE TEMPORARY TABLE __temp__wallabag_entry AS SELECT id, user_id, uuid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public FROM wallabag_entry - DROP TABLE wallabag_entry - CREATE TABLE wallabag_entry (id INTEGER NOT NULL, user_id INTEGER DEFAULT NULL, uuid CLOB DEFAULT NULL COLLATE BINARY, title CLOB DEFAULT NULL COLLATE BINARY, url CLOB DEFAULT NULL COLLATE BINARY, is_archived BOOLEAN NOT NULL, is_starred BOOLEAN NOT NULL, content CLOB DEFAULT NULL COLLATE BINARY, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, mimetype CLOB DEFAULT NULL COLLATE BINARY, language CLOB DEFAULT NULL COLLATE BINARY, reading_time INTEGER DEFAULT NULL, domain_name CLOB DEFAULT NULL COLLATE BINARY, preview_picture CLOB DEFAULT NULL COLLATE BINARY, is_public BOOLEAN DEFAULT '0', PRIMARY KEY(id)) - INSERT INTO wallabag_entry (id, user_id, uuid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public) SELECT id, user_id, uuid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public FROM __temp__wallabag_entry - DROP TABLE __temp__wallabag_entry - CREATE INDEX IDX_F4D18282A76ED395 ON wallabag_entry (user_id) - CREATE INDEX created_at_idx ON wallabag_entry (created_at) - -Migration 20161106113822 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_config ADD action_mark_as_read INT DEFAULT 0 - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_config DROP action_mark_as_read - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_config ADD action_mark_as_read INT DEFAULT 0 - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_config DROP action_mark_as_read - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_config ADD COLUMN action_mark_as_read INTEGER DEFAULT 0 - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX UNIQ_87E64C53A76ED395 - CREATE TEMPORARY TABLE __temp__wallabag_config AS SELECT id, user_id, theme, items_per_page, language, rss_token, rss_limit, reading_speed, pocket_consumer_key FROM wallabag_config - DROP TABLE wallabag_config - CREATE TABLE wallabag_config (id INTEGER NOT NULL, user_id INTEGER DEFAULT NULL, theme VARCHAR(255) NOT NULL COLLATE BINARY, items_per_page INTEGER NOT NULL, language VARCHAR(255) NOT NULL COLLATE BINARY, rss_token VARCHAR(255) DEFAULT NULL COLLATE BINARY, rss_limit INTEGER DEFAULT NULL, reading_speed DOUBLE PRECISION DEFAULT NULL, pocket_consumer_key VARCHAR(255) DEFAULT NULL COLLATE BINARY, PRIMARY KEY(id)) - INSERT INTO wallabag_config (id, user_id, theme, items_per_page, language, rss_token, rss_limit, reading_speed, pocket_consumer_key) SELECT id, user_id, theme, items_per_page, language, rss_token, rss_limit, reading_speed, pocket_consumer_key FROM __temp__wallabag_config - DROP TABLE __temp__wallabag_config - CREATE UNIQUE INDEX UNIQ_87E64C53A76ED395 ON wallabag_config (user_id) - -Migration 20161117071626 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('share_unmark', 0, 'entry') - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('unmark_url', 'https://unmark.it', 'entry') - -Migration down -"""""""""""""" - -.. code-block:: sql - - DELETE FROM wallabag_craue_config_setting WHERE name = 'share_unmark'; - DELETE FROM wallabag_craue_config_setting WHERE name = 'unmark_url'; - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('share_unmark', 0, 'entry') - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('unmark_url', 'https://unmark.it', 'entry') - -Migration down -"""""""""""""" - -.. code-block:: sql - - DELETE FROM wallabag_craue_config_setting WHERE name = 'share_unmark'; - DELETE FROM wallabag_craue_config_setting WHERE name = 'unmark_url'; - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('share_unmark', 0, 'entry') - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('unmark_url', 'https://unmark.it', 'entry') - -Migration down -"""""""""""""" - -.. code-block:: sql - - DELETE FROM wallabag_craue_config_setting WHERE name = 'share_unmark'; - DELETE FROM wallabag_craue_config_setting WHERE name = 'unmark_url'; - -Migration 20161118134328 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_entry ADD http_status VARCHAR(3) DEFAULT NULL - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_entry DROP http_status - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_entry ADD http_status VARCHAR(3) DEFAULT NULL - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_entry DROP http_status - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_entry ADD COLUMN http_status VARCHAR(3) DEFAULT NULL - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX created_at_idx - DROP INDEX IDX_F4D18282A76ED395 - CREATE TEMPORARY TABLE __temp__wallabag_entry AS SELECT id, user_id, uuid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public FROM wallabag_entry - DROP TABLE wallabag_entry - CREATE TABLE wallabag_entry (id INTEGER NOT NULL, user_id INTEGER DEFAULT NULL, uuid CLOB DEFAULT NULL COLLATE BINARY, title CLOB DEFAULT NULL COLLATE BINARY, url CLOB DEFAULT NULL COLLATE BINARY, is_archived BOOLEAN NOT NULL, is_starred BOOLEAN NOT NULL, content CLOB DEFAULT NULL COLLATE BINARY, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, mimetype CLOB DEFAULT NULL COLLATE BINARY, language CLOB DEFAULT NULL COLLATE BINARY, reading_time INTEGER DEFAULT NULL, domain_name CLOB DEFAULT NULL COLLATE BINARY, preview_picture CLOB DEFAULT NULL COLLATE BINARY, is_public BOOLEAN DEFAULT '0', PRIMARY KEY(id)) - INSERT INTO wallabag_entry (id, user_id, uuid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public) SELECT id, user_id, uuid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public FROM __temp__wallabag_entry - DROP TABLE __temp__wallabag_entry - CREATE INDEX created_at_idx ON wallabag_entry (created_at) - CREATE INDEX IDX_F4D18282A76ED395 ON wallabag_entry (user_id) - -Migration 20161122144743 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('restricted_access', 0, 'entry') - -Migration down -"""""""""""""" - -.. code-block:: sql - - DELETE FROM wallabag_craue_config_setting WHERE name = 'restricted_access'; - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('restricted_access', 0, 'entry') - -Migration down -"""""""""""""" - -.. code-block:: sql - - DELETE FROM wallabag_craue_config_setting WHERE name = 'restricted_access'; - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - INSERT INTO wallabag_craue_config_setting (name, value, section) VALUES ('restricted_access', 0, 'entry') - -Migration down -"""""""""""""" - -.. code-block:: sql - - DELETE FROM wallabag_craue_config_setting WHERE name = 'restricted_access'; - -Migration 20161122203647 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_user DROP expired, DROP credentials_expired - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_user ADD expired SMALLINT DEFAULT NULL, ADD credentials_expired SMALLINT DEFAULT NULL - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_user DROP expired - ALTER TABLE wallabag_user DROP credentials_expired - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_user ADD expired SMALLINT DEFAULT NULL - ALTER TABLE wallabag_user ADD credentials_expired SMALLINT DEFAULT NULL - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - DROP INDEX UNIQ_1D63E7E5C05FB297 - DROP INDEX UNIQ_1D63E7E5A0D96FBF - DROP INDEX UNIQ_1D63E7E592FC23A8 - CREATE TEMPORARY TABLE __temp__wallabag_user AS SELECT id, username, username_canonical, email, email_canonical, enabled, salt, password, last_login, locked, expires_at, confirmation_token, password_requested_at, roles, credentials_expire_at, name, created_at, updated_at, authCode, twoFactorAuthentication, trusted FROM wallabag_user - DROP TABLE wallabag_user - CREATE TABLE wallabag_user (id INTEGER NOT NULL, username VARCHAR(180) NOT NULL COLLATE BINARY, username_canonical VARCHAR(180) NOT NULL COLLATE BINARY, email VARCHAR(180) NOT NULL COLLATE BINARY, email_canonical VARCHAR(180) NOT NULL COLLATE BINARY, enabled BOOLEAN NOT NULL, salt VARCHAR(255) NOT NULL COLLATE BINARY, password VARCHAR(255) NOT NULL COLLATE BINARY, last_login DATETIME DEFAULT NULL, locked BOOLEAN NOT NULL, expires_at DATETIME DEFAULT NULL, confirmation_token VARCHAR(180) DEFAULT NULL COLLATE BINARY, password_requested_at DATETIME DEFAULT NULL, roles CLOB NOT NULL COLLATE BINARY, credentials_expire_at DATETIME DEFAULT NULL, name CLOB DEFAULT NULL COLLATE BINARY, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, authCode INTEGER DEFAULT NULL, twoFactorAuthentication BOOLEAN NOT NULL, trusted CLOB DEFAULT NULL COLLATE BINARY, PRIMARY KEY(id)) - INSERT INTO wallabag_user (id, username, username_canonical, email, email_canonical, enabled, salt, password, last_login, locked, expires_at, confirmation_token, password_requested_at, roles, credentials_expire_at, name, created_at, updated_at, authCode, twoFactorAuthentication, trusted) SELECT id, username, username_canonical, email, email_canonical, enabled, salt, password, last_login, locked, expires_at, confirmation_token, password_requested_at, roles, credentials_expire_at, name, created_at, updated_at, authCode, twoFactorAuthentication, trusted FROM __temp__wallabag_user - DROP TABLE __temp__wallabag_user - CREATE UNIQUE INDEX UNIQ_1D63E7E5C05FB297 ON wallabag_user (confirmation_token) - CREATE UNIQUE INDEX UNIQ_1D63E7E5A0D96FBF ON wallabag_user (email_canonical) - CREATE UNIQUE INDEX UNIQ_1D63E7E592FC23A8 ON wallabag_user (username_canonical) - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_user ADD COLUMN expired SMALLINT DEFAULT NULL - ALTER TABLE wallabag_user ADD COLUMN credentials_expired SMALLINT DEFAULT NULL - -Migration 20161128084725 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_config ADD list_mode INT DEFAULT NULL - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_config DROP list_mode - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_config ADD list_mode INT DEFAULT NULL - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_config DROP list_mode - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_config ADD COLUMN list_mode INTEGER DEFAULT NULL - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX UNIQ_87E64C53A76ED395 - CREATE TEMPORARY TABLE __temp__wallabag_config AS SELECT id, user_id, theme, items_per_page, language, rss_token, rss_limit, reading_speed, pocket_consumer_key FROM wallabag_config - DROP TABLE wallabag_config - CREATE TABLE wallabag_config (id INTEGER NOT NULL, user_id INTEGER DEFAULT NULL, theme VARCHAR(255) NOT NULL COLLATE BINARY, items_per_page INTEGER NOT NULL, language VARCHAR(255) NOT NULL COLLATE BINARY, rss_token VARCHAR(255) DEFAULT NULL COLLATE BINARY, rss_limit INTEGER DEFAULT NULL, reading_speed DOUBLE PRECISION DEFAULT NULL, pocket_consumer_key VARCHAR(255) DEFAULT NULL COLLATE BINARY, PRIMARY KEY(id)) - INSERT INTO wallabag_config (id, user_id, theme, items_per_page, language, rss_token, rss_limit, reading_speed, pocket_consumer_key) SELECT id, user_id, theme, items_per_page, language, rss_token, rss_limit, reading_speed, pocket_consumer_key FROM __temp__wallabag_config - DROP TABLE __temp__wallabag_config - CREATE UNIQUE INDEX UNIQ_87E64C53A76ED395 ON wallabag_config (user_id) - -Migration 20161128131503 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_user DROP locked, DROP credentials_expire_at, DROP expires_at - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_user ADD locked SMALLINT DEFAULT NULL, ADD credentials_expire_at DATETIME DEFAULT NULL, ADD expires_at DATETIME DEFAULT NULL - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_user DROP locked - ALTER TABLE wallabag_user DROP credentials_expire_at - ALTER TABLE wallabag_user DROP expires_at - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_user ADD locked SMALLINT DEFAULT NULL - ALTER TABLE wallabag_user ADD credentials_expire_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL - ALTER TABLE wallabag_user ADD expires_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_user ADD COLUMN locked SMALLINT DEFAULT NULL - ALTER TABLE wallabag_user ADD COLUMN credentials_expire_at DATETIME DEFAULT NULL - ALTER TABLE wallabag_user ADD COLUMN expires_at DATETIME DEFAULT NULL - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX UNIQ_1D63E7E592FC23A8 - DROP INDEX UNIQ_1D63E7E5A0D96FBF - DROP INDEX UNIQ_1D63E7E5C05FB297 - CREATE TEMPORARY TABLE __temp__wallabag_user AS SELECT id, username, username_canonical, email, email_canonical, enabled, salt, password, last_login, confirmation_token, password_requested_at, roles, name, created_at, updated_at, authCode, twoFactorAuthentication, trusted, expired, credentials_expired FROM wallabag_user - DROP TABLE wallabag_user - CREATE TABLE wallabag_user (id INTEGER NOT NULL, username VARCHAR(180) NOT NULL COLLATE BINARY, username_canonical VARCHAR(180) NOT NULL COLLATE BINARY, email VARCHAR(180) NOT NULL COLLATE BINARY, email_canonical VARCHAR(180) NOT NULL COLLATE BINARY, enabled BOOLEAN NOT NULL, salt VARCHAR(255) NOT NULL COLLATE BINARY, password VARCHAR(255) NOT NULL COLLATE BINARY, last_login DATETIME DEFAULT NULL, confirmation_token VARCHAR(180) DEFAULT NULL COLLATE BINARY, password_requested_at DATETIME DEFAULT NULL, roles CLOB NOT NULL COLLATE BINARY, name CLOB DEFAULT NULL COLLATE BINARY, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, authCode INTEGER DEFAULT NULL, twoFactorAuthentication BOOLEAN NOT NULL, trusted CLOB DEFAULT NULL COLLATE BINARY, expired SMALLINT DEFAULT NULL, credentials_expired SMALLINT DEFAULT NULL, PRIMARY KEY(id)) - INSERT INTO wallabag_user (id, username, username_canonical, email, email_canonical, enabled, salt, password, last_login, confirmation_token, password_requested_at, roles, name, created_at, updated_at, authCode, twoFactorAuthentication, trusted, expired, credentials_expired) SELECT id, username, username_canonical, email, email_canonical, enabled, salt, password, last_login, confirmation_token, password_requested_at, roles, name, created_at, updated_at, authCode, twoFactorAuthentication, trusted, expired, credentials_expired FROM __temp__wallabag_user - DROP TABLE __temp__wallabag_user - CREATE UNIQUE INDEX UNIQ_1D63E7E592FC23A8 ON wallabag_user (username_canonical) - CREATE UNIQUE INDEX UNIQ_1D63E7E5A0D96FBF ON wallabag_user (email_canonical) - CREATE UNIQUE INDEX UNIQ_1D63E7E5C05FB297 ON wallabag_user (confirmation_token) - -Migration 20161214094402 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_entry CHANGE uuid uid VARCHAR(23) - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_entry CHANGE uid uuid VARCHAR(23) - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_entry RENAME uuid TO uid - -Migration down -"""""""""""""" - -.. code-block:: sql - - ALTER TABLE wallabag_entry RENAME uid TO uuid - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - CREATE TABLE __temp__wallabag_entry ( - id INTEGER NOT NULL, - user_id INTEGER DEFAULT NULL, - uid VARCHAR(23) DEFAULT NULL, - title CLOB DEFAULT NULL, - url CLOB DEFAULT NULL, - is_archived BOOLEAN NOT NULL, - is_starred BOOLEAN NOT NULL, - content CLOB DEFAULT NULL, - created_at DATETIME NOT NULL, - updated_at DATETIME NOT NULL, - mimetype CLOB DEFAULT NULL, - language CLOB DEFAULT NULL, - reading_time INTEGER DEFAULT NULL, - domain_name CLOB DEFAULT NULL, - preview_picture CLOB DEFAULT NULL, - is_public BOOLEAN DEFAULT '0', - http_status VARCHAR(3) DEFAULT NULL, - PRIMARY KEY(id) - ); - INSERT INTO __temp__wallabag_entry SELECT id,user_id,uuid,title,url,is_archived,is_starred,content,created_at,updated_at,mimetype,language,reading_time,domain_name,preview_picture,is_public,http_status FROM wallabag_entry; - DROP TABLE wallabag_entry; - ALTER TABLE __temp__wallabag_entry RENAME TO wallabag_entry - CREATE INDEX uid ON wallabag_entry (uid) - CREATE INDEX created_at ON wallabag_entry (created_at) - CREATE INDEX IDX_F4D18282A76ED395 ON wallabag_entry (user_id) - - -Migration down -"""""""""""""" - -.. code-block:: sql - - CREATE TABLE __temp__wallabag_entry ( - id INTEGER NOT NULL, - user_id INTEGER DEFAULT NULL, - uuid VARCHAR(23) DEFAULT NULL, - title CLOB DEFAULT NULL, - url CLOB DEFAULT NULL, - is_archived BOOLEAN NOT NULL, - is_starred BOOLEAN NOT NULL, - content CLOB DEFAULT NULL, - created_at DATETIME NOT NULL, - updated_at DATETIME NOT NULL, - mimetype CLOB DEFAULT NULL, - language CLOB DEFAULT NULL, - reading_time INTEGER DEFAULT NULL, - domain_name CLOB DEFAULT NULL, - preview_picture CLOB DEFAULT NULL, - is_public BOOLEAN DEFAULT '0', - http_status VARCHAR(3) DEFAULT NULL, - PRIMARY KEY(id) - ); - INSERT INTO __temp__wallabag_entry SELECT id,user_id,uid,title,url,is_archived,is_starred,content,created_at,updated_at,mimetype,language,reading_time,domain_name,preview_picture,is_public,http_status FROM wallabag_entry; - DROP TABLE wallabag_entry; - ALTER TABLE __temp__wallabag_entry RENAME TO wallabag_entry - CREATE INDEX uid ON wallabag_entry (uid) - CREATE INDEX created_at ON wallabag_entry (created_at) - CREATE INDEX IDX_F4D18282A76ED395 ON wallabag_entry (user_id) - -Migration 20161214094403 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - CREATE INDEX IDX_entry_uid ON wallabag_entry (uid) - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX IDX_entry_uid ON wallabag_entry - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - CREATE INDEX IDX_entry_uid ON wallabag_entry (uid) - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX idx_entry_uid - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - DROP INDEX IDX_F4D18282A76ED395 - DROP INDEX created_at_idx - CREATE TEMPORARY TABLE __temp__wallabag_entry AS SELECT id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public FROM wallabag_entry - DROP TABLE wallabag_entry - CREATE TABLE wallabag_entry (id INTEGER NOT NULL, user_id INTEGER DEFAULT NULL, uid CLOB DEFAULT NULL COLLATE BINARY, title CLOB DEFAULT NULL COLLATE BINARY, url CLOB DEFAULT NULL COLLATE BINARY, is_archived BOOLEAN NOT NULL, is_starred BOOLEAN NOT NULL, content CLOB DEFAULT NULL COLLATE BINARY, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, mimetype CLOB DEFAULT NULL COLLATE BINARY, language CLOB DEFAULT NULL COLLATE BINARY, reading_time INTEGER DEFAULT NULL, domain_name CLOB DEFAULT NULL COLLATE BINARY, preview_picture CLOB DEFAULT NULL COLLATE BINARY, is_public BOOLEAN DEFAULT '0', PRIMARY KEY(id)) - INSERT INTO wallabag_entry (id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public) SELECT id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public FROM __temp__wallabag_entry - DROP TABLE __temp__wallabag_entry - CREATE INDEX IDX_F4D18282A76ED395 ON wallabag_entry (user_id) - CREATE INDEX created_at_idx ON wallabag_entry (created_at) - CREATE INDEX IDX_entry_uid ON wallabag_entry (uid) - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX IDX_entry_uid - DROP INDEX created_at_idx - DROP INDEX IDX_F4D18282A76ED395 - CREATE TEMPORARY TABLE __temp__wallabag_entry AS SELECT id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public FROM wallabag_entry - DROP TABLE wallabag_entry - CREATE TABLE wallabag_entry (id INTEGER NOT NULL, user_id INTEGER DEFAULT NULL, uid CLOB DEFAULT NULL COLLATE BINARY, title CLOB DEFAULT NULL COLLATE BINARY, url CLOB DEFAULT NULL COLLATE BINARY, is_archived BOOLEAN NOT NULL, is_starred BOOLEAN NOT NULL, content CLOB DEFAULT NULL COLLATE BINARY, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, mimetype CLOB DEFAULT NULL COLLATE BINARY, language CLOB DEFAULT NULL COLLATE BINARY, reading_time INTEGER DEFAULT NULL, domain_name CLOB DEFAULT NULL COLLATE BINARY, preview_picture CLOB DEFAULT NULL COLLATE BINARY, is_public BOOLEAN DEFAULT '0', PRIMARY KEY(id)) - INSERT INTO wallabag_entry (id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public) SELECT id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public FROM __temp__wallabag_entry - DROP TABLE __temp__wallabag_entry - CREATE INDEX created_at_idx ON wallabag_entry (created_at) - CREATE INDEX IDX_F4D18282A76ED395 ON wallabag_entry (user_id) - -Migration 20170127093841 ------------------------- - -MySQL -^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - CREATE INDEX IDX_entry_starred ON wallabag_entry (is_starred) - CREATE INDEX IDX_entry_archived ON wallabag_entry (is_archived) - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX IDX_entry_starred ON wallabag_entry - DROP INDEX IDX_entry_archived ON wallabag_entry - -PostgreSQL -^^^^^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - CREATE INDEX IDX_entry_starred ON wallabag_entry (is_starred) - CREATE INDEX IDX_entry_archived ON wallabag_entry (is_archived) - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX IDX_entry_starred - DROP INDEX IDX_entry_archived - -SQLite -^^^^^^ - -Migration up -"""""""""""" - -.. code-block:: sql - - DROP INDEX uid - DROP INDEX created_at - DROP INDEX IDX_F4D18282A76ED395 - CREATE TEMPORARY TABLE __temp__wallabag_entry AS SELECT id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public, http_status FROM wallabag_entry - DROP TABLE wallabag_entry - CREATE TABLE wallabag_entry (id INTEGER NOT NULL, user_id INTEGER DEFAULT NULL, uid VARCHAR(23) DEFAULT NULL COLLATE BINARY, title CLOB DEFAULT NULL COLLATE BINARY, url CLOB DEFAULT NULL COLLATE BINARY, is_archived BOOLEAN NOT NULL, is_starred BOOLEAN NOT NULL, content CLOB DEFAULT NULL COLLATE BINARY, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, mimetype CLOB DEFAULT NULL COLLATE BINARY, language CLOB DEFAULT NULL COLLATE BINARY, reading_time INTEGER DEFAULT NULL, domain_name CLOB DEFAULT NULL COLLATE BINARY, preview_picture CLOB DEFAULT NULL COLLATE BINARY, is_public BOOLEAN DEFAULT '0', http_status VARCHAR(3) DEFAULT NULL COLLATE BINARY, PRIMARY KEY(id)) - INSERT INTO wallabag_entry (id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public, http_status) SELECT id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public, http_status FROM __temp__wallabag_entry - DROP TABLE __temp__wallabag_entry - CREATE INDEX uid ON wallabag_entry (uid) - CREATE INDEX created_at ON wallabag_entry (created_at) - CREATE INDEX IDX_F4D18282A76ED395 ON wallabag_entry (user_id) - CREATE INDEX IDX_entry_starred ON wallabag_entry (is_starred) - CREATE INDEX IDX_entry_archived ON wallabag_entry (is_archived) - -Migration down -"""""""""""""" - -.. code-block:: sql - - DROP INDEX IDX_entry_archived - DROP INDEX IDX_entry_starred - DROP INDEX IDX_F4D18282A76ED395 - DROP INDEX created_at - DROP INDEX uid - CREATE TEMPORARY TABLE __temp__wallabag_entry AS SELECT id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public, http_status FROM wallabag_entry - DROP TABLE wallabag_entry - CREATE TABLE wallabag_entry (id INTEGER NOT NULL, user_id INTEGER DEFAULT NULL, uid VARCHAR(23) DEFAULT NULL COLLATE BINARY, title CLOB DEFAULT NULL COLLATE BINARY, url CLOB DEFAULT NULL COLLATE BINARY, is_archived BOOLEAN NOT NULL, is_starred BOOLEAN NOT NULL, content CLOB DEFAULT NULL COLLATE BINARY, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL, mimetype CLOB DEFAULT NULL COLLATE BINARY, language CLOB DEFAULT NULL COLLATE BINARY, reading_time INTEGER DEFAULT NULL, domain_name CLOB DEFAULT NULL COLLATE BINARY, preview_picture CLOB DEFAULT NULL COLLATE BINARY, is_public BOOLEAN DEFAULT '0', http_status VARCHAR(3) DEFAULT NULL COLLATE BINARY, PRIMARY KEY(id)) - INSERT INTO wallabag_entry (id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public, http_status) SELECT id, user_id, uid, title, url, is_archived, is_starred, content, created_at, updated_at, mimetype, language, reading_time, domain_name, preview_picture, is_public, http_status FROM __temp__wallabag_entry - DROP TABLE __temp__wallabag_entry - CREATE INDEX IDX_F4D18282A76ED395 ON wallabag_entry (user_id) - CREATE INDEX created_at ON wallabag_entry (created_at) - CREATE INDEX uid ON wallabag_entry (uid) diff --git a/docs/fr/user/tags.rst b/docs/fr/user/tags.rst deleted file mode 100644 index 8ddc0f40..00000000 --- a/docs/fr/user/tags.rst +++ /dev/null @@ -1,2 +0,0 @@ -Tags -==== \ No newline at end of file diff --git a/docs/fr/user/upgrade.rst b/docs/fr/user/upgrade.rst deleted file mode 100644 index af97ebb2..00000000 --- a/docs/fr/user/upgrade.rst +++ /dev/null @@ -1,140 +0,0 @@ -Mettre à jour votre installation de wallabag -============================================ - -Vous trouverez ici différentes manières de mettre à jour wallabag : - -- `de la 2.1.x à la 2.2.x <#mettre-a-jour-de-la-2-1-x-a-la-2-2-x>`_ -- `de la 2.0.x à la 2.1.1 <#mettre-a-jour-de-la-2-0-x-a-la-2-1-1>`_ -- `de la 1.x à la 2.x <#depuis-wallabag-1-x>`_ - -Mettre à jour de la 2.1.x à la 2.2.x ------------------------------------- - -Mise à jour sur un serveur dédié -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -**Depuis 2.1.x:** - -:: - - make update - php bin/console doctrine:migrations:migrate --no-interaction -e=prod - -**Depuis 2.2.0:** - -:: - - make update - -Explications à propos des migrations de base de données -""""""""""""""""""""""""""""""""""""""""""""""""""""""" - -Durant la mise à jour, nous exécutons des migrations de base de données. - -Toutes les migrations de base de données sont stockées dans le dossier ``app/DoctrineMigrations``. Vous pouvez exécuter chaque migration individuellement : -``bin/console doctrine:migrations:execute 20161001072726 --env=prod``. - -Voici la liste des migrations de la 2.1.x à la 2.2.0 : - -* ``20161001072726``: ajout de clés étrangères pour la réinitialisation de compte -* ``20161022134138``: conversion de la base de données à l'encodage ``utf8mb4`` (pour MySQL uniquement) -* ``20161024212538``: ajout de la colonne ``user_id`` sur la table ``oauth2_clients`` pour empêcher les utilisateurs de supprimer des clients API d'autres utilisateurs -* ``20161031132655``: ajout du paramètre interne pour activer/désactiver le téléchargement des images -* ``20161104073720``: ajout de l'index ``created_at`` sur la table ``entry`` -* ``20161106113822``: ajout du champ ``action_mark_as_read`` sur la table ``config`` -* ``20161117071626``: ajout du paramètre interne pour partager ses articles vers unmark.it -* ``20161118134328``: ajout du champ ``http_status`` sur la table ``entry`` -* ``20161122144743``: ajout du paramètre interne pour activer/désactiver la récupération d'articles derrière un paywall -* ``20161122203647``: suppression des champs ``expired`` et ``credentials_expired`` sur la table ``user`` -* ``20161128084725``: ajout du champ ``list_mode`` sur la table ``config`` -* ``20161128131503``: suppression des champs ``locked``, ``credentials_expire_at`` et ``expires_at`` sur la table ``user`` -* ``20161214094402``: renommage du champ ``uuid`` en ``uid`` sur la table ``entry`` -* ``20161214094403``: ajout de l'index ``uid`` sur la table ``entry`` -* ``20170127093841``: ajout des index ``is_starred`` et ``is_archived`` sur la table ``entry`` - -Mise à jour sur un hébergement mutualisé -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Effectuez une sauvegarde du fichier ``app/config/parameters.yml``. - -Téléchargez la dernière version de wallabag : - -.. code-block:: bash - - wget https://wllbg.org/latest-v2-package && tar xvf latest-v2-package - -Vous trouverez `le hash md5 du dernier package sur notre site `_. - -Décompressez l'archive dans votre répertoire d'installation et remplacez le fichier ``app/config/parameters.yml`` avec le votre. - -Merci de vérifier que votre fichier ``app/config/parameters.yml`` contient tous les paramètres requis. Vous trouverez `ici une documentation détaillée concernant les paramètres `_. - -Si vous utilisez SQLite, vous devez également conserver le contenu du répertoire ``data/``. - -Videz le répertoire ``var/cache``. - -Vous allez devoir également exécuter des requêtes SQL pour mettre à jour votre base de données. Nous partons du principe que le préfixe de vos tables est ``wallabag_``. - -`Vous trouverez toutes les requêtes à exécuter ici `_. - - -Mettre à jour de la 2.0.x à la 2.1.1 ------------------------------------- - -.. warning:: -Avant cette migration, si vous aviez configuré l'import depuis Pocket en ajoutant votre consumer key dans les paramètres internes, pensez à effectuer une sauvegarde de celle-ci : vous devrez l'ajouter dans la configuration de wallabag après la mise à jour. - -Mise à jour sur un serveur dédié -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -:: - - rm -rf var/cache/* - git fetch origin - git fetch --tags - git checkout 2.1.1 --force - SYMFONY_ENV=prod composer install --no-dev -o --prefer-dist - php bin/console doctrine:migrations:migrate --env=prod - php bin/console cache:clear --env=prod - -Mise à jour sur un hébergement mutualisé -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Effectuez une sauvegarde du fichier ``app/config/parameters.yml``. - -Téléchargez la version 2.1.1 de wallabag : - -.. code-block:: bash - - wget http://framabag.org/wallabag-release-2.1.1.tar.gz && tar xvf wallabag-release-2.1.1.tar.gz - -(hash md5 de l'archive 2.1.1 : ``9584a3b60a2b2a4de87f536548caac93``) - -Décompressez l'archive dans votre répertoire d'installation et remplacez le fichier ``app/config/parameters.yml`` avec le votre. - -Merci de vérifier que votre fichier ``app/config/parameters.yml`` contient tous les paramètres requis. Vous trouverez `ici une documentation détaillée concernant les paramètres `_. - -Si vous utilisez SQLite, vous devez également conserver le contenu du répertoire ``data/``. - -Videz le répertoire ``var/cache``. - -Vous allez devoir également exécuter des requêtes SQL pour mettre à jour votre base de données. Nous partons du principe que le préfixe de vos tables est ``wallabag_`` et que le serveur SQL est un serveur MySQL : - -.. code-block:: sql - - ALTER TABLE `wallabag_entry` ADD `uuid` LONGTEXT DEFAULT NULL; - INSERT INTO `wallabag_craue_config_setting` (`name`, `value`, `section`) VALUES ('share_public', '1', 'entry'); - ALTER TABLE `wallabag_oauth2_clients` ADD name longtext COLLATE 'utf8_unicode_ci' DEFAULT NULL; - INSERT INTO `wallabag_craue_config_setting` (`name`, `value`, `section`) VALUES ('import_with_redis', '0', 'import'); - INSERT INTO `wallabag_craue_config_setting` (`name`, `value`, `section`) VALUES ('import_with_rabbitmq', '0', 'import'); - ALTER TABLE `wallabag_config` ADD `pocket_consumer_key` VARCHAR(255) DEFAULT NULL; - DELETE FROM `wallabag_craue_config_setting` WHERE `name` = 'pocket_consumer_key'; - -Depuis wallabag 1.x -------------------- - -Il n'y a pas de script automatique pour mettre à jour wallabag 1.x en wallabag 2.x. Vous devez : - -- exportez vos données -- installer wallabag 2.x (`lisez la documentation d'installation `_ ) -- importer vos données dans votre installation toute propre (`lisez la documentation d'import `_ ) diff --git a/docs/img/user/2FA_form.png b/docs/img/user/2FA_form.png deleted file mode 100644 index 69b4697d..00000000 Binary files a/docs/img/user/2FA_form.png and /dev/null differ diff --git a/docs/img/user/activated_account.png b/docs/img/user/activated_account.png deleted file mode 100644 index d1ecc3cc..00000000 Binary files a/docs/img/user/activated_account.png and /dev/null differ diff --git a/docs/img/user/android_configuration_connection_test.de.png b/docs/img/user/android_configuration_connection_test.de.png deleted file mode 100644 index 63394f87..00000000 Binary files a/docs/img/user/android_configuration_connection_test.de.png and /dev/null differ diff --git a/docs/img/user/android_configuration_connection_test.en.png b/docs/img/user/android_configuration_connection_test.en.png deleted file mode 100644 index 479cd09a..00000000 Binary files a/docs/img/user/android_configuration_connection_test.en.png and /dev/null differ diff --git a/docs/img/user/android_configuration_connection_test_success.de.png b/docs/img/user/android_configuration_connection_test_success.de.png deleted file mode 100644 index f5c1971e..00000000 Binary files a/docs/img/user/android_configuration_connection_test_success.de.png and /dev/null differ diff --git a/docs/img/user/android_configuration_connection_test_success.en.png b/docs/img/user/android_configuration_connection_test_success.en.png deleted file mode 100644 index 840f00cd..00000000 Binary files a/docs/img/user/android_configuration_connection_test_success.en.png and /dev/null differ diff --git a/docs/img/user/android_configuration_feed_credentials_automatically_filled_in.de.png b/docs/img/user/android_configuration_feed_credentials_automatically_filled_in.de.png deleted file mode 100644 index a191a6ee..00000000 Binary files a/docs/img/user/android_configuration_feed_credentials_automatically_filled_in.de.png and /dev/null differ diff --git a/docs/img/user/android_configuration_feed_credentials_automatically_filled_in.en.png b/docs/img/user/android_configuration_feed_credentials_automatically_filled_in.en.png deleted file mode 100644 index df071b8b..00000000 Binary files a/docs/img/user/android_configuration_feed_credentials_automatically_filled_in.en.png and /dev/null differ diff --git a/docs/img/user/android_configuration_filled_in.de.png b/docs/img/user/android_configuration_filled_in.de.png deleted file mode 100644 index 4cd8d709..00000000 Binary files a/docs/img/user/android_configuration_filled_in.de.png and /dev/null differ diff --git a/docs/img/user/android_configuration_filled_in.en.png b/docs/img/user/android_configuration_filled_in.en.png deleted file mode 100644 index 368ac515..00000000 Binary files a/docs/img/user/android_configuration_filled_in.en.png and /dev/null differ diff --git a/docs/img/user/android_configuration_get_feed_credentials.de.png b/docs/img/user/android_configuration_get_feed_credentials.de.png deleted file mode 100644 index 1c401cba..00000000 Binary files a/docs/img/user/android_configuration_get_feed_credentials.de.png and /dev/null differ diff --git a/docs/img/user/android_configuration_get_feed_credentials.en.png b/docs/img/user/android_configuration_get_feed_credentials.en.png deleted file mode 100644 index 2a8958ff..00000000 Binary files a/docs/img/user/android_configuration_get_feed_credentials.en.png and /dev/null differ diff --git a/docs/img/user/android_configuration_saved_feed_update.de.png b/docs/img/user/android_configuration_saved_feed_update.de.png deleted file mode 100644 index 2af41025..00000000 Binary files a/docs/img/user/android_configuration_saved_feed_update.de.png and /dev/null differ diff --git a/docs/img/user/android_configuration_saved_feed_update.en.png b/docs/img/user/android_configuration_saved_feed_update.en.png deleted file mode 100644 index f1c06f17..00000000 Binary files a/docs/img/user/android_configuration_saved_feed_update.en.png and /dev/null differ diff --git a/docs/img/user/android_configuration_screen.de.png b/docs/img/user/android_configuration_screen.de.png deleted file mode 100644 index 7bf72182..00000000 Binary files a/docs/img/user/android_configuration_screen.de.png and /dev/null differ diff --git a/docs/img/user/android_configuration_screen.en.png b/docs/img/user/android_configuration_screen.en.png deleted file mode 100644 index 945fd7c8..00000000 Binary files a/docs/img/user/android_configuration_screen.en.png and /dev/null differ diff --git a/docs/img/user/android_configuration_scroll_bottom.de.png b/docs/img/user/android_configuration_scroll_bottom.de.png deleted file mode 100644 index 82108324..00000000 Binary files a/docs/img/user/android_configuration_scroll_bottom.de.png and /dev/null differ diff --git a/docs/img/user/android_configuration_scroll_bottom.en.png b/docs/img/user/android_configuration_scroll_bottom.en.png deleted file mode 100644 index 11a6fc5a..00000000 Binary files a/docs/img/user/android_configuration_scroll_bottom.en.png and /dev/null differ diff --git a/docs/img/user/android_unread_feed_synced.de.png b/docs/img/user/android_unread_feed_synced.de.png deleted file mode 100644 index a3b6ed00..00000000 Binary files a/docs/img/user/android_unread_feed_synced.de.png and /dev/null differ diff --git a/docs/img/user/android_unread_feed_synced.en.png b/docs/img/user/android_unread_feed_synced.en.png deleted file mode 100644 index 8451557e..00000000 Binary files a/docs/img/user/android_unread_feed_synced.en.png and /dev/null differ diff --git a/docs/img/user/android_welcome_screen.de.png b/docs/img/user/android_welcome_screen.de.png deleted file mode 100644 index 4d3543d8..00000000 Binary files a/docs/img/user/android_welcome_screen.de.png and /dev/null differ diff --git a/docs/img/user/android_welcome_screen.en.png b/docs/img/user/android_welcome_screen.en.png deleted file mode 100644 index a3741d50..00000000 Binary files a/docs/img/user/android_welcome_screen.en.png and /dev/null differ diff --git a/docs/img/user/annotations_1.png b/docs/img/user/annotations_1.png deleted file mode 100644 index 554282f5..00000000 Binary files a/docs/img/user/annotations_1.png and /dev/null differ diff --git a/docs/img/user/annotations_2.png b/docs/img/user/annotations_2.png deleted file mode 100644 index 5351d2c5..00000000 Binary files a/docs/img/user/annotations_2.png and /dev/null differ diff --git a/docs/img/user/annotations_3.png b/docs/img/user/annotations_3.png deleted file mode 100644 index bc371ec3..00000000 Binary files a/docs/img/user/annotations_3.png and /dev/null differ diff --git a/docs/img/user/download_article.png b/docs/img/user/download_article.png deleted file mode 100644 index dd03ab92..00000000 Binary files a/docs/img/user/download_article.png and /dev/null differ diff --git a/docs/img/user/download_articles.png b/docs/img/user/download_articles.png deleted file mode 100644 index afdeffdf..00000000 Binary files a/docs/img/user/download_articles.png and /dev/null differ diff --git a/docs/img/user/export_v1.png b/docs/img/user/export_v1.png deleted file mode 100644 index ad2098a4..00000000 Binary files a/docs/img/user/export_v1.png and /dev/null differ diff --git a/docs/img/user/export_v2.png b/docs/img/user/export_v2.png deleted file mode 100644 index e66967d0..00000000 Binary files a/docs/img/user/export_v2.png and /dev/null differ diff --git a/docs/img/user/export_wllbg_1.png b/docs/img/user/export_wllbg_1.png deleted file mode 100644 index f9d24517..00000000 Binary files a/docs/img/user/export_wllbg_1.png and /dev/null differ diff --git a/docs/img/user/export_wllbg_2.png b/docs/img/user/export_wllbg_2.png deleted file mode 100644 index db2d4cf6..00000000 Binary files a/docs/img/user/export_wllbg_2.png and /dev/null differ diff --git a/docs/img/user/filters.png b/docs/img/user/filters.png deleted file mode 100644 index 1e566bd3..00000000 Binary files a/docs/img/user/filters.png and /dev/null differ diff --git a/docs/img/user/import_wallabagv1.png b/docs/img/user/import_wallabagv1.png deleted file mode 100644 index 4ea1e1d5..00000000 Binary files a/docs/img/user/import_wallabagv1.png and /dev/null differ diff --git a/docs/img/user/import_wllbg.png b/docs/img/user/import_wllbg.png deleted file mode 100644 index 6eec07e4..00000000 Binary files a/docs/img/user/import_wllbg.png and /dev/null differ diff --git a/docs/img/user/login_form.png b/docs/img/user/login_form.png deleted file mode 100644 index fddfb35d..00000000 Binary files a/docs/img/user/login_form.png and /dev/null differ diff --git a/docs/img/user/refetch.png b/docs/img/user/refetch.png deleted file mode 100644 index ff905248..00000000 Binary files a/docs/img/user/refetch.png and /dev/null differ diff --git a/docs/img/user/registration_form.png b/docs/img/user/registration_form.png deleted file mode 100644 index 127bf2a7..00000000 Binary files a/docs/img/user/registration_form.png and /dev/null differ diff --git a/docs/img/user/sent_email.png b/docs/img/user/sent_email.png deleted file mode 100644 index 0713a404..00000000 Binary files a/docs/img/user/sent_email.png and /dev/null differ diff --git a/docs/img/user/share.png b/docs/img/user/share.png deleted file mode 100644 index 4cfe9edb..00000000 Binary files a/docs/img/user/share.png and /dev/null differ diff --git a/docs/img/user/topbar.png b/docs/img/user/topbar.png deleted file mode 100644 index e20d9fc6..00000000 Binary files a/docs/img/user/topbar.png and /dev/null differ diff --git a/docs/img/wallabag.png b/docs/img/wallabag.png deleted file mode 100644 index 2dada55d..00000000 Binary files a/docs/img/wallabag.png and /dev/null differ diff --git a/docs/it/conf.py b/docs/it/conf.py deleted file mode 100644 index 717b35f1..00000000 --- a/docs/it/conf.py +++ /dev/null @@ -1,55 +0,0 @@ -# -*- coding: utf-8 -*- -# -# wallabag documentation build configuration file, created by -# sphinx-quickstart on Fri Oct 16 06:47:23 2015. - -import sys -import os - -extensions = [] -templates_path = ['_templates'] -source_suffix = '.rst' -master_doc = 'index' -project = u'wallabag' -copyright = u'2013-2016, Nicolas Lœuillet - MIT Licence' -version = '2.1.0' -release = version -exclude_patterns = ['_build'] -pygments_style = 'sphinx' -html_theme = 'default' -html_static_path = ['_static'] -htmlhelp_basename = 'wallabagdoc' -latex_elements = { - -} - -latex_documents = [ - ('index', 'wallabag.tex', u'wallabag Documentation', - u'Nicolas Lœuillet', 'manual'), -] - -man_pages = [ - ('index', 'wallabag', u'wallabag Documentation', - [u'Nicolas Lœuillet'], 1) -] - -texinfo_documents = [ - ('index', 'wallabag', u'wallabag Documentation', - u'Nicolas Lœuillet', 'wallabag', 'wallabag is an opensource read-it-later.', - 'Miscellaneous'), -] - -##### Guzzle sphinx theme - -import guzzle_sphinx_theme -html_translator_class = 'guzzle_sphinx_theme.HTMLTranslator' -html_theme_path = guzzle_sphinx_theme.html_theme_path() -html_theme = 'guzzle_sphinx_theme' - -# Custom sidebar templates, maps document names to template names. -html_sidebars = { - '**': ['logo-text.html', 'globaltoc.html', 'searchbox.html'] -} - -# Register the theme as an extension to generate a sitemap.xml -extensions.append("guzzle_sphinx_theme") diff --git a/docs/it/developer/api.rst b/docs/it/developer/api.rst deleted file mode 100644 index fc19a175..00000000 --- a/docs/it/developer/api.rst +++ /dev/null @@ -1,269 +0,0 @@ -Documentazione su API ---------------------- - -Grazie a questa documentazione, vedremo come interagire con l'API di wallabag. - -Requisiti ---------- - -* wallabag appena (o no) installato su http://localhost:8000 -* ``httpie`` installato sul vostro computer (`guardate il sito del progetto `__). Da notare che potete anche adattare i comandi usando curl o wget. -* tutti i metodi API documentati qui http://localhost:8000/api/doc (sulla vostra istanza) e `sulla nostra istanza d'esempio `_ - -Creare un nuovo client API --------------------------- - -Sul vostro account wallabag potete creare un nuovo client API presso questo URL http://localhost:8000/developer/client/create. - -Date solamente l'URL per il reindirizzamento della vostra applicazione e create il vostro client. Se la vostra applicazione è desktop, inserite l'URL che preferite. - -Toverete informazioni come queste: - -:: - - Client ID: - - 1_3o53gl30vhgk0c8ks4cocww08o84448osgo40wgw4gwkoo8skc - - Client secret: - - 636ocbqo978ckw0gsw4gcwwocg8044sco0w8w84cws48ggogs4 - - -Ottenere un token per il ricaricamento --------------------------------------- - -Per ogni chiamata API avrete bisogno di un token. Creiamolo con questo comando (rimpiazzate ``client_id``, ``client_secret``, ``username`` and ``password`` con i loro valori): - -:: - - http POST http://localhost:8000/oauth/v2/token \ - grant_type=password \ - client_id=1_3o53gl30vhgk0c8ks4cocww08o84448osgo40wgw4gwkoo8skc \ - client_secret=636ocbqo978ckw0gsw4gcwwocg8044sco0w8w84cws48ggogs4 \ - username=wallabag \ - password=wallabag - -Otterrete questo risultato: - -:: - - HTTP/1.1 200 OK - Cache-Control: no-store, private - Connection: close - Content-Type: application/json - Date: Tue, 05 Apr 2016 08:44:33 GMT - Host: localhost:8000 - Pragma: no-cache - X-Debug-Token: 19c8e0 - X-Debug-Token-Link: /_profiler/19c8e0 - X-Powered-By: PHP/7.0.4 - - { - "access_token": "ZGJmNTA2MDdmYTdmNWFiZjcxOWY3MWYyYzkyZDdlNWIzOTU4NWY3NTU1MDFjOTdhMTk2MGI3YjY1ZmI2NzM5MA", - "expires_in": 3600, - "refresh_token": "OTNlZGE5OTJjNWQwYzc2NDI5ZGE5MDg3ZTNjNmNkYTY0ZWZhZDVhNDBkZTc1ZTNiMmQ0MjQ0OThlNTFjNTQyMQ", - "scope": null, - "token_type": "bearer" - } - -Lavoreremo con il valore ``access_token`` nelle nostre prossime chiamate. - -esempio di cURL: - -:: - - curl -s "https://localhost:8000/oauth/v2/token?grant_type=password&client_id=1_3o53gl30vhgk0c8ks4cocww08o84448osgo40wgw4gwkoo8skc&client_secret=636ocbqo978ckw0gsw4gcwwocg8044sco0w8w84cws48ggogs4&username=wallabag&password=wallabag" - -Ottenere voci esistenti ------------------------ - -Documentazione per questo metodo: http://localhost:8000/api/doc#get--api-entries.{_format} - -Mentre lavoriamo su una nuova installazione di wallabag, non otterremo risultati con questo comando: - -:: - - http GET http://localhost:8000/api/entries.json \ - "Authorization:Bearer ZGJmNTA2MDdmYTdmNWFiZjcxOWY3MWYyYzkyZDdlNWIzOTU4NWY3NTU1MDFjOTdhMTk2MGI3YjY1ZmI2NzM5MA" - -restituisce: - -:: - - HTTP/1.1 200 OK - 0: application/json - Cache-Control: no-cache - Connection: close - Content-Type: application/json - Date: Tue, 05 Apr 2016 08:51:32 GMT - Host: localhost:8000 - Set-Cookie: PHPSESSID=nrogm748md610ovhu6j70c3q63; path=/; HttpOnly - X-Debug-Token: 4fbbc4 - X-Debug-Token-Link: /_profiler/4fbbc4 - X-Powered-By: PHP/7.0.4 - - { - "_embedded": { - "items": [] - }, - "_links": { - "first": { - "href": "http://localhost:8000/api/entries?page=1&perPage=30" - }, - "last": { - "href": "http://localhost:8000/api/entries?page=1&perPage=30" - }, - "self": { - "href": "http://localhost:8000/api/entries?page=1&perPage=30" - } - }, - "limit": 30, - "page": 1, - "pages": 1, - "total": 0 - } - -L'insieme ``items`` è vuoto. - -esempio di cURL: - -:: - - curl --get "https://localhost:8000/api/entries.html?access_token=ZGJmNTA2MDdmYTdmNWFiZjcxOWY3MWYyYzkyZDdlNWIzOTU4NWY3NTU1MDFjOTdhMTk2MGI3YjY1ZmI2NzM5MA" - -Aggiungere la vostra prima voce -------------------------------- - -Documentazione per questo metodo: http://localhost:8000/api/doc#post--api-entries.{_format} - -:: - - http POST http://localhost:8000/api/entries.json \ - "Authorization:Bearer ZGJmNTA2MDdmYTdmNWFiZjcxOWY3MWYyYzkyZDdlNWIzOTU4NWY3NTU1MDFjOTdhMTk2MGI3YjY1ZmI2NzM5MA" \ - url="http://www.numerama.com/tech/160115-le-pocket-libre-wallabag-fait-le-plein-de-fonctionnalites.html" - -restituisce: - -:: - - HTTP/1.1 200 OK - 0: application/json - Cache-Control: no-cache - Connection: close - Content-Type: application/json - Date: Tue, 05 Apr 2016 09:07:54 GMT - Host: localhost:8000 - Set-Cookie: PHPSESSID=bjie40ck72kp2pst3i71gf43a4; path=/; HttpOnly - X-Debug-Token: e01c51 - X-Debug-Token-Link: /_profiler/e01c51 - X-Powered-By: PHP/7.0.4 - - { - "_links": { - "self": { - "href": "/api/entries/1" - } - }, - "content": "

Fonctionnant sur le même principe que Pocket, Instapaper ou Readability, le logiciel Wallabag permet de mémoriser des articles pour les lire plus tard. Sa nouvelle version apporte une multitude de nouvelles fonctionnalités.

Si vous utilisez Firefox comme navigateur web, vous avez peut-être constaté l’arrivée d’une fonctionnalité intitulée Pocket. Disponible autrefois sous la forme d’un module complémentaire, et sous un autre nom (Read it Later), elle est depuis le mois de juin 2015 directement incluse au sein de Firefox.

\n

Concrètement, Pocket sert à garder en mémoire des contenus que vous croisez au fil de la navigation, comme des articles de presse ou des vidéos, afin de pouvoir les consulter plus tard. Pocket fonctionne un peu comme un système de favoris, mais en bien plus élaboré grâce à ses options supplémentaires.

\n

Mais Pocket fait polémique, car il s’agit d’un projet propriétaire qui est intégré dans un logiciel libre. C’est pour cette raison que des utilisateurs ont choisi de se tourner vers d’autres solutions, comme Wallabag, qui est l’équivalent libre de Pocket et d’autres systèmes du même genre, comme Instapaper et Readability.

\n

Et justement, Wallabag évolue. C’est ce dimanche que la version 2.0.0 du logiciel a été publiée par l’équipe en charge de son développement et celle-ci contient de nombreux changements par rapport aux moutures précédentes (la documentation est traduite en français), lui permettant d’apparaître comme une alternative à Pocket, Instapaper et Readability.

\n

\"homepage\"

\n

Parmi les principaux changements que l’on peut retenir avec cette nouvelle version, notons la possibilité d’écrire des annotations dans les articles mémorisés, de filtrer les contenus selon divers critères (temps de lecture, nom de domaine, date de création, statut…), d’assigner des mots-clés aux entrées, de modifier le titre des articles, le support des flux RSS ou encore le support de plusieurs langues dont le français.

\n

D’autres options sont également à signaler, comme l’aperçu d’un article mémorisé (si l’option est disponible), un guide de démarrage rapide pour les débutants, un outil d’export dans divers formats (PDF, JSON, EPUB, MOBI, XML, CSV et TXT) et, surtout, la possibilité de migrer vers Wallabag depuis Pocket, afin de convaincre les usagers de se lancer.

\n \n \n

Articles liés

\n
\n
\n", - "created_at": "2016-04-05T09:07:54+0000", - "domain_name": "www.numerama.com", - "id": 1, - "is_archived": 0, - "is_starred": 0, - "language": "fr-FR", - "mimetype": "text/html", - "preview_picture": "http://www.numerama.com/content/uploads/2016/04/post-it.jpg", - "reading_time": 2, - "tags": [], - "title": "Le Pocket libre Wallabag fait le plein de fonctionnalités - Tech - Numerama", - "updated_at": "2016-04-05T09:07:54+0000", - "url": "http://www.numerama.com/tech/160115-le-pocket-libre-wallabag-fait-le-plein-de-fonctionnalites.html", - "user_email": "", - "user_id": 1, - "user_name": "wallabag" - } - -Ora, se eseguite il comando precedente (vedere **Ottenere voci esistenti**), avrete dei dati. - -esempio di cURL: - -:: - - curl "https://localhost:8000/api/entries.html?access_token=ZGJmNTA2MDdmYTdmNWFiZjcxOWY3MWYyYzkyZDdlNWIzOTU4NWY3NTU1MDFjOTdhMTk2MGI3YjY1ZmI2NzM5MA&url=http://www.numerama.com/tech/160115-le-pocket-libre-wallabag-fait-le-plein-de-fonctionnalites.html" - -Eliminare una voce ------------------- - -Documentazione per questo metodo: http://localhost:8000/api/doc#delete--api-entries-{entry}.{_format} - -:: - - http DELETE http://localhost:8000/api/entries/1.json \ - "Authorization:Bearer ZGJmNTA2MDdmYTdmNWFiZjcxOWY3MWYyYzkyZDdlNWIzOTU4NWY3NTU1MDFjOTdhMTk2MGI3YjY1ZmI2NzM5MA" - -restituisce - -:: - - HTTP/1.1 200 OK - 0: application/json - Cache-Control: no-cache - Connection: close - Content-Type: application/json - Date: Tue, 05 Apr 2016 09:19:07 GMT - Host: localhost:8000 - Set-Cookie: PHPSESSID=jopgnfvmuc9a62b27sqm6iulr6; path=/; HttpOnly - X-Debug-Token: 887cef - X-Debug-Token-Link: /_profiler/887cef - X-Powered-By: PHP/7.0.4 - - { - "_links": { - "self": { - "href": "/api/entries/" - } - }, - "annotations": [], - "content": "

Fonctionnant sur le même principe que Pocket, Instapaper ou Readability, le logiciel Wallabag permet de mémoriser des articles pour les lire plus tard. Sa nouvelle version apporte une multitude de nouvelles fonctionnalités.

Si vous utilisez Firefox comme navigateur web, vous avez peut-être constaté l’arrivée d’une fonctionnalité intitulée Pocket. Disponible autrefois sous la forme d’un module complémentaire, et sous un autre nom (Read it Later), elle est depuis le mois de juin 2015 directement incluse au sein de Firefox.

\n

Concrètement, Pocket sert à garder en mémoire des contenus que vous croisez au fil de la navigation, comme des articles de presse ou des vidéos, afin de pouvoir les consulter plus tard. Pocket fonctionne un peu comme un système de favoris, mais en bien plus élaboré grâce à ses options supplémentaires.

\n

Mais Pocket fait polémique, car il s’agit d’un projet propriétaire qui est intégré dans un logiciel libre. C’est pour cette raison que des utilisateurs ont choisi de se tourner vers d’autres solutions, comme Wallabag, qui est l’équivalent libre de Pocket et d’autres systèmes du même genre, comme Instapaper et Readability.

\n

Et justement, Wallabag évolue. C’est ce dimanche que la version 2.0.0 du logiciel a été publiée par l’équipe en charge de son développement et celle-ci contient de nombreux changements par rapport aux moutures précédentes (la documentation est traduite en français), lui permettant d’apparaître comme une alternative à Pocket, Instapaper et Readability.

\n

\"homepage\"

\n

Parmi les principaux changements que l’on peut retenir avec cette nouvelle version, notons la possibilité d’écrire des annotations dans les articles mémorisés, de filtrer les contenus selon divers critères (temps de lecture, nom de domaine, date de création, statut…), d’assigner des mots-clés aux entrées, de modifier le titre des articles, le support des flux RSS ou encore le support de plusieurs langues dont le français.

\n

D’autres options sont également à signaler, comme l’aperçu d’un article mémorisé (si l’option est disponible), un guide de démarrage rapide pour les débutants, un outil d’export dans divers formats (PDF, JSON, EPUB, MOBI, XML, CSV et TXT) et, surtout, la possibilité de migrer vers Wallabag depuis Pocket, afin de convaincre les usagers de se lancer.

\n \n \n

Articles liés

\n
\n
\n", - "created_at": "2016-04-05T09:07:54+0000", - "domain_name": "www.numerama.com", - "is_archived": 0, - "is_starred": 0, - "language": "fr-FR", - "mimetype": "text/html", - "preview_picture": "http://www.numerama.com/content/uploads/2016/04/post-it.jpg", - "reading_time": 2, - "tags": [], - "title": "Le Pocket libre Wallabag fait le plein de fonctionnalités - Tech - Numerama", - "updated_at": "2016-04-05T09:07:54+0000", - "url": "http://www.numerama.com/tech/160115-le-pocket-libre-wallabag-fait-le-plein-de-fonctionnalites.html", - "user_email": "", - "user_id": 1, - "user_name": "wallabag" - } - -E se volete elencare le voci esistenti (vedere **Ottenere voci esistenti**), l'insieme è vuoto. - -esempio di cURL: - -:: - - curl --request DELETE "https://localhost:8000/api/entries/1.html?access_token=ZGJmNTA2MDdmYTdmNWFiZjcxOWY3MWYyYzkyZDdlNWIzOTU4NWY3NTU1MDFjOTdhMTk2MGI3YjY1ZmI2NzM5MA" - -Altri metodi ------------- - -Non scriveremo esempi per ogni metodo API. -Guardate l'elenco qui : http://localhost:8000/api/doc per ogni metodo conosciuto. - -Risorse di terze parti ----------------------- - -Alcune applicazioni o librerie usano le nostre API. Ecco una lista non esaustiva: - -- `Wrapper Java per l'API di wallabag `_ di Strubbl. -- `Libreria .NET per l'API di wallabag v2 `_ di Julian Oster. -- `API di Python per wallabag `_ di FoxMaSk, per il suo progetto `Trigger Happy `_. -- `Un plugin `_ creato per `Tiny Tiny RSS `_ questo usa l'API di wallabag v2. Di Josh Panter. -- `Wrapper Golang per l'API di wallabag `_ di Strubbl, per il suo progetto `wallabag-stats graph `_. diff --git a/docs/it/developer/asynchronous.rst b/docs/it/developer/asynchronous.rst deleted file mode 100644 index d753bd23..00000000 --- a/docs/it/developer/asynchronous.rst +++ /dev/null @@ -1,162 +0,0 @@ -Compiti Asincroni -================= - -Per avviare compiti asincroni (utile ad esempio per grandi importazioni), Possiamo usare RabbitMQ o Redis. - -Installare RabbitMQ per compiti asincroni ------------------------------------------ - -Requisiti -^^^^^^^^^ - -Dovete avere RabbitMQ installato sul vostro server. - -Installazione -^^^^^^^^^^^^^ - -.. code:: bash - - wget https://www.rabbitmq.com/rabbitmq-signing-key-public.asc - apt-key add rabbitmq-signing-key-public.asc - apt-get update - apt-get install rabbitmq-server - -Configurazione ed avvio -^^^^^^^^^^^^^^^^^^^^^^^ - -.. code:: bash - - rabbitmq-plugins enable rabbitmq_management # (useful to have a web interface, available at http://localhost:15672/ (guest/guest) - rabbitmq-server -detached - -Fermare RabbitMQ -^^^^^^^^^^^^^^^^ - -.. code:: bash - - rabbitmqctl stop - - -Configurare RabbitMQ in wallabag -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Modificate il vostro file ``app/config/parameters.yml`` per modificare la configurazione di RabbitMQ. Quella di default dovrebbe andare bene: - -.. code:: yaml - - rabbitmq_host: localhost - rabbitmq_port: 5672 - rabbitmq_user: guest - rabbitmq_password: guest - rabbitmq_prefetch_count: 10 # read http://www.rabbitmq.com/consumer-prefetch.html - -Abilitare RabbitMQ su wallabag -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Su Strumenti, nella sezione **Importa**, abilitate RabbitMQ (con il valore 1). - -Avviare RabbitMQ consumer -^^^^^^^^^^^^^^^^^^^^^^^^^ - -Dipendendo da quale servizio vogliate importare, dovrete abilitare uno (o più se volete supportare molti) o più cronjob: - -.. code:: bash - - # per importare da Pocket - bin/console rabbitmq:consumer -e=prod import_pocket -w - - # per importare da Readability - bin/console rabbitmq:consumer -e=prod import_readability -w - - # per importare da Instapaper - bin/console rabbitmq:consumer -e=prod import_instapaper -w - - # per importare da wallabag v1 - bin/console rabbitmq:consumer -e=prod import_wallabag_v1 -w - - # per importare da wallabag v2 - bin/console rabbitmq:consumer -e=prod import_wallabag_v2 -w - - # per importare da Firefox - bin/console rabbitmq:consumer -e=prod import_firefox -w - - # per importare da Chrome - bin/console rabbitmq:consumer -e=prod import_chrome -w - -Installare Redis per compiti asincroni --------------------------------------- - -Per avviare compiti asincroni (utile ad esempio per grandi importazioni), Possiamo usare Redis. - -Requisiti -^^^^^^^^^ - -Dovete avere Redis installato sul vostro server. - -Installazione -^^^^^^^^^^^^^ - -.. code:: bash - - apt-get install redis-server - - -Avvio -^^^^^ - -Il server dovrebbe già essere attivo dopo l'installazione, altrimenti potete avviarlo usando: - -.. code:: bash - - redis-server - - -Configurare Redis su wallabag -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Modificate il vostro file ``app/config/parameters.yml`` per modificare la configurazione di Redis. Quella di default dovrebbe andare bene: - -.. code:: yaml - - redis_host: localhost - redis_port: 6379 - -Abilitare Redis su wallabag -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Su Strumenti, nella sezione **Importa**, abilitate Redis (con il valore 1). - -Avviare Redis consumer -^^^^^^^^^^^^^^^^^^^^^^ - -Dipendendo da quale servizio vogliate importare, dovrete abilitare uno (o più se volete supportare molti) o più cronjob: - -.. code:: bash - - # per importare da Pocket - bin/console wallabag:import:redis-worker -e=prod pocket -vv >> /path/to/wallabag/var/logs/redis-pocket.log - - # per importare da Readability - bin/console wallabag:import:redis-worker -e=prod readability -vv >> /path/to/wallabag/var/logs/redis-readability.log - - # per importare da Instapaper - bin/console wallabag:import:redis-worker -e=prod instapaper -vv >> /path/to/wallabag/var/logs/redis-instapaper.log - - # per importare da wallabag v1 - bin/console wallabag:import:redis-worker -e=prod wallabag_v1 -vv >> /path/to/wallabag/var/logs/redis-wallabag_v1.log - - # per importare da wallabag v2 - bin/console wallabag:import:redis-worker -e=prod wallabag_v2 -vv >> /path/to/wallabag/var/logs/redis-wallabag_v2.log - - # per importare da Firefox - bin/console wallabag:import:redis-worker -e=prod firefox -vv >> /path/to/wallabag/var/logs/redis-firefox.log - - # per importare da Chrome - bin/console wallabag:import:redis-worker -e=prod chrome -vv >> /path/to/wallabag/var/logs/redis-chrome.log - -Se volete avviare l'importazione solamente per alcuni messaggi e non tutti, potete specificare questo numero (qui 12) e il programma si fermerà dopo il dodicesimo messaggio: - -.. code:: bash - - bin/console wallabag:import:redis-worker -e=prod pocket -vv --maxIterations=12 - diff --git a/docs/it/developer/docker.rst b/docs/it/developer/docker.rst deleted file mode 100644 index f07df20d..00000000 --- a/docs/it/developer/docker.rst +++ /dev/null @@ -1,41 +0,0 @@ -Eseguite wallabag in docker-compose -=================================== - -Per eseguire la vostra propria istanza di sviluppo di wallabag, dovreste usare i file docker compose preconfigurati. - -Requisiti ---------- - -Assicuratevi di avere `Docker -` e `Docker -Compose `__ disponibili sul vostro sistema e aggiornati. - -Cambiate DBMS -------------- - -Per default, wallabag inizierá con un database SQLite. -Visto che wallabag supporta Postgresql e MySQL, i container di docker sono disponibili anche per questi. - -In ``docker-compose.yml``, per il DBMS scelto, togliete i commenti: - -- la definizione del container (blocco a livello root ``postgres`` o ``mariadb``) -- il link del container nel container``php`` -- il file env del container nel container ``php`` - -Per far continuare ad eseguire i comandi Symfony sul vostro host (come ``wallabag:install``), dovreste anche: - -- caricare i file env appropriati sulla vostra riga di comando, in modo che possano esistere variabili come ``SYMFONY__ENV__DATABASE_HOST``. -- creare un ``127.0.0.1 rdbms`` sul vostro file di sistema ``hosts`` - -Eseguite wallabag ------------------ - -#. Fate un fork o clonate il progetto -#. Modificate ``app/config/parameters.yml`` per rimpiazzare le proprietá di ``database_*`` con quelle commentate (con valori con prefisso ``env.``) -#. ``composer install`` per installare le dipendenze del progetto -#. ``php bin/console wallabag:install`` per creare lo schema -#. ``docker-compose up`` per eseguire i containers -#. Infine, andate su http://localhost:8080/ per trovare il vostro wallabag appena installato. - -Durante i vari passi potreste incontrare problemi di permessi UNIX, percorsi sbagliati nella cache generata, etc... -Operazioni come cambiare i file della cache o cambiare i proprietari dei file potrebbero essere richiesto frequentemente, per cui non abbiate paura! diff --git a/docs/it/developer/documentation.rst b/docs/it/developer/documentation.rst deleted file mode 100644 index d4b63bb6..00000000 --- a/docs/it/developer/documentation.rst +++ /dev/null @@ -1,12 +0,0 @@ -Contribuite a questa documentazione -=================================== - -Le fonti della nostra documentazione sono qui https://github.com/wallabag/wallabag/tree/master/docs - -Usiamo `ReadTheDocs -`__ per generarla. - -Le pagine sono scritte nel formato `Restructured Text -`__. Potete usare strumenti online come http://rst.aaroniles.net/ o http://rst.ninjs.org/ per vedere un'anteprima dei vostri articoli. - -Se create una nuova pagina, non scordatevi di modificare il file `index.rst `__ per aggiungere un link nella barra laterale. diff --git a/docs/it/developer/paywall.rst b/docs/it/developer/paywall.rst deleted file mode 100644 index 2c4e1bf7..00000000 --- a/docs/it/developer/paywall.rst +++ /dev/null @@ -1,52 +0,0 @@ -Articoli dietro ad un paywall -============================= - -wallabag puó acquisire articoli da siti web che usano un sistema paywall - -Abilitate l'autenticazione paywall ----------------------------------- - -Su impostazioni interne, nella sezione **Articolo**, abilitate l'autenticazione per siti con paywall (con il valore 1). - -Configurate le credenziali in wallabag --------------------------------------- - -Modificate il vostro file ``app/config/parameters.yml`` per modificare le credenziali per ogni sito con paywall. Ecco un esempio di alcuni siti francesi: - -.. code:: yaml - - sites_credentials: - mediapart.fr: {username: "myMediapartLogin", password: "mypassword"} - arretsurimages.net: {username: "myASILogin", password: "mypassword"} - -.. note:: - - These credentials will be shared between each user of your wallabag instance. - -Fate il parsing dei file di configurazione ------------------------------------------- - -Leggete `questa parte della documentazione *link mancante*` per capire i file di configurazione. - -Ogni file di configurazione del parsing deve essere migliorato aggiungendo ``requires_login``, ``login_uri``, ``login_username_field``, ``login_password_field`` e ``not_logged_in_xpath``. - -Fate attenzione, il modulo di login deve essere nel contenuto della pagina quando wallabag lo carica. É impossibile per wallab essere autenticato su un sito dove il modulo di login é caricato dopo la pagina (da ajax per esempio). - -``login_uri`` é l'URL di azione del modulo (l'attributo ``action`` del modulo). -``login_username_field`` é l'attributo ``name`` nel campo di login. -``login_password_field`` é l'attributo ``name`` nel campo password. - -Per esempio: - -.. code:: - - title://div[@id="titrage-contenu"]/h1[@class="title"] - body: //div[@class="contenu-html"]/div[@class="page-pane"] - - requires_login: yes - - login_uri: http://www.arretsurimages.net/forum/login.php - login_username_field: username - login_password_field: password - - not_logged_in_xpath: //body[@class="not-logged-in"] diff --git a/docs/it/developer/testsuite.rst b/docs/it/developer/testsuite.rst deleted file mode 100644 index fdb72401..00000000 --- a/docs/it/developer/testsuite.rst +++ /dev/null @@ -1,6 +0,0 @@ -Testsuite -========= -Per assicurare la qualità di sviluppo di wallabag, abbiamo scritto i test con `PHPUnit `_. -Se contribuite al progetto (traducendo l'applicazione, risolvendo i bug o aggiungendo nuove funzioni), si prega di scrivere i propri test. - -Per avviare la testsuite di wallabag dovete installare `ant `_. Poi, eseguite il comando ``make test``. \ No newline at end of file diff --git a/docs/it/developer/translate.rst b/docs/it/developer/translate.rst deleted file mode 100644 index c0b8e265..00000000 --- a/docs/it/developer/translate.rst +++ /dev/null @@ -1,55 +0,0 @@ -Tradurre wallabag -================= - -wallabag web app ----------------- - -File per la traduzione -~~~~~~~~~~~~~~~~~~~~~~ - -.. note:: - - Visto che wallabag é principalmente sviluppato da un team francese, si prega di considerare che la traduzione francese é la più aggiornata, e si prega di copiarla e di creare la vostra propria traduzione. - -Potete trovare qui i file per la traduzione: -https://github.com/wallabag/wallabag/tree/master/src/Wallabag/CoreBundle/Resources/translations. - -Dovrete creare ``messages.CODE.yml`` e ``validators.CODE.yml``, dove CODE é il codice ISO 639-1 della vostra lingua (`guardate wikipedia `__). - -Altri file da tradurre: - -- https://github.com/wallabag/wallabag/tree/master/app/Resources/CraueConfigBundle/translations. -- https://github.com/wallabag/wallabag/tree/master/src/Wallabag/UserBundle/Resources/translations. - -Dovete creare i file ``THE_TRANSLATION_FILE.CODE.yml``. - -File di configurazione -~~~~~~~~~~~~~~~~~~~~~~ - -Dovete modificare `app/config/config.yml -`__ per mostrare il vostro linguaggio nella pagina di configurazione di wallabag (per consentire agli utenti di passare a questa nuova traduzione). - -Nella sezione ``wallabag_core.languages``, dovete aggiungere una nuova linea con la vostra traduzione. Per esempio: - -:: - - wallabag_core: - ... - languages: - en: 'English' - fr: 'Français' - -Nella prima colonna (``en``, ``fr``, etc.), dovete aggiungere il codice ISO 639-1 della vostra lingua (vedete sopra). - -Nella seconda colonna, aggiungete solamente il nome della vostra lingua. - -documentazione di wallabag --------------------------- - -.. note:: - Contrariamente alla web app, il linguaggio principale per la documentazione é l'inglese. - -I file della documentazione sono memorizzati qui: https://github.com/wallabag/wallabag/tree/master/docs - -Dovete rispettare la struttura della cartella ``en`` quando create la vostra traduzione. - diff --git a/docs/it/index.rst b/docs/it/index.rst deleted file mode 100644 index 92896fbf..00000000 --- a/docs/it/index.rst +++ /dev/null @@ -1,53 +0,0 @@ -Documentazione di wallabag --------------------------- - -.. image:: ../img/wallabag.png - :alt: wallabag logo - :align: center - -**wallabag** è un'applicazione read-it-later: salva una pagina web lasciando solamente il contenuto. Elementi come comandi di navigazione o pubblicità sono rimossi. - -La documentazione principale per quest'applicazione è organizzata in un paio di sezioni: - -* :ref:`user-docs` -* :ref:`dev-docs` - -La documentazione è disponibile anche in altre lingue: - -* `Documentation en français `_ -* `Dokumentation in Deutsch `_ -* `Documentation in English `_ - -.. _user-docs: - -.. toctree:: - :maxdepth: 2 - :caption: User documentation - - user/installation - user/upgrade - user/configuration - user/import - user/create_account - user/articles - user/errors_during_fetching - user/filters - user/tags - user/configuring_mobile - user/android - user/parameters - user/backup - user/faq - -.. _dev-docs: - -.. toctree:: - :maxdepth: 2 - :caption: Developer documentation - - developer/api - developer/docker - developer/paywall - developer/documentation - developer/translate - developer/asynchronous diff --git a/docs/it/requirements.txt b/docs/it/requirements.txt deleted file mode 100644 index 06fc8973..00000000 --- a/docs/it/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -Sphinx>=1.3.0,<1.4.0 -guzzle_sphinx_theme>=0.7.0,<0.8.0 diff --git a/docs/it/user/android.rst b/docs/it/user/android.rst deleted file mode 100644 index a82bbef5..00000000 --- a/docs/it/user/android.rst +++ /dev/null @@ -1,107 +0,0 @@ -Applicazione Android -==================== - -Scopo di questo documento -------------------------- - -Questo documento spiega come configurare la vostra applicazione Android in modo che funzioni con la vostra istanza di wallabag. Non vi é differenza in questa procedura sia per wallabag v1 che per v2. - -Passi per configurare la vostra app ------------------------------------ - -Quando aprirete l'app per la prima volta, vedrete una schermata di benvenuto dove vi si consiglia per prima cosa di configurare l'app per la vostra istanza di wallabag. - -.. image:: ../../img/user/android_welcome_screen.en.png - :alt: Welcome screen - :align: center - -Confermate semplicemente quel messaggio e verrete reindirizzati alla schermata delle impostazioni. - -.. image:: ../../img/user/android_configuration_screen.en.png - :alt: Settings screen - :align: center - -Inserite i vostri dati di wallabag. Dovrete inserire il vostro indirizzo di wallabag. **É importante che questo URL non finisca con una barra**. Inserite anche le vostre credenziali nei campi user name e password. - -.. image:: ../../img/user/android_configuration_filled_in.en.png - :alt: Filled in settings - :align: center - -Dopo aver completato l'inserimento dei dati, premete il bottone Connection test e aspettate che il test finisca. - -.. image:: ../../img/user/android_configuration_connection_test.en.png - :alt: Connection test with your wallabag data - :align: center - -Il test di connessione dovrebbe finire con successo. In caso contrario, dovrete prima risolvere questo problema fino a che possiate procedere. - -.. image:: ../../img/user/android_configuration_connection_test_success.en.png - :alt: Connection test successful - :align: center - -Dopo che il test sará avvenuto con successo, potrete premere il bottone per ottenere le credenziali del vostro feed. L'app cercherá di connettersi alla vostra istanza di wallabag e ottenere l'id utente e il token corrispondente per i feed. - -.. image:: ../../img/user/android_configuration_get_feed_credentials.en.png - :alt: Getting the feed credentials - :align: center - -Quando il processo di ottenimento delle credenziali del vostro feed sará concluso con successo, vedrete un messaggio toast, il quale avviserá che l'id utente ed il token sono stati inseriti nel modulo. - -.. image:: ../../img/user/android_configuration_feed_credentials_automatically_filled_in.en.png - :alt: Getting feed credentials successful - :align: center - -Ora dovrete scorrere fino alla fine del menu delle impostazioni. Ovviamente potrete cambiare le impostazioni in base alle vostre preferenze. -Terminate la configurazione della vostra app premendo il bottone per il salvataggio. - -.. image:: ../../img/user/android_configuration_scroll_bottom.en.png - :alt: Bottom of the settings screen - :align: center - -Dopo aver premuto il bottone apparirá la seguente schermata. L'app proporrá di iniziare il processo di sincronizzazione per aggiornare i vostri feed ed articoli. É raccomandato accettare quest'azione e premere Sí. - -.. image:: ../../img/user/android_configuration_saved_feed_update.en.png - :alt: Settings saved the first time - :align: center - -Alla fine, dopo che la sincronizzazione sará avvenuta con successo, apparirá la lista degli articoli non letti. - -.. image:: ../../img/user/android_unread_feed_synced.en.png - :alt: Filled article list cause feeds successfully synchronized - :align: center - -Limiti conosciuti ------------------ - -Autenticazione a due fattori (2FA) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Attualmente l'applicazione Android non supporta l'autenticazione a due fattori. Dovreste disabilitare questa opzione in modo da far funzionare l'applicazione. - -Quantitá limitata di articoli con wallabag v2 -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Nella vostra istanza web di wallabag potete configurare quanti oggetti sono parte del feed RSS. Questa opzione non esisteva in wallabag v1, dove tutti gli articoli erano parte del feed. Quindi se imposterete il numero di articoli da visualizzare maggiore del numero di oggetti che sono contenuti nel vostro feed RSS, vedrete solamente il numero di oggetti nel vostro feed RSS. - -Crittografia SSL/TLS -~~~~~~~~~~~~~~~~~~~~ - -Se potete raggiungere la vostra istanza web di wallabag via HTTPS, dovreste usare quest'ultimo, in particolar modo se il vostro URL HTTP vi reindirizza a quello HTTPS. Attualmente l'app non puó gestire propriamente il reindirizzamento. - -Riferimenti ------------ - -- `Codice sorgente dell'applicazione Android `_ -- `Applicazione Android su F-Droid `_ -- `Applicazione Android su Google Play `_ - - - - - - - - - - - diff --git a/docs/it/user/articles.rst b/docs/it/user/articles.rst deleted file mode 100644 index 944b23ef..00000000 --- a/docs/it/user/articles.rst +++ /dev/null @@ -1,115 +0,0 @@ - -Articoli -======== - -Salvate il vostro primo articolo --------------------------------- - -Il proposito principale di wallabag é di salvare articoli web, e potete farlo in molti modi. Se pensate che l'articolo sia mostrato in modo sbagliato, `potete leggere questa documentazione`_. - -Usando un bookmarklet -^^^^^^^^^^^^^^^^^^^^^ - -Nella pagina ``Howto`` avete una sezione ``Bookmarklet``. Trascinate il link ``bag it!`` sulla barra dei preferiti del vostro browser. - - -Ora, ogni volta che leggiate un articolo sul web e vogliate salvarlo, cliccate sul link ``bag it!`` sulla vostra barra dei preferiti. L'articolo é salvato. - -Usando la maniera classica -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Sulla barra in alto del vostro schermo avete 3 icone. Con la prima, un simbolo "piú", potrete salvare facilmente un articolo. - -.. image:: ../../img/user/topbar.png - :alt: Top bar - :align: center - -Cliccateci sopra per mostrare un nuovo campo, inserite in quest'ultimo la URL dell'articolo e premete il tasto ``Return``. L'articolo é salvato. - -Usando un add-on del browser -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Firefox -""""""" - -Potete scaricare `qui l'addon per Firefox `_. - -Chrome -"""""" - -Potete scaricare `qui l'addon per Chrome `_. - -Usando la vostra applicazione per smartphone -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Android -""""""" - -Potete scaricare `qui l'applicazione per Android `_. - -Windows Phone -""""""""""""" - -Potete scaricare `qui l'applicazione per Windows Phone `_. - -Scaricate i vostri articoli ---------------------------- - -Potete scaricare ogni articolo in vari formati: ePUB, MOBI, PDF, XML, JSON, CSV. - -Sulla vista dell'articolo, cliccate su questa icona, nella barra laterale: - -.. image:: ../../img/user/download_article.png - :alt: download article - :align: center - -Potete anche scaricare una categoria completa di articoli (unread, starred, archive) in questi formati. Per esempio, nella vista **Unread**, cliccate su questa icona sulla barra superiore: - -.. image:: ../../img/user/download_articles.png - :alt: download articles - :align: center - -Condividete i vostri articoli ------------------------------ - -Quando leggete un articolo, potete condividerlo. Cliccate semplicemente sul bottone di condivisione: - -.. image:: ../../img/user/share.png - :alt: share article - :align: center - -Ora potete condividere l'articolo: - -- attraverso una URL pubblica (avrete una vista semplificata dell'articolo) -- attraverso un tweet -- nel vostro Shaarli -- attraverso un post su Diaspora* -- su Carrot -- attraverso un'email - -Annotate i vostri articoli --------------------------- - -in ogni articolo che leggiate potete scrivere delle note. É piú facile da capire con delle immagini. - -Selezionate la parte dell'articolo che volete annotare e cliccate sulla matita: - -.. image:: ../../img/user/annotations_1.png - :alt: Select your text - :align: center - -Scrivete poi la vostra nota: - -.. image:: ../../img/user/annotations_2.png - :alt: Write your annotation - :align: center - -Il testo é ora sottolineato e potrete leggere la vostra nota muovendo il cursore su di esso. - -.. image:: ../../img/user/annotations_3.png - :alt: Read your annotation - :align: center - -Potete creare quante note vogliate. - - diff --git a/docs/it/user/backup.rst b/docs/it/user/backup.rst deleted file mode 100644 index 548c675b..00000000 --- a/docs/it/user/backup.rst +++ /dev/null @@ -1,30 +0,0 @@ -Eseguire il backup di wallabag -============================== - -Siccome a volte potreste commettere errori con il vostro wallabag e perdere i vostri dati, oppure in caso dobbiate spostare il vostro wallabag su un altro server, dovete fare un backup dei vostri dati. - -Impostazioni base ------------------ - -wallabag memorizza alcuni parametri base (come il server SMTP o il backend del database) nel file `app/config/parameters.yml`. - -Database --------- - -Per il fatto che wallabag supporta vari tipi di database, il modo di eseguire backup dipende dal database che stiate usando, quindi dovrete fare riferimento alla documentazione del venditore. - -Ecco alcuni esempi: - -- MySQL: http://dev.mysql.com/doc/refman/5.7/en/backup-methods.html -- PostgreSQL: https://www.postgresql.org/docs/current/static/backup.html - -SQLite -~~~~~~ - -Per eseguire il backup di un database SQLite, dovete semplicemente copiare la directory `data/db` dalla directory dell'applicazione wallabag. - -Immagini --------- - -Le immagini recuperate da wallabag sono memorizzate in `web/assets/images` (la memoria delle immagini sará implementata in wallabag 2.2). - diff --git a/docs/it/user/configuration.rst b/docs/it/user/configuration.rst deleted file mode 100644 index 3bd3bc91..00000000 --- a/docs/it/user/configuration.rst +++ /dev/null @@ -1,103 +0,0 @@ -Configurazione -============== - -Ora che siete acceduti, é ora di configurare il vostro account come volete. - -cliccate sul menu ``Configurazione``. Avrete cinque tab: ``Impostazioni``, ``RSS``, ``Informazioni utente``, ``Password`` e ``Regole di tagging``. - -Impostazioni ------------- - -Tema -~~~~ - -wallabag é personalizzabile. Potete scegliere il vostro tema preferito qui. Il tema di default é ``Material``, é il tema usato nelle immagini della documentazione. - -Oggetti per pagina -~~~~~~~~~~~~~~~~~~ - -Potete cambiare il numero di articoli mostrati su ogni pagina. - -Velocitá di lettura -~~~~~~~~~~~~~~~~~~~ - -wallabag calcola un tempo di lettura per ogni articolo. Potete definire qui, grazie a questa lista, se siete dei lettori lenti of veloci. wallabag ricalcolerá il tempo di lettura per ogni articolo. - -Lingua -~~~~~~ - -Potete cambiare la lingua dell'interfaccia di wallabag. - -RSS ---- - -wallabag offre feed RSS per ogni stato dell'articolo: non letto, preferito e archiviato. - -Per prima cosa dovete creare un token personale: cliccate su ``Crea il tuo token``. É possibile cambiare il proprio token cliccando su ``Rigenera il tuo token``. - -Ora avrete tre link, uno per ogni stato: aggiungeteli al vostro lettore RSS preferito. - -Potete anche definire quanti articoli volete nel vostro feed RSS (valore di default: 50)- - -Informazioni dell'utente ------------------------- - -Potete cambiare il vostro nome, il vostro indirizzo email e abilitare l'``Autenticazione a due fattori``. - -Autenticazione a due fattori (2FA) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - L'autenticazione a due fattori (conosciuta anche come 2FA) é una tecnologia brevettata nel 1984 che offre l'identificazione degli utenti tramite una combinazione di due componenti differenti. - - https://it.wikipedia.org/wiki/Autenticazione_a_due_fattori - -**Attenzione**: abilitare la 2FA dall'interfaccia di configurazione è possibile solamente se ciò è stato abilitato precedentemente in app/config/parameters.yml impostando la proprietà twofactor_auth su true (non dimenticate di eseguire il comando `php bin/console cache:clear -e=prod` dopo la modifica). - -Se abilitate la 2FA, ogni volta che vogliate accedere a wallabag, riceverete un codice via email. Dovrete inserire il codice nel seguente modulo. - -.. image:: ../../img/user/2FA_form.png - :alt: Two factor authentication - :align: center - -Se non volete ricevere il codice ogni volta che vogliate accedere, potete spuntare la casella ``I'm on a trusted computer``: wallabag vi ricorderá per 15 giorni. - -Password --------- - -Qui potete cambiare la password (minimo 8 caratteri) - -Regole di tagging ------------------ - -Se volete assegnare un tag ai nuovi articoli, questa parte della configurazione fa per voi. - -Cosa significa « regole di tagging » ? -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Sono regole usate da wallabag per etichettare i nuovi articoli. Ogni volta che un nuovo articolo viene aggiunto, verranno usate tutte le regole di tagging per aggiungere le etichette che avete configurato, risparmiandovi quindi il lavoro di classificare manualmente i vostri articoli. - -Come le uso? -~~~~~~~~~~~~ - -Immaginiamo che vogliate taggare un contenuto come *« lettura corta »* quando il tempo di lettura è inferiore ai 3 minuti. In questo caso, dovreste mettere « readingTime <= 3 » nel campo **Regola** -e *« lettura corta »* nel campo **Tag**. Molte etichette possono essere aggiunte simultaneamente separandole con una virgola: *« lettura corta, da leggere »*. -Si possono scrivere regole complesse usando gli operatori predefiniti: -se *« readingTime >= 5 AND domainName = "github.com" »* allora etichetta come *« lettura lunga, github »*. - -Quali variabili ed operatori posso usare per scrivere le regole? - -I seguenti operatori e variabili possono essere usati per creare regole di tagging (attenzione, per alcuni valori, dovete aggiungere le virgolette, per esempio ``language = "en"``): - -=========== ============================================== ========= =========== -Variabile Significato Operatore Significato ------------ ---------------------------------------------- --------- ----------- -title Titolo dell'articolo <= Minore di… -url URL dell'articolo < Strettamente minore di… -isArchived Se l'articolo é archiviato o no => Maggiore di… -isStarred Se l'articolo é preferito o no > Strettamente maggiore di… -content Il contenuto dell'articolo = Uguale a… -language La lingua dell'aritcolo != Diverso da… -mimetype The entry's mime-type OR Una regola o l'altra -readingTime Il tempo di lettura dell'articolo stimato AND Una regola e l'altra -domainName Il nome del dominio dell'articolo matches Vede se un soggetto corrisponde alla ricerca (indipendentemente dal maiuscolo o minuscolo). Esempio: titolo corrisponde a "football" -=========== ============================================== ======== ========== diff --git a/docs/it/user/configuring_mobile.rst b/docs/it/user/configuring_mobile.rst deleted file mode 100644 index f0ea54f1..00000000 --- a/docs/it/user/configuring_mobile.rst +++ /dev/null @@ -1,12 +0,0 @@ -Configurare le app mobili in modo che funzionino con wallabag -============================================================= - -Passi per configurare l'app ---------------------------- - -- Per prima cosa *create un nuovo client* nella sezione ``API clients management``. Il nome della vostra app non importa quanto il ``Client ID`` ed il ``Client secret``. Segnatevi questi due valori. -- Quando scaricate un'app mobile, questa chiederà l'indirizzo del server dell'istanza di wallabag ospitata. Per esempio, con wallabag.it questo è: ``https://app.wallabag.it``. -- Chiederà anche i sopracitati ``Client ID`` e ``secret``. Si prega di inserirli nei campi di testo, quando richiesto. -- Infine dovrete fornire il vostro ``username`` e la vostra ``password``. Queste sono le stesse credenziali che usate per fare il login in wallabag. - -Si prega anche di guardare la pagina `Android `_. Specialmente la sezione *Limiti conosciuti*. diff --git a/docs/it/user/create_account.rst b/docs/it/user/create_account.rst deleted file mode 100644 index c569962d..00000000 --- a/docs/it/user/create_account.rst +++ /dev/null @@ -1,38 +0,0 @@ -Creazione di un account e autenticazione -======================================== - -Registrazione -------------- - -Sulla pagina di login, cliccate sul bottone ``Register`` - -.. image:: ../../img/user/registration_form.png - :alt: Registration form - :align: center - -Dovrete completare il modulo. Assicuratevi di inserire un indirizzo email valido, quindi vi invieremo un'email di attivazione. - -.. image:: ../../img/user/sent_email.png - :alt: Email was sent to activate account - :align: center - -Controllate la vostra casella, in cui avrete una nuova mail con un link che assomiglia a questo ``http://wallabag/register/confirm/Ba19wokGovN-DdBQNfg4YgRkUQWRP4-k2g0Bk-hBTX4``. Cliccateci sopra per attivare il vostro account. - -Il vostro account é ora attivo - -.. image:: ../../img/user/activated_account.png - :alt: Welcome on board! - :align: center - -Login ------ - -Il vostro account é ora abilitato, congratulazioni! - -Se siete sul vostro computer personale e volete restare connessi, spuntate la casella ``Keep me logged in``: wallabag vi ricorderá per un anno. - -.. image:: ../../img/user/login_form.png - :alt: Login form - :align: center - - diff --git a/docs/it/user/errors_during_fetching.rst b/docs/it/user/errors_during_fetching.rst deleted file mode 100644 index d92b83dc..00000000 --- a/docs/it/user/errors_during_fetching.rst +++ /dev/null @@ -1,37 +0,0 @@ - -Errori durante l'ottenimento degli articoli -=========================================== - -Perché l'ottenimento di un articolo fallisce? ---------------------------------------------- - -Ci possono essere varie ragioni: - -- problema del network -- wallabag non può ottenere il contenuto a causa della struttura del sito web - -Potete provare a risolvere il problema da soli ( in modo che noi possiamo concentrarci nel migliorare wallabag internamente invece di scrivere siteconfig -:) ). - -Potete provare a vedere se funziona qui: `http://f43.me/feed/test `_ (usa quasi lo stesso sistema per ottenere contenuto). - -Se funziona lì e non su wallabag, significa che c'è qualcosa all'interno di wallabag che causa il malfunzionamento del parser (difficile da aggiustare: si prega di riportare il problema). - -Se non funziona, provate a estrarre un site config usando: `http://siteconfig.fivefilters.org/ `_ (seleziona quale parte del contenuto é effettivamente contenuto). Potete `leggere prima questa documentazione `_. - -Potete testarlo sul sito **f43.m3**: cliccate su **Want to try a custom siteconfig?** e inseritvi il file generato in and put the generated file from siteconfig.fivefilters.org. - -Ripetete finché non avrete qualcosa di buono. - -Potete poi inviare una pull request a `https://github.com/fivefilters/ftr-site-config `_ il quale é il repository ufficiale per i file siteconfig. - -Come posso provare a riottenere questo articolo? ------------------------------------------------- - -Se wallabag ha fallito a ottenere l'articolo, potete cliccare sul bottone di ricaricamento (il terzo bottone nella figura sottostante). - -.. image:: ../../img/user/refetch.png - :alt: Refetch content - :align: center - - diff --git a/docs/it/user/faq.rst b/docs/it/user/faq.rst deleted file mode 100644 index decdfab1..00000000 --- a/docs/it/user/faq.rst +++ /dev/null @@ -1,53 +0,0 @@ -Domande frequenti -================= - -Durante l'installazione ho riscontrato l'errore ``Error Output: sh: 1: @post-cmd: not found`` ---------------------------------------------------------------------------------------------- - -Sembra che ci sia un problema con la vostra installazione di ``composer``. Provate a disinstallarlo e reinstallarlo. - -`Leggete la documentazione su composer per sapere come installarlo -`__. - -Non riesco a convalidare il modulo di registrazione ---------------------------------------------------- - -Assicuratevi che tutti i campi siano riempiti correttamente: - -* indirizzo email valido -* stessa password nei due campi - -Non riesco a ricevere la mia mail di attivazione ------------------------------------------------- - -Siete sicuri che il vostro indirizzo email sia corretto? avete controllato la cartella di spam? - -Se ancora non vedete la mail di attivazione, assicuratevi di aver installato e configurato a dovere un mail transfer agent. Assicuratevi di includere una regola del firewall per SMTP. Per esempio, se usate firewalld: - -:: - - firewall-cmd --permanent --add-service=smtp - firewall-cmd --reload - -Infine, se avete SELinux abilitato, impostate la seguente regola: - -``setsebool -P httpd_can_sendmail 1`` - -Quando clicco il link di attivazione, mi appare questo messaggio: ``L'utente con token di conferma "DtrOPfbQeVkWf6N" non esiste``. ----------------------------------------------------------------------------------------------------------------------------------- - -Avete giá attivato il vostro account oppure l'URL dell'email di attivazione é sbagliato. - -Ho dimenticato la mia password ------------------------------- - -Potete ripristinare la password cliccando il link``Hai dimenticato la password?``, nella pagina di login. Quindi, riempite il modulo con la vostra email o il vostro username e riceverete un'email per ripristinare la vostra password. - -Ho riscontrato l'errore ``failed to load external entity`` cercando di installare wallabag ------------------------------------------------------------------------------------------- - -Come descritto `qui `_, modificate il vostro file ``web/app.php`` ed aggiungete questa linea: ``libxml_disable_entity_loader(false);`` sulla linea 5. - -Questo é un bug di Doctrine / PHP, non possiamo farci nulla. - - diff --git a/docs/it/user/filters.rst b/docs/it/user/filters.rst deleted file mode 100644 index 105cb1d6..00000000 --- a/docs/it/user/filters.rst +++ /dev/null @@ -1,45 +0,0 @@ -Trovate i vostri articoli grazie ai filtri ------------------------------------------- - -Per trovare facilmente gli articoli, potete usare i filtri. - -.. image:: ../../img/user/topbar.png - :alt: Top bar - :align: center - -Tutti questi filtri possono essere combinati. - -.. image:: ../../img/user/filters.png - :alt: Combine all filters - :align: center - -Stato ------ - -Usate queste caselle per trovare articoli archiviati o preferiti. - -Immagine di anteprima ---------------------- - -Selezionate questo filtro se volete trovare articoli con un'immagine di anteprima. - -Lingua ------- - -wallabag (attraverso graby) puó individuare la lingua di un articolo. É facile per voi trovare articoli scritti in una lingua specifica. - -Tempo di lettura ----------------- - -wallabag stima quanto tempo é necessario per leggere un articolo. Con questo filtro potete, per esempio, trovare articoli con un tempo di lettura compreso fra 2 e 5 minuti. - -Nome di dominio ---------------- - -Grazie a questo filtro potete trovare articoli dallo stesso nome di dominio. Per esempio, scrivete in questo campo ``bbc.co.uk`` per trovare gli articoli di questo sito. - -Data di creazione ------------------ - -Quando salvate un articolo, wallabag memorizza la data corrente. ció é comodo, per esempio, per trovare articoli salvati tra il primo ed il 31 di Gennaio. - diff --git a/docs/it/user/import.rst b/docs/it/user/import.rst deleted file mode 100644 index c249acfd..00000000 --- a/docs/it/user/import.rst +++ /dev/null @@ -1,115 +0,0 @@ -Migrare da... -============= - -In wallabag 2.x, potete importare dati da: - -- `Pocket <#id1>`_ -- `Readability <#id2>`_ -- `Instapaper <#id4>`_ -- `wallabag 1.x <#id6>`_ -- `wallabag 2.x <#id7>`_ - -Abbiamo anche sviluppato `uno script per eseguire migrazioni tramite la linea di comando <#import-via-command-line-interface-cli>`_. - -Poiché le importazioni possono richiedere molto tempo, abbiamo sviluppato un sistema di compiti asincroni. *inserisci qui link una volta tradotto articolo su asynchronous* - -Pocket ------- - -Creare una nuova applicazione su Pocket -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Per importare dati da Pocket usiamo l'API di Pocket. Dovete creare una nuova applicazione sul loro sito per sviluppatori per continuare. - -* Create una nuova applicazione `sul sito per sviluppatori `_ -* Riempite i campi richiesti: nome dell'applicazione, descrizione dell'applicazione, permessi (solo **retrieve**), piattaforma (**web**), accettate i termini di servizio ed inviate la vostra nuova applicazione - -Pocket vi dará una **Consumer Key** (per esempio, `49961-985e4b92fe21fe4c78d682c1`). Dovete configurare la ``pocket_consumer_key`` dal menu ``Config``. - -Ora é tutto pronto per migrare da Pocket. - -Importate i vostri dati su wallabag 2.x -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Cliccate sul link `Importa` nel menu, su `Importa contenuti` nella sezione Pocket e poi su ``Connetti a Pocket ed importa i dati`` - -Dovete autorizzare wallabag a interagire con il vostro account Pocket. -I vostri dati saranno importati. L'importazione di dati puó essere un processo esigente per il vostro server. - -Instapaper ----------- - -Esportate i vostri dati di Instapaper -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Sulla pagina delle impostazioni (`https://www.instapaper.com/user `_), cliccate su "Download .CSV file" nella sezione "Export". Verrá scaricato un file CSV (like ``instapaper-export.csv``). - -Importate i vostri dati in wallabag 2.x -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Cliccate sul link `Importa` sul menu, poi su `Importa contenuti` nella sezione di Instapaper, quindi selezionate il vostro file CSV e caricatelo. - -I vostri dati saranno importati. L'importazione di dati puó essere un'operazione esigente per il server. - -wallabag 1.x ------------- - -Se state usando wallabag 1.x, dovete esportare i dati prima di migrare a wallabag 2.x, poiché l'applicazione ed il suo database sono cambiati molto. Potete esportare i vostri dati dalla vostra vecchia installazione di wallabag sulla pagina Config di questa. - -.. image:: ../../img/user/export_v1.png - :alt: Exporting from wallabag v1 - :align: center - -.. nota:: - Se avete account multipli nella stessa istanza di wallabag, ogni utente dovrá esportare da v1 ed importare su v2. - -.. nota:: - Se riscontrate problemi durante l'importazione o l'esportazione, non esitate a `chiedere supporto `__. - -Quando avrete ottenuto il file json contenente i vostri articoli, potrete installare wallabag v2 seguendo, se necessario, `la procedura standard *link mancante*. - -Dopo aver creato un account utente sulla vostra nuova istanza di wallabag v2, dovete andare alla sezione `Importa` e selezionare `Importa da wallabag v1`. Selezionate il vostro file json e caricatelo. - -.. image:: ../../img/user/import_wallabagv1.png - :alt: Import from wallabag v1 - :align: center - -wallabag 2.x ------------- - -Dalla istanza di wallabag precedente sulla quale eravate prima, andate su `Tutti gli articoli`, poi esportate questi articoli come json. - -.. image:: ../../img/user/export_v2.png - :alt: Export depuis wallabag v2 - :align: center - -Dalla vostra nuova istanza di wallabag, create un account utente e cliccate sul link nel menu per procedere all'importazione. Scegliete di importare da wallabag v2 e selezionate il vostro file json per caricarlo. - -.. nota:: - Se riscontrate problemi durante l'importazione o l'esportazione, non esitate a `chiedere supporto `__. - -Importate dall'interfaccia a riga di comando (CLI) --------------------------------------------------- - -Se avete un accesso CLI al vostro server web, potete eseguire questo comando per importare ció che avete esportato da wallabag v1: - -:: - - bin/console wallabag:import 1 ~/Downloads/wallabag-export-1-2016-04-05.json --env=prod - -Rimpiazzate i valori: - -* ``1`` é l'identificatore utente nel database (l'ID del primo utente creato su wallabag é 1) -* ``~/Downloads/wallabag-export-1-2016-04-05.json`` é il percorso del file esportato da wallabag v1 - -Se volete segnare tutti questi articoli come giá letti, potete aggiungere l'opzione ``--markAsRead``. -Per importare un file di wallabag v2, dovete aggiungere l'opzione ``--importer=v2``. - -Come risultato avrete questo messaggio: - -:: - - Start : 05-04-2016 11:36:07 --- - 403 imported - 0 already saved - End : 05-04-2016 11:36:09 --- diff --git a/docs/it/user/installation.rst b/docs/it/user/installation.rst deleted file mode 100644 index 95d781e6..00000000 --- a/docs/it/user/installation.rst +++ /dev/null @@ -1,339 +0,0 @@ - - -Installa wallabag -================= - -Requisiti ---------- -wallabag é compatibile con PHP >= 5.5, incluso PHP 7. - -.. nota:: - - Per installare facilmente wallabag vi forniamo un Makefile, dunque avrete bisogno dello strumento make. - -wallabag utilizza un gran numero di librerie PHP per funzionare. Queste librerie vanno installate tramite uno strumento chiamato Composer. Dovete installarlo se non lo avete giá fatto e assicuratevi di usare la versione 1.2 ( se giá avete Composer, esegui il comando composer selfupdate). - -Installa Composer: - -:: - curl -s http://getcomposer.org/installer | php - -`Qui `__ puoi trovare istruzioni specifiche. - -Per far funzionare wallabag avrete anche bisogno delle seguenti estensioni. Alcune di queste dovrebbero essere giá attive nella vostra versione di PHP, per cui potrebbe non essere necessario installare tutti i pacchetti corrispondenti. - -- php-session -- php-ctype -- php-dom -- php-hash -- php-simplexml -- php-json -- php-gd -- php-mbstring -- php-xml -- php-tidy -- php-iconv -- php-curl -- php-gettext -- php-tokenizer -- php-bcmath - -wallabag usa PDO per connettersi, per cui avrete bisogno di uno dei seguenti: - --pdo_mysql --pdo_sqlite --pdo_pgsql - -E il corrispondente database server. - -Installazione -------------- - -Su un web server dedicato (raccomandato) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Per installare wallabag stesso dovete eseguire i seguenti comandi: - -:: - - git clone https://github.com/wallabag/wallabag.git - cd wallabag && make install - -Per attivare il server incorporato di PHP e verificare che l’installazione sia andata a buon fine potete eseguire: - -:: - - make run - -E accedere a wallabag all’indirizzo http://ipdeltuoserver:8000 - -.. consiglio:: - - Per definire i parametri con variabili d’ambiente é necessario impostare queste ultime con il prefisso ``SYMFONY_``. Per esempio, ``SYMFONY__DATABASE_DRIVER``. Puoi guardare la `documentazione di Symfony `__ per maggiori informazioni. - -A proposito di hosting condiviso -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Offriamo un pacchetto con tutte le dipendenze incluse. La configurazione di default usa SQLite per il database. Se volete cambiare queste impostazioni, modificate app/config/parameters.yml. - -Abbiamo giá creato un utente: il login e la password sono wallabag. - -.. attenzione: - - Con questo pacchetto, wallabag non controlla le estensioni obbligatorie usate nell’applicazione (questi controlli sono fatti durante ``composer install`` quando hai un server web dedicato, vedi sopra). - -Eseguite questo comando per scaricare ed estrarre il pacchetto piú aggiornato: - -.. code-block:: bash - - wget https://wllbg.org/latest-v2-package && tar xvf latest-v2-package - -Troverete il `hash md5 del pacchetto piú aggiornato sul nostro sito `_. - -Ora leggete la seguente documentazione per creare il vostro host virtuale poi accedete al vostro wallabag. Se avete cambiato la configurazione del database per usare MySQL o PostrgreSQL, dovrete creare un utente con il comando php bin/console wallabag:install --env=prod . - -Installazione con Docker -~~~~~~~~~~~~~~~~~~~~~~~~ - -Offriamo un’immagine Docker per installare wallabag facilmente. Guarda la nostra repository su `Docker Hub `__ per maggiori informazioni. - -Comando per avviare il container - -.. code-block:: bash - - docker pull wallabag/wallabag - - -Host virtuali -------------- - -Configurazione su Apache -~~~~~~~~~~~~~~~~~~~~~~~~ - -Non dimenticate di attivare la mod *rewrite* di Apache - -.. code-block:: bash - - a2enmod rewrite && systemctl reload apache2 - -Assumendo che voi abbiate installato wallabag nella cartella ``/var/www/wallabag`` e che vogliate usare PHP come un modulo Apache, ecco un vhost per l’applicazione: - -:: - - - ServerName domain.tld - ServerAlias www.domain.tld - - DocumentRoot /var/www/wallabag/web - - AllowOverride None - Order Allow,Deny - Allow from All - - - Options -MultiViews - RewriteEngine On - RewriteCond %{REQUEST_FILENAME} !-f - RewriteRule ^(.*)$ app.php [QSA,L] - - - - # uncomment the following lines if you install assets as symlinks - # or run into problems when compiling LESS/Sass/CoffeScript assets - # - # Options FollowSymlinks - # - - # optionally disable the RewriteEngine for the asset directories - # which will allow apache to simply reply with a 404 when files are - # not found instead of passing the request into the full symfony stack - - - RewriteEngine Off - - - ErrorLog /var/log/apache2/wallabag_error.log - CustomLog /var/log/apache2/wallabag_access.log combined - - -Dopo aver riavviato o ricaricato Apache dovreste essere in grado di accedere a wallabag tramite l’indirizzo http://domain.tld. - -Configurazione su Nginx -~~~~~~~~~~~~~~~~~~~~~~~ - -Assumendo che abbiate installato wallabag nella cartella ``/var/www/wallabag``, ecco una ricetta per l’applicazione: - -:: - - server { - server_name domain.tld www.domain.tld; - root /var/www/wallabag/web; - - location / { - # try to serve file directly, fallback to app.php - try_files $uri /app.php$is_args$args; - } - location ~ ^/app\.php(/|$) { - fastcgi_pass unix:/var/run/php5-fpm.sock; - 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; - } - - # return 404 for all other php files not matching the front controller - # this prevents access to other php files you don't want to be accessible. - location ~ \.php$ { - return 404; - } - - error_log /var/log/nginx/wallabag_error.log; - access_log /var/log/nginx/wallabag_access.log; - } - - -Dopo aver riavviato o ricaricato Nginx dovreste essere in grado di accedere a wallabag tramite l’indirizzo http://domain.tld. - -Configurazione su lighttpd -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Assumendo che abbiate installato wallabag nella cartella /var/www/wallabag, ecco una ricetta per l’applicazione (modificate il vostro file lighttpd.conf e incollatevi questa configurazione): - -:: - - server.modules = ( - "mod_fastcgi", - "mod_access", - "mod_alias", - "mod_compress", - "mod_redirect", - "mod_rewrite", - ) - server.document-root = "/var/www/wallabag/web" - server.upload-dirs = ( "/var/cache/lighttpd/uploads" ) - server.errorlog = "/var/log/lighttpd/error.log" - server.pid-file = "/var/run/lighttpd.pid" - server.username = "www-data" - server.groupname = "www-data" - server.port = 80 - server.follow-symlink = "enable" - index-file.names = ( "index.php", "index.html", "index.lighttpd.html") - url.access-deny = ( "~", ".inc" ) - static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" ) - compress.cache-dir = "/var/cache/lighttpd/compress/" - compress.filetype = ( "application/javascript", "text/css", "text/html", "text/plain" ) - include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port - include_shell "/usr/share/lighttpd/create-mime.assign.pl" - include_shell "/usr/share/lighttpd/include-conf-enabled.pl" - dir-listing.activate = "disable" - - url.rewrite-if-not-file = ( - "^/([^?]*)(?:\?(.*))?" => "/app.php?$1&$2", - "^/([^?]*)" => "/app.php?=$1", - ) - - -Diritti di accesso alle cartelle del progetto ---------------------------------------------- - -Ambiente di test -~~~~~~~~~~~~~~~~ - -Quando vorremo solamente testare wallabag, eseguiremo il comando ``make run`` per avviare la nostra istanza di wallabag e tutto funzionerá correttamente poiché l’utente che ha iniziato il progetto puó accedere alla cartella corrente senza problemi. - -Ambiente di produzione -~~~~~~~~~~~~~~~~~~~~~~ - -Non appena useremo Apache o Nginx per accedere alla nostra istanza di wallabag, e non avviandola con il comando ``make run``, dovremo aver cura di concedere i giusti diritti sulle giuste cartelle per far rimanere sicure tutte le cartelle del progetto. - -Per fare ció, il nome della cartella, conosciuta come ``DocumentRoot`` (per Apache) o ``root`` (per Nginx), deve essere assolutamente accessibile all’utente Apache/Nginx. Il suo nome è generalmente ``www-data``, ``apache`` o ``nobody`` (dipendendo dal sistema Linux utilizzato). - -Quindi la cartella ``/var/www/wallabag/web`` deve essere accessibile da quest’ultimo. Questo tuttavia potrebbe non essere sufficiente se solo ci importa di questa cartella poiché potremmo incontrare una pagina bianca o un errore 500 quando cerchiamo di accedere alla homepage del progetto. - -Questo é dato dal fatto che dovremo concedere gli stessi diritti di accesso di ``/var/www/wallabag/web`` alla cartella ``/var/www/wallabag/var`` . Risolveremo quindi il problema con il seguente comando: - -.. code-block:: bash - - chown -R www-data:www-data /var/www/wallabag/var - - -Deve essere tutto uguale per le seguenti cartelle: - -* /var/www/wallabag/bin/ -* /var/www/wallabag/app/config/ -* /var/www/wallabag/vendor/ -* /var/www/wallabag/data/ - -inserendo - -.. code-block:: bash - - chown -R www-data:www-data /var/www/wallabag/bin - chown -R www-data:www-data /var/www/wallabag/app/config - chown -R www-data:www-data /var/www/wallabag/vendor - chown -R www-data:www-data /var/www/wallabag/data/ - -Altrimenti prima o poi incontreremo questi messaggi di errore: - -.. code-block:: bash - - Unable to write to the "bin" directory. - file_put_contents(app/config/parameters.yml): failed to open stream: Permission denied - file_put_contents(/.../wallabag/vendor/autoload.php): failed to open stream: Permission denied - -Regole aggiuntive per SELinux -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -se SELinux é abilitato sul vostro sistema, dovrete configurare contesti aggiuntivi in modo che wallabag funzioni correttamente. Per controllare se SELinux é abilitato, semplicemente inserisci ció che segue: - -``getenforce`` - -Questo mostrerá ``Enforcing`` se SELinux é abilitato. Creare un nuovo contesto coinvolge la seguente sintassi: - -``semanage fcontext -a -t `` - -Per esempio: - -``semanage fcontext -a -t httpd_sys_content_t "/var/www/wallabag(/.*)?"`` - -Questo applicherá ricorsivamente il constesto httpd_sys_content_t alla cartella wallabag e a tutti i file e cartelle sottostanti. Sono necessarie le seguenti regole: - -+-----------------------------------+----------------------------+ -| Percorso completo | Contesto | -+===================================+============================+ -| /var/www/wallabag(/.*)? | ``httpd_sys_content_t`` | -+-----------------------------------+----------------------------+ -| /var/www/wallabag/data(/.*)? | ``httpd_sys_rw_content_t`` | -+-----------------------------------+----------------------------+ -| /var/www/wallabag/var/logs(/.*)? | ``httpd_log_t`` | -+-----------------------------------+----------------------------+ -| /var/www/wallabag/var/cache(/.*)? | ``httpd_cache_t`` | -+-----------------------------------+----------------------------+ - -Dopo aver creato questi contesti, inserite ció che segue per applicare le vostre regole: - -``restorecon -R -v /var/www/wallabag`` - -Potrete controllare i contesti in una cartella scrivendo ``ls -lZ`` e potrete vedere tutte le regole correnti con ``semanage fcontext -l -C``. - -Se state installando il pacchetto latest-v2-package, é necessaria un'ulteriore regola durante la configurazione iniziale: - -``semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/wallabag/var"`` - -Dopo che siate acceduti con successo al vostro wallabag e abbiate completato la configurazione iniziale, questo contesto puó essere rimosso: - -:: - - semanage fcontext -d -t httpd_sys_rw_content_t "/var/www/wallabag/var" - retorecon -R -v /var/www/wallabag/var diff --git a/docs/it/user/parameters.rst b/docs/it/user/parameters.rst deleted file mode 100644 index da9555e1..00000000 --- a/docs/it/user/parameters.rst +++ /dev/null @@ -1,93 +0,0 @@ -Qual'é il significato dei parametri? -==================================== - -File `parameters.yml` di default --------------------------------- - -Ecco l'ultima versione del file `app/config/parameters.yml` di default. Assicuratevi che la vostra rispetti questa. -Se non sapete quale parametro dovete impostare, si prega di lasciare quello di default. - -.. code-block:: yml - - parameters: - database_driver: pdo_sqlite - database_host: 127.0.0.1 - database_port: null - database_name: symfony - database_user: root - database_password: null - database_path: '%kernel.root_dir%/../data/db/wallabag.sqlite' - database_table_prefix: wallabag_ - database_socket: null - mailer_transport: smtp - mailer_host: 127.0.0.1 - mailer_user: null - mailer_password: null - locale: en - secret: ovmpmAWXRCabNlMgzlzFXDYmCFfzGv - twofactor_auth: true - twofactor_sender: no-reply@wallabag.org - fosuser_registration: true - fosuser_confirmation: true - from_email: no-reply@wallabag.org - rss_limit: 50 - rabbitmq_host: localhost - rabbitmq_port: 5672 - rabbitmq_user: guest - rabbitmq_password: guest - redis_scheme: tcp - redis_host: localhost - redis_port: 6379 - redis_path: null - -Significato di ogni parametro ------------------------------ - -.. csv-table:: Parametri del database - :header: "nome", "default", "descrizione" - - "database_driver", "pdo_sqlite", "Dovrebbe essere pdo_sqlite o pdo_mysql o pdo_pgsql" - "database_host", "127.0.0.1", "Host del vostro database (generalmente localhost o 127.0.0.1)" - "database_port", "~", "Porta del vostro database (potete lasciare ``~`` per usare quella di default)" - "database_name", "symfony", "Nome del vostro database" - "database_user", "root", "L'utente che puó scrivere su questo database" - "database_password", "~", "Password di quell'utente" - "database_path", "``""%kernel.root_dir%/../data/db/wallabag.sqlite""``", "Solo per SQLite, definite dove mettere il file del database. Lasciatelo vuoto se usate un altro database" - "database_table_prefix", "wallabag_", "Tutte le tavole di wallabag avranno quella stringa come prefisso. Potete includere un ``_`` per maggior chiarezza" - "database_socket", "null", "Se il vostro database usa un socket al posto di tcp, inserite il percorso del socket (altri parametri di connessione saranno ignorati)" - -.. csv-table:: Configurazione per inviare email da wallabag - :header: "nome", "default", "descrizione" - - "mailer_transport", "smtp", "Il metodo di trasporto esatto usato per consegnare email. Valori validi sono: smtp, gmail, mail, sendmail, null (ció disattiva il mailer)" - "mailer_host", "127.0.0.1", "L'host al quale connettersi quando si usa smtp come metodo di trasporto." - "mailer_user", "~", "Lo username quando si usa smtp come metodo di trasporto." - "mailer_password", "~", "La password quando si usa smtp come metodo di trasporto." - -.. csv-table:: Altre opzioni di wallabag - :header: "nome", "default", "descrizione" - - "locale", "en", "Lingua di default della vostra istanza di wallabag (come en, fr, es, etc.)" - "secret", "ovmpmAWXRCabNlMgzlzFXDYmCFfzGv", "Questa é una stringa che dovrebbe essere unica per la vostra applicazione ed é usata comunemente per aggiungere piú entropia alle operazioni di sicurezza." - "twofactor_auth", "true", "true per abilitare l'autenticazione a due fattori" - "twofactor_sender", "no-reply@wallabag.org", "Email del mittente per ricevere il codice a due fattori" - "fosuser_registration", "true", "true per abilitare la registrazione pubblica" - "fosuser_confirmation", "true", "true per inviare una mail di conferma per ogni registrazione" - "from_email", "no-reply@wallabag.org", "Indirizzo email usato nel campo Da: in ogni email" - "rss_limit", "50", "Limite per i feed RSS" - -.. csv-table:: Configurazione di RabbitMQ - :header: "nome", "default", "descrizione" - - "rabbitmq_host", "localhost", "Host del vostro RabbitMQ" - "rabbitmq_port", "5672", "Porta del vostro RabbitMQ" - "rabbitmq_user", "guest", "Utente che puó leggere le code" - "rabbitmq_password", "guest", "Password di quell'utente" - -.. csv-table:: Configurazione di Redis - :header: "nome", "default", "descrizione" - - "redis_scheme", "tcp", "Specifica il protocollo da usare per comunicare con una istanza di Redis. Valori validi sono: tcp, unix, http" - "redis_host", "localhost", "IP o hostname del server bersaglio (ignorato per lo schema unix)" - "redis_port", "6379", "Porta TCP/IP del server bersaglio (ignorato per lo schema unix)" - "redis_path", "null", "Percorso del file domain socket di UNIX usato quando ci si connette a Redis usando domain socket di UNIX" diff --git a/docs/it/user/upgrade.rst b/docs/it/user/upgrade.rst deleted file mode 100644 index e1c3f041..00000000 --- a/docs/it/user/upgrade.rst +++ /dev/null @@ -1,102 +0,0 @@ -Aggiornate la vostra installazione di wallabag -============================================== - -Troverete qui i differenti modi per aggiornare il vostro wallabag: - -- `da 2.0.x a 2.1.1 <#upgrade-from-2-0-x-to-2-1-1>`_ -- `da 2.1.x a 2.1.y <#upgrading-from-2-1-x-to-2-1-y>`_ -- `da 1.x a 2.x <#from-wallabag-1-x>`_ - -Aggiornate da 2.0.x a 2.1.1 ---------------------------- - -.. attenzione:: - - prima di questa migrazione, se avete configurato l'importazione di Pocket aggiungendo la vostra consumer key nelle Impostazioni interne, si prega di farne un backup: dovrete aggiungere questa nella pagina di configurazione dopo l'aggiornamento. - -Aggiornamento su un web server dedicato -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -: - - rm -rf var/cache/* - git fetch origin - git fetch --tags - git checkout 2.1.1 --force - SYMFONY_ENV=prod composer install --no-dev -o --prefer-dist - php bin/console doctrine:migrations:migrate --env=prod - php bin/console cache:clear --env=prod - -Aggiornamento su un hosting condiviso -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Fate un backup del file ``app/config/parameters.yml``. -Scaricate la versione 2.1.1 di wallabag: - -.. code-block:: bash - - wget http://framabag.org/wallabag-release-2.1.1.tar.gz && tar xvf wallabag-release-2.1.1.tar.gz - -(hash md5 del pacchetto 2.1.1: ``9584a3b60a2b2a4de87f536548caac93``) - -Estraete l'archivio nella vostra cartella di wallabag e sostituite ``app/config/parameters.yml`` con il vostro. - -Controllate che il vostro ``app/config/parameters.yml`` contenga tutti i parametri richiesti. Potete trovare qui la documentazione sui parametri *link mancante*. - -Se usate SQLite, dovete anche copiare la vostra cartella ``data/`` dentro la nuova installazione. - -Svuotate la cartella ``var/cache``. - -Dovete eseguire delle query di SQL per aggiornare il vostro database. Assumiamo che il prefisso della tabella sia ``wallabag_`` e che il database sia MySQL: - -.. code-block:: sql - - ALTER TABLE `wallabag_entry` ADD `uuid` LONGTEXT DEFAULT NULL; - INSERT INTO `wallabag_craue_config_setting` (`name`, `value`, `section`) VALUES ('share_public', '1', 'entry'); - ALTER TABLE `wallabag_oauth2_clients` ADD name longtext COLLATE 'utf8_unicode_ci' DEFAULT NULL; - INSERT INTO `wallabag_craue_config_setting` (`name`, `value`, `section`) VALUES ('import_with_redis', '0', 'import'); - INSERT INTO `wallabag_craue_config_setting` (`name`, `value`, `section`) VALUES ('import_with_rabbitmq', '0', 'import'); - ALTER TABLE `wallabag_config` ADD `pocket_consumer_key` VARCHAR(255) DEFAULT NULL; - DELETE FROM `wallabag_craue_config_setting` WHERE `name` = 'pocket_consumer_key'; - -Aggiornamento da 2.1.x a 2.1.y ------------------------------- - -Aggiornamento su un web server dedicato -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Per aggiornare la vostra installazione di wallabag ed ottenere l'ultima versione, eseguite il seguente comando nella vostra cartella wallabag: - -:: - - make update - -Aggiornamento su un hosting condiviso -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Fate un backup del file ``app/config/parameters.yml``. - -Scaricate l'ultima versione di wallabag: - -. code-block:: bash - - wget https://wllbg.org/latest-v2-package && tar xvf latest-v2-package - -Troverete il `hash md5 dell'ultima versione del pacchetto sul nostro sito `_. - -Estraete l'archivio nella vostra cartella di wallabag e rimpiazzate ``app/config/parameters.yml`` con il vostro. - -Controllate che il vostro ``app/config/parameters.yml`` contenga tutti i parametri richiesti. - -Potete trovare qui la documentazione sui parametri *link mancante*. - -Se usate SQLite, dovete anche copiare la vostra cartella ``data/`` dentro la nuova installazione. - -Svuotate la cartella ``var/cache``. - -Da wallabag 1.x ---------------- - -Non esiste uno script automatico per aggiornare da wallabag 1.x a wallabag 2.x. Dovete: - -- esportare i vostri dati -- installare wallabag 2.x (leggete la documentazione a proposito dell'installazione *link mancante*) -- importate i dati in questa nuova installazione (leggete la documentazione a proposito dell'importazione) diff --git a/package.json b/package.json index 209c46c6..e6659bd3 100644 --- a/package.json +++ b/package.json @@ -1,34 +1,13 @@ { "name": "wallabag", - "version": "2.1.0", + "version": "2.2.2", "description": "wallabag is a self hostable application for saving web pages", - "main": "index.js", "private": true, "directories": { "doc": "docs" }, "engines": { - "node": ">0.12" - }, - "browser": { - "jquery": "./node_modules/jquery/dist/jquery.js", - "jQuery": "./node_modules/jquery/dist/jquery.js", - "materialize": "./node_modules/materialize-css/bin/materialize.js" - }, - "browserify-shim": { - "jquery": { - "exports": "$" - }, - "materialize": "materialize", - "jquery-ui": { - "depends": "jquery", - "exports": null - } - }, - "browserify": { - "transform": [ - "browserify-shim" - ] + "node": ">4.8" }, "repository": { "type": "git", @@ -57,52 +36,49 @@ "url": "https://github.com/wallabag/wallabag/issues" }, "devDependencies": { + "autoprefixer": "^7.1.1", + "babel-core": "^6.24.1", + "babel-eslint": "^7.2.3", + "babel-loader": "^7.0.0", + "babel-preset-env": "^1.4.0", + "css-loader": "^0.28.0", + "eslint": "^4.0.0", + "eslint-config-airbnb-base": "^11.1.3", + "eslint-loader": "^1.7.1", + "eslint-plugin-import": "^2.2.0", + "extract-text-webpack-plugin": "^2.1.0", + "file-loader": "^0.11.0", + "lato-font": "^3.0.0", + "node-sass": "^4.5.2", + "postcss-loader": "^2.0.5", + "sass-loader": "^6.0.3", + "style-loader": "^0.18.2", + "stylelint": "^7.9.0", + "stylelint-config-standard": "^16.0.0", + "stylelint-webpack-plugin": "^0.7.0", + "url-loader": "^0.5.8", + "webpack": "^2.3.2", + "webpack-dev-server": "^2.4.4", + "webpack-manifest-plugin": "^1.1.0", + "webpack-merge": "^4.1.0" + }, + "dependencies": { "annotator": "git://github.com/wallabag/annotator.git#0f076c7d371ed25eb0793346f46982d90f2c4c85", - "autoprefixer": "^6.3.6", - "babel-eslint": "^6.1.2", - "babel-preset-es2015": "^6.14.0", - "babelify": "^7.3.0", - "browserify": "^13.0.0", - "browserify-shim": "^3.8.12", - "cssnano": "^3.5.2", - "es6-promise": "^3.2.1", - "eslint": "^3.7.1", - "eslint-config-airbnb-base": "^8.0.0", - "eslint-plugin-import": "^1.16.0", - "grunt": ">=0.4.0", - "grunt-browserify": "^5.0.0", - "grunt-cli": "^1.2.0", - "grunt-contrib-clean": "^1.0.0", - "grunt-contrib-concat": "^1.0.0", - "grunt-contrib-copy": "^1.0.0", - "grunt-contrib-symlink": "^1.0.0", - "grunt-contrib-uglify": "^1.0.0", - "grunt-contrib-watch": "^1.0.0", - "grunt-eslint": "^19.0.0", - "grunt-postcss": "^0.8.0", - "grunt-stylelint": "^0.6.0", - "hammerjs": "^2.0.6", - "icomoon-free-npm": "0.0.0", - "jquery": "^2.2.4", - "jquery-ui-browserify": "^1.11.0-pre-seelio", + "hammerjs": "^2.0.8", + "icomoon-free-npm": "^0.0.0", + "jquery": "^2.1.4", "jquery.cookie": "^1.4.1", - "jquery.tinydot": "^0.2.1", - "load-grunt-tasks": "^3.4.1", - "material-design-icons-iconfont": "^3.0.0", - "materialize-css": "0.97.5", - "npm": "^3.8.3", - "pickadate": "^3.5.6", - "pixrem": "^3.0.0", - "postcss-cssnext": "^2.5.1", - "prismjs": "^1.4.1", - "ptsans-npm-webfont": "0.0.4", - "roboto-fontface": "^0.6.0", - "stylelint": "^7.3.1", - "stylelint-config-standard": "^13.0.2", - "through": "^2.3.8" + "jr-qrcode": "^1.0.7", + "material-design-icons-iconfont": "^3.0.3", + "materialize-css": "^0.98.1", + "mousetrap": "^1.6.0", + "ptsans-npm-webfont": "^0.0.4", + "roboto-fontface": "^0.7.0", + "waypoints": "^4.0.1" }, - "dependencies": { - "jr-qrcode": "^1.0.5", - "mousetrap": "^1.6.0" + "scripts": { + "watch": "./node_modules/.bin/webpack-dev-server --env=dev", + "build:dev": "./node_modules/.bin/webpack --env=dev", + "build:prod": "./node_modules/.bin/webpack --env=prod" } } diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 3216b6ff..8f5285e6 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -30,4 +30,8 @@ + + + + diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 00000000..5d146314 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,7 @@ +module.exports = { + plugins: function () { + return [ + require('autoprefixer'), + ]; + } +}; diff --git a/src/Wallabag/AnnotationBundle/Controller/WallabagAnnotationController.php b/src/Wallabag/AnnotationBundle/Controller/WallabagAnnotationController.php index c13a034f..2b4b0e8d 100644 --- a/src/Wallabag/AnnotationBundle/Controller/WallabagAnnotationController.php +++ b/src/Wallabag/AnnotationBundle/Controller/WallabagAnnotationController.php @@ -7,6 +7,8 @@ use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; use Wallabag\AnnotationBundle\Entity\Annotation; +use Wallabag\AnnotationBundle\Form\EditAnnotationType; +use Wallabag\AnnotationBundle\Form\NewAnnotationType; use Wallabag\CoreBundle\Entity\Entry; class WallabagAnnotationController extends FOSRestController @@ -49,25 +51,25 @@ class WallabagAnnotationController extends FOSRestController $data = json_decode($request->getContent(), true); $em = $this->getDoctrine()->getManager(); - $annotation = new Annotation($this->getUser()); + $annotation->setEntry($entry); - $annotation->setText($data['text']); - if (array_key_exists('quote', $data)) { - $annotation->setQuote($data['quote']); - } - if (array_key_exists('ranges', $data)) { - $annotation->setRanges($data['ranges']); - } + $form = $this->get('form.factory')->createNamed('', NewAnnotationType::class, $annotation, [ + 'csrf_protection' => false, + 'allow_extra_fields' => true, + ]); + $form->submit($data); - $annotation->setEntry($entry); + if ($form->isValid()) { + $em->persist($annotation); + $em->flush(); - $em->persist($annotation); - $em->flush(); + $json = $this->get('serializer')->serialize($annotation, 'json'); - $json = $this->get('serializer')->serialize($annotation, 'json'); + return JsonResponse::fromJsonString($json); + } - return (new JsonResponse())->setJson($json); + return $form; } /** @@ -86,16 +88,23 @@ class WallabagAnnotationController extends FOSRestController { $data = json_decode($request->getContent(), true); - if (!is_null($data['text'])) { - $annotation->setText($data['text']); - } + $form = $this->get('form.factory')->createNamed('', EditAnnotationType::class, $annotation, [ + 'csrf_protection' => false, + 'allow_extra_fields' => true, + ]); + $form->submit($data); - $em = $this->getDoctrine()->getManager(); - $em->flush(); + if ($form->isValid()) { + $em = $this->getDoctrine()->getManager(); + $em->persist($annotation); + $em->flush(); - $json = $this->get('serializer')->serialize($annotation, 'json'); + $json = $this->get('serializer')->serialize($annotation, 'json'); - return (new JsonResponse())->setJson($json); + return JsonResponse::fromJsonString($json); + } + + return $form; } /** diff --git a/src/Wallabag/AnnotationBundle/Entity/Annotation.php b/src/Wallabag/AnnotationBundle/Entity/Annotation.php index 0838f5aa..c8e41649 100644 --- a/src/Wallabag/AnnotationBundle/Entity/Annotation.php +++ b/src/Wallabag/AnnotationBundle/Entity/Annotation.php @@ -8,6 +8,7 @@ use JMS\Serializer\Annotation\Exclude; use JMS\Serializer\Annotation\VirtualProperty; use JMS\Serializer\Annotation\SerializedName; use JMS\Serializer\Annotation\Groups; +use Symfony\Component\Validator\Constraints as Assert; use Wallabag\UserBundle\Entity\User; use Wallabag\CoreBundle\Entity\Entry; @@ -56,7 +57,11 @@ class Annotation /** * @var string * - * @ORM\Column(name="quote", type="string") + * @Assert\Length( + * max = 10000, + * maxMessage = "validator.quote_length_too_high" + * ) + * @ORM\Column(name="quote", type="text") * * @Groups({"entries_for_user", "export_all"}) */ diff --git a/src/Wallabag/AnnotationBundle/Form/EditAnnotationType.php b/src/Wallabag/AnnotationBundle/Form/EditAnnotationType.php new file mode 100644 index 00000000..3b587478 --- /dev/null +++ b/src/Wallabag/AnnotationBundle/Form/EditAnnotationType.php @@ -0,0 +1,18 @@ +add('text', null, [ + 'empty_data' => '', + ]) + ; + } +} diff --git a/src/Wallabag/AnnotationBundle/Form/NewAnnotationType.php b/src/Wallabag/AnnotationBundle/Form/NewAnnotationType.php new file mode 100644 index 00000000..c73c3ded --- /dev/null +++ b/src/Wallabag/AnnotationBundle/Form/NewAnnotationType.php @@ -0,0 +1,35 @@ +add('text', null, [ + 'empty_data' => '', + ]) + ->add('quote', null, [ + 'empty_data' => null, + ]) + ->add('ranges', CollectionType::class, [ + 'entry_type' => RangeType::class, + 'allow_add' => true, + ]) + ; + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults([ + 'data_class' => Annotation::class, + ]); + } +} diff --git a/src/Wallabag/AnnotationBundle/Form/RangeType.php b/src/Wallabag/AnnotationBundle/Form/RangeType.php new file mode 100644 index 00000000..0647375e --- /dev/null +++ b/src/Wallabag/AnnotationBundle/Form/RangeType.php @@ -0,0 +1,19 @@ +add('start') + ->add('startOffset') + ->add('end') + ->add('endOffset') + ; + } +} diff --git a/src/Wallabag/AnnotationBundle/Repository/AnnotationRepository.php b/src/Wallabag/AnnotationBundle/Repository/AnnotationRepository.php index 8d3f07ee..da361308 100644 --- a/src/Wallabag/AnnotationBundle/Repository/AnnotationRepository.php +++ b/src/Wallabag/AnnotationBundle/Repository/AnnotationRepository.php @@ -122,4 +122,21 @@ class AnnotationRepository extends EntityRepository ->setParameter('userId', $userId) ->execute(); } + + /** + * Find all annotations related to archived entries. + * + * @param $userId + * + * @return mixed + */ + public function findAllArchivedEntriesByUser($userId) + { + return $this->createQueryBuilder('a') + ->leftJoin('a.entry', 'e') + ->where('a.user = :userid')->setParameter(':userid', $userId) + ->andWhere('e.isArchived = true') + ->getQuery() + ->getResult(); + } } diff --git a/src/Wallabag/ApiBundle/Controller/DeveloperController.php b/src/Wallabag/ApiBundle/Controller/DeveloperController.php index 9cb1b626..9cb73f4c 100644 --- a/src/Wallabag/ApiBundle/Controller/DeveloperController.php +++ b/src/Wallabag/ApiBundle/Controller/DeveloperController.php @@ -43,7 +43,7 @@ class DeveloperController extends Controller $clientForm->handleRequest($request); if ($clientForm->isSubmitted() && $clientForm->isValid()) { - $client->setAllowedGrantTypes(['token', 'authorization_code', 'password', 'refresh_token']); + $client->setAllowedGrantTypes(['client_credentials', 'token', 'authorization_code', 'password', 'refresh_token']); $em->persist($client); $em->flush(); diff --git a/src/Wallabag/ApiBundle/Controller/EntryRestController.php b/src/Wallabag/ApiBundle/Controller/EntryRestController.php index 54c1747c..768c4fdc 100644 --- a/src/Wallabag/ApiBundle/Controller/EntryRestController.php +++ b/src/Wallabag/ApiBundle/Controller/EntryRestController.php @@ -5,6 +5,7 @@ namespace Wallabag\ApiBundle\Controller; use Hateoas\Configuration\Route; use Hateoas\Representation\Factory\PagerfantaFactory; use Nelmio\ApiDocBundle\Annotation\ApiDoc; +use Symfony\Component\HttpKernel\Exception\HttpException; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; @@ -41,12 +42,10 @@ class EntryRestController extends WallabagRestController ->getRepository('WallabagCoreBundle:Entry') ->findByUrlAndUserId($url, $this->getUser()->getId()); - $results[$url] = false === $res ? false : true; + $results[$url] = $res instanceof Entry ? $res->getId() : false; } - $json = $this->get('serializer')->serialize($results, 'json'); - - return (new JsonResponse())->setJson($json); + return $this->sendResponse($results); } // let's see if it is a simple url? @@ -60,11 +59,9 @@ class EntryRestController extends WallabagRestController ->getRepository('WallabagCoreBundle:Entry') ->findByUrlAndUserId($url, $this->getUser()->getId()); - $exists = false === $res ? false : true; - - $json = $this->get('serializer')->serialize(['exists' => $exists], 'json'); + $exists = $res instanceof Entry ? $res->getId() : false; - return (new JsonResponse())->setJson($json); + return $this->sendResponse(['exists' => $exists]); } /** @@ -80,6 +77,7 @@ class EntryRestController extends WallabagRestController * {"name"="perPage", "dataType"="integer", "required"=false, "format"="default'30'", "description"="results per page."}, * {"name"="tags", "dataType"="string", "required"=false, "format"="api,rest", "description"="a list of tags url encoded. Will returns entries that matches ALL tags."}, * {"name"="since", "dataType"="integer", "required"=false, "format"="default '0'", "description"="The timestamp since when you want entries updated."}, + * {"name"="public", "dataType"="integer", "required"=false, "format"="1 or 0, all entries by default", "description"="filter by entries with a public link"}, * } * ) * @@ -91,6 +89,7 @@ class EntryRestController extends WallabagRestController $isArchived = (null === $request->query->get('archive')) ? null : (bool) $request->query->get('archive'); $isStarred = (null === $request->query->get('starred')) ? null : (bool) $request->query->get('starred'); + $isPublic = (null === $request->query->get('public')) ? null : (bool) $request->query->get('public'); $sort = $request->query->get('sort', 'created'); $order = $request->query->get('order', 'desc'); $page = (int) $request->query->get('page', 1); @@ -99,9 +98,16 @@ class EntryRestController extends WallabagRestController $since = $request->query->get('since', 0); /** @var \Pagerfanta\Pagerfanta $pager */ - $pager = $this->getDoctrine() - ->getRepository('WallabagCoreBundle:Entry') - ->findEntries($this->getUser()->getId(), $isArchived, $isStarred, $sort, $order, $since, $tags); + $pager = $this->get('wallabag_core.entry_repository')->findEntries( + $this->getUser()->getId(), + $isArchived, + $isStarred, + $isPublic, + $sort, + $order, + $since, + $tags + ); $pager->setMaxPerPage($perPage); $pager->setCurrentPage($page); @@ -114,6 +120,7 @@ class EntryRestController extends WallabagRestController [ 'archive' => $isArchived, 'starred' => $isStarred, + 'public' => $isPublic, 'sort' => $sort, 'order' => $order, 'page' => $page, @@ -125,9 +132,7 @@ class EntryRestController extends WallabagRestController ) ); - $json = $this->get('serializer')->serialize($paginatedCollection, 'json'); - - return (new JsonResponse())->setJson($json); + return $this->sendResponse($paginatedCollection); } /** @@ -146,9 +151,7 @@ class EntryRestController extends WallabagRestController $this->validateAuthentication(); $this->validateUserAccess($entry->getUser()->getId()); - $json = $this->get('serializer')->serialize($entry, 'json'); - - return (new JsonResponse())->setJson($json); + return $this->sendResponse($entry); } /** @@ -174,74 +177,153 @@ class EntryRestController extends WallabagRestController } /** - * Create an entry. + * Handles an entries list and delete URL. * * @ApiDoc( * parameters={ - * {"name"="url", "dataType"="string", "required"=true, "format"="http://www.test.com/article.html", "description"="Url for the entry."}, - * {"name"="title", "dataType"="string", "required"=false, "description"="Optional, we'll get the title from the page."}, - * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, - * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already starred"}, - * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already archived"}, + * {"name"="urls", "dataType"="string", "required"=true, "format"="A JSON array of urls [{'url': 'http://...'}, {'url': 'http://...'}]", "description"="Urls (as an array) to delete."} * } * ) * * @return JsonResponse */ - public function postEntriesAction(Request $request) + public function deleteEntriesListAction(Request $request) { $this->validateAuthentication(); - $url = $request->request->get('url'); - $title = $request->request->get('title'); - $isArchived = $request->request->get('archive'); - $isStarred = $request->request->get('starred'); + $urls = json_decode($request->query->get('urls', [])); + + if (empty($urls)) { + return $this->sendResponse([]); + } - $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId($url, $this->getUser()->getId()); + $results = []; - if (false === $entry) { - $entry = new Entry($this->getUser()); - try { - $entry = $this->get('wallabag_core.content_proxy')->updateEntry( - $entry, - $url - ); - } catch (\Exception $e) { - $this->get('logger')->error('Error while saving an entry', [ - 'exception' => $e, - 'entry' => $entry, - ]); - $entry->setUrl($url); + // handle multiple urls + foreach ($urls as $key => $url) { + $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( + $url, + $this->getUser()->getId() + ); + + $results[$key]['url'] = $url; + + if (false !== $entry) { + $em = $this->getDoctrine()->getManager(); + $em->remove($entry); + $em->flush(); + + // entry deleted, dispatch event about it! + $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry)); } - } - if (!is_null($title)) { - $entry->setTitle($title); + $results[$key]['entry'] = $entry instanceof Entry ? true : false; } - $tags = $request->request->get('tags', ''); - if (!empty($tags)) { - $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags); + return $this->sendResponse($results); + } + + /** + * Handles an entries list and create URL. + * + * @ApiDoc( + * parameters={ + * {"name"="urls", "dataType"="string", "required"=true, "format"="A JSON array of urls [{'url': 'http://...'}, {'url': 'http://...'}]", "description"="Urls (as an array) to create."} + * } + * ) + * + * @return JsonResponse + * + * @throws HttpException When limit is reached + */ + public function postEntriesListAction(Request $request) + { + $this->validateAuthentication(); + + $urls = json_decode($request->query->get('urls', [])); + + $limit = $this->container->getParameter('wallabag_core.api_limit_mass_actions'); + + if (count($urls) > $limit) { + throw new HttpException(400, 'API limit reached'); } - if (!is_null($isStarred)) { - $entry->setStarred((bool) $isStarred); + $results = []; + if (empty($urls)) { + return $this->sendResponse($results); } - if (!is_null($isArchived)) { - $entry->setArchived((bool) $isArchived); + // handle multiple urls + foreach ($urls as $key => $url) { + $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( + $url, + $this->getUser()->getId() + ); + + $results[$key]['url'] = $url; + + if (false === $entry) { + $entry = new Entry($this->getUser()); + + $this->get('wallabag_core.content_proxy')->updateEntry($entry, $url); + } + + $em = $this->getDoctrine()->getManager(); + $em->persist($entry); + $em->flush(); + + $results[$key]['entry'] = $entry instanceof Entry ? $entry->getId() : false; + + // entry saved, dispatch event about it! + $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); } - $em = $this->getDoctrine()->getManager(); - $em->persist($entry); - $em->flush(); + return $this->sendResponse($results); + } - // entry saved, dispatch event about it! - $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); + /** + * Create an entry. + * + * If you want to provide the HTML content (which means wallabag won't fetch it from the url), you must provide `content`, `title` & `url` fields **non-empty**. + * Otherwise, content will be fetched as normal from the url and values will be overwritten. + * + * @ApiDoc( + * parameters={ + * {"name"="url", "dataType"="string", "required"=true, "format"="http://www.test.com/article.html", "description"="Url for the entry."}, + * {"name"="title", "dataType"="string", "required"=false, "description"="Optional, we'll get the title from the page."}, + * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, + * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already starred"}, + * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already archived"}, + * {"name"="content", "dataType"="string", "required"=false, "description"="Content of the entry"}, + * {"name"="language", "dataType"="string", "required"=false, "description"="Language of the entry"}, + * {"name"="preview_picture", "dataType"="string", "required"=false, "description"="Preview picture of the entry"}, + * {"name"="published_at", "dataType"="datetime|integer", "format"="YYYY-MM-DDTHH:II:SS+TZ or a timestamp", "required"=false, "description"="Published date of the entry"}, + * {"name"="authors", "dataType"="string", "format"="Name Firstname,author2,author3", "required"=false, "description"="Authors of the entry"}, + * {"name"="public", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="will generate a public link for the entry"}, + * } + * ) + * + * @return JsonResponse + */ + public function postEntriesAction(Request $request) + { + $this->validateAuthentication(); - $json = $this->get('serializer')->serialize($entry, 'json'); + $url = $request->request->get('url'); - return (new JsonResponse())->setJson($json); + $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( + $url, + $this->getUser()->getId() + ); + + if (false === $entry) { + $entry = new Entry($this->getUser()); + $entry->setUrl($url); + } + + $this->upsertEntry($entry, $request); + + return $this->sendResponse($entry); } /** @@ -256,6 +338,12 @@ class EntryRestController extends WallabagRestController * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="archived the entry."}, * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="starred the entry."}, + * {"name"="content", "dataType"="string", "required"=false, "description"="Content of the entry"}, + * {"name"="language", "dataType"="string", "required"=false, "description"="Language of the entry"}, + * {"name"="preview_picture", "dataType"="string", "required"=false, "description"="Preview picture of the entry"}, + * {"name"="published_at", "dataType"="datetime|integer", "format"="YYYY-MM-DDTHH:II:SS+TZ or a timestamp", "required"=false, "description"="Published date of the entry"}, + * {"name"="authors", "dataType"="string", "format"="Name Firstname,author2,author3", "required"=false, "description"="Authors of the entry"}, + * {"name"="public", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="will generate a public link for the entry"}, * } * ) * @@ -266,33 +354,9 @@ class EntryRestController extends WallabagRestController $this->validateAuthentication(); $this->validateUserAccess($entry->getUser()->getId()); - $title = $request->request->get('title'); - $isArchived = $request->request->get('archive'); - $isStarred = $request->request->get('starred'); - - if (!is_null($title)) { - $entry->setTitle($title); - } - - if (!is_null($isArchived)) { - $entry->setArchived((bool) $isArchived); - } - - if (!is_null($isStarred)) { - $entry->setStarred((bool) $isStarred); - } - - $tags = $request->request->get('tags', ''); - if (!empty($tags)) { - $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags); - } - - $em = $this->getDoctrine()->getManager(); - $em->flush(); - - $json = $this->get('serializer')->serialize($entry, 'json'); + $this->upsertEntry($entry, $request, true); - return (new JsonResponse())->setJson($json); + return $this->sendResponse($entry); } /** @@ -313,7 +377,7 @@ class EntryRestController extends WallabagRestController $this->validateUserAccess($entry->getUser()->getId()); try { - $entry = $this->get('wallabag_core.content_proxy')->updateEntry($entry, $entry->getUrl()); + $this->get('wallabag_core.content_proxy')->updateEntry($entry, $entry->getUrl()); } catch (\Exception $e) { $this->get('logger')->error('Error while saving an entry', [ 'exception' => $e, @@ -335,9 +399,7 @@ class EntryRestController extends WallabagRestController // entry saved, dispatch event about it! $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); - $json = $this->get('serializer')->serialize($entry, 'json'); - - return (new JsonResponse())->setJson($json); + return $this->sendResponse($entry); } /** @@ -363,9 +425,7 @@ class EntryRestController extends WallabagRestController // entry deleted, dispatch event about it! $this->get('event_dispatcher')->dispatch(EntryDeletedEvent::NAME, new EntryDeletedEvent($entry)); - $json = $this->get('serializer')->serialize($entry, 'json'); - - return (new JsonResponse())->setJson($json); + return $this->sendResponse($entry); } /** @@ -384,9 +444,7 @@ class EntryRestController extends WallabagRestController $this->validateAuthentication(); $this->validateUserAccess($entry->getUser()->getId()); - $json = $this->get('serializer')->serialize($entry->getTags(), 'json'); - - return (new JsonResponse())->setJson($json); + return $this->sendResponse($entry->getTags()); } /** @@ -410,16 +468,14 @@ class EntryRestController extends WallabagRestController $tags = $request->request->get('tags', ''); if (!empty($tags)) { - $this->get('wallabag_core.content_proxy')->assignTagsToEntry($entry, $tags); + $this->get('wallabag_core.tags_assigner')->assignTagsToEntry($entry, $tags); } $em = $this->getDoctrine()->getManager(); $em->persist($entry); $em->flush(); - $json = $this->get('serializer')->serialize($entry, 'json'); - - return (new JsonResponse())->setJson($json); + return $this->sendResponse($entry); } /** @@ -444,8 +500,198 @@ class EntryRestController extends WallabagRestController $em->persist($entry); $em->flush(); - $json = $this->get('serializer')->serialize($entry, 'json'); + return $this->sendResponse($entry); + } + + /** + * Handles an entries list delete tags from them. + * + * @ApiDoc( + * parameters={ + * {"name"="list", "dataType"="string", "required"=true, "format"="A JSON array of urls [{'url': 'http://...','tags': 'tag1, tag2'}, {'url': 'http://...','tags': 'tag1, tag2'}]", "description"="Urls (as an array) to handle."} + * } + * ) + * + * @return JsonResponse + */ + public function deleteEntriesTagsListAction(Request $request) + { + $this->validateAuthentication(); + + $list = json_decode($request->query->get('list', [])); + + if (empty($list)) { + return $this->sendResponse([]); + } + + // handle multiple urls + $results = []; + + foreach ($list as $key => $element) { + $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( + $element->url, + $this->getUser()->getId() + ); + + $results[$key]['url'] = $element->url; + $results[$key]['entry'] = $entry instanceof Entry ? $entry->getId() : false; + + $tags = $element->tags; + + if (false !== $entry && !(empty($tags))) { + $tags = explode(',', $tags); + foreach ($tags as $label) { + $label = trim($label); + + $tag = $this->getDoctrine() + ->getRepository('WallabagCoreBundle:Tag') + ->findOneByLabel($label); + + if (false !== $tag) { + $entry->removeTag($tag); + } + } + + $em = $this->getDoctrine()->getManager(); + $em->persist($entry); + $em->flush(); + } + } + + return $this->sendResponse($results); + } + + /** + * Handles an entries list and add tags to them. + * + * @ApiDoc( + * parameters={ + * {"name"="list", "dataType"="string", "required"=true, "format"="A JSON array of urls [{'url': 'http://...','tags': 'tag1, tag2'}, {'url': 'http://...','tags': 'tag1, tag2'}]", "description"="Urls (as an array) to handle."} + * } + * ) + * + * @return JsonResponse + */ + public function postEntriesTagsListAction(Request $request) + { + $this->validateAuthentication(); + + $list = json_decode($request->query->get('list', [])); + + if (empty($list)) { + return $this->sendResponse([]); + } + + $results = []; + + // handle multiple urls + foreach ($list as $key => $element) { + $entry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId( + $element->url, + $this->getUser()->getId() + ); + + $results[$key]['url'] = $element->url; + $results[$key]['entry'] = $entry instanceof Entry ? $entry->getId() : false; + + $tags = $element->tags; + + if (false !== $entry && !(empty($tags))) { + $this->get('wallabag_core.tags_assigner')->assignTagsToEntry($entry, $tags); + + $em = $this->getDoctrine()->getManager(); + $em->persist($entry); + $em->flush(); + } + } + + return $this->sendResponse($results); + } + + /** + * Shortcut to send data serialized in json. + * + * @param mixed $data + * + * @return JsonResponse + */ + private function sendResponse($data) + { + $json = $this->get('serializer')->serialize($data, 'json'); return (new JsonResponse())->setJson($json); } + + /** + * Update or Insert a new entry. + * + * @param Entry $entry + * @param Request $request + * @param bool $disableContentUpdate If we don't want the content to be update by fetching the url (used when patching instead of posting) + */ + private function upsertEntry(Entry $entry, Request $request, $disableContentUpdate = false) + { + $title = $request->request->get('title'); + $tags = $request->request->get('tags', []); + $isArchived = $request->request->get('archive'); + $isStarred = $request->request->get('starred'); + $isPublic = $request->request->get('public'); + $content = $request->request->get('content'); + $language = $request->request->get('language'); + $picture = $request->request->get('preview_picture'); + $publishedAt = $request->request->get('published_at'); + $authors = $request->request->get('authors', ''); + + try { + $this->get('wallabag_core.content_proxy')->updateEntry( + $entry, + $entry->getUrl(), + [ + 'title' => !empty($title) ? $title : $entry->getTitle(), + 'html' => !empty($content) ? $content : $entry->getContent(), + 'url' => $entry->getUrl(), + 'language' => !empty($language) ? $language : $entry->getLanguage(), + 'date' => !empty($publishedAt) ? $publishedAt : $entry->getPublishedAt(), + // faking the open graph preview picture + 'open_graph' => [ + 'og_image' => !empty($picture) ? $picture : $entry->getPreviewPicture(), + ], + 'authors' => is_string($authors) ? explode(',', $authors) : $entry->getPublishedBy(), + ], + $disableContentUpdate + ); + } catch (\Exception $e) { + $this->get('logger')->error('Error while saving an entry', [ + 'exception' => $e, + 'entry' => $entry, + ]); + } + + if (!is_null($isArchived)) { + $entry->setArchived((bool) $isArchived); + } + + if (!is_null($isStarred)) { + $entry->setStarred((bool) $isStarred); + } + + if (!empty($tags)) { + $this->get('wallabag_core.tags_assigner')->assignTagsToEntry($entry, $tags); + } + + if (!is_null($isPublic)) { + if (true === (bool) $isPublic && null === $entry->getUid()) { + $entry->generateUid(); + } elseif (false === (bool) $isPublic) { + $entry->cleanUid(); + } + } + + $em = $this->getDoctrine()->getManager(); + $em->persist($entry); + $em->flush(); + + // entry saved, dispatch event about it! + $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry)); + } } diff --git a/src/Wallabag/ApiBundle/Controller/TagRestController.php b/src/Wallabag/ApiBundle/Controller/TagRestController.php index bc6d4e64..354187a0 100644 --- a/src/Wallabag/ApiBundle/Controller/TagRestController.php +++ b/src/Wallabag/ApiBundle/Controller/TagRestController.php @@ -31,7 +31,7 @@ class TagRestController extends WallabagRestController } /** - * Permanently remove one tag from **every** entry. + * Permanently remove one tag from **every** entry by passing the Tag label. * * @ApiDoc( * requirements={ @@ -44,7 +44,7 @@ class TagRestController extends WallabagRestController public function deleteTagLabelAction(Request $request) { $this->validateAuthentication(); - $label = $request->request->get('tag', ''); + $label = $request->get('tag', ''); $tag = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneByLabel($label); @@ -78,7 +78,7 @@ class TagRestController extends WallabagRestController { $this->validateAuthentication(); - $tagsLabels = $request->request->get('tags', ''); + $tagsLabels = $request->get('tags', ''); $tags = []; @@ -106,7 +106,7 @@ class TagRestController extends WallabagRestController } /** - * Permanently remove one tag from **every** entry. + * Permanently remove one tag from **every** entry by passing the Tag ID. * * @ApiDoc( * requirements={ diff --git a/src/Wallabag/ApiBundle/Controller/UserRestController.php b/src/Wallabag/ApiBundle/Controller/UserRestController.php new file mode 100644 index 00000000..7471f5f6 --- /dev/null +++ b/src/Wallabag/ApiBundle/Controller/UserRestController.php @@ -0,0 +1,157 @@ +validateAuthentication(); + + return $this->sendUser($this->getUser()); + } + + /** + * Register an user and create a client. + * + * @ApiDoc( + * requirements={ + * {"name"="username", "dataType"="string", "required"=true, "description"="The user's username"}, + * {"name"="password", "dataType"="string", "required"=true, "description"="The user's password"}, + * {"name"="email", "dataType"="string", "required"=true, "description"="The user's email"}, + * {"name"="client_name", "dataType"="string", "required"=true, "description"="The client name (to be used by your app)"} + * } + * ) + * + * @todo Make this method (or the whole API) accessible only through https + * + * @return JsonResponse + */ + public function putUserAction(Request $request) + { + if (!$this->getParameter('fosuser_registration') || !$this->get('craue_config')->get('api_user_registration')) { + $json = $this->get('serializer')->serialize(['error' => "Server doesn't allow registrations"], 'json'); + + return (new JsonResponse()) + ->setJson($json) + ->setStatusCode(JsonResponse::HTTP_FORBIDDEN); + } + + $userManager = $this->get('fos_user.user_manager'); + $user = $userManager->createUser(); + // user will be disabled BY DEFAULT to avoid spamming account to be enabled + $user->setEnabled(false); + + $form = $this->createForm('Wallabag\UserBundle\Form\NewUserType', $user, [ + 'csrf_protection' => false, + ]); + + // simulate form submission + $form->submit([ + 'username' => $request->request->get('username'), + 'plainPassword' => [ + 'first' => $request->request->get('password'), + 'second' => $request->request->get('password'), + ], + 'email' => $request->request->get('email'), + ]); + + if ($form->isSubmitted() && false === $form->isValid()) { + $view = $this->view($form, 400); + $view->setFormat('json'); + + // handle errors in a more beautiful way than the default view + $data = json_decode($this->handleView($view)->getContent(), true)['children']; + $errors = []; + + if (isset($data['username']['errors'])) { + $errors['username'] = $this->translateErrors($data['username']['errors']); + } + + if (isset($data['email']['errors'])) { + $errors['email'] = $this->translateErrors($data['email']['errors']); + } + + if (isset($data['plainPassword']['children']['first']['errors'])) { + $errors['password'] = $this->translateErrors($data['plainPassword']['children']['first']['errors']); + } + + $json = $this->get('serializer')->serialize(['error' => $errors], 'json'); + + return (new JsonResponse()) + ->setJson($json) + ->setStatusCode(JsonResponse::HTTP_BAD_REQUEST); + } + + // create a default client + $client = new Client($user); + $client->setName($request->request->get('client_name', 'Default client')); + + $this->getDoctrine()->getManager()->persist($client); + + $user->addClient($client); + + $userManager->updateUser($user); + + // dispatch a created event so the associated config will be created + $event = new UserEvent($user, $request); + $this->get('event_dispatcher')->dispatch(FOSUserEvents::USER_CREATED, $event); + + return $this->sendUser($user, 'user_api_with_client', JsonResponse::HTTP_CREATED); + } + + /** + * Send user response. + * + * @param User $user + * @param string $group Used to define with serialized group might be used + * @param int $status HTTP Status code to send + * + * @return JsonResponse + */ + private function sendUser(User $user, $group = 'user_api', $status = JsonResponse::HTTP_OK) + { + $json = $this->get('serializer')->serialize( + $user, + 'json', + SerializationContext::create()->setGroups([$group]) + ); + + return (new JsonResponse()) + ->setJson($json) + ->setStatusCode($status); + } + + /** + * Translate errors message. + * + * @param array $errors + * + * @return array + */ + private function translateErrors($errors) + { + $translatedErrors = []; + foreach ($errors as $error) { + $translatedErrors[] = $this->get('translator')->trans($error); + } + + return $translatedErrors; + } +} diff --git a/src/Wallabag/ApiBundle/Entity/Client.php b/src/Wallabag/ApiBundle/Entity/Client.php index 9ed9f980..c15fd3fa 100644 --- a/src/Wallabag/ApiBundle/Entity/Client.php +++ b/src/Wallabag/ApiBundle/Entity/Client.php @@ -5,6 +5,9 @@ namespace Wallabag\ApiBundle\Entity; use Doctrine\ORM\Mapping as ORM; use FOS\OAuthServerBundle\Entity\Client as BaseClient; use Wallabag\UserBundle\Entity\User; +use JMS\Serializer\Annotation\Groups; +use JMS\Serializer\Annotation\SerializedName; +use JMS\Serializer\Annotation\VirtualProperty; /** * @ORM\Table("oauth2_clients") @@ -23,6 +26,8 @@ class Client extends BaseClient * @var string * * @ORM\Column(name="name", type="text", nullable=false) + * + * @Groups({"user_api_with_client"}) */ protected $name; @@ -36,6 +41,14 @@ class Client extends BaseClient */ protected $accessTokens; + /** + * @var string + * + * @SerializedName("client_secret") + * @Groups({"user_api_with_client"}) + */ + protected $secret; + /** * @ORM\ManyToOne(targetEntity="Wallabag\UserBundle\Entity\User", inversedBy="clients") */ @@ -78,4 +91,14 @@ class Client extends BaseClient { return $this->user; } + + /** + * @VirtualProperty + * @SerializedName("client_id") + * @Groups({"user_api_with_client"}) + */ + public function getClientId() + { + return $this->getId().'_'.$this->getRandomId(); + } } diff --git a/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml b/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml index 57d37f4b..c0283e71 100644 --- a/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml +++ b/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml @@ -17,3 +17,8 @@ misc: type: rest resource: "WallabagApiBundle:WallabagRest" name_prefix: api_ + +user: + type: rest + resource: "WallabagApiBundle:UserRest" + name_prefix: api_ diff --git a/src/Wallabag/CoreBundle/Command/CleanDuplicatesCommand.php b/src/Wallabag/CoreBundle/Command/CleanDuplicatesCommand.php new file mode 100644 index 00000000..74da1e5f --- /dev/null +++ b/src/Wallabag/CoreBundle/Command/CleanDuplicatesCommand.php @@ -0,0 +1,119 @@ +setName('wallabag:clean-duplicates') + ->setDescription('Cleans the database for duplicates') + ->setHelp('This command helps you to clean your articles list in case of duplicates') + ->addArgument( + 'username', + InputArgument::OPTIONAL, + 'User to clean' + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->output = $output; + + $username = $input->getArgument('username'); + + if ($username) { + try { + $user = $this->getUser($username); + $this->cleanDuplicates($user); + } catch (NoResultException $e) { + $output->writeln(sprintf('User "%s" not found.', $username)); + + return 1; + } + } else { + $users = $this->getContainer()->get('wallabag_user.user_repository')->findAll(); + + $output->writeln(sprintf('Cleaning through %d user accounts', count($users))); + + foreach ($users as $user) { + $output->writeln(sprintf('Processing user %s', $user->getUsername())); + $this->cleanDuplicates($user); + } + $output->writeln(sprintf('Finished cleaning. %d duplicates found in total', $this->duplicates)); + } + + return 0; + } + + /** + * @param User $user + */ + private function cleanDuplicates(User $user) + { + $em = $this->getContainer()->get('doctrine.orm.entity_manager'); + $repo = $this->getContainer()->get('wallabag_core.entry_repository'); + + $entries = $repo->getAllEntriesIdAndUrl($user->getId()); + + $duplicatesCount = 0; + $urls = []; + foreach ($entries as $entry) { + $url = $this->similarUrl($entry['url']); + + /* @var $entry Entry */ + if (in_array($url, $urls)) { + ++$duplicatesCount; + + $em->remove($repo->find($entry['id'])); + $em->flush(); // Flushing at the end of the loop would require the instance not being online + } else { + $urls[] = $entry['url']; + } + } + + $this->duplicates += $duplicatesCount; + + $this->output->writeln(sprintf('Cleaned %d duplicates for user %s', $duplicatesCount, $user->getUserName())); + } + + private function similarUrl($url) + { + if (in_array(substr($url, -1), ['/', '#'])) { // get rid of "/" and "#" and the end of urls + return substr($url, 0, strlen($url)); + } + + return $url; + } + + /** + * Fetches a user from its username. + * + * @param string $username + * + * @return \Wallabag\UserBundle\Entity\User + */ + private function getUser($username) + { + return $this->getContainer()->get('wallabag_user.user_repository')->findOneByUserName($username); + } + + private function getDoctrine() + { + return $this->getContainer()->get('doctrine'); + } +} diff --git a/src/Wallabag/CoreBundle/Command/ExportCommand.php b/src/Wallabag/CoreBundle/Command/ExportCommand.php index e3d3b399..ebb2b4cf 100644 --- a/src/Wallabag/CoreBundle/Command/ExportCommand.php +++ b/src/Wallabag/CoreBundle/Command/ExportCommand.php @@ -32,15 +32,14 @@ class ExportCommand extends ContainerAwareCommand protected function execute(InputInterface $input, OutputInterface $output) { try { - $user = $this->getDoctrine()->getRepository('WallabagUserBundle:User')->findOneByUserName($input->getArgument('username')); + $user = $this->getContainer()->get('wallabag_user.user_repository')->findOneByUserName($input->getArgument('username')); } catch (NoResultException $e) { $output->writeln(sprintf('User "%s" not found.', $input->getArgument('username'))); return 1; } - $entries = $this->getDoctrine() - ->getRepository('WallabagCoreBundle:Entry') + $entries = $this->getContainer()->get('wallabag_core.entry_repository') ->getBuilderForAllByUser($user->getId()) ->getQuery() ->getResult(); diff --git a/src/Wallabag/CoreBundle/Command/InstallCommand.php b/src/Wallabag/CoreBundle/Command/InstallCommand.php index f0738b91..eb725a59 100644 --- a/src/Wallabag/CoreBundle/Command/InstallCommand.php +++ b/src/Wallabag/CoreBundle/Command/InstallCommand.php @@ -63,6 +63,7 @@ class InstallCommand extends ContainerAwareCommand ->setupDatabase() ->setupAdmin() ->setupConfig() + ->runMigrations() ; $output->writeln('wallabag has been successfully installed.'); @@ -71,7 +72,7 @@ class InstallCommand extends ContainerAwareCommand protected function checkRequirements() { - $this->defaultOutput->writeln('Step 1 of 4. Checking system requirements.'); + $this->defaultOutput->writeln('Step 1 of 5. Checking system requirements.'); $doctrineManager = $this->getContainer()->get('doctrine')->getManager(); $rows = []; @@ -175,11 +176,11 @@ class InstallCommand extends ContainerAwareCommand protected function setupDatabase() { - $this->defaultOutput->writeln('Step 2 of 4. Setting up database.'); + $this->defaultOutput->writeln('Step 2 of 5. Setting up database.'); // user want to reset everything? Don't care about what is already here if (true === $this->defaultInput->getOption('reset')) { - $this->defaultOutput->writeln('Droping database, creating database and schema, clearing the cache'); + $this->defaultOutput->writeln('Dropping database, creating database and schema, clearing the cache'); $this ->runCommand('doctrine:database:drop', ['--force' => true]) @@ -211,7 +212,7 @@ class InstallCommand extends ContainerAwareCommand $question = new ConfirmationQuestion('It appears that your database already exists. Would you like to reset it? (y/N)', false); if ($questionHelper->ask($this->defaultInput, $this->defaultOutput, $question)) { - $this->defaultOutput->writeln('Droping database, creating database and schema'); + $this->defaultOutput->writeln('Dropping database, creating database and schema'); $this ->runCommand('doctrine:database:drop', ['--force' => true]) @@ -221,7 +222,7 @@ class InstallCommand extends ContainerAwareCommand } elseif ($this->isSchemaPresent()) { $question = new ConfirmationQuestion('Seems like your database contains schema. Do you want to reset it? (y/N)', false); if ($questionHelper->ask($this->defaultInput, $this->defaultOutput, $question)) { - $this->defaultOutput->writeln('Droping schema and creating schema'); + $this->defaultOutput->writeln('Dropping schema and creating schema'); $this ->runCommand('doctrine:schema:drop', ['--force' => true]) @@ -246,7 +247,7 @@ class InstallCommand extends ContainerAwareCommand protected function setupAdmin() { - $this->defaultOutput->writeln('Step 3 of 4. Administration setup.'); + $this->defaultOutput->writeln('Step 3 of 5. Administration setup.'); $questionHelper = $this->getHelperSet()->get('question'); $question = new ConfirmationQuestion('Would you like to create a new admin user (recommended) ? (Y/n)', true); @@ -285,161 +286,13 @@ class InstallCommand extends ContainerAwareCommand protected function setupConfig() { - $this->defaultOutput->writeln('Step 4 of 4. Config setup.'); + $this->defaultOutput->writeln('Step 4 of 5. Config setup.'); $em = $this->getContainer()->get('doctrine.orm.entity_manager'); // cleanup before insert new stuff $em->createQuery('DELETE FROM CraueConfigBundle:Setting')->execute(); - $settings = [ - [ - 'name' => 'share_public', - 'value' => '1', - 'section' => 'entry', - ], - [ - 'name' => 'carrot', - 'value' => '1', - 'section' => 'entry', - ], - [ - 'name' => 'share_diaspora', - 'value' => '1', - 'section' => 'entry', - ], - [ - 'name' => 'diaspora_url', - 'value' => 'http://diasporapod.com', - 'section' => 'entry', - ], - [ - 'name' => 'share_unmark', - 'value' => '1', - 'section' => 'entry', - ], - [ - 'name' => 'unmark_url', - 'value' => 'https://unmark.it', - 'section' => 'entry', - ], - [ - 'name' => 'share_shaarli', - 'value' => '1', - 'section' => 'entry', - ], - [ - 'name' => 'shaarli_url', - 'value' => 'http://myshaarli.com', - 'section' => 'entry', - ], - [ - 'name' => 'share_mail', - 'value' => '1', - 'section' => 'entry', - ], - [ - 'name' => 'share_twitter', - 'value' => '1', - 'section' => 'entry', - ], - [ - 'name' => 'export_epub', - 'value' => '1', - 'section' => 'export', - ], - [ - 'name' => 'export_mobi', - 'value' => '1', - 'section' => 'export', - ], - [ - 'name' => 'export_pdf', - 'value' => '1', - 'section' => 'export', - ], - [ - 'name' => 'export_csv', - 'value' => '1', - 'section' => 'export', - ], - [ - 'name' => 'export_json', - 'value' => '1', - 'section' => 'export', - ], - [ - 'name' => 'export_txt', - 'value' => '1', - 'section' => 'export', - ], - [ - 'name' => 'export_xml', - 'value' => '1', - 'section' => 'export', - ], - [ - 'name' => 'import_with_redis', - 'value' => '0', - 'section' => 'import', - ], - [ - 'name' => 'import_with_rabbitmq', - 'value' => '0', - 'section' => 'import', - ], - [ - 'name' => 'show_printlink', - 'value' => '1', - 'section' => 'entry', - ], - [ - 'name' => 'wallabag_support_url', - 'value' => 'https://www.wallabag.org/pages/support.html', - 'section' => 'misc', - ], - [ - 'name' => 'wallabag_url', - 'value' => '', - 'section' => 'misc', - ], - [ - 'name' => 'piwik_enabled', - 'value' => '0', - 'section' => 'analytics', - ], - [ - 'name' => 'piwik_host', - 'value' => 'v2.wallabag.org', - 'section' => 'analytics', - ], - [ - 'name' => 'piwik_site_id', - 'value' => '1', - 'section' => 'analytics', - ], - [ - 'name' => 'demo_mode_enabled', - 'value' => '0', - 'section' => 'misc', - ], - [ - 'name' => 'demo_mode_username', - 'value' => 'wallabag', - 'section' => 'misc', - ], - [ - 'name' => 'download_images_enabled', - 'value' => '0', - 'section' => 'misc', - ], - [ - 'name' => 'restricted_access', - 'value' => '0', - 'section' => 'entry', - ], - ]; - - foreach ($settings as $setting) { + foreach ($this->getContainer()->getParameter('wallabag_core.default_internal_settings') as $setting) { $newSetting = new Setting(); $newSetting->setName($setting['name']); $newSetting->setValue($setting['value']); @@ -454,6 +307,16 @@ class InstallCommand extends ContainerAwareCommand return $this; } + protected function runMigrations() + { + $this->defaultOutput->writeln('Step 5 of 5. Run migrations.'); + + $this + ->runCommand('doctrine:migrations:migrate', ['--no-interaction' => true]); + + return $this; + } + /** * Run a command. * @@ -480,20 +343,18 @@ class InstallCommand extends ContainerAwareCommand $output = new BufferedOutput(); $exitCode = $this->getApplication()->run(new ArrayInput($parameters), $output); + // PDO does not always close the connection after Doctrine commands. + // See https://github.com/symfony/symfony/issues/11750. + $this->getContainer()->get('doctrine')->getManager()->getConnection()->close(); + if (0 !== $exitCode) { $this->getApplication()->setAutoExit(true); - $this->defaultOutput->writeln(''); - $this->defaultOutput->writeln('The command "'.$command.'" generates some errors: '); - $this->defaultOutput->writeln($output->fetch()); - - die(); + throw new \RuntimeException( + 'The command "'.$command."\" generates some errors: \n\n" + .$output->fetch()); } - // PDO does not always close the connection after Doctrine commands. - // See https://github.com/symfony/symfony/issues/11750. - $this->getContainer()->get('doctrine')->getManager()->getConnection()->close(); - return $this; } diff --git a/src/Wallabag/CoreBundle/Command/ShowUserCommand.php b/src/Wallabag/CoreBundle/Command/ShowUserCommand.php new file mode 100644 index 00000000..eef04988 --- /dev/null +++ b/src/Wallabag/CoreBundle/Command/ShowUserCommand.php @@ -0,0 +1,77 @@ +setName('wallabag:user:show') + ->setDescription('Show user details') + ->setHelp('This command shows the details for an user') + ->addArgument( + 'username', + InputArgument::REQUIRED, + 'User to show details for' + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $this->output = $output; + + $username = $input->getArgument('username'); + + try { + $user = $this->getUser($username); + $this->showUser($user); + } catch (NoResultException $e) { + $output->writeln(sprintf('User "%s" not found.', $username)); + + return 1; + } + + return 0; + } + + /** + * @param User $user + */ + private function showUser(User $user) + { + $this->output->writeln(sprintf('Username : %s', $user->getUsername())); + $this->output->writeln(sprintf('Email : %s', $user->getEmail())); + $this->output->writeln(sprintf('Display name : %s', $user->getName())); + $this->output->writeln(sprintf('Creation date : %s', $user->getCreatedAt()->format('Y-m-d H:i:s'))); + $this->output->writeln(sprintf('Last login : %s', $user->getLastLogin() !== null ? $user->getLastLogin()->format('Y-m-d H:i:s') : 'never')); + $this->output->writeln(sprintf('2FA activated: %s', $user->isTwoFactorAuthentication() ? 'yes' : 'no')); + } + + /** + * Fetches a user from its username. + * + * @param string $username + * + * @return \Wallabag\UserBundle\Entity\User + */ + private function getUser($username) + { + return $this->getContainer()->get('wallabag_user.user_repository')->findOneByUserName($username); + } + + private function getDoctrine() + { + return $this->getContainer()->get('doctrine'); + } +} diff --git a/src/Wallabag/CoreBundle/Command/TagAllCommand.php b/src/Wallabag/CoreBundle/Command/TagAllCommand.php index 3f9bb04d..9843674e 100644 --- a/src/Wallabag/CoreBundle/Command/TagAllCommand.php +++ b/src/Wallabag/CoreBundle/Command/TagAllCommand.php @@ -59,7 +59,7 @@ class TagAllCommand extends ContainerAwareCommand */ private function getUser($username) { - return $this->getDoctrine()->getRepository('WallabagUserBundle:User')->findOneByUserName($username); + return $this->getContainer()->get('wallabag_user.user_repository')->findOneByUserName($username); } private function getDoctrine() diff --git a/src/Wallabag/CoreBundle/Controller/ConfigController.php b/src/Wallabag/CoreBundle/Controller/ConfigController.php index 907bf78e..d4170d39 100644 --- a/src/Wallabag/CoreBundle/Controller/ConfigController.php +++ b/src/Wallabag/CoreBundle/Controller/ConfigController.php @@ -151,9 +151,8 @@ class ConfigController extends Controller 'token' => $config->getRssToken(), ], 'twofactor_auth' => $this->getParameter('twofactor_auth'), - 'wallabag_url' => $this->get('craue_config')->get('wallabag_url'), - 'enabled_users' => $this->getDoctrine() - ->getRepository('WallabagUserBundle:User') + 'wallabag_url' => $this->getParameter('domain_name'), + 'enabled_users' => $this->get('wallabag_user.user_repository') ->getSumEnabledUsers(), ]); } @@ -248,18 +247,27 @@ class ConfigController extends Controller break; case 'entries': - // SQLite doesn't care about cascading remove, so we need to manually remove associated stuf + // SQLite doesn't care about cascading remove, so we need to manually remove associated stuff // otherwise they won't be removed ... - if ($this->get('doctrine')->getConnection()->getDriver() instanceof \Doctrine\DBAL\Driver\PDOSqlite\Driver) { + if ($this->get('doctrine')->getConnection()->getDatabasePlatform() instanceof \Doctrine\DBAL\Platforms\SqlitePlatform) { $this->getDoctrine()->getRepository('WallabagAnnotationBundle:Annotation')->removeAllByUserId($this->getUser()->getId()); } // manually remove tags to avoid orphan tag $this->removeAllTagsByUserId($this->getUser()->getId()); - $this->getDoctrine() - ->getRepository('WallabagCoreBundle:Entry') - ->removeAllByUserId($this->getUser()->getId()); + $this->get('wallabag_core.entry_repository')->removeAllByUserId($this->getUser()->getId()); + break; + case 'archived': + if ($this->get('doctrine')->getConnection()->getDatabasePlatform() instanceof \Doctrine\DBAL\Platforms\SqlitePlatform) { + $this->removeAnnotationsForArchivedByUserId($this->getUser()->getId()); + } + + // manually remove tags to avoid orphan tag + $this->removeTagsForArchivedByUserId($this->getUser()->getId()); + + $this->get('wallabag_core.entry_repository')->removeArchivedByUserId($this->getUser()->getId()); + break; } $this->get('session')->getFlashBag()->add( @@ -271,20 +279,18 @@ class ConfigController extends Controller } /** - * Remove all tags for a given user and cleanup orphan tags. + * Remove all tags for given tags and a given user and cleanup orphan tags. * - * @param int $userId + * @param array $tags + * @param int $userId */ - private function removeAllTagsByUserId($userId) + private function removeAllTagsByStatusAndUserId($tags, $userId) { - $tags = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findAllTags($userId); - if (empty($tags)) { return; } - $this->getDoctrine() - ->getRepository('WallabagCoreBundle:Entry') + $this->get('wallabag_core.entry_repository') ->removeTags($userId, $tags); // cleanup orphan tags @@ -299,6 +305,43 @@ class ConfigController extends Controller $em->flush(); } + /** + * Remove all tags for a given user and cleanup orphan tags. + * + * @param int $userId + */ + private function removeAllTagsByUserId($userId) + { + $tags = $this->get('wallabag_core.tag_repository')->findAllTags($userId); + $this->removeAllTagsByStatusAndUserId($tags, $userId); + } + + /** + * Remove all tags for a given user and cleanup orphan tags. + * + * @param int $userId + */ + private function removeTagsForArchivedByUserId($userId) + { + $tags = $this->get('wallabag_core.tag_repository')->findForArchivedArticlesByUser($userId); + $this->removeAllTagsByStatusAndUserId($tags, $userId); + } + + private function removeAnnotationsForArchivedByUserId($userId) + { + $em = $this->getDoctrine()->getManager(); + + $archivedEntriesAnnotations = $this->getDoctrine() + ->getRepository('WallabagAnnotationBundle:Annotation') + ->findAllArchivedEntriesByUser($userId); + + foreach ($archivedEntriesAnnotations as $archivedEntriesAnnotation) { + $em->remove($archivedEntriesAnnotation); + } + + $em->flush(); + } + /** * Validate that a rule can be edited/deleted by the current user. * @@ -344,8 +387,7 @@ class ConfigController extends Controller */ public function deleteAccountAction(Request $request) { - $enabledUsers = $this->getDoctrine() - ->getRepository('WallabagUserBundle:User') + $enabledUsers = $this->get('wallabag_user.user_repository') ->getSumEnabledUsers(); if ($enabledUsers <= 1) { diff --git a/src/Wallabag/CoreBundle/Controller/EntryController.php b/src/Wallabag/CoreBundle/Controller/EntryController.php index f7398e69..fafa49f1 100644 --- a/src/Wallabag/CoreBundle/Controller/EntryController.php +++ b/src/Wallabag/CoreBundle/Controller/EntryController.php @@ -53,22 +53,17 @@ class EntryController extends Controller /** * Fetch content and update entry. - * In case it fails, entry will return to avod loosing the data. + * In case it fails, $entry->getContent will return an error message. * * @param Entry $entry * @param string $prefixMessage Should be the translation key: entry_saved or entry_reloaded - * - * @return Entry */ private function updateEntry(Entry $entry, $prefixMessage = 'entry_saved') { - // put default title in case of fetching content failed - $entry->setTitle('No title found'); - $message = 'flashes.entry.notice.'.$prefixMessage; try { - $entry = $this->get('wallabag_core.content_proxy')->updateEntry($entry, $entry->getUrl()); + $this->get('wallabag_core.content_proxy')->updateEntry($entry, $entry->getUrl()); } catch (\Exception $e) { $this->get('logger')->error('Error while saving an entry', [ 'exception' => $e, @@ -79,8 +74,6 @@ class EntryController extends Controller } $this->get('session')->getFlashBag()->add('notice', $message); - - return $entry; } /** @@ -227,7 +220,7 @@ class EntryController extends Controller public function showUnreadAction(Request $request, $page) { // load the quickstart if no entry in database - if ($page == 1 && $this->get('wallabag_core.entry_repository')->countAllEntriesByUsername($this->getUser()->getId()) == 0) { + if ($page == 1 && $this->get('wallabag_core.entry_repository')->countAllEntriesByUser($this->getUser()->getId()) == 0) { return $this->redirect($this->generateUrl('quickstart')); } @@ -321,8 +314,7 @@ class EntryController extends Controller $pagerAdapter = new DoctrineORMAdapter($qb->getQuery(), true, false); - $entries = $this->get('wallabag_core.helper.prepare_pager_for_entries') - ->prepare($pagerAdapter, $page); + $entries = $this->get('wallabag_core.helper.prepare_pager_for_entries')->prepare($pagerAdapter); try { $entries->setCurrentPage($page); diff --git a/src/Wallabag/CoreBundle/Controller/ExportController.php b/src/Wallabag/CoreBundle/Controller/ExportController.php index abc3336a..fda04cfb 100644 --- a/src/Wallabag/CoreBundle/Controller/ExportController.php +++ b/src/Wallabag/CoreBundle/Controller/ExportController.php @@ -7,7 +7,6 @@ use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Wallabag\CoreBundle\Entity\Entry; -use Wallabag\CoreBundle\Entity\Tag; /** * The try/catch can be removed once all formats will be implemented. @@ -57,16 +56,17 @@ class ExportController extends Controller { $method = ucfirst($category); $methodBuilder = 'getBuilderFor'.$method.'ByUser'; + $repository = $this->get('wallabag_core.entry_repository'); if ($category == 'tag_entries') { - $tag = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneBySlug($request->query->get('tag')); + $tag = $this->get('wallabag_core.tag_repository')->findOneBySlug($request->query->get('tag')); - $entries = $this->getDoctrine() - ->getRepository('WallabagCoreBundle:Entry') - ->findAllByTagId($this->getUser()->getId(), $tag->getId()); + $entries = $repository->findAllByTagId( + $this->getUser()->getId(), + $tag->getId() + ); } else { - $entries = $this->getDoctrine() - ->getRepository('WallabagCoreBundle:Entry') + $entries = $repository ->$methodBuilder($this->getUser()->getId()) ->getQuery() ->getResult(); diff --git a/src/Wallabag/CoreBundle/Controller/RssController.php b/src/Wallabag/CoreBundle/Controller/RssController.php index 92f18707..e87dd9a1 100644 --- a/src/Wallabag/CoreBundle/Controller/RssController.php +++ b/src/Wallabag/CoreBundle/Controller/RssController.php @@ -3,13 +3,16 @@ namespace Wallabag\CoreBundle\Controller; use Pagerfanta\Adapter\DoctrineORMAdapter; +use Pagerfanta\Adapter\ArrayAdapter; use Pagerfanta\Exception\OutOfRangeCurrentPageException; use Pagerfanta\Pagerfanta; use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Wallabag\CoreBundle\Entity\Entry; +use Wallabag\CoreBundle\Entity\Tag; use Wallabag\UserBundle\Entity\User; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; @@ -23,7 +26,7 @@ class RssController extends Controller * * @return \Symfony\Component\HttpFoundation\Response */ - public function showUnreadAction(Request $request, User $user) + public function showUnreadRSSAction(Request $request, User $user) { return $this->showEntries('unread', $user, $request->query->get('page', 1)); } @@ -31,12 +34,12 @@ class RssController extends Controller /** * Shows read entries for current user. * - * @Route("/{username}/{token}/archive.xml", name="archive_rss") + * @Route("/{username}/{token}/archive.xml", name="archive_rss", defaults={"_format"="xml"}) * @ParamConverter("user", class="WallabagUserBundle:User", converter="username_rsstoken_converter") * * @return \Symfony\Component\HttpFoundation\Response */ - public function showArchiveAction(Request $request, User $user) + public function showArchiveRSSAction(Request $request, User $user) { return $this->showEntries('archive', $user, $request->query->get('page', 1)); } @@ -44,16 +47,88 @@ class RssController extends Controller /** * Shows starred entries for current user. * - * @Route("/{username}/{token}/starred.xml", name="starred_rss") + * @Route("/{username}/{token}/starred.xml", name="starred_rss", defaults={"_format"="xml"}) * @ParamConverter("user", class="WallabagUserBundle:User", converter="username_rsstoken_converter") * * @return \Symfony\Component\HttpFoundation\Response */ - public function showStarredAction(Request $request, User $user) + public function showStarredRSSAction(Request $request, User $user) { return $this->showEntries('starred', $user, $request->query->get('page', 1)); } + /** + * Shows all entries for current user. + * + * @Route("/{username}/{token}/all.xml", name="all_rss", defaults={"_format"="xml"}) + * @ParamConverter("user", class="WallabagUserBundle:User", converter="username_rsstoken_converter") + * + * @return \Symfony\Component\HttpFoundation\Response + */ + public function showAllRSSAction(Request $request, User $user) + { + return $this->showEntries('all', $user, $request->query->get('page', 1)); + } + + /** + * Shows entries associated to a tag for current user. + * + * @Route("/{username}/{token}/tags/{slug}.xml", name="tag_rss", defaults={"_format"="xml"}) + * @ParamConverter("user", class="WallabagUserBundle:User", converter="username_rsstoken_converter") + * @ParamConverter("tag", options={"mapping": {"slug": "slug"}}) + * + * @return \Symfony\Component\HttpFoundation\Response + */ + public function showTagsAction(Request $request, User $user, Tag $tag) + { + $page = $request->query->get('page', 1); + + $url = $this->generateUrl( + 'tag_rss', + [ + 'username' => $user->getUsername(), + 'token' => $user->getConfig()->getRssToken(), + 'slug' => $tag->getSlug(), + ], + UrlGeneratorInterface::ABSOLUTE_URL + ); + + $entriesByTag = $this->get('wallabag_core.entry_repository')->findAllByTagId( + $user->getId(), + $tag->getId() + ); + + $pagerAdapter = new ArrayAdapter($entriesByTag); + + $entries = $this->get('wallabag_core.helper.prepare_pager_for_entries')->prepare( + $pagerAdapter, + $user + ); + + if (null === $entries) { + throw $this->createNotFoundException('No entries found?'); + } + + try { + $entries->setCurrentPage($page); + } catch (OutOfRangeCurrentPageException $e) { + if ($page > 1) { + return $this->redirect($url.'?page='.$entries->getNbPages(), 302); + } + } + + return $this->render( + '@WallabagCore/themes/common/Entry/entries.xml.twig', + [ + 'url_html' => $this->generateUrl('tag_entries', ['slug' => $tag->getSlug()], UrlGeneratorInterface::ABSOLUTE_URL), + 'type' => 'tag ('.$tag->getLabel().')', + 'url' => $url, + 'entries' => $entries, + ], + new Response('', 200, ['Content-Type' => 'application/rss+xml']) + ); + } + /** * Global method to retrieve entries depending on the given type * It returns the response to be send. @@ -66,7 +141,7 @@ class RssController extends Controller */ private function showEntries($type, User $user, $page = 1) { - $repository = $this->getDoctrine()->getRepository('WallabagCoreBundle:Entry'); + $repository = $this->get('wallabag_core.entry_repository'); switch ($type) { case 'starred': @@ -81,6 +156,10 @@ class RssController extends Controller $qb = $repository->getBuilderForUnreadByUser($user->getId()); break; + case 'all': + $qb = $repository->getBuilderForAllByUser($user->getId()); + break; + default: throw new \InvalidArgumentException(sprintf('Type "%s" is not implemented.', $type)); } @@ -108,10 +187,15 @@ class RssController extends Controller } } - return $this->render('@WallabagCore/themes/common/Entry/entries.xml.twig', [ - 'type' => $type, - 'url' => $url, - 'entries' => $entries, - ]); + return $this->render( + '@WallabagCore/themes/common/Entry/entries.xml.twig', + [ + 'url_html' => $this->generateUrl($type, [], UrlGeneratorInterface::ABSOLUTE_URL), + 'type' => $type, + 'url' => $url, + 'entries' => $entries, + ], + new Response('', 200, ['Content-Type' => 'application/rss+xml']) + ); } } diff --git a/src/Wallabag/CoreBundle/Controller/SiteCredentialController.php b/src/Wallabag/CoreBundle/Controller/SiteCredentialController.php new file mode 100644 index 00000000..98781dab --- /dev/null +++ b/src/Wallabag/CoreBundle/Controller/SiteCredentialController.php @@ -0,0 +1,174 @@ +get('wallabag_core.site_credential_repository')->findByUser($this->getUser()); + + return $this->render('WallabagCoreBundle:SiteCredential:index.html.twig', [ + 'credentials' => $credentials, + ]); + } + + /** + * Creates a new site credential entity. + * + * @Route("/new", name="site_credentials_new") + * @Method({"GET", "POST"}) + * + * @param Request $request + * + * @return \Symfony\Component\HttpFoundation\Response + */ + public function newAction(Request $request) + { + $credential = new SiteCredential($this->getUser()); + + $form = $this->createForm('Wallabag\CoreBundle\Form\Type\SiteCredentialType', $credential); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $credential->setUsername($this->get('wallabag_core.helper.crypto_proxy')->crypt($credential->getUsername())); + $credential->setPassword($this->get('wallabag_core.helper.crypto_proxy')->crypt($credential->getPassword())); + + $em = $this->getDoctrine()->getManager(); + $em->persist($credential); + $em->flush(); + + $this->get('session')->getFlashBag()->add( + 'notice', + $this->get('translator')->trans('flashes.site_credential.notice.added', ['%host%' => $credential->getHost()]) + ); + + return $this->redirectToRoute('site_credentials_index'); + } + + return $this->render('WallabagCoreBundle:SiteCredential:new.html.twig', [ + 'credential' => $credential, + 'form' => $form->createView(), + ]); + } + + /** + * Displays a form to edit an existing site credential entity. + * + * @Route("/{id}/edit", name="site_credentials_edit") + * @Method({"GET", "POST"}) + * + * @param Request $request + * @param SiteCredential $siteCredential + * + * @return \Symfony\Component\HttpFoundation\Response + */ + public function editAction(Request $request, SiteCredential $siteCredential) + { + $this->checkUserAction($siteCredential); + + $deleteForm = $this->createDeleteForm($siteCredential); + $editForm = $this->createForm('Wallabag\CoreBundle\Form\Type\SiteCredentialType', $siteCredential); + $editForm->handleRequest($request); + + if ($editForm->isSubmitted() && $editForm->isValid()) { + $siteCredential->setUsername($this->get('wallabag_core.helper.crypto_proxy')->crypt($siteCredential->getUsername())); + $siteCredential->setPassword($this->get('wallabag_core.helper.crypto_proxy')->crypt($siteCredential->getPassword())); + + $em = $this->getDoctrine()->getManager(); + $em->persist($siteCredential); + $em->flush(); + + $this->get('session')->getFlashBag()->add( + 'notice', + $this->get('translator')->trans('flashes.site_credential.notice.updated', ['%host%' => $siteCredential->getHost()]) + ); + + return $this->redirectToRoute('site_credentials_index'); + } + + return $this->render('WallabagCoreBundle:SiteCredential:edit.html.twig', [ + 'credential' => $siteCredential, + 'edit_form' => $editForm->createView(), + 'delete_form' => $deleteForm->createView(), + ]); + } + + /** + * Deletes a site credential entity. + * + * @Route("/{id}", name="site_credentials_delete") + * @Method("DELETE") + * + * @param Request $request + * @param SiteCredential $siteCredential + * + * @return \Symfony\Component\HttpFoundation\RedirectResponse + */ + public function deleteAction(Request $request, SiteCredential $siteCredential) + { + $this->checkUserAction($siteCredential); + + $form = $this->createDeleteForm($siteCredential); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $this->get('session')->getFlashBag()->add( + 'notice', + $this->get('translator')->trans('flashes.site_credential.notice.deleted', ['%host%' => $siteCredential->getHost()]) + ); + + $em = $this->getDoctrine()->getManager(); + $em->remove($siteCredential); + $em->flush(); + } + + return $this->redirectToRoute('site_credentials_index'); + } + + /** + * Creates a form to delete a site credential entity. + * + * @param SiteCredential $siteCredential The site credential entity + * + * @return \Symfony\Component\Form\Form The form + */ + private function createDeleteForm(SiteCredential $siteCredential) + { + return $this->createFormBuilder() + ->setAction($this->generateUrl('site_credentials_delete', ['id' => $siteCredential->getId()])) + ->setMethod('DELETE') + ->getForm() + ; + } + + /** + * Check if the logged user can manage the given site credential. + * + * @param SiteCredential $siteCredential The site credential entity + */ + private function checkUserAction(SiteCredential $siteCredential) + { + if (null === $this->getUser() || $this->getUser()->getId() != $siteCredential->getUser()->getId()) { + throw $this->createAccessDeniedException('You can not access this site credential.'); + } + } +} diff --git a/src/Wallabag/CoreBundle/Controller/TagController.php b/src/Wallabag/CoreBundle/Controller/TagController.php index 8a093289..a8b1eadd 100644 --- a/src/Wallabag/CoreBundle/Controller/TagController.php +++ b/src/Wallabag/CoreBundle/Controller/TagController.php @@ -28,7 +28,7 @@ class TagController extends Controller $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { - $this->get('wallabag_core.content_proxy')->assignTagsToEntry( + $this->get('wallabag_core.tags_assigner')->assignTagsToEntry( $entry, $form->get('label')->getData() ); @@ -70,7 +70,7 @@ class TagController extends Controller $em->flush(); } - $redirectUrl = $this->get('wallabag_core.helper.redirect')->to($request->headers->get('referer')); + $redirectUrl = $this->get('wallabag_core.helper.redirect')->to($request->headers->get('referer'), '', true); return $this->redirect($redirectUrl); } @@ -84,16 +84,17 @@ class TagController extends Controller */ public function showTagAction() { - $tags = $this->getDoctrine() - ->getRepository('WallabagCoreBundle:Tag') + $repository = $this->get('wallabag_core.entry_repository'); + $tags = $this->get('wallabag_core.tag_repository') ->findAllTags($this->getUser()->getId()); $flatTags = []; foreach ($tags as $tag) { - $nbEntries = $this->getDoctrine() - ->getRepository('WallabagCoreBundle:Entry') - ->countAllEntriesByUserIdAndTagId($this->getUser()->getId(), $tag->getId()); + $nbEntries = $repository->countAllEntriesByUserIdAndTagId( + $this->getUser()->getId(), + $tag->getId() + ); $flatTags[] = [ 'id' => $tag->getId(), @@ -119,14 +120,14 @@ class TagController extends Controller */ public function showEntriesForTagAction(Tag $tag, $page, Request $request) { - $entriesByTag = $this->getDoctrine() - ->getRepository('WallabagCoreBundle:Entry') - ->findAllByTagId($this->getUser()->getId(), $tag->getId()); + $entriesByTag = $this->get('wallabag_core.entry_repository')->findAllByTagId( + $this->getUser()->getId(), + $tag->getId() + ); $pagerAdapter = new ArrayAdapter($entriesByTag); - $entries = $this->get('wallabag_core.helper.prepare_pager_for_entries') - ->prepare($pagerAdapter, $page); + $entries = $this->get('wallabag_core.helper.prepare_pager_for_entries')->prepare($pagerAdapter); try { $entries->setCurrentPage($page); @@ -143,7 +144,7 @@ class TagController extends Controller 'form' => null, 'entries' => $entries, 'currentPage' => $page, - 'tag' => $tag->getSlug(), + 'tag' => $tag, ]); } } diff --git a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php index a723656e..a52288e6 100644 --- a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php +++ b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php @@ -6,163 +6,27 @@ use Doctrine\Common\DataFixtures\AbstractFixture; use Doctrine\Common\DataFixtures\OrderedFixtureInterface; use Doctrine\Common\Persistence\ObjectManager; use Craue\ConfigBundle\Entity\Setting; +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; -class LoadSettingData extends AbstractFixture implements OrderedFixtureInterface +class LoadSettingData extends AbstractFixture implements OrderedFixtureInterface, ContainerAwareInterface { + /** + * @var ContainerInterface + */ + private $container; + + public function setContainer(ContainerInterface $container = null) + { + $this->container = $container; + } + /** * {@inheritdoc} */ public function load(ObjectManager $manager) { - $settings = [ - [ - 'name' => 'share_public', - 'value' => '1', - 'section' => 'entry', - ], - [ - 'name' => 'carrot', - 'value' => '1', - 'section' => 'entry', - ], - [ - 'name' => 'share_diaspora', - 'value' => '1', - 'section' => 'entry', - ], - [ - 'name' => 'diaspora_url', - 'value' => 'http://diasporapod.com', - 'section' => 'entry', - ], - [ - 'name' => 'share_unmark', - 'value' => '1', - 'section' => 'entry', - ], - [ - 'name' => 'unmark_url', - 'value' => 'https://unmark.it', - 'section' => 'entry', - ], - [ - 'name' => 'share_shaarli', - 'value' => '1', - 'section' => 'entry', - ], - [ - 'name' => 'shaarli_url', - 'value' => 'http://myshaarli.com', - 'section' => 'entry', - ], - [ - 'name' => 'share_mail', - 'value' => '1', - 'section' => 'entry', - ], - [ - 'name' => 'share_twitter', - 'value' => '1', - 'section' => 'entry', - ], - [ - 'name' => 'export_epub', - 'value' => '1', - 'section' => 'export', - ], - [ - 'name' => 'export_mobi', - 'value' => '1', - 'section' => 'export', - ], - [ - 'name' => 'export_pdf', - 'value' => '1', - 'section' => 'export', - ], - [ - 'name' => 'export_csv', - 'value' => '1', - 'section' => 'export', - ], - [ - 'name' => 'export_json', - 'value' => '1', - 'section' => 'export', - ], - [ - 'name' => 'export_txt', - 'value' => '1', - 'section' => 'export', - ], - [ - 'name' => 'export_xml', - 'value' => '1', - 'section' => 'export', - ], - [ - 'name' => 'import_with_redis', - 'value' => '0', - 'section' => 'import', - ], - [ - 'name' => 'import_with_rabbitmq', - 'value' => '0', - 'section' => 'import', - ], - [ - 'name' => 'show_printlink', - 'value' => '1', - 'section' => 'entry', - ], - [ - 'name' => 'wallabag_support_url', - 'value' => 'https://www.wallabag.org/pages/support.html', - 'section' => 'misc', - ], - [ - 'name' => 'wallabag_url', - 'value' => 'http://v2.wallabag.org', - 'section' => 'misc', - ], - [ - 'name' => 'piwik_enabled', - 'value' => '0', - 'section' => 'analytics', - ], - [ - 'name' => 'piwik_host', - 'value' => 'v2.wallabag.org', - 'section' => 'analytics', - ], - [ - 'name' => 'piwik_site_id', - 'value' => '1', - 'section' => 'analytics', - ], - [ - 'name' => 'demo_mode_enabled', - 'value' => '0', - 'section' => 'misc', - ], - [ - 'name' => 'demo_mode_username', - 'value' => 'wallabag', - 'section' => 'misc', - ], - [ - 'name' => 'download_images_enabled', - 'value' => '0', - 'section' => 'misc', - ], - [ - 'name' => 'restricted_access', - 'value' => '0', - 'section' => 'entry', - ], - ]; - - foreach ($settings as $setting) { + foreach ($this->container->getParameter('wallabag_core.default_internal_settings') as $setting) { $newSetting = new Setting(); $newSetting->setName($setting['name']); $newSetting->setValue($setting['value']); diff --git a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSiteCredentialData.php b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSiteCredentialData.php new file mode 100644 index 00000000..866f55a4 --- /dev/null +++ b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSiteCredentialData.php @@ -0,0 +1,34 @@ +getReference('admin-user')); + $credential->setHost('example.com'); + $credential->setUsername('foo'); + $credential->setPassword('bar'); + + $manager->persist($credential); + + $manager->flush(); + } + + /** + * {@inheritdoc} + */ + public function getOrder() + { + return 50; + } +} diff --git a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadTaggingRuleData.php b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadTaggingRuleData.php index 7efe6356..55abd63c 100644 --- a/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadTaggingRuleData.php +++ b/src/Wallabag/CoreBundle/DataFixtures/ORM/LoadTaggingRuleData.php @@ -36,6 +36,13 @@ class LoadTaggingRuleData extends AbstractFixture implements OrderedFixtureInter $manager->persist($tr3); + $tr4 = new TaggingRule(); + $tr4->setRule('content notmatches "basket"'); + $tr4->setTags(['foot']); + $tr4->setConfig($this->getReference('admin-config')); + + $manager->persist($tr4); + $manager->flush(); } diff --git a/src/Wallabag/CoreBundle/DependencyInjection/Configuration.php b/src/Wallabag/CoreBundle/DependencyInjection/Configuration.php index 006a18c3..a9791f6b 100644 --- a/src/Wallabag/CoreBundle/DependencyInjection/Configuration.php +++ b/src/Wallabag/CoreBundle/DependencyInjection/Configuration.php @@ -41,12 +41,30 @@ class Configuration implements ConfigurationInterface ->end() ->scalarNode('fetching_error_message') ->end() + ->scalarNode('fetching_error_message_title') + ->end() ->scalarNode('action_mark_as_read') ->defaultValue(1) ->end() ->scalarNode('list_mode') ->defaultValue(1) ->end() + ->scalarNode('api_limit_mass_actions') + ->defaultValue(10) + ->end() + ->arrayNode('default_internal_settings') + ->prototype('array') + ->children() + ->scalarNode('name')->end() + ->scalarNode('value')->end() + ->enumNode('section') + ->values(['entry', 'misc', 'api', 'analytics', 'export', 'import']) + ->end() + ->end() + ->end() + ->end() + ->scalarNode('encryption_key_path') + ->end() ->end() ; diff --git a/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php b/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php index aa9ee339..532ce238 100644 --- a/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php +++ b/src/Wallabag/CoreBundle/DependencyInjection/WallabagCoreExtension.php @@ -26,6 +26,10 @@ class WallabagCoreExtension extends Extension $container->setParameter('wallabag_core.action_mark_as_read', $config['action_mark_as_read']); $container->setParameter('wallabag_core.list_mode', $config['list_mode']); $container->setParameter('wallabag_core.fetching_error_message', $config['fetching_error_message']); + $container->setParameter('wallabag_core.fetching_error_message_title', $config['fetching_error_message_title']); + $container->setParameter('wallabag_core.api_limit_mass_actions', $config['api_limit_mass_actions']); + $container->setParameter('wallabag_core.default_internal_settings', $config['default_internal_settings']); + $container->setParameter('wallabag_core.site_credentials.encryption_key_path', $config['encryption_key_path']); $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); $loader->load('services.yml'); diff --git a/src/Wallabag/CoreBundle/Entity/Entry.php b/src/Wallabag/CoreBundle/Entity/Entry.php index 7276b437..a0503c39 100644 --- a/src/Wallabag/CoreBundle/Entity/Entry.php +++ b/src/Wallabag/CoreBundle/Entity/Entry.php @@ -121,6 +121,24 @@ class Entry */ private $updatedAt; + /** + * @var \DateTime + * + * @ORM\Column(name="published_at", type="datetime", nullable=true) + * + * @Groups({"entries_for_user", "export_all"}) + */ + private $publishedAt; + + /** + * @var array + * + * @ORM\Column(name="published_by", type="array", nullable=true) + * + * @Groups({"entries_for_user", "export_all"}) + */ + private $publishedBy; + /** * @ORM\OneToMany(targetEntity="Wallabag\AnnotationBundle\Entity\Annotation", mappedBy="entry", cascade={"persist", "remove"}) * @ORM\JoinTable @@ -175,22 +193,22 @@ class Entry private $previewPicture; /** - * @var bool + * @var string * - * @ORM\Column(name="is_public", type="boolean", nullable=true, options={"default" = false}) + * @ORM\Column(name="http_status", type="string", length=3, nullable=true) * - * @Groups({"export_all"}) + * @Groups({"entries_for_user", "export_all"}) */ - private $isPublic; + private $httpStatus; /** - * @var string + * @var array * - * @ORM\Column(name="http_status", type="string", length=3, nullable=true) + * @ORM\Column(name="headers", type="array", nullable=true) * * @Groups({"entries_for_user", "export_all"}) */ - private $httpStatus; + private $headers; /** * @Exclude @@ -532,23 +550,7 @@ class Entry } /** - * @return bool - */ - public function isPublic() - { - return $this->isPublic; - } - - /** - * @param bool $isPublic - */ - public function setIsPublic($isPublic) - { - $this->isPublic = $isPublic; - } - - /** - * @return ArrayCollection + * @return ArrayCollection */ public function getTags() { @@ -683,7 +685,22 @@ class Entry } /** - * @return int + * Used in the entries filter so it's more explicit for the end user than the uid. + * Also used in the API. + * + * @VirtualProperty + * @SerializedName("is_public") + * @Groups({"entries_for_user"}) + * + * @return bool + */ + public function isPublic() + { + return null !== $this->uid; + } + + /** + * @return string */ public function getHttpStatus() { @@ -691,7 +708,7 @@ class Entry } /** - * @param int $httpStatus + * @param string $httpStatus * * @return Entry */ @@ -701,4 +718,64 @@ class Entry return $this; } + + /** + * @return \Datetime + */ + public function getPublishedAt() + { + return $this->publishedAt; + } + + /** + * @param \Datetime $publishedAt + * + * @return Entry + */ + public function setPublishedAt(\Datetime $publishedAt) + { + $this->publishedAt = $publishedAt; + + return $this; + } + + /** + * @return array + */ + public function getPublishedBy() + { + return $this->publishedBy; + } + + /** + * @param array $publishedBy + * + * @return Entry + */ + public function setPublishedBy($publishedBy) + { + $this->publishedBy = $publishedBy; + + return $this; + } + + /** + * @return array + */ + public function getHeaders() + { + return $this->headers; + } + + /** + * @param array $headers + * + * @return Entry + */ + public function setHeaders($headers) + { + $this->headers = $headers; + + return $this; + } } diff --git a/src/Wallabag/CoreBundle/Entity/SiteCredential.php b/src/Wallabag/CoreBundle/Entity/SiteCredential.php new file mode 100644 index 00000000..58075e92 --- /dev/null +++ b/src/Wallabag/CoreBundle/Entity/SiteCredential.php @@ -0,0 +1,195 @@ +user = $user; + } + + /** + * Get id. + * + * @return int + */ + public function getId() + { + return $this->id; + } + + /** + * Set host. + * + * @param string $host + * + * @return SiteCredential + */ + public function setHost($host) + { + $this->host = $host; + + return $this; + } + + /** + * Get host. + * + * @return string + */ + public function getHost() + { + return $this->host; + } + + /** + * Set username. + * + * @param string $username + * + * @return SiteCredential + */ + public function setUsername($username) + { + $this->username = $username; + + return $this; + } + + /** + * Get username. + * + * @return string + */ + public function getUsername() + { + return $this->username; + } + + /** + * Set password. + * + * @param string $password + * + * @return SiteCredential + */ + public function setPassword($password) + { + $this->password = $password; + + return $this; + } + + /** + * Get password. + * + * @return string + */ + public function getPassword() + { + return $this->password; + } + + /** + * Set createdAt. + * + * @param \DateTime $createdAt + * + * @return SiteCredential + */ + public function setCreatedAt($createdAt) + { + $this->createdAt = $createdAt; + + return $this; + } + + /** + * Get createdAt. + * + * @return \DateTime + */ + public function getCreatedAt() + { + return $this->createdAt; + } + + /** + * @return User + */ + public function getUser() + { + return $this->user; + } + + /** + * @ORM\PrePersist + */ + public function timestamps() + { + if (is_null($this->createdAt)) { + $this->createdAt = new \DateTime(); + } + } +} diff --git a/src/Wallabag/CoreBundle/Entity/TaggingRule.php b/src/Wallabag/CoreBundle/Entity/TaggingRule.php index 72651b19..84e11e26 100644 --- a/src/Wallabag/CoreBundle/Entity/TaggingRule.php +++ b/src/Wallabag/CoreBundle/Entity/TaggingRule.php @@ -31,7 +31,7 @@ class TaggingRule * @Assert\Length(max=255) * @RulerZAssert\ValidRule( * allowed_variables={"title", "url", "isArchived", "isStared", "content", "language", "mimetype", "readingTime", "domainName"}, - * allowed_operators={">", "<", ">=", "<=", "=", "is", "!=", "and", "not", "or", "matches"} + * allowed_operators={">", "<", ">=", "<=", "=", "is", "!=", "and", "not", "or", "matches", "notmatches"} * ) * @ORM\Column(name="rule", type="string", nullable=false) */ @@ -87,7 +87,7 @@ class TaggingRule /** * Set tags. * - * @param array $tags + * @param array $tags * * @return TaggingRule */ diff --git a/src/Wallabag/CoreBundle/Event/Subscriber/SQLiteCascadeDeleteSubscriber.php b/src/Wallabag/CoreBundle/Event/Subscriber/SQLiteCascadeDeleteSubscriber.php index 3b4c4cf9..5e6af8cc 100644 --- a/src/Wallabag/CoreBundle/Event/Subscriber/SQLiteCascadeDeleteSubscriber.php +++ b/src/Wallabag/CoreBundle/Event/Subscriber/SQLiteCascadeDeleteSubscriber.php @@ -45,9 +45,8 @@ class SQLiteCascadeDeleteSubscriber implements EventSubscriber public function preRemove(LifecycleEventArgs $args) { $entity = $args->getEntity(); - - if (!$this->doctrine->getConnection()->getDriver() instanceof \Doctrine\DBAL\Driver\PDOSqlite\Driver || - !$entity instanceof Entry) { + if (!$this->doctrine->getConnection()->getDatabasePlatform() instanceof \Doctrine\DBAL\Platforms\SqlitePlatform + || !$entity instanceof Entry) { return; } diff --git a/src/Wallabag/CoreBundle/Form/Type/EditEntryType.php b/src/Wallabag/CoreBundle/Form/Type/EditEntryType.php index c3715646..1627cc44 100644 --- a/src/Wallabag/CoreBundle/Form/Type/EditEntryType.php +++ b/src/Wallabag/CoreBundle/Form/Type/EditEntryType.php @@ -3,7 +3,6 @@ namespace Wallabag\CoreBundle\Form\Type; use Symfony\Component\Form\AbstractType; -use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; @@ -18,11 +17,6 @@ class EditEntryType extends AbstractType 'required' => true, 'label' => 'entry.edit.title_label', ]) - ->add('is_public', CheckboxType::class, [ - 'required' => false, - 'label' => 'entry.edit.is_public_label', - 'property_path' => 'isPublic', - ]) ->add('url', TextType::class, [ 'disabled' => true, 'required' => false, diff --git a/src/Wallabag/CoreBundle/Form/Type/EntryFilterType.php b/src/Wallabag/CoreBundle/Form/Type/EntryFilterType.php index 556578d1..6a4c485f 100644 --- a/src/Wallabag/CoreBundle/Form/Type/EntryFilterType.php +++ b/src/Wallabag/CoreBundle/Form/Type/EntryFilterType.php @@ -150,6 +150,20 @@ class EntryFilterType extends AbstractType }, 'label' => 'entry.filters.preview_picture_label', ]) + ->add('isPublic', CheckboxFilterType::class, [ + 'apply_filter' => function (QueryInterface $filterQuery, $field, $values) { + if (false === $values['value']) { + return; + } + + // is_public isn't a real field + // we should use the "uid" field to determine if the entry has been made public + $expression = $filterQuery->getExpr()->isNotNull($values['alias'].'.uid'); + + return $filterQuery->createCondition($expression); + }, + 'label' => 'entry.filters.is_public_label', + ]) ->add('language', ChoiceFilterType::class, [ 'choices' => array_flip($this->repository->findDistinctLanguageByUser($this->user->getId())), 'label' => 'entry.filters.language_label', diff --git a/src/Wallabag/CoreBundle/Form/Type/SiteCredentialType.php b/src/Wallabag/CoreBundle/Form/Type/SiteCredentialType.php new file mode 100644 index 00000000..fd409ad2 --- /dev/null +++ b/src/Wallabag/CoreBundle/Form/Type/SiteCredentialType.php @@ -0,0 +1,44 @@ +add('host', TextType::class, [ + 'label' => 'site_credential.form.host_label', + ]) + ->add('username', TextType::class, [ + 'label' => 'site_credential.form.username_label', + 'data' => '', + ]) + ->add('password', PasswordType::class, [ + 'label' => 'site_credential.form.password_label', + ]) + ->add('save', SubmitType::class, [ + 'label' => 'config.form.save', + ]) + ; + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults([ + 'data_class' => 'Wallabag\CoreBundle\Entity\SiteCredential', + ]); + } + + public function getBlockPrefix() + { + return 'site_credential'; + } +} diff --git a/src/Wallabag/CoreBundle/GuzzleSiteAuthenticator/GrabySiteConfigBuilder.php b/src/Wallabag/CoreBundle/GuzzleSiteAuthenticator/GrabySiteConfigBuilder.php index 6d4129e8..a79e6ebe 100644 --- a/src/Wallabag/CoreBundle/GuzzleSiteAuthenticator/GrabySiteConfigBuilder.php +++ b/src/Wallabag/CoreBundle/GuzzleSiteAuthenticator/GrabySiteConfigBuilder.php @@ -5,39 +5,53 @@ namespace Wallabag\CoreBundle\GuzzleSiteAuthenticator; use BD\GuzzleSiteAuthenticator\SiteConfig\SiteConfig; use BD\GuzzleSiteAuthenticator\SiteConfig\SiteConfigBuilder; use Graby\SiteConfig\ConfigBuilder; -use OutOfRangeException; +use Psr\Log\LoggerInterface; +use Wallabag\CoreBundle\Repository\SiteCredentialRepository; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; class GrabySiteConfigBuilder implements SiteConfigBuilder { /** - * @var \Graby\SiteConfig\ConfigBuilder + * @var ConfigBuilder */ private $grabyConfigBuilder; + + /** + * @var SiteCredentialRepository + */ + private $credentialRepository; + /** - * @var array + * @var LoggerInterface */ - private $credentials; + private $logger; + + /** + * @var Wallabag\UserBundle\Entity\User|null + */ + private $currentUser; /** * GrabySiteConfigBuilder constructor. * - * @param \Graby\SiteConfig\ConfigBuilder $grabyConfigBuilder - * @param array $credentials + * @param ConfigBuilder $grabyConfigBuilder + * @param TokenStorage $token + * @param SiteCredentialRepository $credentialRepository + * @param LoggerInterface $logger */ - public function __construct(ConfigBuilder $grabyConfigBuilder, array $credentials = []) + public function __construct(ConfigBuilder $grabyConfigBuilder, TokenStorage $token, SiteCredentialRepository $credentialRepository, LoggerInterface $logger) { $this->grabyConfigBuilder = $grabyConfigBuilder; - $this->credentials = $credentials; + $this->credentialRepository = $credentialRepository; + $this->logger = $logger; + + if ($token->getToken()) { + $this->currentUser = $token->getToken()->getUser(); + } } /** - * Builds the SiteConfig for a host. - * - * @param string $host The "www." prefix is ignored - * - * @return SiteConfig - * - * @throws OutOfRangeException If there is no config for $host + * {@inheritdoc} */ public function buildForHost($host) { @@ -47,6 +61,17 @@ class GrabySiteConfigBuilder implements SiteConfigBuilder $host = substr($host, 4); } + $credentials = null; + if ($this->currentUser) { + $credentials = $this->credentialRepository->findOneByHostAndUser($host, $this->currentUser->getId()); + } + + if (null === $credentials) { + $this->logger->debug('Auth: no credentials available for host.', ['host' => $host]); + + return false; + } + $config = $this->grabyConfigBuilder->buildForHost($host); $parameters = [ 'host' => $host, @@ -54,15 +79,47 @@ class GrabySiteConfigBuilder implements SiteConfigBuilder 'loginUri' => $config->login_uri ?: null, 'usernameField' => $config->login_username_field ?: null, 'passwordField' => $config->login_password_field ?: null, - 'extraFields' => is_array($config->login_extra_fields) ? $config->login_extra_fields : [], + 'extraFields' => $this->processExtraFields($config->login_extra_fields), 'notLoggedInXpath' => $config->not_logged_in_xpath ?: null, + 'username' => $credentials['username'], + 'password' => $credentials['password'], ]; - if (isset($this->credentials[$host])) { - $parameters['username'] = $this->credentials[$host]['username']; - $parameters['password'] = $this->credentials[$host]['password']; + $config = new SiteConfig($parameters); + + // do not leak usernames and passwords in log + $parameters['username'] = '**masked**'; + $parameters['password'] = '**masked**'; + + $this->logger->debug('Auth: add parameters.', ['host' => $host, 'parameters' => $parameters]); + + return $config; + } + + /** + * Processes login_extra_fields config, transforming an '=' separated array of strings + * into a key/value array. + * + * @param array|mixed $extraFieldsStrings + * + * @return array + */ + protected function processExtraFields($extraFieldsStrings) + { + if (!is_array($extraFieldsStrings)) { + return []; + } + + $extraFields = []; + foreach ($extraFieldsStrings as $extraField) { + if (strpos($extraField, '=') === false) { + continue; + } + + list($fieldName, $fieldValue) = explode('=', $extraField, 2); + $extraFields[$fieldName] = $fieldValue; } - return new SiteConfig($parameters); + return $extraFields; } } diff --git a/src/Wallabag/CoreBundle/Helper/ContentProxy.php b/src/Wallabag/CoreBundle/Helper/ContentProxy.php index f222dd88..51bb2ca2 100644 --- a/src/Wallabag/CoreBundle/Helper/ContentProxy.php +++ b/src/Wallabag/CoreBundle/Helper/ContentProxy.php @@ -5,10 +5,11 @@ namespace Wallabag\CoreBundle\Helper; use Graby\Graby; use Psr\Log\LoggerInterface; use Wallabag\CoreBundle\Entity\Entry; -use Wallabag\CoreBundle\Entity\Tag; use Wallabag\CoreBundle\Tools\Utils; -use Wallabag\CoreBundle\Repository\TagRepository; use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeExtensionGuesser; +use Symfony\Component\Validator\Constraints\Locale as LocaleConstraint; +use Symfony\Component\Validator\Constraints\Url as UrlConstraint; +use Symfony\Component\Validator\Validator\ValidatorInterface; /** * This kind of proxy class take care of getting the content from an url @@ -18,38 +19,37 @@ class ContentProxy { protected $graby; protected $tagger; + protected $validator; protected $logger; - protected $tagRepository; protected $mimeGuesser; protected $fetchingErrorMessage; + protected $eventDispatcher; - public function __construct(Graby $graby, RuleBasedTagger $tagger, TagRepository $tagRepository, LoggerInterface $logger, $fetchingErrorMessage) + public function __construct(Graby $graby, RuleBasedTagger $tagger, ValidatorInterface $validator, LoggerInterface $logger, $fetchingErrorMessage) { $this->graby = $graby; $this->tagger = $tagger; + $this->validator = $validator; $this->logger = $logger; - $this->tagRepository = $tagRepository; $this->mimeGuesser = new MimeTypeExtensionGuesser(); $this->fetchingErrorMessage = $fetchingErrorMessage; } /** - * Fetch content using graby and hydrate given entry with results information. - * In case we couldn't find content, we'll try to use Open Graph data. + * Update entry using either fetched or provided content. * - * We can also force the content, in case of an import from the v1 for example, so the function won't - * fetch the content from the website but rather use information given with the $content parameter. - * - * @param Entry $entry Entry to update - * @param string $url Url to grab content for - * @param array $content An array with AT LEAST keys title, html, url, language & content_type to skip the fetchContent from the url - * - * @return Entry + * @param Entry $entry Entry to update + * @param string $url Url of the content + * @param array $content Array with content provided for import with AT LEAST keys title, html, url to skip the fetchContent from the url + * @param bool $disableContentUpdate Whether to skip trying to fetch content using Graby */ - public function updateEntry(Entry $entry, $url, array $content = []) + public function updateEntry(Entry $entry, $url, array $content = [], $disableContentUpdate = false) { - // do we have to fetch the content or the provided one is ok? - if (empty($content) || false === $this->validateContent($content)) { + if (!empty($content['html'])) { + $content['html'] = $this->graby->cleanupHtml($content['html'], $url); + } + + if ((empty($content) || false === $this->validateContent($content)) && false === $disableContentUpdate) { $fetchedContent = $this->graby->fetchContent($url); // when content is imported, we have information in $content @@ -59,8 +59,24 @@ class ContentProxy } } + // be sure to keep the url in case of error + // so we'll be able to refetch it in the future + $content['url'] = !empty($content['url']) ? $content['url'] : $url; + + $this->stockEntry($entry, $content); + } + + /** + * Stock entry with fetched or imported content. + * Will fall back to OpenGraph data if available. + * + * @param Entry $entry Entry to stock + * @param array $content Array with at least title, url & html + */ + private function stockEntry(Entry $entry, array $content) + { $title = $content['title']; - if (!$title && isset($content['open_graph']['og_title'])) { + if (!$title && !empty($content['open_graph']['og_title'])) { $title = $content['open_graph']['og_title']; } @@ -68,18 +84,58 @@ class ContentProxy if (false === $html) { $html = $this->fetchingErrorMessage; - if (isset($content['open_graph']['og_description'])) { + if (!empty($content['open_graph']['og_description'])) { $html .= '

But we found a short description:

'; $html .= $content['open_graph']['og_description']; } } - $entry->setUrl($content['url'] ?: $url); + $entry->setUrl($content['url']); $entry->setTitle($title); $entry->setContent($html); $entry->setHttpStatus(isset($content['status']) ? $content['status'] : ''); - $entry->setLanguage(isset($content['language']) ? $content['language'] : ''); + if (!empty($content['date'])) { + $date = $content['date']; + + // is it a timestamp? + if (filter_var($date, FILTER_VALIDATE_INT) !== false) { + $date = '@'.$content['date']; + } + + try { + $entry->setPublishedAt(new \DateTime($date)); + } catch (\Exception $e) { + $this->logger->warning('Error while defining date', ['e' => $e, 'url' => $content['url'], 'date' => $content['date']]); + } + } + + if (!empty($content['authors']) && is_array($content['authors'])) { + $entry->setPublishedBy($content['authors']); + } + + if (!empty($content['all_headers'])) { + $entry->setHeaders($content['all_headers']); + } + + $this->validateAndSetLanguage( + $entry, + isset($content['language']) ? $content['language'] : null + ); + + $this->validateAndSetPreviewPicture( + $entry, + isset($content['open_graph']['og_image']) ? $content['open_graph']['og_image'] : null + ); + + // if content is an image, define it as a preview too + if (!empty($content['content_type']) && in_array($this->mimeGuesser->guess($content['content_type']), ['jpeg', 'jpg', 'gif', 'png'], true)) { + $this->validateAndSetPreviewPicture( + $entry, + $content['url'] + ); + } + $entry->setMimetype(isset($content['content_type']) ? $content['content_type'] : ''); $entry->setReadingTime(Utils::getReadingTime($html)); @@ -88,85 +144,73 @@ class ContentProxy $entry->setDomainName($domainName); } - if (isset($content['open_graph']['og_image']) && $content['open_graph']['og_image']) { - $entry->setPreviewPicture($content['open_graph']['og_image']); - } - - // if content is an image define as a preview too - if (isset($content['content_type']) && in_array($this->mimeGuesser->guess($content['content_type']), ['jpeg', 'jpg', 'gif', 'png'], true)) { - $entry->setPreviewPicture($content['url']); - } - try { $this->tagger->tag($entry); } catch (\Exception $e) { $this->logger->error('Error while trying to automatically tag an entry.', [ - 'entry_url' => $url, + 'entry_url' => $content['url'], 'error_msg' => $e->getMessage(), ]); } - - return $entry; } /** - * Assign some tags to an entry. + * Validate that the given content has at least a title, an html and a url. + * + * @param array $content * - * @param Entry $entry - * @param array|string $tags An array of tag or a string coma separated of tag - * @param array $entitiesReady Entities from the EntityManager which are persisted but not yet flushed - * It is mostly to fix duplicate tag on import @see http://stackoverflow.com/a/7879164/569101 + * @return bool true if valid otherwise false */ - public function assignTagsToEntry(Entry $entry, $tags, array $entitiesReady = []) + private function validateContent(array $content) { - if (!is_array($tags)) { - $tags = explode(',', $tags); - } - - // keeps only Tag entity from the "not yet flushed entities" - $tagsNotYetFlushed = []; - foreach ($entitiesReady as $entity) { - if ($entity instanceof Tag) { - $tagsNotYetFlushed[$entity->getLabel()] = $entity; - } - } - - foreach ($tags as $label) { - $label = trim($label); + return !empty($content['title']) && !empty($content['html']) && !empty($content['url']); + } - // avoid empty tag - if (0 === strlen($label)) { - continue; - } + /** + * Use a Symfony validator to ensure the language is well formatted. + * + * @param Entry $entry + * @param string $value Language to validate + */ + private function validateAndSetLanguage($entry, $value) + { + // some lang are defined as fr-FR, es-ES. + // replacing - by _ might increase language support + $value = str_replace('-', '_', $value); - if (isset($tagsNotYetFlushed[$label])) { - $tagEntity = $tagsNotYetFlushed[$label]; - } else { - $tagEntity = $this->tagRepository->findOneByLabel($label); + $errors = $this->validator->validate( + $value, + (new LocaleConstraint()) + ); - if (is_null($tagEntity)) { - $tagEntity = new Tag(); - $tagEntity->setLabel($label); - } - } + if (0 === count($errors)) { + $entry->setLanguage($value); - // only add the tag on the entry if the relation doesn't exist - if (false === $entry->getTags()->contains($tagEntity)) { - $entry->addTag($tagEntity); - } + return; } + + $this->logger->warning('Language validation failed. '.(string) $errors); } /** - * Validate that the given content as enough value to be used - * instead of fetch the content from the url. - * - * @param array $content + * Use a Symfony validator to ensure the preview picture is a real url. * - * @return bool true if valid otherwise false + * @param Entry $entry + * @param string $value URL to validate */ - private function validateContent(array $content) + private function validateAndSetPreviewPicture($entry, $value) { - return isset($content['title']) && isset($content['html']) && isset($content['url']) && isset($content['language']) && isset($content['content_type']); + $errors = $this->validator->validate( + $value, + (new UrlConstraint()) + ); + + if (0 === count($errors)) { + $entry->setPreviewPicture($value); + + return; + } + + $this->logger->warning('PreviewPicture validation failed. '.(string) $errors); } } diff --git a/src/Wallabag/CoreBundle/Helper/CryptoProxy.php b/src/Wallabag/CoreBundle/Helper/CryptoProxy.php new file mode 100644 index 00000000..e8b19cb9 --- /dev/null +++ b/src/Wallabag/CoreBundle/Helper/CryptoProxy.php @@ -0,0 +1,86 @@ +logger = $logger; + + if (!file_exists($encryptionKeyPath)) { + $key = Key::createNewRandomKey(); + + file_put_contents($encryptionKeyPath, $key->saveToAsciiSafeString()); + chmod($encryptionKeyPath, 0600); + } + + $this->encryptionKey = file_get_contents($encryptionKeyPath); + } + + /** + * Ensure the given value will be crypted. + * + * @param string $secretValue Secret valye to crypt + * + * @return string + */ + public function crypt($secretValue) + { + $this->logger->debug('Crypto: crypting value: '.$this->mask($secretValue)); + + return Crypto::encrypt($secretValue, $this->loadKey()); + } + + /** + * Ensure the given crypted value will be decrypted. + * + * @param string $cryptedValue The value to be decrypted + * + * @return string + */ + public function decrypt($cryptedValue) + { + $this->logger->debug('Crypto: decrypting value: '.$this->mask($cryptedValue)); + + try { + return Crypto::decrypt($cryptedValue, $this->loadKey()); + } catch (WrongKeyOrModifiedCiphertextException $e) { + throw new \RuntimeException('Decrypt fail: '.$e->getMessage()); + } + } + + /** + * Load the private key. + * + * @return Key + */ + private function loadKey() + { + return Key::loadFromAsciiSafeString($this->encryptionKey); + } + + /** + * Keep first and last character and put some stars in between. + * + * @param string $value Value to mask + * + * @return string + */ + private function mask($value) + { + return strlen($value) > 0 ? $value[0].'*****'.$value[strlen($value) - 1] : 'Empty value'; + } +} diff --git a/src/Wallabag/CoreBundle/Helper/DownloadImages.php b/src/Wallabag/CoreBundle/Helper/DownloadImages.php index 0d330d2a..ed888cdb 100644 --- a/src/Wallabag/CoreBundle/Helper/DownloadImages.php +++ b/src/Wallabag/CoreBundle/Helper/DownloadImages.php @@ -5,6 +5,7 @@ namespace Wallabag\CoreBundle\Helper; use Psr\Log\LoggerInterface; use Symfony\Component\DomCrawler\Crawler; use GuzzleHttp\Client; +use GuzzleHttp\Message\Response; use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeExtensionGuesser; use Symfony\Component\Finder\Finder; @@ -54,7 +55,7 @@ class DownloadImages $crawler = new Crawler($html); $result = $crawler ->filterXpath('//img') - ->extract(array('src')); + ->extract(['src']); $relativePath = $this->getRelativePath($entryId); @@ -66,6 +67,11 @@ class DownloadImages continue; } + // if image contains "&" and we can't find it in the html it might be because it's encoded as & + if (false !== stripos($image, '&') && false === stripos($html, $image)) { + $image = str_replace('&', '&', $image); + } + $html = str_replace($image, $imagePath, $html); } @@ -111,13 +117,11 @@ class DownloadImages return false; } - $ext = $this->mimeGuesser->guess($res->getHeader('content-type')); - $this->logger->debug('DownloadImages: Checking extension', ['ext' => $ext, 'header' => $res->getHeader('content-type')]); - if (!in_array($ext, ['jpeg', 'jpg', 'gif', 'png'], true)) { - $this->logger->error('DownloadImages: Processed image with not allowed extension. Skipping '.$imagePath); - + $ext = $this->getExtensionFromResponse($res, $imagePath); + if (false === $res) { return false; } + $hashImage = hash('crc32', $absolutePath); $localPath = $folderPath.'/'.$hashImage.'.'.$ext; @@ -232,4 +236,45 @@ class DownloadImages return false; } + + /** + * Retrieve and validate the extension from the response of the url of the image. + * + * @param Response $res Guzzle Response + * @param string $imagePath Path from the src image from the content (used for log only) + * + * @return string|false Extension name or false if validation failed + */ + private function getExtensionFromResponse(Response $res, $imagePath) + { + $ext = $this->mimeGuesser->guess($res->getHeader('content-type')); + $this->logger->debug('DownloadImages: Checking extension', ['ext' => $ext, 'header' => $res->getHeader('content-type')]); + + // ok header doesn't have the extension, try a different way + if (empty($ext)) { + $types = [ + 'jpeg' => "\xFF\xD8\xFF", + 'gif' => 'GIF', + 'png' => "\x89\x50\x4e\x47\x0d\x0a", + ]; + $bytes = substr((string) $res->getBody(), 0, 8); + + foreach ($types as $type => $header) { + if (0 === strpos($bytes, $header)) { + $ext = $type; + break; + } + } + + $this->logger->debug('DownloadImages: Checking extension (alternative)', ['ext' => $ext]); + } + + if (!in_array($ext, ['jpeg', 'jpg', 'gif', 'png'], true)) { + $this->logger->error('DownloadImages: Processed image with not allowed extension. Skipping: '.$imagePath); + + return false; + } + + return $ext; + } } diff --git a/src/Wallabag/CoreBundle/Helper/HttpClientFactory.php b/src/Wallabag/CoreBundle/Helper/HttpClientFactory.php index 1ac8feb1..43f5b119 100644 --- a/src/Wallabag/CoreBundle/Helper/HttpClientFactory.php +++ b/src/Wallabag/CoreBundle/Helper/HttpClientFactory.php @@ -13,8 +13,8 @@ use Psr\Log\LoggerInterface; */ class HttpClientFactory { - /** @var \GuzzleHttp\Event\SubscriberInterface */ - private $authenticatorSubscriber; + /** @var [\GuzzleHttp\Event\SubscriberInterface] */ + private $subscribers = []; /** @var \GuzzleHttp\Cookie\CookieJar */ private $cookieJar; @@ -25,14 +25,12 @@ class HttpClientFactory /** * HttpClientFactory constructor. * - * @param \GuzzleHttp\Event\SubscriberInterface $authenticatorSubscriber - * @param \GuzzleHttp\Cookie\CookieJar $cookieJar - * @param string $restrictedAccess this param is a kind of boolean. Values: 0 or 1 - * @param LoggerInterface $logger + * @param \GuzzleHttp\Cookie\CookieJar $cookieJar + * @param string $restrictedAccess This param is a kind of boolean. Values: 0 or 1 + * @param LoggerInterface $logger */ - public function __construct(SubscriberInterface $authenticatorSubscriber, CookieJar $cookieJar, $restrictedAccess, LoggerInterface $logger) + public function __construct(CookieJar $cookieJar, $restrictedAccess, LoggerInterface $logger) { - $this->authenticatorSubscriber = $authenticatorSubscriber; $this->cookieJar = $cookieJar; $this->restrictedAccess = $restrictedAccess; $this->logger = $logger; @@ -53,8 +51,21 @@ class HttpClientFactory $this->cookieJar->clear(); // need to set the (shared) cookie jar $client = new Client(['handler' => new SafeCurlHandler(), 'defaults' => ['cookies' => $this->cookieJar]]); - $client->getEmitter()->attach($this->authenticatorSubscriber); + + foreach ($this->subscribers as $subscriber) { + $client->getEmitter()->attach($subscriber); + } return $client; } + + /** + * Adds a subscriber to the HTTP client. + * + * @param SubscriberInterface $subscriber + */ + public function addSubscriber(SubscriberInterface $subscriber) + { + $this->subscribers[] = $subscriber; + } } diff --git a/src/Wallabag/CoreBundle/Helper/PreparePagerForEntries.php b/src/Wallabag/CoreBundle/Helper/PreparePagerForEntries.php index 7d3798b9..231a0b52 100644 --- a/src/Wallabag/CoreBundle/Helper/PreparePagerForEntries.php +++ b/src/Wallabag/CoreBundle/Helper/PreparePagerForEntries.php @@ -4,6 +4,7 @@ namespace Wallabag\CoreBundle\Helper; use Pagerfanta\Adapter\AdapterInterface; use Pagerfanta\Pagerfanta; +use Wallabag\UserBundle\Entity\User; use Symfony\Component\Routing\Router; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; @@ -20,16 +21,18 @@ class PreparePagerForEntries /** * @param AdapterInterface $adapter - * @param int $page + * @param User $user If user isn't logged in, we can force it (like for rss) * * @return null|Pagerfanta */ - public function prepare(AdapterInterface $adapter, $page = 1) + public function prepare(AdapterInterface $adapter, User $user = null) { - $user = $this->tokenStorage->getToken() ? $this->tokenStorage->getToken()->getUser() : null; + if (null === $user) { + $user = $this->tokenStorage->getToken() ? $this->tokenStorage->getToken()->getUser() : null; + } if (null === $user || !is_object($user)) { - return null; + return; } $entries = new Pagerfanta($adapter); diff --git a/src/Wallabag/CoreBundle/Helper/Redirect.php b/src/Wallabag/CoreBundle/Helper/Redirect.php index f78b7fe0..abc84d08 100644 --- a/src/Wallabag/CoreBundle/Helper/Redirect.php +++ b/src/Wallabag/CoreBundle/Helper/Redirect.php @@ -21,12 +21,13 @@ class Redirect } /** - * @param string $url URL to redirect - * @param string $fallback Fallback URL if $url is null + * @param string $url URL to redirect + * @param string $fallback Fallback URL if $url is null + * @param bool $ignoreActionMarkAsRead Ignore configured action when mark as read * * @return string */ - public function to($url, $fallback = '') + public function to($url, $fallback = '', $ignoreActionMarkAsRead = false) { $user = $this->tokenStorage->getToken() ? $this->tokenStorage->getToken()->getUser() : null; @@ -34,7 +35,8 @@ class Redirect return $url; } - if (Config::REDIRECT_TO_HOMEPAGE === $user->getConfig()->getActionMarkAsRead()) { + if (!$ignoreActionMarkAsRead && + Config::REDIRECT_TO_HOMEPAGE === $user->getConfig()->getActionMarkAsRead()) { return $this->router->generate('homepage'); } diff --git a/src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php b/src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php index b490e209..509d0dec 100644 --- a/src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php +++ b/src/Wallabag/CoreBundle/Helper/RuleBasedTagger.php @@ -8,18 +8,21 @@ use Wallabag\CoreBundle\Entity\Tag; use Wallabag\CoreBundle\Repository\EntryRepository; use Wallabag\CoreBundle\Repository\TagRepository; use Wallabag\UserBundle\Entity\User; +use Psr\Log\LoggerInterface; class RuleBasedTagger { private $rulerz; private $tagRepository; private $entryRepository; + private $logger; - public function __construct(RulerZ $rulerz, TagRepository $tagRepository, EntryRepository $entryRepository) + public function __construct(RulerZ $rulerz, TagRepository $tagRepository, EntryRepository $entryRepository, LoggerInterface $logger) { $this->rulerz = $rulerz; $this->tagRepository = $tagRepository; $this->entryRepository = $entryRepository; + $this->logger = $logger; } /** @@ -36,6 +39,11 @@ class RuleBasedTagger continue; } + $this->logger->info('Matching rule.', [ + 'rule' => $rule->getRule(), + 'tags' => $rule->getTags(), + ]); + foreach ($rule->getTags() as $label) { $tag = $this->getTag($label); diff --git a/src/Wallabag/CoreBundle/Helper/TagsAssigner.php b/src/Wallabag/CoreBundle/Helper/TagsAssigner.php new file mode 100644 index 00000000..a2fb0b9a --- /dev/null +++ b/src/Wallabag/CoreBundle/Helper/TagsAssigner.php @@ -0,0 +1,75 @@ +tagRepository = $tagRepository; + } + + /** + * Assign some tags to an entry. + * + * @param Entry $entry + * @param array|string $tags An array of tag or a string coma separated of tag + * @param array $entitiesReady Entities from the EntityManager which are persisted but not yet flushed + * It is mostly to fix duplicate tag on import @see http://stackoverflow.com/a/7879164/569101 + * + * @return Tag[] + */ + public function assignTagsToEntry(Entry $entry, $tags, array $entitiesReady = []) + { + $tagsEntities = []; + + if (!is_array($tags)) { + $tags = explode(',', $tags); + } + + // keeps only Tag entity from the "not yet flushed entities" + $tagsNotYetFlushed = []; + foreach ($entitiesReady as $entity) { + if ($entity instanceof Tag) { + $tagsNotYetFlushed[$entity->getLabel()] = $entity; + } + } + + foreach ($tags as $label) { + $label = trim($label); + + // avoid empty tag + if (0 === strlen($label)) { + continue; + } + + if (isset($tagsNotYetFlushed[$label])) { + $tagEntity = $tagsNotYetFlushed[$label]; + } else { + $tagEntity = $this->tagRepository->findOneByLabel($label); + + if (null === $tagEntity) { + $tagEntity = new Tag(); + $tagEntity->setLabel($label); + } + } + + // only add the tag on the entry if the relation doesn't exist + if (false === $entry->getTags()->contains($tagEntity)) { + $entry->addTag($tagEntity); + $tagsEntities[] = $tagEntity; + } + } + + return $tagsEntities; + } +} diff --git a/src/Wallabag/CoreBundle/Operator/Doctrine/NotMatches.php b/src/Wallabag/CoreBundle/Operator/Doctrine/NotMatches.php new file mode 100644 index 00000000..b7f9da57 --- /dev/null +++ b/src/Wallabag/CoreBundle/Operator/Doctrine/NotMatches.php @@ -0,0 +1,25 @@ +createQueryBuilder('e') ->leftJoin('e.tags', 't') ->where('e.user =:userId')->setParameter('userId', $userId); if (null !== $isArchived) { - $qb->andWhere('e.isArchived =:isArchived')->setParameter('isArchived', (bool) $isArchived); + $qb->andWhere('e.isArchived = :isArchived')->setParameter('isArchived', (bool) $isArchived); } if (null !== $isStarred) { - $qb->andWhere('e.isStarred =:isStarred')->setParameter('isStarred', (bool) $isStarred); + $qb->andWhere('e.isStarred = :isStarred')->setParameter('isStarred', (bool) $isStarred); + } + + if (null !== $isPublic) { + $qb->andWhere('e.uid IS '.(true === $isPublic ? 'NOT' : '').' NULL'); } if ($since > 0) { @@ -328,7 +333,7 @@ class EntryRepository extends EntityRepository * * @return int */ - public function countAllEntriesByUsername($userId) + public function countAllEntriesByUser($userId) { $qb = $this->createQueryBuilder('e') ->select('count(e)') @@ -371,4 +376,42 @@ class EntryRepository extends EntityRepository ->setParameter('userId', $userId) ->execute(); } + + public function removeArchivedByUserId($userId) + { + $this->getEntityManager() + ->createQuery('DELETE FROM Wallabag\CoreBundle\Entity\Entry e WHERE e.user = :userId AND e.isArchived = TRUE') + ->setParameter('userId', $userId) + ->execute(); + } + + /** + * Get id and url from all entries + * Used for the clean-duplicates command. + */ + public function getAllEntriesIdAndUrl($userId) + { + $qb = $this->createQueryBuilder('e') + ->select('e.id, e.url') + ->where('e.user = :userid')->setParameter(':userid', $userId); + + return $qb->getQuery()->getArrayResult(); + } + + /** + * Find all entries by url and owner. + * + * @param $url + * @param $userId + * + * @return array + */ + public function findAllByUrlAndUserId($url, $userId) + { + return $this->createQueryBuilder('e') + ->where('e.url = :url')->setParameter('url', urldecode($url)) + ->andWhere('e.user = :user_id')->setParameter('user_id', $userId) + ->getQuery() + ->getResult(); + } } diff --git a/src/Wallabag/CoreBundle/Repository/SiteCredentialRepository.php b/src/Wallabag/CoreBundle/Repository/SiteCredentialRepository.php new file mode 100644 index 00000000..36906761 --- /dev/null +++ b/src/Wallabag/CoreBundle/Repository/SiteCredentialRepository.php @@ -0,0 +1,47 @@ +cryptoProxy = $cryptoProxy; + } + + /** + * Retrieve one username/password for the given host and userId. + * + * @param string $host + * @param int $userId + * + * @return null|array + */ + public function findOneByHostAndUser($host, $userId) + { + $res = $this->createQueryBuilder('s') + ->select('s.username', 's.password') + ->where('s.host = :hostname')->setParameter('hostname', $host) + ->andWhere('s.user = :userId')->setParameter('userId', $userId) + ->setMaxResults(1) + ->getQuery() + ->getOneOrNullResult(); + + if (null === $res) { + return; + } + + // decrypt user & password before returning them + $res['username'] = $this->cryptoProxy->decrypt($res['username']); + $res['password'] = $this->cryptoProxy->decrypt($res['password']); + + return $res; + } +} diff --git a/src/Wallabag/CoreBundle/Repository/TagRepository.php b/src/Wallabag/CoreBundle/Repository/TagRepository.php index 2182df25..6c63a6a2 100644 --- a/src/Wallabag/CoreBundle/Repository/TagRepository.php +++ b/src/Wallabag/CoreBundle/Repository/TagRepository.php @@ -76,4 +76,24 @@ class TagRepository extends EntityRepository ->getQuery() ->getSingleResult(); } + + public function findForArchivedArticlesByUser($userId) + { + $ids = $this->createQueryBuilder('t') + ->select('t.id') + ->leftJoin('t.entries', 'e') + ->where('e.user = :userId')->setParameter('userId', $userId) + ->andWhere('e.isArchived = true') + ->groupBy('t.id') + ->orderBy('t.slug') + ->getQuery() + ->getArrayResult(); + + $tags = []; + foreach ($ids as $id) { + $tags[] = $this->find($id); + } + + return $tags; + } } diff --git a/src/Wallabag/CoreBundle/Resources/config/services.yml b/src/Wallabag/CoreBundle/Resources/config/services.yml index 51d6ab47..e09b0f18 100644 --- a/src/Wallabag/CoreBundle/Resources/config/services.yml +++ b/src/Wallabag/CoreBundle/Resources/config/services.yml @@ -41,6 +41,7 @@ services: arguments: - error_message: '%wallabag_core.fetching_error_message%' + error_message_title: '%wallabag_core.fetching_error_message_title%' - "@wallabag_core.guzzle.http_client" - "@wallabag_core.graby.config_builder" calls: @@ -62,7 +63,11 @@ services: class: Wallabag\CoreBundle\GuzzleSiteAuthenticator\GrabySiteConfigBuilder arguments: - "@wallabag_core.graby.config_builder" - - "%sites_credentials%" + - "@security.token_storage" + - "@wallabag_core.site_credential_repository" + - '@logger' + tags: + - { name: monolog.logger, channel: graby } # service alias override bd_guzzle_site_authenticator.site_config_builder: @@ -71,10 +76,11 @@ services: wallabag_core.guzzle.http_client_factory: class: Wallabag\CoreBundle\Helper\HttpClientFactory arguments: - - "@bd_guzzle_site_authenticator.authenticator_subscriber" - "@wallabag_core.guzzle.cookie_jar" - '@=service(''craue_config'').get(''restricted_access'')' - '@logger' + calls: + - ["addSubscriber", ["@bd_guzzle_site_authenticator.authenticator_subscriber"]] wallabag_core.guzzle.cookie_jar: class: GuzzleHttp\Cookie\FileCookieJar @@ -85,16 +91,22 @@ services: arguments: - "@wallabag_core.graby" - "@wallabag_core.rule_based_tagger" - - "@wallabag_core.tag_repository" + - "@validator" - "@logger" - '%wallabag_core.fetching_error_message%' + wallabag_core.tags_assigner: + class: Wallabag\CoreBundle\Helper\TagsAssigner + arguments: + - "@wallabag_core.tag_repository" + wallabag_core.rule_based_tagger: class: Wallabag\CoreBundle\Helper\RuleBasedTagger arguments: - "@rulerz" - "@wallabag_core.tag_repository" - "@wallabag_core.entry_repository" + - "@logger" # repository as a service wallabag_core.entry_repository: @@ -109,10 +121,18 @@ services: arguments: - WallabagCoreBundle:Tag + wallabag_core.site_credential_repository: + class: Wallabag\CoreBundle\Repository\SiteCredentialRepository + factory: [ "@doctrine.orm.default_entity_manager", getRepository ] + arguments: + - WallabagCoreBundle:SiteCredential + calls: + - [ setCrypto, [ "@wallabag_core.helper.crypto_proxy" ] ] + wallabag_core.helper.entries_export: class: Wallabag\CoreBundle\Helper\EntriesExport arguments: - - '@=service(''craue_config'').get(''wallabag_url'')' + - '%domain_name%' - src/Wallabag/CoreBundle/Resources/public/themes/_global/img/appicon/apple-touch-icon-152.png wallabag.operator.array.matches: @@ -125,6 +145,16 @@ services: tags: - { name: rulerz.operator, target: doctrine, operator: matches, inline: true } + wallabag.operator.array.notmatches: + class: Wallabag\CoreBundle\Operator\PHP\NotMatches + tags: + - { name: rulerz.operator, target: native, operator: notmatches } + + wallabag.operator.doctrine.notmatches: + class: Wallabag\CoreBundle\Operator\Doctrine\NotMatches + tags: + - { name: rulerz.operator, target: doctrine, operator: notmatches, inline: true } + wallabag_core.helper.redirect: class: Wallabag\CoreBundle\Helper\Redirect arguments: @@ -175,8 +205,14 @@ services: arguments: - "@wallabag_core.entry.download_images.client" - "%kernel.root_dir%/../web/assets/images" - - '@=service(''craue_config'').get(''wallabag_url'')' + - '%domain_name%' - "@logger" wallabag_core.entry.download_images.client: class: GuzzleHttp\Client + + wallabag_core.helper.crypto_proxy: + class: Wallabag\CoreBundle\Helper\CryptoProxy + arguments: + - "%wallabag_core.site_credentials.encryption_key_path%" + - "@logger" diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml index a52b579a..5229ac73 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml @@ -32,6 +32,7 @@ menu: # save_link: 'Save a link' back_to_unread: 'Tilbage til de ulæste artikler' # users_management: 'Users management' + # site_credentials: 'Site credentials' top: add_new_entry: 'Tilføj ny artikel' search: 'Søg' @@ -76,6 +77,7 @@ config: # redirect_current_page: 'To the current page' pocket_consumer_key_label: Brugers nøgle til Pocket for at importere materialer # android_configuration: Configure your Android application + # android_instruction: "Touch here to prefill your Android application" # help_theme: "wallabag is customizable. You can choose your prefered theme here." # help_items_per_page: "You can change the number of articles displayed on each page." # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article." @@ -89,9 +91,10 @@ config: token_reset: 'Nulstil token' rss_links: 'RSS-Links' rss_link: - unread: 'ulæst' - starred: 'favoritter' - archive: 'arkiv' + unread: 'Ulæst' + starred: 'Favoritter' + archive: 'Arkiv' + # all: 'All' # rss_limit: 'Number of items in the feed' form_user: # two_factor_description: "Enabling two factor authentication means you'll receive an email with a code on every new untrusted connexion" @@ -110,6 +113,7 @@ config: # annotations: Remove ALL annotations # tags: Remove ALL tags # entries: Remove ALL entries + # archived: Remove ALL archived entries # confirm: Are you really really sure? (THIS CAN'T BE UNDONE) form_password: # description: "You can change your password here. Your new password should by at least 8 characters long." @@ -154,6 +158,7 @@ config: # or: 'One rule OR another' # and: 'One rule AND another' # matches: 'Tests that a subject is matches a search (case-insensitive).
Example: title matches "football"' + # notmatches: 'Tests that a subject is not matches a search (case-insensitive).
Example: title notmatches "football"' entry: page_titles: @@ -185,6 +190,8 @@ entry: unread_label: 'Ulæst' preview_picture_label: 'Har et vist billede' preview_picture_help: 'Forhåndsvis billede' + # is_public_label: 'Has a public link' + # is_public_help: 'Public link' language_label: 'Sprog' # http_status_label: 'HTTP status' reading_time: @@ -223,6 +230,8 @@ entry: original_article: 'original' # annotations_on_the_entry: '{0} No annotations|{1} One annotation|]1,Inf[ %count% annotations' created_at: 'Oprettelsesdato' + # published_at: 'Publication date' + # published_by: 'Published by' new: page_title: 'Gem ny artikel' placeholder: 'http://website.com' @@ -234,10 +243,12 @@ entry: # page_title: 'Edit an entry' # title_label: 'Title' url_label: 'Url' - # is_public_label: 'Public' save_label: 'Gem' public: - # shared_by_wallabag: "This article has been shared by wallabag" + # shared_by_wallabag: "This article has been shared by %username% with wallabag" + confirm: + # delete: "Are you sure you want to remove that article?" + # delete_tag: "Are you sure you want to remove that tag from that article?" about: page_title: 'Om' @@ -510,6 +521,28 @@ user: # delete: Delete # delete_confirm: Are you sure? # back_to_list: Back to list + search: + # placeholder: Filter by username or email + +site_credential: + # page_title: Site credentials management + # new_site_credential: Create a credential + # edit_site_credential: Edit an existing credential + # description: "Here you can manage all credentials for sites which required them (create, edit and delete), like a paywall, an authentication, etc." + # list: + # actions: Actions + # edit_action: Edit + # yes: Yes + # no: No + # create_new_one: Create a new credential + # form: + # username_label: 'Username' + # host_label: 'Host' + # password_label: 'Password' + # save: Save + # delete: Delete + # delete_confirm: Are you sure? + # back_to_list: Back to list error: # page_title: An error occurred @@ -528,6 +561,7 @@ flashes: # annotations_reset: Annotations reset # tags_reset: Tags reset # entries_reset: Entries reset + # archived_reset: Archived entries deleted entry: notice: # entry_already_saved: 'Entry already saved on %date%' @@ -562,3 +596,8 @@ flashes: # added: 'User "%username%" added' # updated: 'User "%username%" updated' # deleted: 'User "%username%" deleted' + site_credential: + notice: + # added: 'Site credential for "%host%" added' + # updated: 'Site credential for "%host%" updated' + # deleted: 'Site credential for "%host%" deleted' diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml index 35cb4b5b..996f173a 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml @@ -32,6 +32,7 @@ menu: save_link: 'Link speichern' back_to_unread: 'Zurück zu ungelesenen Artikeln' users_management: 'Benutzerverwaltung' + # site_credentials: 'Site credentials' top: add_new_entry: 'Neuen Artikel hinzufügen' search: 'Suche' @@ -76,6 +77,7 @@ config: redirect_current_page: 'Zur aktuellen Seite' pocket_consumer_key_label: Consumer-Key für Pocket, um Inhalte zu importieren android_configuration: Konfiguriere deine Android Application + # android_instruction: "Touch here to prefill your Android application" help_theme: "wallabag ist anpassbar. Du kannst dein bevorzugtes Theme hier auswählen." help_items_per_page: "Du kannst die Nummer von Artikeln pro Seite anpassen." help_reading_speed: "wallabag berechnet eine Lesezeit pro Artikel. Hier kannst du definieren, ob du ein schneller oder langsamer Leser bist. wallabag wird die Lesezeiten danach neu berechnen." @@ -92,6 +94,7 @@ config: unread: 'Ungelesene' starred: 'Favoriten' archive: 'Archivierte' + # all: 'All' rss_limit: 'Anzahl der Einträge pro Feed' form_user: two_factor_description: "Wenn du die Zwei-Faktor-Authentifizierung aktivierst, erhältst du eine E-Mail mit einem Code bei jeder nicht vertrauenswürdigen Verbindung" @@ -110,6 +113,7 @@ config: annotations: Entferne ALLE Annotationen tags: Entferne ALLE Tags entries: Entferne ALLE Einträge + archived: Entferne ALLE archivierten Einträge confirm: Bist du wirklich sicher? (DIES KANN NICHT RÜCKGÄNGIG GEMACHT WERDEN) form_password: description: "Hier kannst du dein Kennwort ändern. Dieses sollte mindestens acht Zeichen enthalten." @@ -154,6 +158,7 @@ config: or: 'Eine Regel ODER die andere' and: 'Eine Regel UND eine andere' matches: 'Testet, ob eine Variable auf eine Suche zutrifft (Groß- und Kleinschreibung wird nicht berücksichtigt).
Beispiel: title matches "Fußball"' + notmatches: 'Testet, ob ein Titel nicht auf eine Suche zutrifft (Groß- und Kleinschreibung wird nicht berücksichtigt).
Beispiel: title notmatches "Fußball"' entry: page_titles: @@ -185,6 +190,8 @@ entry: unread_label: 'Ungelesene' preview_picture_label: 'Vorschaubild vorhanden' preview_picture_help: 'Vorschaubild' + # is_public_label: 'Has a public link' + # is_public_help: 'Public link' language_label: 'Sprache' http_status_label: 'HTTP-Status' reading_time: @@ -223,6 +230,8 @@ entry: original_article: 'original' annotations_on_the_entry: '{0} Keine Anmerkungen|{1} Eine Anmerkung|]1,Inf[ %count% Anmerkungen' created_at: 'Erstellungsdatum' + published_at: 'Erscheinungsdatum' + published_by: 'Veröffentlicht von' new: page_title: 'Neuen Artikel speichern' placeholder: 'https://website.de' @@ -234,10 +243,12 @@ entry: page_title: 'Eintrag bearbeiten' title_label: 'Titel' url_label: 'URL' - is_public_label: 'Öffentlich' save_label: 'Speichern' public: - shared_by_wallabag: "Dieser Artikel wurde mittels wallabag geteilt" + shared_by_wallabag: "Dieser Artikel wurde von %username% mittels wallabag geteilt" + confirm: + delete: "Bist du sicher, dass du diesen Artikel löschen möchtest?" + delete_tag: "Bist du sicher, dass du diesen Tag vom Artikel entfernen möchtest?" about: page_title: 'Über' @@ -510,6 +521,28 @@ user: delete: Löschen delete_confirm: Bist du sicher? back_to_list: Zurück zur Liste + search: + placeholder: Filtere nach Benutzer oder E-Mail-Adresse + +site_credential: + # page_title: Site credentials management + # new_site_credential: Create a credential + # edit_site_credential: Edit an existing credential + # description: "Here you can manage all credentials for sites which required them (create, edit and delete), like a paywall, an authentication, etc." + list: + actions: Aktionen + edit_action: Bearbeiten + yes: Ja + no: Nein + # create_new_one: Create a new credential + form: + # username_label: 'Username' + # host_label: 'Host' + # password_label: 'Password' + save: Speichern + delete: Löschen + delete_confirm: Bist du sicher? + back_to_list: Zurück zur Liste error: page_title: Ein Fehler ist aufgetreten @@ -528,6 +561,7 @@ flashes: annotations_reset: Anmerkungen zurücksetzen tags_reset: Tags zurücksetzen entries_reset: Einträge zurücksetzen + archived_reset: Archiverte Einträge zurücksetzen entry: notice: entry_already_saved: 'Eintrag bereits am %date% gespeichert' @@ -562,3 +596,8 @@ flashes: added: 'Benutzer "%username%" hinzugefügt' updated: 'Benutzer "%username%" aktualisiert' deleted: 'Benutzer "%username%" gelöscht' + site_credential: + notice: + # added: 'Site credential for "%host%" added' + # updated: 'Site credential for "%host%" updated' + # deleted: 'Site credential for "%host%" deleted' diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml index c72e5958..aa1cd1a9 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml @@ -32,6 +32,7 @@ menu: save_link: 'Save a link' back_to_unread: 'Back to unread articles' users_management: 'Users management' + site_credentials: 'Site credentials' top: add_new_entry: 'Add a new entry' search: 'Search' @@ -76,6 +77,7 @@ config: redirect_current_page: 'To the current page' pocket_consumer_key_label: Consumer key for Pocket to import contents android_configuration: Configure your Android application + android_instruction: "Touch here to prefill your Android application" help_theme: "wallabag is customizable. You can choose your prefered theme here." help_items_per_page: "You can change the number of articles displayed on each page." help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article." @@ -89,9 +91,10 @@ config: token_reset: 'Regenerate your token' rss_links: 'RSS links' rss_link: - unread: 'unread' - starred: 'starred' - archive: 'archived' + unread: 'Unread' + starred: 'Starred' + archive: 'Archived' + all: 'All' rss_limit: 'Number of items in the feed' form_user: two_factor_description: "Enabling two factor authentication means you'll receive an email with a code on every new untrusted connection." @@ -110,6 +113,7 @@ config: annotations: Remove ALL annotations tags: Remove ALL tags entries: Remove ALL entries + archived: Remove ALL archived entries confirm: Are you really sure? (THIS CAN'T BE UNDONE) form_password: description: "You can change your password here. Your new password should by at least 8 characters long." @@ -154,6 +158,7 @@ config: or: 'One rule OR another' and: 'One rule AND another' matches: 'Tests that a subject is matches a search (case-insensitive).
Example: title matches "football"' + notmatches: 'Tests that a subject is not matches a search (case-insensitive).
Example: title notmatches "football"' entry: page_titles: @@ -185,6 +190,8 @@ entry: unread_label: 'Unread' preview_picture_label: 'Has a preview picture' preview_picture_help: 'Preview picture' + is_public_label: 'Has a public link' + is_public_help: 'Public link' language_label: 'Language' http_status_label: 'HTTP status' reading_time: @@ -223,6 +230,8 @@ entry: original_article: 'original' annotations_on_the_entry: '{0} No annotations|{1} One annotation|]1,Inf[ %count% annotations' created_at: 'Creation date' + published_at: 'Publication date' + published_by: 'Published by' new: page_title: 'Save new entry' placeholder: 'http://website.com' @@ -234,10 +243,12 @@ entry: page_title: 'Edit an entry' title_label: 'Title' url_label: 'Url' - is_public_label: 'Public' save_label: 'Save' public: - shared_by_wallabag: "This article has been shared by wallabag" + shared_by_wallabag: "This article has been shared by %username% with wallabag" + confirm: + delete: "Are you sure you want to remove that article?" + delete_tag: "Are you sure you want to remove that tag from that article?" about: page_title: 'About' @@ -510,6 +521,28 @@ user: delete: Delete delete_confirm: Are you sure? back_to_list: Back to list + search: + placeholder: Filter by username or email + +site_credential: + page_title: Site credentials management + new_site_credential: Create a credential + edit_site_credential: Edit an existing credential + description: "Here you can manage all credentials for sites which required them (create, edit and delete), like a paywall, an authentication, etc." + list: + actions: Actions + edit_action: Edit + yes: Yes + no: No + create_new_one: Create a new credential + form: + username_label: 'Username' + host_label: 'Host' + password_label: 'Password' + save: Save + delete: Delete + delete_confirm: Are you sure? + back_to_list: Back to list error: page_title: An error occurred @@ -528,6 +561,7 @@ flashes: annotations_reset: Annotations reset tags_reset: Tags reset entries_reset: Entries reset + archived_reset: Archived entries deleted entry: notice: entry_already_saved: 'Entry already saved on %date%' @@ -562,3 +596,8 @@ flashes: added: 'User "%username%" added' updated: 'User "%username%" updated' deleted: 'User "%username%" deleted' + site_credential: + notice: + added: 'Site credential for "%host%" added' + updated: 'Site credential for "%host%" updated' + deleted: 'Site credential for "%host%" deleted' diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml index 2f769b7e..96998f53 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml @@ -32,6 +32,7 @@ menu: save_link: 'Guardar un enlace' back_to_unread: 'Volver a los artículos sin leer' users_management: 'Configuración de usuarios' + # site_credentials: 'Site credentials' top: add_new_entry: 'Añadir un nuevo artículo' search: 'Buscar' @@ -76,6 +77,7 @@ config: redirect_current_page: 'A la página actual' pocket_consumer_key_label: Clave de consumidor para importar contenidos de Pocket android_configuration: Configura tu aplicación Android + # android_instruction: "Touch here to prefill your Android application" help_theme: "wallabag es personalizable. Puedes elegir tu tema preferido aquí." help_items_per_page: "Puedes cambiar el número de artículos mostrados en cada página." help_reading_speed: "wallabag calcula un tiempo de lectura para cada artículo. Puedes definir aquí, gracias a esta lista, si eres un lector rápido o lento. wallabag recalculará el tiempo de lectura para cada artículo." @@ -92,6 +94,7 @@ config: unread: 'sin leer' starred: 'favoritos' archive: 'archivados' + # all: 'All' rss_limit: 'Límite de artículos en feed RSS' form_user: two_factor_description: "Con la autenticación en dos pasos recibirá código por e-mail en cada nueva conexión que no sea de confianza." @@ -110,6 +113,7 @@ config: annotations: Eliminar TODAS las anotaciones tags: Eliminar TODAS las etiquetas entries: Eliminar TODOS los artículos + # archived: Remove ALL archived entries confirm: ¿Estás completamente seguro? (NO SE PUEDE DESHACER) form_password: description: "Puedes cambiar la contraseña aquí. Tu nueva contraseña debe tener al menos 8 caracteres." @@ -154,6 +158,7 @@ config: or: 'Una regla U otra' and: 'Una regla Y la otra' matches: 'Prueba si un sujeto corresponde a una búsqueda (insensible a mayusculas).
Ejemplo : title matches "fútbol"' + # notmatches: 'Tests that a subject is not matches a search (case-insensitive).
Example: title notmatches "football"' entry: page_titles: @@ -185,6 +190,8 @@ entry: unread_label: 'Sin leer' preview_picture_label: 'Tiene imagen de previsualización' preview_picture_help: 'Imagen de previsualización' + # is_public_label: 'Has a public link' + # is_public_help: 'Public link' language_label: 'Idioma' http_status_label: 'Código de estado HTTP' reading_time: @@ -223,6 +230,8 @@ entry: original_article: 'original' annotations_on_the_entry: '{0} Sin anotaciones|{1} Una anotación|]1,Inf[ %count% anotaciones' created_at: 'Fecha de creación' + # published_at: 'Publication date' + # published_by: 'Published by' new: page_title: 'Guardar un nuevo artículo' placeholder: 'http://sitioweb.com' @@ -234,10 +243,12 @@ entry: page_title: 'Editar un artículo' title_label: 'Título' url_label: 'URL' - is_public_label: 'Es público' save_label: 'Guardar' public: shared_by_wallabag: "Este artículo se ha compartido con wallabag" + confirm: + # delete: "Are you sure you want to remove that article?" + # delete_tag: "Are you sure you want to remove that tag from that article?" about: page_title: 'Acerca de' @@ -510,6 +521,28 @@ user: delete: Eliminar delete_confirm: ¿Estás seguro? back_to_list: Volver a la lista + search: + # placeholder: Filter by username or email + +site_credential: + # page_title: Site credentials management + # new_site_credential: Create a credential + # edit_site_credential: Edit an existing credential + # description: "Here you can manage all credentials for sites which required them (create, edit and delete), like a paywall, an authentication, etc." + # list: + # actions: Actions + # edit_action: Edit + # yes: Yes + # no: No + # create_new_one: Create a new credential + # form: + # username_label: 'Username' + # host_label: 'Host' + # password_label: 'Password' + # save: Save + # delete: Delete + # delete_confirm: Are you sure? + # back_to_list: Back to list error: page_title: Ha ocurrido un error @@ -528,6 +561,7 @@ flashes: annotations_reset: Anotaciones reiniciadas tags_reset: Etiquetas reiniciadas entries_reset: Artículos reiniciados + # archived_reset: Archived entries deleted entry: notice: entry_already_saved: 'Artículo ya guardado el %fecha%' @@ -562,3 +596,8 @@ flashes: added: 'Añadido el usuario "%username%"' updated: 'Actualizado el usuario "%username%"' deleted: 'Eliminado el usuario "%username%"' + site_credential: + notice: + # added: 'Site credential for "%host%" added' + # updated: 'Site credential for "%host%" updated' + # deleted: 'Site credential for "%host%" deleted' diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml index 647f12ad..57e6c029 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml @@ -32,6 +32,7 @@ menu: save_link: 'ذخیرهٔ یک پیوند' back_to_unread: 'بازگشت به خوانده‌نشده‌ها' # users_management: 'Users management' + # site_credentials: 'Site credentials' top: add_new_entry: 'افزودن مقالهٔ تازه' search: 'جستجو' @@ -77,6 +78,7 @@ config: pocket_consumer_key_label: کلید کاربری Pocket برای درون‌ریزی مطالب # android_configuration: Configure your Android application # help_theme: "wallabag is customizable. You can choose your prefered theme here." + # android_instruction: "Touch here to prefill your Android application" # help_items_per_page: "You can change the number of articles displayed on each page." # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article." # help_language: "You can change the language of wallabag interface." @@ -92,6 +94,7 @@ config: unread: 'خوانده‌نشده' starred: 'برگزیده' archive: 'بایگانی' + # all: 'All' rss_limit: 'محدودیت آر-اس-اس' form_user: two_factor_description: "با فعال‌کردن تأیید ۲مرحله‌ای هر بار که اتصال تأییدنشده‌ای برقرار شد، به شما یک کد از راه ایمیل فرستاده می‌شود" @@ -110,6 +113,7 @@ config: # annotations: Remove ALL annotations # tags: Remove ALL tags # entries: Remove ALL entries + # archived: Remove ALL archived entries # confirm: Are you really really sure? (THIS CAN'T BE UNDONE) form_password: # description: "You can change your password here. Your new password should by at least 8 characters long." @@ -154,6 +158,7 @@ config: # or: 'One rule OR another' # and: 'One rule AND another' # matches: 'Tests that a subject is matches a search (case-insensitive).
Example: title matches "football"' + # notmatches: 'Tests that a subject is not matches a search (case-insensitive).
Example: title notmatches "football"' entry: page_titles: @@ -185,6 +190,8 @@ entry: unread_label: 'خوانده‌نشده' preview_picture_label: 'دارای عکس پیش‌نمایش' preview_picture_help: 'پیش‌نمایش عکس' + # is_public_label: 'Has a public link' + # is_public_help: 'Public link' language_label: 'زبان' # http_status_label: 'HTTP status' reading_time: @@ -223,6 +230,8 @@ entry: original_article: 'اصلی' annotations_on_the_entry: '{0} بدون حاشیه|{1} یک حاشیه|]1,Inf[ %nbحاشیه% annotations' created_at: 'زمان ساخت' + # published_at: 'Publication date' + # published_by: 'Published by' new: page_title: 'ذخیرهٔ مقالهٔ تازه' placeholder: 'http://website.com' @@ -234,10 +243,12 @@ entry: page_title: 'ویرایش مقاله' title_label: 'عنوان' url_label: 'نشانی' - is_public_label: 'عمومی' save_label: 'ذخیره' public: - # shared_by_wallabag: "This article has been shared by wallabag" + # shared_by_wallabag: "This article has been shared by %username% with wallabag" + confirm: + # delete: "Are you sure you want to remove that article?" + # delete_tag: "Are you sure you want to remove that tag from that article?" about: page_title: 'درباره' @@ -510,6 +521,28 @@ user: # delete: Delete # delete_confirm: Are you sure? # back_to_list: Back to list + search: + # placeholder: Filter by username or email + +site_credential: + # page_title: Site credentials management + # new_site_credential: Create a credential + # edit_site_credential: Edit an existing credential + # description: "Here you can manage all credentials for sites which required them (create, edit and delete), like a paywall, an authentication, etc." + # list: + # actions: Actions + # edit_action: Edit + # yes: Yes + # no: No + # create_new_one: Create a new credential + # form: + # username_label: 'Username' + # host_label: 'Host' + # password_label: 'Password' + # save: Save + # delete: Delete + # delete_confirm: Are you sure? + # back_to_list: Back to list error: # page_title: An error occurred @@ -528,6 +561,7 @@ flashes: # annotations_reset: Annotations reset # tags_reset: Tags reset # entries_reset: Entries reset + # archived_reset: Archived entries deleted entry: notice: entry_already_saved: 'این مقاله در تاریخ %date% ذخیره شده بود' @@ -562,3 +596,8 @@ flashes: # added: 'User "%username%" added' # updated: 'User "%username%" updated' # deleted: 'User "%username%" deleted' + site_credential: + notice: + # added: 'Site credential for "%host%" added' + # updated: 'Site credential for "%host%" updated' + # deleted: 'Site credential for "%host%" deleted' diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml index efddc46a..6eac4c36 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml @@ -32,6 +32,7 @@ menu: save_link: "Sauvegarder un nouvel article" back_to_unread: "Retour aux articles non lus" users_management: "Gestion des utilisateurs" + site_credentials: 'Accès aux sites' top: add_new_entry: "Sauvegarder un nouvel article" search: "Rechercher" @@ -46,7 +47,7 @@ footer: social: "Social" powered_by: "propulsé par" about: "À propos" - stats: Depuis le %user_creation%, vous avez lu %nb_archives% articles. Ce qui fait %per_day% par jour ! + stats: "Depuis le %user_creation%, vous avez lu %nb_archives% articles. Ce qui fait %per_day% par jour !" config: page_title: "Configuration" @@ -71,27 +72,29 @@ config: 300_word: "Je lis environ 300 mots par minute" 400_word: "Je lis environ 400 mots par minute" action_mark_as_read: - label: 'Où souhaitez-vous être redirigé après avoir marqué un article comme lu ?' - redirect_homepage: "À la page d'accueil" - redirect_current_page: 'À la page courante' - pocket_consumer_key_label: Clé d’authentification Pocket pour importer les données - android_configuration: Configurez votre application Android - help_theme: "L'affichage de wallabag est personnalisable. C'est ici que vous choisissez le thème que vous préférez." - help_items_per_page: "Vous pouvez définir le nombre d'articles affichés sur chaque page." + label: "Où souhaitez-vous être redirigé après avoir marqué un article comme lu ?" + redirect_homepage: "À la page d’accueil" + redirect_current_page: "À la page courante" + pocket_consumer_key_label: "Clé d’authentification Pocket pour importer les données" + android_configuration: "Configurez votre application Android" + android_instruction: "Appuyez ici pour préremplir votre application Android" + help_theme: "L’affichage de wallabag est personnalisable. C’est ici que vous choisissez le thème que vous préférez." + help_items_per_page: "Vous pouvez définir le nombre d’articles affichés sur chaque page." help_reading_speed: "wallabag calcule une durée de lecture pour chaque article. Vous pouvez définir ici, grâce à cette liste déroulante, si vous lisez plus ou moins vite. wallabag recalculera la durée de lecture de chaque article." - help_language: "Vous pouvez définir la langue de l'interface de wallabag." - help_pocket_consumer_key: "Nécessaire pour l'import depuis Pocket. Vous pouvez le créer depuis votre compte Pocket." + help_language: "Vous pouvez définir la langue de l’interface de wallabag." + help_pocket_consumer_key: "Nécessaire pour l’import depuis Pocket. Vous pouvez le créer depuis votre compte Pocket." form_rss: description: "Les flux RSS fournis par wallabag vous permettent de lire vos articles sauvegardés dans votre lecteur de flux préféré. Pour pouvoir les utiliser, vous devez d’abord créer un jeton." token_label: "Jeton RSS" no_token: "Aucun jeton généré" token_create: "Créez votre jeton" token_reset: "Réinitialisez votre jeton" - rss_links: "Adresse de vos flux RSS" + rss_links: "Adresses de vos flux RSS" rss_link: - unread: "non lus" - starred: "favoris" - archive: "lus" + unread: "Non lus" + starred: "Favoris" + archive: "Lus" + all: "Tous" rss_limit: "Nombre d’articles dans le flux" form_user: two_factor_description: "Activer l’authentification double-facteur veut dire que vous allez recevoir un code par courriel à chaque nouvelle connexion non approuvée." @@ -100,17 +103,18 @@ config: twoFactorAuthentication_label: "Double authentification" help_twoFactorAuthentication: "Si vous activez 2FA, à chaque tentative de connexion à wallabag, vous recevrez un code par email." delete: - title: Supprimer mon compte (attention danger !) - description: Si vous confirmez la suppression de votre compte, TOUS les articles, TOUS les tags, TOUTES les annotations et votre compte seront DÉFINITIVEMENT supprimé (c'est IRRÉVERSIBLE). Vous serez ensuite déconnecté. - confirm: Vous êtes vraiment sûr ? (C'EST IRRÉVERSIBLE) - button: 'Supprimer mon compte' + title: "Supprimer mon compte (attention danger !)" + description: "Si vous confirmez la suppression de votre compte, TOUS les articles, TOUS les tags, TOUTES les annotations et votre compte seront DÉFINITIVEMENT supprimé (c’est IRRÉVERSIBLE). Vous serez ensuite déconnecté." + confirm: "Vous êtes vraiment sûr ? (C’EST IRRÉVERSIBLE)" + button: "Supprimer mon compte" reset: - title: Réinitialisation (attention danger !) - description: En cliquant sur les boutons ci-dessous vous avez la possibilité de supprimer certaines informations de votre compte. Attention, ces actions sont IRRÉVERSIBLES ! - annotations: Supprimer TOUTES les annotations - tags: Supprimer TOUS les tags - entries: Supprimer TOUS les articles - confirm: Êtes-vous vraiment vraiment sûr ? (C'EST IRRÉVERSIBLE) + title: "Réinitialisation (attention danger !)" + description: "En cliquant sur les boutons ci-dessous vous avez la possibilité de supprimer certaines informations de votre compte. Attention, ces actions sont IRRÉVERSIBLES !" + annotations: "Supprimer TOUTES les annotations" + tags: "Supprimer TOUS les tags" + entries: "Supprimer TOUS les articles" + archived: "Supprimer TOUS les articles archivés" + confirm: "Êtes-vous vraiment vraiment sûr ? (C’EST IRRÉVERSIBLE)" form_password: description: "Vous pouvez changer ici votre mot de passe. Le mot de passe doit contenir au moins 8 caractères." old_password_label: "Mot de passe actuel" @@ -154,6 +158,7 @@ config: or: "Une règle OU l’autre" and: "Une règle ET l’autre" matches: "Teste si un sujet correspond à une recherche (non sensible à la casse).
Exemple : title matches \"football\"" + notmatches: "Teste si un sujet ne correspond pas à une recherche (non sensible à la casse).
Exemple : title notmatches \"football\"" entry: page_titles: @@ -162,7 +167,7 @@ entry: archived: "Articles lus" filtered: "Articles filtrés" filtered_tags: "Articles filtrés par tags :" - filtered_search: 'Articles filtrés par recherche :' + filtered_search: "Articles filtrés par recherche :" untagged: "Article sans tag" list: number_on_the_page: "{0} Il n’y a pas d’article.|{1} Il y a un article.|]1,Inf[ Il y a %count% articles." @@ -185,8 +190,10 @@ entry: unread_label: "Non lus" preview_picture_label: "A une photo" preview_picture_help: "Photo" + is_public_label: 'A un lien public' + is_public_help: 'Lien public' language_label: "Langue" - http_status_label: 'Statut HTTP' + http_status_label: "Statut HTTP" reading_time: label: "Durée de lecture en minutes" from: "de" @@ -223,6 +230,8 @@ entry: original_article: "original" annotations_on_the_entry: "{0} Aucune annotation|{1} Une annotation|]1,Inf[ %count% annotations" created_at: "Date de création" + published_at: "Date de publication" + published_by: "Publié par" new: page_title: "Sauvegarder un nouvel article" placeholder: "http://website.com" @@ -234,10 +243,12 @@ entry: page_title: "Éditer un article" title_label: "Titre" url_label: "Adresse" - is_public_label: "Public" save_label: "Enregistrer" public: - shared_by_wallabag: "Cet article a été partagé par wallabag" + shared_by_wallabag: "Cet article a été partagé par %username% avec wallabag" + confirm: + delete: "Voulez-vous vraiment supprimer cet article ?" + delete_tag: "Voulez-vous vraiment supprimer ce tag de cet article ?" about: page_title: "À propos" @@ -295,32 +306,32 @@ howto: bookmarklet: description: "Glissez et déposez ce lien dans votre barre de favoris :" shortcuts: - page_description: Voici les raccourcis disponibles dans wallabag. - shortcut: Raccourci - action: Action - all_pages_title: Raccourcis disponibles dans toutes les pages - go_unread: Afficher les articles non lus - go_starred: Afficher les articles favoris - go_archive: Afficher les articles lus - go_all: Afficher tous les articles - go_tags: Afficher les tags - go_config: Aller à la configuration - go_import: Aller aux imports - go_developers: Aller à la section Développeurs - go_howto: Afficher l'aide (cette page !) - go_logout: Se déconnecter - list_title: Raccourcis disponibles dans les pages de liste - search: Afficher le formulaire de recherche - article_title: Raccourcis disponibles quand on affiche un article - open_original: Ouvrir l'URL originale de l'article - toggle_favorite: Changer le statut Favori de l'article - toggle_archive: Changer le status Lu de l'article - delete: Supprimer l'article - material_title: Raccourcis disponibles avec le thème Material uniquement - add_link: Ajouter un nouvel article - hide_form: Masquer le formulaire courant (recherche ou nouvel article) - arrows_navigation: Naviguer à travers les articles - open_article: Afficher l'article sélectionné + page_description: "Voici les raccourcis disponibles dans wallabag." + shortcut: "Raccourci" + action: "Action" + all_pages_title: "Raccourcis disponibles dans toutes les pages" + go_unread: "Afficher les articles non lus" + go_starred: "Afficher les articles favoris" + go_archive: "Afficher les articles lus" + go_all: "Afficher tous les articles" + go_tags: "Afficher les tags" + go_config: "Aller à la configuration" + go_import: "Aller aux imports" + go_developers: "Aller à la section Développeurs" + go_howto: "Afficher l’aide (cette page !)" + go_logout: "Se déconnecter" + list_title: "Raccourcis disponibles dans les pages de liste" + search: "Afficher le formulaire de recherche" + article_title: "Raccourcis disponibles quand on affiche un article" + open_original: "Ouvrir l’URL originale de l’article" + toggle_favorite: "Changer le statut Favori de l’article" + toggle_archive: "Changer le status Lu de l’article" + delete: "Supprimer l’article" + material_title: "Raccourcis disponibles avec le thème Material uniquement" + add_link: "Ajouter un nouvel article" + hide_form: "Masquer le formulaire courant (recherche ou nouvel article)" + arrows_navigation: "Naviguer à travers les articles" + open_article: "Afficher l’article sélectionné" quickstart: page_title: "Pour bien débuter" @@ -382,8 +393,8 @@ tag: number_on_the_page: "{0} Il n’y a pas de tag.|{1} Il y a un tag.|]1,Inf[ Il y a %count% tags." see_untagged_entries: "Voir les articles sans tag" new: - add: 'Ajouter' - placeholder: 'Vous pouvez ajouter plusieurs tags, séparés par une virgule.' + add: "Ajouter" + placeholder: "Vous pouvez ajouter plusieurs tags, séparés par une virgule." import: page_title: "Importer" @@ -417,7 +428,7 @@ import: how_to: "Choisissez le fichier de votre export Readability et cliquez sur le bouton ci-dessous pour l’importer." worker: enabled: "Les imports sont asynchrones. Une fois l’import commencé un worker externe traitera les messages un par un. Le service activé est :" - download_images_warning: "Vous avez configuré le téléchagement des images pour vos articles. Combiné à l'import classique, cette opération peut être très très longue (voire échouer). Nous vous conseillons vivement d'activer les imports asynchrones." + download_images_warning: "Vous avez configuré le téléchagement des images pour vos articles. Combiné à l’import classique, cette opération peut être très très longue (voire échouer). Nous vous conseillons vivement d’activer les imports asynchrones." firefox: page_title: "Import > Firefox" description: "Cet outil va vous permettre d’importer tous vos marques-pages de Firefox. Ouvrez le panneau des marques-pages (Ctrl+Maj+O), puis dans « Importation et sauvegarde », choisissez « Sauvegarde… ». Vous allez récupérer un fichier .json.

" @@ -486,16 +497,16 @@ developer: back: "Retour" user: - page_title: Gestion des utilisateurs - new_user: Créer un nouvel utilisateur - edit_user: Éditer un utilisateur existant - description: Ici vous pouvez gérer vos utilisateurs (création, mise à jour et suppression) + page_title: "Gestion des utilisateurs" + new_user: "Créer un nouvel utilisateur" + edit_user: "Éditer un utilisateur existant" + description: "Ici vous pouvez gérer vos utilisateurs (création, mise à jour et suppression)" list: - actions: Actions - edit_action: Éditer - yes: Oui - no: Non - create_new_one: Créer un nouvel utilisateur + actions: "Actions" + edit_action: "Éditer" + yes: "Oui" + no: "Non" + create_new_one: "Créer un nouvel utilisateur" form: username_label: "Nom d’utilisateur" name_label: "Nom" @@ -508,11 +519,33 @@ user: twofactor_label: "Double authentification" save: "Sauvegarder" delete: "Supprimer" - delete_confirm: "Voulez-vous vraiment ?" + delete_confirm: "Êtes-vous sûr ?" + back_to_list: "Revenir à la liste" + search: + placeholder: "Filtrer par nom d’utilisateur ou email" + +site_credential: + page_title: Gestion des accès aux sites + new_site_credential: Créer un accès à un site + edit_site_credential: Éditer l'accès d'un site + description: "Ici vous pouvez gérer les accès aux différents sites. Ces accès permettent de récupérer des contenus sur des sites qui requièrent une authentification ou un paywall" + list: + actions: Actions + edit_action: Éditer + yes: Oui + no: Non + create_new_one: Créer un nouvel accès à un site + form: + username_label: 'Identifiant' + host_label: 'Domaine' + password_label: 'Mot de passe' + save: "Sauvegarder" + delete: "Supprimer" + delete_confirm: "Êtes-vous sûr ?" back_to_list: "Revenir à la liste" error: - page_title: Une erreur est survenue + page_title: "Une erreur est survenue" flashes: config: @@ -525,9 +558,10 @@ flashes: tagging_rules_updated: "Règles mises à jour" tagging_rules_deleted: "Règle supprimée" rss_token_updated: "Jeton RSS mis à jour" - annotations_reset: Annotations supprimées - tags_reset: Tags supprimés - entries_reset: Articles supprimés + annotations_reset: "Annotations supprimées" + tags_reset: "Tags supprimés" + entries_reset: "Articles supprimés" + archived_reset: "Articles archivés supprimés" entry: notice: entry_already_saved: "Article déjà sauvegardé le %date%" @@ -562,3 +596,8 @@ flashes: added: 'Utilisateur "%username%" ajouté' updated: 'Utilisateur "%username%" mis à jour' deleted: 'Utilisateur "%username%" supprimé' + site_credential: + notice: + added: 'Accès au site "%host%" ajouté' + updated: 'Accès au site "%host%" mis à jour' + deleted: 'Accès au site "%host%" supprimé' diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml index 3cd3fd17..fa7ae0b2 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml @@ -91,9 +91,10 @@ config: token_reset: 'Rigenera il tuo token' rss_links: 'Collegamenti RSS' rss_link: - unread: 'non letti' - starred: 'preferiti' - archive: 'archiviati' + unread: 'Non letti' + starred: 'Preferiti' + archive: 'Archiviati' + # all: 'All' rss_limit: 'Numero di elementi nel feed' form_user: two_factor_description: "Abilitando l'autenticazione a due fattori riceverai una e-mail con un codice per ogni nuova connesione non verificata" @@ -112,6 +113,7 @@ config: annotations: Rimuovi TUTTE le annotazioni tags: Rimuovi TUTTE le etichette entries: Rimuovi TUTTI gli articoli + # archived: Remove ALL archived entries confirm: Sei veramente sicuro? (NON PUOI TORNARE INDIETRO) form_password: description: "Qui puoi cambiare la tua password. La tua nuova password dovrebbe essere composta da almeno 8 caratteri." @@ -156,6 +158,7 @@ config: or: "Una regola O un'altra" and: "Una regola E un'altra" matches: 'Verifica che un oggetto risulti in una ricerca (case-insensitive).
Esempio: titolo contiene "football"' + # notmatches: 'Tests that a subject is not matches a search (case-insensitive).
Example: title notmatches "football"' entry: page_titles: @@ -187,6 +190,8 @@ entry: unread_label: 'Non letti' preview_picture_label: "Ha un'immagine di anteprima" preview_picture_help: 'Immagine di anteprima' + # is_public_label: 'Has a public link' + # is_public_help: 'Public link' language_label: 'Lingua' http_status_label: 'Stato HTTP' reading_time: @@ -211,7 +216,7 @@ entry: view_original_article: 'Contenuto originale' re_fetch_content: 'Ri-ottieni pagina' delete: 'Elimina' - add_a_tag: 'Aggiungi un tag' + add_a_tag: 'Aggiungi un''etichetta' share_content: 'Condividi' share_email_label: 'E-mail' public_link: 'Link pubblico' @@ -222,7 +227,7 @@ entry: label: 'Problemi?' description: 'Questo contenuto viene visualizzato male?' edit_title: 'Modifica titolo' - original_article: 'originale' + original_article: 'Originale' annotations_on_the_entry: '{0} Nessuna annotazione|{1} Una annotazione|]1,Inf[ %count% annotazioni' created_at: 'Data di creazione' published_at: 'Data di pubblicazione' @@ -238,13 +243,15 @@ entry: page_title: 'Modifica voce' title_label: 'Titolo' url_label: 'Url' - is_public_label: 'Pubblico' save_label: 'Salva' public: shared_by_wallabag: "Questo articolo è stato condiviso da %username% con wallabag" + confirm: + delete: "Vuoi veramente rimuovere quell'articolo?" + delete_tag: "Vuoi veramente rimuovere quell'etichetta da quell'articolo?" about: - page_title: 'About' + page_title: 'A proposito' top_menu: who_behind_wallabag: "Chi c'è dietro a wallabag" getting_help: 'Ottieni aiuto' @@ -263,7 +270,7 @@ about: bug_reports: 'Bug reports' support: 'su GitHub' helping: - description: 'wallabag è gratuito opensource. Puoi aiutarci:' + description: 'wallabag è gratuito ed OpenSource. Puoi aiutarci:' by_contributing: 'per contribuire al progetto:' by_contributing_2: 'un elenco delle attività richieste' by_paypal: 'via Paypal' @@ -331,7 +338,7 @@ quickstart: more: 'Più…' intro: title: 'Benvenuto su wallabag!' - paragraph_1: "Un tour in cui ti guideremo per scoprire e che ti mostrerà delle funzionalità che potrebbero interessarti." + paragraph_1: "Ti accompagneremo alla scoperta di wallabag e ti mostreremo delle funzionalità che potrebbero interessarti." paragraph_2: 'Seguici!' configure: title: "Configura l'applicazione" @@ -401,20 +408,20 @@ import: save_label: 'Carica file' pocket: page_title: 'Importa da > Pocket' - description: "Questo importatore copierà tutti i tuoi dati da Pocket. Pocket non ci consente di ottenere contenuti dal loro servzio, così il contenuto leggibile di ogni articolo verrà ri-ottenuto da wallabag." + description: "Questo importatore copierà tutti i tuoi dati da Pocket. Pocket non ci consente di ottenere contenuti dal loro servizio, così il contenuto leggibile di ogni articolo verrà ri-ottenuto da wallabag." config_missing: description: "Importazione da Pocket non configurata." admin_message: 'Devi definire %keyurls% una pocket_consumer_key %keyurle%.' - user_message: 'Il tuo amministratore di server deve define una API Key per Pocket.' + user_message: 'Il tuo amministratore del server deve definire una API Key per Pocket.' authorize_message: 'Puoi importare dati dal tuo account Pocket. Devi solo cliccare sul pulsante sottostante e autorizzare la connessione a getpocket.com.' connect_to_pocket: 'Connetti a Pocket and importa i dati' wallabag_v1: page_title: 'Importa da > Wallabag v1' - description: 'Questo importatore copierà tutti i tuoi dati da un wallabag v1. Nella tua pagina di configurazione, clicca su "JSON export" nella sezione "Esport i tuoi dati di wallabag". Otterrai un file "wallabag-export-1-xxxx-xx-xx.json".' + description: 'Questo importatore copierà tutti i tuoi dati da un wallabag v1. Nella tua pagina di configurazione, clicca su "JSON export" nella sezione "Esporta i tuoi dati di wallabag". Otterrai un file "wallabag-export-1-xxxx-xx-xx.json".' how_to: 'Seleziona la tua esportazione di wallabag e clicca sul pulsante sottostante caricare il file e importare i dati.' wallabag_v2: page_title: 'Importa da > Wallabag v2' - description: 'Questo importatore copierà tutti i tuoi dati da un wallabag v2. Vai in "Tutti i contenuti", e, nella sidebar di esportazione, clicca su "JSON". Otterrai un file "Tutti i contenuti.json".' + description: 'Questo importatore copierà tutti i tuoi dati da un wallabag v2. Vai in "Tutti i contenuti", e, nella barra laterale di esportazione, clicca su "JSON". Otterrai un file "Tutti i contenuti.json' readability: page_title: 'Importa da > Readability' description: 'Questo importatore copierà tutti i tuoi articoli da Readability. Nella pagina strumenti (https://www.readability.com/tools/), clicca su "Export your data" nella sezione "Data Export". Riceverai una E-mail per scaricare un file json (che tuttavia non termina con .json).' @@ -514,6 +521,28 @@ user: delete: Cancella delete_confirm: Sei sicuro? back_to_list: Torna alla lista + search: + # placeholder: Filter by username or email + +site_credential: + # page_title: Site credentials management + # new_site_credential: Create a credential + # edit_site_credential: Edit an existing credential + # description: "Here you can manage all credentials for sites which required them (create, edit and delete), like a paywall, an authentication, etc." + # list: + # actions: Actions + # edit_action: Edit + # yes: Yes + # no: No + # create_new_one: Create a new credential + # form: + # username_label: 'Username' + # host_label: 'Host' + # password_label: 'Password' + # save: Save + # delete: Delete + # delete_confirm: Are you sure? + # back_to_list: Back to list error: page_title: Si è verificato un errore @@ -532,6 +561,7 @@ flashes: annotations_reset: Reset annotazioni tags_reset: Reset etichette entries_reset: Reset articoli + # archived_reset: Archived entries deleted entry: notice: entry_already_saved: 'Contenuto già salvato in data %date%' @@ -566,3 +596,8 @@ flashes: added: 'Utente "%username%" aggiunto' updated: 'Utente "%username%" aggiornato' deleted: 'Utente "%username%" eliminato' + site_credential: + notice: + # added: 'Site credential for "%host%" added' + # updated: 'Site credential for "%host%" updated' + # deleted: 'Site credential for "%host%" deleted' diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml index 913e3bcb..be57e903 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml @@ -32,6 +32,7 @@ menu: save_link: 'Enregistrar un novèl article' back_to_unread: 'Tornar als articles pas legits' users_management: 'Gestion dels utilizaires' + # site_credentials: 'Site credentials' top: add_new_entry: 'Enregistrar un novèl article' search: 'Cercar' @@ -76,6 +77,7 @@ config: redirect_current_page: 'A la pagina actuala' pocket_consumer_key_label: Clau d'autentificacion Pocket per importar las donadas android_configuration: Configuratz vòstra aplicacion Android + # android_instruction: "Touch here to prefill your Android application" help_theme: "wallabag es personalizable. Podètz causir vòstre tèma preferit aquí." help_items_per_page: "Podètz cambiar lo nombre d'articles afichats per pagina." help_reading_speed: "wallabag calcula lo temps de lectura per cada article. Podètz lo definir aquí, gràcias a aquesta lista, se sètz un legeire rapid o lent. wallabag tornarà calcular lo temps de lectura per cada article." @@ -87,11 +89,12 @@ config: no_token: 'Pas cap de geton generat' token_create: 'Creatz vòstre geton' token_reset: 'Reïnicializatz vòstre geton' - rss_links: 'URL de vòstres fluxes RSS' + rss_links: 'URLs de vòstres fluxes RSS' rss_link: - unread: 'pas legits' - starred: 'favorits' - archive: 'legits' + unread: 'Pas legits' + starred: 'Favorits' + archive: 'Legits' + # all: 'All' rss_limit: "Nombre d'articles dins un flux RSS" form_user: two_factor_description: "Activar l'autentificacion doble-factor vòl dire que recebretz un còdi per corrièl per cada novèla connexion pas aprovada." @@ -110,6 +113,7 @@ config: annotations: Levar TOTAS las anotacions tags: Levar TOTAS las etiquetas entries: Levar TOTES los articles + archived: Levar TOTES los articles archivats confirm: Sètz vertadièrament segur ? (ES IRREVERSIBLE) form_password: description: "Podètz cambiar vòstre senhal aquí. Vòstre senhal deu èsser long d'almens 8 caractèrs." @@ -153,7 +157,8 @@ config: not_equal_to: 'Diferent de…' or: "Una règla O l'autra" and: "Una règla E l'autra" - matches: 'Teste se un subjècte correspond a una recerca (non sensibla a la cassa).
Exemple : title matches \"football\"' + matches: 'Teste se un subjècte correspond a una recèrca (non sensibla a la cassa).
Exemple : title matches \"football\"' + notmatches: 'Teste se subjècte correspond pas a una recèrca (sensibla a la cassa).
Example : title notmatches "football"' entry: page_titles: @@ -183,8 +188,10 @@ entry: archived_label: 'Legits' starred_label: 'Favorits' unread_label: 'Pas legits' - preview_picture_label: 'A una fotò' - preview_picture_help: 'Fotò' + preview_picture_label: 'A un imatge' + preview_picture_help: 'Imatge' + # is_public_label: 'Has a public link' + # is_public_help: 'Public link' language_label: 'Lenga' http_status_label: 'Estatut HTTP' reading_time: @@ -223,6 +230,8 @@ entry: original_article: 'original' annotations_on_the_entry: "{0} Pas cap d'anotacion|{1} Una anotacion|]1,Inf[ %count% anotacions" created_at: 'Data de creacion' + published_at: 'Data de publicacion' + published_by: 'Publicat per' new: page_title: 'Enregistrar un novèl article' placeholder: 'http://website.com' @@ -234,10 +243,12 @@ entry: page_title: 'Modificar un article' title_label: 'Títol' url_label: 'Url' - is_public_label: 'Public' save_label: 'Enregistrar' public: shared_by_wallabag: "Aqueste article es estat partejat per wallabag" + confirm: + # delete: "Are you sure you want to remove that article?" + # delete_tag: "Are you sure you want to remove that tag from that article?" about: page_title: 'A prepaus' @@ -341,8 +352,8 @@ quickstart: new_user: 'Crear un novèl utilizaire' analytics: 'Configurar las estadisticas' sharing: 'Activar de paramètres de partatge' - export: 'Configurar los expòrt' - import: 'Configurar los impòrt' + export: 'Configurar los expòrts' + import: 'Configurar los impòrts' first_steps: title: 'Primièrs passes' description: "Ara wallabag es ben configurat, es lo moment d'archivar lo web. Podètz clicar sul signe + a man drecha amont per ajustar un ligam." @@ -458,7 +469,7 @@ developer: action: 'Suprimir aqueste client' client: page_title: 'Gestion dels clients API > Novèl client' - page_description: "Anatz crear un novèl client. Mercés de cumplir l'url de redireccion cap a vòstra aplicacion." + page_description: "Anatz crear un novèl client. Mercés de garnir l'url de redireccion cap a vòstra aplicacion." form: name_label: "Nom del client" redirect_uris_label: 'URLs de redireccion' @@ -469,7 +480,7 @@ developer: page_description: 'Vaquí los paramètres de vòstre client.' field_name: 'Nom del client' field_id: 'ID Client' - field_secret: 'Clau secreta' + field_secret: 'Clau secrèta' back: 'Retour' read_howto: 'Legir "cossí crear ma primièra aplicacion"' howto: @@ -510,6 +521,28 @@ user: delete: 'Suprimir' delete_confirm: 'Sètz segur ?' back_to_list: 'Tornar a la lista' + search: + placeholder: "Filtrar per nom d'utilizaire o corrièl" + +site_credential: + # page_title: Site credentials management + # new_site_credential: Create a credential + # edit_site_credential: Edit an existing credential + # description: "Here you can manage all credentials for sites which required them (create, edit and delete), like a paywall, an authentication, etc." + list: + actions: 'Accions' + edit_action: 'Modificar' + yes: 'Òc' + no: 'Non' + # create_new_one: Create a new credential + form: + # username_label: 'Username' + # host_label: 'Host' + # password_label: 'Password' + save: 'Enregistrar' + delete: 'Suprimir' + delete_confirm: 'Sètz segur ?' + back_to_list: 'Tornar a la lista' error: page_title: Una error s'es produsida @@ -528,6 +561,7 @@ flashes: annotations_reset: Anotacions levadas tags_reset: Etiquetas levadas entries_reset: Articles levats + archived_reset: Articles archivat suprimits entry: notice: entry_already_saved: 'Article ja salvargardat lo %date%' @@ -562,3 +596,8 @@ flashes: added: 'Utilizaire "%username%" ajustat' updated: 'Utilizaire "%username%" mes a jorn' deleted: 'Utilizaire "%username%" suprimit' + site_credential: + notice: + # added: 'Site credential for "%host%" added' + # updated: 'Site credential for "%host%" updated' + # deleted: 'Site credential for "%host%" deleted' diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml index b990a6b9..00c559ed 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml @@ -32,6 +32,7 @@ menu: save_link: 'Zapisz link' back_to_unread: 'Powrót do nieprzeczytanych artykułów' users_management: 'Zarządzanie użytkownikami' + # site_credentials: 'Site credentials' top: add_new_entry: 'Dodaj nowy wpis' search: 'Szukaj' @@ -76,6 +77,7 @@ config: redirect_current_page: 'do bieżącej strony' pocket_consumer_key_label: 'Klucz klienta Pocket do importu zawartości' android_configuration: Skonfiguruj swoją androidową aplikację + android_instruction: "Dotknij tutaj, aby wstępnie uzupełnij androidową aplikację" help_theme: "Dopasuj wallabag do swoich potrzeb. Tutaj możesz wybrać preferowany przez ciebie motyw." help_items_per_page: "Możesz zmienić ilość artykułów wyświetlanych na każdej stronie." help_reading_speed: "wallabag oblicza czas czytania każdego artykułu. Dzięki tej liście możesz określić swoje tempo. Wallabag przeliczy ponownie czas potrzebny, na przeczytanie każdego z artykułów." @@ -89,9 +91,10 @@ config: token_reset: 'Zresetuj swojego tokena' rss_links: 'RSS links' rss_link: - unread: 'nieprzeczytane' - starred: 'oznaczone gwiazdką' - archive: 'archiwum' + unread: 'Nieprzeczytane' + starred: 'Oznaczone gwiazdką' + archive: 'Archiwum' + # all: 'All' rss_limit: 'Link do RSS' form_user: two_factor_description: "Włączenie autoryzacji dwuetapowej oznacza, że będziesz otrzymywał maile z kodem przy każdym nowym, niezaufanym połączeniu" @@ -110,6 +113,7 @@ config: annotations: Usuń WSZYSTKIE adnotacje tags: Usuń WSZYSTKIE tagi entries: usuń WSZYTSTKIE wpisy + archived: usuń WSZYSTKIE zarchiwizowane wpisy confirm: Jesteś pewien? (tej operacji NIE MOŻNA cofnąć) form_password: description: "Tutaj możesz zmienić swoje hasło. Twoje nowe hasło powinno mieć conajmniej 8 znaków." @@ -154,6 +158,7 @@ config: or: 'Jedna reguła LUB inna' and: 'Jedna reguła I inna' matches: 'Sprawdź czy temat pasuje szukaj (duże lub małe litery).
Przykład: tytuł zawiera "piłka nożna"' + notmatches: 'Sprawdź czy temat nie zawiera szukaj (duże lub małe litery).
Przykład: tytuł nie zawiera "piłka nożna"' entry: page_titles: @@ -185,6 +190,8 @@ entry: unread_label: 'Nieprzeczytane' preview_picture_label: 'Posiada podgląd obrazu' preview_picture_help: 'Podgląd obrazu' + is_public_label: 'Posiada publiczny link' + is_public_help: 'Publiczny link' language_label: 'Język' http_status_label: 'Status HTTP' reading_time: @@ -223,6 +230,8 @@ entry: original_article: 'oryginalny' annotations_on_the_entry: '{0} Nie ma adnotacji |{1} Jedna adnotacja |]1,Inf[ %count% adnotacji' created_at: 'Czas stworzenia' + published_at: 'Data publikacji' + published_by: 'Opublikowane przez' new: page_title: 'Zapisz nowy wpis' placeholder: 'http://website.com' @@ -234,10 +243,12 @@ entry: page_title: 'Edytuj wpis' title_label: 'Tytuł' url_label: 'Adres URL' - is_public_label: 'Publiczny' save_label: 'Zapisz' public: shared_by_wallabag: "Ten artykuł został udostępniony przez wallabag" + confirm: + delete: "Czy jesteś pewien, że chcesz usunąć ten artykuł?" + delete_tag: "Czy jesteś pewien, że chcesz usunąć ten tag, z tego artykułu?" about: page_title: 'O nas' @@ -510,6 +521,28 @@ user: delete: Usuń delete_confirm: Jesteś pewien? back_to_list: Powrót do listy + search: + placeholder: Filtruj po nazwie użytkownika lub adresie e-mail + +site_credential: + # page_title: Site credentials management + # new_site_credential: Create a credential + # edit_site_credential: Edit an existing credential + # description: "Here you can manage all credentials for sites which required them (create, edit and delete), like a paywall, an authentication, etc." + list: + actions: Akcje + edit_action: Edytuj + yes: Tak + no: Nie + # create_new_one: Create a new credential + form: + # username_label: 'Username' + # host_label: 'Host' + # password_label: 'Password' + save: Zapisz + delete: Usuń + delete_confirm: Jesteś pewien? + back_to_list: Powrót do listy error: page_title: Wystąpił błąd @@ -528,6 +561,7 @@ flashes: annotations_reset: Zresetuj adnotacje tags_reset: Zresetuj tagi entries_reset: Zresetuj wpisy + archived_reset: Zarchiwizowane wpisy usunięte entry: notice: entry_already_saved: 'Wpis już został dodany %date%' @@ -562,3 +596,8 @@ flashes: added: 'Użytkownik "%username%" dodany' updated: 'Użytkownik "%username%" zaktualizowany' deleted: 'Użytkownik "%username%" usunięty' + site_credential: + notice: + # added: 'Site credential for "%host%" added' + # updated: 'Site credential for "%host%" updated' + # deleted: 'Site credential for "%host%" deleted' diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml index 3b1f9cb6..4ab5f144 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.pt.yml @@ -32,6 +32,7 @@ menu: save_link: 'Salvar um link' back_to_unread: 'Voltar para os artigos não lidos' users_management: 'Gestão de Usuários' + # site_credentials: 'Site credentials' top: add_new_entry: 'Adicionar uma nova entrada' search: 'Pesquisa' @@ -76,6 +77,7 @@ config: # redirect_current_page: 'To the current page' pocket_consumer_key_label: 'Chave do consumidor do Pocket para importar conteúdo' # android_configuration: Configure your Android application + # android_instruction: "Touch here to prefill your Android application" # help_theme: "wallabag is customizable. You can choose your prefered theme here." # help_items_per_page: "You can change the number of articles displayed on each page." # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article." @@ -89,9 +91,10 @@ config: token_reset: 'Gerar novamente seu token' rss_links: 'Links RSS' rss_link: - unread: 'não lido' - starred: 'destacado' - archive: 'arquivado' + unread: 'Não lido' + starred: 'Destacado' + archive: 'Arquivado' + # all: 'All' rss_limit: 'Número de itens no feed' form_user: two_factor_description: 'Habilitar autenticação de dois passos significa que você receberá um e-mail com um código a cada nova conexão desconhecida.' @@ -110,6 +113,7 @@ config: # annotations: Remove ALL annotations # tags: Remove ALL tags # entries: Remove ALL entries + # archived: Remove ALL archived entries # confirm: Are you really really sure? (THIS CAN'T BE UNDONE) form_password: # description: "You can change your password here. Your new password should by at least 8 characters long." @@ -154,6 +158,7 @@ config: or: 'Uma regra OU outra' and: 'Uma regra E outra' matches: 'Testa que um assunto corresponde a uma pesquisa (maiúscula ou minúscula).
Exemplo: título corresponde a "futebol"' + # notmatches: 'Tests that a subject is not matches a search (case-insensitive).
Example: title notmatches "football"' entry: page_titles: @@ -185,6 +190,8 @@ entry: unread_label: 'Não Lido' preview_picture_label: 'Possui uma imagem de preview' preview_picture_help: 'Imagem de preview' + # is_public_label: 'Has a public link' + # is_public_help: 'Public link' language_label: 'Idioma' # http_status_label: 'HTTP status' reading_time: @@ -223,6 +230,8 @@ entry: original_article: 'original' annotations_on_the_entry: '{0} Sem anotações|{1} Uma anotação|]1,Inf[ %nbAnnotations% anotações' created_at: 'Data de criação' + # published_at: 'Publication date' + # published_by: 'Published by' new: page_title: 'Salvar nova entrada' placeholder: 'http://website.com' @@ -234,10 +243,12 @@ entry: page_title: 'Editar uma entrada' title_label: 'Título' url_label: 'Url' - is_public_label: 'Público' save_label: 'Salvar' public: shared_by_wallabag: "Este artigo foi compartilhado pelo wallabag" + confirm: + # delete: "Are you sure you want to remove that article?" + # delete_tag: "Are you sure you want to remove that tag from that article?" about: page_title: 'Sobre' @@ -510,6 +521,28 @@ user: delete: 'Apagar' delete_confirm: 'Tem certeza?' back_to_list: 'Voltar para a lista' + search: + # placeholder: Filter by username or email + +site_credential: + # page_title: Site credentials management + # new_site_credential: Create a credential + # edit_site_credential: Edit an existing credential + # description: "Here you can manage all credentials for sites which required them (create, edit and delete), like a paywall, an authentication, etc." + list: + actions: 'Ações' + edit_action: 'Editar' + yes: 'Sim' + no: 'Não' + # create_new_one: Create a new credential + form: + # username_label: 'Username' + # host_label: 'Host' + # password_label: 'Password' + save: 'Salvar' + delete: 'Apagar' + delete_confirm: 'Tem certeza?' + back_to_list: 'Voltar para a lista' error: # page_title: An error occurred @@ -528,6 +561,7 @@ flashes: # annotations_reset: Annotations reset # tags_reset: Tags reset # entries_reset: Entries reset + # archived_reset: Archived entries deleted entry: notice: entry_already_saved: 'Entrada já foi salva em %date%' @@ -562,3 +596,8 @@ flashes: added: 'Usuário "%username%" adicionado' updated: 'Usuário "%username%" atualizado' deleted: 'Usuário "%username%" removido' + site_credential: + notice: + # added: 'Site credential for "%host%" added' + # updated: 'Site credential for "%host%" updated' + # deleted: 'Site credential for "%host%" deleted' diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml index 728eed58..f16504ed 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml @@ -32,6 +32,7 @@ menu: # save_link: 'Save a link' back_to_unread: 'Înapoi la articolele necitite' # users_management: 'Users management' + # site_credentials: 'Site credentials' top: add_new_entry: 'Introdu un nou articol' search: 'Căutare' @@ -76,6 +77,7 @@ config: # redirect_current_page: 'To the current page' pocket_consumer_key_label: Cheie consumator pentru importarea contentului din Pocket # android_configuration: Configure your Android application + # android_instruction: "Touch here to prefill your Android application" # help_theme: "wallabag is customizable. You can choose your prefered theme here." # help_items_per_page: "You can change the number of articles displayed on each page." # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article." @@ -89,9 +91,10 @@ config: token_reset: 'Resetează-ți token-ul' rss_links: 'Link-uri RSS' rss_link: - unread: 'unread' - starred: 'starred' - archive: 'archived' + unread: 'Unread' + starred: 'Starred' + archive: 'Archived' + # all: 'All' rss_limit: 'Limită RSS' form_user: # two_factor_description: "Enabling two factor authentication means you'll receive an email with a code on every new untrusted connexion" @@ -110,6 +113,7 @@ config: # annotations: Remove ALL annotations # tags: Remove ALL tags # entries: Remove ALL entries + # archived: Remove ALL archived entries # confirm: Are you really really sure? (THIS CAN'T BE UNDONE) form_password: # description: "You can change your password here. Your new password should by at least 8 characters long." @@ -154,6 +158,7 @@ config: # or: 'One rule OR another' # and: 'One rule AND another' # matches: 'Tests that a subject is matches a search (case-insensitive).
Example: title matches "football"' + # notmatches: 'Tests that a subject is not matches a search (case-insensitive).
Example: title notmatches "football"' entry: page_titles: @@ -185,6 +190,8 @@ entry: unread_label: 'Necitite' preview_picture_label: 'Are o imagine de previzualizare' preview_picture_help: 'Previzualizare imagine' + # is_public_label: 'Has a public link' + # is_public_help: 'Public link' language_label: 'Limbă' # http_status_label: 'HTTP status' reading_time: @@ -223,6 +230,8 @@ entry: original_article: 'original' # annotations_on_the_entry: '{0} No annotations|{1} One annotation|]1,Inf[ %count% annotations' created_at: 'Data creării' + # published_at: 'Publication date' + # published_by: 'Published by' new: page_title: 'Salvează un nou articol' placeholder: 'http://website.com' @@ -234,10 +243,12 @@ entry: # page_title: 'Edit an entry' # title_label: 'Title' url_label: 'Url' - # is_public_label: 'Public' save_label: 'Salvează' public: - # shared_by_wallabag: "This article has been shared by wallabag" + # shared_by_wallabag: "This article has been shared by %username% with wallabag" + confirm: + # delete: "Are you sure you want to remove that article?" + # delete_tag: "Are you sure you want to remove that tag from that article?" about: page_title: 'Despre' @@ -510,6 +521,28 @@ user: # delete: Delete # delete_confirm: Are you sure? # back_to_list: Back to list + search: + # placeholder: Filter by username or email + +site_credential: + # page_title: Site credentials management + # new_site_credential: Create a credential + # edit_site_credential: Edit an existing credential + # description: "Here you can manage all credentials for sites which required them (create, edit and delete), like a paywall, an authentication, etc." + # list: + # actions: Actions + # edit_action: Edit + # yes: Yes + # no: No + # create_new_one: Create a new credential + # form: + # username_label: 'Username' + # host_label: 'Host' + # password_label: 'Password' + # save: Save + # delete: Delete + # delete_confirm: Are you sure? + # back_to_list: Back to list error: # page_title: An error occurred @@ -528,6 +561,7 @@ flashes: # annotations_reset: Annotations reset # tags_reset: Tags reset # entries_reset: Entries reset + # archived_reset: Archived entries deleted entry: notice: # entry_already_saved: 'Entry already saved on %date%' @@ -562,3 +596,8 @@ flashes: # added: 'User "%username%" added' # updated: 'User "%username%" updated' # deleted: 'User "%username%" deleted' + site_credential: + notice: + # added: 'Site credential for "%host%" added' + # updated: 'Site credential for "%host%" updated' + # deleted: 'Site credential for "%host%" deleted' diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml index d3180f42..90a140cd 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml @@ -32,6 +32,7 @@ menu: # save_link: 'Save a link' back_to_unread: 'Okunmayan makalelere geri dön' # users_management: 'Users management' + # site_credentials: 'Site credentials' top: add_new_entry: 'Yeni bir makale ekle' search: 'Ara' @@ -76,6 +77,7 @@ config: # redirect_current_page: 'To the current page' # pocket_consumer_key_label: Consumer key for Pocket to import contents # android_configuration: Configure your Android application + # android_instruction: "Touch here to prefill your Android application" # help_theme: "wallabag is customizable. You can choose your prefered theme here." # help_items_per_page: "You can change the number of articles displayed on each page." # help_reading_speed: "wallabag calculates a reading time for each article. You can define here, thanks to this list, if you are a fast or a slow reader. wallabag will recalculate the reading time for each article." @@ -89,9 +91,10 @@ config: token_reset: 'Belirteci (token) sıfırla' rss_links: 'RSS akış bağlantıları' rss_link: - unread: 'okunmayan' - starred: 'favoriler' - archive: 'arşiv' + unread: 'Okunmayan' + starred: 'Favoriler' + archive: 'Arşiv' + # all: 'All' rss_limit: 'RSS içeriğinden talep edilecek makale limiti' form_user: two_factor_description: "İki adımlı doğrulamayı aktifleştirdiğinizde, her yeni güvenilmeyen bağlantılarda size e-posta ile bir kod alacaksınız." @@ -110,6 +113,7 @@ config: # annotations: Remove ALL annotations # tags: Remove ALL tags # entries: Remove ALL entries + # archived: Remove ALL archived entries # confirm: Are you really really sure? (THIS CAN'T BE UNDONE) form_password: # description: "You can change your password here. Your new password should by at least 8 characters long." @@ -154,6 +158,7 @@ config: or: 'Bir kural veya birbaşkası' and: 'Bir kural ve diğeri' # matches: 'Tests that a subject is matches a search (case-insensitive).
Example: title matches "football"' + # notmatches: 'Tests that a subject is not matches a search (case-insensitive).
Example: title notmatches "football"' entry: page_titles: @@ -223,6 +228,8 @@ entry: original_article: 'orijinal' # annotations_on_the_entry: '{0} No annotations|{1} One annotation|]1,Inf[ %count% annotations' created_at: 'Oluşturulma tarihi' + # published_at: 'Publication date' + # published_by: 'Published by' new: page_title: 'Yeni makaleyi kaydet' placeholder: 'http://website.com' @@ -234,10 +241,12 @@ entry: page_title: 'Makaleyi düzenle' title_label: 'Başlık' url_label: 'Url' - is_public_label: 'Herkes tarafından erişime açık olsun mu?' save_label: 'Kaydet' public: - # shared_by_wallabag: "This article has been shared by wallabag" + # shared_by_wallabag: "This article has been shared by %username% with wallabag" + confirm: + # delete: "Are you sure you want to remove that article?" + # delete_tag: "Are you sure you want to remove that tag from that article?" about: page_title: 'Hakkımızda' @@ -510,6 +519,8 @@ user: # delete: Delete # delete_confirm: Are you sure? # back_to_list: Back to list + search: + # placeholder: Filter by username or email error: # page_title: An error occurred @@ -528,6 +539,7 @@ flashes: # annotations_reset: Annotations reset # tags_reset: Tags reset # entries_reset: Entries reset + # archived_reset: Archived entries deleted entry: notice: entry_already_saved: 'Entry already saved on %date%' @@ -562,3 +574,8 @@ flashes: # added: 'User "%username%" added' # updated: 'User "%username%" updated' # deleted: 'User "%username%" deleted' + site_credential: + notice: + # added: 'Site credential for "%host%" added' + # updated: 'Site credential for "%host%" updated' + # deleted: 'Site credential for "%host%" deleted' diff --git a/src/Wallabag/CoreBundle/Resources/translations/validators.da.yml b/src/Wallabag/CoreBundle/Resources/translations/validators.da.yml index 32a8b4a8..c6a84209 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/validators.da.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/validators.da.yml @@ -4,3 +4,4 @@ validator: # password_wrong_value: 'Wrong value for your current password' # item_per_page_too_high: 'This will certainly kill the app' # rss_limit_too_high: 'This will certainly kill the app' + # quote_length_too_high: 'The quote is too long. It should have {{ limit }} characters or less.' diff --git a/src/Wallabag/CoreBundle/Resources/translations/validators.de.yml b/src/Wallabag/CoreBundle/Resources/translations/validators.de.yml index 37b9888f..c74c00ca 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/validators.de.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/validators.de.yml @@ -4,3 +4,4 @@ validator: password_wrong_value: 'Falscher Wert für dein aktuelles Kennwort' item_per_page_too_high: 'Dies wird die Anwendung möglicherweise beenden' rss_limit_too_high: 'Dies wird die Anwendung möglicherweise beenden' + # quote_length_too_high: 'The quote is too long. It should have {{ limit }} characters or less.' diff --git a/src/Wallabag/CoreBundle/Resources/translations/validators.en.yml b/src/Wallabag/CoreBundle/Resources/translations/validators.en.yml index 29217497..8cc117fe 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/validators.en.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/validators.en.yml @@ -4,3 +4,4 @@ validator: password_wrong_value: 'Wrong value for your current password' item_per_page_too_high: 'This will certainly kill the app' rss_limit_too_high: 'This will certainly kill the app' + quote_length_too_high: 'The quote is too long. It should have {{ limit }} characters or less.' diff --git a/src/Wallabag/CoreBundle/Resources/translations/validators.es.yml b/src/Wallabag/CoreBundle/Resources/translations/validators.es.yml index 57ddaa5a..97a8edfa 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/validators.es.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/validators.es.yml @@ -4,3 +4,4 @@ validator: password_wrong_value: 'Entrada equivocada para su contraseña actual' item_per_page_too_high: 'Esto matará la aplicación' rss_limit_too_high: 'Esto matará la aplicación' + # quote_length_too_high: 'The quote is too long. It should have {{ limit }} characters or less.' diff --git a/src/Wallabag/CoreBundle/Resources/translations/validators.fa.yml b/src/Wallabag/CoreBundle/Resources/translations/validators.fa.yml index e0536d18..ef677525 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/validators.fa.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/validators.fa.yml @@ -4,3 +4,4 @@ validator: password_wrong_value: 'رمز فعلی را اشتباه وارد کرده‌اید' item_per_page_too_high: 'با این تعداد برنامه به فنا می‌رود' rss_limit_too_high: 'با این تعداد برنامه به فنا می‌رود' + # quote_length_too_high: 'The quote is too long. It should have {{ limit }} characters or less.' diff --git a/src/Wallabag/CoreBundle/Resources/translations/validators.fr.yml b/src/Wallabag/CoreBundle/Resources/translations/validators.fr.yml index 64574709..f31b4ed2 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/validators.fr.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/validators.fr.yml @@ -4,3 +4,4 @@ validator: password_wrong_value: "Votre mot de passe actuel est faux" item_per_page_too_high: "Ça ne va pas plaire à l’application" rss_limit_too_high: "Ça ne va pas plaire à l’application" + quote_length_too_high: "La citation est trop longue. Elle doit avoir au maximum {{ limit }} caractères." diff --git a/src/Wallabag/CoreBundle/Resources/translations/validators.it.yml b/src/Wallabag/CoreBundle/Resources/translations/validators.it.yml index d9beb54f..d949cc3b 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/validators.it.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/validators.it.yml @@ -4,3 +4,4 @@ validator: password_wrong_value: 'Valore inserito per la password corrente errato' item_per_page_too_high: 'Questo valore è troppo alto' rss_limit_too_high: 'Questo valore è troppo alto' + # quote_length_too_high: 'The quote is too long. It should have {{ limit }} characters or less.' diff --git a/src/Wallabag/CoreBundle/Resources/translations/validators.oc.yml b/src/Wallabag/CoreBundle/Resources/translations/validators.oc.yml index f92c2708..fb4aa592 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/validators.oc.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/validators.oc.yml @@ -4,3 +4,4 @@ validator: password_wrong_value: 'Vòstre senhal actual es pas bon' item_per_page_too_high: "Aquò li agradarà pas a l'aplicacion" rss_limit_too_high: "Aquò li agradarà pas a l'aplicacion" + # quote_length_too_high: 'The quote is too long. It should have {{ limit }} characters or less.' diff --git a/src/Wallabag/CoreBundle/Resources/translations/validators.pl.yml b/src/Wallabag/CoreBundle/Resources/translations/validators.pl.yml index ffcd5e7f..e4165c14 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/validators.pl.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/validators.pl.yml @@ -4,3 +4,4 @@ validator: password_wrong_value: 'Twoje obecne hasło jest błędne' item_per_page_too_high: 'To może spowodować problemy z aplikacją' rss_limit_too_high: 'To może spowodować problemy z aplikacją' + quote_length_too_high: 'Cytat jest zbyt długi. powinien mieć {{ limit }} znaków lub mniej.' diff --git a/src/Wallabag/CoreBundle/Resources/translations/validators.pt.yml b/src/Wallabag/CoreBundle/Resources/translations/validators.pt.yml index 4eddff10..a8c1f9de 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/validators.pt.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/validators.pt.yml @@ -4,3 +4,4 @@ validator: password_wrong_value: 'A senha atual informada está errada' item_per_page_too_high: 'Certamente isso pode matar a aplicação' rss_limit_too_high: 'Certamente isso pode matar a aplicação' + # quote_length_too_high: 'The quote is too long. It should have {{ limit }} characters or less.' diff --git a/src/Wallabag/CoreBundle/Resources/translations/validators.ro.yml b/src/Wallabag/CoreBundle/Resources/translations/validators.ro.yml index 59a8cdd8..6840cf11 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/validators.ro.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/validators.ro.yml @@ -4,3 +4,4 @@ validator: # password_wrong_value: 'Wrong value for your current password' # item_per_page_too_high: 'This will certainly kill the app' # rss_limit_too_high: 'This will certainly kill the app' + # quote_length_too_high: 'The quote is too long. It should have {{ limit }} characters or less.' diff --git a/src/Wallabag/CoreBundle/Resources/translations/validators.tr.yml b/src/Wallabag/CoreBundle/Resources/translations/validators.tr.yml index 01388771..e1e7317f 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/validators.tr.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/validators.tr.yml @@ -4,3 +4,4 @@ validator: # password_wrong_value: 'Wrong value for your current password' # item_per_page_too_high: 'This will certainly kill the app' # rss_limit_too_high: 'This will certainly kill the app' + # quote_length_too_high: 'The quote is too long. It should have {{ limit }} characters or less.' diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Config/index.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Config/index.html.twig index 3548f590..bcc57dac 100644 --- a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Config/index.html.twig +++ b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Config/index.html.twig @@ -82,7 +82,7 @@

{{ 'config.form_settings.android_configuration'|trans }}

- Touch here to prefill your Android application + {{ 'config.form_settings.android_instruction' | trans }}
+ {% endblock %} {% block header %} @@ -36,6 +38,9 @@ {{ render(controller("WallabagCoreBundle:Entry:searchForm", {'currentRoute': app.request.attributes.get('_route')})) }}
+ {% if craue_setting('restricted_access') %} + + {% endif %} {% if is_granted('ROLE_SUPER_ADMIN') %} diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/common/Developer/index.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/common/Developer/index.html.twig index b3f0affb..528b055c 100644 --- a/src/Wallabag/CoreBundle/Resources/views/themes/common/Developer/index.html.twig +++ b/src/Wallabag/CoreBundle/Resources/views/themes/common/Developer/index.html.twig @@ -33,7 +33,7 @@ - + diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/_rss_link.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/_rss_link.html.twig new file mode 100644 index 00000000..2bf9b2bd --- /dev/null +++ b/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/_rss_link.html.twig @@ -0,0 +1,6 @@ +{% if tag is defined %} + rss_feed +{% elseif currentRoute in ['unread', 'starred', 'archive', 'all'] %} + rss_feed +{% endif %} + diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/entries.xml.twig b/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/entries.xml.twig index 12e8c79f..d70aa5dc 100644 --- a/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/entries.xml.twig +++ b/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/entries.xml.twig @@ -1,8 +1,8 @@ - wallabag — {{type}} feed - {{ url(type) }} + wallabag - {{ type }} feed + {{ url_html }} {% if entries.hasPreviousPage -%} @@ -13,7 +13,7 @@ {{ "now"|date('D, d M Y H:i:s') }} wallabag - wallabag {{type}} elements + wallabag {{ type }} elements {% for entry in entries %} diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/share.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/share.html.twig index 623cf1c4..a67807f9 100644 --- a/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/share.html.twig +++ b/src/Wallabag/CoreBundle/Resources/views/themes/common/Entry/share.html.twig @@ -1,32 +1,6 @@ {{ entry.title|e|raw }} - @@ -40,12 +14,22 @@ + {% if app.debug %} + + {% else %} + + {% endif %} +

{{ entry.title|e|raw }}

-
{{ "entry.public.shared_by_wallabag"|trans({'%wallabag_instance%': url('homepage')})|raw }}
+
{{ "entry.public.shared_by_wallabag"|trans({'%wallabag_instance%': url('homepage'), '%username%': entry.user.username})|raw }}.
+ + {% if entry.previewPicture is not null %} +
{{ entry.title|striptags|e('html_attr') }}
+ {% endif %}
{{ entry.content | raw }} diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig index 5d411fdd..a8143315 100644 --- a/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig +++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig @@ -25,9 +25,9 @@
- {{ form_label(form.config.theme) }} {{ form_errors(form.config.theme) }} {{ form_widget(form.config.theme) }} + {{ form_label(form.config.theme) }}
@@ -38,9 +38,9 @@
- {{ form_label(form.config.items_per_page) }} {{ form_errors(form.config.items_per_page) }} {{ form_widget(form.config.items_per_page) }} + {{ form_label(form.config.items_per_page) }}
@@ -51,9 +51,9 @@ -
-
- {{ form_label(form.config.action_mark_as_read) }} - {{ form_errors(form.config.action_mark_as_read) }} - {{ form_widget(form.config.action_mark_as_read) }} +
+
+ {{ form_label(form.config.action_mark_as_read) }} + {{ form_errors(form.config.action_mark_as_read) }} + {{ form_widget(form.config.action_mark_as_read) }} +
-
- {{ form_label(form.config.language) }} {{ form_errors(form.config.language) }} {{ form_widget(form.config.language) }} + {{ form_label(form.config.language) }}
@@ -89,9 +89,9 @@
- {{ form_label(form.config.pocket_consumer_key) }} {{ form_errors(form.config.pocket_consumer_key) }} {{ form_widget(form.config.pocket_consumer_key) }} + {{ form_label(form.config.pocket_consumer_key) }}

» https://getpocket.com/developer/docs/authentication @@ -107,7 +107,7 @@

+ {% endblock %} {% block header %} @@ -64,6 +66,11 @@
  • {{ 'menu.left.config'|trans }}
  • + {% if craue_setting('restricted_access') %} +
  • + {{ 'menu.left.site_credentials'|trans }} +
  • + {% endif %} {% if is_granted('ROLE_SUPER_ADMIN') %}
  • {{ 'menu.left.users_management'|trans }} @@ -116,12 +123,12 @@
  • diff --git a/src/Wallabag/ImportBundle/Command/ImportCommand.php b/src/Wallabag/ImportBundle/Command/ImportCommand.php index 28d01715..5f1ab0af 100644 --- a/src/Wallabag/ImportBundle/Command/ImportCommand.php +++ b/src/Wallabag/ImportBundle/Command/ImportCommand.php @@ -5,6 +5,7 @@ namespace Wallabag\ImportBundle\Command; use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; use Symfony\Component\Config\Definition\Exception\Exception; use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -15,10 +16,12 @@ class ImportCommand extends ContainerAwareCommand $this ->setName('wallabag:import') ->setDescription('Import entries from a JSON export') - ->addArgument('userId', InputArgument::REQUIRED, 'User ID to populate') + ->addArgument('username', InputArgument::REQUIRED, 'User to populate') ->addArgument('filepath', InputArgument::REQUIRED, 'Path to the JSON file') - ->addOption('importer', null, InputArgument::OPTIONAL, 'The importer to use: v1, v2, instapaper, pinboard, readability, firefox or chrome', 'v1') - ->addOption('markAsRead', null, InputArgument::OPTIONAL, 'Mark all entries as read', false) + ->addOption('importer', null, InputOption::VALUE_OPTIONAL, 'The importer to use: v1, v2, instapaper, pinboard, readability, firefox or chrome', 'v1') + ->addOption('markAsRead', null, InputOption::VALUE_OPTIONAL, 'Mark all entries as read', false) + ->addOption('useUserId', null, InputOption::VALUE_NONE, 'Use user id instead of username to find account') + ->addOption('disableContentUpdate', null, InputOption::VALUE_NONE, 'Disable fetching updated content from URL') ; } @@ -34,10 +37,14 @@ class ImportCommand extends ContainerAwareCommand // Turning off doctrine default logs queries for saving memory $em->getConnection()->getConfiguration()->setSQLLogger(null); - $user = $em->getRepository('WallabagUserBundle:User')->findOneById($input->getArgument('userId')); + if ($input->getOption('useUserId')) { + $user = $em->getRepository('WallabagUserBundle:User')->findOneById($input->getArgument('username')); + } else { + $user = $em->getRepository('WallabagUserBundle:User')->findOneByUsername($input->getArgument('username')); + } if (!is_object($user)) { - throw new Exception(sprintf('User with id "%s" not found', $input->getArgument('userId'))); + throw new Exception(sprintf('User "%s" not found', $input->getArgument('username'))); } switch ($input->getOption('importer')) { @@ -64,6 +71,7 @@ class ImportCommand extends ContainerAwareCommand } $import->setMarkAsRead($input->getOption('markAsRead')); + $import->setDisableContentUpdate($input->getOption('disableContentUpdate')); $import->setUser($user); $res = $import diff --git a/src/Wallabag/ImportBundle/Import/AbstractImport.php b/src/Wallabag/ImportBundle/Import/AbstractImport.php index 1d4a6e27..9b624296 100644 --- a/src/Wallabag/ImportBundle/Import/AbstractImport.php +++ b/src/Wallabag/ImportBundle/Import/AbstractImport.php @@ -8,6 +8,7 @@ use Doctrine\ORM\EntityManager; use Wallabag\CoreBundle\Helper\ContentProxy; use Wallabag\CoreBundle\Entity\Entry; use Wallabag\CoreBundle\Entity\Tag; +use Wallabag\CoreBundle\Helper\TagsAssigner; use Wallabag\UserBundle\Entity\User; use OldSound\RabbitMqBundle\RabbitMq\ProducerInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -18,19 +19,22 @@ abstract class AbstractImport implements ImportInterface protected $em; protected $logger; protected $contentProxy; + protected $tagsAssigner; protected $eventDispatcher; protected $producer; protected $user; protected $markAsRead; + protected $disableContentUpdate = false; protected $skippedEntries = 0; protected $importedEntries = 0; protected $queuedEntries = 0; - public function __construct(EntityManager $em, ContentProxy $contentProxy, EventDispatcherInterface $eventDispatcher) + public function __construct(EntityManager $em, ContentProxy $contentProxy, TagsAssigner $tagsAssigner, EventDispatcherInterface $eventDispatcher) { $this->em = $em; $this->logger = new NullLogger(); $this->contentProxy = $contentProxy; + $this->tagsAssigner = $tagsAssigner; $this->eventDispatcher = $eventDispatcher; } @@ -81,6 +85,18 @@ abstract class AbstractImport implements ImportInterface return $this->markAsRead; } + /** + * Set whether articles should be fetched for updated content. + * + * @param bool $disableContentUpdate + */ + public function setDisableContentUpdate($disableContentUpdate) + { + $this->disableContentUpdate = $disableContentUpdate; + + return $this; + } + /** * Fetch content from the ContentProxy (using graby). * If it fails return the given entry to be saved in all case (to avoid user to loose the content). @@ -88,15 +104,16 @@ abstract class AbstractImport implements ImportInterface * @param Entry $entry Entry to update * @param string $url Url to grab content for * @param array $content An array with AT LEAST keys title, html, url, language & content_type to skip the fetchContent from the url - * - * @return Entry */ protected function fetchContent(Entry $entry, $url, array $content = []) { try { - return $this->contentProxy->updateEntry($entry, $url, $content); + $this->contentProxy->updateEntry($entry, $url, $content, $this->disableContentUpdate); } catch (\Exception $e) { - return $entry; + $this->logger->error('Error trying to import an entry.', [ + 'entry_url' => $url, + 'error_msg' => $e->getMessage(), + ]); } } diff --git a/src/Wallabag/ImportBundle/Import/BrowserImport.php b/src/Wallabag/ImportBundle/Import/BrowserImport.php index 8bf7d92e..71e65e59 100644 --- a/src/Wallabag/ImportBundle/Import/BrowserImport.php +++ b/src/Wallabag/ImportBundle/Import/BrowserImport.php @@ -4,7 +4,6 @@ namespace Wallabag\ImportBundle\Import; use Wallabag\CoreBundle\Entity\Entry; use Wallabag\UserBundle\Entity\User; -use Wallabag\CoreBundle\Helper\ContentProxy; use Wallabag\CoreBundle\Event\EntrySavedEvent; abstract class BrowserImport extends AbstractImport @@ -202,10 +201,10 @@ abstract class BrowserImport extends AbstractImport $entry->setTitle($data['title']); // update entry with content (in case fetching failed, the given entry will be return) - $entry = $this->fetchContent($entry, $data['url'], $data); + $this->fetchContent($entry, $data['url'], $data); if (array_key_exists('tags', $data)) { - $this->contentProxy->assignTagsToEntry( + $this->tagsAssigner->assignTagsToEntry( $entry, $data['tags'] ); diff --git a/src/Wallabag/ImportBundle/Import/InstapaperImport.php b/src/Wallabag/ImportBundle/Import/InstapaperImport.php index 70a53f1a..3aa12f6f 100644 --- a/src/Wallabag/ImportBundle/Import/InstapaperImport.php +++ b/src/Wallabag/ImportBundle/Import/InstapaperImport.php @@ -68,6 +68,14 @@ class InstapaperImport extends AbstractImport continue; } + // last element in the csv is the folder where the content belong + // BUT it can also be the status (since status = folder in Instapaper) + // and we don't want archive, unread & starred to become a tag + $tags = null; + if (false === in_array($data[3], ['Archive', 'Unread', 'Starred'])) { + $tags = [$data[3]]; + } + $entries[] = [ 'url' => $data[0], 'title' => $data[1], @@ -75,6 +83,7 @@ class InstapaperImport extends AbstractImport 'is_archived' => $data[3] === 'Archive' || $data[3] === 'Starred', 'is_starred' => $data[3] === 'Starred', 'html' => false, + 'tags' => $tags, ]; } fclose($handle); @@ -116,7 +125,15 @@ class InstapaperImport extends AbstractImport $entry->setTitle($importedEntry['title']); // update entry with content (in case fetching failed, the given entry will be return) - $entry = $this->fetchContent($entry, $importedEntry['url'], $importedEntry); + $this->fetchContent($entry, $importedEntry['url'], $importedEntry); + + if (!empty($importedEntry['tags'])) { + $this->tagsAssigner->assignTagsToEntry( + $entry, + $importedEntry['tags'], + $this->em->getUnitOfWork()->getScheduledEntityInsertions() + ); + } $entry->setArchived($importedEntry['is_archived']); $entry->setStarred($importedEntry['is_starred']); diff --git a/src/Wallabag/ImportBundle/Import/PinboardImport.php b/src/Wallabag/ImportBundle/Import/PinboardImport.php index d9865534..110b0464 100644 --- a/src/Wallabag/ImportBundle/Import/PinboardImport.php +++ b/src/Wallabag/ImportBundle/Import/PinboardImport.php @@ -109,10 +109,10 @@ class PinboardImport extends AbstractImport $entry->setTitle($data['title']); // update entry with content (in case fetching failed, the given entry will be return) - $entry = $this->fetchContent($entry, $data['url'], $data); + $this->fetchContent($entry, $data['url'], $data); if (!empty($data['tags'])) { - $this->contentProxy->assignTagsToEntry( + $this->tagsAssigner->assignTagsToEntry( $entry, $data['tags'], $this->em->getUnitOfWork()->getScheduledEntityInsertions() diff --git a/src/Wallabag/ImportBundle/Import/PocketImport.php b/src/Wallabag/ImportBundle/Import/PocketImport.php index 33093480..c1d5b6da 100644 --- a/src/Wallabag/ImportBundle/Import/PocketImport.php +++ b/src/Wallabag/ImportBundle/Import/PocketImport.php @@ -5,7 +5,6 @@ namespace Wallabag\ImportBundle\Import; use GuzzleHttp\Client; use GuzzleHttp\Exception\RequestException; use Wallabag\CoreBundle\Entity\Entry; -use Wallabag\CoreBundle\Helper\ContentProxy; class PocketImport extends AbstractImport { @@ -193,7 +192,7 @@ class PocketImport extends AbstractImport $entry->setUrl($url); // update entry with content (in case fetching failed, the given entry will be return) - $entry = $this->fetchContent($entry, $url); + $this->fetchContent($entry, $url); // 0, 1, 2 - 1 if the item is archived - 2 if the item should be deleted $entry->setArchived($importedEntry['status'] == 1 || $this->markAsRead); @@ -216,7 +215,7 @@ class PocketImport extends AbstractImport } if (isset($importedEntry['tags']) && !empty($importedEntry['tags'])) { - $this->contentProxy->assignTagsToEntry( + $this->tagsAssigner->assignTagsToEntry( $entry, array_keys($importedEntry['tags']), $this->em->getUnitOfWork()->getScheduledEntityInsertions() diff --git a/src/Wallabag/ImportBundle/Import/ReadabilityImport.php b/src/Wallabag/ImportBundle/Import/ReadabilityImport.php index de320d23..002b27f4 100644 --- a/src/Wallabag/ImportBundle/Import/ReadabilityImport.php +++ b/src/Wallabag/ImportBundle/Import/ReadabilityImport.php @@ -109,7 +109,7 @@ class ReadabilityImport extends AbstractImport $entry->setTitle($data['title']); // update entry with content (in case fetching failed, the given entry will be return) - $entry = $this->fetchContent($entry, $data['url'], $data); + $this->fetchContent($entry, $data['url'], $data); $entry->setArchived($data['is_archived']); $entry->setStarred($data['is_starred']); diff --git a/src/Wallabag/ImportBundle/Import/WallabagImport.php b/src/Wallabag/ImportBundle/Import/WallabagImport.php index 702da057..c64ccd64 100644 --- a/src/Wallabag/ImportBundle/Import/WallabagImport.php +++ b/src/Wallabag/ImportBundle/Import/WallabagImport.php @@ -108,10 +108,10 @@ abstract class WallabagImport extends AbstractImport $entry->setTitle($data['title']); // update entry with content (in case fetching failed, the given entry will be return) - $entry = $this->fetchContent($entry, $data['url'], $data); + $this->fetchContent($entry, $data['url'], $data); if (array_key_exists('tags', $data)) { - $this->contentProxy->assignTagsToEntry( + $this->tagsAssigner->assignTagsToEntry( $entry, $data['tags'], $this->em->getUnitOfWork()->getScheduledEntityInsertions() diff --git a/src/Wallabag/ImportBundle/Import/WallabagV1Import.php b/src/Wallabag/ImportBundle/Import/WallabagV1Import.php index 59e3ce02..1f0df646 100644 --- a/src/Wallabag/ImportBundle/Import/WallabagV1Import.php +++ b/src/Wallabag/ImportBundle/Import/WallabagV1Import.php @@ -4,6 +4,17 @@ namespace Wallabag\ImportBundle\Import; class WallabagV1Import extends WallabagImport { + protected $fetchingErrorMessage; + protected $fetchingErrorMessageTitle; + + public function __construct($em, $contentProxy, $tagsAssigner, $eventDispatcher, $fetchingErrorMessageTitle, $fetchingErrorMessage) + { + $this->fetchingErrorMessageTitle = $fetchingErrorMessageTitle; + $this->fetchingErrorMessage = $fetchingErrorMessage; + + parent::__construct($em, $contentProxy, $tagsAssigner, $eventDispatcher); + } + /** * {@inheritdoc} */ @@ -43,10 +54,11 @@ class WallabagV1Import extends WallabagImport 'created_at' => '', ]; - // force content to be refreshed in case on bad fetch in the v1 installation + // In case of a bad fetch in v1, replace title and content with v2 error strings + // If fetching fails again, they will get this instead of the v1 strings if (in_array($entry['title'], $this->untitled)) { - $data['title'] = ''; - $data['html'] = ''; + $data['title'] = $this->fetchingErrorMessageTitle; + $data['html'] = $this->fetchingErrorMessage; } if (array_key_exists('tags', $entry) && $entry['tags'] != '') { diff --git a/src/Wallabag/ImportBundle/Import/WallabagV2Import.php b/src/Wallabag/ImportBundle/Import/WallabagV2Import.php index d2a89d79..3e085ecf 100644 --- a/src/Wallabag/ImportBundle/Import/WallabagV2Import.php +++ b/src/Wallabag/ImportBundle/Import/WallabagV2Import.php @@ -36,8 +36,8 @@ class WallabagV2Import extends WallabagImport return [ 'html' => $entry['content'], 'content_type' => $entry['mimetype'], - 'is_archived' => (int) ($entry['is_archived'] || $this->markAsRead), - 'is_starred' => false, + 'is_archived' => (bool) ($entry['is_archived'] || $this->markAsRead), + 'is_starred' => (bool) $entry['is_starred'], ] + $entry; } diff --git a/src/Wallabag/ImportBundle/Resources/config/services.yml b/src/Wallabag/ImportBundle/Resources/config/services.yml index c4fe3f92..b224a6a2 100644 --- a/src/Wallabag/ImportBundle/Resources/config/services.yml +++ b/src/Wallabag/ImportBundle/Resources/config/services.yml @@ -20,6 +20,7 @@ services: arguments: - "@doctrine.orm.entity_manager" - "@wallabag_core.content_proxy" + - "@wallabag_core.tags_assigner" - "@event_dispatcher" calls: - [ setClient, [ "@wallabag_import.pocket.client" ] ] @@ -32,7 +33,10 @@ services: arguments: - "@doctrine.orm.entity_manager" - "@wallabag_core.content_proxy" + - "@wallabag_core.tags_assigner" - "@event_dispatcher" + - "%wallabag_core.fetching_error_message_title%" + - "%wallabag_core.fetching_error_message%" calls: - [ setLogger, [ "@logger" ]] tags: @@ -43,6 +47,7 @@ services: arguments: - "@doctrine.orm.entity_manager" - "@wallabag_core.content_proxy" + - "@wallabag_core.tags_assigner" - "@event_dispatcher" calls: - [ setLogger, [ "@logger" ]] @@ -54,6 +59,7 @@ services: arguments: - "@doctrine.orm.entity_manager" - "@wallabag_core.content_proxy" + - "@wallabag_core.tags_assigner" - "@event_dispatcher" calls: - [ setLogger, [ "@logger" ]] @@ -65,6 +71,7 @@ services: arguments: - "@doctrine.orm.entity_manager" - "@wallabag_core.content_proxy" + - "@wallabag_core.tags_assigner" - "@event_dispatcher" calls: - [ setLogger, [ "@logger" ]] @@ -76,6 +83,7 @@ services: arguments: - "@doctrine.orm.entity_manager" - "@wallabag_core.content_proxy" + - "@wallabag_core.tags_assigner" - "@event_dispatcher" calls: - [ setLogger, [ "@logger" ]] @@ -87,6 +95,7 @@ services: arguments: - "@doctrine.orm.entity_manager" - "@wallabag_core.content_proxy" + - "@wallabag_core.tags_assigner" - "@event_dispatcher" calls: - [ setLogger, [ "@logger" ]] @@ -97,6 +106,7 @@ services: arguments: - "@doctrine.orm.entity_manager" - "@wallabag_core.content_proxy" + - "@wallabag_core.tags_assigner" - "@event_dispatcher" calls: - [ setLogger, [ "@logger" ]] diff --git a/src/Wallabag/UserBundle/Controller/ManageController.php b/src/Wallabag/UserBundle/Controller/ManageController.php index 92ee2b41..084f2c67 100644 --- a/src/Wallabag/UserBundle/Controller/ManageController.php +++ b/src/Wallabag/UserBundle/Controller/ManageController.php @@ -4,35 +4,21 @@ namespace Wallabag\UserBundle\Controller; use FOS\UserBundle\Event\UserEvent; use FOS\UserBundle\FOSUserEvents; +use Pagerfanta\Adapter\DoctrineORMAdapter; +use Pagerfanta\Exception\OutOfRangeCurrentPageException; +use Pagerfanta\Pagerfanta; use Symfony\Component\HttpFoundation\Request; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Wallabag\UserBundle\Entity\User; -use Wallabag\CoreBundle\Entity\Config; +use Wallabag\UserBundle\Form\SearchUserType; /** * User controller. */ class ManageController extends Controller { - /** - * Lists all User entities. - * - * @Route("/", name="user_index") - * @Method("GET") - */ - public function indexAction() - { - $em = $this->getDoctrine()->getManager(); - - $users = $em->getRepository('WallabagUserBundle:User')->findAll(); - - return $this->render('WallabagUserBundle:Manage:index.html.twig', array( - 'users' => $users, - )); - } - /** * Creates a new User entity. * @@ -47,9 +33,7 @@ class ManageController extends Controller // enable created user by default $user->setEnabled(true); - $form = $this->createForm('Wallabag\UserBundle\Form\NewUserType', $user, [ - 'validation_groups' => ['Profile'], - ]); + $form = $this->createForm('Wallabag\UserBundle\Form\NewUserType', $user); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { @@ -146,4 +130,49 @@ class ManageController extends Controller ->getForm() ; } + + /** + * @param Request $request + * @param int $page + * + * @Route("/list/{page}", name="user_index", defaults={"page" = 1}) + * + * Default parameter for page is hardcoded (in duplication of the defaults from the Route) + * because this controller is also called inside the layout template without any page as argument + * + * @return \Symfony\Component\HttpFoundation\Response + */ + public function searchFormAction(Request $request, $page = 1) + { + $em = $this->getDoctrine()->getManager(); + $qb = $em->getRepository('WallabagUserBundle:User')->createQueryBuilder('u'); + + $form = $this->createForm(SearchUserType::class); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $this->get('logger')->info('searching users'); + + $searchTerm = (isset($request->get('search_user')['term']) ? $request->get('search_user')['term'] : ''); + + $qb = $em->getRepository('WallabagUserBundle:User')->getQueryBuilderForSearch($searchTerm); + } + + $pagerAdapter = new DoctrineORMAdapter($qb->getQuery(), true, false); + $pagerFanta = new Pagerfanta($pagerAdapter); + $pagerFanta->setMaxPerPage(50); + + try { + $pagerFanta->setCurrentPage($page); + } catch (OutOfRangeCurrentPageException $e) { + if ($page > 1) { + return $this->redirect($this->generateUrl('user_index', ['page' => $pagerFanta->getNbPages()]), 302); + } + } + + return $this->render('WallabagUserBundle:Manage:index.html.twig', [ + 'searchForm' => $form->createView(), + 'users' => $pagerFanta, + ]); + } } diff --git a/src/Wallabag/UserBundle/Entity/User.php b/src/Wallabag/UserBundle/Entity/User.php index 3a167de7..aba76ca7 100644 --- a/src/Wallabag/UserBundle/Entity/User.php +++ b/src/Wallabag/UserBundle/Entity/User.php @@ -4,11 +4,12 @@ namespace Wallabag\UserBundle\Entity; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\Mapping as ORM; +use JMS\Serializer\Annotation\Groups; +use JMS\Serializer\Annotation\XmlRoot; +use JMS\Serializer\Annotation\Accessor; use Scheb\TwoFactorBundle\Model\Email\TwoFactorInterface; use Scheb\TwoFactorBundle\Model\TrustedComputerInterface; use FOS\UserBundle\Model\User as BaseUser; -use JMS\Serializer\Annotation\ExclusionPolicy; -use JMS\Serializer\Annotation\Expose; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Component\Security\Core\User\UserInterface; use Wallabag\ApiBundle\Entity\Client; @@ -18,23 +19,25 @@ use Wallabag\CoreBundle\Entity\Entry; /** * User. * + * @XmlRoot("user") * @ORM\Entity(repositoryClass="Wallabag\UserBundle\Repository\UserRepository") * @ORM\Table(name="`user`") * @ORM\HasLifecycleCallbacks() - * @ExclusionPolicy("all") * * @UniqueEntity("email") * @UniqueEntity("username") */ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterface { + /** @Serializer\XmlAttribute */ /** * @var int * - * @Expose * @ORM\Column(name="id", type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") + * + * @Groups({"user_api", "user_api_with_client"}) */ protected $id; @@ -42,20 +45,40 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf * @var string * * @ORM\Column(name="name", type="text", nullable=true) + * + * @Groups({"user_api", "user_api_with_client"}) */ protected $name; /** - * @var date + * @var string + * + * @Groups({"user_api", "user_api_with_client"}) + */ + protected $username; + + /** + * @var string + * + * @Groups({"user_api", "user_api_with_client"}) + */ + protected $email; + + /** + * @var \DateTime * * @ORM\Column(name="created_at", type="datetime") + * + * @Groups({"user_api", "user_api_with_client"}) */ protected $createdAt; /** - * @var date + * @var \DateTime * * @ORM\Column(name="updated_at", type="datetime") + * + * @Groups({"user_api", "user_api_with_client"}) */ protected $updatedAt; @@ -75,7 +98,8 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf private $authCode; /** - * @var bool Enabled yes/no + * @var bool + * * @ORM\Column(type="boolean") */ private $twoFactorAuthentication = false; @@ -86,10 +110,20 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf private $trusted; /** + * @var ArrayCollection + * * @ORM\OneToMany(targetEntity="Wallabag\ApiBundle\Entity\Client", mappedBy="user", cascade={"remove"}) */ protected $clients; + /** + * @see getFirstClient() below + * + * @Groups({"user_api_with_client"}) + * @Accessor(getter="getFirstClient") + */ + protected $default_client; + public function __construct() { parent::__construct(); @@ -135,7 +169,7 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf } /** - * @return string + * @return \DateTime */ public function getCreatedAt() { @@ -143,7 +177,7 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf } /** - * @return string + * @return \DateTime */ public function getUpdatedAt() { @@ -266,4 +300,16 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf { return $this->clients; } + + /** + * Only used by the API when creating a new user it'll also return the first client (which was also created at the same time). + * + * @return Client + */ + public function getFirstClient() + { + if (!empty($this->clients)) { + return $this->clients->first(); + } + } } diff --git a/src/Wallabag/UserBundle/EventListener/AuthenticationFailureListener.php b/src/Wallabag/UserBundle/EventListener/AuthenticationFailureListener.php new file mode 100644 index 00000000..10f13233 --- /dev/null +++ b/src/Wallabag/UserBundle/EventListener/AuthenticationFailureListener.php @@ -0,0 +1,40 @@ +requestStack = $requestStack; + $this->logger = $logger; + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() + { + return [ + AuthenticationEvents::AUTHENTICATION_FAILURE => 'onAuthenticationFailure', + ]; + } + + /** + * On failure, add a custom error in log so server admin can configure fail2ban to block IP from people who try to login too much. + */ + public function onAuthenticationFailure() + { + $request = $this->requestStack->getMasterRequest(); + + $this->logger->error('Authentication failure for user "'.$request->request->get('_username').'", from IP "'.$request->getClientIp().'", with UA: "'.$request->server->get('HTTP_USER_AGENT').'".'); + } +} diff --git a/src/Wallabag/UserBundle/EventListener/CreateConfigListener.php b/src/Wallabag/UserBundle/EventListener/CreateConfigListener.php index 0bdd1cae..e4d55c19 100644 --- a/src/Wallabag/UserBundle/EventListener/CreateConfigListener.php +++ b/src/Wallabag/UserBundle/EventListener/CreateConfigListener.php @@ -5,7 +5,6 @@ namespace Wallabag\UserBundle\EventListener; use Doctrine\ORM\EntityManager; use FOS\UserBundle\Event\UserEvent; use FOS\UserBundle\FOSUserEvents; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Wallabag\CoreBundle\Entity\Config; @@ -47,7 +46,7 @@ class CreateConfigListener implements EventSubscriberInterface ]; } - public function createConfig(UserEvent $event, $eventName = null, EventDispatcherInterface $eventDispatcher = null) + public function createConfig(UserEvent $event) { $config = new Config($event->getUser()); $config->setTheme($this->theme); diff --git a/src/Wallabag/UserBundle/Form/SearchUserType.php b/src/Wallabag/UserBundle/Form/SearchUserType.php new file mode 100644 index 00000000..9ce46ee1 --- /dev/null +++ b/src/Wallabag/UserBundle/Form/SearchUserType.php @@ -0,0 +1,29 @@ +setMethod('GET') + ->add('term', TextType::class, [ + 'required' => true, + 'label' => 'user.new.form_search.term_label', + ]) + ; + } + + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults([ + 'csrf_protection' => false, + ]); + } +} diff --git a/src/Wallabag/UserBundle/Repository/UserRepository.php b/src/Wallabag/UserBundle/Repository/UserRepository.php index f913f52d..6adbe329 100644 --- a/src/Wallabag/UserBundle/Repository/UserRepository.php +++ b/src/Wallabag/UserBundle/Repository/UserRepository.php @@ -52,4 +52,17 @@ class UserRepository extends EntityRepository ->getQuery() ->getSingleScalarResult(); } + + /** + * Retrieves users filtered with a search term. + * + * @param string $term + * + * @return QueryBuilder + */ + public function getQueryBuilderForSearch($term) + { + return $this->createQueryBuilder('u') + ->andWhere('lower(u.username) LIKE lower(:term) OR lower(u.email) LIKE lower(:term) OR lower(u.name) LIKE lower(:term)')->setParameter('term', '%'.$term.'%'); + } } diff --git a/src/Wallabag/UserBundle/Resources/config/services.yml b/src/Wallabag/UserBundle/Resources/config/services.yml index 72f6f12c..d3925de3 100644 --- a/src/Wallabag/UserBundle/Resources/config/services.yml +++ b/src/Wallabag/UserBundle/Resources/config/services.yml @@ -7,7 +7,7 @@ services: - "%scheb_two_factor.email.sender_email%" - "%scheb_two_factor.email.sender_name%" - '@=service(''craue_config'').get(''wallabag_support_url'')' - - '@=service(''craue_config'').get(''wallabag_url'')' + - '%domain_name%' wallabag_user.password_resetting: class: Wallabag\UserBundle\EventListener\PasswordResettingListener @@ -35,3 +35,11 @@ services: - "%wallabag_core.list_mode%" tags: - { name: kernel.event_subscriber } + + wallabag_user.listener.authentication_failure_event_listener: + class: Wallabag\UserBundle\EventListener\AuthenticationFailureListener + arguments: + - "@request_stack" + - "@logger" + tags: + - { name: kernel.event_listener, event: security.authentication.failure, method: onAuthenticationFailure } diff --git a/src/Wallabag/UserBundle/Resources/views/Manage/index.html.twig b/src/Wallabag/UserBundle/Resources/views/Manage/index.html.twig index daba29e4..15002632 100644 --- a/src/Wallabag/UserBundle/Resources/views/Manage/index.html.twig +++ b/src/Wallabag/UserBundle/Resources/views/Manage/index.html.twig @@ -7,37 +7,60 @@
    + {% if users.getNbPages > 1 %} + {{ pagerfanta(users, 'twitter_bootstrap_translated', {'proximity': 1}) }} + {% endif %}
    -
    +

    {{ 'user.description'|trans|raw }}

    +
    +
    +
    +
    + {% if form_errors(searchForm) %} + {{ form_errors(searchForm) }} + {% endif %} + + {% if form_errors(searchForm.term) %} + {{ form_errors(searchForm.term) }} + {% endif %} -
    {{ 'developer.existing_clients.field_id'|trans }}{{ client.id }}_{{ client.randomId }}{{ client.clientId }}
    {{ 'developer.existing_clients.field_secret'|trans }}
    - - - - - - - - - - {% for user in users %} - - - - - - - {% endfor %} - -
    {{ 'user.form.username_label'|trans }}{{ 'user.form.email_label'|trans }}{{ 'user.form.last_login_label'|trans }}{{ 'user.list.actions'|trans }}
    {{ user.username }}{{ user.email }}{% if user.lastLogin %}{{ user.lastLogin|date('Y-m-d H:i:s') }}{% endif %} - {{ 'user.list.edit_action'|trans }} -
    -
    -

    - {{ 'user.list.create_new_one'|trans }} -

    + {{ form_widget(searchForm.term, { 'attr': {'autocomplete': 'off', 'placeholder': 'user.search.placeholder'} }) }} + + {{ form_rest(searchForm) }} + + + + + + + + + + + + + + {% for user in users %} + + + + + + + {% endfor %} + +
    {{ 'user.form.username_label'|trans }}{{ 'user.form.email_label'|trans }}{{ 'user.form.last_login_label'|trans }}{{ 'user.list.actions'|trans }}
    {{ user.username }}{{ user.email }}{% if user.lastLogin %}{{ user.lastLogin|date('Y-m-d H:i:s') }}{% endif %} + {{ 'user.list.edit_action'|trans }} +
    +
    +

    + {{ 'user.list.create_new_one'|trans }} +

    + {% if users.getNbPages > 1 %} + {{ pagerfanta(users, 'twitter_bootstrap_translated', {'proximity': 1}) }} + {% endif %} diff --git a/tests/Wallabag/AnnotationBundle/Controller/AnnotationControllerTest.php b/tests/Wallabag/AnnotationBundle/Controller/AnnotationControllerTest.php index 81f9e9ec..3c94382c 100644 --- a/tests/Wallabag/AnnotationBundle/Controller/AnnotationControllerTest.php +++ b/tests/Wallabag/AnnotationBundle/Controller/AnnotationControllerTest.php @@ -84,7 +84,9 @@ class AnnotationControllerTest extends WallabagAnnotationTestCase $content = json_encode([ 'text' => 'my annotation', 'quote' => 'my quote', - 'ranges' => ['start' => '', 'startOffset' => 24, 'end' => '', 'endOffset' => 31], + 'ranges' => [ + ['start' => '', 'startOffset' => 24, 'end' => '', 'endOffset' => 31], + ], ]); $this->client->request('POST', $prefixUrl.'/'.$entry->getId().'.json', [], [], $headers, $content); @@ -106,6 +108,36 @@ class AnnotationControllerTest extends WallabagAnnotationTestCase $this->assertEquals('my annotation', $annotation->getText()); } + /** + * @dataProvider dataForEachAnnotations + */ + public function testSetAnnotationWithQuoteTooLong($prefixUrl) + { + $em = $this->client->getContainer()->get('doctrine.orm.entity_manager'); + + if ('annotations' === $prefixUrl) { + $this->logInAs('admin'); + } + + /** @var Entry $entry */ + $entry = $em + ->getRepository('WallabagCoreBundle:Entry') + ->findOneByUsernameAndNotArchived('admin'); + + $longQuote = str_repeat('a', 10001); + $headers = ['CONTENT_TYPE' => 'application/json']; + $content = json_encode([ + 'text' => 'my annotation', + 'quote' => $longQuote, + 'ranges' => [ + ['start' => '', 'startOffset' => 24, 'end' => '', 'endOffset' => 31], + ], + ]); + $this->client->request('POST', $prefixUrl.'/'.$entry->getId().'.json', [], [], $headers, $content); + + $this->assertEquals(400, $this->client->getResponse()->getStatusCode()); + } + /** * Test editing an existing annotation. * diff --git a/tests/Wallabag/ApiBundle/Controller/DeveloperControllerTest.php b/tests/Wallabag/ApiBundle/Controller/DeveloperControllerTest.php index 6659443b..53aed12b 100644 --- a/tests/Wallabag/ApiBundle/Controller/DeveloperControllerTest.php +++ b/tests/Wallabag/ApiBundle/Controller/DeveloperControllerTest.php @@ -3,6 +3,7 @@ namespace Tests\Wallabag\ApiBundle\Controller; use Tests\Wallabag\CoreBundle\WallabagCoreTestCase; +use Wallabag\ApiBundle\Entity\Client; class DeveloperControllerTest extends WallabagCoreTestCase { @@ -33,14 +34,10 @@ class DeveloperControllerTest extends WallabagCoreTestCase $this->assertContains('My app', $alert[0]); } - /** - * @depends testCreateClient - */ - public function testCreateToken() + public function testCreateTokenFromPasswords() { $client = $this->getClient(); - $em = $client->getContainer()->get('doctrine.orm.entity_manager'); - $apiClient = $em->getRepository('WallabagApiBundle:Client')->findOneByName('My app'); + $apiClient = $this->createApiClientForUser('admin'); $client->request('POST', '/oauth/v2/token', [ 'grant_type' => 'password', @@ -59,6 +56,26 @@ class DeveloperControllerTest extends WallabagCoreTestCase $this->assertArrayHasKey('refresh_token', $data); } + public function testCreateTokenFromClientCredentialsOnly() + { + $client = $this->getClient(); + $apiClient = $this->createApiClientForUser('admin', ['client_credentials']); + + $client->request('POST', '/oauth/v2/token', [ + 'grant_type' => 'client_credentials', + 'client_id' => $apiClient->getPublicId(), + 'client_secret' => $apiClient->getSecret(), + ]); + + $this->assertEquals(200, $client->getResponse()->getStatusCode()); + + $data = json_decode($client->getResponse()->getContent(), true); + $this->assertArrayHasKey('access_token', $data); + $this->assertArrayHasKey('expires_in', $data); + $this->assertArrayHasKey('token_type', $data); + // Client Credentials created-clients have no refresh tokens + } + public function testListingClient() { $this->logInAs('admin'); @@ -83,6 +100,7 @@ class DeveloperControllerTest extends WallabagCoreTestCase public function testRemoveClient() { $client = $this->getClient(); + $adminApiClient = $this->createApiClientForUser('admin'); $em = $client->getContainer()->get('doctrine.orm.entity_manager'); // Try to remove an admin's client with a wrong user @@ -90,12 +108,8 @@ class DeveloperControllerTest extends WallabagCoreTestCase $client->request('GET', '/developer'); $this->assertContains('no_client', $client->getResponse()->getContent()); - // get an ID of a admin's client - $this->logInAs('admin'); - $nbClients = $em->getRepository('WallabagApiBundle:Client')->findByUser($this->getLoggedInUserId()); - $this->logInAs('bob'); - $client->request('GET', '/developer/client/delete/'.$nbClients[0]->getId()); + $client->request('GET', '/developer/client/delete/'.$adminApiClient->getId()); $this->assertEquals(403, $client->getResponse()->getStatusCode()); // Try to remove the admin's client with the good user @@ -111,7 +125,30 @@ class DeveloperControllerTest extends WallabagCoreTestCase $client->click($link); $this->assertEquals(302, $client->getResponse()->getStatusCode()); - $newNbClients = $em->getRepository('WallabagApiBundle:Client')->findByUser($this->getLoggedInUserId()); - $this->assertGreaterThan(count($newNbClients), count($nbClients)); + $this->assertNull( + $em->getRepository('WallabagApiBundle:Client')->find($adminApiClient->getId()), + 'The client should have been removed' + ); + } + + /** + * @param string $username + * + * @param array $grantTypes + * @return Client + */ + private function createApiClientForUser($username, $grantTypes = ['password']) + { + $client = $this->getClient(); + $em = $client->getContainer()->get('doctrine.orm.entity_manager'); + $userManager = $client->getContainer()->get('fos_user.user_manager'); + $user = $userManager->findUserBy(array('username' => $username)); + $apiClient = new Client($user); + $apiClient->setName('My app'); + $apiClient->setAllowedGrantTypes($grantTypes); + $em->persist($apiClient); + $em->flush(); + + return $apiClient; } } diff --git a/tests/Wallabag/ApiBundle/Controller/EntryRestControllerTest.php b/tests/Wallabag/ApiBundle/Controller/EntryRestControllerTest.php index 0a65f9ce..067aed2c 100644 --- a/tests/Wallabag/ApiBundle/Controller/EntryRestControllerTest.php +++ b/tests/Wallabag/ApiBundle/Controller/EntryRestControllerTest.php @@ -3,8 +3,10 @@ namespace Tests\Wallabag\ApiBundle\Controller; use Tests\Wallabag\ApiBundle\WallabagApiTestCase; +use Wallabag\CoreBundle\Entity\Entry; use Wallabag\CoreBundle\Entity\Tag; use Wallabag\CoreBundle\Helper\ContentProxy; +use Wallabag\UserBundle\Entity\User; class EntryRestControllerTest extends WallabagApiTestCase { @@ -126,6 +128,7 @@ class EntryRestControllerTest extends WallabagApiTestCase 'perPage' => 2, 'tags' => 'foo', 'since' => 1443274283, + 'public' => 0, ]); $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); @@ -152,6 +155,53 @@ class EntryRestControllerTest extends WallabagApiTestCase $this->assertContains('order=asc', $content['_links'][$link]['href']); $this->assertContains('tags=foo', $content['_links'][$link]['href']); $this->assertContains('since=1443274283', $content['_links'][$link]['href']); + $this->assertContains('public=0', $content['_links'][$link]['href']); + } + + $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-Type')); + } + + public function testGetEntriesPublicOnly() + { + $entry = $this->client->getContainer() + ->get('doctrine.orm.entity_manager') + ->getRepository('WallabagCoreBundle:Entry') + ->findOneByUser(1); + + if (!$entry) { + $this->markTestSkipped('No content found in db.'); + } + + // generate at least one public entry + $entry->generateUid(); + + $em = $this->client->getContainer()->get('doctrine.orm.entity_manager'); + $em->persist($entry); + $em->flush(); + + $this->client->request('GET', '/api/entries', [ + 'public' => 1, + ]); + + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + + $content = json_decode($this->client->getResponse()->getContent(), true); + + $this->assertGreaterThanOrEqual(1, count($content)); + $this->assertArrayHasKey('items', $content['_embedded']); + $this->assertGreaterThanOrEqual(1, $content['total']); + $this->assertEquals(1, $content['page']); + $this->assertEquals(30, $content['limit']); + $this->assertGreaterThanOrEqual(1, $content['pages']); + + $this->assertArrayHasKey('_links', $content); + $this->assertArrayHasKey('self', $content['_links']); + $this->assertArrayHasKey('first', $content['_links']); + $this->assertArrayHasKey('last', $content['_links']); + + foreach (['self', 'first', 'last'] as $link) { + $this->assertArrayHasKey('href', $content['_links'][$link]); + $this->assertContains('public=1', $content['_links'][$link]['href']); } $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-Type')); @@ -315,7 +365,7 @@ class EntryRestControllerTest extends WallabagApiTestCase $entry = $this->client->getContainer() ->get('doctrine.orm.entity_manager') ->getRepository('WallabagCoreBundle:Entry') - ->findOneByUser(1); + ->findOneByUser(1, ['id' => 'asc']); if (!$entry) { $this->markTestSkipped('No content found in db.'); @@ -342,6 +392,11 @@ class EntryRestControllerTest extends WallabagApiTestCase 'url' => 'http://www.lemonde.fr/pixels/article/2015/03/28/plongee-dans-l-univers-d-ingress-le-jeu-de-google-aux-frontieres-du-reel_4601155_4408996.html', 'tags' => 'google', 'title' => 'New title for my article', + 'content' => 'my content', + 'language' => 'de', + 'published_at' => '2016-09-08T11:55:58+0200', + 'authors' => 'bob,helen', + 'public' => 1, ]); $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); @@ -354,7 +409,14 @@ class EntryRestControllerTest extends WallabagApiTestCase $this->assertEquals(false, $content['is_starred']); $this->assertEquals('New title for my article', $content['title']); $this->assertEquals(1, $content['user_id']); - $this->assertCount(1, $content['tags']); + $this->assertCount(2, $content['tags']); + $this->assertSame('my content', $content['content']); + $this->assertSame('de', $content['language']); + $this->assertSame('2016-09-08T11:55:58+0200', $content['published_at']); + $this->assertCount(2, $content['published_by']); + $this->assertContains('bob', $content['published_by']); + $this->assertContains('helen', $content['published_by']); + $this->assertTrue($content['is_public'], 'A public link has been generated for that entry'); } public function testPostSameEntry() @@ -373,7 +435,7 @@ class EntryRestControllerTest extends WallabagApiTestCase $this->assertEquals('http://www.lemonde.fr/pixels/article/2015/03/28/plongee-dans-l-univers-d-ingress-le-jeu-de-google-aux-frontieres-du-reel_4601155_4408996.html', $content['url']); $this->assertEquals(true, $content['is_archived']); $this->assertEquals(false, $content['is_starred']); - $this->assertCount(2, $content['tags']); + $this->assertCount(3, $content['tags']); } public function testPostEntryWhenFetchContentFails() @@ -465,6 +527,11 @@ class EntryRestControllerTest extends WallabagApiTestCase 'tags' => 'new tag '.uniqid(), 'starred' => '1', 'archive' => '0', + 'language' => 'de_AT', + 'preview_picture' => 'http://preview.io/picture.jpg', + 'authors' => 'bob,sponge', + 'content' => 'awesome', + 'public' => 0, ]); $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); @@ -476,6 +543,12 @@ class EntryRestControllerTest extends WallabagApiTestCase $this->assertEquals('New awesome title', $content['title']); $this->assertGreaterThan($nbTags, count($content['tags'])); $this->assertEquals(1, $content['user_id']); + $this->assertEquals('de_AT', $content['language']); + $this->assertEquals('http://preview.io/picture.jpg', $content['preview_picture']); + $this->assertContains('sponge', $content['published_by']); + $this->assertContains('bob', $content['published_by']); + $this->assertEquals('awesome', $content['content']); + $this->assertFalse($content['is_public'], 'Entry is no more shared'); } public function testPatchEntryWithoutQuotes() @@ -497,6 +570,7 @@ class EntryRestControllerTest extends WallabagApiTestCase 'tags' => 'new tag '.uniqid(), 'starred' => 1, 'archive' => 0, + 'authors' => ['bob', 'sponge'], ]); $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); @@ -507,6 +581,7 @@ class EntryRestControllerTest extends WallabagApiTestCase $this->assertEquals($entry->getUrl(), $content['url']); $this->assertEquals('New awesome title', $content['title']); $this->assertGreaterThan($nbTags, count($content['tags'])); + $this->assertTrue(empty($content['published_by']), 'Authors were not saved because of an array instead of a string'); } public function testGetTagsEntry() @@ -692,7 +767,7 @@ class EntryRestControllerTest extends WallabagApiTestCase $content = json_decode($this->client->getResponse()->getContent(), true); - $this->assertEquals(true, $content['exists']); + $this->assertEquals(2, $content['exists']); } public function testGetEntriesExistsWithManyUrls() @@ -707,7 +782,7 @@ class EntryRestControllerTest extends WallabagApiTestCase $this->assertArrayHasKey($url1, $content); $this->assertArrayHasKey($url2, $content); - $this->assertEquals(true, $content[$url1]); + $this->assertEquals(2, $content[$url1]); $this->assertEquals(false, $content[$url2]); } @@ -764,4 +839,131 @@ class EntryRestControllerTest extends WallabagApiTestCase $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-Type')); } + + public function testPostEntriesTagsListAction() + { + $entry = $this->client->getContainer()->get('doctrine.orm.entity_manager') + ->getRepository('WallabagCoreBundle:Entry') + ->findByUrlAndUserId('http://0.0.0.0/entry4', 1); + + $tags = $entry->getTags(); + + $this->assertCount(2, $tags); + + $list = [ + [ + 'url' => 'http://0.0.0.0/entry4', + 'tags' => 'new tag 1, new tag 2', + ], + ]; + + $this->client->request('POST', '/api/entries/tags/lists?list='.json_encode($list)); + + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + + $content = json_decode($this->client->getResponse()->getContent(), true); + + $this->assertInternalType('int', $content[0]['entry']); + $this->assertEquals('http://0.0.0.0/entry4', $content[0]['url']); + + $entry = $this->client->getContainer()->get('doctrine.orm.entity_manager') + ->getRepository('WallabagCoreBundle:Entry') + ->findByUrlAndUserId('http://0.0.0.0/entry4', 1); + + $tags = $entry->getTags(); + $this->assertCount(4, $tags); + } + + public function testDeleteEntriesTagsListAction() + { + $em = $this->client->getContainer()->get('doctrine.orm.entity_manager'); + $entry = new Entry($em->getReference(User::class, 1)); + $entry->setUrl('http://0.0.0.0/test-entry'); + $entry->addTag((new Tag())->setLabel('foo-tag')); + $entry->addTag((new Tag())->setLabel('bar-tag')); + $em->persist($entry); + $em->flush(); + + $em->clear(); + + $list = [ + [ + 'url' => 'http://0.0.0.0/test-entry', + 'tags' => 'foo-tag, bar-tag', + ], + ]; + + $this->client->request('DELETE', '/api/entries/tags/list?list='.json_encode($list)); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + + $entry = $em->getRepository('WallabagCoreBundle:Entry')->find($entry->getId()); + $this->assertCount(0, $entry->getTags()); + } + + public function testPostEntriesListAction() + { + $list = [ + 'http://www.lemonde.fr/musiques/article/2017/04/23/loin-de-la-politique-le-printemps-de-bourges-retombe-en-enfance_5115862_1654986.html', + 'http://0.0.0.0/entry2', + ]; + + $this->client->request('POST', '/api/entries/lists?urls='.json_encode($list)); + + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + + $content = json_decode($this->client->getResponse()->getContent(), true); + + $this->assertInternalType('int', $content[0]['entry']); + $this->assertEquals('http://www.lemonde.fr/musiques/article/2017/04/23/loin-de-la-politique-le-printemps-de-bourges-retombe-en-enfance_5115862_1654986.html', $content[0]['url']); + + $this->assertInternalType('int', $content[1]['entry']); + $this->assertEquals('http://0.0.0.0/entry2', $content[1]['url']); + } + + public function testDeleteEntriesListAction() + { + $em = $this->client->getContainer()->get('doctrine.orm.entity_manager'); + $em->persist((new Entry($em->getReference(User::class, 1)))->setUrl('http://0.0.0.0/test-entry1')); + + $em->flush(); + $em->clear(); + $list = [ + 'http://0.0.0.0/test-entry1', + 'http://0.0.0.0/test-entry-not-exist', + ]; + + $this->client->request('DELETE', '/api/entries/list?urls='.json_encode($list)); + + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + + $content = json_decode($this->client->getResponse()->getContent(), true); + + $this->assertTrue($content[0]['entry']); + $this->assertEquals('http://0.0.0.0/test-entry1', $content[0]['url']); + + $this->assertFalse($content[1]['entry']); + $this->assertEquals('http://0.0.0.0/test-entry-not-exist', $content[1]['url']); + } + + public function testLimitBulkAction() + { + $list = [ + 'http://0.0.0.0/entry1', + 'http://0.0.0.0/entry1', + 'http://0.0.0.0/entry1', + 'http://0.0.0.0/entry1', + 'http://0.0.0.0/entry1', + 'http://0.0.0.0/entry1', + 'http://0.0.0.0/entry1', + 'http://0.0.0.0/entry1', + 'http://0.0.0.0/entry1', + 'http://0.0.0.0/entry1', + 'http://0.0.0.0/entry1', + ]; + + $this->client->request('POST', '/api/entries/lists?urls='.json_encode($list)); + + $this->assertEquals(400, $this->client->getResponse()->getStatusCode()); + $this->assertContains('API limit reached', $this->client->getResponse()->getContent()); + } } diff --git a/tests/Wallabag/ApiBundle/Controller/TagRestControllerTest.php b/tests/Wallabag/ApiBundle/Controller/TagRestControllerTest.php index bde5251f..7f69bd67 100644 --- a/tests/Wallabag/ApiBundle/Controller/TagRestControllerTest.php +++ b/tests/Wallabag/ApiBundle/Controller/TagRestControllerTest.php @@ -22,39 +22,49 @@ class TagRestControllerTest extends WallabagApiTestCase return end($content); } - /** - * @depends testGetUserTags - */ - public function testDeleteUserTag($tag) + public function testDeleteUserTag() { - $tagName = $tag['label']; + $tagLabel = 'tagtest'; + $tag = new Tag(); + $tag->setLabel($tagLabel); - $this->client->request('DELETE', '/api/tags/'.$tag['id'].'.json'); + $em = $this->client->getContainer()->get('doctrine.orm.entity_manager'); + $em->persist($tag); + $em->flush(); + $em->clear(); + + $this->client->request('DELETE', '/api/tags/'.$tag->getId().'.json'); $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); $content = json_decode($this->client->getResponse()->getContent(), true); $this->assertArrayHasKey('label', $content); - $this->assertEquals($tag['label'], $content['label']); - $this->assertEquals($tag['slug'], $content['slug']); + $this->assertEquals($tag->getLabel(), $content['label']); + $this->assertEquals($tag->getSlug(), $content['slug']); - $entries = $this->client->getContainer() - ->get('doctrine.orm.entity_manager') - ->getRepository('WallabagCoreBundle:Entry') - ->findAllByTagId($this->user->getId(), $tag['id']); + $entries = $em->getRepository('WallabagCoreBundle:Entry') + ->findAllByTagId($this->user->getId(), $tag->getId()); $this->assertCount(0, $entries); - $tag = $this->client->getContainer() - ->get('doctrine.orm.entity_manager') - ->getRepository('WallabagCoreBundle:Tag') - ->findOneByLabel($tagName); + $tag = $em->getRepository('WallabagCoreBundle:Tag')->findOneByLabel($tagLabel); + + $this->assertNull($tag, $tagLabel.' was removed because it begun an orphan tag'); + } - $this->assertNull($tag, $tagName.' was removed because it begun an orphan tag'); + public function dataForDeletingTagByLabel() + { + return [ + 'by_query' => [true], + 'by_body' => [false], + ]; } - public function testDeleteTagByLabel() + /** + * @dataProvider dataForDeletingTagByLabel + */ + public function testDeleteTagByLabel($useQueryString) { $em = $this->client->getContainer()->get('doctrine.orm.entity_manager'); $entry = $this->client->getContainer() @@ -73,7 +83,11 @@ class TagRestControllerTest extends WallabagApiTestCase $em->persist($entry); $em->flush(); - $this->client->request('DELETE', '/api/tag/label.json', ['tag' => $tag->getLabel()]); + if ($useQueryString) { + $this->client->request('DELETE', '/api/tag/label.json?tag='.$tag->getLabel()); + } else { + $this->client->request('DELETE', '/api/tag/label.json', ['tag' => $tag->getLabel()]); + } $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); @@ -98,7 +112,10 @@ class TagRestControllerTest extends WallabagApiTestCase $this->assertEquals(404, $this->client->getResponse()->getStatusCode()); } - public function testDeleteTagsByLabel() + /** + * @dataProvider dataForDeletingTagByLabel + */ + public function testDeleteTagsByLabel($useQueryString) { $em = $this->client->getContainer()->get('doctrine.orm.entity_manager'); $entry = $this->client->getContainer() @@ -122,7 +139,11 @@ class TagRestControllerTest extends WallabagApiTestCase $em->persist($entry); $em->flush(); - $this->client->request('DELETE', '/api/tags/label.json', ['tags' => $tag->getLabel().','.$tag2->getLabel()]); + if ($useQueryString) { + $this->client->request('DELETE', '/api/tags/label.json?tags='.$tag->getLabel().','.$tag2->getLabel()); + } else { + $this->client->request('DELETE', '/api/tags/label.json', ['tags' => $tag->getLabel().','.$tag2->getLabel()]); + } $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); diff --git a/tests/Wallabag/ApiBundle/Controller/UserRestControllerTest.php b/tests/Wallabag/ApiBundle/Controller/UserRestControllerTest.php new file mode 100644 index 00000000..4e65f130 --- /dev/null +++ b/tests/Wallabag/ApiBundle/Controller/UserRestControllerTest.php @@ -0,0 +1,185 @@ +client->request('GET', '/api/user.json'); + $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + + $content = json_decode($this->client->getResponse()->getContent(), true); + + $this->assertArrayHasKey('id', $content); + $this->assertArrayHasKey('email', $content); + $this->assertArrayHasKey('name', $content); + $this->assertArrayHasKey('username', $content); + $this->assertArrayHasKey('created_at', $content); + $this->assertArrayHasKey('updated_at', $content); + + $this->assertEquals('bigboss@wallabag.org', $content['email']); + $this->assertEquals('Big boss', $content['name']); + $this->assertEquals('admin', $content['username']); + + $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-Type')); + } + + public function testGetUserWithoutAuthentication() + { + $client = static::createClient(); + $client->request('GET', '/api/user.json'); + $this->assertEquals(401, $client->getResponse()->getStatusCode()); + + $content = json_decode($client->getResponse()->getContent(), true); + + $this->assertArrayHasKey('error', $content); + $this->assertArrayHasKey('error_description', $content); + + $this->assertEquals('access_denied', $content['error']); + + $this->assertEquals('application/json', $client->getResponse()->headers->get('Content-Type')); + } + + public function testCreateNewUser() + { + $this->client->getContainer()->get('craue_config')->set('api_user_registration', 1); + $this->client->request('PUT', '/api/user.json', [ + 'username' => 'google', + 'password' => 'googlegoogle', + 'email' => 'wallabag@google.com', + ]); + + $this->assertEquals(201, $this->client->getResponse()->getStatusCode()); + + $content = json_decode($this->client->getResponse()->getContent(), true); + + $this->assertArrayHasKey('id', $content); + $this->assertArrayHasKey('email', $content); + $this->assertArrayHasKey('username', $content); + $this->assertArrayHasKey('created_at', $content); + $this->assertArrayHasKey('updated_at', $content); + $this->assertArrayHasKey('default_client', $content); + + $this->assertEquals('wallabag@google.com', $content['email']); + $this->assertEquals('google', $content['username']); + + $this->assertArrayHasKey('client_secret', $content['default_client']); + $this->assertArrayHasKey('client_id', $content['default_client']); + + $this->assertEquals('Default client', $content['default_client']['name']); + + $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-Type')); + + $this->client->getContainer()->get('craue_config')->set('api_user_registration', 0); + } + + public function testCreateNewUserWithoutAuthentication() + { + // create a new client instead of using $this->client to be sure client isn't authenticated + $client = static::createClient(); + $client->getContainer()->get('craue_config')->set('api_user_registration', 1); + $client->request('PUT', '/api/user.json', [ + 'username' => 'google', + 'password' => 'googlegoogle', + 'email' => 'wallabag@google.com', + 'client_name' => 'My client name !!', + ]); + + $this->assertEquals(201, $client->getResponse()->getStatusCode()); + + $content = json_decode($client->getResponse()->getContent(), true); + + $this->assertArrayHasKey('id', $content); + $this->assertArrayHasKey('email', $content); + $this->assertArrayHasKey('username', $content); + $this->assertArrayHasKey('created_at', $content); + $this->assertArrayHasKey('updated_at', $content); + $this->assertArrayHasKey('default_client', $content); + + $this->assertEquals('wallabag@google.com', $content['email']); + $this->assertEquals('google', $content['username']); + + $this->assertArrayHasKey('client_secret', $content['default_client']); + $this->assertArrayHasKey('client_id', $content['default_client']); + + $this->assertEquals('My client name !!', $content['default_client']['name']); + + $this->assertEquals('application/json', $client->getResponse()->headers->get('Content-Type')); + + $client->getContainer()->get('craue_config')->set('api_user_registration', 0); + } + + public function testCreateNewUserWithExistingEmail() + { + $client = static::createClient(); + $client->getContainer()->get('craue_config')->set('api_user_registration', 1); + $client->request('PUT', '/api/user.json', [ + 'username' => 'admin', + 'password' => 'googlegoogle', + 'email' => 'bigboss@wallabag.org', + ]); + + $this->assertEquals(400, $client->getResponse()->getStatusCode()); + + $content = json_decode($client->getResponse()->getContent(), true); + + $this->assertArrayHasKey('error', $content); + $this->assertArrayHasKey('username', $content['error']); + $this->assertArrayHasKey('email', $content['error']); + + // $this->assertEquals('fos_user.username.already_used', $content['error']['username'][0]); + // $this->assertEquals('fos_user.email.already_used', $content['error']['email'][0]); + // This shouldn't be translated ... + $this->assertEquals('This value is already used.', $content['error']['username'][0]); + $this->assertEquals('This value is already used.', $content['error']['email'][0]); + + $this->assertEquals('application/json', $client->getResponse()->headers->get('Content-Type')); + + $client->getContainer()->get('craue_config')->set('api_user_registration', 0); + } + + public function testCreateNewUserWithTooShortPassword() + { + $client = static::createClient(); + $client->getContainer()->get('craue_config')->set('api_user_registration', 1); + $client->request('PUT', '/api/user.json', [ + 'username' => 'facebook', + 'password' => 'face', + 'email' => 'facebook@wallabag.org', + ]); + + $this->assertEquals(400, $client->getResponse()->getStatusCode()); + + $content = json_decode($client->getResponse()->getContent(), true); + + $this->assertArrayHasKey('error', $content); + $this->assertArrayHasKey('password', $content['error']); + + $this->assertEquals('validator.password_too_short', $content['error']['password'][0]); + + $this->assertEquals('application/json', $client->getResponse()->headers->get('Content-Type')); + + $client->getContainer()->get('craue_config')->set('api_user_registration', 0); + } + + public function testCreateNewUserWhenRegistrationIsDisabled() + { + $client = static::createClient(); + $client->request('PUT', '/api/user.json', [ + 'username' => 'facebook', + 'password' => 'face', + 'email' => 'facebook@wallabag.org', + ]); + + $this->assertEquals(403, $client->getResponse()->getStatusCode()); + + $content = json_decode($client->getResponse()->getContent(), true); + + $this->assertArrayHasKey('error', $content); + + $this->assertEquals('application/json', $client->getResponse()->headers->get('Content-Type')); + } +} diff --git a/tests/Wallabag/ApiBundle/Controller/WallabagRestControllerTest.php b/tests/Wallabag/ApiBundle/Controller/WallabagRestControllerTest.php index c87e58de..df638e8f 100644 --- a/tests/Wallabag/ApiBundle/Controller/WallabagRestControllerTest.php +++ b/tests/Wallabag/ApiBundle/Controller/WallabagRestControllerTest.php @@ -8,12 +8,14 @@ class WallabagRestControllerTest extends WallabagApiTestCase { public function testGetVersion() { - $this->client->request('GET', '/api/version'); + // create a new client instead of using $this->client to be sure client isn't authenticated + $client = static::createClient(); + $client->request('GET', '/api/version'); - $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); + $this->assertEquals(200, $client->getResponse()->getStatusCode()); - $content = json_decode($this->client->getResponse()->getContent(), true); + $content = json_decode($client->getResponse()->getContent(), true); - $this->assertEquals($this->client->getContainer()->getParameter('wallabag_core.version'), $content); + $this->assertEquals($client->getContainer()->getParameter('wallabag_core.version'), $content); } } diff --git a/tests/Wallabag/ApiBundle/WallabagApiTestCase.php b/tests/Wallabag/ApiBundle/WallabagApiTestCase.php index cf9b3347..8709da70 100644 --- a/tests/Wallabag/ApiBundle/WallabagApiTestCase.php +++ b/tests/Wallabag/ApiBundle/WallabagApiTestCase.php @@ -8,7 +8,7 @@ use Symfony\Component\BrowserKit\Cookie; abstract class WallabagApiTestCase extends WebTestCase { /** - * @var Client + * @var \Symfony\Bundle\FrameworkBundle\Client */ protected $client = null; @@ -23,7 +23,7 @@ abstract class WallabagApiTestCase extends WebTestCase } /** - * @return Client + * @return \Symfony\Bundle\FrameworkBundle\Client */ protected function createAuthorizedClient() { @@ -37,7 +37,7 @@ abstract class WallabagApiTestCase extends WebTestCase $firewallName = $container->getParameter('fos_user.firewall_name'); $this->user = $userManager->findUserBy(['username' => 'admin']); - $loginManager->loginUser($firewallName, $this->user); + $loginManager->logInUser($firewallName, $this->user); // save the login token into the session and put it in a cookie $container->get('session')->set('_security_'.$firewallName, serialize($container->get('security.token_storage')->getToken())); diff --git a/tests/Wallabag/CoreBundle/Command/CleanDuplicatesCommandTest.php b/tests/Wallabag/CoreBundle/Command/CleanDuplicatesCommandTest.php new file mode 100644 index 00000000..e6e57f30 --- /dev/null +++ b/tests/Wallabag/CoreBundle/Command/CleanDuplicatesCommandTest.php @@ -0,0 +1,108 @@ +getClient()->getKernel()); + $application->add(new CleanDuplicatesCommand()); + + $command = $application->find('wallabag:clean-duplicates'); + + $tester = new CommandTester($command); + $tester->execute([ + 'command' => $command->getName(), + ]); + + $this->assertContains('Cleaning through 3 user accounts', $tester->getDisplay()); + $this->assertContains('Finished cleaning. 0 duplicates found in total', $tester->getDisplay()); + } + + public function testRunCleanDuplicatesCommandWithBadUsername() + { + $application = new Application($this->getClient()->getKernel()); + $application->add(new CleanDuplicatesCommand()); + + $command = $application->find('wallabag:clean-duplicates'); + + $tester = new CommandTester($command); + $tester->execute([ + 'command' => $command->getName(), + 'username' => 'unknown', + ]); + + $this->assertContains('User "unknown" not found', $tester->getDisplay()); + } + + public function testRunCleanDuplicatesCommandForUser() + { + $application = new Application($this->getClient()->getKernel()); + $application->add(new CleanDuplicatesCommand()); + + $command = $application->find('wallabag:clean-duplicates'); + + $tester = new CommandTester($command); + $tester->execute([ + 'command' => $command->getName(), + 'username' => 'admin', + ]); + + $this->assertContains('Cleaned 0 duplicates for user admin', $tester->getDisplay()); + } + + public function testDuplicate() + { + $url = 'http://www.lemonde.fr/sport/visuel/2017/05/05/rondelle-prison-blanchissage-comprendre-le-hockey-sur-glace_5122587_3242.html'; + $client = $this->getClient(); + $em = $client->getContainer()->get('doctrine.orm.entity_manager'); + + $this->logInAs('admin'); + + $nbEntries = $em->getRepository('WallabagCoreBundle:Entry')->findAllByUrlAndUserId($url, $this->getLoggedInUserId()); + $this->assertCount(0, $nbEntries); + + $user = $em->getRepository('WallabagUserBundle:User')->findOneById($this->getLoggedInUserId()); + + $entry1 = new Entry($user); + $entry1->setUrl($url); + + $entry2 = new Entry($user); + $entry2->setUrl($url); + + $em->persist($entry1); + $em->persist($entry2); + + $em->flush(); + + $nbEntries = $em->getRepository('WallabagCoreBundle:Entry')->findAllByUrlAndUserId($url, $this->getLoggedInUserId()); + $this->assertCount(2, $nbEntries); + + $application = new Application($this->getClient()->getKernel()); + $application->add(new CleanDuplicatesCommand()); + + $command = $application->find('wallabag:clean-duplicates'); + + $tester = new CommandTester($command); + $tester->execute([ + 'command' => $command->getName(), + 'username' => 'admin', + ]); + + $this->assertContains('Cleaned 1 duplicates for user admin', $tester->getDisplay()); + + $nbEntries = $em->getRepository('WallabagCoreBundle:Entry')->findAllByUrlAndUserId($url, $this->getLoggedInUserId()); + $this->assertCount(1, $nbEntries); + + $query = $em->createQuery('DELETE FROM Wallabag\CoreBundle\Entity\Entry e WHERE e.url = :url'); + $query->setParameter('url', $url); + $query->execute(); + } +} diff --git a/tests/Wallabag/CoreBundle/Command/ExportCommandTest.php b/tests/Wallabag/CoreBundle/Command/ExportCommandTest.php index 6798c5d7..2eebf39b 100644 --- a/tests/Wallabag/CoreBundle/Command/ExportCommandTest.php +++ b/tests/Wallabag/CoreBundle/Command/ExportCommandTest.php @@ -10,7 +10,7 @@ use Tests\Wallabag\CoreBundle\WallabagCoreTestCase; class ExportCommandTest extends WallabagCoreTestCase { /** - * @expectedException Symfony\Component\Console\Exception\RuntimeException + * @expectedException \Symfony\Component\Console\Exception\RuntimeException * @expectedExceptionMessage Not enough arguments (missing: "username") */ public function testExportCommandWithoutUsername() @@ -55,7 +55,7 @@ class ExportCommandTest extends WallabagCoreTestCase 'username' => 'admin', ]); - $this->assertContains('Exporting 6 entrie(s) for user « admin »... Done', $tester->getDisplay()); + $this->assertContains('Exporting 5 entrie(s) for user « admin »... Done', $tester->getDisplay()); $this->assertFileExists('admin-export.json'); } @@ -70,7 +70,7 @@ class ExportCommandTest extends WallabagCoreTestCase $tester->execute([ 'command' => $command->getName(), 'username' => 'admin', - 'filepath' => 'specialexport.json' + 'filepath' => 'specialexport.json', ]); $this->assertFileExists('specialexport.json'); diff --git a/tests/Wallabag/CoreBundle/Command/InstallCommandTest.php b/tests/Wallabag/CoreBundle/Command/InstallCommandTest.php index 1bfd41d5..94fc0b94 100644 --- a/tests/Wallabag/CoreBundle/Command/InstallCommandTest.php +++ b/tests/Wallabag/CoreBundle/Command/InstallCommandTest.php @@ -2,8 +2,11 @@ namespace Tests\Wallabag\CoreBundle\Command; +use DAMA\DoctrineTestBundle\Doctrine\DBAL\StaticDriver; use Doctrine\Bundle\DoctrineBundle\Command\CreateDatabaseDoctrineCommand; use Doctrine\Bundle\DoctrineBundle\Command\DropDatabaseDoctrineCommand; +use Doctrine\DBAL\Platforms\PostgreSqlPlatform; +use Doctrine\DBAL\Platforms\SqlitePlatform; use Symfony\Bundle\FrameworkBundle\Console\Application; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Output\NullOutput; @@ -18,7 +21,9 @@ class InstallCommandTest extends WallabagCoreTestCase { parent::setUp(); - if ($this->getClient()->getContainer()->get('doctrine')->getConnection()->getDriver() instanceof \Doctrine\DBAL\Driver\PDOPgSql\Driver) { + /** @var \Doctrine\DBAL\Connection $connection */ + $connection = $this->getClient()->getContainer()->get('doctrine')->getConnection(); + if ($connection->getDatabasePlatform() instanceof PostgreSqlPlatform) { /* * LOG: statement: CREATE DATABASE "wallabag" * ERROR: source database "template1" is being accessed by other users @@ -30,34 +35,44 @@ class InstallCommandTest extends WallabagCoreTestCase */ $this->markTestSkipped('PostgreSQL spotted: can\'t find a good way to drop current database, skipping.'); } - } - /** - * Ensure next tests will have a clean database. - */ - public static function tearDownAfterClass() - { - $application = new Application(static::$kernel); - $application->setAutoExit(false); + if ($connection->getDatabasePlatform() instanceof SqlitePlatform) { + // Environnement variable useful only for sqlite to avoid the error "attempt to write a readonly database" + // We can't define always this environnement variable because pdo_mysql seems to use it + // and we have the error: + // SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; + // check the manual that corresponds to your MariaDB server version for the right syntax to use + // near '/tmp/wallabag_testTYj1kp' at line 1 + $databasePath = tempnam(sys_get_temp_dir(), 'wallabag_test'); + putenv("TEST_DATABASE_PATH=$databasePath"); + + // The environnement has been changed, recreate the client in order to update connection + parent::setUp(); + } - $application->run(new ArrayInput([ - 'command' => 'doctrine:schema:drop', - '--no-interaction' => true, - '--force' => true, - '--env' => 'test', - ]), new NullOutput()); + // disable doctrine-test-bundle + StaticDriver::setKeepStaticConnections(false); - $application->run(new ArrayInput([ - 'command' => 'doctrine:schema:create', - '--no-interaction' => true, - '--env' => 'test', - ]), new NullOutput()); + $this->resetDatabase($this->getClient()); + } - $application->run(new ArrayInput([ - 'command' => 'doctrine:fixtures:load', - '--no-interaction' => true, - '--env' => 'test', - ]), new NullOutput()); + public function tearDown() + { + $databasePath = getenv('TEST_DATABASE_PATH'); + // Remove variable environnement + putenv('TEST_DATABASE_PATH'); + if ($databasePath && file_exists($databasePath)) { + unlink($databasePath); + } else { + // Create a new client to avoid the error: + // Transaction commit failed because the transaction has been marked for rollback only. + $client = static::createClient(); + $this->resetDatabase($client); + } + + // enable doctrine-test-bundle + StaticDriver::setKeepStaticConnections(true); + parent::tearDown(); } public function testRunInstallCommand() @@ -67,18 +82,14 @@ class InstallCommandTest extends WallabagCoreTestCase $command = $application->find('wallabag:install'); - // We mock the QuestionHelper - $question = $this->getMockBuilder('Symfony\Component\Console\Helper\QuestionHelper') - ->disableOriginalConstructor() - ->getMock(); - $question->expects($this->any()) - ->method('ask') - ->will($this->returnValue('yes_'.uniqid('', true))); - - // We override the standard helper with our mock - $command->getHelperSet()->set($question, 'question'); - $tester = new CommandTester($command); + $tester->setInputs([ + 'y', // dropping database + 'y', // create super admin + 'username_'.uniqid('', true), // username + 'password_'.uniqid('', true), // password + 'email_'.uniqid('', true).'@wallabag.it', // email + ]); $tester->execute([ 'command' => $command->getName(), ]); @@ -87,6 +98,7 @@ class InstallCommandTest extends WallabagCoreTestCase $this->assertContains('Setting up database.', $tester->getDisplay()); $this->assertContains('Administration setup.', $tester->getDisplay()); $this->assertContains('Config setup.', $tester->getDisplay()); + $this->assertContains('Run migrations.', $tester->getDisplay()); } public function testRunInstallCommandWithReset() @@ -96,18 +108,13 @@ class InstallCommandTest extends WallabagCoreTestCase $command = $application->find('wallabag:install'); - // We mock the QuestionHelper - $question = $this->getMockBuilder('Symfony\Component\Console\Helper\QuestionHelper') - ->disableOriginalConstructor() - ->getMock(); - $question->expects($this->any()) - ->method('ask') - ->will($this->returnValue('yes_'.uniqid('', true))); - - // We override the standard helper with our mock - $command->getHelperSet()->set($question, 'question'); - $tester = new CommandTester($command); + $tester->setInputs([ + 'y', // create super admin + 'username_'.uniqid('', true), // username + 'password_'.uniqid('', true), // password + 'email_'.uniqid('', true).'@wallabag.it', // email + ]); $tester->execute([ 'command' => $command->getName(), '--reset' => true, @@ -115,19 +122,20 @@ class InstallCommandTest extends WallabagCoreTestCase $this->assertContains('Checking system requirements.', $tester->getDisplay()); $this->assertContains('Setting up database.', $tester->getDisplay()); - $this->assertContains('Droping database, creating database and schema, clearing the cache', $tester->getDisplay()); + $this->assertContains('Dropping database, creating database and schema, clearing the cache', $tester->getDisplay()); $this->assertContains('Administration setup.', $tester->getDisplay()); $this->assertContains('Config setup.', $tester->getDisplay()); + $this->assertContains('Run migrations.', $tester->getDisplay()); // we force to reset everything - $this->assertContains('Droping database, creating database and schema, clearing the cache', $tester->getDisplay()); + $this->assertContains('Dropping database, creating database and schema, clearing the cache', $tester->getDisplay()); } public function testRunInstallCommandWithDatabaseRemoved() { // skipped SQLite check when database is removed because while testing for the connection, // the driver will create the file (so the database) before testing if database exist - if ($this->getClient()->getContainer()->get('doctrine')->getConnection()->getDriver() instanceof \Doctrine\DBAL\Driver\PDOSqlite\Driver) { + if ($this->getClient()->getContainer()->get('doctrine')->getConnection()->getDatabasePlatform() instanceof \Doctrine\DBAL\Platforms\SqlitePlatform) { $this->markTestSkipped('SQLite spotted: can\'t test with database removed.'); } @@ -148,18 +156,13 @@ class InstallCommandTest extends WallabagCoreTestCase $command = $application->find('wallabag:install'); - // We mock the QuestionHelper - $question = $this->getMockBuilder('Symfony\Component\Console\Helper\QuestionHelper') - ->disableOriginalConstructor() - ->getMock(); - $question->expects($this->any()) - ->method('ask') - ->will($this->returnValue('yes_'.uniqid('', true))); - - // We override the standard helper with our mock - $command->getHelperSet()->set($question, 'question'); - $tester = new CommandTester($command); + $tester->setInputs([ + 'y', // create super admin + 'username_'.uniqid('', true), // username + 'password_'.uniqid('', true), // password + 'email_'.uniqid('', true).'@wallabag.it', // email + ]); $tester->execute([ 'command' => $command->getName(), ]); @@ -168,6 +171,7 @@ class InstallCommandTest extends WallabagCoreTestCase $this->assertContains('Setting up database.', $tester->getDisplay()); $this->assertContains('Administration setup.', $tester->getDisplay()); $this->assertContains('Config setup.', $tester->getDisplay()); + $this->assertContains('Run migrations.', $tester->getDisplay()); // the current database doesn't already exist $this->assertContains('Creating database and schema, clearing the cache', $tester->getDisplay()); @@ -180,23 +184,12 @@ class InstallCommandTest extends WallabagCoreTestCase $command = $application->find('wallabag:install'); - // We mock the QuestionHelper - $question = $this->getMockBuilder('Symfony\Component\Console\Helper\QuestionHelper') - ->disableOriginalConstructor() - ->getMock(); - - $question->expects($this->exactly(3)) - ->method('ask') - ->will($this->onConsecutiveCalls( - false, // don't want to reset the entire database - true, // do want to reset the schema - false // don't want to create a new user - )); - - // We override the standard helper with our mock - $command->getHelperSet()->set($question, 'question'); - $tester = new CommandTester($command); + $tester->setInputs([ + 'n', // don't want to reset the entire database + 'y', // do want to reset the schema + 'n', // don't want to create a new user + ]); $tester->execute([ 'command' => $command->getName(), ]); @@ -205,8 +198,9 @@ class InstallCommandTest extends WallabagCoreTestCase $this->assertContains('Setting up database.', $tester->getDisplay()); $this->assertContains('Administration setup.', $tester->getDisplay()); $this->assertContains('Config setup.', $tester->getDisplay()); + $this->assertContains('Run migrations.', $tester->getDisplay()); - $this->assertContains('Droping schema and creating schema', $tester->getDisplay()); + $this->assertContains('Dropping schema and creating schema', $tester->getDisplay()); } public function testRunInstallCommandChooseNothing() @@ -235,22 +229,11 @@ class InstallCommandTest extends WallabagCoreTestCase $command = $application->find('wallabag:install'); - // We mock the QuestionHelper - $question = $this->getMockBuilder('Symfony\Component\Console\Helper\QuestionHelper') - ->disableOriginalConstructor() - ->getMock(); - - $question->expects($this->exactly(2)) - ->method('ask') - ->will($this->onConsecutiveCalls( - false, // don't want to reset the entire database - false // don't want to create a new user - )); - - // We override the standard helper with our mock - $command->getHelperSet()->set($question, 'question'); - $tester = new CommandTester($command); + $tester->setInputs([ + 'n', // don't want to reset the entire database + 'n', // don't want to create a new user + ]); $tester->execute([ 'command' => $command->getName(), ]); @@ -259,6 +242,7 @@ class InstallCommandTest extends WallabagCoreTestCase $this->assertContains('Setting up database.', $tester->getDisplay()); $this->assertContains('Administration setup.', $tester->getDisplay()); $this->assertContains('Config setup.', $tester->getDisplay()); + $this->assertContains('Run migrations.', $tester->getDisplay()); $this->assertContains('Creating schema', $tester->getDisplay()); } @@ -270,26 +254,17 @@ class InstallCommandTest extends WallabagCoreTestCase $command = $application->find('wallabag:install'); - // We mock the QuestionHelper - $question = $this->getMockBuilder('Symfony\Component\Console\Helper\QuestionHelper') - ->disableOriginalConstructor() - ->getMock(); - $question->expects($this->any()) - ->method('ask') - ->will($this->returnValue('yes_'.uniqid('', true))); - - // We override the standard helper with our mock - $command->getHelperSet()->set($question, 'question'); - $tester = new CommandTester($command); $tester->execute([ 'command' => $command->getName(), - '--no-interaction' => true, + ], [ + 'interactive' => false, ]); $this->assertContains('Checking system requirements.', $tester->getDisplay()); $this->assertContains('Setting up database.', $tester->getDisplay()); $this->assertContains('Administration setup.', $tester->getDisplay()); $this->assertContains('Config setup.', $tester->getDisplay()); + $this->assertContains('Run migrations.', $tester->getDisplay()); } } diff --git a/tests/Wallabag/CoreBundle/Command/ShowUserCommandTest.php b/tests/Wallabag/CoreBundle/Command/ShowUserCommandTest.php new file mode 100644 index 00000000..c0a4acfa --- /dev/null +++ b/tests/Wallabag/CoreBundle/Command/ShowUserCommandTest.php @@ -0,0 +1,93 @@ +getClient()->getKernel()); + $application->add(new ShowUserCommand()); + + $command = $application->find('wallabag:user:show'); + + $tester = new CommandTester($command); + $tester->execute([ + 'command' => $command->getName(), + ]); + } + + public function testRunShowUserCommandWithBadUsername() + { + $application = new Application($this->getClient()->getKernel()); + $application->add(new ShowUserCommand()); + + $command = $application->find('wallabag:user:show'); + + $tester = new CommandTester($command); + $tester->execute([ + 'command' => $command->getName(), + 'username' => 'unknown', + ]); + + $this->assertContains('User "unknown" not found', $tester->getDisplay()); + } + + public function testRunShowUserCommandForUser() + { + $application = new Application($this->getClient()->getKernel()); + $application->add(new ShowUserCommand()); + + $command = $application->find('wallabag:user:show'); + + $tester = new CommandTester($command); + $tester->execute([ + 'command' => $command->getName(), + 'username' => 'admin', + ]); + + $this->assertContains('Username : admin', $tester->getDisplay()); + $this->assertContains('Email : bigboss@wallabag.org', $tester->getDisplay()); + $this->assertContains('Display name : Big boss', $tester->getDisplay()); + $this->assertContains('2FA activated: no', $tester->getDisplay()); + } + + public function testShowUser() + { + $client = $this->getClient(); + $em = $client->getContainer()->get('doctrine.orm.entity_manager'); + + $this->logInAs('admin'); + + /** @var User $user */ + $user = $em->getRepository('WallabagUserBundle:User')->findOneById($this->getLoggedInUserId()); + + $user->setName('Bug boss'); + $em->persist($user); + + $em->flush(); + + $application = new Application($this->getClient()->getKernel()); + $application->add(new ShowUserCommand()); + + $command = $application->find('wallabag:user:show'); + + $tester = new CommandTester($command); + $tester->execute([ + 'command' => $command->getName(), + 'username' => 'admin', + ]); + + $this->assertContains('Display name : Bug boss', $tester->getDisplay()); + } +} diff --git a/tests/Wallabag/CoreBundle/Command/TagAllCommandTest.php b/tests/Wallabag/CoreBundle/Command/TagAllCommandTest.php index ec31708f..4cde3679 100644 --- a/tests/Wallabag/CoreBundle/Command/TagAllCommandTest.php +++ b/tests/Wallabag/CoreBundle/Command/TagAllCommandTest.php @@ -10,7 +10,7 @@ use Tests\Wallabag\CoreBundle\WallabagCoreTestCase; class TagAllCommandTest extends WallabagCoreTestCase { /** - * @expectedException Symfony\Component\Console\Exception\RuntimeException + * @expectedException \Symfony\Component\Console\Exception\RuntimeException * @expectedExceptionMessage Not enough arguments (missing: "username") */ public function testRunTagAllCommandWithoutUsername() diff --git a/tests/Wallabag/CoreBundle/Controller/ConfigControllerTest.php b/tests/Wallabag/CoreBundle/Controller/ConfigControllerTest.php index beb0598a..5bc815ee 100644 --- a/tests/Wallabag/CoreBundle/Controller/ConfigControllerTest.php +++ b/tests/Wallabag/CoreBundle/Controller/ConfigControllerTest.php @@ -67,8 +67,17 @@ class ConfigControllerTest extends WallabagCoreTestCase public function testChangeReadingSpeed() { $this->logInAs('admin'); + $this->useTheme('baggy'); $client = $this->getClient(); + $entry = new Entry($this->getLoggedInUser()); + $entry->setUrl('http://0.0.0.0/test-entry1') + ->setReadingTime(22); + $this->getEntityManager()->persist($entry); + + $this->getEntityManager()->flush(); + $this->getEntityManager()->clear(); + $crawler = $client->request('GET', '/unread/list'); $form = $crawler->filter('button[id=submit-filter]')->form(); $dataFilters = [ @@ -409,6 +418,7 @@ class ConfigControllerTest extends WallabagCoreTestCase public function testTaggingRuleCreation() { $this->logInAs('admin'); + $this->useTheme('baggy'); $client = $this->getClient(); $crawler = $client->request('GET', '/config'); @@ -798,11 +808,87 @@ class ConfigControllerTest extends WallabagCoreTestCase $entryReset = $em ->getRepository('WallabagCoreBundle:Entry') - ->countAllEntriesByUsername($user->getId()); + ->countAllEntriesByUser($user->getId()); $this->assertEquals(0, $entryReset, 'Entries were reset'); } + public function testResetArchivedEntries() + { + $this->logInAs('empty'); + $client = $this->getClient(); + + $em = $client->getContainer()->get('doctrine.orm.entity_manager'); + + $user = static::$kernel->getContainer()->get('security.token_storage')->getToken()->getUser(); + + $tag = new Tag(); + $tag->setLabel('super'); + $em->persist($tag); + + $entry = new Entry($user); + $entry->setUrl('http://www.lemonde.fr/europe/article/2016/10/01/pour-le-psoe-chaque-election-s-est-transformee-en-une-agonie_5006476_3214.html'); + $entry->setContent('Youhou'); + $entry->setTitle('Youhou'); + $entry->addTag($tag); + $em->persist($entry); + + $annotation = new Annotation($user); + $annotation->setText('annotated'); + $annotation->setQuote('annotated'); + $annotation->setRanges([]); + $annotation->setEntry($entry); + $em->persist($annotation); + + $tagArchived = new Tag(); + $tagArchived->setLabel('super'); + $em->persist($tagArchived); + + $entryArchived = new Entry($user); + $entryArchived->setUrl('http://www.lemonde.fr/europe/article/2016/10/01/pour-le-psoe-chaque-election-s-est-transformee-en-une-agonie_5006476_3214.html'); + $entryArchived->setContent('Youhou'); + $entryArchived->setTitle('Youhou'); + $entryArchived->addTag($tagArchived); + $entryArchived->setArchived(true); + $em->persist($entryArchived); + + $annotationArchived = new Annotation($user); + $annotationArchived->setText('annotated'); + $annotationArchived->setQuote('annotated'); + $annotationArchived->setRanges([]); + $annotationArchived->setEntry($entryArchived); + $em->persist($annotationArchived); + + $em->flush(); + + $crawler = $client->request('GET', '/config#set3'); + + $this->assertEquals(200, $client->getResponse()->getStatusCode()); + + $crawler = $client->click($crawler->selectLink('config.reset.archived')->link()); + + $this->assertEquals(302, $client->getResponse()->getStatusCode()); + $this->assertContains('flashes.config.notice.archived_reset', $client->getContainer()->get('session')->getFlashBag()->get('notice')[0]); + + $entryReset = $em + ->getRepository('WallabagCoreBundle:Entry') + ->countAllEntriesByUser($user->getId()); + + $this->assertEquals(1, $entryReset, 'Entries were reset'); + + $tagReset = $em + ->getRepository('WallabagCoreBundle:Tag') + ->countAllTags($user->getId()); + + $this->assertEquals(1, $tagReset, 'Tags were reset'); + + $annotationsReset = $em + ->getRepository('WallabagAnnotationBundle:Annotation') + ->findAnnotationsByPageId($annotationArchived->getId(), $user->getId()); + + $this->assertEmpty($annotationsReset, 'Annotations were reset'); + } + public function testResetEntriesCascade() { $this->logInAs('empty'); @@ -843,7 +929,7 @@ class ConfigControllerTest extends WallabagCoreTestCase $entryReset = $em ->getRepository('WallabagCoreBundle:Entry') - ->countAllEntriesByUsername($user->getId()); + ->countAllEntriesByUser($user->getId()); $this->assertEquals(0, $entryReset, 'Entries were reset'); @@ -863,6 +949,7 @@ class ConfigControllerTest extends WallabagCoreTestCase public function testSwitchViewMode() { $this->logInAs('admin'); + $this->useTheme('baggy'); $client = $this->getClient(); $client->request('GET', '/unread/list'); diff --git a/tests/Wallabag/CoreBundle/Controller/EntryControllerTest.php b/tests/Wallabag/CoreBundle/Controller/EntryControllerTest.php index 7db4cf1f..7cf28bfe 100644 --- a/tests/Wallabag/CoreBundle/Controller/EntryControllerTest.php +++ b/tests/Wallabag/CoreBundle/Controller/EntryControllerTest.php @@ -5,11 +5,28 @@ namespace Tests\Wallabag\CoreBundle\Controller; use Tests\Wallabag\CoreBundle\WallabagCoreTestCase; use Wallabag\CoreBundle\Entity\Config; use Wallabag\CoreBundle\Entity\Entry; +use Wallabag\CoreBundle\Entity\SiteCredential; class EntryControllerTest extends WallabagCoreTestCase { + public $downloadImagesEnabled = false; public $url = 'http://www.lemonde.fr/pixels/article/2015/03/28/plongee-dans-l-univers-d-ingress-le-jeu-de-google-aux-frontieres-du-reel_4601155_4408996.html'; + /** + * @after + * + * Ensure download_images_enabled is disabled after each script + */ + public function tearDownImagesEnabled() + { + if ($this->downloadImagesEnabled) { + $client = static::createClient(); + $client->getContainer()->get('craue_config')->set('download_images_enabled', 0); + + $this->downloadImagesEnabled = false; + } + } + public function testLogin() { $client = $this->getClient(); @@ -55,6 +72,7 @@ class EntryControllerTest extends WallabagCoreTestCase public function testGetNew() { $this->logInAs('admin'); + $this->useTheme('baggy'); $client = $this->getClient(); $crawler = $client->request('GET', '/new'); @@ -68,6 +86,7 @@ class EntryControllerTest extends WallabagCoreTestCase public function testPostNewViaBookmarklet() { $this->logInAs('admin'); + $this->useTheme('baggy'); $client = $this->getClient(); $crawler = $client->request('GET', '/'); @@ -135,14 +154,58 @@ class EntryControllerTest extends WallabagCoreTestCase ->getRepository('WallabagCoreBundle:Entry') ->findByUrlAndUserId($this->url, $this->getLoggedInUserId()); + $author = $content->getPublishedBy(); + $this->assertInstanceOf('Wallabag\CoreBundle\Entity\Entry', $content); $this->assertEquals($this->url, $content->getUrl()); $this->assertContains('Google', $content->getTitle()); + $this->assertEquals('fr', $content->getLanguage()); + $this->assertEquals('2015-03-28 15:37:39', $content->getPublishedAt()->format('Y-m-d H:i:s')); + $this->assertEquals('Morgane Tual', $author[0]); + $this->assertArrayHasKey('x-varnish1', $content->getHeaders()); + } + + public function testPostWithMultipleAuthors() + { + $url = 'http://www.liberation.fr/planete/2017/04/05/donald-trump-et-xi-jinping-tentative-de-flirt-en-floride_1560768'; + $this->logInAs('admin'); + $client = $this->getClient(); + + $crawler = $client->request('GET', '/new'); + + $this->assertEquals(200, $client->getResponse()->getStatusCode()); + + $form = $crawler->filter('form[name=entry]')->form(); + + $data = [ + 'entry[url]' => $url, + ]; + + $client->submit($form, $data); + + $this->assertEquals(302, $client->getResponse()->getStatusCode()); + + $content = $client->getContainer() + ->get('doctrine.orm.entity_manager') + ->getRepository('WallabagCoreBundle:Entry') + ->findByUrlAndUserId($url, $this->getLoggedInUserId()); + + $authors = $content->getPublishedBy(); + $this->assertEquals('2017-04-05 19:26:13', $content->getPublishedAt()->format('Y-m-d H:i:s')); + $this->assertEquals('fr', $content->getLanguage()); + $this->assertEquals('Raphaël Balenieri, correspondant à Pékin', $authors[0]); + $this->assertEquals('Frédéric Autran, correspondant à New York', $authors[1]); } public function testPostNewOkUrlExist() { $this->logInAs('admin'); + + $entry = new Entry($this->getLoggedInUser()); + $entry->setUrl($this->url); + $this->getEntityManager()->persist($entry); + $this->getEntityManager()->flush(); + $client = $this->getClient(); $crawler = $client->request('GET', '/new'); @@ -194,15 +257,6 @@ class EntryControllerTest extends WallabagCoreTestCase $this->assertEquals(302, $client->getResponse()->getStatusCode()); $this->assertContains('/view/', $client->getResponse()->getTargetUrl()); - - $em = $client->getContainer() - ->get('doctrine.orm.entity_manager'); - $entry = $em - ->getRepository('WallabagCoreBundle:Entry') - ->findOneByUrl(urldecode($url)); - - $em->remove($entry); - $em->flush(); } /** @@ -235,8 +289,9 @@ class EntryControllerTest extends WallabagCoreTestCase ->findOneByUrl($url); $tags = $entry->getTags(); - $this->assertCount(1, $tags); - $this->assertEquals('wallabag', $tags[0]->getLabel()); + $this->assertCount(2, $tags); + $this->assertContains('wallabag', $tags); + $this->assertEquals('en', $entry->getLanguage()); $em->remove($entry); $em->flush(); @@ -264,8 +319,8 @@ class EntryControllerTest extends WallabagCoreTestCase $tags = $entry->getTags(); - $this->assertCount(1, $tags); - $this->assertEquals('wallabag', $tags[0]->getLabel()); + $this->assertCount(2, $tags); + $this->assertContains('wallabag', $tags); $em->remove($entry); $em->flush(); @@ -312,29 +367,26 @@ class EntryControllerTest extends WallabagCoreTestCase $this->assertEquals('/all/list', $client->getResponse()->getTargetUrl()); } - /** - * @depends testPostNewOk - */ public function testView() { $this->logInAs('admin'); $client = $this->getClient(); - $content = $client->getContainer() - ->get('doctrine.orm.entity_manager') - ->getRepository('WallabagCoreBundle:Entry') - ->findByUrlAndUserId($this->url, $this->getLoggedInUserId()); + $entry = new Entry($this->getLoggedInUser()); + $entry->setUrl('http://example.com/foo'); + $entry->setTitle('title foo'); + $entry->setContent('foo bar baz'); + $this->getEntityManager()->persist($entry); + $this->getEntityManager()->flush(); - $crawler = $client->request('GET', '/view/'.$content->getId()); + $crawler = $client->request('GET', '/view/'.$entry->getId()); $this->assertEquals(200, $client->getResponse()->getStatusCode()); $this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text'])); - $this->assertContains($content->getTitle(), $body[0]); + $this->assertContains($entry->getTitle(), $body[0]); } /** - * @depends testPostNewOk - * * This test will require an internet connection. */ public function testReload() @@ -342,63 +394,45 @@ class EntryControllerTest extends WallabagCoreTestCase $this->logInAs('admin'); $client = $this->getClient(); - $em = $client->getContainer() - ->get('doctrine.orm.entity_manager'); - - $content = $em - ->getRepository('WallabagCoreBundle:Entry') - ->findByUrlAndUserId($this->url, $this->getLoggedInUserId()); + $entry = new Entry($this->getLoggedInUser()); + $entry->setUrl($this->url); + $entry->setTitle('title foo'); + $entry->setContent(''); + $this->getEntityManager()->persist($entry); + $this->getEntityManager()->flush(); + $this->getEntityManager()->clear(); - // empty content - $content->setContent(''); - $em->persist($content); - $em->flush(); - - $client->request('GET', '/reload/'.$content->getId()); + $client->request('GET', '/reload/'.$entry->getId()); $this->assertEquals(302, $client->getResponse()->getStatusCode()); - $content = $em + $entry = $this->getEntityManager() ->getRepository('WallabagCoreBundle:Entry') - ->find($content->getId()); + ->find($entry->getId()); - $this->assertNotEmpty($content->getContent()); + $this->assertNotEmpty($entry->getContent()); } - /** - * @depends testPostNewOk - */ public function testReloadWithFetchingFailed() { $this->logInAs('admin'); $client = $this->getClient(); - $em = $client->getContainer() - ->get('doctrine.orm.entity_manager'); - - $content = $em - ->getRepository('WallabagCoreBundle:Entry') - ->findByUrlAndUserId($this->url, $this->getLoggedInUserId()); - - // put a known failed url - $content->setUrl('http://0.0.0.0/failed.html'); - $em->persist($content); - $em->flush(); + $entry = new Entry($this->getLoggedInUser()); + $entry->setUrl('http://0.0.0.0/failed.html'); + $this->getEntityManager()->persist($entry); + $this->getEntityManager()->flush(); - $client->request('GET', '/reload/'.$content->getId()); + $client->request('GET', '/reload/'.$entry->getId()); $this->assertEquals(302, $client->getResponse()->getStatusCode()); // force EntityManager to clear previous entity // otherwise, retrieve the same entity will retrieve change from the previous request :0 - $em->clear(); - $newContent = $em + $this->getEntityManager()->clear(); + $newContent = $this->getEntityManager() ->getRepository('WallabagCoreBundle:Entry') - ->find($content->getId()); - - $newContent->setUrl($this->url); - $em->persist($newContent); - $em->flush(); + ->find($entry->getId()); $this->assertNotEquals($client->getContainer()->getParameter('wallabag_core.fetching_error_message'), $newContent->getContent()); } @@ -408,12 +442,12 @@ class EntryControllerTest extends WallabagCoreTestCase $this->logInAs('admin'); $client = $this->getClient(); - $content = $client->getContainer() - ->get('doctrine.orm.entity_manager') - ->getRepository('WallabagCoreBundle:Entry') - ->findByUrlAndUserId($this->url, $this->getLoggedInUserId()); + $entry = new Entry($this->getLoggedInUser()); + $entry->setUrl($this->url); + $this->getEntityManager()->persist($entry); + $this->getEntityManager()->flush(); - $crawler = $client->request('GET', '/edit/'.$content->getId()); + $crawler = $client->request('GET', '/edit/'.$entry->getId()); $this->assertEquals(200, $client->getResponse()->getStatusCode()); @@ -426,12 +460,12 @@ class EntryControllerTest extends WallabagCoreTestCase $this->logInAs('admin'); $client = $this->getClient(); - $content = $client->getContainer() - ->get('doctrine.orm.entity_manager') - ->getRepository('WallabagCoreBundle:Entry') - ->findByUrlAndUserId($this->url, $this->getLoggedInUserId()); + $entry = new Entry($this->getLoggedInUser()); + $entry->setUrl($this->url); + $this->getEntityManager()->persist($entry); + $this->getEntityManager()->flush(); - $crawler = $client->request('GET', '/edit/'.$content->getId()); + $crawler = $client->request('GET', '/edit/'.$entry->getId()); $this->assertEquals(200, $client->getResponse()->getStatusCode()); @@ -456,19 +490,20 @@ class EntryControllerTest extends WallabagCoreTestCase $this->logInAs('admin'); $client = $this->getClient(); - $content = $client->getContainer() - ->get('doctrine.orm.entity_manager') - ->getRepository('WallabagCoreBundle:Entry') - ->findByUrlAndUserId($this->url, $this->getLoggedInUserId()); + $entry = new Entry($this->getLoggedInUser()); + $entry->setUrl($this->url); + $this->getEntityManager()->persist($entry); + $this->getEntityManager()->flush(); + $this->getEntityManager()->clear(); - $client->request('GET', '/archive/'.$content->getId()); + $client->request('GET', '/archive/'.$entry->getId()); $this->assertEquals(302, $client->getResponse()->getStatusCode()); $res = $client->getContainer() ->get('doctrine.orm.entity_manager') ->getRepository('WallabagCoreBundle:Entry') - ->find($content->getId()); + ->find($entry->getId()); $this->assertEquals($res->isArchived(), true); } @@ -478,19 +513,20 @@ class EntryControllerTest extends WallabagCoreTestCase $this->logInAs('admin'); $client = $this->getClient(); - $content = $client->getContainer() - ->get('doctrine.orm.entity_manager') - ->getRepository('WallabagCoreBundle:Entry') - ->findByUrlAndUserId($this->url, $this->getLoggedInUserId()); + $entry = new Entry($this->getLoggedInUser()); + $entry->setUrl($this->url); + $this->getEntityManager()->persist($entry); + $this->getEntityManager()->flush(); + $this->getEntityManager()->clear(); - $client->request('GET', '/star/'.$content->getId()); + $client->request('GET', '/star/'.$entry->getId()); $this->assertEquals(302, $client->getResponse()->getStatusCode()); $res = $client->getContainer() ->get('doctrine.orm.entity_manager') ->getRepository('WallabagCoreBundle:Entry') - ->findOneById($content->getId()); + ->findOneById($entry->getId()); $this->assertEquals($res->isStarred(), true); } @@ -500,16 +536,16 @@ class EntryControllerTest extends WallabagCoreTestCase $this->logInAs('admin'); $client = $this->getClient(); - $content = $client->getContainer() - ->get('doctrine.orm.entity_manager') - ->getRepository('WallabagCoreBundle:Entry') - ->findByUrlAndUserId($this->url, $this->getLoggedInUserId()); + $entry = new Entry($this->getLoggedInUser()); + $entry->setUrl($this->url); + $this->getEntityManager()->persist($entry); + $this->getEntityManager()->flush(); - $client->request('GET', '/delete/'.$content->getId()); + $client->request('GET', '/delete/'.$entry->getId()); $this->assertEquals(302, $client->getResponse()->getStatusCode()); - $client->request('GET', '/delete/'.$content->getId()); + $client->request('GET', '/delete/'.$entry->getId()); $this->assertEquals(404, $client->getResponse()->getStatusCode()); } @@ -575,7 +611,13 @@ class EntryControllerTest extends WallabagCoreTestCase public function testFilterOnReadingTime() { $this->logInAs('admin'); + $this->useTheme('baggy'); $client = $this->getClient(); + $entry = new Entry($this->getLoggedInUser()); + $entry->setUrl($this->url); + $entry->setReadingTime(22); + $this->getEntityManager()->persist($entry); + $this->getEntityManager()->flush(); $crawler = $client->request('GET', '/unread/list'); @@ -614,9 +656,20 @@ class EntryControllerTest extends WallabagCoreTestCase public function testFilterOnReadingTimeOnlyUpper() { $this->logInAs('admin'); + $this->useTheme('baggy'); $client = $this->getClient(); - $crawler = $client->request('GET', '/unread/list'); + $crawler = $client->request('GET', '/all/list'); + $this->assertCount(5, $crawler->filter('div[class=entry]')); + + $entry = new Entry($this->getLoggedInUser()); + $entry->setUrl($this->url); + $entry->setReadingTime(23); + $this->getEntityManager()->persist($entry); + $this->getEntityManager()->flush(); + + $crawler = $client->request('GET', '/all/list'); + $this->assertCount(6, $crawler->filter('div[class=entry]')); $form = $crawler->filter('button[id=submit-filter]')->form(); @@ -626,12 +679,13 @@ class EntryControllerTest extends WallabagCoreTestCase $crawler = $client->submit($form, $data); - $this->assertCount(2, $crawler->filter('div[class=entry]')); + $this->assertCount(5, $crawler->filter('div[class=entry]')); } public function testFilterOnReadingTimeOnlyLower() { $this->logInAs('admin'); + $this->useTheme('baggy'); $client = $this->getClient(); $crawler = $client->request('GET', '/unread/list'); @@ -644,12 +698,22 @@ class EntryControllerTest extends WallabagCoreTestCase $crawler = $client->submit($form, $data); - $this->assertCount(4, $crawler->filter('div[class=entry]')); + $this->assertCount(0, $crawler->filter('div[class=entry]')); + + $entry = new Entry($this->getLoggedInUser()); + $entry->setUrl($this->url); + $entry->setReadingTime(23); + $this->getEntityManager()->persist($entry); + $this->getEntityManager()->flush(); + + $crawler = $client->submit($form, $data); + $this->assertCount(1, $crawler->filter('div[class=entry]')); } public function testFilterOnUnreadStatus() { $this->logInAs('admin'); + $this->useTheme('baggy'); $client = $this->getClient(); $crawler = $client->request('GET', '/all/list'); @@ -663,11 +727,22 @@ class EntryControllerTest extends WallabagCoreTestCase $crawler = $client->submit($form, $data); $this->assertCount(4, $crawler->filter('div[class=entry]')); + + $entry = new Entry($this->getLoggedInUser()); + $entry->setUrl($this->url); + $entry->setArchived(false); + $this->getEntityManager()->persist($entry); + $this->getEntityManager()->flush(); + + $crawler = $client->submit($form, $data); + + $this->assertCount(5, $crawler->filter('div[class=entry]')); } public function testFilterOnCreationDate() { $this->logInAs('admin'); + $this->useTheme('baggy'); $client = $this->getClient(); $crawler = $client->request('GET', '/unread/list'); @@ -734,6 +809,7 @@ class EntryControllerTest extends WallabagCoreTestCase public function testFilterOnDomainName() { $this->logInAs('admin'); + $this->useTheme('baggy'); $client = $this->getClient(); $crawler = $client->request('GET', '/unread/list'); @@ -766,6 +842,7 @@ class EntryControllerTest extends WallabagCoreTestCase public function testFilterOnStatus() { $this->logInAs('admin'); + $this->useTheme('baggy'); $client = $this->getClient(); $crawler = $client->request('GET', '/unread/list'); @@ -784,9 +861,24 @@ class EntryControllerTest extends WallabagCoreTestCase $this->assertCount(1, $crawler->filter('div[class=entry]')); } + public function testFilterOnIsPublic() + { + $this->logInAs('admin'); + $this->useTheme('baggy'); + $client = $this->getClient(); + + $crawler = $client->request('GET', '/unread/list'); + $form = $crawler->filter('button[id=submit-filter]')->form(); + $form['entry_filter[isPublic]']->tick(); + + $crawler = $client->submit($form); + $this->assertCount(0, $crawler->filter('div[class=entry]')); + } + public function testPreviewPictureFilter() { $this->logInAs('admin'); + $this->useTheme('baggy'); $client = $this->getClient(); $crawler = $client->request('GET', '/unread/list'); @@ -800,8 +892,15 @@ class EntryControllerTest extends WallabagCoreTestCase public function testFilterOnLanguage() { $this->logInAs('admin'); + $this->useTheme('baggy'); $client = $this->getClient(); + $entry = new Entry($this->getLoggedInUser()); + $entry->setUrl($this->url); + $entry->setLanguage('fr'); + $this->getEntityManager()->persist($entry); + $this->getEntityManager()->flush(); + $crawler = $client->request('GET', '/unread/list'); $form = $crawler->filter('button[id=submit-filter]')->form(); $data = [ @@ -809,7 +908,7 @@ class EntryControllerTest extends WallabagCoreTestCase ]; $crawler = $client->submit($form, $data); - $this->assertCount(2, $crawler->filter('div[class=entry]')); + $this->assertCount(3, $crawler->filter('div[class=entry]')); $form = $crawler->filter('button[id=submit-filter]')->form(); $data = [ @@ -825,10 +924,14 @@ class EntryControllerTest extends WallabagCoreTestCase $this->logInAs('admin'); $client = $this->getClient(); - $content = $client->getContainer() - ->get('doctrine.orm.entity_manager') - ->getRepository('WallabagCoreBundle:Entry') - ->findOneByUser($this->getLoggedInUserId()); + // sharing is enabled + $client->getContainer()->get('craue_config')->set('share_public', 1); + + $content = new Entry($this->getLoggedInUser()); + $content->setUrl($this->url); + $this->getEntityManager()->persist($content); + $this->getEntityManager()->flush(); + $this->getEntityManager()->clear(); // no uid $client->request('GET', '/share/'.$content->getUid()); @@ -869,6 +972,7 @@ class EntryControllerTest extends WallabagCoreTestCase public function testNewEntryWithDownloadImagesEnabled() { + $this->downloadImagesEnabled = true; $this->logInAs('admin'); $client = $this->getClient(); @@ -899,7 +1003,8 @@ class EntryControllerTest extends WallabagCoreTestCase $this->assertInstanceOf('Wallabag\CoreBundle\Entity\Entry', $entry); $this->assertEquals($url, $entry->getUrl()); $this->assertContains('Perpignan', $entry->getTitle()); - $this->assertContains('/d9bc0fcd.jpeg', $entry->getContent()); + // instead of checking for the filename (which might change) check that the image is now local + $this->assertContains('https://your-wallabag-url-instance.com/assets/images/', $entry->getContent()); $client->getContainer()->get('craue_config')->set('download_images_enabled', 0); } @@ -909,12 +1014,27 @@ class EntryControllerTest extends WallabagCoreTestCase */ public function testRemoveEntryWithDownloadImagesEnabled() { + $this->downloadImagesEnabled = true; $this->logInAs('admin'); $client = $this->getClient(); $url = 'http://www.20minutes.fr/montpellier/1952003-20161030-video-car-tombe-panne-rugbymen-perpignan-improvisent-melee-route'; $client->getContainer()->get('craue_config')->set('download_images_enabled', 1); + $crawler = $client->request('GET', '/new'); + + $this->assertEquals(200, $client->getResponse()->getStatusCode()); + + $form = $crawler->filter('form[name=entry]')->form(); + + $data = [ + 'entry[url]' => $url, + ]; + + $client->submit($form, $data); + + $this->assertEquals(302, $client->getResponse()->getStatusCode()); + $content = $client->getContainer() ->get('doctrine.orm.entity_manager') ->getRepository('WallabagCoreBundle:Entry') @@ -932,28 +1052,19 @@ class EntryControllerTest extends WallabagCoreTestCase $this->logInAs('empty'); $client = $this->getClient(); - $em = $client->getContainer()->get('doctrine.orm.entity_manager'); - $user = $em - ->getRepository('WallabagUserBundle:User') - ->find($this->getLoggedInUserId()); - - if (!$user) { - $this->markTestSkipped('No user found in db.'); - } - // Redirect to homepage - $config = $user->getConfig(); + $config = $this->getLoggedInUser()->getConfig(); $config->setActionMarkAsRead(Config::REDIRECT_TO_HOMEPAGE); - $em->persist($config); - $em->flush(); + $this->getEntityManager()->persist($config); - $content = $client->getContainer() - ->get('doctrine.orm.entity_manager') - ->getRepository('WallabagCoreBundle:Entry') - ->findByUrlAndUserId($this->url, $this->getLoggedInUserId()); + $entry = new Entry($this->getLoggedInUser()); + $entry->setUrl($this->url); + $this->getEntityManager()->persist($entry); - $client->request('GET', '/view/'.$content->getId()); - $client->request('GET', '/archive/'.$content->getId()); + $this->getEntityManager()->flush(); + + $client->request('GET', '/view/'.$entry->getId()); + $client->request('GET', '/archive/'.$entry->getId()); $this->assertEquals(302, $client->getResponse()->getStatusCode()); $this->assertEquals('/', $client->getResponse()->headers->get('location')); @@ -964,46 +1075,36 @@ class EntryControllerTest extends WallabagCoreTestCase $this->logInAs('empty'); $client = $this->getClient(); - $em = $client->getContainer()->get('doctrine.orm.entity_manager'); - $user = $em - ->getRepository('WallabagUserBundle:User') - ->find($this->getLoggedInUserId()); - - if (!$user) { - $this->markTestSkipped('No user found in db.'); - } - // Redirect to current page - $config = $user->getConfig(); + $config = $this->getLoggedInUser()->getConfig(); $config->setActionMarkAsRead(Config::REDIRECT_TO_CURRENT_PAGE); - $em->persist($config); - $em->flush(); + $this->getEntityManager()->persist($config); - $content = $client->getContainer() - ->get('doctrine.orm.entity_manager') - ->getRepository('WallabagCoreBundle:Entry') - ->findByUrlAndUserId($this->url, $this->getLoggedInUserId()); + $entry = new Entry($this->getLoggedInUser()); + $entry->setUrl($this->url); + $this->getEntityManager()->persist($entry); - $client->request('GET', '/view/'.$content->getId()); - $client->request('GET', '/archive/'.$content->getId()); + $this->getEntityManager()->flush(); + + $client->request('GET', '/view/'.$entry->getId()); + $client->request('GET', '/archive/'.$entry->getId()); $this->assertEquals(302, $client->getResponse()->getStatusCode()); - $this->assertContains('/view/'.$content->getId(), $client->getResponse()->headers->get('location')); + $this->assertContains('/view/'.$entry->getId(), $client->getResponse()->headers->get('location')); } public function testFilterOnHttpStatus() { $this->logInAs('admin'); + $this->useTheme('baggy'); $client = $this->getClient(); - $crawler = $client->request('GET', '/new'); - $form = $crawler->filter('form[name=entry]')->form(); + $entry = new Entry($this->getLoggedInUser()); + $entry->setUrl('http://www.lemonde.fr/incorrect-url/'); + $entry->setHttpStatus(404); + $this->getEntityManager()->persist($entry); - $data = [ - 'entry[url]' => 'http://www.lemonde.fr/incorrect-url/', - ]; - - $client->submit($form, $data); + $this->getEntityManager()->flush(); $crawler = $client->request('GET', '/all/list'); $form = $crawler->filter('button[id=submit-filter]')->form(); @@ -1016,14 +1117,17 @@ class EntryControllerTest extends WallabagCoreTestCase $this->assertCount(1, $crawler->filter('div[class=entry]')); - $crawler = $client->request('GET', '/new'); - $form = $crawler->filter('form[name=entry]')->form(); + $entry = new Entry($this->getLoggedInUser()); + $entry->setUrl($this->url); + $entry->setHttpStatus(200); + $this->getEntityManager()->persist($entry); - $data = [ - 'entry[url]' => 'http://www.nextinpact.com/news/101235-wallabag-alternative-libre-a-pocket-creuse-petit-a-petit-son-nid.htm', - ]; + $entry = new Entry($this->getLoggedInUser()); + $entry->setUrl('http://www.nextinpact.com/news/101235-wallabag-alternative-libre-a-pocket-creuse-petit-a-petit-son-nid.htm'); + $entry->setHttpStatus(200); + $this->getEntityManager()->persist($entry); - $client->submit($form, $data); + $this->getEntityManager()->flush(); $crawler = $client->request('GET', '/all/list'); $form = $crawler->filter('button[id=submit-filter]')->form(); @@ -1034,7 +1138,7 @@ class EntryControllerTest extends WallabagCoreTestCase $crawler = $client->submit($form, $data); - $this->assertCount(1, $crawler->filter('div[class=entry]')); + $this->assertCount(2, $crawler->filter('div[class=entry]')); $crawler = $client->request('GET', '/all/list'); $form = $crawler->filter('button[id=submit-filter]')->form(); @@ -1045,14 +1149,21 @@ class EntryControllerTest extends WallabagCoreTestCase $crawler = $client->submit($form, $data); - $this->assertCount(7, $crawler->filter('div[class=entry]')); + $this->assertCount(8, $crawler->filter('div[class=entry]')); } public function testSearch() { $this->logInAs('admin'); + $this->useTheme('baggy'); $client = $this->getClient(); + $entry = new Entry($this->getLoggedInUser()); + $entry->setUrl($this->url); + $entry->setTitle('test'); + $this->getEntityManager()->persist($entry); + $this->getEntityManager()->flush(); + // Search on unread list $crawler = $client->request('GET', '/unread/list'); @@ -1063,35 +1174,37 @@ class EntryControllerTest extends WallabagCoreTestCase $crawler = $client->submit($form, $data); - $this->assertCount(5, $crawler->filter('div[class=entry]')); + $this->assertCount(4, $crawler->filter('div[class=entry]')); // Search on starred list $crawler = $client->request('GET', '/starred/list'); + $entry = new Entry($this->getLoggedInUser()); + $entry->setUrl('http://localhost/foo/bar'); + $entry->setTitle('testeur'); + $entry->setStarred(true); + $this->getEntityManager()->persist($entry); + $this->getEntityManager()->flush(); + $form = $crawler->filter('form[name=search]')->form(); $data = [ - 'search_entry[term]' => 'title', + 'search_entry[term]' => 'testeur', ]; $crawler = $client->submit($form, $data); $this->assertCount(1, $crawler->filter('div[class=entry]')); - // Added new article to test on archive list - $crawler = $client->request('GET', '/new'); - $form = $crawler->filter('form[name=entry]')->form(); - $data = [ - 'entry[url]' => $this->url, - ]; - $client->submit($form, $data); - $content = $client->getContainer() - ->get('doctrine.orm.entity_manager') - ->getRepository('WallabagCoreBundle:Entry') - ->findByUrlAndUserId($this->url, $this->getLoggedInUserId()); - $client->request('GET', '/archive/'.$content->getId()); - $crawler = $client->request('GET', '/archive/list'); + // Added new article to test on archive list + $entry = new Entry($this->getLoggedInUser()); + $entry->setUrl('http://0.0.0.0/foo/baz/qux'); + $entry->setTitle('Le manège'); + $entry->setArchived(true); + $this->getEntityManager()->persist($entry); + $this->getEntityManager()->flush(); + $form = $crawler->filter('form[name=search]')->form(); $data = [ 'search_entry[term]' => 'manège', @@ -1100,7 +1213,7 @@ class EntryControllerTest extends WallabagCoreTestCase $crawler = $client->submit($form, $data); $this->assertCount(1, $crawler->filter('div[class=entry]')); - $client->request('GET', '/delete/'.$content->getId()); + $client->request('GET', '/delete/'.$entry->getId()); // test on list of all articles $crawler = $client->request('GET', '/all/list'); @@ -1115,6 +1228,13 @@ class EntryControllerTest extends WallabagCoreTestCase $this->assertCount(0, $crawler->filter('div[class=entry]')); // test url search on list of all articles + $entry = new Entry($this->getLoggedInUser()); + $entry->setUrl('http://domain/qux'); + $entry->setTitle('Le manège'); + $entry->setArchived(true); + $this->getEntityManager()->persist($entry); + $this->getEntityManager()->flush(); + $crawler = $client->request('GET', '/all/list'); $form = $crawler->filter('form[name=search]')->form(); @@ -1138,4 +1258,134 @@ class EntryControllerTest extends WallabagCoreTestCase $this->assertCount(1, $crawler->filter('div[class=entry]')); } + + public function dataForLanguage() + { + return [ + 'ru' => [ + 'https://www.pravda.ru/world/09-06-2017/1337283-qatar-0/', + 'ru', + ], + 'fr-FR' => [ + 'http://www.zataz.com/90-des-dossiers-medicaux-des-coreens-du-sud-vendus-a-des-entreprises-privees/', + 'fr_FR', + ], + 'de' => [ + 'http://www.bild.de/politik/ausland/theresa-may/wahlbeben-grossbritannien-analyse-52108924.bild.html', + 'de', + ], + 'it' => [ + 'http://www.ansa.it/sito/notizie/mondo/europa/2017/06/08/voto-gb-seggi-aperti-misure-sicurezza-rafforzate_0cb71f7f-e23b-4d5f-95ca-bc12296419f0.html', + 'it', + ], + 'zh_CN' => [ + 'http://www.hao123.com/shequ?__noscript__-=1', + 'zh_CN', + ], + 'de_AT' => [ + 'https://buy.garmin.com/de-AT/AT/catalog/product/compareResult.ep?compareProduct=112885&compareProduct=36728', + 'de_AT', + ], + 'ru_RU' => [ + 'http://netler.ru/ikt/windows-error-reporting.htm', + 'ru_RU', + ], + 'pt_BR' => [ + 'http://precodoscombustiveis.com.br/postos/cidade/4121/pr/maringa', + 'pt_BR', + ], + 'fucked_list_of_languages' => [ + 'http://geocatalog.webservice-energy.org/geonetwork/srv/eng/main.home', + '', + ], + 'es-ES' => [ + 'http://www.muylinux.com/2015/04/17/odf-reino-unido-microsoft-google', + 'es_ES', + ], + ]; + } + + /** + * @dataProvider dataForLanguage + */ + public function testLanguageValidation($url, $expectedLanguage) + { + $this->logInAs('admin'); + $client = $this->getClient(); + + $crawler = $client->request('GET', '/new'); + + $this->assertEquals(200, $client->getResponse()->getStatusCode()); + + $form = $crawler->filter('form[name=entry]')->form(); + + $data = [ + 'entry[url]' => $url, + ]; + + $client->submit($form, $data); + + $this->assertEquals(302, $client->getResponse()->getStatusCode()); + + $content = $client->getContainer() + ->get('doctrine.orm.entity_manager') + ->getRepository('WallabagCoreBundle:Entry') + ->findByUrlAndUserId($url, $this->getLoggedInUserId()); + + $this->assertInstanceOf('Wallabag\CoreBundle\Entity\Entry', $content); + $this->assertEquals($url, $content->getUrl()); + $this->assertEquals($expectedLanguage, $content->getLanguage()); + } + + /** + * This test will require an internet connection. + */ + public function testRestrictedArticle() + { + $url = 'http://www.monde-diplomatique.fr/2017/05/BONNET/57475'; + $this->logInAs('admin'); + $client = $this->getClient(); + $em = $client->getContainer()->get('doctrine.orm.entity_manager'); + + // enable restricted access + $client->getContainer()->get('craue_config')->set('restricted_access', 1); + + // create a new site_credential + $user = $client->getContainer()->get('security.token_storage')->getToken()->getUser(); + $credential = new SiteCredential($user); + $credential->setHost('monde-diplomatique.fr'); + $credential->setUsername($client->getContainer()->get('wallabag_core.helper.crypto_proxy')->crypt('foo')); + $credential->setPassword($client->getContainer()->get('wallabag_core.helper.crypto_proxy')->crypt('bar')); + + $em->persist($credential); + $em->flush(); + + $crawler = $client->request('GET', '/new'); + + $this->assertEquals(200, $client->getResponse()->getStatusCode()); + + $form = $crawler->filter('form[name=entry]')->form(); + + $data = [ + 'entry[url]' => $url, + ]; + + $client->submit($form, $data); + + $this->assertEquals(302, $client->getResponse()->getStatusCode()); + + $crawler = $client->followRedirect(); + + $this->assertEquals(200, $client->getResponse()->getStatusCode()); + $this->assertContains('flashes.entry.notice.entry_saved', $crawler->filter('body')->extract(['_text'])[0]); + + $content = $em + ->getRepository('WallabagCoreBundle:Entry') + ->findByUrlAndUserId($url, $this->getLoggedInUserId()); + + $this->assertInstanceOf('Wallabag\CoreBundle\Entity\Entry', $content); + $this->assertSame('Crimes et réformes aux Philippines', $content->getTitle()); + + $client->getContainer()->get('craue_config')->set('restricted_access', 0); + } } diff --git a/tests/Wallabag/CoreBundle/Controller/ExportControllerTest.php b/tests/Wallabag/CoreBundle/Controller/ExportControllerTest.php index 32a18e26..b38961d3 100644 --- a/tests/Wallabag/CoreBundle/Controller/ExportControllerTest.php +++ b/tests/Wallabag/CoreBundle/Controller/ExportControllerTest.php @@ -189,11 +189,9 @@ class ExportControllerTest extends WallabagCoreTestCase $this->assertContains($contentInDB[0]['language'], $csv[1]); $this->assertContains($contentInDB[0]['createdAt']->format('d/m/Y h:i:s'), $csv[1]); - $expectedTag = []; foreach ($contentInDB[0]['tags'] as $tag) { - $expectedTag[] = $tag['label']; + $this->assertContains($tag['label'], $csv[1]); } - $this->assertContains(implode(', ', $expectedTag), $csv[1]); } public function testJsonExport() diff --git a/tests/Wallabag/CoreBundle/Controller/RssControllerTest.php b/tests/Wallabag/CoreBundle/Controller/RssControllerTest.php index 5a59654d..530c8bbf 100644 --- a/tests/Wallabag/CoreBundle/Controller/RssControllerTest.php +++ b/tests/Wallabag/CoreBundle/Controller/RssControllerTest.php @@ -6,7 +6,7 @@ use Tests\Wallabag\CoreBundle\WallabagCoreTestCase; class RssControllerTest extends WallabagCoreTestCase { - public function validateDom($xml, $type, $nb = null) + public function validateDom($xml, $type, $urlPagination, $nb = null) { $doc = new \DOMDocument(); $doc->loadXML($xml); @@ -23,7 +23,7 @@ class RssControllerTest extends WallabagCoreTestCase $this->assertEquals(1, $xpath->query('/rss/channel')->length); $this->assertEquals(1, $xpath->query('/rss/channel/title')->length); - $this->assertEquals('wallabag — '.$type.' feed', $xpath->query('/rss/channel/title')->item(0)->nodeValue); + $this->assertEquals('wallabag - '.$type.' feed', $xpath->query('/rss/channel/title')->item(0)->nodeValue); $this->assertEquals(1, $xpath->query('/rss/channel/pubDate')->length); @@ -34,10 +34,10 @@ class RssControllerTest extends WallabagCoreTestCase $this->assertEquals('wallabag '.$type.' elements', $xpath->query('/rss/channel/description')->item(0)->nodeValue); $this->assertEquals(1, $xpath->query('/rss/channel/link[@rel="self"]')->length); - $this->assertContains($type.'.xml', $xpath->query('/rss/channel/link[@rel="self"]')->item(0)->getAttribute('href')); + $this->assertContains($urlPagination.'.xml', $xpath->query('/rss/channel/link[@rel="self"]')->item(0)->getAttribute('href')); $this->assertEquals(1, $xpath->query('/rss/channel/link[@rel="last"]')->length); - $this->assertContains($type.'.xml?page=', $xpath->query('/rss/channel/link[@rel="last"]')->item(0)->getAttribute('href')); + $this->assertContains($urlPagination.'.xml?page=', $xpath->query('/rss/channel/link[@rel="last"]')->item(0)->getAttribute('href')); foreach ($xpath->query('//item') as $item) { $this->assertEquals(1, $xpath->query('title', $item)->length); @@ -94,7 +94,7 @@ class RssControllerTest extends WallabagCoreTestCase $this->assertEquals(200, $client->getResponse()->getStatusCode()); - $this->validateDom($client->getResponse()->getContent(), 'unread', 2); + $this->validateDom($client->getResponse()->getContent(), 'unread', 'unread', 2); } public function testStarred() @@ -116,7 +116,7 @@ class RssControllerTest extends WallabagCoreTestCase $this->assertEquals(200, $client->getResponse()->getStatusCode(), 1); - $this->validateDom($client->getResponse()->getContent(), 'starred'); + $this->validateDom($client->getResponse()->getContent(), 'starred', 'starred'); } public function testArchives() @@ -138,7 +138,7 @@ class RssControllerTest extends WallabagCoreTestCase $this->assertEquals(200, $client->getResponse()->getStatusCode()); - $this->validateDom($client->getResponse()->getContent(), 'archive'); + $this->validateDom($client->getResponse()->getContent(), 'archive', 'archive'); } public function testPagination() @@ -159,13 +159,38 @@ class RssControllerTest extends WallabagCoreTestCase $client->request('GET', '/admin/SUPERTOKEN/unread.xml'); $this->assertEquals(200, $client->getResponse()->getStatusCode()); - $this->validateDom($client->getResponse()->getContent(), 'unread'); + $this->validateDom($client->getResponse()->getContent(), 'unread', 'unread'); $client->request('GET', '/admin/SUPERTOKEN/unread.xml?page=2'); $this->assertEquals(200, $client->getResponse()->getStatusCode()); - $this->validateDom($client->getResponse()->getContent(), 'unread'); + $this->validateDom($client->getResponse()->getContent(), 'unread', 'unread'); $client->request('GET', '/admin/SUPERTOKEN/unread.xml?page=3000'); $this->assertEquals(302, $client->getResponse()->getStatusCode()); } + + public function testTags() + { + $client = $this->getClient(); + $em = $client->getContainer()->get('doctrine.orm.entity_manager'); + $user = $em + ->getRepository('WallabagUserBundle:User') + ->findOneByUsername('admin'); + + $config = $user->getConfig(); + $config->setRssToken('SUPERTOKEN'); + $config->setRssLimit(null); + $em->persist($config); + $em->flush(); + + $client = $this->getClient(); + $client->request('GET', '/admin/SUPERTOKEN/tags/foo-bar.xml'); + + $this->assertEquals(200, $client->getResponse()->getStatusCode()); + + $this->validateDom($client->getResponse()->getContent(), 'tag (foo bar)', 'tags/foo-bar'); + + $client->request('GET', '/admin/SUPERTOKEN/tags/foo-bar.xml?page=3000'); + $this->assertEquals(302, $client->getResponse()->getStatusCode()); + } } diff --git a/tests/Wallabag/CoreBundle/Controller/SiteCredentialControllerTest.php b/tests/Wallabag/CoreBundle/Controller/SiteCredentialControllerTest.php new file mode 100644 index 00000000..e73a9743 --- /dev/null +++ b/tests/Wallabag/CoreBundle/Controller/SiteCredentialControllerTest.php @@ -0,0 +1,139 @@ +logInAs('admin'); + $client = $this->getClient(); + + $crawler = $client->request('GET', '/site-credentials/'); + + $this->assertEquals(200, $client->getResponse()->getStatusCode()); + + $body = $crawler->filter('body')->extract(['_text'])[0]; + + $this->assertContains('site_credential.description', $body); + $this->assertContains('site_credential.list.create_new_one', $body); + } + + public function testNewSiteCredential() + { + $this->logInAs('admin'); + $client = $this->getClient(); + + $crawler = $client->request('GET', '/site-credentials/new'); + + $this->assertEquals(200, $client->getResponse()->getStatusCode()); + + $body = $crawler->filter('body')->extract(['_text'])[0]; + + $this->assertContains('site_credential.new_site_credential', $body); + $this->assertContains('site_credential.form.back_to_list', $body); + + $form = $crawler->filter('button[id=site_credential_save]')->form(); + + $data = [ + 'site_credential[host]' => 'google.io', + 'site_credential[username]' => 'sergei', + 'site_credential[password]' => 'microsoft', + ]; + + $client->submit($form, $data); + + $this->assertEquals(302, $client->getResponse()->getStatusCode()); + + $crawler = $client->followRedirect(); + + $this->assertContains('flashes.site_credential.notice.added', $crawler->filter('body')->extract(['_text'])[0]); + } + + public function testEditSiteCredential() + { + $this->logInAs('admin'); + $client = $this->getClient(); + + $credential = $this->createSiteCredential($client); + + $crawler = $client->request('GET', '/site-credentials/'.$credential->getId().'/edit'); + + $this->assertEquals(200, $client->getResponse()->getStatusCode()); + + $body = $crawler->filter('body')->extract(['_text'])[0]; + + $this->assertContains('site_credential.edit_site_credential', $body); + $this->assertContains('site_credential.form.back_to_list', $body); + + $form = $crawler->filter('button[id=site_credential_save]')->form(); + + $data = [ + 'site_credential[host]' => 'google.io', + 'site_credential[username]' => 'larry', + 'site_credential[password]' => 'microsoft', + ]; + + $client->submit($form, $data); + + $this->assertEquals(302, $client->getResponse()->getStatusCode()); + + $crawler = $client->followRedirect(); + + $this->assertContains('flashes.site_credential.notice.updated', $crawler->filter('body')->extract(['_text'])[0]); + } + + public function testEditFromADifferentUserSiteCredential() + { + $this->logInAs('admin'); + $client = $this->getClient(); + + $credential = $this->createSiteCredential($client); + + $this->logInAs('bob'); + + $client->request('GET', '/site-credentials/'.$credential->getId().'/edit'); + + $this->assertEquals(403, $client->getResponse()->getStatusCode()); + } + + public function testDeleteSiteCredential() + { + $this->logInAs('admin'); + $client = $this->getClient(); + + $credential = $this->createSiteCredential($client); + + $crawler = $client->request('GET', '/site-credentials/'.$credential->getId().'/edit'); + + $this->assertEquals(200, $client->getResponse()->getStatusCode()); + + $deleteForm = $crawler->filter('body')->selectButton('site_credential.form.delete')->form(); + + $client->submit($deleteForm, []); + + $this->assertEquals(302, $client->getResponse()->getStatusCode()); + + $crawler = $client->followRedirect(); + + $this->assertContains('flashes.site_credential.notice.deleted', $crawler->filter('body')->extract(['_text'])[0]); + } + + private function createSiteCredential(Client $client) + { + $credential = new SiteCredential($this->getLoggedInUser()); + $credential->setHost('google.io'); + $credential->setUsername('sergei'); + $credential->setPassword('microsoft'); + + $em = $client->getContainer()->get('doctrine.orm.entity_manager'); + $em->persist($credential); + $em->flush(); + + return $credential; + } +} diff --git a/tests/Wallabag/CoreBundle/Controller/TagControllerTest.php b/tests/Wallabag/CoreBundle/Controller/TagControllerTest.php index fa1a3539..af1ad7af 100644 --- a/tests/Wallabag/CoreBundle/Controller/TagControllerTest.php +++ b/tests/Wallabag/CoreBundle/Controller/TagControllerTest.php @@ -3,6 +3,7 @@ namespace Tests\Wallabag\CoreBundle\Controller; use Tests\Wallabag\CoreBundle\WallabagCoreTestCase; +use Wallabag\CoreBundle\Entity\Entry; use Wallabag\CoreBundle\Entity\Tag; class TagControllerTest extends WallabagCoreTestCase @@ -24,10 +25,11 @@ class TagControllerTest extends WallabagCoreTestCase $this->logInAs('admin'); $client = $this->getClient(); - $entry = $client->getContainer() - ->get('doctrine.orm.entity_manager') - ->getRepository('WallabagCoreBundle:Entry') - ->findByUrlAndUserId('http://0.0.0.0/entry1', $this->getLoggedInUserId()); + $entry = new Entry($this->getLoggedInUser()); + $entry->setUrl('http://0.0.0.0/foo'); + $this->getEntityManager()->persist($entry); + $this->getEntityManager()->flush(); + $this->getEntityManager()->clear(); $crawler = $client->request('GET', '/view/'.$entry->getId()); @@ -41,23 +43,15 @@ class TagControllerTest extends WallabagCoreTestCase $this->assertEquals(302, $client->getResponse()->getStatusCode()); // be sure to reload the entry - $entry = $client->getContainer() - ->get('doctrine.orm.entity_manager') - ->getRepository('WallabagCoreBundle:Entry') - ->findByUrlAndUserId('http://0.0.0.0/entry1', $this->getLoggedInUserId()); - - $this->assertEquals(3, count($entry->getTags())); + $entry = $this->getEntityManager()->getRepository(Entry::class)->find($entry->getId()); + $this->assertCount(1, $entry->getTags()); // tag already exists and already assigned $client->submit($form, $data); $this->assertEquals(302, $client->getResponse()->getStatusCode()); - $newEntry = $client->getContainer() - ->get('doctrine.orm.entity_manager') - ->getRepository('WallabagCoreBundle:Entry') - ->find($entry->getId()); - - $this->assertEquals(3, count($newEntry->getTags())); + $entry = $this->getEntityManager()->getRepository(Entry::class)->find($entry->getId()); + $this->assertCount(1, $entry->getTags()); // tag already exists but still not assigned to this entry $data = [ @@ -67,12 +61,8 @@ class TagControllerTest extends WallabagCoreTestCase $client->submit($form, $data); $this->assertEquals(302, $client->getResponse()->getStatusCode()); - $newEntry = $client->getContainer() - ->get('doctrine.orm.entity_manager') - ->getRepository('WallabagCoreBundle:Entry') - ->find($entry->getId()); - - $this->assertEquals(3, count($newEntry->getTags())); + $entry = $this->getEntityManager()->getRepository(Entry::class)->find($entry->getId()); + $this->assertCount(2, $entry->getTags()); } public function testAddMultipleTagToEntry() @@ -116,20 +106,25 @@ class TagControllerTest extends WallabagCoreTestCase $this->logInAs('admin'); $client = $this->getClient(); - $entry = $client->getContainer() - ->get('doctrine.orm.entity_manager') - ->getRepository('WallabagCoreBundle:Entry') - ->findByUrlAndUserId('http://0.0.0.0/entry1', $this->getLoggedInUserId()); - - $tag = $client->getContainer() - ->get('doctrine.orm.entity_manager') - ->getRepository('WallabagCoreBundle:Tag') - ->findOneByEntryAndTagLabel($entry, $this->tagName); - + $tag = new Tag(); + $tag->setLabel($this->tagName); + $entry = new Entry($this->getLoggedInUser()); + $entry->setUrl('http://0.0.0.0/foo'); + $entry->addTag($tag); + $this->getEntityManager()->persist($entry); + $this->getEntityManager()->flush(); + $this->getEntityManager()->clear(); + + // We make a first request to set an history and test redirection after tag deletion + $client->request('GET', '/view/'.$entry->getId()); + $entryUri = $client->getRequest()->getUri(); $client->request('GET', '/remove-tag/'.$entry->getId().'/'.$tag->getId()); $this->assertEquals(302, $client->getResponse()->getStatusCode()); + $this->assertEquals($entryUri, $client->getResponse()->getTargetUrl()); + // re-retrieve the entry to be sure to get fresh data from database (mostly for tags) + $entry = $this->getEntityManager()->getRepository(Entry::class)->find($entry->getId()); $this->assertNotContains($this->tagName, $entry->getTags()); $client->request('GET', '/remove-tag/'.$entry->getId().'/'.$tag->getId()); diff --git a/tests/Wallabag/CoreBundle/GuzzleSiteAuthenticator/GrabySiteConfigBuilderTest.php b/tests/Wallabag/CoreBundle/GuzzleSiteAuthenticator/GrabySiteConfigBuilderTest.php index aee67259..b0c81e7b 100644 --- a/tests/Wallabag/CoreBundle/GuzzleSiteAuthenticator/GrabySiteConfigBuilderTest.php +++ b/tests/Wallabag/CoreBundle/GuzzleSiteAuthenticator/GrabySiteConfigBuilderTest.php @@ -2,12 +2,15 @@ namespace Tests\Wallabag\CoreBundle\GuzzleSiteAuthenticator; +use Monolog\Handler\TestHandler; +use Monolog\Logger; use BD\GuzzleSiteAuthenticator\SiteConfig\SiteConfig; use Graby\SiteConfig\SiteConfig as GrabySiteConfig; -use PHPUnit_Framework_TestCase; use Wallabag\CoreBundle\GuzzleSiteAuthenticator\GrabySiteConfigBuilder; +use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; -class GrabySiteConfigBuilderTest extends PHPUnit_Framework_TestCase +class GrabySiteConfigBuilderTest extends \PHPUnit_Framework_TestCase { /** @var \Wallabag\CoreBundle\GuzzleSiteAuthenticator\GrabySiteConfigBuilder */ protected $builder; @@ -15,16 +18,16 @@ class GrabySiteConfigBuilderTest extends PHPUnit_Framework_TestCase public function testBuildConfigExists() { /* @var \Graby\SiteConfig\ConfigBuilder|\PHPUnit_Framework_MockObject_MockObject */ - $grabyConfigBuilderMock = $this->getMockBuilder('\Graby\SiteConfig\ConfigBuilder') + $grabyConfigBuilderMock = $this->getMockBuilder('Graby\SiteConfig\ConfigBuilder') ->disableOriginalConstructor() ->getMock(); $grabySiteConfig = new GrabySiteConfig(); $grabySiteConfig->requires_login = true; - $grabySiteConfig->login_uri = 'http://example.com/login'; + $grabySiteConfig->login_uri = 'http://www.example.com/login'; $grabySiteConfig->login_username_field = 'login'; $grabySiteConfig->login_password_field = 'password'; - $grabySiteConfig->login_extra_fields = ['field' => 'value']; + $grabySiteConfig->login_extra_fields = ['field=value']; $grabySiteConfig->not_logged_in_xpath = '//div[@class="need-login"]'; $grabyConfigBuilderMock @@ -32,18 +35,44 @@ class GrabySiteConfigBuilderTest extends PHPUnit_Framework_TestCase ->with('example.com') ->will($this->returnValue($grabySiteConfig)); + $logger = new Logger('foo'); + $handler = new TestHandler(); + $logger->pushHandler($handler); + + $siteCrentialRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\SiteCredentialRepository') + ->disableOriginalConstructor() + ->getMock(); + $siteCrentialRepo->expects($this->once()) + ->method('findOneByHostAndUser') + ->with('example.com', 1) + ->willReturn(['username' => 'foo', 'password' => 'bar']); + + $user = $this->getMockBuilder('Wallabag\UserBundle\Entity\User') + ->disableOriginalConstructor() + ->getMock(); + $user->expects($this->once()) + ->method('getId') + ->willReturn(1); + + $token = new UsernamePasswordToken($user, 'pass', 'provider'); + + $tokenStorage = new TokenStorage(); + $tokenStorage->setToken($token); + $this->builder = new GrabySiteConfigBuilder( $grabyConfigBuilderMock, - ['example.com' => ['username' => 'foo', 'password' => 'bar']] + $tokenStorage, + $siteCrentialRepo, + $logger ); - $config = $this->builder->buildForHost('example.com'); + $config = $this->builder->buildForHost('www.example.com'); - self::assertEquals( + $this->assertEquals( new SiteConfig([ 'host' => 'example.com', 'requiresLogin' => true, - 'loginUri' => 'http://example.com/login', + 'loginUri' => 'http://www.example.com/login', 'usernameField' => 'login', 'passwordField' => 'password', 'extraFields' => ['field' => 'value'], @@ -53,6 +82,10 @@ class GrabySiteConfigBuilderTest extends PHPUnit_Framework_TestCase ]), $config ); + + $records = $handler->getRecords(); + + $this->assertCount(1, $records, 'One log was recorded'); } public function testBuildConfigDoesntExist() @@ -67,19 +100,43 @@ class GrabySiteConfigBuilderTest extends PHPUnit_Framework_TestCase ->with('unknown.com') ->will($this->returnValue(new GrabySiteConfig())); - $this->builder = new GrabySiteConfigBuilder($grabyConfigBuilderMock, []); + $logger = new Logger('foo'); + $handler = new TestHandler(); + $logger->pushHandler($handler); - $config = $this->builder->buildForHost('unknown.com'); + $siteCrentialRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\SiteCredentialRepository') + ->disableOriginalConstructor() + ->getMock(); + $siteCrentialRepo->expects($this->once()) + ->method('findOneByHostAndUser') + ->with('unknown.com', 1) + ->willReturn(null); - self::assertEquals( - new SiteConfig([ - 'host' => 'unknown.com', - 'requiresLogin' => false, - 'username' => null, - 'password' => null, - 'extraFields' => [], - ]), - $config + $user = $this->getMockBuilder('Wallabag\UserBundle\Entity\User') + ->disableOriginalConstructor() + ->getMock(); + $user->expects($this->once()) + ->method('getId') + ->willReturn(1); + + $token = new UsernamePasswordToken($user, 'pass', 'provider'); + + $tokenStorage = new TokenStorage(); + $tokenStorage->setToken($token); + + $this->builder = new GrabySiteConfigBuilder( + $grabyConfigBuilderMock, + $tokenStorage, + $siteCrentialRepo, + $logger ); + + $config = $this->builder->buildForHost('unknown.com'); + + $this->assertFalse($config); + + $records = $handler->getRecords(); + + $this->assertCount(1, $records, 'One log was recorded'); } } diff --git a/tests/Wallabag/CoreBundle/Helper/ContentProxyTest.php b/tests/Wallabag/CoreBundle/Helper/ContentProxyTest.php index 4f70ed0c..dbddbc5c 100644 --- a/tests/Wallabag/CoreBundle/Helper/ContentProxyTest.php +++ b/tests/Wallabag/CoreBundle/Helper/ContentProxyTest.php @@ -3,10 +3,17 @@ namespace Tests\Wallabag\CoreBundle\Helper; use Psr\Log\NullLogger; +use Monolog\Logger; +use Monolog\Handler\TestHandler; use Wallabag\CoreBundle\Helper\ContentProxy; use Wallabag\CoreBundle\Entity\Entry; use Wallabag\CoreBundle\Entity\Tag; use Wallabag\UserBundle\Entity\User; +use Wallabag\CoreBundle\Helper\RuleBasedTagger; +use Graby\Graby; +use Symfony\Component\Validator\Validator\RecursiveValidator; +use Symfony\Component\Validator\ConstraintViolationList; +use Symfony\Component\Validator\ConstraintViolation; class ContentProxyTest extends \PHPUnit_Framework_TestCase { @@ -33,8 +40,9 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase 'language' => '', ]); - $proxy = new ContentProxy($graby, $tagger, $this->getTagRepositoryMock(), $this->getLogger(), $this->fetchingErrorMessage); - $entry = $proxy->updateEntry(new Entry(new User()), 'http://user@:80'); + $proxy = new ContentProxy($graby, $tagger, $this->getValidator(), $this->getLogger(), $this->fetchingErrorMessage); + $entry = new Entry(new User()); + $proxy->updateEntry($entry, 'http://user@:80'); $this->assertEquals('http://user@:80', $entry->getUrl()); $this->assertEmpty($entry->getTitle()); @@ -67,8 +75,9 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase 'language' => '', ]); - $proxy = new ContentProxy($graby, $tagger, $this->getTagRepositoryMock(), $this->getLogger(), $this->fetchingErrorMessage); - $entry = $proxy->updateEntry(new Entry(new User()), 'http://0.0.0.0'); + $proxy = new ContentProxy($graby, $tagger, $this->getValidator(), $this->getLogger(), $this->fetchingErrorMessage); + $entry = new Entry(new User()); + $proxy->updateEntry($entry, 'http://0.0.0.0'); $this->assertEquals('http://0.0.0.0', $entry->getUrl()); $this->assertEmpty($entry->getTitle()); @@ -106,12 +115,13 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase ], ]); - $proxy = new ContentProxy($graby, $tagger, $this->getTagRepositoryMock(), $this->getLogger(), $this->fetchingErrorMessage); - $entry = $proxy->updateEntry(new Entry(new User()), 'http://domain.io'); + $proxy = new ContentProxy($graby, $tagger, $this->getValidator(), $this->getLogger(), $this->fetchingErrorMessage); + $entry = new Entry(new User()); + $proxy->updateEntry($entry, 'http://domain.io'); $this->assertEquals('http://domain.io', $entry->getUrl()); $this->assertEquals('my title', $entry->getTitle()); - $this->assertEquals($this->fetchingErrorMessage . '

    But we found a short description:

    desc', $entry->getContent()); + $this->assertEquals($this->fetchingErrorMessage.'

    But we found a short description:

    desc', $entry->getContent()); $this->assertEmpty($entry->getPreviewPicture()); $this->assertEmpty($entry->getLanguage()); $this->assertEmpty($entry->getHttpStatus()); @@ -147,8 +157,9 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase ], ]); - $proxy = new ContentProxy($graby, $tagger, $this->getTagRepositoryMock(), $this->getLogger(), $this->fetchingErrorMessage); - $entry = $proxy->updateEntry(new Entry(new User()), 'http://0.0.0.0'); + $proxy = new ContentProxy($graby, $tagger, $this->getValidator(), $this->getLogger(), $this->fetchingErrorMessage); + $entry = new Entry(new User()); + $proxy->updateEntry($entry, 'http://0.0.0.0'); $this->assertEquals('http://1.1.1.1', $entry->getUrl()); $this->assertEquals('this is my title', $entry->getTitle()); @@ -184,12 +195,13 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase 'open_graph' => [ 'og_title' => 'my OG title', 'og_description' => 'OG desc', - 'og_image' => false, + 'og_image' => null, ], ]); - $proxy = new ContentProxy($graby, $tagger, $this->getTagRepositoryMock(), $this->getLogger(), $this->fetchingErrorMessage); - $entry = $proxy->updateEntry(new Entry(new User()), 'http://0.0.0.0'); + $proxy = new ContentProxy($graby, $tagger, $this->getValidator(), $this->getLogger(), $this->fetchingErrorMessage); + $entry = new Entry(new User()); + $proxy->updateEntry($entry, 'http://0.0.0.0'); $this->assertEquals('http://1.1.1.1', $entry->getUrl()); $this->assertEquals('this is my title', $entry->getTitle()); @@ -202,187 +214,308 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase $this->assertEquals('1.1.1.1', $entry->getDomainName()); } - public function testWithForcedContent() + public function testWithContentAndBadLanguage() { $tagger = $this->getTaggerMock(); $tagger->expects($this->once()) ->method('tag'); - $graby = $this->getMockBuilder('Graby\Graby')->getMock(); + $validator = $this->getValidator(); + $validator->expects($this->exactly(2)) + ->method('validate') + ->will($this->onConsecutiveCalls( + new ConstraintViolationList([new ConstraintViolation('oops', 'oops', [], 'oops', 'language', 'dontexist')]), + new ConstraintViolationList() + )); - $proxy = new ContentProxy($graby, $tagger, $this->getTagRepositoryMock(), $this->getLogger(), $this->fetchingErrorMessage); - $entry = $proxy->updateEntry(new Entry(new User()), 'http://0.0.0.0', [ - 'html' => str_repeat('this is my content', 325), - 'title' => 'this is my title', - 'url' => 'http://1.1.1.1', - 'content_type' => 'text/html', - 'language' => 'fr', - ]); + $graby = $this->getMockBuilder('Graby\Graby') + ->setMethods(['fetchContent']) + ->disableOriginalConstructor() + ->getMock(); + + $graby->expects($this->any()) + ->method('fetchContent') + ->willReturn([ + 'html' => str_repeat('this is my content', 325), + 'title' => 'this is my title', + 'url' => 'http://1.1.1.1', + 'content_type' => 'text/html', + 'language' => 'dontexist', + 'status' => '200', + ]); + + $proxy = new ContentProxy($graby, $tagger, $validator, $this->getLogger(), $this->fetchingErrorMessage); + $entry = new Entry(new User()); + $proxy->updateEntry($entry, 'http://0.0.0.0'); $this->assertEquals('http://1.1.1.1', $entry->getUrl()); $this->assertEquals('this is my title', $entry->getTitle()); $this->assertContains('this is my content', $entry->getContent()); $this->assertEquals('text/html', $entry->getMimetype()); - $this->assertEquals('fr', $entry->getLanguage()); + $this->assertNull($entry->getLanguage()); + $this->assertEquals('200', $entry->getHttpStatus()); $this->assertEquals(4.0, $entry->getReadingTime()); $this->assertEquals('1.1.1.1', $entry->getDomainName()); } - public function testTaggerThrowException() + public function testWithContentAndBadOgImage() { - $graby = $this->getMockBuilder('Graby\Graby') - ->disableOriginalConstructor() - ->getMock(); - $tagger = $this->getTaggerMock(); $tagger->expects($this->once()) - ->method('tag') - ->will($this->throwException(new \Exception())); - - $tagRepo = $this->getTagRepositoryMock(); - $proxy = new ContentProxy($graby, $tagger, $tagRepo, $this->getLogger(), $this->fetchingErrorMessage); - - $entry = $proxy->updateEntry(new Entry(new User()), 'http://0.0.0.0', [ - 'html' => str_repeat('this is my content', 325), - 'title' => 'this is my title', - 'url' => 'http://1.1.1.1', - 'content_type' => 'text/html', - 'language' => 'fr', - ]); + ->method('tag'); - $this->assertCount(0, $entry->getTags()); - } + $validator = $this->getValidator(); + $validator->expects($this->exactly(2)) + ->method('validate') + ->will($this->onConsecutiveCalls( + new ConstraintViolationList(), + new ConstraintViolationList([new ConstraintViolation('oops', 'oops', [], 'oops', 'url', 'https://')]) + )); - public function testAssignTagsWithArrayAndExtraSpaces() - { $graby = $this->getMockBuilder('Graby\Graby') + ->setMethods(['fetchContent']) ->disableOriginalConstructor() ->getMock(); - $tagRepo = $this->getTagRepositoryMock(); - $proxy = new ContentProxy($graby, $this->getTaggerMock(), $tagRepo, $this->getLogger(), $this->fetchingErrorMessage); + $graby->expects($this->any()) + ->method('fetchContent') + ->willReturn([ + 'html' => str_repeat('this is my content', 325), + 'title' => 'this is my title', + 'url' => 'http://1.1.1.1', + 'content_type' => 'text/html', + 'language' => 'fr', + 'status' => '200', + 'open_graph' => [ + 'og_title' => 'my OG title', + 'og_description' => 'OG desc', + 'og_image' => 'https://', + ], + ]); + $proxy = new ContentProxy($graby, $tagger, $validator, $this->getLogger(), $this->fetchingErrorMessage); $entry = new Entry(new User()); + $proxy->updateEntry($entry, 'http://0.0.0.0'); - $proxy->assignTagsToEntry($entry, [' tag1', 'tag2 ']); - - $this->assertCount(2, $entry->getTags()); - $this->assertEquals('tag1', $entry->getTags()[0]->getLabel()); - $this->assertEquals('tag2', $entry->getTags()[1]->getLabel()); + $this->assertEquals('http://1.1.1.1', $entry->getUrl()); + $this->assertEquals('this is my title', $entry->getTitle()); + $this->assertContains('this is my content', $entry->getContent()); + $this->assertNull($entry->getPreviewPicture()); + $this->assertEquals('text/html', $entry->getMimetype()); + $this->assertEquals('fr', $entry->getLanguage()); + $this->assertEquals('200', $entry->getHttpStatus()); + $this->assertEquals(4.0, $entry->getReadingTime()); + $this->assertEquals('1.1.1.1', $entry->getDomainName()); } - public function testAssignTagsWithString() + public function testWithForcedContent() { - $graby = $this->getMockBuilder('Graby\Graby') - ->disableOriginalConstructor() - ->getMock(); - - $tagRepo = $this->getTagRepositoryMock(); - $proxy = new ContentProxy($graby, $this->getTaggerMock(), $tagRepo, $this->getLogger(), $this->fetchingErrorMessage); + $tagger = $this->getTaggerMock(); + $tagger->expects($this->once()) + ->method('tag'); + $proxy = new ContentProxy((new Graby()), $tagger, $this->getValidator(), $this->getLogger(), $this->fetchingErrorMessage); $entry = new Entry(new User()); + $proxy->updateEntry( + $entry, + 'http://0.0.0.0', + [ + 'html' => str_repeat('this is my content', 325), + 'title' => 'this is my title', + 'url' => 'http://1.1.1.1', + 'content_type' => 'text/html', + 'language' => 'fr', + 'date' => '1395635872', + 'authors' => ['Jeremy', 'Nico', 'Thomas'], + 'all_headers' => [ + 'Cache-Control' => 'no-cache', + ], + ] + ); - $proxy->assignTagsToEntry($entry, 'tag1, tag2'); - - $this->assertCount(2, $entry->getTags()); - $this->assertEquals('tag1', $entry->getTags()[0]->getLabel()); - $this->assertEquals('tag2', $entry->getTags()[1]->getLabel()); + $this->assertEquals('http://1.1.1.1', $entry->getUrl()); + $this->assertEquals('this is my title', $entry->getTitle()); + $this->assertContains('this is my content', $entry->getContent()); + $this->assertEquals('text/html', $entry->getMimetype()); + $this->assertEquals('fr', $entry->getLanguage()); + $this->assertEquals(4.0, $entry->getReadingTime()); + $this->assertEquals('1.1.1.1', $entry->getDomainName()); + $this->assertEquals('24/03/2014', $entry->getPublishedAt()->format('d/m/Y')); + $this->assertContains('Jeremy', $entry->getPublishedBy()); + $this->assertContains('Nico', $entry->getPublishedBy()); + $this->assertContains('Thomas', $entry->getPublishedBy()); + $this->assertContains('no-cache', $entry->getHeaders()); } - public function testAssignTagsWithEmptyArray() + public function testWithForcedContentAndDatetime() { - $graby = $this->getMockBuilder('Graby\Graby') - ->disableOriginalConstructor() - ->getMock(); + $tagger = $this->getTaggerMock(); + $tagger->expects($this->once()) + ->method('tag'); - $tagRepo = $this->getTagRepositoryMock(); - $proxy = new ContentProxy($graby, $this->getTaggerMock(), $tagRepo, $this->getLogger(), $this->fetchingErrorMessage); + $logHandler = new TestHandler(); + $logger = new Logger('test', [$logHandler]); + $proxy = new ContentProxy((new Graby()), $tagger, $this->getValidator(), $logger, $this->fetchingErrorMessage); $entry = new Entry(new User()); + $proxy->updateEntry( + $entry, + 'http://1.1.1.1', + [ + 'html' => str_repeat('this is my content', 325), + 'title' => 'this is my title', + 'url' => 'http://1.1.1.1', + 'content_type' => 'text/html', + 'language' => 'fr', + 'date' => '2016-09-08T11:55:58+0200', + ] + ); - $proxy->assignTagsToEntry($entry, []); - - $this->assertCount(0, $entry->getTags()); + $this->assertEquals('http://1.1.1.1', $entry->getUrl()); + $this->assertEquals('this is my title', $entry->getTitle()); + $this->assertContains('this is my content', $entry->getContent()); + $this->assertEquals('text/html', $entry->getMimetype()); + $this->assertEquals('fr', $entry->getLanguage()); + $this->assertEquals(4.0, $entry->getReadingTime()); + $this->assertEquals('1.1.1.1', $entry->getDomainName()); + $this->assertEquals('08/09/2016', $entry->getPublishedAt()->format('d/m/Y')); } - public function testAssignTagsWithEmptyString() + public function testWithForcedContentAndBadDate() { - $graby = $this->getMockBuilder('Graby\Graby') - ->disableOriginalConstructor() - ->getMock(); + $tagger = $this->getTaggerMock(); + $tagger->expects($this->once()) + ->method('tag'); - $tagRepo = $this->getTagRepositoryMock(); - $proxy = new ContentProxy($graby, $this->getTaggerMock(), $tagRepo, $this->getLogger(), $this->fetchingErrorMessage); + $logger = new Logger('foo'); + $handler = new TestHandler(); + $logger->pushHandler($handler); + $proxy = new ContentProxy((new Graby()), $tagger, $this->getValidator(), $logger, $this->fetchingErrorMessage); $entry = new Entry(new User()); + $proxy->updateEntry( + $entry, + 'http://1.1.1.1', + [ + 'html' => str_repeat('this is my content', 325), + 'title' => 'this is my title', + 'url' => 'http://1.1.1.1', + 'content_type' => 'text/html', + 'language' => 'fr', + 'date' => '01 02 2012', + ] + ); + + $this->assertEquals('http://1.1.1.1', $entry->getUrl()); + $this->assertEquals('this is my title', $entry->getTitle()); + $this->assertContains('this is my content', $entry->getContent()); + $this->assertEquals('text/html', $entry->getMimetype()); + $this->assertEquals('fr', $entry->getLanguage()); + $this->assertEquals(4.0, $entry->getReadingTime()); + $this->assertEquals('1.1.1.1', $entry->getDomainName()); + $this->assertNull($entry->getPublishedAt()); - $proxy->assignTagsToEntry($entry, ''); + $records = $handler->getRecords(); - $this->assertCount(0, $entry->getTags()); + $this->assertCount(1, $records); + $this->assertContains('Error while defining date', $records[0]['message']); } - public function testAssignTagsAlreadyAssigned() + public function testTaggerThrowException() { - $graby = $this->getMockBuilder('Graby\Graby') - ->disableOriginalConstructor() - ->getMock(); - - $tagRepo = $this->getTagRepositoryMock(); - $proxy = new ContentProxy($graby, $this->getTaggerMock(), $tagRepo, $this->getLogger(), $this->fetchingErrorMessage); - - $tagEntity = new Tag(); - $tagEntity->setLabel('tag1'); + $tagger = $this->getTaggerMock(); + $tagger->expects($this->once()) + ->method('tag') + ->will($this->throwException(new \Exception())); + $proxy = new ContentProxy((new Graby()), $tagger, $this->getValidator(), $this->getLogger(), $this->fetchingErrorMessage); $entry = new Entry(new User()); - $entry->addTag($tagEntity); - - $proxy->assignTagsToEntry($entry, 'tag1, tag2'); + $proxy->updateEntry( + $entry, + 'http://1.1.1.1', + [ + 'html' => str_repeat('this is my content', 325), + 'title' => 'this is my title', + 'url' => 'http://1.1.1.1', + 'content_type' => 'text/html', + 'language' => 'fr', + ] + ); - $this->assertCount(2, $entry->getTags()); - $this->assertEquals('tag1', $entry->getTags()[0]->getLabel()); - $this->assertEquals('tag2', $entry->getTags()[1]->getLabel()); + $this->assertCount(0, $entry->getTags()); } - public function testAssignTagsNotFlushed() + public function dataForCrazyHtml() { - $graby = $this->getMockBuilder('Graby\Graby') - ->disableOriginalConstructor() - ->getMock(); - - $tagRepo = $this->getTagRepositoryMock(); - $tagRepo->expects($this->never()) - ->method('__call'); - - $proxy = new ContentProxy($graby, $this->getTaggerMock(), $tagRepo, $this->getLogger(), $this->fetchingErrorMessage); + return [ + 'script and comment' => [ + 'Script inside:
    ', + 'lol', + ], + 'script' => [ + 'Script inside:', + 'script', + ], + ]; + } - $tagEntity = new Tag(); - $tagEntity->setLabel('tag1'); + /** + * @dataProvider dataForCrazyHtml + */ + public function testWithCrazyHtmlContent($html, $escapedString) + { + $tagger = $this->getTaggerMock(); + $tagger->expects($this->once()) + ->method('tag'); + $proxy = new ContentProxy((new Graby()), $tagger, $this->getValidator(), $this->getLogger(), $this->fetchingErrorMessage); $entry = new Entry(new User()); + $proxy->updateEntry( + $entry, + 'http://1.1.1.1', + [ + 'html' => $html, + 'title' => 'this is my title', + 'url' => 'http://1.1.1.1', + 'content_type' => 'text/html', + 'language' => 'fr', + 'status' => '200', + 'open_graph' => [ + 'og_title' => 'my OG title', + 'og_description' => 'OG desc', + 'og_image' => 'http://3.3.3.3/cover.jpg', + ], + ] + ); - $proxy->assignTagsToEntry($entry, 'tag1', [$tagEntity]); - - $this->assertCount(1, $entry->getTags()); - $this->assertEquals('tag1', $entry->getTags()[0]->getLabel()); + $this->assertEquals('http://1.1.1.1', $entry->getUrl()); + $this->assertEquals('this is my title', $entry->getTitle()); + $this->assertNotContains($escapedString, $entry->getContent()); + $this->assertEquals('http://3.3.3.3/cover.jpg', $entry->getPreviewPicture()); + $this->assertEquals('text/html', $entry->getMimetype()); + $this->assertEquals('fr', $entry->getLanguage()); + $this->assertEquals('200', $entry->getHttpStatus()); + $this->assertEquals('1.1.1.1', $entry->getDomainName()); } private function getTaggerMock() { - return $this->getMockBuilder('Wallabag\CoreBundle\Helper\RuleBasedTagger') + return $this->getMockBuilder(RuleBasedTagger::class) ->setMethods(['tag']) ->disableOriginalConstructor() ->getMock(); } - private function getTagRepositoryMock() + private function getLogger() { - return $this->getMockBuilder('Wallabag\CoreBundle\Repository\TagRepository') - ->disableOriginalConstructor() - ->getMock(); + return new NullLogger(); } - private function getLogger() + private function getValidator() { - return new NullLogger(); + return $this->getMockBuilder(RecursiveValidator::class) + ->setMethods(['validate']) + ->disableOriginalConstructor() + ->getMock(); } } diff --git a/tests/Wallabag/CoreBundle/Helper/CryptoProxyTest.php b/tests/Wallabag/CoreBundle/Helper/CryptoProxyTest.php new file mode 100644 index 00000000..cede8696 --- /dev/null +++ b/tests/Wallabag/CoreBundle/Helper/CryptoProxyTest.php @@ -0,0 +1,40 @@ +crypt('test'); + $decrypted = $crypto->decrypt($crypted); + + $this->assertSame('test', $decrypted); + + $records = $logHandler->getRecords(); + $this->assertCount(2, $records); + $this->assertContains('Crypto: crypting value', $records[0]['message']); + $this->assertContains('Crypto: decrypting value', $records[1]['message']); + } + + /** + * @expectedException RuntimeException + * @expectedExceptionMessage Decrypt fail + * + * @return [type] [description] + */ + public function testDecryptBadValue() + { + $crypto = new CryptoProxy(sys_get_temp_dir().'/'.uniqid('', true).'.txt', new NullLogger()); + $crypto->decrypt('badvalue'); + } +} diff --git a/tests/Wallabag/CoreBundle/Helper/DownloadImagesTest.php b/tests/Wallabag/CoreBundle/Helper/DownloadImagesTest.php index 85f12d87..c02f9658 100644 --- a/tests/Wallabag/CoreBundle/Helper/DownloadImagesTest.php +++ b/tests/Wallabag/CoreBundle/Helper/DownloadImagesTest.php @@ -12,7 +12,24 @@ use GuzzleHttp\Stream\Stream; class DownloadImagesTest extends \PHPUnit_Framework_TestCase { - public function testProcessHtml() + public function dataForSuccessImage() + { + return [ + 'imgur' => [ + '
    ', + 'http://imgur.com/gallery/WxtWY', + ], + 'image with &' => [ + '
    ', + 'https://www.tvaddons.ag/realdebrid-kodi-jarvis/', + ], + ]; + } + + /** + * @dataProvider dataForSuccessImage + */ + public function testProcessHtml($html, $url) { $client = new Client(); @@ -27,9 +44,10 @@ class DownloadImagesTest extends \PHPUnit_Framework_TestCase $download = new DownloadImages($client, sys_get_temp_dir().'/wallabag_test', 'http://wallabag.io/', $logger); - $res = $download->processHtml(123, '
    ', 'http://imgur.com/gallery/WxtWY'); + $res = $download->processHtml(123, $html, $url); - $this->assertContains('http://wallabag.io/assets/images/9/b/9b0ead26/c638b4c2.png', $res); + // this the base path of all image (since it's calculated using the entry id: 123) + $this->assertContains('http://wallabag.io/assets/images/9/b/9b0ead26/', $res); } public function testProcessHtmlWithBadImage() @@ -139,4 +157,29 @@ class DownloadImagesTest extends \PHPUnit_Framework_TestCase $this->assertFalse($res, 'Absolute image can not be determined, so it will not be replaced'); } + + public function testProcessRealImage() + { + $client = new Client(); + + $mock = new Mock([ + new Response(200, ['content-type' => null], Stream::factory(file_get_contents(__DIR__.'/../fixtures/image-no-content-type.jpg'))), + ]); + + $client->getEmitter()->attach($mock); + + $logHandler = new TestHandler(); + $logger = new Logger('test', array($logHandler)); + + $download = new DownloadImages($client, sys_get_temp_dir().'/wallabag_test', 'http://wallabag.io/', $logger); + + $res = $download->processSingleImage( + 123, + 'https://cdn.theconversation.com/files/157200/article/width926/gsj2rjp2-1487348607.jpg', + 'https://theconversation.com/conversation-avec-gerald-bronner-ce-nest-pas-la-post-verite-qui-nous-menace-mais-lextension-de-notre-credulite-73089' + ); + + $this->assertContains('http://wallabag.io/assets/images/9/b/9b0ead26/', $res, 'Content-Type was empty but data is ok for an image'); + $this->assertContains('DownloadImages: Checking extension (alternative)', $logHandler->getRecords()[3]['message']); + } } diff --git a/tests/Wallabag/CoreBundle/Helper/RedirectTest.php b/tests/Wallabag/CoreBundle/Helper/RedirectTest.php index 0539f20a..f420d06a 100644 --- a/tests/Wallabag/CoreBundle/Helper/RedirectTest.php +++ b/tests/Wallabag/CoreBundle/Helper/RedirectTest.php @@ -89,4 +89,22 @@ class RedirectTest extends \PHPUnit_Framework_TestCase $this->assertEquals($this->routerMock->generate('homepage'), $redirectUrl); } + + public function testUserForRedirectWithIgnoreActionMarkAsRead() + { + $this->token->getUser()->getConfig()->setActionMarkAsRead(Config::REDIRECT_TO_HOMEPAGE); + + $redirectUrl = $this->redirect->to('/unread/list', '', true); + + $this->assertEquals('/unread/list', $redirectUrl); + } + + public function testUserForRedirectNullWithFallbackWithIgnoreActionMarkAsRead() + { + $this->token->getUser()->getConfig()->setActionMarkAsRead(Config::REDIRECT_TO_HOMEPAGE); + + $redirectUrl = $this->redirect->to(null, 'fallback', true); + + $this->assertEquals('fallback', $redirectUrl); + } } diff --git a/tests/Wallabag/CoreBundle/Helper/RuleBasedTaggerTest.php b/tests/Wallabag/CoreBundle/Helper/RuleBasedTaggerTest.php index 17b08c2a..1e21f400 100644 --- a/tests/Wallabag/CoreBundle/Helper/RuleBasedTaggerTest.php +++ b/tests/Wallabag/CoreBundle/Helper/RuleBasedTaggerTest.php @@ -2,6 +2,8 @@ namespace Tests\Wallabag\CoreBundle\Helper; +use Monolog\Handler\TestHandler; +use Monolog\Logger; use Wallabag\CoreBundle\Entity\Config; use Wallabag\CoreBundle\Entity\Entry; use Wallabag\CoreBundle\Entity\Tag; @@ -15,14 +17,19 @@ class RuleBasedTaggerTest extends \PHPUnit_Framework_TestCase private $tagRepository; private $entryRepository; private $tagger; + private $logger; + private $handler; public function setUp() { $this->rulerz = $this->getRulerZMock(); $this->tagRepository = $this->getTagRepositoryMock(); $this->entryRepository = $this->getEntryRepositoryMock(); + $this->logger = $this->getLogger(); + $this->handler = new TestHandler(); + $this->logger->pushHandler($this->handler); - $this->tagger = new RuleBasedTagger($this->rulerz, $this->tagRepository, $this->entryRepository); + $this->tagger = new RuleBasedTagger($this->rulerz, $this->tagRepository, $this->entryRepository, $this->logger); } public function testTagWithNoRule() @@ -32,6 +39,8 @@ class RuleBasedTaggerTest extends \PHPUnit_Framework_TestCase $this->tagger->tag($entry); $this->assertTrue($entry->getTags()->isEmpty()); + $records = $this->handler->getRecords(); + $this->assertCount(0, $records); } public function testTagWithNoMatchingRule() @@ -49,6 +58,8 @@ class RuleBasedTaggerTest extends \PHPUnit_Framework_TestCase $this->tagger->tag($entry); $this->assertTrue($entry->getTags()->isEmpty()); + $records = $this->handler->getRecords(); + $this->assertCount(0, $records); } public function testTagWithAMatchingRule() @@ -70,6 +81,9 @@ class RuleBasedTaggerTest extends \PHPUnit_Framework_TestCase $tags = $entry->getTags(); $this->assertSame('foo', $tags[0]->getLabel()); $this->assertSame('bar', $tags[1]->getLabel()); + + $records = $this->handler->getRecords(); + $this->assertCount(1, $records); } public function testTagWithAMixOfMatchingRules() @@ -90,6 +104,8 @@ class RuleBasedTaggerTest extends \PHPUnit_Framework_TestCase $tags = $entry->getTags(); $this->assertSame('foo', $tags[0]->getLabel()); + $records = $this->handler->getRecords(); + $this->assertCount(1, $records); } public function testWhenTheTagExists() @@ -118,6 +134,8 @@ class RuleBasedTaggerTest extends \PHPUnit_Framework_TestCase $tags = $entry->getTags(); $this->assertSame($tag, $tags[0]); + $records = $this->handler->getRecords(); + $this->assertCount(1, $records); } public function testSameTagWithDifferentfMatchingRules() @@ -138,6 +156,8 @@ class RuleBasedTaggerTest extends \PHPUnit_Framework_TestCase $tags = $entry->getTags(); $this->assertCount(1, $tags); + $records = $this->handler->getRecords(); + $this->assertCount(2, $records); } public function testTagAllEntriesForAUser() @@ -209,4 +229,9 @@ class RuleBasedTaggerTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->getMock(); } + + private function getLogger() + { + return new Logger('foo'); + } } diff --git a/tests/Wallabag/CoreBundle/Helper/TagsAssignerTest.php b/tests/Wallabag/CoreBundle/Helper/TagsAssignerTest.php new file mode 100644 index 00000000..6d6d6484 --- /dev/null +++ b/tests/Wallabag/CoreBundle/Helper/TagsAssignerTest.php @@ -0,0 +1,108 @@ +getTagRepositoryMock(); + $tagsAssigner = new TagsAssigner($tagRepo); + + $entry = new Entry(new User()); + + $tagsAssigner->assignTagsToEntry($entry, [' tag1', 'tag2 ']); + + $this->assertCount(2, $entry->getTags()); + $this->assertEquals('tag1', $entry->getTags()[0]->getLabel()); + $this->assertEquals('tag2', $entry->getTags()[1]->getLabel()); + } + + public function testAssignTagsWithString() + { + $tagRepo = $this->getTagRepositoryMock(); + $tagsAssigner = new TagsAssigner($tagRepo); + + $entry = new Entry(new User()); + + $tagsAssigner->assignTagsToEntry($entry, 'tag1, tag2'); + + $this->assertCount(2, $entry->getTags()); + $this->assertEquals('tag1', $entry->getTags()[0]->getLabel()); + $this->assertEquals('tag2', $entry->getTags()[1]->getLabel()); + } + + public function testAssignTagsWithEmptyArray() + { + $tagRepo = $this->getTagRepositoryMock(); + $tagsAssigner = new TagsAssigner($tagRepo); + + $entry = new Entry(new User()); + + $tagsAssigner->assignTagsToEntry($entry, []); + + $this->assertCount(0, $entry->getTags()); + } + + public function testAssignTagsWithEmptyString() + { + $tagRepo = $this->getTagRepositoryMock(); + $tagsAssigner = new TagsAssigner($tagRepo); + + $entry = new Entry(new User()); + + $tagsAssigner->assignTagsToEntry($entry, ''); + + $this->assertCount(0, $entry->getTags()); + } + + public function testAssignTagsAlreadyAssigned() + { + $tagRepo = $this->getTagRepositoryMock(); + $tagsAssigner = new TagsAssigner($tagRepo); + + $tagEntity = new Tag(); + $tagEntity->setLabel('tag1'); + + $entry = new Entry(new User()); + $entry->addTag($tagEntity); + + $tagsAssigner->assignTagsToEntry($entry, 'tag1, tag2'); + + $this->assertCount(2, $entry->getTags()); + $this->assertEquals('tag1', $entry->getTags()[0]->getLabel()); + $this->assertEquals('tag2', $entry->getTags()[1]->getLabel()); + } + + public function testAssignTagsNotFlushed() + { + $tagRepo = $this->getTagRepositoryMock(); + $tagRepo->expects($this->never()) + ->method('__call'); + + $tagsAssigner = new TagsAssigner($tagRepo); + + $tagEntity = new Tag(); + $tagEntity->setLabel('tag1'); + + $entry = new Entry(new User()); + + $tagsAssigner->assignTagsToEntry($entry, 'tag1', [$tagEntity]); + + $this->assertCount(1, $entry->getTags()); + $this->assertEquals('tag1', $entry->getTags()[0]->getLabel()); + } + + private function getTagRepositoryMock() + { + return $this->getMockBuilder(TagRepository::class) + ->disableOriginalConstructor() + ->getMock(); + } +} diff --git a/tests/Wallabag/CoreBundle/ParamConverter/UsernameRssTokenConverterTest.php b/tests/Wallabag/CoreBundle/ParamConverter/UsernameRssTokenConverterTest.php index 2e6fccfb..ca8e0d50 100644 --- a/tests/Wallabag/CoreBundle/ParamConverter/UsernameRssTokenConverterTest.php +++ b/tests/Wallabag/CoreBundle/ParamConverter/UsernameRssTokenConverterTest.php @@ -136,7 +136,7 @@ class UsernameRssTokenConverterTest extends \PHPUnit_Framework_TestCase } /** - * @expectedException Symfony\Component\HttpKernel\Exception\NotFoundHttpException + * @expectedException \Symfony\Component\HttpKernel\Exception\NotFoundHttpException * @expectedExceptionMessage User not found */ public function testApplyUserNotFound() diff --git a/tests/Wallabag/CoreBundle/WallabagCoreTestCase.php b/tests/Wallabag/CoreBundle/WallabagCoreTestCase.php index 7bf4b43c..eec6939d 100644 --- a/tests/Wallabag/CoreBundle/WallabagCoreTestCase.php +++ b/tests/Wallabag/CoreBundle/WallabagCoreTestCase.php @@ -2,11 +2,20 @@ namespace Tests\Wallabag\CoreBundle; +use Symfony\Bundle\FrameworkBundle\Client; +use Symfony\Bundle\FrameworkBundle\Console\Application; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Symfony\Component\BrowserKit\Cookie; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Output\NullOutput; +use Wallabag\CoreBundle\Entity\Config; +use Wallabag\UserBundle\Entity\User; abstract class WallabagCoreTestCase extends WebTestCase { + /** + * @var Client|null + */ private $client = null; public function getClient() @@ -21,6 +30,44 @@ abstract class WallabagCoreTestCase extends WebTestCase $this->client = static::createClient(); } + public function resetDatabase(Client $client) + { + $application = new Application($client->getKernel()); + $application->setAutoExit(false); + + $application->run(new ArrayInput([ + 'command' => 'doctrine:schema:drop', + '--no-interaction' => true, + '--force' => true, + '--env' => 'test', + ]), new NullOutput()); + + $application->run(new ArrayInput([ + 'command' => 'doctrine:schema:create', + '--no-interaction' => true, + '--env' => 'test', + ]), new NullOutput()); + + $application->run(new ArrayInput([ + 'command' => 'doctrine:fixtures:load', + '--no-interaction' => true, + '--env' => 'test', + ]), new NullOutput()); + + /* + * Recreate client to avoid error: + * + * [Doctrine\DBAL\ConnectionException] + * Transaction commit failed because the transaction has been marked for rollback only. + */ + $this->client = static::createClient(); + } + + public function getEntityManager() + { + return $this->client->getContainer()->get('doctrine.orm.entity_manager'); + } + /** * Login a user without making a HTTP request. * If we make a HTTP request we lose ability to mock service in the container. @@ -37,7 +84,7 @@ abstract class WallabagCoreTestCase extends WebTestCase $firewallName = $container->getParameter('fos_user.firewall_name'); $user = $userManager->findUserBy(array('username' => $username)); - $loginManager->loginUser($firewallName, $user); + $loginManager->logInUser($firewallName, $user); $session->set('_security_'.$firewallName, serialize($container->get('security.token_storage')->getToken())); $session->save(); @@ -65,22 +112,41 @@ abstract class WallabagCoreTestCase extends WebTestCase } /** - * Return the user id of the logged in user. + * Return the user of the logged in user. * You should be sure that you called `logInAs` before. * - * @return int + * @return User */ - public function getLoggedInUserId() + public function getLoggedInUser() { $token = static::$kernel->getContainer()->get('security.token_storage')->getToken(); if (null !== $token) { - return $token->getUser()->getId(); + return $token->getUser(); } throw new \RuntimeException('No logged in User.'); } + /** + * Return the user id of the logged in user. + * You should be sure that you called `logInAs` before. + * + * @return int + */ + public function getLoggedInUserId() + { + return $this->getLoggedInUser()->getId(); + } + + public function useTheme($theme) + { + $config = $this->getEntityManager()->getRepository(Config::class)->findOneByUser($this->getLoggedInUser()); + $config->setTheme($theme); + $this->getEntityManager()->persist($config); + $this->getEntityManager()->flush(); + } + /** * Check if Redis is installed. * If not, mark test as skip. diff --git a/tests/Wallabag/CoreBundle/fixtures/image-no-content-type.jpg b/tests/Wallabag/CoreBundle/fixtures/image-no-content-type.jpg new file mode 100644 index 00000000..0c60e952 Binary files /dev/null and b/tests/Wallabag/CoreBundle/fixtures/image-no-content-type.jpg differ diff --git a/tests/Wallabag/ImportBundle/Command/ImportCommandTest.php b/tests/Wallabag/ImportBundle/Command/ImportCommandTest.php index 7be1eb18..f507563c 100644 --- a/tests/Wallabag/ImportBundle/Command/ImportCommandTest.php +++ b/tests/Wallabag/ImportBundle/Command/ImportCommandTest.php @@ -10,7 +10,7 @@ use Tests\Wallabag\CoreBundle\WallabagCoreTestCase; class ImportCommandTest extends WallabagCoreTestCase { /** - * @expectedException Symfony\Component\Console\Exception\RuntimeException + * @expectedException \Symfony\Component\Console\Exception\RuntimeException * @expectedExceptionMessage Not enough arguments */ public function testRunImportCommandWithoutArguments() @@ -27,7 +27,7 @@ class ImportCommandTest extends WallabagCoreTestCase } /** - * @expectedException Symfony\Component\Config\Definition\Exception\Exception + * @expectedException \Symfony\Component\Config\Definition\Exception\Exception * @expectedExceptionMessage not found */ public function testRunImportCommandWithoutFilepath() @@ -40,16 +40,15 @@ class ImportCommandTest extends WallabagCoreTestCase $tester = new CommandTester($command); $tester->execute([ 'command' => $command->getName(), - 'userId' => 1, + 'username' => 'admin', 'filepath' => 1, ]); } /** - * @expectedException Symfony\Component\Config\Definition\Exception\Exception - * @expectedExceptionMessage User with id + * @expectedException \Doctrine\ORM\NoResultException */ - public function testRunImportCommandWithoutUserId() + public function testRunImportCommandWithWrongUsername() { $application = new Application($this->getClient()->getKernel()); $application->add(new ImportCommand()); @@ -59,7 +58,7 @@ class ImportCommandTest extends WallabagCoreTestCase $tester = new CommandTester($command); $tester->execute([ 'command' => $command->getName(), - 'userId' => 0, + 'username' => 'random', 'filepath' => './', ]); } @@ -74,7 +73,7 @@ class ImportCommandTest extends WallabagCoreTestCase $tester = new CommandTester($command); $tester->execute([ 'command' => $command->getName(), - 'userId' => 1, + 'username' => 'admin', 'filepath' => $application->getKernel()->getContainer()->getParameter('kernel.root_dir').'/../tests/Wallabag/ImportBundle/fixtures/wallabag-v2-read.json', '--importer' => 'v2', ]); @@ -82,4 +81,21 @@ class ImportCommandTest extends WallabagCoreTestCase $this->assertContains('imported', $tester->getDisplay()); $this->assertContains('already saved', $tester->getDisplay()); } + + public function testRunImportCommandWithUserId() + { + $application = new Application($this->getClient()->getKernel()); + $application->add(new ImportCommand()); + + $command = $application->find('wallabag:import'); + + $tester = new CommandTester($command); + $tester->execute([ + 'command' => $command->getName(), + 'username' => 1, + 'filepath' => $application->getKernel()->getContainer()->getParameter('kernel.root_dir').'/../tests/Wallabag/ImportBundle/fixtures/wallabag-v2-read.json', + '--useUserId' => true, + '--importer' => 'v2', + ]); + } } diff --git a/tests/Wallabag/ImportBundle/Command/RedisWorkerCommandTest.php b/tests/Wallabag/ImportBundle/Command/RedisWorkerCommandTest.php index 74952847..e5e251a0 100644 --- a/tests/Wallabag/ImportBundle/Command/RedisWorkerCommandTest.php +++ b/tests/Wallabag/ImportBundle/Command/RedisWorkerCommandTest.php @@ -11,7 +11,7 @@ use M6Web\Component\RedisMock\RedisMockFactory; class RedisWorkerCommandTest extends WallabagCoreTestCase { /** - * @expectedException Symfony\Component\Console\Exception\RuntimeException + * @expectedException \Symfony\Component\Console\Exception\RuntimeException * @expectedExceptionMessage Not enough arguments (missing: "serviceName") */ public function testRunRedisWorkerCommandWithoutArguments() @@ -28,7 +28,7 @@ class RedisWorkerCommandTest extends WallabagCoreTestCase } /** - * @expectedException Symfony\Component\Config\Definition\Exception\Exception + * @expectedException \Symfony\Component\Config\Definition\Exception\Exception * @expectedExceptionMessage No queue or consumer found for service name */ public function testRunRedisWorkerCommandWithBadService() diff --git a/tests/Wallabag/ImportBundle/Controller/ChromeControllerTest.php b/tests/Wallabag/ImportBundle/Controller/ChromeControllerTest.php index c1f82ea9..8e9f65e3 100644 --- a/tests/Wallabag/ImportBundle/Controller/ChromeControllerTest.php +++ b/tests/Wallabag/ImportBundle/Controller/ChromeControllerTest.php @@ -120,7 +120,7 @@ class ChromeControllerTest extends WallabagCoreTestCase $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->assertEquals(0, count($content->getTags())); + $this->assertEquals(1, count($content->getTags())); $createdAt = $content->getCreatedAt(); $this->assertEquals('2011', $createdAt->format('Y')); diff --git a/tests/Wallabag/ImportBundle/Controller/FirefoxControllerTest.php b/tests/Wallabag/ImportBundle/Controller/FirefoxControllerTest.php index 7557ea32..5354439c 100644 --- a/tests/Wallabag/ImportBundle/Controller/FirefoxControllerTest.php +++ b/tests/Wallabag/ImportBundle/Controller/FirefoxControllerTest.php @@ -121,19 +121,19 @@ class FirefoxControllerTest extends WallabagCoreTestCase $this->assertNotEmpty($content->getMimetype(), 'Mimetype for http://lexpansion.lexpress.fr is ok'); $this->assertNotEmpty($content->getPreviewPicture(), 'Preview picture for http://lexpansion.lexpress.fr is ok'); $this->assertNotEmpty($content->getLanguage(), 'Language for http://lexpansion.lexpress.fr is ok'); - $this->assertEquals(2, count($content->getTags())); + $this->assertEquals(3, count($content->getTags())); $content = $client->getContainer() ->get('doctrine.orm.entity_manager') ->getRepository('WallabagCoreBundle:Entry') ->findByUrlAndUserId( - 'http://stackoverflow.com/questions/15017163/parser-for-exported-bookmarks-html-file-of-google-chrome-and-mozilla-in-java', + 'https://stackoverflow.com/questions/15017163/parser-for-exported-bookmarks-html-file-of-google-chrome-and-mozilla-in-java', $this->getLoggedInUserId() ); - $this->assertNotEmpty($content->getMimetype(), 'Mimetype for http://stackoverflow.com is ok'); - $this->assertNotEmpty($content->getPreviewPicture(), 'Preview picture for http://stackoverflow.com is ok'); - $this->assertEmpty($content->getLanguage(), 'Language for http://stackoverflow.com is ok'); + $this->assertNotEmpty($content->getMimetype(), 'Mimetype for https://stackoverflow.com is ok'); + $this->assertNotEmpty($content->getPreviewPicture(), 'Preview picture for https://stackoverflow.com is ok'); + $this->assertEmpty($content->getLanguage(), 'Language for https://stackoverflow.com is ok'); $createdAt = $content->getCreatedAt(); $this->assertEquals('2013', $createdAt->format('Y')); diff --git a/tests/Wallabag/ImportBundle/Controller/InstapaperControllerTest.php b/tests/Wallabag/ImportBundle/Controller/InstapaperControllerTest.php index 3f6f2b9f..84742e0a 100644 --- a/tests/Wallabag/ImportBundle/Controller/InstapaperControllerTest.php +++ b/tests/Wallabag/ImportBundle/Controller/InstapaperControllerTest.php @@ -107,6 +107,9 @@ class InstapaperControllerTest extends WallabagCoreTestCase $crawler = $client->followRedirect(); + $this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text'])); + $this->assertContains('flashes.import.notice.summary', $body[0]); + $content = $client->getContainer() ->get('doctrine.orm.entity_manager') ->getRepository('WallabagCoreBundle:Entry') @@ -115,14 +118,25 @@ class InstapaperControllerTest extends WallabagCoreTestCase $this->getLoggedInUserId() ); - $this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text'])); - $this->assertContains('flashes.import.notice.summary', $body[0]); - $this->assertNotEmpty($content->getMimetype(), 'Mimetype for http://www.liberation.fr is ok'); $this->assertNotEmpty($content->getPreviewPicture(), 'Preview picture for http://www.liberation.fr is ok'); $this->assertNotEmpty($content->getLanguage(), 'Language for http://www.liberation.fr is ok'); - $this->assertEquals(0, count($content->getTags())); + $this->assertContains('foot', $content->getTags(), 'It includes the "foot" tag'); + $this->assertEquals(1, count($content->getTags())); $this->assertInstanceOf(\DateTime::class, $content->getCreatedAt()); + + $content = $client->getContainer() + ->get('doctrine.orm.entity_manager') + ->getRepository('WallabagCoreBundle:Entry') + ->findByUrlAndUserId( + 'http://www.20minutes.fr/high-tech/2077615-20170531-dis-donc-donald-trump-quoi-exactement-covfefe', + $this->getLoggedInUserId() + ); + + $this->assertContains('foot', $content->getTags()); + $this->assertContains('test_tag', $content->getTags()); + + $this->assertEquals(2, count($content->getTags())); } public function testImportInstapaperWithFileAndMarkAllAsRead() diff --git a/tests/Wallabag/ImportBundle/Controller/PinboardControllerTest.php b/tests/Wallabag/ImportBundle/Controller/PinboardControllerTest.php index 75a7e332..e2b6e7b6 100644 --- a/tests/Wallabag/ImportBundle/Controller/PinboardControllerTest.php +++ b/tests/Wallabag/ImportBundle/Controller/PinboardControllerTest.php @@ -121,7 +121,13 @@ class PinboardControllerTest extends WallabagCoreTestCase $this->assertNotEmpty($content->getMimetype(), 'Mimetype for https://ma.ttias.be is ok'); $this->assertNotEmpty($content->getPreviewPicture(), 'Preview picture for https://ma.ttias.be is ok'); $this->assertNotEmpty($content->getLanguage(), 'Language for https://ma.ttias.be is ok'); - $this->assertEquals(2, count($content->getTags())); + + $tags = $content->getTags(); + $this->assertContains('foot', $tags, 'It includes the "foot" tag'); + $this->assertContains('varnish', $tags, 'It includes the "varnish" tag'); + $this->assertContains('PHP', $tags, 'It includes the "PHP" tag'); + $this->assertEquals(3, count($tags)); + $this->assertInstanceOf(\DateTime::class, $content->getCreatedAt()); $this->assertEquals('2016-10-26', $content->getCreatedAt()->format('Y-m-d')); } diff --git a/tests/Wallabag/ImportBundle/Controller/ReadabilityControllerTest.php b/tests/Wallabag/ImportBundle/Controller/ReadabilityControllerTest.php index acb61ca1..bde0a600 100644 --- a/tests/Wallabag/ImportBundle/Controller/ReadabilityControllerTest.php +++ b/tests/Wallabag/ImportBundle/Controller/ReadabilityControllerTest.php @@ -121,7 +121,11 @@ class ReadabilityControllerTest extends WallabagCoreTestCase $this->assertNotEmpty($content->getMimetype(), 'Mimetype for http://www.zataz.com is ok'); $this->assertNotEmpty($content->getPreviewPicture(), 'Preview picture for http://www.zataz.com is ok'); $this->assertNotEmpty($content->getLanguage(), 'Language for http://www.zataz.com is ok'); - $this->assertEquals(0, count($content->getTags())); + + $tags = $content->getTags(); + $this->assertContains('foot', $tags, 'It includes the "foot" tag'); + $this->assertEquals(1, count($tags)); + $this->assertInstanceOf(\DateTime::class, $content->getCreatedAt()); $this->assertEquals('2016-09-08', $content->getCreatedAt()->format('Y-m-d')); } diff --git a/tests/Wallabag/ImportBundle/Controller/WallabagV1ControllerTest.php b/tests/Wallabag/ImportBundle/Controller/WallabagV1ControllerTest.php index acc39997..2c492c20 100644 --- a/tests/Wallabag/ImportBundle/Controller/WallabagV1ControllerTest.php +++ b/tests/Wallabag/ImportBundle/Controller/WallabagV1ControllerTest.php @@ -112,24 +112,22 @@ class WallabagV1ControllerTest extends WallabagCoreTestCase ->get('doctrine.orm.entity_manager') ->getRepository('WallabagCoreBundle:Entry') ->findByUrlAndUserId( - 'https://framablog.org/2014/02/05/framabag-service-libre-gratuit-interview-developpeur/', + 'http://www.framablog.org/index.php/post/2014/02/05/Framabag-service-libre-gratuit-interview-developpeur', $this->getLoggedInUserId() ); - $tag = $client->getContainer() - ->get('doctrine.orm.entity_manager') - ->getRepository('WallabagCoreBundle:Tag') - ->findOneByLabel('Framabag'); - - $this->assertTrue($content->getTags()->contains($tag)); - $this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text'])); $this->assertContains('flashes.import.notice.summary', $body[0]); - $this->assertNotEmpty($content->getMimetype(), 'Mimetype for http://www.framablog.org is ok'); - $this->assertNotEmpty($content->getPreviewPicture(), 'Preview picture for http://www.framablog.org is ok'); - $this->assertNotEmpty($content->getLanguage(), 'Language for http://www.framablog.org is ok'); - $this->assertEquals(1, count($content->getTags())); + $this->assertEmpty($content->getMimetype(), 'Mimetype for http://www.framablog.org is empty'); + $this->assertEmpty($content->getPreviewPicture(), 'Preview picture for http://www.framablog.org is empty'); + $this->assertEmpty($content->getLanguage(), 'Language for http://www.framablog.org is empty'); + + $tags = $content->getTags(); + $this->assertContains('foot', $tags, 'It includes the "foot" tag'); + $this->assertContains('Framabag', $tags, 'It includes the "Framabag" tag'); + $this->assertEquals(2, count($tags)); + $this->assertInstanceOf(\DateTime::class, $content->getCreatedAt()); } diff --git a/tests/Wallabag/ImportBundle/Controller/WallabagV2ControllerTest.php b/tests/Wallabag/ImportBundle/Controller/WallabagV2ControllerTest.php index 26e2f40b..24893259 100644 --- a/tests/Wallabag/ImportBundle/Controller/WallabagV2ControllerTest.php +++ b/tests/Wallabag/ImportBundle/Controller/WallabagV2ControllerTest.php @@ -119,10 +119,14 @@ class WallabagV2ControllerTest extends WallabagCoreTestCase $this->getLoggedInUserId() ); - $this->assertNotEmpty($content->getMimetype(), 'Mimetype for http://www.liberation.fr is ok'); - $this->assertNotEmpty($content->getPreviewPicture(), 'Preview picture for http://www.liberation.fr is ok'); - $this->assertNotEmpty($content->getLanguage(), 'Language for http://www.liberation.fr is ok'); - $this->assertEquals(0, count($content->getTags())); + // empty because it wasn't re-imported + $this->assertEmpty($content->getMimetype(), 'Mimetype for http://www.liberation.fr is empty'); + $this->assertEmpty($content->getPreviewPicture(), 'Preview picture for http://www.liberation.fr is empty'); + $this->assertEmpty($content->getLanguage(), 'Language for http://www.liberation.fr is empty'); + + $tags = $content->getTags(); + $this->assertContains('foot', $tags, 'It includes the "foot" tag'); + $this->assertEquals(1, count($tags)); $content = $client->getContainer() ->get('doctrine.orm.entity_manager') @@ -135,9 +139,16 @@ class WallabagV2ControllerTest extends WallabagCoreTestCase $this->assertNotEmpty($content->getMimetype(), 'Mimetype for https://www.mediapart.fr is ok'); $this->assertNotEmpty($content->getPreviewPicture(), 'Preview picture for https://www.mediapart.fr is ok'); $this->assertNotEmpty($content->getLanguage(), 'Language for https://www.mediapart.fr is ok'); - $this->assertEquals(2, count($content->getTags())); + + $tags = $content->getTags(); + $this->assertContains('foot', $tags, 'It includes the "foot" tag'); + $this->assertContains('mediapart', $tags, 'It includes the "mediapart" tag'); + $this->assertContains('blog', $tags, 'It includes the "blog" tag'); + $this->assertEquals(3, count($tags)); + $this->assertInstanceOf(\DateTime::class, $content->getCreatedAt()); $this->assertEquals('2016-09-08', $content->getCreatedAt()->format('Y-m-d')); + $this->assertTrue($content->isStarred(), 'Entry is starred'); } public function testImportWallabagWithEmptyFile() diff --git a/tests/Wallabag/ImportBundle/Import/ChromeImportTest.php b/tests/Wallabag/ImportBundle/Import/ChromeImportTest.php index 6b3adda4..cec19534 100644 --- a/tests/Wallabag/ImportBundle/Import/ChromeImportTest.php +++ b/tests/Wallabag/ImportBundle/Import/ChromeImportTest.php @@ -17,6 +17,7 @@ class ChromeImportTest extends \PHPUnit_Framework_TestCase protected $em; protected $logHandler; protected $contentProxy; + protected $tagsAssigner; private function getChromeImport($unsetUser = false, $dispatched = 0) { @@ -30,6 +31,10 @@ class ChromeImportTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->getMock(); + $this->tagsAssigner = $this->getMockBuilder('Wallabag\CoreBundle\Helper\TagsAssigner') + ->disableOriginalConstructor() + ->getMock(); + $dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcher') ->disableOriginalConstructor() ->getMock(); @@ -38,7 +43,7 @@ class ChromeImportTest extends \PHPUnit_Framework_TestCase ->expects($this->exactly($dispatched)) ->method('dispatch'); - $wallabag = new ChromeImport($this->em, $this->contentProxy, $dispatcher); + $wallabag = new ChromeImport($this->em, $this->contentProxy, $this->tagsAssigner, $dispatcher); $this->logHandler = new TestHandler(); $logger = new Logger('test', [$this->logHandler]); diff --git a/tests/Wallabag/ImportBundle/Import/FirefoxImportTest.php b/tests/Wallabag/ImportBundle/Import/FirefoxImportTest.php index b516fbc5..c186c820 100644 --- a/tests/Wallabag/ImportBundle/Import/FirefoxImportTest.php +++ b/tests/Wallabag/ImportBundle/Import/FirefoxImportTest.php @@ -17,6 +17,7 @@ class FirefoxImportTest extends \PHPUnit_Framework_TestCase protected $em; protected $logHandler; protected $contentProxy; + protected $tagsAssigner; private function getFirefoxImport($unsetUser = false, $dispatched = 0) { @@ -30,6 +31,10 @@ class FirefoxImportTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->getMock(); + $this->tagsAssigner = $this->getMockBuilder('Wallabag\CoreBundle\Helper\TagsAssigner') + ->disableOriginalConstructor() + ->getMock(); + $dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcher') ->disableOriginalConstructor() ->getMock(); @@ -38,7 +43,7 @@ class FirefoxImportTest extends \PHPUnit_Framework_TestCase ->expects($this->exactly($dispatched)) ->method('dispatch'); - $wallabag = new FirefoxImport($this->em, $this->contentProxy, $dispatcher); + $wallabag = new FirefoxImport($this->em, $this->contentProxy, $this->tagsAssigner, $dispatcher); $this->logHandler = new TestHandler(); $logger = new Logger('test', [$this->logHandler]); diff --git a/tests/Wallabag/ImportBundle/Import/InstapaperImportTest.php b/tests/Wallabag/ImportBundle/Import/InstapaperImportTest.php index e262a808..9158c8a2 100644 --- a/tests/Wallabag/ImportBundle/Import/InstapaperImportTest.php +++ b/tests/Wallabag/ImportBundle/Import/InstapaperImportTest.php @@ -17,6 +17,8 @@ class InstapaperImportTest extends \PHPUnit_Framework_TestCase protected $em; protected $logHandler; protected $contentProxy; + protected $tagsAssigner; + protected $uow; private function getInstapaperImport($unsetUser = false, $dispatched = 0) { @@ -26,10 +28,28 @@ class InstapaperImportTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->getMock(); + $this->uow = $this->getMockBuilder('Doctrine\ORM\UnitOfWork') + ->disableOriginalConstructor() + ->getMock(); + + $this->em + ->expects($this->any()) + ->method('getUnitOfWork') + ->willReturn($this->uow); + + $this->uow + ->expects($this->any()) + ->method('getScheduledEntityInsertions') + ->willReturn([]); + $this->contentProxy = $this->getMockBuilder('Wallabag\CoreBundle\Helper\ContentProxy') ->disableOriginalConstructor() ->getMock(); + $this->tagsAssigner = $this->getMockBuilder('Wallabag\CoreBundle\Helper\TagsAssigner') + ->disableOriginalConstructor() + ->getMock(); + $dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcher') ->disableOriginalConstructor() ->getMock(); @@ -38,7 +58,7 @@ class InstapaperImportTest extends \PHPUnit_Framework_TestCase ->expects($this->exactly($dispatched)) ->method('dispatch'); - $import = new InstapaperImport($this->em, $this->contentProxy, $dispatcher); + $import = new InstapaperImport($this->em, $this->contentProxy, $this->tagsAssigner, $dispatcher); $this->logHandler = new TestHandler(); $logger = new Logger('test', [$this->logHandler]); @@ -62,14 +82,14 @@ class InstapaperImportTest extends \PHPUnit_Framework_TestCase public function testImport() { - $instapaperImport = $this->getInstapaperImport(false, 3); + $instapaperImport = $this->getInstapaperImport(false, 4); $instapaperImport->setFilepath(__DIR__.'/../fixtures/instapaper-export.csv'); $entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository') ->disableOriginalConstructor() ->getMock(); - $entryRepo->expects($this->exactly(3)) + $entryRepo->expects($this->exactly(4)) ->method('findByUrlAndUserId') ->willReturn(false); @@ -83,14 +103,14 @@ class InstapaperImportTest extends \PHPUnit_Framework_TestCase ->getMock(); $this->contentProxy - ->expects($this->exactly(3)) + ->expects($this->exactly(4)) ->method('updateEntry') ->willReturn($entry); $res = $instapaperImport->import(); $this->assertTrue($res); - $this->assertEquals(['skipped' => 0, 'imported' => 3, 'queued' => 0], $instapaperImport->getSummary()); + $this->assertEquals(['skipped' => 0, 'imported' => 4, 'queued' => 0], $instapaperImport->getSummary()); } public function testImportAndMarkAllAsRead() @@ -102,9 +122,9 @@ class InstapaperImportTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->getMock(); - $entryRepo->expects($this->exactly(3)) + $entryRepo->expects($this->exactly(4)) ->method('findByUrlAndUserId') - ->will($this->onConsecutiveCalls(false, true, true)); + ->will($this->onConsecutiveCalls(false, true, true, true)); $this->em ->expects($this->any()) @@ -128,7 +148,7 @@ class InstapaperImportTest extends \PHPUnit_Framework_TestCase $this->assertTrue($res); - $this->assertEquals(['skipped' => 2, 'imported' => 1, 'queued' => 0], $instapaperImport->getSummary()); + $this->assertEquals(['skipped' => 3, 'imported' => 1, 'queued' => 0], $instapaperImport->getSummary()); } public function testImportWithRabbit() @@ -160,7 +180,7 @@ class InstapaperImportTest extends \PHPUnit_Framework_TestCase ->getMock(); $producer - ->expects($this->exactly(3)) + ->expects($this->exactly(4)) ->method('publish'); $instapaperImport->setProducer($producer); @@ -168,7 +188,7 @@ class InstapaperImportTest extends \PHPUnit_Framework_TestCase $res = $instapaperImport->setMarkAsRead(true)->import(); $this->assertTrue($res); - $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 3], $instapaperImport->getSummary()); + $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 4], $instapaperImport->getSummary()); } public function testImportWithRedis() @@ -206,7 +226,7 @@ class InstapaperImportTest extends \PHPUnit_Framework_TestCase $res = $instapaperImport->setMarkAsRead(true)->import(); $this->assertTrue($res); - $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 3], $instapaperImport->getSummary()); + $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 4], $instapaperImport->getSummary()); $this->assertNotEmpty($redisMock->lpop('instapaper')); } diff --git a/tests/Wallabag/ImportBundle/Import/PocketImportTest.php b/tests/Wallabag/ImportBundle/Import/PocketImportTest.php index 141ece36..b81ebe15 100644 --- a/tests/Wallabag/ImportBundle/Import/PocketImportTest.php +++ b/tests/Wallabag/ImportBundle/Import/PocketImportTest.php @@ -23,6 +23,8 @@ class PocketImportTest extends \PHPUnit_Framework_TestCase protected $em; protected $contentProxy; protected $logHandler; + protected $tagsAssigner; + protected $uow; private function getPocketImport($consumerKey = 'ConsumerKey', $dispatched = 0) { @@ -37,6 +39,10 @@ class PocketImportTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->getMock(); + $this->tagsAssigner = $this->getMockBuilder('Wallabag\CoreBundle\Helper\TagsAssigner') + ->disableOriginalConstructor() + ->getMock(); + $this->em = $this->getMockBuilder('Doctrine\ORM\EntityManager') ->disableOriginalConstructor() ->getMock(); @@ -63,7 +69,7 @@ class PocketImportTest extends \PHPUnit_Framework_TestCase ->expects($this->exactly($dispatched)) ->method('dispatch'); - $pocket = new PocketImport($this->em, $this->contentProxy, $dispatcher); + $pocket = new PocketImport($this->em, $this->contentProxy, $this->tagsAssigner, $dispatcher); $pocket->setUser($this->user); $this->logHandler = new TestHandler(); diff --git a/tests/Wallabag/ImportBundle/Import/ReadabilityImportTest.php b/tests/Wallabag/ImportBundle/Import/ReadabilityImportTest.php index d1bbe648..8f466d38 100644 --- a/tests/Wallabag/ImportBundle/Import/ReadabilityImportTest.php +++ b/tests/Wallabag/ImportBundle/Import/ReadabilityImportTest.php @@ -17,6 +17,7 @@ class ReadabilityImportTest extends \PHPUnit_Framework_TestCase protected $em; protected $logHandler; protected $contentProxy; + protected $tagsAssigner; private function getReadabilityImport($unsetUser = false, $dispatched = 0) { @@ -30,6 +31,10 @@ class ReadabilityImportTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->getMock(); + $this->tagsAssigner = $this->getMockBuilder('Wallabag\CoreBundle\Helper\TagsAssigner') + ->disableOriginalConstructor() + ->getMock(); + $dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcher') ->disableOriginalConstructor() ->getMock(); @@ -38,7 +43,7 @@ class ReadabilityImportTest extends \PHPUnit_Framework_TestCase ->expects($this->exactly($dispatched)) ->method('dispatch'); - $wallabag = new ReadabilityImport($this->em, $this->contentProxy, $dispatcher); + $wallabag = new ReadabilityImport($this->em, $this->contentProxy, $this->tagsAssigner, $dispatcher); $this->logHandler = new TestHandler(); $logger = new Logger('test', [$this->logHandler]); @@ -62,14 +67,14 @@ class ReadabilityImportTest extends \PHPUnit_Framework_TestCase public function testImport() { - $readabilityImport = $this->getReadabilityImport(false, 24); + $readabilityImport = $this->getReadabilityImport(false, 3); $readabilityImport->setFilepath(__DIR__.'/../fixtures/readability.json'); $entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository') ->disableOriginalConstructor() ->getMock(); - $entryRepo->expects($this->exactly(24)) + $entryRepo->expects($this->exactly(3)) ->method('findByUrlAndUserId') ->willReturn(false); @@ -83,14 +88,14 @@ class ReadabilityImportTest extends \PHPUnit_Framework_TestCase ->getMock(); $this->contentProxy - ->expects($this->exactly(24)) + ->expects($this->exactly(3)) ->method('updateEntry') ->willReturn($entry); $res = $readabilityImport->import(); $this->assertTrue($res); - $this->assertEquals(['skipped' => 0, 'imported' => 24, 'queued' => 0], $readabilityImport->getSummary()); + $this->assertEquals(['skipped' => 0, 'imported' => 3, 'queued' => 0], $readabilityImport->getSummary()); } public function testImportAndMarkAllAsRead() @@ -160,7 +165,7 @@ class ReadabilityImportTest extends \PHPUnit_Framework_TestCase ->getMock(); $producer - ->expects($this->exactly(24)) + ->expects($this->exactly(3)) ->method('publish'); $readabilityImport->setProducer($producer); @@ -168,7 +173,7 @@ class ReadabilityImportTest extends \PHPUnit_Framework_TestCase $res = $readabilityImport->setMarkAsRead(true)->import(); $this->assertTrue($res); - $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 24], $readabilityImport->getSummary()); + $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 3], $readabilityImport->getSummary()); } public function testImportWithRedis() @@ -206,7 +211,7 @@ class ReadabilityImportTest extends \PHPUnit_Framework_TestCase $res = $readabilityImport->setMarkAsRead(true)->import(); $this->assertTrue($res); - $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 24], $readabilityImport->getSummary()); + $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 3], $readabilityImport->getSummary()); $this->assertNotEmpty($redisMock->lpop('readability')); } diff --git a/tests/Wallabag/ImportBundle/Import/WallabagV1ImportTest.php b/tests/Wallabag/ImportBundle/Import/WallabagV1ImportTest.php index 4dbced60..834b7ef5 100644 --- a/tests/Wallabag/ImportBundle/Import/WallabagV1ImportTest.php +++ b/tests/Wallabag/ImportBundle/Import/WallabagV1ImportTest.php @@ -17,6 +17,10 @@ class WallabagV1ImportTest extends \PHPUnit_Framework_TestCase protected $em; protected $logHandler; protected $contentProxy; + protected $tagsAssigner; + protected $uow; + protected $fetchingErrorMessageTitle = 'No title found'; + protected $fetchingErrorMessage = 'wallabag can\'t retrieve contents for this article. Please troubleshoot this issue.'; private function getWallabagV1Import($unsetUser = false, $dispatched = 0) { @@ -44,6 +48,10 @@ class WallabagV1ImportTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->getMock(); + $this->tagsAssigner = $this->getMockBuilder('Wallabag\CoreBundle\Helper\TagsAssigner') + ->disableOriginalConstructor() + ->getMock(); + $dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcher') ->disableOriginalConstructor() ->getMock(); @@ -52,7 +60,14 @@ class WallabagV1ImportTest extends \PHPUnit_Framework_TestCase ->expects($this->exactly($dispatched)) ->method('dispatch'); - $wallabag = new WallabagV1Import($this->em, $this->contentProxy, $dispatcher); + $wallabag = new WallabagV1Import( + $this->em, + $this->contentProxy, + $this->tagsAssigner, + $dispatcher, + $this->fetchingErrorMessageTitle, + $this->fetchingErrorMessage + ); $this->logHandler = new TestHandler(); $logger = new Logger('test', [$this->logHandler]); @@ -76,14 +91,14 @@ class WallabagV1ImportTest extends \PHPUnit_Framework_TestCase public function testImport() { - $wallabagV1Import = $this->getWallabagV1Import(false, 3); + $wallabagV1Import = $this->getWallabagV1Import(false, 1); $wallabagV1Import->setFilepath(__DIR__.'/../fixtures/wallabag-v1.json'); $entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository') ->disableOriginalConstructor() ->getMock(); - $entryRepo->expects($this->exactly(4)) + $entryRepo->expects($this->exactly(2)) ->method('findByUrlAndUserId') ->will($this->onConsecutiveCalls(false, true, false, false)); @@ -97,14 +112,14 @@ class WallabagV1ImportTest extends \PHPUnit_Framework_TestCase ->getMock(); $this->contentProxy - ->expects($this->exactly(3)) + ->expects($this->exactly(1)) ->method('updateEntry') ->willReturn($entry); $res = $wallabagV1Import->import(); $this->assertTrue($res); - $this->assertEquals(['skipped' => 1, 'imported' => 3, 'queued' => 0], $wallabagV1Import->getSummary()); + $this->assertEquals(['skipped' => 1, 'imported' => 1, 'queued' => 0], $wallabagV1Import->getSummary()); } public function testImportAndMarkAllAsRead() @@ -174,7 +189,7 @@ class WallabagV1ImportTest extends \PHPUnit_Framework_TestCase ->getMock(); $producer - ->expects($this->exactly(4)) + ->expects($this->exactly(2)) ->method('publish'); $wallabagV1Import->setProducer($producer); @@ -182,7 +197,7 @@ class WallabagV1ImportTest extends \PHPUnit_Framework_TestCase $res = $wallabagV1Import->setMarkAsRead(true)->import(); $this->assertTrue($res); - $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 4], $wallabagV1Import->getSummary()); + $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 2], $wallabagV1Import->getSummary()); } public function testImportWithRedis() @@ -220,7 +235,7 @@ class WallabagV1ImportTest extends \PHPUnit_Framework_TestCase $res = $wallabagV1Import->setMarkAsRead(true)->import(); $this->assertTrue($res); - $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 4], $wallabagV1Import->getSummary()); + $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 2], $wallabagV1Import->getSummary()); $this->assertNotEmpty($redisMock->lpop('wallabag_v1')); } diff --git a/tests/Wallabag/ImportBundle/Import/WallabagV2ImportTest.php b/tests/Wallabag/ImportBundle/Import/WallabagV2ImportTest.php index 0e50b8b2..5cc04aa5 100644 --- a/tests/Wallabag/ImportBundle/Import/WallabagV2ImportTest.php +++ b/tests/Wallabag/ImportBundle/Import/WallabagV2ImportTest.php @@ -17,6 +17,8 @@ class WallabagV2ImportTest extends \PHPUnit_Framework_TestCase protected $em; protected $logHandler; protected $contentProxy; + protected $tagsAssigner; + protected $uow; private function getWallabagV2Import($unsetUser = false, $dispatched = 0) { @@ -44,6 +46,10 @@ class WallabagV2ImportTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->getMock(); + $this->tagsAssigner = $this->getMockBuilder('Wallabag\CoreBundle\Helper\TagsAssigner') + ->disableOriginalConstructor() + ->getMock(); + $dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcher') ->disableOriginalConstructor() ->getMock(); @@ -52,7 +58,7 @@ class WallabagV2ImportTest extends \PHPUnit_Framework_TestCase ->expects($this->exactly($dispatched)) ->method('dispatch'); - $wallabag = new WallabagV2Import($this->em, $this->contentProxy, $dispatcher); + $wallabag = new WallabagV2Import($this->em, $this->contentProxy, $this->tagsAssigner, $dispatcher); $this->logHandler = new TestHandler(); $logger = new Logger('test', [$this->logHandler]); @@ -83,7 +89,7 @@ class WallabagV2ImportTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->getMock(); - $entryRepo->expects($this->exactly(24)) + $entryRepo->expects($this->exactly(6)) ->method('findByUrlAndUserId') ->will($this->onConsecutiveCalls(false, true, false)); @@ -100,7 +106,7 @@ class WallabagV2ImportTest extends \PHPUnit_Framework_TestCase $res = $wallabagV2Import->import(); $this->assertTrue($res); - $this->assertEquals(['skipped' => 22, 'imported' => 2, 'queued' => 0], $wallabagV2Import->getSummary()); + $this->assertEquals(['skipped' => 4, 'imported' => 2, 'queued' => 0], $wallabagV2Import->getSummary()); } public function testImportAndMarkAllAsRead() @@ -166,7 +172,7 @@ class WallabagV2ImportTest extends \PHPUnit_Framework_TestCase ->getMock(); $producer - ->expects($this->exactly(24)) + ->expects($this->exactly(6)) ->method('publish'); $wallabagV2Import->setProducer($producer); @@ -174,7 +180,7 @@ class WallabagV2ImportTest extends \PHPUnit_Framework_TestCase $res = $wallabagV2Import->setMarkAsRead(true)->import(); $this->assertTrue($res); - $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 24], $wallabagV2Import->getSummary()); + $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 6], $wallabagV2Import->getSummary()); } public function testImportWithRedis() @@ -208,7 +214,7 @@ class WallabagV2ImportTest extends \PHPUnit_Framework_TestCase $res = $wallabagV2Import->setMarkAsRead(true)->import(); $this->assertTrue($res); - $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 24], $wallabagV2Import->getSummary()); + $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 6], $wallabagV2Import->getSummary()); $this->assertNotEmpty($redisMock->lpop('wallabag_v2')); } @@ -261,7 +267,7 @@ class WallabagV2ImportTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->getMock(); - $entryRepo->expects($this->exactly(24)) + $entryRepo->expects($this->exactly(6)) ->method('findByUrlAndUserId') ->will($this->onConsecutiveCalls(false, true, false)); @@ -278,6 +284,6 @@ class WallabagV2ImportTest extends \PHPUnit_Framework_TestCase $res = $wallabagV2Import->import(); $this->assertTrue($res); - $this->assertEquals(['skipped' => 22, 'imported' => 2, 'queued' => 0], $wallabagV2Import->getSummary()); + $this->assertEquals(['skipped' => 4, 'imported' => 2, 'queued' => 0], $wallabagV2Import->getSummary()); } } diff --git a/tests/Wallabag/ImportBundle/fixtures/instapaper-export.csv b/tests/Wallabag/ImportBundle/fixtures/instapaper-export.csv index 28a4c8e6..1a648f8a 100644 --- a/tests/Wallabag/ImportBundle/fixtures/instapaper-export.csv +++ b/tests/Wallabag/ImportBundle/fixtures/instapaper-export.csv @@ -2,3 +2,4 @@ URL,Title,Selection,Folder http://www.liberation.fr/societe/2012/12/06/baumettes-un-tour-en-cellule_865551,Baumettes : un tour en cellule,,Unread https://redditblog.com/2016/09/20/amp-and-reactredux/,AMP and React+Redux: Why Not?,,Archive https://medium.com/@the_minh/why-foursquare-swarm-is-still-my-favourite-social-network-e38228493e6c,Why Foursquare / Swarm is still my favourite social network,,Starred +http://www.20minutes.fr/high-tech/2077615-20170531-dis-donc-donald-trump-quoi-exactement-covfefe,"Dis donc Donald Trump, c'est quoi exactement «covfefe»?",,test_tag diff --git a/tests/Wallabag/ImportBundle/fixtures/pinboard_export b/tests/Wallabag/ImportBundle/fixtures/pinboard_export index 2dd744d3..b5f653e7 100644 --- a/tests/Wallabag/ImportBundle/fixtures/pinboard_export +++ b/tests/Wallabag/ImportBundle/fixtures/pinboard_export @@ -1,5 +1,3 @@ [{"href":"https:\/\/developers.google.com\/web\/updates\/2016\/07\/infinite-scroller","description":"Complexities of an Infinite Scroller","extended":"TL;DR: Re-use your DOM elements and remove the ones that are far away from the viewport. Use placeholders to account for delayed data","meta":"21ff61c6f648901168f9e6119f53df7d","hash":"e69b65724cca1c585b446d4c47865d76","time":"2016-10-31T15:57:56Z","shared":"yes","toread":"no","tags":"infinite dom performance scroll"}, {"href":"https:\/\/ma.ttias.be\/varnish-explained\/","description":"Varnish (explained) for PHP developers","extended":"A few months ago, I gave a presentation at LaraconEU in Amsterdam titled \"Varnish for PHP developers\". The generic title of that presentation is actually Varnish Explained and this is a write-up of that presentation, the video and the slides.","meta":"d32ad9fac2ed29da4aec12c562e9afb1","hash":"21dd6bdda8ad62666a2c9e79f6e80f98","time":"2016-10-26T06:43:03Z","shared":"yes","toread":"no","tags":"varnish PHP"}, -{"href":"https:\/\/ilia.ws\/files\/nginx_torontophpug.pdf","description":"Nginx Tricks for PHP Developers","extended":"","meta":"9adbb5c4ca6760e335b920800d88c70a","hash":"0189bb08f8bd0122c6544bed4624c546","time":"2016-10-05T07:11:27Z","shared":"yes","toread":"no","tags":"nginx PHP best_practice"}, -{"href":"https:\/\/jolicode.com\/blog\/starting-a-mobile-application-with-react-native","description":"Starting a mobile application with React Native","extended":"While preparing our next React Native training, I learnt a lot on the library and discovered an amazing community with a lot of packages.","meta":"bd140bd3e53e3a0b4cb08cdaf64bcbfc","hash":"015fa10cd97f56186420555e52cfab62","time":"2016-09-23T10:58:20Z","shared":"yes","toread":"no","tags":"react-native"}, -{"href":"http:\/\/open.blogs.nytimes.com\/2016\/08\/29\/testing-varnish-using-varnishtest\/","description":"Testing Varnish Using Varnishtest","extended":"Varnish ships with the ability to test using the testing tool varnishtest. Varnishtest gives you the ability to write VCL tests you can run on the command line or as part of your build process.","meta":"ca2752a07adea4bab52cd19e8fdbf356","hash":"d3e642cc1274d10e4c12ee31f5dde736","time":"2016-08-30T09:33:24Z","shared":"yes","toread":"no","tags":"varnish test vcl"}] +{"href":"https:\/\/ilia.ws\/files\/nginx_torontophpug.pdf","description":"Nginx Tricks for PHP Developers","extended":"","meta":"9adbb5c4ca6760e335b920800d88c70a","hash":"0189bb08f8bd0122c6544bed4624c546","time":"2016-10-05T07:11:27Z","shared":"yes","toread":"no","tags":"nginx PHP best_practice"}] diff --git a/tests/Wallabag/ImportBundle/fixtures/readability.json b/tests/Wallabag/ImportBundle/fixtures/readability.json index 32f6fa53..b9fd570d 100644 --- a/tests/Wallabag/ImportBundle/fixtures/readability.json +++ b/tests/Wallabag/ImportBundle/fixtures/readability.json @@ -10,13 +10,6 @@ "article__title": "We Looked At 167,943 Tweets & Found Out Hashtags Are Worthless", "archive": false }, - { - "article__title": "Réfugiés: l'UE va créer 100 000 places d'accueil dans les Balkans", - "article__url": "http://www.liberation.fr/planete/2015/10/26/refugies-l-ue-va-creer-100-000-places-d-accueil-dans-les-balkans_1408867", - "archive": false, - "date_added": "2016-09-08T11:55:58+0200", - "favorite": false - }, { "article__title": "No title found", "article__url": "http://news.nationalgeographic.com/2016/02/160211-albatrosses-mothers-babies-animals-science/&sf20739758=1", @@ -24,152 +17,12 @@ "date_added": "2016-09-08T11:55:58+0200", "favorite": true }, - { - "archive": 0, - "date_added": "2016-09-08T11:55:58+0200", - "favorite": 0, - "article__title": "Échecs", - "article__url": "https://fr.wikipedia.org/wiki/Échecs" - }, { "archive": 0, "date_added": "2016-09-08T11:55:58+0200", "favorite": 0, "article__title": "90% des dossiers médicaux des Coréens du sud vendus à des entreprises privées - ZATAZ", "article__url": "http://www.zataz.com/90-des-dossiers-medicaux-des-coreens-du-sud-vendus-a-des-entreprises-privees/" - }, - { - "archive": 0, - "date_added": "2016-09-08T11:55:58+0200", - "favorite": 0, - "article__title": "Mass Surveillance As Art", - "article__url": "https://www.nationaljournal.com/s/73311/mass-surveillance-art" - }, - { - "archive": 0, - "date_added": "2016-09-08T11:55:58+0200", - "favorite": 0, - "article__title": "What David Cameron did to the pig, his party is now doing to the country", - "article__url": "http://www.newstatesman.com/2015/09/what-david-cameron-did-pig-his-party-now-doing-country" - }, - { - "archive": 1, - "date_added": "2016-09-08T11:55:58+0200", - "favorite": 0, - "article__title": "CLICK HERE to support 2016 CES Winner, Revolutionary Auto-Tracking Robot", - "article__url": "https://www.indiegogo.com/projects/2016-ces-winner-revolutionary-auto-tracking-robot" - }, - { - "archive": 0, - "date_added": "2016-09-08T11:55:58+0200", - "favorite": 1, - "article__title": "No title found", - "article__url": "http://carnetdevol.shost.ca/wordpress/aide-memoire-sur-les-commandes-associees-a-systemd/" - }, - { - "archive": 1, - "date_added": "2016-09-08T11:55:58+0200", - "favorite": 0, - "article__title": "Présentation d'Arduino - Tuto Arduino - Le blog d'Eskimon", - "article__url": "http://eskimon.fr/73-arduino-101-presentation" - }, - { - "archive": 1, - "date_added": "2016-09-08T11:55:58+0200", - "favorite": 0, - "article__title": "Lenovo ThinkPad X1 Carbon Ultrabook Review", - "article__url": "http://www.notebookcheck.net/Lenovo-ThinkPad-X1-Carbon-Ultrabook-Review.138033.0.html" - }, - { - "archive": 0, - "date_added": "2016-09-08T11:55:58+0200", - "favorite": 0, - "article__title": "Visitons le Château de Landsberg !", - "article__url": "http://autour-du-mont-sainte-odile.overblog.com/2016/01/visitons-le-chateau-de-landsberg.html" - }, - { - "archive": 1, - "date_added": "2016-09-08T11:55:58+0200", - "favorite": 0, - "article__title": "Contrer les stéréotypes par les livres : “C'est dès l'enfance qu'ils se construisent”", - "article__url": "https://www.actualitte.com/article/monde-edition/contrer-les-stereotypes-par-les-livres-c-est-des-l-enfance-qu-ils-se-construisent/64058" - }, - { - "archive": 1, - "date_added": "2016-09-08T11:55:58+0200", - "favorite": 0, - "article__title": "[ROM][6.0.1][Layers][N5] TipsyOS official builds {UBER TCs}", - "article__url": "http://forum.xda-developers.com/google-nexus-5/development/rom-tipsyos-official-builds-uber-tcs-t3325989" - }, - { - "archive": 0, - "date_added": "2016-09-08T11:55:58+0200", - "favorite": 0, - "article__title": "Top 15 Podcasts All Web Developers Should Follow - Envato Tuts+ Code Article", - "article__url": "http://code.tutsplus.com/articles/top-15-podcasts-all-web-developers-should-follow--net-14461" - }, - { - "archive": 1, - "date_added": "2016-09-08T11:55:58+0200", - "favorite": 0, - "article__title": "University of Mississippi", - "article__url": "http://olemiss.edu" - }, - { - "archive": 1, - "date_added": "2016-09-08T11:55:58+0200", - "favorite": 0, - "article__title": "FinnChristiansen.de Jetzt Dank Let’s Encrypt Per HTTPS Erreichbar", - "article__url": "https://www.finnchristiansen.de/2015/12/06/finnchristiansen-de-jetzt-dank-lets-encrypt-per-https-erreichbar/" - }, - { - "archive": 1, - "date_added": "2016-09-08T11:55:58+0200", - "favorite": 0, - "article__title": "Le développeur et l'ingénierie logicielle", - "article__url": "http://wemucs.com/le-developpeur-et-lingenierie-logicielle/" - }, - { - "archive": 1, - "date_added": "2016-09-08T11:55:58+0200", - "favorite": 0, - "article__title": "The Role of Methylation in Gene Expression", - "article__url": "http://www.nature.com/scitable/topicpage/the-role-of-methylation-in-gene-expression-1070" - }, - { - "archive": 1, - "date_added": "2016-09-08T11:55:58+0200", - "favorite": 0, - "article__title": "E-Mail-Adresse kostenlos, FreeMail, De-Mail & Nachrichten", - "article__url": "http://web.de" - }, - { - "archive": 1, - "date_added": "2016-09-08T11:55:58+0200", - "favorite": 0, - "article__title": "OpenSSH Server on Arch Linux | DominicM test", - "article__url": "http://dominicm.com/openssh-server-arch-linux/" - }, - { - "archive": 1, - "date_added": "2016-09-08T11:55:58+0200", - "favorite": 0, - "article__title": "Site Moved | Site Help", - "article__url": "http://g1.com/help/sitemoved.asp" - }, - { - "archive": 1, - "date_added": "2016-09-08T11:55:58+0200", - "favorite": 0, - "article__title": "#Maroc : le stylo anti-pédophiles EAGLE d’AMESYS est moins bien configuré que les faux-lowers Twitter du roi Mohammed VI", - "article__url": "https://reflets.info/maroc-le-stylo-anti-pedophiles-eagle-damesys-est-moins-bien-configure-que-les-faux-lowers-twitter-du-roi-mohammed-vi/" - }, - { - "archive": 1, - "date_added": "2016-09-08T11:55:58+0200", - "favorite": 0, - "article__title": "Simple Cloud Infrastructure for Developers", - "article__url": "https://www.digitalocean.com/" } ], "recommendations": [] diff --git a/tests/Wallabag/ImportBundle/fixtures/wallabag-v1.json b/tests/Wallabag/ImportBundle/fixtures/wallabag-v1.json index f298469f..28270ec4 100644 --- a/tests/Wallabag/ImportBundle/fixtures/wallabag-v1.json +++ b/tests/Wallabag/ImportBundle/fixtures/wallabag-v1.json @@ -32,38 +32,5 @@ "content": "README.md

    wallabag is a self hostable application allowing you to not miss any content anymore. Click, save, read it when you can. It extracts content so that you can read it when you have time.

    \n

    More informations on our website: wallabag.org

    \n

    License

    \n

    Copyright © 2010-2014 Nicolas Lœuillet nicolas@loeuillet.org This work is free. You can redistribute it and/or modify it under the terms of the Do What The Fuck You Want To Public License, Version 2, as published by Sam Hocevar. See the COPYING file for more details.

    \n", "user_id": "1", "tags":"" - }, - { - "0": "3", - "1": "a self hostable application for saving web pages | wallabag", - "2": "https://www.wallabag.org/", - "3": "1", - "4": "0", - "5": "\n
    \n
    \n

    wallabag (formerly poche) is a self hostable application for saving web pages. Unlike other services, wallabag is free (as in freedom) and open source.

    \n
    \n\n
    \n
    \n
    \n

    With this application you will not miss content anymore. Click, save, read it when you want. It saves the content you select so that you can read it when you have time.

    \n
    \n\n
    \n
    \n
    \n

    How it works

    \n

    Thanks to the bookmarklet or third-party applications, you save an article in your wallabag to read it later. Then, when you open your wallabag, you can comfortably read your articles.

    \n

    How to use wallabag

    \n

    There are two ways to use wallabag: you can install it on your web server or you can create an account at Framabag (we install and upgrade wallabag for you).

    \n
    \n\n
    \n", - "6": "1", - "id": "3", - "title": "a self hostable application for saving web pages | wallabag", - "url": "https://www.wallabag.org/", - "is_read": "1", - "is_fav": "0", - "content": "\n
    \n
    \n

    wallabag (formerly poche) is a self hostable application for saving web pages. Unlike other services, wallabag is free (as in freedom) and open source.

    \n
    \n\n
    \n
    \n
    \n

    With this application you will not miss content anymore. Click, save, read it when you want. It saves the content you select so that you can read it when you have time.

    \n
    \n\n
    \n
    \n
    \n

    How it works

    \n

    Thanks to the bookmarklet or third-party applications, you save an article in your wallabag to read it later. Then, when you open your wallabag, you can comfortably read your articles.

    \n

    How to use wallabag

    \n

    There are two ways to use wallabag: you can install it on your web server or you can create an account at Framabag (we install and upgrade wallabag for you).

    \n
    \n\n
    \n", - "user_id": "1" - }, - { - "0": "4", - "1": "Sans titre", - "2": "http:\/\/www.konradlischka.info\/2016\/01\/blog\/wie-ein-deutsches-start-up-mit-wagniskapital-die-marktluecke-fuer-lokalen-digitaljournalismus-schliessen-will\/", - "3": "0", - "4": "0", - "5": "[unable to retrieve full-text content]", - "6": "1", - "id": "4", - "title": "Sans titre", - "url": "http:\/\/www.konradlischka.info\/2016\/01\/blog\/wie-ein-deutsches-start-up-mit-wagniskapital-die-marktluecke-fuer-lokalen-digitaljournalismus-schliessen-will\/", - "is_read": "0", - "is_fav": "0", - "content": "[unable to retrieve full-text content]", - "user_id": "1", - "tags": "" } ] diff --git a/tests/Wallabag/ImportBundle/fixtures/wallabag-v2.json b/tests/Wallabag/ImportBundle/fixtures/wallabag-v2.json index efa8faf2..0e30d95b 100644 --- a/tests/Wallabag/ImportBundle/fixtures/wallabag-v2.json +++ b/tests/Wallabag/ImportBundle/fixtures/wallabag-v2.json @@ -6,7 +6,7 @@ "is_archived": false, "created_at": "2016-09-08T11:55:58+0200", "updated_at": "2016-09-08T11:57:16+0200", - "is_starred": false, + "is_starred": true, "content": "
    Édition CAMédia\n

    Deux nouvelles éditions pour débattre dans le club sur la laïcité et sur la démocratie

    \n

    18 janv. 2016 | Par

    \n

    CAMédia après un échange sur « l'éthique du débat » a lancé deux discussions , l'une sur le thème de la laïcité, l'autre ( encore en cours) sur celui de la démocratie. Nous sommes heureux de pouvoir signaler la création de deux nouvelles éditions participatives sur ces thèmes. Nous vous invitons à les lire et à participer à leurs débats.

    \n
    \n

    De l'importance de rêver, éloge du merveilleux

    \n

    17 janv. 2016 | Par

    \n

    Je parlerai ici des rêves comme moteur de vie, de ces rêves qui vous rattachent et vous font espérer à ce qu’il y a de plus humain dans l’homme, même au milieu de la plus noire des détresses.

    \n
    \n

    Fin(s) d'une toute-puissance

    \n

    18 janv. 2016 | Par

    \n

    En ce début d’année, je recommande la lecture du dernier ouvrage de Guillaume Duval, La France ne sera jamais plus une grande puissance ? Tant mieux !

    \n
    \n

    L’Allier, département de destruction massive du tissu culturel

    \n

    18 janv. 2016 | Par

    \n

    Les temps sont durs pour les petites structures, les associations culturelles qui, de bourgades en villages, travaillent au cœur des régions. Leurs subventions sont souvent revues à la baisse. Le département de l’Allier les a carrément supprimées. Pour favoriser « l’événementiel ».

    \n
    Édition Les invités de Mediapart\n

    La démocratie déjà attaquée par la coopération réglementaire transatlantique

    \n

    18 janv. 2016 | Par

    \n

    Lora Verheecke et David Lundy travaillent pour Corporate Europe Observatory, une ONG basée à Bruxelles qui enquête sur le pouvoir des lobbies des grandes entreprises sur la politique de l’Union européenne. Ils révèlent que depuis 25 ans le projet de « coopération réglementaire » mené par l’Union européenne et les États-Unis a été dominé par les grandes entreprises. ET que le TTIP cherche à entériner ce projet.

    \n
    \n

    2016, une année test pour Jacob Zuma et son gouvernement

    \n

    18 janv. 2016 | Par

    \n

    Les turbulences de l’an passé ont toutes les chances de continuer à troubler le climat politique et social de l’Afrique du Sud en 2016. La situation exige des changements profonds dans la conduite des affaires du pays. Jacob Zuma tout en admettant la nécessité de ces changements, est-il l’homme de la situation ? Son gouvernement répondra-t-il aux attentes des citoyens sud-africains ?

    \n
    \n

    Un mal fou (janvier 2016)

    \n

    14 janv. 2016 | Par

    \n

    J’ai une fringale d’aventure, d’aventures à venir. J’ai la fringale de la fringale des aventures et soudain, rupture. Je n’y arrive plus, tout est bloqué, tout empêché. Faut dire que depuis un an environ, tout est devenu plus compliqué. Ecrire va de moins en moins de soi.

    \n
    \n

    Redoublement : le changement à bas bruit ?

    \n

    17 janv. 2016 | Par

    \n

    S’il est une caractéristique de la forme scolaire française bien établie dans la culture des personnels, des élèves et des parents, c’est bien le redoublement, censé sanctionner des résultats insuffisants pour envisager le passage dans la classe supérieure. Or, en ce domaine, l’évolution est nette.

    \n
    \n

    Samedi-sciences (196): des chasseurs de mammouths en Arctique il y a 45 000 ans

    \n

    16 janv. 2016 | Par Michel de Pracontal

    \n

    Les restes d’un mammouth retrouvés en Arctique sibérien, datés de 45 000 ans, portent les traces de blessures infligées par des chasseurs humains. Les scientifiques pensaient jusqu’ici que notre espèce ne s’était pas aventurée dans cette région glaciale il y a plus de 30 000 ou 35 0000 ans. En réalité, des hommes ont réussi à survivre en Arctique au moins 10 000 ans plus tôt que l’on croyait.

    \n
    \n

    De la démocratie, du citoyen et de l'éthique

    \n

    14 janv. 2016 | Par

    \n

    Trois ouvrages sont parus au Seuil, qui font état de la nécessité d’intégrer le citoyen dans la gouvernance de la nation. Non pas à titre consultatif mais doté d’un pouvoir délibératif pour constituer une contre-force face aux clans politico-financiers qui dominent la vie publique.

    \n
    ", "mimetype": "text/html", "language": "fr", @@ -96,299 +96,5 @@ "domain_name": "www.nationaljournal.com", "preview_picture": "https://www.nationaljournal.com/media/media/2015/09/17/06Julian-Assange.jpg", "tags": [] - }, - { - "is_archived": 0, - "created_at": "2016-09-08T11:55:58+0200", - "updated_at": "2016-09-08T11:57:16+0200", - "is_starred": 0, - "id": 605, - "title": "What David Cameron did to the pig, his party is now doing to the country", - "url": "http://www.newstatesman.com/2015/09/what-david-cameron-did-pig-his-party-now-doing-country", - "content": "

    Whatever you do, don’t think about David Cameron and a dead pig. I know, I know it’s like trying not to think of an elephant, but the fact is that the allegations that the Prime Minister may have put a 'private part of his anatomy\" into a dead pig's mouth as part of an initiation ritual for an elite drinking society at Oxford University are actually a very serious matter, and it’s all about corruption and the nature of elected power, and it would help if we could all just calm down for a second and stop giggling. Don’t think I don’t see you at the back there.

    \n

    You know, I feel for David Cameron today, I really do. Politicians’ private sex lives should never be used against them - unless their particular proclivities implicate them in gross hypocrisy or they have harmed another human being. If the rumours are true, it’s unlikely that the pig in question was hurt by the Prime Minister’s ministrations, given that it was already missing its limbs and torso.

    \n

    Sniggering aside, this is unlikely to hurt David Cameron in the long run. He’s not looking for re-election, and besides, everyone knows posh people get up to weird sex stuff. Weird sex stuff is as British as weak tea and racism. When I was at Oxford, it was an open secret that the posh kids had naughty parties, and, of course, so did the rest of us - the difference was the much lower budget, and the fact that the posh kids didn’t seem to enjoy it as much as we did. It all seemed to be more about getting on than getting off. You didn’t shag or not shag the pig’s face because that was what you were into, you did it because you had your eye on a safe seat in Dorset in 20 years’ time and you needed to make the right friends.

    \n

    There is a reason that David Cameron is allowed to hold office when everyone assumes he spent the 1980s taking drugs and getting up to weird things with his Eton mates, but Jeremy Corbyn is considered unelectable because he didn’t sing the national anthem last week. Cameron is part of a select group of people to whom different rules apply, and he knows it, and his friends know it, and the tabloids know it, and the whole cosy British political machine knows it. This is why Corbyn will spend the next five years being savaged for having a slightly rumpled tie by the same newspapers that reported on the dead pig allegations under the title \"the making of an extraordinary Prime Minister\".

    \n

    The thing that's really horrifying about what has already been dubbed the 'Hameron' scandal is that it demonstrates what entitlement of this kind actually means, and how embarrassing it all is. There are people out there who can spend their early twenties in close proximity to cocaine and popping their peckers in offal and not even consider for a second that there might be anyone better placed to run the country. These are people who know the rules don’t apply to them, who know they can do whatever they want and still end up in charge.

    \n

    I don't honestly care whether or not David Cameron shagged a dead pig. I've been to enough house parties in Bethnal Green that this sort of thing doesn't shock me. Come back to me when there’s video evidence of Cameron dressed in a leather gimp-suit tanned from the flayed skins of the former shadow cabinet, leaping into an entire Shropshire field full of pigs and screaming that his name is Legion. Then we’ll talk. There are a lot of things that David Cameron has definitely done that I do find disgusting, though. Taking away benefits from sick and disabled people, pricing poor kids out of higher education, and forcing millions of families to rely on food banks. That, to me, is shocking and grotesque. I don't give a damn about what he did or didn’t do to that pig, and whether there was mood-lighting involved.

    \n

    But the fact is that a lot of people do, and they're precisely the sort of people whose votes Cameron has relied on to shore up the power he clearly feels is his by right, might and various dodgy initiation rituals involving sex workers, smashing up pubs and knobbing bits of meat. Cameron clearly believes those people are there to be manipulated, and that’s the reason this story actually matters, beyond the immediate risk that a handful of pearl-clutchers in the Home Counties might splutter themselves to death.

    \n

    I was explaining all this to an American friend who asked, not unreasonably, why I'd spent all morning scrolling through Twitter and cackling like a toddler with a nerf gun. I did my best to describe seriously what had happened, and my friend, who does not follow British politics, asked me, 'so this guy, was he elected or appointed?'

    \n

    The answer, of course, is both. David Cameron is not just prime minister because a quarter of the country voted for him. That's not how power works in Britain, or anywhere, and it's moments like this that show it plainly, which is why we're all vaguely embarrassed today. Cameron's route to the office he clearly believes himself born to began much earlier, possibly even on a balmy Oxford night, just Dave, a dead pig and a select group of wide-eyed, gurning future business leaders, all whooping and cheering.

    \n

    It would surely have been a moment more important to Cameron's career than any number of photoshoots with builders in Totnes. Power and money are accessed through the back door, or, as it may be, the pig's mouth, and as with any kink, the eroticism isn't about the act, but about what the act symbolises. It's about humiliation, about control, about power play. What might the young swain have been thinking as he unzipped? What went through his head? If you ask me, I'll bet he was thinking: Soon. Someday soon, I will do this to the whole bloody country.

    \n", - "annotations": [], - "mimetype": "text/html", - "language": "en", - "reading_time": 5, - "domain_name": "www.newstatesman.com", - "preview_picture": "http://www.newstatesman.com/sites/default/files/styles/thumb_730/public/blogs_2015/09/gettyimages-464604046.jpg?itok=EaABrZda", - "tags": [] - }, - { - "is_archived": 1, - "created_at": "2016-09-08T11:55:58+0200", - "updated_at": "2016-09-08T11:57:16+0200", - "is_starred": 0, - "id": 604, - "title": "CLICK HERE to support 2016 CES Winner, Revolutionary Auto-Tracking Robot", - "url": "https://www.indiegogo.com/projects/2016-ces-winner-revolutionary-auto-tracking-robot", - "content": "
    \n
    \n

    Sign Up for Inspiration

    \n

    Private, secure, spam-free

    \n

    Follow us

    \n
    \n
    \n

    About Indiegogo

    \n
    \n

    Language

    \n
    \n
    \n
    \n
    \n
    \n

    Campaigning

    \n
    \n
    \n

    Contributing

    \n
    \n
    \n

    Sign Up for Inspiration

    \n

    Private, secure, spam-free

    \n
    \n
    \n

    About Indiegogo

    \n
    \n
    \n

    Follow us

    \n
    \n
    \n

    Language

    \n
    \n
    \n
    \n
    ", - "annotations": [], - "mimetype": "text/html", - "reading_time": 0, - "domain_name": "www.indiegogo.com", - "preview_picture": "https://c1.iggcdn.com/indiegogo-media-prod-cld/image/upload/c_fill,f_auto,h_630,w_1200/v1447395263/d6ckex9ynild6ica1xdk.jpg", - "tags": [] - }, - { - "is_archived": 0, - "created_at": "2016-09-08T11:55:58+0200", - "updated_at": "2016-09-08T11:57:16+0200", - "is_starred": 1, - "id": 603, - "title": "No title found", - "url": "http://carnetdevol.shost.ca/wordpress/aide-memoire-sur-les-commandes-associees-a-systemd/", - "content": "wallabag can't retrieve contents for this article. Please report this issue to us.", - "annotations": [], - "mimetype": "text/html", - "reading_time": 0, - "domain_name": "carnetdevol.shost.ca", - "tags": [] - }, - { - "is_archived": 1, - "created_at": "2016-09-08T11:55:58+0200", - "updated_at": "2016-09-08T11:57:16+0200", - "is_starred": 0, - "id": 602, - "title": "Présentation d'Arduino - Tuto Arduino - Le blog d'Eskimon", - "url": "http://eskimon.fr/73-arduino-101-presentation", - "content": "
    \n

    Comment faire de l’électronique en utilisant un langage de programmation ? La réponse, c’est le projet Arduino qui l’apporte. Vous allez le voir, celui-ci a été conçu pour être accessible à tous par sa simplicité. Mais il peut également être d’usage professionnel, tant les possibilités d’applications sont nombreuses.

    \n\n
    \n
    \n
    \n

    Qu’est-ce que c’est ?

    \n
    Une équipe de développeurs composée de Massimo Banzi, David Cuartielles, Tom Igoe, Gianluca Martino, David Mellis et Nicholas Zambetti a imaginé un projet répondant au doux nom de Arduino et mettant en œuvre une petite carte électronique programmable et un logiciel multiplateforme, qui puisse être accessible à tout un chacun dans le but de créer facilement des systèmes électroniques. Étant donné qu’il y a des débutants parmi nous, commençons par voir un peu le vocabulaire commun propre au domaine de l’électronique et de l’informatique.\n

    Une carte électronique

    \n

    Une carte électronique est un support plan, flexible ou rigide, généralement composé d’epoxy ou de fibre de verre. Elle possède des pistes électriques disposées sur une, deux ou plusieurs couches (en surface et/ou en interne) qui permettent la mise en relation électrique des composants électroniques. Chaque piste relie tel composant à tel autre, de façon à créer un système électronique qui fonctionne et qui réalise les opérations demandées.

    \n
    \"\"\"\"
    Exemples de cartes électroniques
    \n

    Évidemment, tous les composants d’une carte électronique ne sont pas forcément reliés entre eux. Le câblage des composants suit un plan spécifique à chaque carte électronique, qui se nomme le schéma électronique.

    \n
    \"\"
    Exemple de schéma électronique – carte Arduino Uno
    \n

    Enfin, avant de passer à la réalisation d’un carte électronique, il est nécessaire de transformer le schéma électronique en un schéma de câblage, appelé typon.

    \n
    \"\"
    Exemple de typon – carte Arduino
    \n

    Une fois que l’on a une carte électronique, on fait quoi avec ?

    \n

    Eh bien une fois que la carte électronique est faite, nous n’avons plus qu’à la tester et l’utiliser ! Dans notre cas, avec Arduino, nous n’aurons pas à fabriquer la carte et encore moins à la concevoir. Elle existe, elle est déjà prête à l’emploi et nous n’avons plus qu’à l’utiliser. Et pour cela, vous allez devoir apprendre comment l’utiliser, ce que je vais vous montrer dans ce tutoriel.

    \n

    Programmable ?

    \n

    J’ai parlé de carte électronique programmable au début de ce chapitre. Mais savez-vous ce que c’est exactement ? Non pas vraiment. Alors voyons ensemble de quoi il s’agit. La carte Arduino est une carte électronique qui ne sait rien faire sans qu’on lui dise quoi faire. Pourquoi ? Eh bien c’est du au fait qu’elle est programmable. Cela signifie qu’elle a besoin d’un programme pour fonctionner.

    \n

    Un programme

    \n

    Un programme est une liste d’instructions qui est exécuté par un système. Par exemple votre navigateur internet, avec lequel vous lisez probablement ce cours, est un programme. On peut analogiquement faire référence à une liste de course :

    \n
    \"\"
    \n

    Chaque élément de cette liste est une instruction qui vous dis : « Va chercher le lait » ou « Va chercher le pain », etc. Dans un programme le fonctionnement est similaire :

    \n
    • Attendre que l’utilisateur rentre un site internet à consulter
    • \n
    • Rechercher sur internet la page demandée
    • \n
    • Afficher le résultat
    • \n

    Tel pourrait être le fonctionnement de votre navigateur internet. Il va attendre que vous lui demandiez quelque chose pour aller le chercher et ensuite vous le montrer. Eh bien, tout aussi simplement que ces deux cas, une carte électronique programmable suit une liste d’instructions pour effectuer les opérations demandées par le programme.

    \n

    Et on les trouves où ces programmes ? Comment on fait pour le mettre dans la carte ? o_O

    \n

    Des programmes, on peut en trouver de partout. Mais restons concentré sur Arduino. Le programme que nous allons mettre dans la carte Arduino, c’est nous qui allons le réaliser. Oui, vous avez bien lu. Nous allons programmer cette carte Arduino. Bien sûr, ce ne sera pas aussi simple qu’une liste de course, mais rassurez-vous cependant car nous allons réussir quand même ! Je vous montrerais comment y parvenir, puisque avant tout c’est un des objectifs de ce tutoriel. Voici un exemple de programme : \"\" Vous le voyez comme moi, il s’agit de plusieurs lignes de texte, chacune étant une instruction. Ce langage ressemble à un véritable baragouin et ne semble vouloir à priori rien dire du tout… Et pourtant, c’est ce que nous saurons faire dans quelques temps ! Car nous apprendrons le langage informatique utilisé pour programmer la carte Arduino. Je ne m’attarde pas sur les détails, nous aurons amplement le temps de revenir sur le sujet plus tard. Pour répondre à la deuxième question, nous allons avoir besoin d’un logiciel…

    \n

    Et un logiciel ?

    \n

    Bon, je ne vais pas vous faire le détail de ce qu’est un logiciel, vous savez sans aucun doute de quoi il s’agit. Ce n’est autre qu’un programme informatique exécuté sur un ordinateur. Oui, pour programmer la carte Arduino, nous allons utiliser un programme ! En fait, il va s’agir d’un compilateur. Alors qu’est-ce que c’est exactement ?

    \n

    Un compilateur

    \n

    En informatique, ce terme désigne un logiciel qui est capable de traduire un langage informatique, ou plutôt un programme utilisant un langage informatique, vers un langage plus approprié afin que la machine qui va le lire puisse le comprendre. C’est un peu comme si le patron anglais d’une firme Chinoise donnait des instructions en anglais à un de ses ouvriers chinois. L’ouvrier ne pourrait comprendre ce qu’il doit faire. Pour cela, il a besoin que l’on traduise ce que lui dit son patron. C’est le rôle du traducteur. Le compilateur va donc traduire les instructions du programme précédent, écrites en langage texte, vers un langage dit « machine ». Ce langage utilise uniquement des 0 et des 1. Nous verrons plus tard pourquoi. Cela pourrait être imagé de la façon suivante :

    \n
    \"\"
    \n

    Donc, pour traduire le langage texte vers le langage machine (avec des 0 et des 1), nous aurons besoin de ce fameux compilateur. Et pas n’importe lequel, il faut celui qui soit capable de traduire le langage texte Arduino vers le langage machine Arduino. Et oui, sinon rien ne va fonctionner. Si vous mettez un traducteur Français vers Allemand entre notre patron anglais et son ouvrier chinois, ça ne fonctionnera pas mieux que s’ils discutaient directement. Vous comprenez ?

    \n

    Et pourquoi on doit utiliser un traducteur, on peut pas simplement apprendre le langage machine directement ?

    \n

    Comment dire… non ! Non parce que le langage machine est quasiment impossible à utiliser tel quel. Par exemple, comme il est composé de 0 et de 1, si je vous montre ça : « 0001011100111010101000111 », vous serez incapable, tout comme moi, de dire ce que cela signifie ! Et même si je vous dis que la suite « 01000001 » correspond à la lettre « A », je vous donne bien du courage pour coder rien qu’une phrase ! 😛 Bref, oubliez cette idée. C’est quand même plus facile d’utiliser des mots anglais (car oui nous allons être obligé de faire un peu d’anglais pour programmer, mais rien de bien compliqué rassure-vous) que des suites de 0 et de 1. Vous ne croyez pas ?

    \n

    Envoyer le programme dans la carte

    \n

    Là, je ne vais pas vous dire grand chose car c’est l’environnement de développement qui va gérer tout ça. Nous n’aurons qu’à apprendre comment utiliser ce dernier et il se débrouillera tout seul pour envoyer le programme dans la carte. Nah ! Nous n’aurons donc qu’à créer le programme sans nous soucier du reste.

    \n
    \n
    \n
    \n

    Pourquoi choisir Arduino ?

    \n
    \n

    Que va-t-on faire avec ?

    \n

    Avec Arduino, nous allons commencer par apprendre à programmer puis à utiliser des composants électroniques. Au final, nous saurons créer des systèmes électroniques plus ou moins complexes. Mais ce n’est pas tout…

    \n

    D’abord, Arduino c’est…

    \n

    … une carte électronique programmable et un logiciel gratuit :

    \n
    \"\"
    \n

    Mais aussi

    \n

    – Un prix dérisoire étant donné l’étendue des applications possibles. On comptera 20 euros pour la carte que l’on va utiliser dans le cours. Le logiciel est fournit gratuitement ! – Une compatibilité sous toutes les plateformes, à savoir : Windows, Linux et Mac OS. – Une communauté ultra développée ! Des milliers de forums d’entre-aide, de présentations de projets, de propositions de programmes et de bibliothèques, … – Un site en anglais arduino.cc et un autre en français arduino.cc où vous trouverez tout de la référence Arduino, le matériel, des exemples d’utilisations, de l’aide pour débuter, des explications sur le logiciel et le matériel, etc. – Une liberté quasi absolue. Elle constitue en elle même deux choses :

    \n
    • Le logiciel : gratuit et open source, développé en Java, dont la simplicité d’utilisation relève du savoir cliquer sur la souris
    • \n
    • Le matériel : cartes électroniques dont les schémas sont en libre circulation sur internet
    • \n

    Cette liberté a une condition : le nom « Arduino » ne doit être employé que pour les cartes « officielles ». En somme, vous ne pouvez pas fabriquer votre propre carte sur le modèle Arduino et lui assigner le nom « Arduino ».

    \n

    Et enfin, les applications possibles

    \n

    Voici une liste non exhaustive des applications possible réalisées grâce à Arduino :

    \n
    • contrôler des appareils domestiques
    • \n
    • donner une « intelligence » à un robot
    • \n
    • réaliser des jeux de lumières
    • \n
    • permettre à un ordinateur de communiquer avec une carte électronique et différents capteurs
    • \n
    • télécommander un appareil mobile (modélisme)
    • \n
    • etc.
    • \n

    Il y a tellement d’autres infinités d’utilisations, vous pouvez simplement chercher sur votre moteur de recherche préféré ou sur Youtube le mot « Arduino » pour découvrir les milliers de projets réalisés avec !

    \n

    Arduino dans ce tutoriel

    \n

    Je vais quand même rappeler les principaux objectifs de ce cours. Nous allons avant tout découvrir Arduino dans son ensemble et apprendre à l’utiliser. Dans un premier temps, il s’agira de vous présenter ce qu’est Arduino, comment cela fonctionne globalement, pour ensuite entrer un peu plus dans le détail. Nous allons alors apprendre à utiliser le langage Arduino pour pouvoir créer des programmes très simple pour débuter. Nous enchainerons ensuite avec les différentes fonctionnalités de la carte et ferons de petits TP qui vous permettront d’assimiler chaque notion abordée. Dès lors que vous serez plutôt à l’aise avec toutes les bases, nous nous rapprocherons de l’utilisation de composants électroniques plus ou moins complexes et finirons par un plus « gros » TP alliant la programmation et l’électronique. De quoi vous mettre de l’eau à la bouche ! 😛

    \n

    Arduino à l’école ?

    \n

    Pédagogiquement, Arduino a aussi pas mal d’atout. En effet, ses créateurs ont d’abord pensé ce projet pour qu’il soit facile d’accès. Il permet ainsi une très bonne approche de nombreux domaines et ainsi d’apprendre plein de choses assez simplement.

    \n

    Des exemples

    \n

    Voici quelques exemples d’utilisation possible :

    \n
    • Simuler le fonctionnement des portes logiques
    • \n
    • Permettre l’utilisation de différents capteurs
    • \n
    • Mettre en œuvre et faciliter la compréhension d’un réseau informatique
    • \n
    • Se servir d’Arduino pour créer des maquettes animées montrant le fonctionnement des collisions entres les plaques de la croute terrestre, par exemple \":mrgreen:\"
    • \n
    • Donner un exemple concret d’utilisation des matrices avec un clavier alphanumérique 16 touches ou plus
    • \n
    • Être la base pour des élèves ayant un TPE à faire pour le BAC
    • \n
    • …
    • \n

    De plus, énormément de ressources et tutoriels (mais souvent en anglais) se trouvent sur internet, ce qui offre un autonomie particulière à l’apprenant.

    \n

    Des outils existant !

    \n

    Enfin, pour terminer de vous convaincre d’utiliser Arduino pour découvrir le monde merveilleux de l’embarqué, il existe différents outils qui puissent être utilisé avec Arduino. Je vais en citer deux qui me semble être les principaux : Ardublock est un outil qui se greffe au logiciel Arduino et qui permet de programmer avec des blocs. Chaque bloc est une instruction. On peut aisément faire des programmes avec cet outil et mêmes des plutôt complexes. Cela permet par exemple de se concentrer sur ce que l’on doit faire avec Arduino et non se concentrer sur Arduino pour ensuite ce que l’on doit comprendre avec. Citons entre autre la simulation de porte logique : plutôt créer des programmes rapidement sans connaitre le langage pour comprendre plus facilement comment fonctionne une porte logique. Et ce n’est qu’un exemple. Car cela permet aussi de permettre à de jeunes enfants de commencer à programmer sans de trop grandes complications.

    \n
    \"\"\"\"\"\"
    Exemple de programmes réalisés avec Ardublock
    \n

    Processing est une autre plateforme en lien avec Arduino. Là il n’y a pas de matériel, uniquement un logiciel. Il permet entre autre de créer des interfaces graphiques avec un langage de programmation très similaire à celui d’Arduino. Par contre, cela demande un niveau un peu plus élevé pour pouvoir l’utiliser, même si cela reste simple dans l’ensemble.

    \n
    \"\"
    Voilà un exemple de ce que j’avais réalisé avec Processing pour faire communiquer mon ordinateur avec ma carte Arduino
    \n

    J’espère avoir été assez convaincant afin que vous franchissiez le pas et ayez du plaisir à apprendre ! \":)\"

    \n
    \n
    \n
    \n

    Les cartes Arduino

    \n
    Le matériel que j’ai choisi d’utiliser tout au long de ce cours n’a pas un prix excessif et, je l’ai dit, tourne aux alentours de 25 € TTC. Il existe plusieurs magasins en lignes et en boutiques qui vendent des cartes Arduino. Je vais vous en donner quelques-uns, mais avant, il va falloir différencier certaines choses.\n

    Les fabricants

    \n

    Le projet Arduino est libre et les schémas des cartes circulent librement sur internet. D’où la mise en garde que je vais faire : il se peut qu’un illustre inconnu fabrique lui même ses cartes Arduino. Cela n’a rien de mal en soi, s’il veut les commercialiser, il peut. Mais s’il est malhonnête, il peut vous vendre un produit défectueux. Bien sûr, tout le monde ne cherchera pas à vous arnaquer. Mais la prudence est de rigueur. Faites donc attention où vous achetez vos cartes.

    \n

    Les types de cartes

    \n
    \"\"
    \n

    Il y a trois types de cartes :

    \n
    • Lesdites « officielles » qui sont fabriquées en Italie par le fabricant officiel : Smart Projects
    • \n
    • Lesdits « compatibles » qui ne sont pas fabriqués par Smart Projects, mais qui sont totalement compatibles avec les Arduino officielles.
    • \n
    • Les « autres » fabriquées par diverse entreprise et commercialisées sous un nom différent (Freeduino, Seeduino, Femtoduino, …).
    • \n

    Les différentes cartes

    \n

    Des cartes Arduino il en existe beaucoup ! Voyons celles qui nous intéressent… La carte Uno et Duemilanove Nous choisirons d’utiliser la carte portant le nom de « Uno » ou « Duemilanove ». Ces deux versions sont presque identiques.

    \n
    \"\"\"\"
    Carte Arduino « Duemilavove » et « Uno » avec laquelle nous allons travailler
    \n

    La carte Mega La carte Arduino Mega est une autre carte qui offre toutes les fonctionnalités de la carte précédente, mais avec des fonctionnalités supplémentaires. On retrouve notamment un nombre d’entrées et de sorties plus important ainsi que plusieurs liaisons séries. Bien sûr, le prix est plus élevé : > 40 € !

    \n
    \"\"
    Carte Arduino « Mega »
    \n

    Les autres cartes Il existe encore beaucoup d’autres cartes, je vous laisse vous débrouiller pour trouver celle qui conviendra à vos projets. Cela dit, je vous conseil dans un premier temps d’utiliser la carte Arduino Uno ou Duemilanove d’une part car elle vous sera largement suffisante pour débuter et d’autre part car c’est avec celle-ci que nous présentons le cours.

    \n

    Où acheter ?

    \n

    Il existe sur le net une multitude de magasins qui proposent des cartes Arduino. Pour consulter la liste de ces magasins, rien de plus simple, il suffit de vous rendre sur le forum dédié :

    \n\n

    J’ai vu des cartes officielles « édition SMD/CMS ». Ca à l’air bien aussi, c’est quoi la différence ? Je peux m’en servir ?

    \n

    Il n’y a pas de différence ! enfin presque… « SMD » signifie Surface Mount Device, en français on appelle ça des « CMS » pour Composants Montés en Surface. Ces composants sont soudés directement sur le cuivre de la carte, il ne la traverse pas comme les autres. Pour les cartes Arduino, on retrouve le composant principal en édition SMD dans ces cartes. La carte est donc la même, aucune différence pour le tuto. Les composants sont les mêmes, seule l’allure « physique » est différente. Par exemple, ci-dessus la « Mega » est en SMD et la Uno est « classique ».

    \n
    \n
    \n
    \n

    Liste d’achat

    \n
    Tout au long du cours, nous allons utiliser du matériel en supplément de la carte. Rassurez-vous le prix est bien moindre. Je vous donne cette liste, cela vous évitera d’acheter en plusieurs fois. Vous allez devoir me croire sur parole sur leur intérêt. Nous découvrirons comment chaque composant fonctionne et comment les utiliser tout au long du tutoriel. \":)\"

    Attention, cette liste ne contient que les composants en quantités minimales strictes. Libre à vous de prendre plus de LED et de résistances par exemple (au cas où vous en perdriez ou détruisiez…). Pour ce qui est des prix, j’ai regardé sur différents sites grands publics (donc pas Farnell par exemple), ils peuvent donc paraître plus élevé que la normale dans la mesure où ces sites amortissent moins sur des ventes à des clients fidèles qui prennent tout en grande quantité…

    \n

    Avant que j’oublie, quatres éléments n’apparaitront pas dans la liste et sont indispensables :

    \n\n\n\n\n
    Une Arduino Uno ou DuemilanoveUn câble USB A mâle/B mâle
    \"\"\"\"
    \n\n\n\n
    Une BreadBoard (plaque d’essai)Un lot de fils pour brancher le tout !
    \"\"\"\"

    Liste Globale

    \n

    Voici donc la liste du matériel nécessaire pour suivre le cours. Libre à vous de tout acheter ou non.

    \n
    Liste incomplète, le tutoriel n’est pas terminé ! Mais elle suffit pour suivre les chapitres en ligne.
    \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n
    DésignationQuantitéPhotoDescription
    LED rouge7\"\"Ce composant est une sorte de lampe un peu spécial. Nous nous en servirons principalement pour faire de la signalisation.
    LED verte3
    LED jaune (ou orange)2
    Résistance (entre 220 et 470 Ohm)10\"\"La résistance est un composant de base qui s’oppose au passage du courant. On s’en sert pour limiter des courants maximums mais aussi pour d’autres choses.
    Résistance (entre 2.2 et 4.7 kOhm)2
    Résistance (10 kOhm)2
    Bouton Poussoir2\"\"Un bouton poussoir sert à faire passer le courant lorsqu’on appuie dessus ou au contraire garder le circuit « éteint » lorsqu’il est relâché.
    Transistor (2N2222 ou BC547)2\"\"Le transistor sert à plein de chose. Il peut être utilisé pour faire de l’amplification (de courant ou de tension) mais aussi comme un interrupteur commandé électriquement.
    Afficheur 7 segments (anode commune)2\"\"Un afficheur 7 segments est un ensemble de LEDs (cf. ci-dessus) disposées géométriquement pour afficher des chiffres.
    Décodeur BCD (CD4543BE)1\"\"Le décodeur BCD (Binaire Codé Décimal) permet piloter des afficheurs 7 segments en limitant le nombre de fils de données (4 au lieu de 7).
    Condensateur (10nF/5V ou plus)2\"\"Le condensateur est un composant de base. Il sert à plein de chose. On peut se le représenter comme un petit réservoir à électricité.
    Condensateur (1000µF 15V ou plus1\"\"Celui-ci est un plus gros réservoir que le précédent
    Potentiomètre linéaire (10 kOhm)1\"\"Le potentiomètre est une résistance que l’on peut faire varier manuellement.
    LED RVB1\"\"Une LED RVB (Rouge Vert Bleu) est une LED permettant de mélanger les couleurs de bases pour en créer d’autres.
    Écran LCD alphanumérique1\"\"L’écran LCD alphanumérique permet d’afficher des caractères tels que les chiffres et les lettres. Il va apporter de l’interactivité à vos projets les plus fous !

    Les revendeurs

    \n

    Je vous ai déjà donné le lien, vous pourrez trouver ces composants chez les revendeurs listés dans ce sujet du forum :

    \n\n

    Les kits

    \n

    Enfin, il existe des kits tout prêts chez certains revendeurs. Nous n’en conseillerons aucun pour plusieurs raisons. Tout d’abord, pour ne pas faire trop de publicité et rester conforme avec la charte du site. Ensuite, car il est difficile de trouver un kit « complet ». Ils ont tous des avantages et des inconvénients mais aucun (au moment de la publication de ces lignes) ne propose absolument tous les composants que nous allons utiliser. Nous ne voulons donc pas que vous reveniez vous plaindre sur les forums car nous vous aurions fait dépenser votre argent inutilement !

    \n
    Cela étant dit, merci de ne pas nous spammer de MP pour que l’on donne notre avis sur tel ou tel kit ! Usez des forums pour cela, il y a toujours quelqu’un qui sera là pour vous aider. Et puis nous n’avons pas les moyens de tous les acheter et tester leur qualité !
    \n
    \n
    \n
    \n
    \n
    \n\n\n", - "annotations": [], - "mimetype": "text/html", - "language": "fr-FR", - "reading_time": 17, - "domain_name": "eskimon.fr", - "preview_picture": "http://eskimon.fr/wp-content/uploads/tuto/385800.jpg", - "tags": [] - }, - { - "is_archived": 1, - "created_at": "2016-09-08T11:55:58+0200", - "updated_at": "2016-09-08T11:57:16+0200", - "is_starred": 0, - "id": 543, - "title": "Lenovo ThinkPad X1 Carbon Ultrabook Review", - "url": "http://www.notebookcheck.net/Lenovo-ThinkPad-X1-Carbon-Ultrabook-Review.138033.0.html", - "content": "

    The Lenovo X1 Carbon 3rd Gen is a beautiful machine. Much like the Dell XPS 13 took the initiative to cram a 13-inch screen into an 11-inch form factor, the X1 Carbon sports dimensions that are more comparable to a typical 13-inch machine—and that includes its weight and thinness, both of which are indisputably manageable. It’s also practically designed; the matte black surfaces that comprise the majority of the case are minimalist and attractive, but they’re simultaneously haptically comfortable, with an unmistakably cool metal feel and a comfortable fit for use on both lap and desk. The case also feels fairly solid, though the incidence of flex and relative lack of torsion resistance in some regards gave us pause.

    \n

    Perhaps more exciting, however, is what has improved over the X1 Carbon 2nd Gen. Criticism of the 2nd Gen’s radical (and arguably illogical) keyboard design and polarizing full-depression clickpad scared away many prospective buyers—as such fearlessly progressive and experimental design decisions generally don’t fit well with the business market, where practicality rules supreme. The Gen 3 wholeheartedly acknowledges these complaints and implements a complete reversal of those decisions. As a result, the keyboard—immediately familiar and accessible—is one of the absolute best we have ever used on an Ultrabook. Meanwhile, the three classic top-mounted physical buttons for use with the Trackpoint have returned, and the touchpad itself ditches the controversial full-click design in favor of a far more comfortable (and, in our judgment, reliable) clickpad approach. The end result is that the X1 Carbon Gen 3 features some of the best input devices we’ve tested on an Ultrabook.

    \n

    What about performance? CPU performance differences between the 2nd Gen and 3rd Gen X1 Carbon models were essentially nil in our testing—for all intents and purposes, the machines are identical in this regard. However in GPU testing, we witnessed a notable speed boost—in some cases up to 19% better. As compared with other modern notebooks of its class, apart from some multi-core synthetic performance hiccups, the X1 Carbon 3rd Gen holds its own, both in terms of CPU/GPU and general system performance. The only final niggle here is the Samsung PM851 SSD, whose write speeds are conspicuously capped at around 250 MB/s.

    \n

    While the leap to a Broadwell chipset and slightly larger battery seemed sure to promise improved battery runtimes, we were surprised to find throughout our testing that there wasn’t much of a difference at all. Our classic Wi-Fi Surfing Test produced a result only slightly better than that of the 2nd Gen, and the revised Wi-Fi test we just recently implemented—which is more broad and aggressive and arguably closer to actual real-world usage patterns—recorded under five hours before the machine shut down. That’s hardly an impressive number on one hand given the 50 Wh battery and supposed enhanced efficiency, though it’s still likely to get most users through a typical trip unplugged, especially if more restrictive power savings options are employed. If longer battery life is a priority, we’d suggest taking a look instead at the Dell XPS 13-9343 or the MacBook Air 13.

    \n

    Rounding out the list of considerations is an underwhelming screen, at least in terms of brightness, contrast, and color saturation—though we do most certainly appreciate the anti-glare display filter for both its diffusion of reflections and relative ease of cleaning. The X1 Carbon Gen 3 is also invariably cool and quiet, clearly favoring comfort over top-end performance (as we discovered during our stress testing of the device).

    \n

    Summing up, the X1 Carbon Gen 3 is indisputably superior to its predecessor. Although some of these improvements come in the form of better GPU performance, cooler temperatures, and lower average system noise levels, the vast majority of them center on the thankful retreat from the experimental (and finicky) input devices of the Gen 2 design and back to sanity. This isn’t just a return to form, either; to reiterate, by our judgment, they are some of the best input devices on any Ultrabook we’ve tested to date. But in spite of this self-improvement, how does the X1 Carbon compare with its modern competitors? In truth, though it’s a compelling option, it’s lost some ground since our last encounter. While notebooks such as the MacBook Air 13 and (especially) the Dell XPS 13-9343 have sprinted forward with such massive improvements in portability, battery life, and LCD quality, the X1 Carbon 3rd Gen has only marginally improved, mostly regaining footing it’s lost elsewhere. It’s still a strong contender, and it’s certainly the best Carbon to date, but especially at a pricey $1,574, we fear that its inability to innovate further may relegate it to the shadows of these more aggressive contenders.

    \n", - "annotations": [], - "mimetype": "text/html", - "language": "en", - "reading_time": 3, - "domain_name": "www.notebookcheck.net", - "preview_picture": "http://www.notebookcheck.net/fileadmin/Notebooks/Lenovo/ThinkPad_X1_Carbon_2015/x12015.jpg", - "tags": [] - }, - { - "is_archived": 0, - "created_at": "2016-09-08T11:55:58+0200", - "updated_at": "2016-09-08T11:57:16+0200", - "is_starred": 0, - "id": 541, - "title": "Visitons le Château de Landsberg !", - "url": "http://autour-du-mont-sainte-odile.overblog.com/2016/01/visitons-le-chateau-de-landsberg.html", - "content": "

    Situé au dessus de Heiligenstein et de Barr, à mi-hauteur de la montée vers plateau de la Bloss, le château de Landsberg est un but de promenade apprécié des randonneurs et des amateurs de vieilles pierres. Posé sur un éperon de granite, visible de loin, la forteresse médiévale domine fièrement la plaine d’Alsace.

    \n

    \"Visitons

    \n

    Le château primitif

    \n

    Fin du onzième siècle, Philippe de Souabe et Otton de Brunswick se disputent le trône impérial. Philippe, tenant des Hohenstaufen, veut conforter la puissance des couvents du Mont Sainte Odile. Il s’appuie sur un de ses représentants pour construire une nouvelle forteresse sur le Mont. Conrad de Vinhege érige le château de Landsberg dont sa famille prendra bientôt le nom. Nous sommes en 1197-1198. Le burg est construit sur les terres de l’abbaye de Niedermunster, le Couvent du Bas du Mont Sainte Odile. Un acte est signé de l’Abbesse Edelinde, il est daté du 23 juin 1200, il confirme la cession du terrain au chevalier Conrad.
    Nous avons publié le texte de l’abbesse Edelinde, l’an passé. (cliquez sur le lien)
    Ce premier château n’avait pas l’importance des ruines que nous pouvons admirer aujourd’hui. Il ne comportait qu’un donjon et un corps de logis, complété par une basse-cour. Commençons la visite des ruines par cette partie. Le donjon est construit sur un carré de dix mètres de côté. Puissant, placé en diagonale par rapport au corps de logis, il formait bouclier pour défendre celui-ci du côté de la plaine, au nord-est. En levant la tête, le visiteur découvre l’accès : une porte haute, qui était accessible par un pont volant à partir de l’étage du logis seigneurial. Tout en haut du donjon, on distingue encore les corbeaux qui portaient les hourds. Les hourds, constructions de bois, ancêtres des mâchicoulis, permettaient aux défenseurs de surplomber les assaillants du château.
    \"VisitonsLe logis seigneurial comportait un rez-de-chaussée dédié à l’usage domestique : cuisines, citerne, salle des gardes. Ces pièces basses n’étaient éclairées que par quelques meurtrières. Le premier étage était l’habitation de Conrad et de ses successeurs : la façade nord-est est largement éclairée par quatre baies géminées en plein-cintre. Les fenêtres doubles sont séparées par de fines colonnes et surmontées d’élégants occuli. Au sud-est, deux doubles baies et un magnifique oriel agrémentent la façade. Situé sur le coté de la porte du château, cet élément de décor est le plus frappant du site du Landsberg, un genre d’échauguette semi-cylindrique, portée par un cul de lampe conique. Nous serions là dans la chapelle castrale du Landsberg. Coté extérieur, deux petites sculptures ornent la base de l’oriel : une fleur de lys et un petit personnage accroupi. A l’intérieur, une frise d’arceaux délimite les petites fenêtres du chœur de la chapelle. L’une d’elles a la forme d’une croix. Admirez la finesse des dessins du chapiteau de la colonne toujours en place au droite de l’oriel !
    Le château de Landsberg est situé à la limite géologique des grès, prédominants au nord, et des granites d’Andlau, côté sud. Les bases et les assises de la forteresse sont en granite, roche dure extraite du fossé creusé au nord, par les carriers du moyen âge. Les parties hautes sont en grès rose du Maennelstein, plus facile à travailler.

    \n

    Le château neuf

    \n

    Ce premier château était, somme toute, de dimensions modestes. Quelques dizaines d’ années plus tard, preuve de l’importance des couvents du Mont Sainte Odile et signe de la puissance croissante de la famille de Landsberg, le site fut agrandi de façon conséquente. Nous sommes alors en 1240-1250.
    L’extension se fait au nord-ouest. Les deux tours rondes et l’imposante courtine délimitent l’emprise du Château Neuf. A l’origine, cette nouvelle enceinte était détachée du vieux burg, les deux sites ne furent rattachés que postérieurement. Les tours circulaires mesurent environ sept mètres de diamètre. Leur sommet portaient des créneaux. Un chemin de ronde surmontait la courtine : il était garni de hourds et traversait les deux tours. Archères et bretèches sont toutes orientées au nord, côté montagne, là où le risque d’attaque était le plus grand.
    Deux corps de logis se partageaient le site. Au sud, le bâtiment ne comportait qu’un seul étage, éclairé au sud-ouest par des fenêtres à banquettes, aujourd’hui murées. Le deuxième bâtiment, côté nord, fut construit plus tard. Composé de deux étages, il communiquait directement avec la tour nord.
    L’ensemble a connu plusieurs remaniements au cours des siècles.

    \n

    \"Visitons

    \n

    Les extensions du XVème et du XVIème siècles

    \n

    L’apparition des armes à feu modifie l’art de la guerre. La géométrie des châteaux doit s’adapter aux armes nouvelles : résister aux boulets et accueillir couleuvrines et canons. Tous les châteaux des Vosges n’ont pas connu cette évolution, certains, trop solitaires, trop isolés, ont été abandonnés dés cette époque. Au Landsberg, les seigneurs ont respecté le vieux burg dans sa forme initiale, mais ils l’ont adapté tout d’abord en renforçant ses murs, puis en l’entourant de nouvelles murailles. Au nord, le château neuf se voit délaissé, ses nombreuses ouvertures extérieures sont murées, à part les archères, bien entendu. Ses courtines sont prolongées pour assurer la continuité avec celles du vieux burg. Un puissant bastion est construit au sud-est. Les armes nouvelles sont mises en place : couleuvrines, poivrière portant une canonnière protégée par des vastes vantaux. Ainsi, un vaste glacis s’étend devant la forteresse. C’est en faisant le tour du château dans les fossés que le promeneur se rendra le mieux compte de la force de la place et de son adaptation aux débuts de l’artillerie.
    \"VisitonsQuelques temps, le château est passé des Landsberg au Comte Palatin, avant de revenir à la famille qui resta maître des lieux jusqu’à la Révolution.
    On ne trouve guère de textes relatant l’histoire du château de Landsberg dans les temps troublés que connut l’Alsace. Lors de la Guerre du Bundschuh, les paysans révoltés s’étaient installés dans la prévôté de Truttenhausen, toute proche. Cependant, rien ne dit que les Rustauds aient attaqué le château. Lors de la Guerre de Trente Ans, il semble que les troupes de Mansfeld, puis les Suédois se soient plutôt attaqués aux riches villes de la plaine, comme Obernai, oubliant les forteresses de montagne, déjà devenues inutiles. Cependant, le château est décrit comme ruiné au milieu du XVIIème siècle.
    Depuis longtemps, les Landsberg avaient délaissé le burg de leurs ancêtres pour lui préférer leur résidence de Niedernai, située en plaine. Le château fut confié à des gardes, puis servit de ferme. Aujourd’hui, les ruines du Landsberg sont une propriété privée. Merci à vous de respecter le site.

    \n

    Dans la basse cour du château fleurit au printemps, l’éranthe d’hiver. Sa floraison ne dure que quelques jours. Cette petite fleur jaune est rare sous nos climats. De son nom latin ‘Eranthis hyemalis’, l’éranthe est originaire d’Italie ou de Turquie. La légende nous dit qu’elle aurait été rapportée des croisades par un sire de Landsberg. Elle serait un remède à la mélancolie.

    \n

    Promenade et accès au château de Landsberg\"Visitons

    \n

    Les automobilistes pourront garer leur véhicule sur le petit parking situé à l’ouest d’Heiligenstein. Ils gagneront Truttenhausen sur la petite route à travers prés. Un sentier du Club Vosgien ( disque bleu ) monte vers les ruines.
    Les marcheurs préfèreront effectuer une boucle à partir de Saint-Jacques, par exemple. Saint-Jacques, Kapellenhausfelsen, Ameisenberg, Landsberg ( balisage : triangle bleu ) avec un retour sur Saint-Jacques par le sentier sans dénivelé ( rectangle : bicolore rouge et blanc).
    A moins qu’ils ne préfèrent rechercher les pierres sculptées du Chemin des Chameaux. (cliquez sur le lien ), ou découvrir la Chapelle du Frère Léon dans la vallée de la Kirneck.

    \n

    Illustrations

    \n
    • Photographies du château de Landsberg ( BrR, FrP et PiP)
    • \n
    • Schéma des ruines ( PiP)
    • \n
    • L’Orgueil, Herrade de landsberg, Hortus Deliciarum
    • \n
    • Les Ruines du Landsberg, aquarelle de Osterwald, 1873
    • \n
    \n", - "annotations": [], - "mimetype": "text/html", - "language": "fr", - "reading_time": 7, - "domain_name": "autour-du-mont-sainte-odile.overblog.com", - "preview_picture": "http://img.over-blog-kiwi.com/0/28/39/78/20160128/ob_68f59d_12.jpg", - "tags": [] - }, - { - "is_archived": 1, - "created_at": "2016-09-08T11:55:58+0200", - "updated_at": "2016-09-08T11:57:16+0200", - "is_starred": 0, - "id": 454, - "title": "Contrer les stéréotypes par les livres : “C'est dès l'enfance qu'ils se construisent”", - "url": "https://www.actualitte.com/article/monde-edition/contrer-les-stereotypes-par-les-livres-c-est-des-l-enfance-qu-ils-se-construisent/64058", - "content": "

    Même dans l'espace jeunesse d'un immense salon littéraire, difficile de passer à côté du livre de coloriage féministe — et fier de l'être — de la maison d'édition Goater. Cette structure atypique, adossée à un bar de Rennes, Le Papier-Timbré, propose des titres tout aussi uniques, de ces livres que l'on devine importants avant même de les ouvrir.

    \n\n

    \"Livre

    \n

    Judikael et Jean-Marie Goater (ActuaLitté, CC BY SA 2.0)

    \n\n\n

    Le Papier-Timbré et les éditions Goater avancent main dans la main, dans une même structure, depuis 2009. En plus de la licence IV, permis de publier : « On est très occupé par les soirées étudiantes et festives, mais on développe en plus des projets autour des livres qui, parfois, émergent d'ailleurs avec la clientèle. C'est surtout un motif supplémentaire pour se retrouver et partager des moments de convivialité, partager des goûts, des envies, de la littérature, des essais et de la jeunesse », explique Jean-Marie Goater.

    \n

    \n

    En plus de la production maison, le café-librairie propose celle de maisons soeurs : les éditions de juillet, L'Oeuf, les Éditions Pontcerq, essentiellement des petits éditeurs de Rennes et de la Bretagne. La maison est diffusée et distribuée en Bretagne par Coop Breizh, diffusé par Hobo Diffusion, distribué par Makassar pour la France (comme les éditions surréalistes Prairial). Les tirages vont de 500 exemplaires à 3000 sauf exceptions et coéditions comme Détachez vos ceintures, projet collectif des éditions du Kyste contre l'aéroport de Notre-Dame-des-Landes, ou Galette-saucisse, je t’aime ! de Benjamin Keltz, avec les Éditions du coin de la rue.

    \n

    \n

    Mon premier cahier de coloriage féministe ! sera en librairie d'ici quelques jours, et la maison Goater y croit dur comme fer : « Nous avons commencé par traduire et adapter C'est quoi ton genre ?, un livre écrit par Jacinta Bunnell et publié par l'éditeur anarchiste américain PM Press », explique Jean-Marie Goater. Dans les pages du livre, on croise des monstres qui aiment les petits sacs à main et les chaussures, des princesses qui ne suivent pas vraiment le dress code, ou des enfants en fauteuil roulant, encore rares dans les livres jeunesse.

    \n

    \n

    \"Livre

    \n

    (ActuaLitté, CC BY SA 2.0)

    \n

    \n

    \n

    Après cette publication, la maison a souhaité développer un projet en France, en réunissant 16 dessinatrices dont 4 dessinateurs pour leur proposer d'expliquer le féminisme aux enfants, à travers un dessin. « Le livre aborde la vie à l'école, les habillements, les métiers, le sport, mais aussi les quelques femmes féministes importantes de l'histoire... Ça reste ludique et sans prétention encyclopédique sur le féminisme, mais il est plus simple d'aborder le sujet avec un support comme celui-ci à la maison, à l'école ou au centre de loisirs. » Comme le précédent, l'ouvrage présente d'une nouvelle manière les situations traditionnelles des livres de coloriage ou jeunesse.

    \n

    \n

    Des ressources rares, des besoins importants

    \n

    \n

    Pas la peine d'insister pour que Jean-Marie Goater partage son avis : « La production majoritaire est quand même très caricaturale et stéréotypée, cependant on remarque depuis quelques années des éditeurs intéressants qui essaient de bousculer un peu ces stéréotypes comme La Ville Brûle, ou encore l'édition LGBT qui commence à arriver avec Des ailes sur un tracteur qui a publié un cahier de coloriage avec Sophie Labelle, plutôt sur les questions trans. »

    \n

    \n

    Contrairement à ce que les détracteurs des livres jeunesse atypiques prétendent (coucou, Jean-François Copé), lutter contre les stéréotypes n'a rien d'une guerre de civilisation ou autre affabulation du genre. Il s'agit simplement de montrer que chacun doit être fier de ce qu'il est, respecter ce que l'autre est, et ne pas chercher l'assentiment des uns ou des autres. « Fuck the world », comme dirait 2Pac... « Se poser ces questions est indispensable, il faut qu'il y ait ce débat : les enfants ne sont pas si naïfs que ça, ils ont besoin de se poser ce genre de questions. »

    \n

    \n

    \"Livre

    \n

    (ActuaLitté, CC BY SA 2.0)

    \n

    \n

    \n

    Judikael, venu aider son père, acquiesce : il a répondu à l'appel à dessins et proposé une activité dans le cahier de coloriage féministe. « Ce genre de ressources pour enfant est important, parce que c'est dès l'enfance que se construisent certains préjugés, certains stéréotypes qui restent ensuite. Quand on voit que 90 % des personnes présentes dans les manuels scolaires sont des hommes par exemple, ce genre d'ouvrages permet à certaines personnes de se reconnaître davantage dans certains rôles, qu'on ne leur attribue pas forcément de base. »

    \n

    \n

    Là est la lutte, résumée par Judikael : « On parle souvent de “déconstruire” dans le féminisme, les préjugés ou autre, ces livres peuvent permettre d'éviter de les construire. » En Terminale L, Judikael confirme que les préjugés sont toujours présents, forcément surtout en sport ou vis-à-vis de « la filière homme » (comprendre, la filière scientifique) et de « la filière femme » (comprendre, la filière littéraire). Si l'histoire du féminisme est désormais abordée en classe, certains sujets restent touchy : le journal du lycée s'est vu censurer un article sur la culture du viol, et la ségrégation hommes-femmes, « au prétexte que c'était trop hard, que les lycéens n'allaient pas comprendre »...

    \n

    \n

    « L'édition, c'est un milieu qui est quand même très hypocrite »

    \n

    \n

    À votre avis, comment réagit l'éditeur de Mon premier cahier de coloriage féministe lorsqu'on lui parle des différences entre les salaires des hommes et des femmes dans l'édition, ou dans les aides attribuées par le CNL ? Sans langue de bois : « C'est pas trop surprenant malheureusement, parce que c'est à l'image des autres professions. En tant que bar-maison d'édition, de toute façon, je ne rentre pas dans la case du CNL, je me tourne plutôt vers la région », explique Jean-Marie Goater.

    \n

    \n

    Haut les coeurs : « C'est pas grave, je m'en passe très bien. L'édition, c'est un milieu qui a quand même une grande dimension d'hypocrisie sur pas mal d'aspects, on le voit sur certaines pratiques... Il y a des cons dans ce métier-là comme dans d'autres métiers, mais je pense que ce serait bien de faire le ménage, comme à Angoulême, c'est quand même criant. Dans certains secteurs du livre, la majorité des lecteurs sont des lectrices, très clairement, ce serait quand même la moindre des choses qu'il n'y ait pas des inégalités de ce type qui existent dans le monde de l'édition. »

    \n

    \n

    \"Livre

    \n

    Un album jeunesse bilingue français-langue des signes (ActuaLitté, CC BY SA 2.0)

    \n

    \n

    \n

    Même sans aide du CNL, les éditions Goater produisent de quoi lire : Les Joyeux Punks, un album à compter, mais aussi une collection d'albums bilingues français-langue des signes, des livres en breton, dont une traduction du Persepolis de Marjane Satrapi. Pour les amateurs de polar, Goater noir, une collection de 14 titres qui a notamment fait revivre Le Soviet, la série culte des années 89-90 d'abord publiée chez Fleuve Noir et Série Noire. Pour les amateurs des écrits du Colonel Durruti, un inédit est prévu pour le mois d'octobre prochain.

    \n

    \n

    Perdez-vous sans hésiter dans le catalogue de la maison, qui propose aussi de la littérature blanche \"classique\", des essais sur l'écologie et le convivialisme ou la convivialité, l'écologie sociale, et l'Histoire, surtout XXe.

    \n\n
    ", - "annotations": [], - "mimetype": "text/html", - "language": "en", - "reading_time": 6, - "domain_name": "www.actualitte.com", - "preview_picture": "//cdn.actualitte.com/images/facebook/25284788384-ea234db7b9-z-56eeff88c23a0.jpg", - "tags": [] - }, - { - "is_archived": 1, - "created_at": "2016-09-08T11:55:58+0200", - "updated_at": "2016-09-08T11:57:16+0200", - "is_starred": 0, - "id": 99, - "title": "[ROM][6.0.1][Layers][N5] TipsyOS official builds {UBER TCs}", - "url": "http://forum.xda-developers.com/google-nexus-5/development/rom-tipsyos-official-builds-uber-tcs-t3325989", - "content": "
    \n
    \"\"

    *Welcome to Tipsy OS*

    \n
    \"\"
    \n
    This is something Martin Coulon (@martinusbe) started in his 'free' time when waiting for GZR Validus or Tesla builds and having some drinks.
    He just started with a slim base; on lp and early mm, and now using AOSPB as a base (wich Martin is also a part of)....then added what we thought useful.
    It grew up to be a fully functional Rom
    The main goal is to keep it AOSPB/Slim based and will not add any cm features unless AOSPB/Slim does.
    \n

    TipsyOS is Black and Yellow default themed, we don't like white UI, so it may look a bit weird on light switch,
    but heh! use layers to suit it your needs =)

    \n

    We are a team wich is constantly learning and ...drinking because both gets along so well hahaha \"\"

    \n

    Just report the bugs and request features, we'll see what we can do!.......

    \n

    And don't get it twisted, its not because we're Tipsy that this project is not a serious one, try it and feel speed and stability \"\"

    \n

    My builds are compiled with UBER toolchains 4.9 on both rom and kernel code.
    We can go over with 5.2/3, 6 ...but to be really honest, 4.9 is the most battery friendly in my opinion, and is still super smooth and snappy!

    \n

    Im using a cm device tree base a bit modified...
    This rom includes a custom kernel, cm was my base, and i have decided to go a bit wild and cherry-picked up some stuff from:
    Francisco Franco, Chet Kener, Faux123, Flar2, Hellsgod and some others
    So a huge thanks for them and their AWESOME open sources work!
    I will continue to work on this kernel because its fun \"\"

    \n
    \"\"

    For cool wallpapers, test builds, requests; join us on our community!

    \n
    \nGoogle+ Community\n
    Features:\n
    \n

    Code:

    \n
    \nWe slowly adding only what we need, we don't want a 1236544547 features rom...\n\"The Tavern\":\nPower menu customisations\nToasts/ListView/System animations\nBattery bar\nLCD Density\nGesture Anywhere\nExpanded Desktop\nStatus bar customisations\nAosp Recents and OmniSwitch \nBuilt in:\nDashboard (settings) columns selector\nDashboard lines remover\nSlim Navbar customisation\nVolume steps\nNotification led changer\nHeadsUp switch\nKernel Adiutor app\nLayers manager app\nLayers backup/restore app\nAdaway\nNova launcher\nViper4Android\nES Manager\nSnapCam\nChangelog generator in about phone menu....\nand prolly some other stuff that i can't remember but heh,\n just flash dat sh#t to figure out by yourself =)\n
    \n
    \n


    Installation Instructions

    \n

    1. Make sure you have a working recovery and a nandroid backup

    \n

    2. Download the ROM.

    \n

    3. Reboot to recovery.

    \n

    4.Wipe everything! system/data/cache partitions (except internal storage indeed!)

    \n

    7. Flash the ROM.

    \n

    Optional- Flash 6.x GApps.

    \n

    8. Reboot and feel Tipsy!

    \n

    Download:

    \n

    Tipsy HammerHead downloads folder

    \n

    STAY TIPSY \"\"\"\"
    \"\"

    \n

    TipsyOs, a ROM for Nexus 5 aka Hammerhead

    \n
    \n
    Kernel features:
    Kexec patch for multirom support, intelliplug, hellsactive governor, extra io schedulers, intellithermal v2, etc etc and growing, just check commits on history on the link below\n

    Known bugs:
    quick tiles may be a bit messy while re arranging them....
    u tell me then.

    \n

    don't even think to report a bug with:
    a dirty flashed rom,
    xposed frameworks installed,
    all of your apps installed, if u have a bug, clean flash the build, (flash the gapps if needed) and reproduce your bug without any data restore.
    BRING BACK LOGCAT or u will be simply ignored....

    \n

    Contributors
    martinusbe
    @Alx31
    ROM OS Version: 6.0.1 Marshmallow
    ROM Kernel: Linux 3.4.x
    Based On: AOSPB

    \n

    Version Information
    Status: Stable
    Created 2016_29_02

    \n

    Credits:
    AOSPB/Slim team for an amazing base, Google, CyanogenMod for device trees and some other repos, Dirty Unicorns/CrDroid/AICP and other roms with their open sources i may have forgotten...

    \n

    Rom code: https://github.com/TipsyOs
    Device: Hammerhead commits history
    Kernel code: Kernel commits history
    My github: https://github.com/Alx31

    \n

    \"\"

    \n

    Last edited by Alx31; 29th February 2016 at 10:48 PM.

    ", - "annotations": [], - "mimetype": "text/html", - "language": "en", - "reading_time": 3, - "domain_name": "forum.xda-developers.com", - "preview_picture": "http://cdn3.xda-developers.com/images/xda-facebook-default.jpg", - "tags": [] - }, - { - "is_archived": 0, - "created_at": "2016-09-08T11:55:58+0200", - "updated_at": "2016-09-08T11:57:16+0200", - "is_starred": 0, - "id": 98, - "title": "Top 15 Podcasts All Web Developers Should Follow - Envato Tuts+ Code Article", - "url": "http://code.tutsplus.com/articles/top-15-podcasts-all-web-developers-should-follow--net-14461", - "content": "

    As web developers, we’re always trying to get better at what we do. One of the best ways to do that is to listen to what other developers have to share. And even if you’re not learning, it’s still fun to hear what other devs are talking about. Today, I’ll share 15 podcasts that you should definitely check out.

    \n
    \n
    \n

    It seems that as often as a few times a month, Yahoo! brings in developers on the cutting edge of web technology to keep their employees up to date. For the benefit of the rest of us, these talks are recorded and published. You’ll find well-known devs like Douglas Crockford and NNicolas Zakas, and talks on everything from performance and accessibility to JavaScript and the DOM.

    \n
    \n
    \n

    This may be my favourite show from this list. The Dev Show, hosted weekly by Dan Benjamin and Jason Seifer, will give you a carefully curated set of development-related links (usually with a web dev slant) to enjoy. As an added bonus, you can watch the show live on Tuesdays at 1pm EST.

    \n
    \n
    \n

    The tagline for the Changelog says it all: “Open Source moves fast. Keep up.” This podcast, and the accompanying blog, is all about keeping you updated with the latest in Open Source Technology. It’s hosted by Adam Stacoviak and Wynn Netherland, and seems to be the official Github podcast.

    \n
    \n
    \n

    If you’re familiar with jQuery (and you probably are), you know there’s a podcast to go with it. Each week, hosts Ralph Whitbeck and Rey Bango bring you the latest in jQuery news, as well as great interviews with important people in the jQuery community. You can listen in to the jQuery wisdom of people like Remy Sharp, Yehuda Katz, Cody Lindley, and our own Jeffrey Way, among so many others.

    \n
    \n
    \n

    Sitepoint is a great resource for anyone interesting in technology, design, and even business. Books, courses, forums, blogs, articles, they’ve got it all. Of course, there’s a podcast too: check it out to find out what’s going on in the web industry.

    \n
    \n
    \n

    According to the site, WebPulp is “a podcast about technology that powers the web.” In each podcast, host Josh Owens interviews someone from behind the scenes of a well-known webapp; you’ll find out what hardware and software it takes to run apps like the 37signals apps, or GitHub.

    \n
    \n
    \n

    It’s pretty apparent that both Nettuts+ readers and writers are big fans of WordPress. If you can’t get enough WordPress goodness, you’ll probably want to sign up for the WordPress Podcast, “a weekly podcast with news, interviews and plugin tips.” There’s a bonus here: one of the most recent interviewees was none other than Collis Ta’eed, CEO of Envato.

    \n
    \n
    \n

    If you’re a user of Ellis Lab’s Expression Engine, you’ll enjoy the EE Podcast (Ellis Lab is the company behind CodeIgniter; in fact, EE is build completely on CI). Each week, Ryan Irelan and Lea Alcantara will fill in you on a certain aspect of of EE deveopment.

    \n
    \n
    \n

    If you’re a web developer, you’re probably pretty familiar with Chris Coyier’s website CSS Tricks. Besides his excellent articles, Chris occasionally puts out a screencast every few weeks. With his relaxed style, you’ll learn about a random—but always practical—part of web development in each episode.

    \n
    \n
    \n

    Hosted by Jeffrey Zeldman and Dan Benjamin, the Big Web Show “features special guests and topics like web publishing, art direction, content strategy, typography, web technology, and more. It’s everything web that matters.” You’ll listen to interviews in which famous web personalities like Eric Meyer, Jason Fried, Nicole Sullivan, Ethan Marcotte, and other professionals you should know open their minds and let you learn from the best. You can catch this show live on Thursdays a 1PM EST. Just like the Dev Show and the EE Podcast, the Big Web Show is part of Dan Benjamin’s incredible 5by5 podcast network.

    \n
    \n
    \n

    You may be familiar with Carsonified, the company behind many web dev / design training initiatives (including the Future of Web Design and Future of Web Apps confernces). On Think Vitamin, Carsonified’s “blog about the web”, you can catch Think Vitamin Radio, “a bi-weekly chat about web design, development, and entrepreneurship.”

    \n
    \n
    \n

    User Interface Engineering “is a leading research, training, and consulting firm specializing in web site and product usability.” You can take advantage of some of the free usability training they offer in their podcast, the Userability Podcast.

    \n
    \n
    \n

    This is a great resource for any beginner (and even intermediate) jQuery developers. In each episode, Remy Sharp will explain how to create an popular web effect using jQuery. You’ll learn how to build pop-up bubbles, sliding headers, and simple tabs.

    \n
    \n
    \n

    Of course, Nettuts+ publishes screencasts, too! For your convenience, you can get these in an iTunes feed, or subscribe to them on YouTube.

    \n
    \n
    \n

    HuffDuffer is a site created by Jeremy Keith; it allows you to easily create your own podcasts. From the tag cloud above, you can see that a lot of the content being collected is related to web development. Check it out!

    \n
    \n

    I’m sure most developers listen to the occasional podcast. Have I missed your favourite podcast? Let us all know in the comments!

    \n", - "annotations": [], - "mimetype": "text/html", - "reading_time": 4, - "domain_name": "code.tutsplus.com", - "preview_picture": "https://cdn.tutsplus.com/net/uploads/legacy/793_podcasts/preview.jpg", - "tags": [] - }, - { - "is_archived": 1, - "created_at": "2016-09-08T11:55:58+0200", - "updated_at": "2016-09-08T11:57:16+0200", - "is_starred": 0, - "id": 97, - "title": "University of Mississippi", - "url": "http://olemiss.edu", - "content": "
    \n
    \n
    \n
    \n

    UNIVERSITY OF MISSISSIPPI SCHOOLS AND COLLEGES

    \n

    The Schools of Nursing and Pharmacy operate on both the Oxford and Jackson campuses. The Schools of Dentistry, Health Related Professionals and Medicine, and the Health Sciences Graduate School, are based in Jackson only. (Additional healthcare programs are available through the School of Applied Sciences on the Oxford campus.) Other than these exceptions, the schools above are on the Oxford campus.

    \n
    \n
    \n

    Public Service Announcement: UM Health Center Seeing Increase in Flu Cases MORE INFO

    \n
    \n
    • \n
      \"\"
      Hotty Toddy
      \n

      Martavious Newby celebrates with fans after Ole Miss' 86-78 win over Mississippi State.

      \n
    • \n
    • \n
      \"\"
      Moody's Amazing Night
      \n

      Stefan Moody celebrates at the end of Ole Miss' 86-78 win over Mississippi State. Moody finished his final game at The Pavilion on Senior Night with a career-high 43 points.

      \n
    • \n
    • \n
      \"\"
      Career Expo
      \n

      Students talk to prospective employers during an all-majors career fair held at The Inn at Ole Miss.

      \n
    • \n
    • \n
      \"\"
      Choir Rehearsal
      \n

      Members of the University of Mississippi Concert Singers prepare for a performance at the American Choral Directors Association convention March 10 in Chattanooga, Tenn.

      \n
    • \n
    • \n
      \"\"
      Dentist's Pledge
      \n

      Kendra Clark (right) and other students in the UMMC School of Dentistry Class of 2018 recite the Dentist's Pledge at the American College of Dentists White Coat Ceremony held Feb. 26 in Jackson.

      \n
    • \n
    \n

    45°

    \n

    Oxford, MS

    \n
    \n
    \n
    \n
    \n
    ALL NEWS\n

    Latest News

    \n
    \n

    UM Honors 150 Students with Who's Who Distinction

    \n
    \n

    UM Lazarus Project Attracts International Collaborations

    \n
    \n

    Schedule Set for BancorpSouth Rebel Road Trip

    \n
    ALL ANNOUNCEMENTS\n

    Announcements

    \n
    \n

    olemisssports.com\n

    UM Athletics

    \n
    \n
    \n

    Men's Basketball

    \n
    \"team Ole Miss 86
    \n
    \"team Miss. St. 78
    \n

    Wednesday, Mar. 2

    \n
    \n
    \n

    Men's Baseball

    \n
    \"team Ole Miss 9
    \n
    \"team Memphis 7
    \n

    Wednesday, Mar. 2

    \n
    \n
    \n

    Women's Basketball

    \n
    \"team Ole Miss 59
    \n
    \"team Vanderbilt 74
    \n

    Wednesday, Mar. 2

    \n
    \n
    \n