]> git.immae.eu Git - github/wallabag/wallabag.git/commitdiff
Merge pull request #2002 from wallabag/feature-display-itemsNumber
authorJeremy Benoist <j0k3r@users.noreply.github.com>
Sat, 3 Sep 2016 14:13:08 +0000 (16:13 +0200)
committerGitHub <noreply@github.com>
Sat, 3 Sep 2016 14:13:08 +0000 (16:13 +0200)
Feature display items number

82 files changed:
.travis.yml
Gruntfile.js
app/DoctrineMigrations/Version20160410190541.php [new file with mode: 0644]
app/DoctrineMigrations/Version20160812120952.php
app/Resources/CraueConfigBundle/translations/CraueConfigBundle.da.yml
app/Resources/CraueConfigBundle/translations/CraueConfigBundle.de.yml
app/Resources/CraueConfigBundle/translations/CraueConfigBundle.en.yml
app/Resources/CraueConfigBundle/translations/CraueConfigBundle.es.yml
app/Resources/CraueConfigBundle/translations/CraueConfigBundle.fa.yml
app/Resources/CraueConfigBundle/translations/CraueConfigBundle.fr.yml
app/Resources/CraueConfigBundle/translations/CraueConfigBundle.it.yml
app/Resources/CraueConfigBundle/translations/CraueConfigBundle.oc.yml
app/Resources/CraueConfigBundle/translations/CraueConfigBundle.pl.yml
app/Resources/CraueConfigBundle/translations/CraueConfigBundle.ro.yml
app/Resources/static/themes/baggy/css/print.css
app/Resources/static/themes/material/css/main.css
app/Resources/static/themes/material/css/print.css
app/config/config.yml
app/config/parameters.yml.dist
app/config/security.yml
docs/de/conf.py [new file with mode: 0644]
docs/de/requirements.txt [new file with mode: 0644]
docs/de/user/errors_during_fetching.rst
docs/en/user/errors_during_fetching.rst
docs/en/user/import.rst
docs/fr/user/errors_during_fetching.rst
docs/fr/user/import.rst
package.json
src/Wallabag/ApiBundle/Controller/WallabagRestController.php
src/Wallabag/CoreBundle/Command/InstallCommand.php
src/Wallabag/CoreBundle/Controller/EntryController.php
src/Wallabag/CoreBundle/Controller/ExportController.php
src/Wallabag/CoreBundle/Controller/TagController.php
src/Wallabag/CoreBundle/DataFixtures/ORM/LoadSettingData.php
src/Wallabag/CoreBundle/DataFixtures/ORM/LoadTagData.php
src/Wallabag/CoreBundle/Entity/Entry.php
src/Wallabag/CoreBundle/Helper/PreparePagerForEntries.php [new file with mode: 0644]
src/Wallabag/CoreBundle/Repository/EntryRepository.php
src/Wallabag/CoreBundle/Resources/config/services.yml
src/Wallabag/CoreBundle/Resources/translations/messages.da.yml
src/Wallabag/CoreBundle/Resources/translations/messages.de.yml
src/Wallabag/CoreBundle/Resources/translations/messages.en.yml
src/Wallabag/CoreBundle/Resources/translations/messages.es.yml
src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml
src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml
src/Wallabag/CoreBundle/Resources/translations/messages.it.yml
src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml
src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml
src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml
src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml
src/Wallabag/CoreBundle/Resources/views/themes/_title.html.twig [new file with mode: 0644]
src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entries.html.twig
src/Wallabag/CoreBundle/Resources/views/themes/baggy/Entry/entry.html.twig
src/Wallabag/CoreBundle/Resources/views/themes/baggy/Static/quickstart.html.twig
src/Wallabag/CoreBundle/Resources/views/themes/baggy/Tag/tags.html.twig
src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig
src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entries.html.twig
src/Wallabag/CoreBundle/Resources/views/themes/material/Entry/entry.html.twig
src/Wallabag/CoreBundle/Resources/views/themes/material/Static/quickstart.html.twig
src/Wallabag/CoreBundle/Resources/views/themes/material/Tag/tags.html.twig
src/Wallabag/CoreBundle/Resources/views/themes/share.html.twig [new file with mode: 0644]
src/Wallabag/ImportBundle/Controller/ReadabilityController.php [new file with mode: 0644]
src/Wallabag/ImportBundle/Import/ReadabilityImport.php [new file with mode: 0644]
src/Wallabag/ImportBundle/Resources/config/services.yml
src/Wallabag/ImportBundle/Resources/views/Readability/index.html.twig [new file with mode: 0644]
src/Wallabag/UserBundle/Controller/RegistrationController.php [new file with mode: 0644]
src/Wallabag/UserBundle/Controller/SecurityController.php [new file with mode: 0644]
src/Wallabag/UserBundle/DependencyInjection/Configuration.php
src/Wallabag/UserBundle/DependencyInjection/WallabagUserExtension.php
src/Wallabag/UserBundle/Resources/translations/wallabag_user.oc.yml [new file with mode: 0644]
src/Wallabag/UserBundle/Resources/views/Security/login.html.twig
tests/Wallabag/ApiBundle/Controller/WallabagRestControllerTest.php
tests/Wallabag/CoreBundle/Command/InstallCommandTest.php
tests/Wallabag/CoreBundle/Controller/EntryControllerTest.php
tests/Wallabag/CoreBundle/Controller/SecurityControllerTest.php
tests/Wallabag/CoreBundle/Controller/TagControllerTest.php
tests/Wallabag/ImportBundle/Controller/ImportControllerTest.php
tests/Wallabag/ImportBundle/Controller/ReadabilityControllerTest.php [new file with mode: 0644]
tests/Wallabag/ImportBundle/Import/ReadabilityImportTest.php [new file with mode: 0644]
tests/Wallabag/ImportBundle/fixtures/readability-read.json [new file with mode: 0644]
tests/Wallabag/ImportBundle/fixtures/readability.json [new file with mode: 0644]
var/SymfonyRequirements.php

index 65e7e3047520236450f84b824093b53187e5feba..c343d5ae4f9020126109df2b3d118a42440e380c 100644 (file)
@@ -62,6 +62,7 @@ matrix:
           env: DB=sqlite ASSETS=build
     allow_failures:
         - php: hhvm-3.12
+        - php: 7.1
         - php: nightly
 
 # exclude v1 branches
index c741954973cc3321b78c893f3c92f32385e6b745..8dc5b83bdb3a8fd74d9298c6064085d7338687e4 100644 (file)
@@ -45,6 +45,7 @@ module.exports = function (grunt) {
         src: [
           '<%= appDir %>/themes/material/js/init.js',
           '<%= appDir %>/themes/_global/js/restoreScroll.js',
+          'node_modules/jquery.tinydot/src/jquery.tinydot.js',
         ],
         dest: '<%= buildDir %>/material.js',
       },
diff --git a/app/DoctrineMigrations/Version20160410190541.php b/app/DoctrineMigrations/Version20160410190541.php
new file mode 100644 (file)
index 0000000..4014857
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+
+namespace Application\Migrations;
+
+use Doctrine\DBAL\Migrations\AbstractMigration;
+use Doctrine\DBAL\Schema\Schema;
+use Symfony\Component\DependencyInjection\ContainerAwareInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+class Version20160410190541 extends AbstractMigration implements ContainerAwareInterface
+{
+    /**
+     * @var ContainerInterface
+     */
+    private $container;
+
+    public function setContainer(ContainerInterface $container = null)
+    {
+        $this->container = $container;
+    }
+
+    private function getTable($tableName)
+    {
+        return $this->container->getParameter('database_table_prefix') . $tableName;
+    }
+
+    /**
+     * @param Schema $schema
+     */
+    public function up(Schema $schema)
+    {
+        $this->addSql('ALTER TABLE `'.$this->getTable('entry').'` ADD `uuid` LONGTEXT DEFAULT NULL');
+        $this->addSql("INSERT INTO `".$this->getTable('craue_config_setting')."` (`name`, `value`, `section`) VALUES ('share_public', '1', 'entry')");
+    }
+
+    /**
+     * @param Schema $schema
+     */
+    public function down(Schema $schema)
+    {
+        $this->abortIf($this->connection->getDatabasePlatform()->getName() != 'sqlite', 'This down migration can\'t be executed on SQLite databases, because SQLite don\'t support DROP COLUMN.');
+
+        $this->addSql('ALTER TABLE `'.$this->getTable('entry').'` DROP `uuid`');
+        $this->addSql("DELETE FROM `".$this->getTable('craue_config_setting')."` WHERE `name` = 'share_public'");
+    }
+}
index 58f070c7e152b21b0bd24ebc2f24bc5d799695d5..9adfdc8be35e87f66fcc49fac5c161e56ef1ae75 100644 (file)
@@ -4,15 +4,32 @@ namespace Application\Migrations;
 
 use Doctrine\DBAL\Migrations\AbstractMigration;
 use Doctrine\DBAL\Schema\Schema;
+use Symfony\Component\DependencyInjection\ContainerAwareInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
-class Version20160812120952 extends AbstractMigration
+class Version20160812120952 extends AbstractMigration implements ContainerAwareInterface
 {
+    /**
+     * @var ContainerInterface
+     */
+    private $container;
+
+    public function setContainer(ContainerInterface $container = null)
+    {
+        $this->container = $container;
+    }
+
+    private function getTable($tableName)
+    {
+        return $this->container->getParameter('database_table_prefix') . $tableName;
+    }
+
     /**
      * @param Schema $schema
      */
     public function up(Schema $schema)
     {
-        $this->addSql('ALTER TABLE wallabag_oauth2_clients ADD name CLOB DEFAULT NULL COLLATE BINARY');
+        $this->addSql('ALTER TABLE '.$this->getTable('oauth2_clients').' ADD name longtext COLLATE \'utf8_unicode_ci\' DEFAULT NULL');
     }
 
     /**
@@ -21,7 +38,7 @@ class Version20160812120952 extends AbstractMigration
     public function down(Schema $schema)
     {
         $this->abortIf($this->connection->getDatabasePlatform()->getName() == 'sqlite', 'Migration can only be executed safely on \'mysql\' or \'postgresql\'.');
-        $this->addSql('ALTER TABLE wallabag_oauth2_clients DROP COLUMN name;
-');
+
+        $this->addSql('ALTER TABLE '.$this->getTable('oauth2_clients').' DROP COLUMN name');
     }
 }
index c46fdb142519f82f47424cfb0f03d07fa57042f6..3478d6381edd5af416a4eb14cbff4b736b35eb62 100644 (file)
@@ -27,3 +27,4 @@ piwik_site_id: ID for din side hos Piwik
 piwik_enabled: Aktiver Piwik
 demo_mode_enabled: "Aktiver demo-indstilling? (anvendes kun til wallabags offentlige demo)"
 demo_mode_username: "Demobruger"
+# share_public: Allow public url for entries
index fa2093b52c5dc6ce23d85157a79e6adfc4b0e7cd..f655a27fcd4c06957fdd2a2d3765614f9955010a 100644 (file)
@@ -27,3 +27,4 @@ piwik_site_id: ID deiner Webseite in Piwik
 piwik_enabled: Piwik aktivieren
 demo_mode_enabled: "Test-Modus aktivieren? (nur für die öffentliche wallabag-Demo genutzt)"
 demo_mode_username: "Test-Benutzer"
+# share_public: Allow public url for entries
index b627376ec72700f1032e880b03e8e76b340fff3e..48d0ec4b4a082dbea8d794653116ada901c72c35 100644 (file)
@@ -27,3 +27,4 @@ piwik_site_id: ID of your website in Piwik
 piwik_enabled: Enable Piwik
 demo_mode_enabled: "Enable demo mode ? (only used for the wallabag public demo)"
 demo_mode_username: "Demo user"
+share_public: Allow public url for entries
index af1657e4a466680f87d0a0137866e5c0c9177bae..7aac9adff9160a92666cbce879625b8997cd98f6 100644 (file)
@@ -27,3 +27,4 @@ piwik_site_id: ID de tu website de Piwik
 piwik_enabled: Activar Piwik
 demo_mode_enabled: "Activar modo demo (sólo usado para la demo de wallabag)"
 demo_mode_username: "Nombre de usuario demo"
+# share_public: Allow public url for entries
index 7cb4e833a06dd1139f0c105b17dcc527f47049bc..613cf86d4d0f307daaac1917bf63942bea115094 100644 (file)
@@ -22,3 +22,9 @@ export: "برون‌سپاری"
 import: "درون‌ریزی"
 misc: "غیره"
 modify_settings: "اعمال"
+# piwik_host: Host of your website in Piwik
+# piwik_site_id: ID of your website in Piwik
+# piwik_enabled: Enable Piwik
+# demo_mode_enabled: "Enable demo mode ? (only used for the wallabag public demo)"
+# demo_mode_username: "Demo user"
+# share_public: Allow public url for entries
index 084eb6df11c30eb01205b4a8c564dd549739178b..f21f2439ac0222d388177e8a27c7df3592d726ee 100644 (file)
@@ -27,3 +27,4 @@ piwik_site_id: ID de votre site dans Piwik
 piwik_enabled: Activer Piwik
 demo_mode_enabled: "Activer le mode démo ? (utiliser uniquement pour la démo publique de wallabag)"
 demo_mode_username: "Utilisateur de la démo"
+share_public: Autoriser une URL publique pour les articles
index e4417139358a069e8d0aee90c4fad2475719c2c5..1202edd582c95e76b435c371d3758e7a575f4408 100644 (file)
@@ -27,3 +27,4 @@ piwik_site_id: ID del tuo sito in Piwik
 piwik_enabled: Abilita Piwik
 demo_mode_enabled: "Abilita modalità demo ? (usato solo per la demo pubblica di wallabag)"
 demo_mode_username: "Utente Demo"
+# share_public: Allow public url for entries
index 67880b8bbb0ec7e5455852d93e5c3e84cfd7d47c..dd91ee497934c547ab943942199b659f9f1a3796 100644 (file)
@@ -27,3 +27,4 @@ piwik_site_id: ID de vòstre site dins Piwik
 piwik_enabled: Activar Piwik
 demo_mode_enabled: "Activar lo mode demostracion ? (utilizar solament per la demostracion publica de wallabag)"
 demo_mode_username: "Utilizaire de la demostracion"
+# share_public: Allow public url for entries
index 87ca5060e5c4654704af98bed460622b6b957678..93b36c8f644f5f1ad1e52993f93310f2e3b051f2 100644 (file)
@@ -22,3 +22,9 @@ export: "eksport"
 import: "import"
 misc: "różne"
 modify_settings: "zatwierdz"
+# piwik_host: Host of your website in Piwik
+# piwik_site_id: ID of your website in Piwik
+# piwik_enabled: Enable Piwik
+# demo_mode_enabled: "Enable demo mode ? (only used for the wallabag public demo)"
+# demo_mode_username: "Demo user"
+# share_public: Allow public url for entries
index f5ff8c6a458194c0716851f258ce13bb27a6b04d..326c9f90ff753d73bf2e288f7b33f5454c9fa14f 100644 (file)
@@ -22,3 +22,9 @@ export: "exportă"
 import: "importă"
 misc: "diverse"
 modify_settings: "aplică"
+# piwik_host: Host of your website in Piwik
+# piwik_site_id: ID of your website in Piwik
+# piwik_enabled: Enable Piwik
+# demo_mode_enabled: "Enable demo mode ? (only used for the wallabag public demo)"
+# demo_mode_username: "Demo user"
+# share_public: Allow public url for entries
index 6f187ff54f863fa2a2f71d052d6c8751819b6b5d..f7f6a8ad341ca974558e9bec04f56a1a3e2664f6 100755 (executable)
@@ -26,7 +26,9 @@
   div.tools,
   header div,
   .messages,
-  .entrie + .results {
+  .entrie + .results,
+  #article .mbm a,
+  #article-informations {
     display: none !important;
   }
 
index 99892401620b43404b7f3ad284bb90ff57ae69ee..f4230734cf4e674d0c206fec20e47ce50812fbdd 100755 (executable)
@@ -91,6 +91,7 @@ body {
 
 body.login main {
   padding: 0;
+  min-height: 100vh;
 }
 
 .border-bottom {
@@ -256,6 +257,10 @@ nav input {
   display: none;
 }
 
+.input-field.nav-panel-add, .input-field.nav-panel-add form {
+    height: 100%;
+}
+
 /* ==========================================================================
    2 = Side-nav
    ========================================================================== */
@@ -346,6 +351,12 @@ main ul.row {
 
 .card .card-content .card-title {
   line-height: 32px;
+  max-height: 64px;
+  display: block;
+}
+
+.card .card-content i.right, .card .card-reveal i.right {
+    margin-left: 0;
 }
 
 .card .card-entry-labels {
@@ -634,6 +645,10 @@ main ul.row {
   content: "\eae6";
 }
 
+.icon-link::before {
+    content: "\e9cb";
+}
+
 footer [class^="icon-"],
 footer [class*=" icon-"] {
   font-size: 2em;
@@ -660,3 +675,7 @@ div.settings div.input-field ul {
 div.settings div.file-field div {
   margin-top: inherit;
 }
+
+.input-field label.active {
+    font-size: 1rem;
+}
index a16be71e945bb4e7bd7e55608742cea3ef2979db..1eb96735aadcf5a27119eccddd1df78e5432b347 100755 (executable)
   div.tools,
   header div,
   .messages,
-  .entry + .results {
+  .entry + .results,
+  #slide-out,
+  .progress, .hide-on-large-only,
+  #article > aside,
+  #article .mbm a
+  {
     display: none !important;
   }
 
+  main {
+    padding-left: 0 !important;
+  }
+
+  #article {
+    margin: inherit !important;
+  }
+
   article {
     border: none !important;
   }
index eb53fc5d45b5a819a08aa857c4f6237b6f5ce58d..31bd8a8c0597a96cc533a7e7a27ccc402504694a 100644 (file)
@@ -51,6 +51,9 @@ wallabag_core:
     reading_speed: 1
     cache_lifetime: 10
 
+wallabag_user:
+    registration_enabled: "%fosuser_registration%"
+
 wallabag_import:
     allow_mimetypes: ['application/octet-stream', 'application/json', 'text/plain']
     resource_dir: "%kernel.root_dir%/../web/uploads/import"
index d45839f4dacedf3386d332ef3a515d4497b740e3..d092e13932586cba0901c6ce33bb4e832c4fe268 100644 (file)
@@ -34,6 +34,7 @@ parameters:
     twofactor_sender: no-reply@wallabag.org
 
     # fosuser stuff
+    fosuser_registration: true
     fosuser_confirmation: true
 
     from_email: no-reply@wallabag.org
index e24e03df2e921d98eff8325b86a1ae2733a8ea6d..1f30e58b845abceb50011a28ed32b0f7ab57daca 100644 (file)
@@ -60,6 +60,7 @@ security:
         - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
         - { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
         - { path: /(unread|starred|archive).xml$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
+        - { path: ^/share, roles: IS_AUTHENTICATED_ANONYMOUSLY }
         - { path: ^/settings, roles: ROLE_SUPER_ADMIN }
         - { path: ^/annotations, roles: ROLE_USER }
         - { path: ^/, roles: ROLE_USER }
diff --git a/docs/de/conf.py b/docs/de/conf.py
new file mode 100644 (file)
index 0000000..8f2d130
--- /dev/null
@@ -0,0 +1,55 @@
+# -*- 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.0.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/requirements.txt b/docs/de/requirements.txt
new file mode 100644 (file)
index 0000000..06fc897
--- /dev/null
@@ -0,0 +1,2 @@
+Sphinx>=1.3.0,<1.4.0
+guzzle_sphinx_theme>=0.7.0,<0.8.0
index b8e3e9999733292cb7b5c117b2b0ea103a61a60b..296215598da436877c50e341669c046e7ba40170 100644 (file)
@@ -12,7 +12,7 @@ Das kann verschiedene Ursachen haben:
 Wie kann ich helfen das zu beheben?
 -----------------------------------
 
-- `indem du uns eine Mail mit der URL des Artikels sendest <mailto:hello\@wallabag.org>`_
+- `indem du uns eine Mail mit der URL des Artikels sendest <mailto:hello@wallabag.org>`_
 - indem du versuchst das Laden des Artikels durch Erstellen einer Datei für den Artikel
   selbst zu beheben
   Du kannst `dieses Tool <http://siteconfig.fivefilters.org/>`__ nutzen.
index da6ee7450720cf88de219e198835227804a009d5..68892750350409d837bdc3db42f54395d6c1efef 100644 (file)
@@ -12,7 +12,7 @@ There may be several reasons:
 How can I help to fix that?
 ---------------------------
 
-- `by sending us an email with the article's URL <mailto:hello\@wallabag.org>`_
+- `by sending us an email with the article's URL <mailto:hello@wallabag.org>`_
 - by trying to fix this article by yourself :) by creating a file for the article.
   You can use `this tool <http://siteconfig.fivefilters.org/>`__.
 
index 63210484700ccafb0b503707a67b416d9198124d..e6c37d722c48a746357ce408367c477462317337 100644 (file)
@@ -30,14 +30,25 @@ 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
 (we need to work on this import to improve it).
 
-From Instapaper
----------------
-
-*Feature not yet implemented in wallabag v2.*
-
 From Readability
 ----------------
 
+Export your Readability data
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+On the tools (`https://www.readability.com/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 (we need to work on this import to improve it).
+
+From Instapaper
+---------------
+
 *Feature not yet implemented in wallabag v2.*
 
 From HTML or JSON file
index 0f26fbc805df7f11a475aca0e66c5a2129fc8ef7..74290bef405f9dc78d24771b2f32e82fa57021ce 100644 (file)
@@ -12,7 +12,7 @@ Il peut y avoir plusieurs raisons :
 Comment puis-je aider pour réparer ça ?
 ---------------------------------------
 
-- `en nous envoyant un email avec l'URL de l'article <mailto:hello\@wallabag.org>`_
+- `en nous envoyant un email avec l'URL de l'article <mailto:hello@wallabag.org>`_
 - en essayant de réparer cet article par vous-même :) en créant un fichier pour l'article.
   Vous pouvez utiliser `cet outil <http://siteconfig.fivefilters.org/>`__.
 
index e6c2fa02d2092b8baf3e0694ac78b4a360c4c953..99ac602b6313c9f11127b294ff529b39ac507472 100644 (file)
@@ -30,15 +30,27 @@ 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 (nous devons encore travailler pour améliorer cet import).
 
+Depuis Readability
+------------------
+
+Exportez vos données de Readability
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Sur la page des outils (`https://www.readability.com/tools/<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 (nous devons encore travailler pour améliorer cet import).
+
 Depuis Instapaper
 -----------------
 
 *Fonctionnalité pas encore implémentée dans wallabag v2.*
 
-Depuis Readability
-------------------
-
-*Fonctionnalité pas encore implémentée dans wallabag v2.*
 
 Depuis un fichier HTML ou JSON
 ------------------------------
index 3ed7a49f4689b5f3eb0ccfd7f15a5fb0e9c8e817..39a10e9c949f868d1b65ea97a18cc6680d6038ec 100644 (file)
@@ -28,6 +28,7 @@
     "jquery": "^2.2.1",
     "jquery-ui": "^1.10.5",
     "jquery.cookie": "^1.4.1",
+    "jquery.tinydot": "^0.2.1",
     "load-grunt-tasks": "^3.4.1",
     "materialize-css": "0.97.5",
     "npm": "^3.8.3",
index 03eb9b0848272381bfbc3dd550c476068d55dcd3..869fdc56ab1ba54dbf787d3f281fc357a180fa44 100644 (file)
@@ -329,6 +329,77 @@ class WallabagRestController extends FOSRestController
         return $this->renderJsonResponse($json);
     }
 
+    /**
+     * Permanently remove one tag from **every** entry.
+     *
+     * @ApiDoc(
+     *      requirements={
+     *          {"name"="tag", "dataType"="string", "required"=true, "requirement"="\w+", "description"="Tag as a string"}
+     *      }
+     * )
+     *
+     * @return Response
+     */
+    public function deleteTagLabelAction(Request $request)
+    {
+        $this->validateAuthentication();
+        $label = $request->request->get('tag', '');
+
+        $tag = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneByLabel($label);
+
+        if (empty($tag)) {
+            throw $this->createNotFoundException('Tag not found');
+        }
+
+        $this->getDoctrine()
+            ->getRepository('WallabagCoreBundle:Entry')
+            ->removeTag($this->getUser()->getId(), $tag);
+
+        $json = $this->get('serializer')->serialize($tag, 'json');
+
+        return $this->renderJsonResponse($json);
+    }
+
+    /**
+     * Permanently remove some tags from **every** entry.
+     *
+     * @ApiDoc(
+     *      requirements={
+     *          {"name"="tags", "dataType"="string", "required"=true, "format"="tag1,tag2", "description"="Tags as strings (comma splitted)"}
+     *      }
+     * )
+     *
+     * @return Response
+     */
+    public function deleteTagsLabelAction(Request $request)
+    {
+        $this->validateAuthentication();
+
+        $tagsLabels = $request->request->get('tags', '');
+
+        $tags = [];
+
+        foreach (explode(',', $tagsLabels) as $tagLabel) {
+            $tagEntity = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findOneByLabel($tagLabel);
+
+            if (!empty($tagEntity)) {
+                $tags[] = $tagEntity;
+            }
+        }
+
+        if (empty($tags)) {
+            throw $this->createNotFoundException('Tags not found');
+        }
+
+        $this->getDoctrine()
+            ->getRepository('WallabagCoreBundle:Entry')
+            ->removeTags($this->getUser()->getId(), $tags);
+
+        $json = $this->get('serializer')->serialize($tags, 'json');
+
+        return $this->renderJsonResponse($json);
+    }
+
     /**
      * Permanently remove one tag from **every** entry.
      *
@@ -352,6 +423,7 @@ class WallabagRestController extends FOSRestController
 
         return $this->renderJsonResponse($json);
     }
+
     /**
      * Retrieve version number.
      *
index e134ced52b2cc5116bb97c7f6dc01bc5895d002a..143def4f158ea572ad871db1fbbbc2316df3e1e1 100644 (file)
@@ -241,6 +241,11 @@ class InstallCommand extends ContainerAwareCommand
         $em->createQuery('DELETE FROM CraueConfigBundle:Setting')->execute();
 
         $settings = [
+            [
+                'name' => 'share_public',
+                'value' => '1',
+                'section' => 'entry',
+            ],
             [
                 'name' => 'carrot',
                 'value' => '1',
index ccdf940669bde3d5aa00073256c5607e8ba1805c..624576b5bee67a43be98418a72e5c4ce93831233 100644 (file)
@@ -4,7 +4,6 @@ namespace Wallabag\CoreBundle\Controller;
 
 use Pagerfanta\Adapter\DoctrineORMAdapter;
 use Pagerfanta\Exception\OutOfRangeCurrentPageException;
-use Pagerfanta\Pagerfanta;
 use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
 use Symfony\Bundle\FrameworkBundle\Controller\Controller;
 use Symfony\Component\HttpFoundation\Request;
@@ -13,6 +12,7 @@ use Wallabag\CoreBundle\Entity\Entry;
 use Wallabag\CoreBundle\Form\Type\EntryFilterType;
 use Wallabag\CoreBundle\Form\Type\EditEntryType;
 use Wallabag\CoreBundle\Form\Type\NewEntryType;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
 
 class EntryController extends Controller
 {
@@ -226,6 +226,10 @@ class EntryController extends Controller
         $repository = $this->get('wallabag_core.entry_repository');
 
         switch ($type) {
+            case 'untagged':
+                $qb = $repository->getBuilderForUntaggedByUser($this->getUser()->getId());
+
+                break;
             case 'starred':
                 $qb = $repository->getBuilderForStarredByUser($this->getUser()->getId());
                 break;
@@ -257,9 +261,10 @@ class EntryController extends Controller
         }
 
         $pagerAdapter = new DoctrineORMAdapter($qb->getQuery());
-        $entries = new Pagerfanta($pagerAdapter);
 
-        $entries->setMaxPerPage($this->getUser()->getConfig()->getItemsPerPage());
+        $entries = $this->get('wallabag_core.helper.prepare_pager_for_entries')
+            ->prepare($pagerAdapter, $page);
+
         try {
             $entries->setCurrentPage($page);
         } catch (OutOfRangeCurrentPageException $e) {
@@ -434,7 +439,7 @@ class EntryController extends Controller
      */
     private function checkUserAction(Entry $entry)
     {
-        if ($this->getUser()->getId() != $entry->getUser()->getId()) {
+        if (null === $this->getUser() || $this->getUser()->getId() != $entry->getUser()->getId()) {
             throw $this->createAccessDeniedException('You can not access this entry.');
         }
     }
@@ -450,4 +455,91 @@ class EntryController extends Controller
     {
         return $this->get('wallabag_core.entry_repository')->findByUrlAndUserId($entry->getUrl(), $this->getUser()->getId());
     }
+
+    /**
+     * Get public URL for entry (and generate it if necessary).
+     *
+     * @param Entry $entry
+     *
+     * @Route("/share/{id}", requirements={"id" = "\d+"}, name="share")
+     *
+     * @return \Symfony\Component\HttpFoundation\Response
+     */
+    public function shareAction(Entry $entry)
+    {
+        $this->checkUserAction($entry);
+
+        if (null === $entry->getUuid()) {
+            $entry->generateUuid();
+
+            $em = $this->getDoctrine()->getManager();
+            $em->persist($entry);
+            $em->flush();
+        }
+
+        return $this->redirect($this->generateUrl('share_entry', [
+            'uuid' => $entry->getUuid(),
+        ]));
+    }
+
+    /**
+     * Disable public sharing for an entry.
+     *
+     * @param Entry $entry
+     *
+     * @Route("/share/delete/{id}", requirements={"id" = "\d+"}, name="delete_share")
+     *
+     * @return \Symfony\Component\HttpFoundation\Response
+     */
+    public function deleteShareAction(Entry $entry)
+    {
+        $this->checkUserAction($entry);
+
+        $entry->cleanUuid();
+
+        $em = $this->getDoctrine()->getManager();
+        $em->persist($entry);
+        $em->flush();
+
+        return $this->redirect($this->generateUrl('view', [
+            'id' => $entry->getId(),
+        ]));
+    }
+
+    /**
+     * Ability to view a content publicly.
+     *
+     * @param Entry $entry
+     *
+     * @Route("/share/{uuid}", requirements={"uuid" = ".+"}, name="share_entry")
+     * @Cache(maxage="25200", smaxage="25200", public=true)
+     *
+     * @return \Symfony\Component\HttpFoundation\Response
+     */
+    public function shareEntryAction(Entry $entry)
+    {
+        if (!$this->get('craue_config')->get('share_public')) {
+            throw $this->createAccessDeniedException('Sharing an entry is disabled for this user.');
+        }
+
+        return $this->render(
+            '@WallabagCore/themes/share.html.twig',
+            ['entry' => $entry]
+        );
+    }
+
+    /**
+     * Shows untagged articles for current user.
+     *
+     * @param Request $request
+     * @param int     $page
+     *
+     * @Route("/untagged/list/{page}", name="untagged", defaults={"page" = "1"})
+     *
+     * @return \Symfony\Component\HttpFoundation\Response
+     */
+    public function showUntaggedEntriesAction(Request $request, $page)
+    {
+        return $this->showEntries('untagged', $request, $page);
+    }
 }
index 944c755def342a0a8b1b99484df7776212d5ed25..6191d5d7973241f4582007b1db063766856e9f9b 100644 (file)
@@ -46,7 +46,7 @@ class ExportController extends Controller
      *
      * @Route("/export/{category}.{format}", name="export_entries", requirements={
      *     "format": "epub|mobi|pdf|json|xml|txt|csv",
-     *     "category": "all|unread|starred|archive"
+     *     "category": "all|unread|starred|archive|tag_entries|untagged"
      * })
      *
      * @return \Symfony\Component\HttpFoundation\Response
index 8645fb442621b933a10df0d127c5919b4cecd3d6..1cbc413d644e616727ce0dca82af9b6f5582cd15 100644 (file)
@@ -2,12 +2,15 @@
 
 namespace Wallabag\CoreBundle\Controller;
 
+use Pagerfanta\Adapter\ArrayAdapter;
+use Pagerfanta\Exception\OutOfRangeCurrentPageException;
 use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
 use Symfony\Bundle\FrameworkBundle\Controller\Controller;
 use Symfony\Component\HttpFoundation\Request;
 use Wallabag\CoreBundle\Entity\Entry;
 use Wallabag\CoreBundle\Entity\Tag;
 use Wallabag\CoreBundle\Form\Type\NewTagType;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
 
 class TagController extends Controller
 {
@@ -90,4 +93,45 @@ class TagController extends Controller
             ]
         );
     }
+
+    /**
+     * @param Tag $tag
+     * @param int $page
+     *
+     * @Route("/tag/list/{slug}/{page}", name="tag_entries", defaults={"page" = "1"})
+     * @ParamConverter("tag", options={"mapping": {"slug": "slug"}})
+     *
+     * @return \Symfony\Component\HttpFoundation\Response
+     */
+    public function showEntriesForTagAction(Tag $tag, $page, Request $request)
+    {
+        $entriesByTag = $this->getDoctrine()
+            ->getRepository('WallabagCoreBundle:Entry')
+            ->findAllByTagId($this->getUser()->getId(), $tag->getId());
+
+        $pagerAdapter = new ArrayAdapter($entriesByTag);
+
+        $entries = $this->get('wallabag_core.helper.prepare_pager_for_entries')
+            ->prepare($pagerAdapter, $page);
+
+        try {
+            $entries->setCurrentPage($page);
+        } catch (OutOfRangeCurrentPageException $e) {
+            if ($page > 1) {
+                return $this->redirect($this->generateUrl($request->get('_route'), [
+                    'slug' => $tag->getSlug(),
+                    'page' => $entries->getNbPages(),
+                ]), 302);
+            }
+        }
+
+        return $this->render(
+            'WallabagCoreBundle:Entry:entries.html.twig',
+            [
+                'form' => null,
+                'entries' => $entries,
+                'currentPage' => $page,
+            ]
+        );
+    }
 }
index 0905879616e06afa13b302c62df918dfcc8c47c6..b4309304ecfea459328a239a337d994b36894f7e 100644 (file)
@@ -15,6 +15,11 @@ class LoadSettingData extends AbstractFixture implements OrderedFixtureInterface
     public function load(ObjectManager $manager)
     {
         $settings = [
+            [
+                'name' => 'share_public',
+                'value' => '1',
+                'section' => 'entry',
+            ],
             [
                 'name' => 'carrot',
                 'value' => '1',
index 8553dced4596c36abe852aaaabc6b58666a563d0..09e99f36bb46b222544d6cfe11484e270231ccac 100644 (file)
@@ -28,6 +28,13 @@ class LoadTagData extends AbstractFixture implements OrderedFixtureInterface
 
         $this->addReference('bar-tag', $tag2);
 
+        $tag3 = new Tag();
+        $tag3->setLabel('baz');
+
+        $manager->persist($tag3);
+
+        $this->addReference('baz-tag', $tag3);
+
         $manager->flush();
     }
 
index ceae78b05556e4005f0c51e35e1eb93a5bf6c57a..4d7e001bed619a58bc85d1f1d886fd8f21130208 100644 (file)
@@ -37,6 +37,15 @@ class Entry
      */
     private $id;
 
+    /**
+     * @var string
+     *
+     * @ORM\Column(name="uuid", type="text", nullable=true)
+     *
+     * @Groups({"entries_for_user", "export_all"})
+     */
+    private $uuid;
+
     /**
      * @var string
      *
@@ -595,4 +604,37 @@ class Entry
     {
         return $this->language;
     }
+
+    /**
+     * @return string
+     */
+    public function getUuid()
+    {
+        return $this->uuid;
+    }
+
+    /**
+     * @param string $uuid
+     *
+     * @return Entry
+     */
+    public function setUuid($uuid)
+    {
+        $this->uuid = $uuid;
+
+        return $this;
+    }
+
+    public function generateUuid()
+    {
+        if (null === $this->uuid) {
+            // @see http://blog.kevingomez.fr/til/2015/07/26/why-is-uniqid-slow/ for true parameter
+            $this->uuid = uniqid('', true);
+        }
+    }
+
+    public function cleanUuid()
+    {
+        $this->uuid = null;
+    }
 }
diff --git a/src/Wallabag/CoreBundle/Helper/PreparePagerForEntries.php b/src/Wallabag/CoreBundle/Helper/PreparePagerForEntries.php
new file mode 100644 (file)
index 0000000..f9066be
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+
+namespace Wallabag\CoreBundle\Helper;
+
+use Pagerfanta\Adapter\AdapterInterface;
+use Pagerfanta\Pagerfanta;
+use Symfony\Component\Routing\Router;
+use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
+
+class PreparePagerForEntries
+{
+    private $user;
+    private $router;
+
+    public function __construct(TokenStorage $token, Router $router)
+    {
+        $this->user = $token->getToken()->getUser();
+        $this->router = $router;
+    }
+
+    /**
+     * @param AdapterInterface $adapter
+     * @param int              $page
+     *
+     * @return null|Pagerfanta
+     */
+    public function prepare(AdapterInterface $adapter, $page = 1)
+    {
+        $entries = new Pagerfanta($adapter);
+        $entries->setMaxPerPage($this->user->getConfig()->getItemsPerPage());
+
+        return $entries;
+    }
+}
index 4b205f6e52637ba69dfa8415815256aba5f875ea..24d1a57af0550eda5782c3fdd40d86f22c5aca24 100644 (file)
@@ -87,6 +87,22 @@ class EntryRepository extends EntityRepository
         ;
     }
 
+    /**
+     * Retrieves untagged entries for a user.
+     *
+     * @param int $userId
+     *
+     * @return QueryBuilder
+     */
+    public function getBuilderForUntaggedByUser($userId)
+    {
+        return $this
+            ->getBuilderByUser($userId)
+            ->leftJoin('e.tags', 't')
+            ->groupBy('e.id')
+            ->having('count(t.id) = 0');
+    }
+
     /**
      * Find Entries.
      *
@@ -225,6 +241,19 @@ class EntryRepository extends EntityRepository
         $this->getEntityManager()->flush();
     }
 
+    /**
+     * Remove tags from all user entries.
+     *
+     * @param int        $userId
+     * @param Array<Tag> $tags
+     */
+    public function removeTags($userId, $tags)
+    {
+        foreach ($tags as $tag) {
+            $this->removeTag($userId, $tag);
+        }
+    }
+
     /**
      * Find all entries that are attached to a give tag id.
      *
index b70d9b8cbb41466a025b78b654e3df47282ea85a..1c1457e705fee2200503b3a53128b22ab4c6a69f 100644 (file)
@@ -121,3 +121,9 @@ services:
         class: Wallabag\CoreBundle\Helper\Redirect
         arguments:
             - "@router"
+
+    wallabag_core.helper.prepare_pager_for_entries:
+        class: Wallabag\CoreBundle\Helper\PreparePagerForEntries
+        arguments:
+            - "@security.token_storage"
+            - "@router"
index c24475d2688b1780bd4e78a124d5a264f67e048b..073dee28e674ac1c6bdd335f5fc5ec99ae13ae4a 100644 (file)
@@ -139,6 +139,8 @@ entry:
         # starred: 'Starred entries'
         # archived: 'Archived entries'
         # filtered: 'Filtered entries'
+        # filtered_tags: 'Filtered by tags'
+        # untagged: 'Untagged entries'
     list:
         # number_on_the_page: '{0} There is no entry.|{1} There is one entry.|]1,Inf[ There are %count% entries.'
         reading_time: 'estimeret læsetid'
@@ -186,6 +188,8 @@ entry:
             add_a_tag: 'Tliføj et tag'
             share_content: 'Deling'
             # share_email_label: 'Email'
+            # public_link: 'public link'
+            # delete_public_link: 'delete public link'
             download: 'Download'
             # print: 'Print'
             problem:
@@ -288,6 +292,7 @@ quickstart:
     #     pocket: 'Migrate from Pocket'
     #     wallabag_v1: 'Migrate from wallabag v1'
     #     wallabag_v2: 'Migrate from wallabag v2'
+    #     readability: 'Migrate from Readability'
     # developer:
     #     title: 'Developers'
     #     create_application: 'Create your third application'
@@ -309,6 +314,7 @@ tag:
     page_title: 'Tags'
     list:
         # number_on_the_page: '{0} There is no tag.|{1} There is one tag.|]1,Inf[ There are %count% tags.'
+        # see_untagged_entries: 'See untagged entries'
 
 import:
     # page_title: 'Import'
@@ -336,6 +342,10 @@ import:
     # wallabag_v2:
     #     page_title: 'Import > Wallabag v2'
     #     description: 'This importer will import all your wallabag v2 articles. Go to All articles, then, on the export sidebar, click on "JSON". You will have a "All articles.json" file.'
+    # readability:
+    #     page_title: 'Import > Readability'
+    #     description: 'This importer will import all your Readability articles. 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).'
+    #     how_to: 'Please select your Readability export and click on the below button to upload and import it.'
 
 developer:
     # page_title: 'Developer'
index 384ec09a15eeda9ad4a312f2aa5332508bae6923..4cfd240f46b0e33438b73f807a5c05fbaf9f7dd9 100644 (file)
@@ -139,6 +139,8 @@ entry:
         starred: 'Favorisierte Einträge'
         archived: 'Archivierte Einträge'
         filtered: 'Gefilterte Einträge'
+        # filtered_tags: 'Filtered by tags'
+        # untagged: 'Untagged entries'
     list:
         number_on_the_page: '{0} Es gibt keine Einträge.|{1} Es gibt einen Eintrag.|]1,Inf[ Es gibt %count% Einträge.'
         reading_time: 'geschätzte Lesezeit'
@@ -186,6 +188,8 @@ entry:
             add_a_tag: 'Tag hinzufügen'
             share_content: 'Teilen'
             share_email_label: 'E-Mail'
+            # public_link: 'public link'
+            # delete_public_link: 'delete public link'
             download: 'Herunterladen'
             print: 'Drucken'
             problem:
@@ -288,6 +292,7 @@ quickstart:
         pocket: 'von Pocket migrieren'
         wallabag_v1: 'von wallabag v1 migrieren'
         wallabag_v2: 'von wallabag v2 migrieren'
+        readability: 'von Readability migrieren'
     developer:
         title: 'Entwickler'
         create_application: 'Erstelle eine Anwendung und nutze die wallabag API'
@@ -309,6 +314,7 @@ tag:
     page_title: 'Tags'
     list:
         number_on_the_page: '{0} Es gibt keine Tags.|{1} Es gibt einen Tag.|]1,Inf[ Es gibt %count% Tags.'
+        # see_untagged_entries: 'See untagged entries'
 
 import:
     page_title: 'Importieren'
@@ -336,6 +342,10 @@ import:
     wallabag_v2:
         page_title: 'Aus wallabag v2 importieren'
         description: 'Dieser Import wird all deine Artikel aus wallabag v2 importieren. Gehe auf "Alle Artikel" und dann, in der Exportieren-Seitenleiste auf "JSON". Dabei erhältst du eine "All articles.json"-Datei.'
+    readability:
+        page_title: 'Aus Readability importieren'
+    #     description: 'This importer will import all your Readability articles. 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).'
+    #     how_to: 'Please select your Readability export and click on the below button to upload and import it.'
 
 developer:
     page_title: 'Entwickler'
index ea860564b9eabf177b824bd59d6dfc241d15104e..42374b40e9e211fd025a07541907bf7763664cb8 100644 (file)
@@ -139,6 +139,8 @@ entry:
         starred: 'Starred entries'
         archived: 'Archived entries'
         filtered: 'Filtered entries'
+        filtered_tags: 'Filtered by tags'
+        untagged: 'Untagged entries'
     list:
         number_on_the_page: '{0} There are no entries.|{1} There is one entry.|]1,Inf[ There are %count% entries.'
         reading_time: 'estimated reading time'
@@ -186,6 +188,8 @@ entry:
             add_a_tag: 'Add a tag'
             share_content: 'Share'
             share_email_label: 'Email'
+            public_link: 'public link'
+            delete_public_link: 'delete public link'
             download: 'Download'
             print: 'Print'
             problem:
@@ -288,6 +292,7 @@ quickstart:
         pocket: 'Migrate from Pocket'
         wallabag_v1: 'Migrate from wallabag v1'
         wallabag_v2: 'Migrate from wallabag v2'
+        readability: 'Migrate from Readability'
     developer:
         title: 'Developers'
         create_application: 'Create your third application'
@@ -309,6 +314,7 @@ tag:
     page_title: 'Tags'
     list:
         number_on_the_page: '{0} There are no tags.|{1} There is one tag.|]1,Inf[ There are %count% tags.'
+        see_untagged_entries: 'See untagged entries'
 
 import:
     page_title: 'Import'
@@ -336,6 +342,10 @@ import:
     wallabag_v2:
         page_title: 'Import > Wallabag v2'
         description: 'This importer will import all your wallabag v2 articles. Go to All articles, then, on the export sidebar, click on "JSON". You will have a "All articles.json" file.'
+    readability:
+        page_title: 'Import > Readability'
+        description: 'This importer will import all your Readability articles. 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).'
+        how_to: 'Please select your Readability export and click on the below button to upload and import it.'
 
 developer:
     page_title: 'Developer'
index f64e95d5acac8bf3943db45d55f841169e93f8d4..ee84cc62ff0e2196363eddafe95876ad3244b5d0 100644 (file)
@@ -139,6 +139,8 @@ entry:
         starred: 'Artículos favoritos'
         archived: 'Artículos archivados'
         filtered: 'Artículos filtrados'
+        # filtered_tags: 'Filtered by tags'
+        # untagged: 'Untagged entries'
     list:
         number_on_the_page: '{0} No hay artículos.|{1} Hay un artículo.|]1,Inf[ Hay %count% artículos.'
         reading_time: 'tiempo estimado de lectura'
@@ -186,6 +188,8 @@ entry:
             add_a_tag: 'Añadir una etiqueta'
             share_content: 'Compartir'
             share_email_label: 'Dirección e-mail'
+            # public_link: 'public link'
+            # delete_public_link: 'delete public link'
             download: 'Descargar'
             print: 'Imprimir'
             problem:
@@ -288,6 +292,7 @@ quickstart:
         pocket: 'Migrar desde Pocket'
         wallabag_v1: 'Migrar desde wallabag v1'
         wallabag_v2: 'Migrar desde wallabag v2'
+        readability: 'Migrar desde Readability'
     developer:
         title: 'Promotores'
         create_application: 'Cree su tercera aplicación'
@@ -309,6 +314,7 @@ tag:
     page_title: 'Etiquetas'
     list:
         number_on_the_page: '{0} No hay ninguna etiqueta.|{1} Hay una etiqueta.|]1,Inf[ Hay %count% etiquetas.'
+        # see_untagged_entries: 'See untagged entries'
 
 import:
     page_title: 'Importar'
@@ -336,6 +342,10 @@ import:
     wallabag_v2:
         page_title: 'Importar > Wallabag v2'
         description: 'Va a importar sus artículos de otra instancia de wallabag v2. Vaya a Todos los artículos, entonces, en la barra lateral, oprima en "JSON". Usted tendrá un fichero "All articles.json"'
+    readability:
+        page_title: 'Importar > Readability'
+    #     description: 'This importer will import all your Readability articles. 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).'
+    #     how_to: 'Please select your Readability export and click on the below button to upload and import it.'
 
 developer:
     page_title: 'Promotor'
index e3592a782865c8bb0f0a6acfa7d7f116325e2086..e9af1e8d139e2b1b0af45b916164451a6782e3d1 100644 (file)
@@ -139,6 +139,8 @@ entry:
         starred: 'مقاله‌های برگزیده'
         archived: 'مقاله‌های بایگانی‌شده'
         filtered: 'مقاله‌های فیلترشده'
+        # filtered_tags: 'Filtered by tags'
+        # untagged: 'Untagged entries'
     list:
         number_on_the_page: '{0} هیج مقاله‌ای نیست.|{1} یک مقاله هست.|]1,Inf[ %count% مقاله هست.'
         reading_time: 'زمان تخمینی برای خواندن'
@@ -186,6 +188,8 @@ entry:
             add_a_tag: 'افزودن برچسب'
             share_content: 'هم‌رسانی'
             share_email_label: 'نشانی ایمیل'
+            # public_link: 'public link'
+            # delete_public_link: 'delete public link'
             download: 'بارگیری'
             print: 'چاپ'
             problem:
@@ -288,6 +292,7 @@ quickstart:
         pocket: 'مهاجرت از Pocket'
         wallabag_v1: 'مهاجرت از نسخهٔ یکم wallabag'
         wallabag_v2: 'مهاجرت از نسخهٔ دوم wallabag'
+        readability: 'مهاجرت از نسخهٔ دوم Readability'
     developer:
         title: 'برنامه‌نویسان'
         create_application: 'برنامهٔ wallabag خود را بسازید'
@@ -309,18 +314,19 @@ tag:
     page_title: 'برچسب‌ها'
     list:
         number_on_the_page: '{0} هیچ برچسبی نیست.|{1} یک برچسب هست.|]1,Inf[ %count% برچسب هست.'
+        # see_untagged_entries: 'See untagged entries'
 
 import:
-     page_title: 'درون‌ریزی'
-     page_description: 'به درون‌ریز wallabag خوش آمدید. لطفاً سرویس قبلی خود را که می‌خواهید از آن مهاجرت کنید انتخاب کنید.'
-     action:
+    page_title: 'درون‌ریزی'
+    page_description: 'به درون‌ریز wallabag خوش آمدید. لطفاً سرویس قبلی خود را که می‌خواهید از آن مهاجرت کنید انتخاب کنید.'
+    action:
         import_contents: 'درون‌ریزی مقاله‌ها'
-     form:
+    form:
         mark_as_read_title: 'علامت‌زدن همه به عنوان خوانده‌شده؟'
         mark_as_read_label: 'همهٔ مقاله‌های درون‌ریزی شده را به عنوان خوانده‌شده علامت بزن'
         file_label: 'پرونده'
         save_label: 'بارگذاری پرونده'
-     pocket:
+    pocket:
         page_title: 'درون‌ریزی > Pocket'
         description: "این برنامه همهٔ داده‌های Pocket شما را درون‌ریزی می‌کند. سرویس Pocket اجازه نمی‌دهد که متن مقاله‌ها را درون‌ریزی کنیم، بنابراین wallabag متن مقاله‌ها را دوباره از اینترنت دریافت می‌کند."
         config_missing:
@@ -329,13 +335,17 @@ import:
            user_message: 'مدیر سرور شما باید یک API Key برای Pocket تعریف کند.'
         authorize_message: 'شما می‌توانید داده‌هایتان را از حساب Pocket خود درون‌ریزی کنید. روی دکمهٔ زیر کلیک کنید و به برنامه اجازه دهید تا به getpocket.com وصل شود.'
         connect_to_pocket: 'به Pocket وصل شو و داده‌ها را دریافت کن'
-     wallabag_v1:
+    wallabag_v1:
         page_title: 'درون‌ریزی > Wallabag v1'
         description: 'این برنامه همهٔ داده‌های شما را در نسخهٔ ۱ wallabag درون‌ریزی می‌کند. در صفحهٔ تنظیمات، روی "JSON export" در بخش "Export your wallabag data" کلیک کنید. با این کار شما پرونده‌ای به شکل "wallabag-export-1-xxxx-xx-xx.json" دریافت خواهید کرد.'
         how_to: 'لطفاً پرونده را انتخاب کنید و روی دکمهٔ زیر کلیک کنید تا بارگذاری و درون‌ریزی شود.'
-     wallabag_v2:
+    wallabag_v2:
         page_title: 'درون‌ریزی > Wallabag v2'
         description: 'این برنامه همهٔ داده‌های شما را در نسخهٔ ۲ wallabag درون‌ریزی می‌کند. به بخش «همهٔ مقاله‌ها» بروید و در بخش «برون‌ریزی» روی "JSON" کلیک کنید. با این کار شما پرونده‌ای به شکل "All articles.json" دریافت خواهید کرد.'
+    readability:
+        page_title: 'درون‌ریزی > Readability'
+    #     description: 'This importer will import all your Readability articles. 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).'
+    #     how_to: 'Please select your Readability export and click on the below button to upload and import it.'
 
 developer:
     # page_title: 'Developer'
index 9e47d600a005af84c7b2faae6441c7514cfe659b..402cdf4a6736b6d430e7c3caeaf0f1bf74d79285 100644 (file)
@@ -139,6 +139,8 @@ entry:
         starred: 'Articles favoris'
         archived: 'Articles lus'
         filtered: 'Articles filtrés'
+        filtered_tags: 'Articles filtrés par tags'
+        untagged: 'Article sans tag'
     list:
         number_on_the_page: "{0} Il n'y a pas d'articles.|{1} Il y a un article.|]1,Inf[ Il y a %count% articles."
         reading_time: 'durée de lecture'
@@ -186,6 +188,8 @@ entry:
             add_a_tag: 'Ajouter un tag'
             share_content: 'Partager'
             share_email_label: 'Email'
+            public_link: 'Lien public'
+            delete_public_link: 'Supprimer lien public'
             download: 'Télécharger'
             print: 'Imprimer'
             problem:
@@ -288,6 +292,7 @@ quickstart:
         pocket: 'Migrer depuis Pocket'
         wallabag_v1: 'Migrer depuis wallabag v1'
         wallabag_v2: 'Migrer depuis wallabag v2'
+        readability: 'Migrer depuis Readability'
     developer:
         title: 'Pour les développeurs'
         create_application: 'Créer votre application tierce'
@@ -309,6 +314,7 @@ tag:
     page_title: 'Tags'
     list:
         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'
 
 import:
     page_title: 'Importer'
@@ -336,6 +342,10 @@ import:
     wallabag_v2:
         page_title: 'Importer > Wallabag v2'
         description: "Cet outil va importer tous vos articles d'une autre instance de wallabag v2. Allez dans tous vos articles, puis, sur la barre latérale, cliquez sur \"JSON\". Vous allez récupérer un fichier \"All articles.json\""
+    readability:
+        page_title: 'Importer > Readability'
+        description: 'Cet outil va importer toutes 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.'
+        how_to: "Choisissez le fichier de votre export Readability et cliquez sur le bouton ci-dessous pour l'importer."
 
 developer:
     page_title: 'Développeur'
index 3760c2d64f4d3de3e290a48d87cee085dcbf40b7..3aee4816727db48824101bcedfa0eabe33613dbc 100644 (file)
@@ -139,6 +139,8 @@ entry:
         starred: 'Contenuti preferiti'
         archived: 'Contenuti archiviati'
         filtered: 'Contenuti filtrati'
+        # filtered_tags: 'Filtered by tags'
+        # untagged: 'Untagged entries'
     list:
         number_on_the_page: "{0} Non ci sono contenuti.|{1} C'è un contenuto.|]1,Inf[ Ci sono %count% contenuti."
         reading_time: 'tempo di lettura stimato'
@@ -185,6 +187,8 @@ entry:
             add_a_tag: 'Aggiungi un tag'
             share_content: 'Condividi'
             share_email_label: 'E-mail'
+            # public_link: 'public link'
+            # delete_public_link: 'delete public link'
             download: 'Download'
             print: 'Stampa'
             problem:
@@ -287,6 +291,7 @@ quickstart:
         pocket: 'Trasferisci da Pocket'
         wallabag_v1: 'Trasferisci da wallabag v1'
         wallabag_v2: 'Trasferisci da wallabag v2'
+        readability: 'Trasferisci da Readability'
     developer:
         title: 'Sviluppatori'
         create_application: 'Crea la tua applicazione'
@@ -308,6 +313,7 @@ tag:
     page_title: 'Tags'
     list:
         number_on_the_page: "{0} Non ci sono tag.|{1} C'è un tag.|]1,Inf[ ci sono %count% tag."
+        # see_untagged_entries: 'See untagged entries'
 
 import:
     page_title: 'Importa'
@@ -335,6 +341,10 @@ import:
     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".'
+    readability:
+        page_title: 'Importa da > Readability'
+    #     description: 'This importer will import all your Readability articles. 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).'
+    #     how_to: 'Please select your Readability export and click on the below button to upload and import it.'
 
 developer:
     page_title: 'Sviluppatori'
index 1e23168bf7ff8c49e20837ef0f741037f4f98b45..855f236152b7cb86fdfa73287b4d3b05fad88e26 100644 (file)
@@ -139,6 +139,8 @@ entry:
         starred: 'Articles favorits'
         archived: 'Articles legits'
         filtered: 'Articles filtrats'
+        # filtered_tags: 'Filtered by tags'
+        # untagged: 'Untagged entries'
     list:
         number_on_the_page: "{0} I a pas cap d'article.|{1} I a un article.|]1,Inf[ I a %count% articles."
         reading_time: 'durada de lectura'
@@ -186,6 +188,8 @@ entry:
             add_a_tag: 'Ajustar una etiqueta'
             share_content: 'Partatjar'
             share_email_label: 'Corrièl'
+            # public_link: 'public link'
+            # delete_public_link: 'delete public link'
             download: 'Telecargar'
             print: 'Imprimir'
             problem:
@@ -288,6 +292,7 @@ quickstart:
         pocket: 'Migrar dempuèi Pocket'
         wallabag_v1: 'Migrar dempuèi wallabag v1'
         wallabag_v2: 'Migrar dempuèi wallabag v2'
+        readability: 'Migrar dempuèi Readability'
     developer:
         title: 'Pels desvolopadors'
         create_application: 'Crear vòstra aplicacion tèrça'
@@ -309,6 +314,7 @@ tag:
     page_title: 'Etiquetas'
     list:
         number_on_the_page: "{0} I a pas cap d'etiquetas.|{1} I a una etiqueta.|]1,Inf[ I a %count% etiquetas."
+        # see_untagged_entries: 'See untagged entries'
 
 import:
     page_title: 'Importar'
@@ -336,6 +342,10 @@ import:
     wallabag_v2:
         page_title: 'Importer > Wallabag v2'
         description: "Aquesta aisina importarà totas vòstras donadas d'una instància mai de wallabag v2. Anatz dins totes vòstres articles, puèi, sus la barra laterala, clicatz sus \"JSON\". Traparatz un fichièr \"All articles.json\""
+    readability:
+        page_title: 'Importer > Readability'
+    #     description: 'This importer will import all your Readability articles. 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).'
+    #     how_to: 'Please select your Readability export and click on the below button to upload and import it.'
 
 developer:
     page_title: 'Desvolopador'
index 0a325c574ff68e3857247c7bf13a9f6437071f9e..da50cd4cd84e7b5ee9569cc91d18f7f0b8e80455 100644 (file)
@@ -139,12 +139,14 @@ entry:
         starred: 'Wpisy oznaczone gwiazdką'
         archived: 'Zarchiwizowane wpisy'
         filtered: 'Odfiltrowane wpisy'
+        filtered_tags: 'Filtrowane po tagach'
+        untagged: 'Odtaguj wpisy'
     list:
         number_on_the_page: '{0} Nie ma wpisów.|{1} Jest jeden wpis.|]1,Inf[ Są %count% wpisy.'
         reading_time: 'szacunkowy czas czytania'
         reading_time_minutes: 'szacunkowy czas czytania: %readingTime% min'
         reading_time_less_one_minute: 'szacunkowy czas czytania: <small class="inferieur">&lt;</small> 1 min'
-        # number_of_tags: '{1}and one other tag|]1,Inf[and %count% other tags'
+        number_of_tags: '{1} i inny tag|]1,Inf[i %count% innych tagów'
         reading_time_minutes_short: '%readingTime% min'
         reading_time_less_one_minute_short: '<small class="inferieur">&lt;</small> 1 min'
         original_article: 'oryginał'
@@ -186,6 +188,8 @@ entry:
             add_a_tag: 'Dodaj tag'
             share_content: 'Udostępnij'
             share_email_label: 'Adres email'
+            public_link: 'Publiczny link'
+            delete_public_link: 'Usuń publiczny link'
             download: 'Pobierz'
             print: 'Drukuj'
             problem:
@@ -288,6 +292,7 @@ quickstart:
         pocket: 'Migruj z Pocket'
         wallabag_v1: 'Migruj z wallabag v1'
         wallabag_v2: 'Migruj z wallabag v2'
+        readability: 'Migruj z Readability'
     developer:
         title: 'Deweloperzy'
         create_application: 'Stwórz swoją aplikację'
@@ -309,6 +314,7 @@ tag:
     page_title: 'Tagi'
     list:
         number_on_the_page: '{0} Nie ma tagów.|{1} Jest jeden tag.|]1,Inf[ Są %count% tagi.'
+        see_untagged_entries: 'Zobacz nieotagowane wpisy'
 
 import:
     page_title: 'Import'
@@ -336,6 +342,10 @@ import:
     wallabag_v2:
         page_title: 'Import > Wallabag v2'
         description: 'Ten importer, zaimportuje wszystkie twoje artykułu z wallabag v2. Idź do wszystkich artykułów, a następnie na panelu exportu kliknij na "JSON". Otrzymasz plik "All articles.json".'
+    readability:
+        page_title: 'Import > Readability'
+        description: 'Ten importer, zaimportuje wszystkie twoje artykuły z Readability. Na stronie narzędzi (https://www.readability.com/tools/), kliknij na "Export your data" w sekcji "Data Export". Otrzymach email z plikiem JSON (plik nie będzie zawierał rozszerzenia .json).'
+        how_to: 'Wybierz swój plik eksportu z Readability i kliknij poniższy przycisk, aby go załadować.'
 
 developer:
     page_title: 'Deweloper'
@@ -343,7 +353,7 @@ developer:
     documentation: 'Dokumentacja'
     how_to_first_app: 'Jak stworzyć moją pierwszą aplikację'
     full_documentation: 'Pokaż pełne API'
-    # list_methods: 'List API methods'
+    list_methods: 'Lista metod API'
     clients:
          title: 'Klienci'
          create_new: 'Utwórz nowego klienta'
@@ -401,7 +411,7 @@ flashes:
         notice:
             entry_already_saved: 'Wpis już został dodany %date%'
             entry_saved: 'Wpis zapisany'
-            # entry_saved_failed: 'Failed to save entry'
+            entry_saved_failed: 'Zapis artykułu się nie powiódł'
             entry_updated: 'Wpis zaktualizowany'
             entry_reloaded: 'Wpis ponownie załadowany'
             entry_reload_failed: 'Błąd ponownego załadowania'
index 42ad28eec58f04dd45edd9ac0cf5c9c3ff7fb7fa..f41609d02db0427f0d6a3579d2b1a36cb42a2639 100644 (file)
@@ -139,6 +139,8 @@ entry:
         # starred: 'Starred entries'
         # archived: 'Archived entries'
         # filtered: 'Filtered entries'
+        # filtered_tags: 'Filtered by tags'
+        # untagged: 'Untagged entries'
     list:
         # number_on_the_page: '{0} There is no entry.|{1} There is one entry.|]1,Inf[ There are %count% entries.'
         reading_time: 'timp estimat de citire'
@@ -186,6 +188,8 @@ entry:
             add_a_tag: 'Adaugă un tag'
             share_content: 'Dă mai departe'
             share_email_label: 'E-mail'
+            # public_link: 'public link'
+            # delete_public_link: 'delete public link'
             download: 'Descarcă'
             # print: 'Print'
             problem:
@@ -288,6 +292,7 @@ quickstart:
     #     pocket: 'Migrate from Pocket'
     #     wallabag_v1: 'Migrate from wallabag v1'
     #     wallabag_v2: 'Migrate from wallabag v2'
+    #     readability: 'Migrate from Readability'
     # developer:
     #     title: 'Developers'
     #     create_application: 'Create your third application'
@@ -309,6 +314,7 @@ tag:
     page_title: 'Tag-uri'
     list:
         # number_on_the_page: '{0} There is no tag.|{1} There is one tag.|]1,Inf[ There are %count% tags.'
+        # see_untagged_entries: 'See untagged entries'
 
 import:
     # page_title: 'Import'
@@ -336,6 +342,10 @@ import:
     # wallabag_v2:
     #     page_title: 'Import > Wallabag v2'
     #     description: 'This importer will import all your wallabag v2 articles. Go to All articles, then, on the export sidebar, click on "JSON". You will have a "All articles.json" file.'
+    # readability:
+    #     page_title: 'Import > Readability'
+    #     description: 'This importer will import all your Readability articles. 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).'
+    #     how_to: 'Please select your Readability export and click on the below button to upload and import it.'
 
 developer:
     # page_title: 'Developer'
index a60dfc86105c9b226c4d58c485979711fd56a85b..6dfbfa8960afbe5369bc2ebfd89a150ecb6b7cbe 100644 (file)
@@ -139,6 +139,8 @@ entry:
         # starred: 'Starred entries'
         # archived: 'Archived entries'
         # filtered: 'Filtered entries'
+        # filtered_tags: 'Filtered by tags'
+        # untagged: 'Untagged entries'
     list:
         number_on_the_page: '{0} Herhangi bir makale yok.|{1} Burada bir adet makale var.|]1,Inf[ Burada %count% adet makale var.'
         reading_time: 'tahmini okuma süresi'
@@ -186,6 +188,8 @@ entry:
             add_a_tag: 'Bir etiket ekle'
             share_content: 'Paylaş'
             share_email_label: 'E-posta'
+            # public_link: 'public link'
+            # delete_public_link: 'delete public link'
             download: 'İndir'
             # print: 'Print'
             problem:
@@ -288,6 +292,7 @@ quickstart:
         pocket: "Pocket üzerindeki verilerinizi wallabag'e aktarın"
         wallabag_v1: "wallabag v1 üzerindeki verilerinizi wallabag'in yeni sürümüne aktarın"
         wallabag_v2: "wallabag v2 üzerindeki verilerinizi wallabag'in yeni sürümüne aktarın"
+        readability: "Readability üzerindeki verilerinizi wallabag'e aktarın'"
     developer:
         # title: 'Developers'
         # create_application: 'Create your third application'
@@ -309,6 +314,7 @@ tag:
     page_title: 'Etiketler'
     list:
         number_on_the_page: '{0} Herhangi bir etiket yok.|{1} Burada bir adet etiket var.|]1,Inf[ Burada %count% adet etiket var.'
+        # see_untagged_entries: 'See untagged entries'
 
 import:
     page_title: 'İçe Aktar'
@@ -336,6 +342,10 @@ import:
     wallabag_v2:
         page_title: 'İçe Aktar > Wallabag v2'
         # description: 'This importer will import all your wallabag v2 articles. Go to All articles, then, on the export sidebar, click on "JSON". You will have a "All articles.json" file.'
+    readability:
+        page_title: 'İçe Aktar > Readability'
+    #     description: 'This importer will import all your Readability articles. 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).'
+    #     how_to: 'Please select your Readability export and click on the below button to upload and import it.'
 
 developer:
     # page_title: 'Developer'
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/_title.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/_title.html.twig
new file mode 100644 (file)
index 0000000..d1c2f20
--- /dev/null
@@ -0,0 +1,15 @@
+{% set currentRoute = app.request.attributes.get('_route') %}
+
+{% if currentRoute == 'starred' %}
+    {{ 'entry.page_titles.starred'|trans }}
+{% elseif currentRoute == 'archive' %}
+    {{ 'entry.page_titles.archived'|trans }}
+{% elseif currentRoute == 'all' %}
+    {{ 'entry.page_titles.filtered'|trans }}
+{% elseif currentRoute == 'tag_entries' %}
+    {{ 'entry.page_titles.filtered_tags'|trans }}
+{% elseif currentRoute == 'untagged' %}
+    {{ 'entry.page_titles.untagged'|trans }}
+{% else %}
+    {{ 'entry.page_titles.unread'|trans }}
+{% endif %}
index 92eecb9be2053fab0ac7a4d0ddf5289a9386e2ac..1554cce4789b33c9cebaee7f3394e5573feadd5f 100644 (file)
@@ -1,6 +1,8 @@
 {% extends "WallabagCoreBundle::layout.html.twig" %}
 
-{% block title %}{{ 'entry.page_titles.unread'|trans }}{% endblock %}
+{% block title %}
+    {% include "@WallabagCore/themes/_title.html.twig" %}
+{% endblock %}
 
 {% block content %}
     {% include "WallabagCoreBundle:Entry:pager.html.twig" with {'entries': entries} %}
@@ -44,7 +46,6 @@
         </div>
     {% endfor %}
 
-
     <!-- Export -->
     <aside id="download-form">
     {% set currentRoute = app.request.attributes.get('_route') %}
             {% if craue_setting('export_xml') %}<li class="bold"><a class="waves-effect" href="{{ path('export_entries', { 'category': currentRoute, 'format': 'xml' }) }}">XML</a></li>{% endif %}
         </ul>
     </aside>
+
     <!-- Filter -->
-    <aside id="filter-form">
+    {% if form is not null %}
+    <aside id="filter-form" class="">
         <form method="get" action="{{ path('all') }}">
             <h2>{{ 'entry.filters.title'|trans }}</h2>
             <a href="javascript: void(null);" id="filter-form-close" class="close-button--popup close-button">&times;</a>
             </div>
         </form>
     </aside>
-
-    {% include "WallabagCoreBundle:Entry:pager.html.twig" with {'entries': entries} %}
+    {% endif %}
 {% endblock %}
index ce47a6771977718ce7a590e27f67df3483468a18..675168bbae601c51cb2ad3591803d5b2d2d51151 100644 (file)
@@ -17,6 +17,7 @@
                 <li><a title="{{ 'entry.view.left_menu.set_as_starred'|trans }}" class="tool icon icon-star {% if entry.isStarred == 0 %}fav-off{% else %}fav{% endif %}" href="{{ path('star_entry', { 'id': entry.id }) }}"><span>{{ 'entry.view.left_menu.set_as_starred'|trans }}</span></a></li>
                 <li><a id="nav-btn-add-tag" title="{{ 'entry.view.left_menu.add_a_tag'|trans }}"><span>{{ 'entry.view.left_menu.add_a_tag'|trans }}</span></a></li>
                 <li><a title="{{ 'entry.view.left_menu.delete'|trans }}" class="tool delete icon icon-trash" href="{{ path('delete_entry', { 'id': entry.id }) }}"><span>{{ 'entry.view.left_menu.delete'|trans }}</span></a></li>
+                {% if craue_setting('share_public') %}<li><a href="{{ path('share', {'id': entry.id }) }}" target="_blank" class="tool public" title="{{ 'entry.view.left_menu.public_link'|trans }}"><span>{{ 'entry.view.left_menu.public_link'|trans }}</span></a></li> <li><a href="{{ path('delete_share', {'id': entry.id }) }}" class="tool public" title="{{ 'entry.view.left_menu.delete_public_link'|trans }}"><span>{{ 'entry.view.left_menu.delete_public_link'|trans }}</span></a></li>{% endif %}
                 {% if craue_setting('share_twitter') %}<li><a href="https://twitter.com/home?status={{entry.title|url_encode}}%20{{ entry.url|url_encode }}%20via%20@wallabagapp" target="_blank" class="tool twitter icon icon-twitter" title="Tweet"><span>Tweet</span></a></li>{% endif %}
                 {% if craue_setting('share_mail') %}<li><a href="mailto:?subject={{ entry.title|url_encode }}&amp;body={{ entry.url|url_encode }}%20via%20@wallabagapp" class="tool email icon icon-mail" title="Email"><span>Email</span></a></li>{% endif %}
                 {% if craue_setting('share_shaarli') %}<li><a href="{{ craue_setting('shaarli_url') }}/index.php?post={{ entry.url|url_encode }}&amp;title={{ entry.title|url_encode }}" target="_blank" class="tool shaarli" title="shaarli"><span>shaarli</span></a></li>{% endif %}
                 <li><a href="mailto:hello@wallabag.org?subject=Wrong%20display%20in%20wallabag&amp;body={{ entry.url|url_encode }}" title="{{ 'entry.view.left_menu.problem.label'|trans }}" class="tool bad-display icon icon-delete"><span>{{ 'entry.view.left_menu.problem.label'|trans }}</span></a></li>
             </ul>
         </div>
-        <div class="link mdi-action-today">
-            {{ 'entry.view.created_at'|trans }}: {{ entry.createdAt|date('Y-m-d') }}
-        </div>
 
-        <div class="link mdi-action-query-builder">
-            {% set readingTime = entry.readingTime / app.user.config.readingSpeed %}
-            {% if readingTime > 0 %}
-                {{ 'entry.list.reading_time_minutes'|trans({'%readingTime%': readingTime|round})|capitalize }}
-            {% else %}
-                {{ 'entry.list.reading_time_less_one_minute'|trans|raw }}
-            {% endif %}
-        </div>
+        <div id="article-informations">
+            <div class="link mdi-action-today">
+                {{ 'entry.view.created_at'|trans }}: {{ entry.createdAt|date('Y-m-d') }}
+            </div>
 
-        {% set nbAnnotations = entry.annotations | length %}
-        <span class="tool link"><i class="material-icons link">comment</i> {{ 'entry.view.annotations_on_the_entry'|transchoice(entry.annotations | length) }}</span>
-        <aside class="tags">
-            {% for tag in entry.tags %}
-                <span class="label-outline"><i class="material-icons">label_outline</i> {{ tag.label }}</span> <a href="{{ path('remove_tag', { 'entry': entry.id, 'tag': tag.id }) }}"class="nostyle"><i>✘</i></a>
-            {% endfor %}
-            <div class="input-field nav-panel-add-tag" style="display: none">
-                {{ render(controller( "WallabagCoreBundle:Tag:addTagForm", { 'id': entry.id } )) }}
+            <div class="link mdi-action-query-builder">
+                {% set readingTime = entry.readingTime / app.user.config.readingSpeed %}
+                {% if readingTime > 0 %}
+                    {{ 'entry.list.reading_time_minutes'|trans({'%readingTime%': readingTime|round})|capitalize }}
+                {% else %}
+                    {{ 'entry.list.reading_time_less_one_minute'|trans|raw }}
+                {% endif %}
             </div>
-        </aside>
+
+            {% set nbAnnotations = entry.annotations | length %}
+            <span class="tool link"><i class="material-icons link">comment</i> {{ 'entry.view.annotations_on_the_entry'|transchoice(entry.annotations | length) }}</span>
+            <aside class="tags">
+                {% for tag in entry.tags %}
+                    <span class="label-outline"><i class="material-icons">label_outline</i> {{ tag.label }}</span> <a href="{{ path('remove_tag', { 'entry': entry.id, 'tag': tag.id }) }}"class="nostyle"><i>✘</i></a>
+                {% endfor %}
+                <div class="input-field nav-panel-add-tag" style="display: none">
+                    {{ render(controller( "WallabagCoreBundle:Tag:addTagForm", { 'id': entry.id } )) }}
+                </div>
+            </aside>
+        </div>
         {% if entry.previewPicture is not null %}
             <div><img class="preview" src="{{ entry.previewPicture }}" alt="{{ entry.title|raw }}" /></div>
         {% endif %}
index b3d3d5a08cb7305ad656744781292cda3237a5fc..ea1c1cbec6189e593f7b0ada7271a171b09db302 100644 (file)
@@ -39,6 +39,7 @@
         <li><a href="{{ path('import_pocket') }}">{{ 'quickstart.migrate.pocket'|trans }}</a></li>
         <li><a href="{{ path('import_wallabag_v1') }}">{{ 'quickstart.migrate.wallabag_v1'|trans }}</a></li>
         <li><a href="{{ path('import_wallabag_v2') }}">{{ 'quickstart.migrate.wallabag_v2'|trans }}</a></li>
+        <li><a href="{{ path('import_readability') }}">{{ 'quickstart.migrate.readability'|trans }}</a></li>
     </ul>
 
     <h4>{{ 'quickstart.developer.title'|trans }}</h4>
index 524a1d237bd818dfe3749854acd6fa7debdbcb80..50043907a1ee54d5f86d615f3d848d931cc15e43 100644 (file)
@@ -9,7 +9,11 @@
 
     <ul>
     {% for tag in tags %}
-        <li id="tag-{{ tag.id|e }}">{{tag.label}} ({{ tag.getEntriesByUserId(app.user.id) | length }})</li>
+        <li id="tag-{{ tag.id|e }}"><a href="{{ path('tag_entries', {'slug': tag.slug}) }}">{{tag.label}} ({{ tag.entries.getValues | length }})</a></li>
     {% endfor %}
     </ul>
+
+    <div>
+        <a href="{{ path('untagged') }}">{{ 'tag.list.see_untagged_entries'|trans }}</a>
+    </div>
 {% endblock %}
index 11b02294b131dc8e4cc40f9446e3c4a0525598fe..bf390e8994d81d81b96ce3d45cc0357959b1eac6 100644 (file)
                     </div>
 
                     <div id="set5" class="col s12">
+                        {% if app.user.config.taggingRules is not empty %}
                         <div class="row">
                             <div class="input-field col s12">
                                 <ul>
                                 </ul>
                             </div>
                         </div>
+                        {% endif %}
 
                         {{ form_start(form.new_tagging_rule) }}
                             {{ form_errors(form.new_tagging_rule) }}
index eca8924ec6149845bbc473a5f0e0f56d14c4a365..806a4eefe795473bb3d882c06c20d524efd9a443 100644 (file)
@@ -1,18 +1,7 @@
 {% extends "WallabagCoreBundle::layout.html.twig" %}
 
 {% block title %}
-  {% set currentRoute = app.request.attributes.get('_route') %}
-
-  {% if currentRoute == 'starred' %}
-    {{ 'entry.page_titles.starred'|trans }}
-  {% elseif currentRoute == 'archive' %}
-    {{ 'entry.page_titles.archived'|trans }}
-  {% elseif currentRoute == 'all' %}
-    {{ 'entry.page_titles.filtered'|trans }}
-  {% else %}
-    {{ 'entry.page_titles.unread'|trans }}
-  {% endif %}
-
+  {% include "@WallabagCore/themes/_title.html.twig" %}
 {% endblock %}
 
 {% block content %}
@@ -40,7 +29,7 @@
                                 <i class="card-title grey-text text-darken-4 activator material-icons right">more_vert</i>
                             {% endif %}
 
-                            <span class="card-title"><a href="{{ path('view', { 'id': entry.id }) }}" title="{{ entry.title|striptags }}">{{ entry.title|striptags|raw }}</a></span>
+                            <span class="card-title dot-ellipsis dot-resize-update"><a href="{{ path('view', { 'id': entry.id }) }}" title="{{ entry.title|raw }}">{{ entry.title|striptags|raw }}</a></span>
 
                             <div class="estimatedTime grey-text">
                                 <span class="tool reading-time">
     </div>
 
     <!-- Filters -->
+    {% if form is not null %}
     <div id="filters" class="side-nav fixed right-aligned">
         <form action="{{ path('all') }}">
 
 
         </form>
     </div>
-    {% include "WallabagCoreBundle:Entry:pager.html.twig" with {'entries': entries} %}
+    {% endif %}
+
 {% endblock %}
index 5dd2afb3e32d58d52b953067086da402ea844557..9b587c34a578cedb96e0691bb196fd90f51e5519 100644 (file)
             </a>
             <div class="collapsible-body">
                 <ul>
+                    {% if craue_setting('share_public') %}
+                        <li>
+                            <a href="{{ path('share', {'id': entry.id }) }}" target="_blank" class="public icon-link" title="{{ 'entry.view.left_menu.public_link'|trans }}">
+                                <span>{{ 'entry.view.left_menu.public_link'|trans }}</span>
+                            </a>
+                        </li>
+                        <li>
+                            <a href="{{ path('delete_share', {'id': entry.id }) }}" class="tool public" title="{{ 'entry.view.left_menu.delete_public_link'|trans }}">
+                                <span>{{ 'entry.view.left_menu.delete_public_link'|trans }}</span>
+                            </a>
+                        </li>
+                    {% endif %}
                     {% if craue_setting('share_twitter') %}
                         <li>
                             <a href="https://twitter.com/home?status={{entry.title|url_encode}}%20{{ entry.url|url_encode }}%20via%20@wallabagapp" target="_blank" class="tool twitter icon icon-twitter" title="twitter">
         </li>
         {% endif %}
 
-
         <li class="bold">
             <a class="waves-effect collapsible-header">
                 <i class="material-icons small">file_download</i>
         </header>
         <aside>
             {% set readingTime = entry.readingTime / app.user.config.readingSpeed %}
-            <span class="link mdi-action-query-builder">
+            <span class="mdi-action-query-builder"></span>
+            <span class="link">
                 {% if readingTime > 0 %}
                     {{ 'entry.list.reading_time_minutes_short'|trans({'%readingTime%': readingTime|round}) }}
                 {% else %}
                     {{ 'entry.list.reading_time_less_one_minute_short'|trans|raw }}
                 {% endif %}
             </span>
-            <span class="link mdi-action-today" title="{{ 'entry.view.created_at'|trans }}"> {{ entry.createdAt|date('Y-m-d') }}</span>
+            <span class="mdi-action-today" title="{{ 'entry.view.created_at'|trans }}"> </span> <span class="link">{{ entry.createdAt|date('Y-m-d') }}</span>
             <a href="{{ entry.url|e }}" target="_blank" title="{{ 'entry.view.original_article'|trans }} : {{ entry.title|e }}" class="tool">
-            <span class="link"><i class="material-icons link">link</i> {{ entry.domainName|removeWww }}</span></a>
-            <span class="tool link"><i class="material-icons link">comment</i> {{ 'entry.view.annotations_on_the_entry'|transchoice(entry.annotations | length) }}</span>
+            <i class="material-icons link">link</i> <span class="link">{{ entry.domainName|removeWww }}</span></a>
+            <span class="tool"><i class="material-icons link">comment</i> <span class="link">{{ 'entry.view.annotations_on_the_entry'|transchoice(entry.annotations | length) }}</span>
             <div id="list">
                 {% for tag in entry.tags %}
                     <div class="chip">
index 59dd037b85bcbe85009d4957633506683dc9f45c..8cbf4ab40b82c574db6f504b3a8b23982b73e594 100644 (file)
@@ -44,6 +44,7 @@
                         <li><a href="{{ path('import_pocket') }}">{{ 'quickstart.migrate.pocket'|trans }}</a></li>
                         <li><a href="{{ path('import_wallabag_v1') }}">{{ 'quickstart.migrate.wallabag_v1'|trans }}</a></li>
                         <li><a href="{{ path('import_wallabag_v2') }}">{{ 'quickstart.migrate.wallabag_v2'|trans }}</a></li>
+                        <li><a href="{{ path('import_readability') }}">{{ 'quickstart.migrate.readability'|trans }}</a></li>
                     </ul>
 
                     <h4>{{ 'quickstart.developer.title'|trans }}</h4>
index d958c4b85e51aaf3067791b13ce925a11ee2ca72..1690633a531150656a2c388a5c2574899f35e6c2 100644 (file)
@@ -9,7 +9,10 @@
     <br />
     <ul class="row data">
     {% for tag in tags %}
-        <li id="tag-{{ tag.id|e }}" class="col l4 m6 s12">{{tag.label}} ({{ tag.getEntriesByUserId(app.user.id) | length }})</li>
+        <li id="tag-{{ tag.id|e }}" class="col l4 m6 s12"><a href="{{ path('tag_entries', {'slug': tag.slug}) }}">{{tag.label}} ({{ tag.entries.getValues | length }})</a></li>
     {% endfor %}
     </ul>
+    <div>
+        <a href="{{ path('untagged') }}">{{ 'tag.list.see_untagged_entries'|trans }}</a>
+    </div>
 {% endblock %}
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/share.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/share.html.twig
new file mode 100644 (file)
index 0000000..b82b3d3
--- /dev/null
@@ -0,0 +1,40 @@
+<html>
+    <head>
+        <title>{{ entry.title | raw }}</title>
+        <style>
+            body {
+                margin: 10px;
+                font-family: 'Roboto',Verdana,Geneva,sans-serif;
+                font-size: 16px;
+                color: #000;
+            }
+            header {
+                text-align: center;
+            }
+
+            header h1 {
+                font-size: 1.3em;
+            }
+
+            a,
+            a:hover,
+            a:visited {
+                color: #000;
+            }
+
+            article {
+                margin: 0 auto;
+                width: 600px;
+            }
+        </style>
+    </head>
+    <body>
+        <header>
+            <h1>{{ entry.title | raw }}</h1>
+            <span><a href="{{ entry.url|e }}" target="_blank" title="{{ 'entry.view.original_article'|trans }} : {{ entry.title|e }}" class="tool">{{ entry.domainName|removeWww }}</a></span>
+        </header>
+        <article>
+            {{ entry.content | raw }}
+        </article>
+    </body>
+</html>
diff --git a/src/Wallabag/ImportBundle/Controller/ReadabilityController.php b/src/Wallabag/ImportBundle/Controller/ReadabilityController.php
new file mode 100644 (file)
index 0000000..b61aa99
--- /dev/null
@@ -0,0 +1,65 @@
+<?php
+
+namespace Wallabag\ImportBundle\Controller;
+
+use Symfony\Bundle\FrameworkBundle\Controller\Controller;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
+use Symfony\Component\HttpFoundation\Request;
+use Wallabag\ImportBundle\Form\Type\UploadImportType;
+
+class ReadabilityController extends Controller
+{
+    /**
+     * @Route("/readability", name="import_readability")
+     */
+    public function indexAction(Request $request)
+    {
+        $form = $this->createForm(UploadImportType::class);
+        $form->handleRequest($request);
+
+        $readability = $this->get('wallabag_import.readability.import');
+
+        if ($form->isValid()) {
+            $file = $form->get('file')->getData();
+            $markAsRead = $form->get('mark_as_read')->getData();
+            $name = 'readability_'.$this->getUser()->getId().'.json';
+
+            if (in_array($file->getClientMimeType(), $this->getParameter('wallabag_import.allow_mimetypes')) && $file->move($this->getParameter('wallabag_import.resource_dir'), $name)) {
+                $res = $readability
+                    ->setUser($this->getUser())
+                    ->setFilepath($this->getParameter('wallabag_import.resource_dir').'/'.$name)
+                    ->setMarkAsRead($markAsRead)
+                    ->import();
+
+                $message = 'flashes.import.notice.failed';
+
+                if (true === $res) {
+                    $summary = $readability->getSummary();
+                    $message = $this->get('translator')->trans('flashes.import.notice.summary', [
+                        '%imported%' => $summary['imported'],
+                        '%skipped%' => $summary['skipped'],
+                    ]);
+
+                    unlink($this->getParameter('wallabag_import.resource_dir').'/'.$name);
+                }
+
+                $this->get('session')->getFlashBag()->add(
+                    'notice',
+                    $message
+                );
+
+                return $this->redirect($this->generateUrl('homepage'));
+            } else {
+                $this->get('session')->getFlashBag()->add(
+                    'notice',
+                    'flashes.import.notice.failed_on_file'
+                );
+            }
+        }
+
+        return $this->render('WallabagImportBundle:Readability:index.html.twig', [
+            'form' => $form->createView(),
+            'import' => $readability,
+        ]);
+    }
+}
diff --git a/src/Wallabag/ImportBundle/Import/ReadabilityImport.php b/src/Wallabag/ImportBundle/Import/ReadabilityImport.php
new file mode 100644 (file)
index 0000000..37b160c
--- /dev/null
@@ -0,0 +1,179 @@
+<?php
+
+namespace Wallabag\ImportBundle\Import;
+
+use Wallabag\CoreBundle\Entity\Entry;
+use Wallabag\UserBundle\Entity\User;
+
+class ReadabilityImport extends AbstractImport
+{
+    private $user;
+    private $skippedEntries = 0;
+    private $importedEntries = 0;
+    private $filepath;
+    private $markAsRead;
+
+    /**
+     * We define the user in a custom call because on the import command there is no logged in user.
+     * So we can't retrieve user from the `security.token_storage` service.
+     *
+     * @param User $user
+     */
+    public function setUser(User $user)
+    {
+        $this->user = $user;
+
+        return $this;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getName()
+    {
+        return 'Readability';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getUrl()
+    {
+        return 'import_readability';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getDescription()
+    {
+        return 'import.readability.description';
+    }
+
+    /**
+     * Set file path to the json file.
+     *
+     * @param string $filepath
+     */
+    public function setFilepath($filepath)
+    {
+        $this->filepath = $filepath;
+
+        return $this;
+    }
+
+    /**
+     * Set whether articles must be all marked as read.
+     *
+     * @param bool $markAsRead
+     */
+    public function setMarkAsRead($markAsRead)
+    {
+        $this->markAsRead = $markAsRead;
+
+        return $this;
+    }
+
+    /**
+     * Get whether articles must be all marked as read.
+     */
+    public function getMarkAsRead()
+    {
+        return $this->markAsRead;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSummary()
+    {
+        return [
+            'skipped' => $this->skippedEntries,
+            'imported' => $this->importedEntries,
+        ];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function import()
+    {
+        if (!$this->user) {
+            $this->logger->error('ReadabilityImport: user is not defined');
+
+            return false;
+        }
+
+        if (!file_exists($this->filepath) || !is_readable($this->filepath)) {
+            $this->logger->error('ReadabilityImport: unable to read file', ['filepath' => $this->filepath]);
+
+            return false;
+        }
+
+        $data = json_decode(file_get_contents($this->filepath), true);
+
+        if (empty($data) || empty($data['bookmarks'])) {
+            return false;
+        }
+
+        $this->parseEntries($data['bookmarks']);
+
+        return true;
+    }
+
+    /**
+     * Parse and insert all given entries.
+     *
+     * @param $entries
+     */
+    protected function parseEntries($entries)
+    {
+        $i = 1;
+
+        foreach ($entries as $importedEntry) {
+            $existingEntry = $this->em
+                ->getRepository('WallabagCoreBundle:Entry')
+                ->findByUrlAndUserId($importedEntry['article__url'], $this->user->getId());
+
+            if (false !== $existingEntry) {
+                ++$this->skippedEntries;
+                continue;
+            }
+
+            $data = [
+                'title' => $importedEntry['article__title'],
+                'url' => $importedEntry['article__url'],
+                'content_type' => '',
+                'language' => '',
+                'is_archived' => $importedEntry['archive'] || $this->markAsRead,
+                'is_starred' => $importedEntry['favorite'],
+            ];
+
+            $entry = $this->fetchContent(
+                new Entry($this->user),
+                $data['url'],
+                $data
+            );
+
+            // jump to next entry in case of problem while getting content
+            if (false === $entry) {
+                ++$this->skippedEntries;
+                continue;
+            }
+            $entry->setArchived($data['is_archived']);
+            $entry->setStarred($data['is_starred']);
+
+            $this->em->persist($entry);
+            ++$this->importedEntries;
+
+            // flush every 20 entries
+            if (($i % 20) === 0) {
+                $this->em->flush();
+                $this->em->clear($entry);
+            }
+            ++$i;
+        }
+
+        $this->em->flush();
+    }
+}
index 86b44cb3e8fbf0bebac1fc4741983efc20c5094f..520d43aff9d0a30519d67923b8b2cc5ea2b22e65 100644 (file)
@@ -43,3 +43,13 @@ services:
             - [ setLogger, [ "@logger" ]]
         tags:
             -  { name: wallabag_import.import, alias: wallabag_v2 }
+
+    wallabag_import.readability.import:
+        class: Wallabag\ImportBundle\Import\ReadabilityImport
+        arguments:
+            - "@doctrine.orm.entity_manager"
+            - "@wallabag_core.content_proxy"
+        calls:
+            - [ setLogger, [ "@logger" ]]
+        tags:
+            -  { name: wallabag_import.import, alias: readability }
diff --git a/src/Wallabag/ImportBundle/Resources/views/Readability/index.html.twig b/src/Wallabag/ImportBundle/Resources/views/Readability/index.html.twig
new file mode 100644 (file)
index 0000000..f527d30
--- /dev/null
@@ -0,0 +1,43 @@
+{% extends "WallabagCoreBundle::layout.html.twig" %}
+
+{% block title %}{{ 'import.readability.page_title'|trans }}{% endblock %}
+
+{% block content %}
+<div class="row">
+    <div class="col s12">
+        <div class="card-panel settings">
+            <div class="row">
+                <blockquote>{{ import.description|trans }}</blockquote>
+                <p>{{ 'import.readability.how_to'|trans }}</p>
+
+                <div class="col s12">
+                    {{ form_start(form, {'method': 'POST'}) }}
+                        {{ form_errors(form) }}
+                        <div class="row">
+                            <div class="file-field input-field col s12">
+                                {{ form_errors(form.file) }}
+                                <div class="btn">
+                                    <span>{{ form.file.vars.label|trans }}</span>
+                                    {{ form_widget(form.file) }}
+                                </div>
+                                <div class="file-path-wrapper">
+                                    <input class="file-path validate" type="text">
+                                </div>
+                            </div>
+                            <div class="input-field col s6 with-checkbox">
+                                <h6>{{ 'import.form.mark_as_read_title'|trans }}</h6>
+                                {{ form_widget(form.mark_as_read) }}
+                                {{ form_label(form.mark_as_read) }}
+                            </div>
+                        </div>
+
+                        {{ form_widget(form.save, { 'attr': {'class': 'btn waves-effect waves-light'} }) }}
+
+                        {{ form_rest(form) }}
+                    </form>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>
+{% endblock %}
diff --git a/src/Wallabag/UserBundle/Controller/RegistrationController.php b/src/Wallabag/UserBundle/Controller/RegistrationController.php
new file mode 100644 (file)
index 0000000..f81f3a7
--- /dev/null
@@ -0,0 +1,18 @@
+<?php
+
+namespace Wallabag\UserBundle\Controller;
+
+use FOS\UserBundle\Controller\RegistrationController as FOSRegistrationController;
+use Symfony\Component\HttpFoundation\Request;
+
+class RegistrationController extends FOSRegistrationController
+{
+    public function registerAction(Request $request)
+    {
+        if ($this->container->getParameter('wallabag_user.registration_enabled')) {
+            return parent::registerAction($request);
+        }
+
+        return $this->redirectToRoute('fos_user_security_login', [], 301);
+    }
+}
diff --git a/src/Wallabag/UserBundle/Controller/SecurityController.php b/src/Wallabag/UserBundle/Controller/SecurityController.php
new file mode 100644 (file)
index 0000000..83fa0b2
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+
+namespace Wallabag\UserBundle\Controller;
+
+use FOS\UserBundle\Controller\SecurityController as FOSSecurityController;
+
+/**
+ * Extends login form in order to pass the registration_enabled parameter.
+ */
+class SecurityController extends FOSSecurityController
+{
+    protected function renderLogin(array $data)
+    {
+        return $this->render('FOSUserBundle:Security:login.html.twig',
+            array_merge(
+                $data,
+                ['registration_enabled' => $this->container->getParameter('wallabag_user.registration_enabled')]
+            )
+        );
+    }
+}
index 4223f8dba5e6547597b5356fa1a2e92e050a600c..971ce1a0f9a2358b663fecc04f3370e35913cf95 100644 (file)
@@ -12,6 +12,14 @@ class Configuration implements ConfigurationInterface
         $treeBuilder = new TreeBuilder();
         $rootNode = $treeBuilder->root('wallabag_user');
 
+        $rootNode
+            ->children()
+                ->booleanNode('registration_enabled')
+                    ->defaultValue(true)
+                ->end()
+            ->end()
+        ;
+
         return $treeBuilder;
     }
 }
index c12a893782e34a6ce416c77f631223edb8c0a510..99040f6946bbca1743c7a0e850185659935f9171 100644 (file)
@@ -16,6 +16,7 @@ class WallabagUserExtension extends Extension
 
         $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
         $loader->load('services.yml');
+        $container->setParameter('wallabag_user.registration_enabled', $config['registration_enabled']);
     }
 
     public function getAlias()
diff --git a/src/Wallabag/UserBundle/Resources/translations/wallabag_user.oc.yml b/src/Wallabag/UserBundle/Resources/translations/wallabag_user.oc.yml
new file mode 100644 (file)
index 0000000..53a1afd
--- /dev/null
@@ -0,0 +1,11 @@
+# Two factor mail
+auth_code:
+    on: 'sus'
+    mailer:
+        subject: "Còdi d'autentificacion wallabag"
+        body:
+            hello: "Bonjorn %user%,"
+            first_para: "Estant qu'avètz activat la dobla autentificacion sus vòtre compte wallabag e que venètz de vos conectar dempuèi un novèl aparelh (ordinador, mobil, etc.) vos mandem un còdi per validar la connexion."
+            second_para: "Vaquí lo còdi a dintrar :"
+            support: "S'avètz un problèma de connexion, dobtetz pas a contacter l'assisténcia : "
+            signature: "La còla de wallabag"
index 8474b497b069a9964e807c4d6c9a378539a861f5..938f1a31a6367da9d69e461e4bdbd710fa23cd50 100644 (file)
     </div>
     <div class="card-action center">
         <input type="hidden" name="_csrf_token" value="{{ csrf_token('authenticate') }}" />
-        <a href="{{ path('fos_user_registration_register') }}" class="waves-effect waves-light grey btn">{{ 'security.login.register'|trans }}</a>
+        {% if registration_enabled %}
+            <a href="{{ path('fos_user_registration_register') }}" class="waves-effect waves-light grey btn">{{ 'security.login.register'|trans }}</a>
+        {% endif %}
         <button class="btn waves-effect waves-light" type="submit" name="send">
             {{ 'security.login.submit'|trans }}
             <i class="material-icons right">send</i>
         </button>
     </div>
-    <div class="center">
+    <div class="row center">
         <a href="{{ path('fos_user_resetting_request') }}">{{ 'security.login.forgot_password'|trans }}</a>
     </div>
 </form>
index 528366afa08517c6a88d0cde45743286778f0882..ee5b2ab7c94b196041875be53bc2bd91670b8d6e 100644 (file)
@@ -3,6 +3,7 @@
 namespace Tests\Wallabag\ApiBundle\Controller;
 
 use Tests\Wallabag\ApiBundle\WallabagApiTestCase;
+use Wallabag\CoreBundle\Entity\Tag;
 
 class WallabagRestControllerTest extends WallabagApiTestCase
 {
@@ -359,7 +360,7 @@ class WallabagRestControllerTest extends WallabagApiTestCase
         $entry = $this->client->getContainer()
             ->get('doctrine.orm.entity_manager')
             ->getRepository('WallabagCoreBundle:Entry')
-            ->findOneWithTags(1);
+            ->findOneWithTags($this->user->getId());
 
         $entry = $entry[0];
 
@@ -421,7 +422,7 @@ class WallabagRestControllerTest extends WallabagApiTestCase
         $entry = $this->client->getContainer()
             ->get('doctrine.orm.entity_manager')
             ->getRepository('WallabagCoreBundle:Entry')
-            ->findOneWithTags(1);
+            ->findOneWithTags($this->user->getId());
         $entry = $entry[0];
 
         if (!$entry) {
@@ -472,7 +473,7 @@ class WallabagRestControllerTest extends WallabagApiTestCase
         $this->assertEquals($tag['label'], $content['label']);
         $this->assertEquals($tag['slug'], $content['slug']);
 
-        $entries = $entry = $this->client->getContainer()
+        $entries = $this->client->getContainer()
             ->get('doctrine.orm.entity_manager')
             ->getRepository('WallabagCoreBundle:Entry')
             ->findAllByTagId($this->user->getId(), $tag['id']);
@@ -480,6 +481,112 @@ class WallabagRestControllerTest extends WallabagApiTestCase
         $this->assertCount(0, $entries);
     }
 
+    public function testDeleteTagByLabel()
+    {
+        $em = $this->client->getContainer()->get('doctrine.orm.entity_manager');
+        $entry = $this->client->getContainer()
+            ->get('doctrine.orm.entity_manager')
+            ->getRepository('WallabagCoreBundle:Entry')
+            ->findOneWithTags($this->user->getId());
+
+        $entry = $entry[0];
+
+        $tag = new Tag();
+        $tag->setLabel('Awesome tag for test');
+        $em->persist($tag);
+
+        $entry->addTag($tag);
+
+        $em->persist($entry);
+        $em->flush();
+
+        $this->client->request('DELETE', '/api/tag/label.json', ['tag' => $tag->getLabel()]);
+
+        $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
+
+        $content = json_decode($this->client->getResponse()->getContent(), true);
+
+        $this->assertArrayHasKey('label', $content);
+        $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->getId());
+
+        $this->assertCount(0, $entries);
+    }
+
+    public function testDeleteTagByLabelNotFound()
+    {
+        $this->client->request('DELETE', '/api/tag/label.json', ['tag' => 'does not exist']);
+
+        $this->assertEquals(404, $this->client->getResponse()->getStatusCode());
+    }
+
+    public function testDeleteTagsByLabel()
+    {
+        $em = $this->client->getContainer()->get('doctrine.orm.entity_manager');
+        $entry = $this->client->getContainer()
+            ->get('doctrine.orm.entity_manager')
+            ->getRepository('WallabagCoreBundle:Entry')
+            ->findOneWithTags($this->user->getId());
+
+        $entry = $entry[0];
+
+        $tag = new Tag();
+        $tag->setLabel('Awesome tag for tagsLabel');
+        $em->persist($tag);
+
+        $tag2 = new Tag();
+        $tag2->setLabel('Awesome tag for tagsLabel 2');
+        $em->persist($tag2);
+
+        $entry->addTag($tag);
+        $entry->addTag($tag2);
+
+        $em->persist($entry);
+        $em->flush();
+
+        $this->client->request('DELETE', '/api/tags/label.json', ['tags' => $tag->getLabel().','.$tag2->getLabel()]);
+
+        $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
+
+        $content = json_decode($this->client->getResponse()->getContent(), true);
+
+        $this->assertCount(2, $content);
+
+        $this->assertArrayHasKey('label', $content[0]);
+        $this->assertEquals($tag->getLabel(), $content[0]['label']);
+        $this->assertEquals($tag->getSlug(), $content[0]['slug']);
+
+        $this->assertArrayHasKey('label', $content[1]);
+        $this->assertEquals($tag2->getLabel(), $content[1]['label']);
+        $this->assertEquals($tag2->getSlug(), $content[1]['slug']);
+
+        $entries = $this->client->getContainer()
+            ->get('doctrine.orm.entity_manager')
+            ->getRepository('WallabagCoreBundle:Entry')
+            ->findAllByTagId($this->user->getId(), $tag->getId());
+
+        $this->assertCount(0, $entries);
+
+        $entries = $this->client->getContainer()
+            ->get('doctrine.orm.entity_manager')
+            ->getRepository('WallabagCoreBundle:Entry')
+            ->findAllByTagId($this->user->getId(), $tag2->getId());
+
+        $this->assertCount(0, $entries);
+    }
+
+    public function testDeleteTagsByLabelNotFound()
+    {
+        $this->client->request('DELETE', '/api/tags/label.json', ['tags' => 'does not exist']);
+
+        $this->assertEquals(404, $this->client->getResponse()->getStatusCode());
+    }
+
     public function testGetVersion()
     {
         $this->client->request('GET', '/api/version');
index c0133af4ab6cb7260dade5ca34f03dc9dacf5352..07ff2772e05d4b7f50255975f3d44f4347c8065f 100644 (file)
@@ -33,7 +33,7 @@ class InstallCommandTest extends WallabagCoreTestCase
     }
 
     /**
-     * Ensure next tests will have a clean database
+     * Ensure next tests will have a clean database.
      */
     public static function tearDownAfterClass()
     {
index 5c739c78a3349f167f49c2d273971e31cd453526..a74c17d9549372324632edbf5910765c9d6a8087 100644 (file)
@@ -236,6 +236,16 @@ class EntryControllerTest extends WallabagCoreTestCase
         $this->assertEquals(200, $client->getResponse()->getStatusCode());
     }
 
+    public function testUntagged()
+    {
+        $this->logInAs('admin');
+        $client = $this->getClient();
+
+        $client->request('GET', '/untagged/list');
+
+        $this->assertEquals(200, $client->getResponse()->getStatusCode());
+    }
+
     public function testStarred()
     {
         $this->logInAs('admin');
@@ -698,4 +708,47 @@ class EntryControllerTest extends WallabagCoreTestCase
         $crawler = $client->submit($form, $data);
         $this->assertCount(2, $crawler->filter('div[class=entry]'));
     }
+
+    public function testCache()
+    {
+        $this->logInAs('admin');
+        $client = $this->getClient();
+
+        $content = $client->getContainer()
+            ->get('doctrine.orm.entity_manager')
+            ->getRepository('WallabagCoreBundle:Entry')
+            ->findOneByUser($this->getLoggedInUserId());
+
+        // no uuid
+        $client->request('GET', '/share/'.$content->getUuid());
+        $this->assertEquals(404, $client->getResponse()->getStatusCode());
+
+        // generating the uuid
+        $client->request('GET', '/share/'.$content->getId());
+        $this->assertEquals(302, $client->getResponse()->getStatusCode());
+
+        // follow link with uuid
+        $crawler = $client->followRedirect();
+        $this->assertEquals(200, $client->getResponse()->getStatusCode());
+        $this->assertContains('max-age=25200', $client->getResponse()->headers->get('cache-control'));
+        $this->assertContains('public', $client->getResponse()->headers->get('cache-control'));
+        $this->assertContains('s-maxage=25200', $client->getResponse()->headers->get('cache-control'));
+        $this->assertNotContains('no-cache', $client->getResponse()->headers->get('cache-control'));
+
+        // sharing is now disabled
+        $client->getContainer()->get('craue_config')->set('share_public', 0);
+        $client->request('GET', '/share/'.$content->getUuid());
+        $this->assertEquals(404, $client->getResponse()->getStatusCode());
+
+        $client->request('GET', '/view/'.$content->getId());
+        $this->assertContains('no-cache', $client->getResponse()->headers->get('cache-control'));
+
+        // removing the share
+        $client->request('GET', '/share/delete/'.$content->getId());
+        $this->assertEquals(302, $client->getResponse()->getStatusCode());
+
+        // share is now disable
+        $client->request('GET', '/share/'.$content->getUuid());
+        $this->assertEquals(404, $client->getResponse()->getStatusCode());
+    }
 }
index 03355f5abde2d02820ea5ce842c8938dccbe8dda..08f4676ed010d2b3db60410c6bbc4fe4a057dff4 100644 (file)
@@ -69,4 +69,19 @@ class SecurityControllerTest extends WallabagCoreTestCase
         $this->assertTrue($user->isTrustedComputer('ABCDEF'));
         $this->assertFalse($user->isTrustedComputer('FEDCBA'));
     }
+
+    public function testEnabledRegistration()
+    {
+        $client = $this->getClient();
+
+        if (!$client->getContainer()->getParameter('fosuser_registration')) {
+            $this->markTestSkipped('fosuser_registration is not enabled.');
+
+            return;
+        }
+
+        $client->followRedirects();
+        $crawler = $client->request('GET', '/register');
+        $this->assertContains('registration.submit', $crawler->filter('body')->extract(['_text'])[0]);
+    }
 }
index 58450e5fb0933632480e8a7c60e6c30a20efc7a3..71652760d8a592c2729147ff4691527d1c819d3b 100644 (file)
@@ -131,4 +131,35 @@ class TagControllerTest extends WallabagCoreTestCase
 
         $this->assertEquals(404, $client->getResponse()->getStatusCode());
     }
+
+    public function testShowEntriesForTagAction()
+    {
+        $this->logInAs('admin');
+        $client = $this->getClient();
+
+        $entry = $client->getContainer()
+            ->get('doctrine.orm.entity_manager')
+            ->getRepository('WallabagCoreBundle:Entry')
+            ->findOneByUsernameAndNotArchived('admin');
+
+        $tag = $client->getContainer()
+            ->get('doctrine.orm.entity_manager')
+            ->getRepository('WallabagCoreBundle:Tag')
+            ->findOneByEntryAndTagLabel($entry, 'foo');
+
+        $crawler = $client->request('GET', '/tag/list/'.$tag->getSlug());
+
+        $this->assertEquals(200, $client->getResponse()->getStatusCode());
+        $this->assertCount(2, $crawler->filter('div[class=entry]'));
+
+        $tag = $client->getContainer()
+            ->get('doctrine.orm.entity_manager')
+            ->getRepository('WallabagCoreBundle:Tag')
+            ->findOneByLabel('baz');
+
+        $crawler = $client->request('GET', '/tag/list/'.$tag->getSlug());
+
+        $this->assertEquals(200, $client->getResponse()->getStatusCode());
+        $this->assertCount(0, $crawler->filter('div[class=entry]'));
+    }
 }
index 96b5300ba3531c3378841d7544943871898fe99d..d869cdf9c56bc34f68cdaeae9fdb16eaf8a34096 100644 (file)
@@ -24,6 +24,6 @@ class ImportControllerTest extends WallabagCoreTestCase
         $crawler = $client->request('GET', '/import/');
 
         $this->assertEquals(200, $client->getResponse()->getStatusCode());
-        $this->assertEquals(3, $crawler->filter('blockquote')->count());
+        $this->assertEquals(4, $crawler->filter('blockquote')->count());
     }
 }
diff --git a/tests/Wallabag/ImportBundle/Controller/ReadabilityControllerTest.php b/tests/Wallabag/ImportBundle/Controller/ReadabilityControllerTest.php
new file mode 100644 (file)
index 0000000..92cf4bf
--- /dev/null
@@ -0,0 +1,122 @@
+<?php
+
+namespace Tests\Wallabag\ImportBundle\Controller;
+
+use Tests\Wallabag\CoreBundle\WallabagCoreTestCase;
+use Symfony\Component\HttpFoundation\File\UploadedFile;
+
+class ReadabilityControllerTest extends WallabagCoreTestCase
+{
+    public function testImportReadability()
+    {
+        $this->logInAs('admin');
+        $client = $this->getClient();
+
+        $crawler = $client->request('GET', '/import/readability');
+
+        $this->assertEquals(200, $client->getResponse()->getStatusCode());
+        $this->assertEquals(1, $crawler->filter('form[name=upload_import_file] > button[type=submit]')->count());
+        $this->assertEquals(1, $crawler->filter('input[type=file]')->count());
+    }
+
+    public function testImportReadabilityWithFile()
+    {
+        $this->logInAs('admin');
+        $client = $this->getClient();
+
+        $crawler = $client->request('GET', '/import/readability');
+        $form = $crawler->filter('form[name=upload_import_file] > button[type=submit]')->form();
+
+        $file = new UploadedFile(__DIR__.'/../fixtures/readability.json', 'readability.json');
+
+        $data = [
+            'upload_import_file[file]' => $file,
+        ];
+
+        $client->submit($form, $data);
+
+        $this->assertEquals(302, $client->getResponse()->getStatusCode());
+
+        $crawler = $client->followRedirect();
+
+        $content = $client->getContainer()
+            ->get('doctrine.orm.entity_manager')
+            ->getRepository('WallabagCoreBundle:Entry')
+            ->findByUrlAndUserId(
+                'https://venngage.com/blog/hashtags-are-worthless/',
+                $this->getLoggedInUserId()
+            );
+
+        $this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text']));
+        $this->assertContains('flashes.import.notice.summary', $body[0]);
+    }
+
+    public function testImportReadabilityWithFileAndMarkAllAsRead()
+    {
+        $this->logInAs('admin');
+        $client = $this->getClient();
+
+        $crawler = $client->request('GET', '/import/readability');
+        $form = $crawler->filter('form[name=upload_import_file] > button[type=submit]')->form();
+
+        $file = new UploadedFile(__DIR__.'/../fixtures/readability-read.json', 'readability-read.json');
+
+        $data = [
+            'upload_import_file[file]' => $file,
+            'upload_import_file[mark_as_read]' => 1,
+        ];
+
+        $client->submit($form, $data);
+
+        $this->assertEquals(302, $client->getResponse()->getStatusCode());
+
+        $crawler = $client->followRedirect();
+
+        $content1 = $client->getContainer()
+            ->get('doctrine.orm.entity_manager')
+            ->getRepository('WallabagCoreBundle:Entry')
+            ->findByUrlAndUserId(
+                'https://blog.travis-ci.com/2016-07-28-what-we-learned-from-analyzing-2-million-travis-builds/',
+                $this->getLoggedInUserId()
+            );
+
+        $this->assertTrue($content1->isArchived());
+
+        $content2 = $client->getContainer()
+            ->get('doctrine.orm.entity_manager')
+            ->getRepository('WallabagCoreBundle:Entry')
+            ->findByUrlAndUserId(
+                'https://facebook.github.io/graphql/',
+                $this->getLoggedInUserId()
+            );
+
+        $this->assertTrue($content2->isArchived());
+
+        $this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text']));
+        $this->assertContains('flashes.import.notice.summary', $body[0]);
+    }
+
+    public function testImportReadabilityWithEmptyFile()
+    {
+        $this->logInAs('admin');
+        $client = $this->getClient();
+
+        $crawler = $client->request('GET', '/import/readability');
+        $form = $crawler->filter('form[name=upload_import_file] > button[type=submit]')->form();
+
+        $file = new UploadedFile(__DIR__.'/../fixtures/test.txt', 'test.txt');
+
+        $data = [
+            'upload_import_file[file]' => $file,
+        ];
+
+        $client->submit($form, $data);
+
+        $this->assertEquals(302, $client->getResponse()->getStatusCode());
+
+        $crawler = $client->followRedirect();
+
+        $this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text']));
+        $this->assertContains('flashes.import.notice.failed', $body[0]);
+    }
+}
diff --git a/tests/Wallabag/ImportBundle/Import/ReadabilityImportTest.php b/tests/Wallabag/ImportBundle/Import/ReadabilityImportTest.php
new file mode 100644 (file)
index 0000000..706d707
--- /dev/null
@@ -0,0 +1,150 @@
+<?php
+
+namespace Tests\Wallabag\ImportBundle\Import;
+
+use Wallabag\ImportBundle\Import\ReadabilityImport;
+use Wallabag\UserBundle\Entity\User;
+use Wallabag\CoreBundle\Entity\Entry;
+use Monolog\Logger;
+use Monolog\Handler\TestHandler;
+
+class ReadabilityImportTest extends \PHPUnit_Framework_TestCase
+{
+    protected $user;
+    protected $em;
+    protected $logHandler;
+    protected $contentProxy;
+
+    private function getReadabilityImport($unsetUser = false)
+    {
+        $this->user = new User();
+
+        $this->em = $this->getMockBuilder('Doctrine\ORM\EntityManager')
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->contentProxy = $this->getMockBuilder('Wallabag\CoreBundle\Helper\ContentProxy')
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $wallabag = new ReadabilityImport($this->em, $this->contentProxy);
+
+        $this->logHandler = new TestHandler();
+        $logger = new Logger('test', [$this->logHandler]);
+        $wallabag->setLogger($logger);
+
+        if (false === $unsetUser) {
+            $wallabag->setUser($this->user);
+        }
+
+        return $wallabag;
+    }
+
+    public function testInit()
+    {
+        $readabilityImport = $this->getReadabilityImport();
+
+        $this->assertEquals('Readability', $readabilityImport->getName());
+        $this->assertNotEmpty($readabilityImport->getUrl());
+        $this->assertEquals('import.readability.description', $readabilityImport->getDescription());
+    }
+
+    public function testImport()
+    {
+        $readabilityImport = $this->getReadabilityImport();
+        $readabilityImport->setFilepath(__DIR__.'/../fixtures/readability.json');
+
+        $entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository')
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $entryRepo->expects($this->exactly(2))
+            ->method('findByUrlAndUserId')
+            ->will($this->onConsecutiveCalls(false, true));
+
+        $this->em
+            ->expects($this->any())
+            ->method('getRepository')
+            ->willReturn($entryRepo);
+
+        $entry = $this->getMockBuilder('Wallabag\CoreBundle\Entity\Entry')
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->contentProxy
+            ->expects($this->exactly(1))
+            ->method('updateEntry')
+            ->willReturn($entry);
+
+        $res = $readabilityImport->import();
+
+        $this->assertTrue($res);
+        $this->assertEquals(['skipped' => 1, 'imported' => 1], $readabilityImport->getSummary());
+    }
+
+    public function testImportAndMarkAllAsRead()
+    {
+        $readabilityImport = $this->getReadabilityImport();
+        $readabilityImport->setFilepath(__DIR__.'/../fixtures/readability-read.json');
+
+        $entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository')
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $entryRepo->expects($this->exactly(2))
+            ->method('findByUrlAndUserId')
+            ->will($this->onConsecutiveCalls(false, false));
+
+        $this->em
+            ->expects($this->any())
+            ->method('getRepository')
+            ->willReturn($entryRepo);
+
+        $this->contentProxy
+            ->expects($this->exactly(2))
+            ->method('updateEntry')
+            ->willReturn(new Entry($this->user));
+
+        // check that every entry persisted are archived
+        $this->em
+            ->expects($this->any())
+            ->method('persist')
+            ->with($this->callback(function ($persistedEntry) {
+                return $persistedEntry->isArchived();
+            }));
+
+        $res = $readabilityImport->setMarkAsRead(true)->import();
+
+        $this->assertTrue($res);
+
+        $this->assertEquals(['skipped' => 0, 'imported' => 2], $readabilityImport->getSummary());
+    }
+
+    public function testImportBadFile()
+    {
+        $readabilityImport = $this->getReadabilityImport();
+        $readabilityImport->setFilepath(__DIR__.'/../fixtures/wallabag-v1.jsonx');
+
+        $res = $readabilityImport->import();
+
+        $this->assertFalse($res);
+
+        $records = $this->logHandler->getRecords();
+        $this->assertContains('ReadabilityImport: unable to read file', $records[0]['message']);
+        $this->assertEquals('ERROR', $records[0]['level_name']);
+    }
+
+    public function testImportUserNotDefined()
+    {
+        $readabilityImport = $this->getReadabilityImport(true);
+        $readabilityImport->setFilepath(__DIR__.'/../fixtures/readability.json');
+
+        $res = $readabilityImport->import();
+
+        $this->assertFalse($res);
+
+        $records = $this->logHandler->getRecords();
+        $this->assertContains('ReadabilityImport: user is not defined', $records[0]['message']);
+        $this->assertEquals('ERROR', $records[0]['level_name']);
+    }
+}
diff --git a/tests/Wallabag/ImportBundle/fixtures/readability-read.json b/tests/Wallabag/ImportBundle/fixtures/readability-read.json
new file mode 100644 (file)
index 0000000..c60767d
--- /dev/null
@@ -0,0 +1,25 @@
+{
+    "bookmarks": [
+        {
+            "article__excerpt": "This is a guest post from Moritz Beller from the Delft University of Technology in The Netherlands. His team produced amazing research on several million Travis CI builds, creating invaluable&hellip;",
+            "favorite": false,
+            "date_archived": "2016-08-02T06:49:30",
+            "article__url": "https://blog.travis-ci.com/2016-07-28-what-we-learned-from-analyzing-2-million-travis-builds/",
+            "date_added": "2016-08-01T05:24:16",
+            "date_favorited": null,
+            "article__title": "Travis",
+            "archive": true
+        },
+        {
+            "article__excerpt": "The GraphQL Type system describes the capabilities of a GraphQL server and is used to determine if a query is valid. The type system also describes the input types of query variables to determine if&hellip;",
+            "favorite": false,
+            "date_archived": "2016-07-19T06:48:31",
+            "article__url": "https://facebook.github.io/graphql/",
+            "date_added": "2016-06-24T17:50:16",
+            "date_favorited": null,
+            "article__title": "GraphQL",
+            "archive": true
+        }
+   ],
+    "recommendations": []
+}
diff --git a/tests/Wallabag/ImportBundle/fixtures/readability.json b/tests/Wallabag/ImportBundle/fixtures/readability.json
new file mode 100644 (file)
index 0000000..3437990
--- /dev/null
@@ -0,0 +1,25 @@
+{
+    "bookmarks": [
+        {
+            "article__excerpt": "When Twitter started it had so much promise to change the way we communicate. But now it has been ruined by the amount of garbage and hate we have to wade through. It&#x2019;s like that polluted&hellip;",
+            "favorite": false,
+            "date_archived": null,
+            "article__url": "https://venngage.com/blog/hashtags-are-worthless/",
+            "date_added": "2016-08-25T12:05:00",
+            "date_favorited": null,
+            "article__title": "We Looked At 167,943 Tweets & Found Out Hashtags Are Worthless",
+            "archive": false
+        },
+        {
+            "article__excerpt": "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. Here&#x2019;s a demo and the code for the infinite&hellip;",
+            "favorite": false,
+            "date_archived": "2016-08-26T12:21:54",
+            "article__url": "https://developers.google.com/web/updates/2016/07/infinite-scroller?imm_mid=0e6839&cmp=em-webops-na-na-newsltr_20160805",
+            "date_added": "2016-08-06T05:35:26",
+            "date_favorited": null,
+            "article__title": "Complexities of an infinite scroller | Web Updates",
+            "archive": true
+        }
+   ],
+    "recommendations": []
+}
index 841338f443bebd1c6ef1fb9490fc05fa00d81800..0a5de5461b46d31fd1006244bb33c235676efc29 100644 (file)
@@ -681,10 +681,17 @@ class SymfonyRequirements extends RequirementCollection
 
             if (class_exists('Symfony\Component\Intl\Intl')) {
                 $this->addRecommendation(
-                    \Symfony\Component\Intl\Intl::getIcuDataVersion() === \Symfony\Component\Intl\Intl::getIcuVersion(),
-                    sprintf('intl ICU version installed on your system (%s) should match the ICU data bundled with Symfony (%s)', \Symfony\Component\Intl\Intl::getIcuVersion(), \Symfony\Component\Intl\Intl::getIcuDataVersion()),
-                    'In most cases you should be fine, but please verify there is no inconsistencies between data provided by Symfony and the intl extension. See https://github.com/symfony/symfony/issues/15007 for an example of inconsistencies you might run into.'
+                    \Symfony\Component\Intl\Intl::getIcuDataVersion() <= \Symfony\Component\Intl\Intl::getIcuVersion(),
+                    sprintf('intl ICU version installed on your system is outdated (%s) and does not match the ICU data bundled with Symfony (%s)', \Symfony\Component\Intl\Intl::getIcuVersion(), \Symfony\Component\Intl\Intl::getIcuDataVersion()),
+                    'To get the latest internationalization data upgrade the ICU system package and the intl PHP extension.'
                 );
+                if (\Symfony\Component\Intl\Intl::getIcuDataVersion() <= \Symfony\Component\Intl\Intl::getIcuVersion()) {
+                    $this->addRecommendation(
+                        \Symfony\Component\Intl\Intl::getIcuDataVersion() === \Symfony\Component\Intl\Intl::getIcuVersion(),
+                        sprintf('intl ICU version installed on your system (%s) does not match the ICU data bundled with Symfony (%s)', \Symfony\Component\Intl\Intl::getIcuVersion(), \Symfony\Component\Intl\Intl::getIcuDataVersion()),
+                        'To avoid internationalization data incosistencies upgrade the symfony/intl component.'
+                    );
+                }
             }
 
             $this->addPhpIniRecommendation(