diff options
47 files changed, 1331 insertions, 221 deletions
diff --git a/app/DoctrineMigrations/Version20160812120952.php b/app/DoctrineMigrations/Version20160812120952.php index 3aafea64..39423e2f 100644 --- a/app/DoctrineMigrations/Version20160812120952.php +++ b/app/DoctrineMigrations/Version20160812120952.php | |||
@@ -33,9 +33,11 @@ class Version20160812120952 extends AbstractMigration implements ContainerAwareI | |||
33 | case 'sqlite': | 33 | case 'sqlite': |
34 | $this->addSql('ALTER TABLE '.$this->getTable('oauth2_clients').' ADD name longtext DEFAULT NULL'); | 34 | $this->addSql('ALTER TABLE '.$this->getTable('oauth2_clients').' ADD name longtext DEFAULT NULL'); |
35 | break; | 35 | break; |
36 | |||
36 | case 'mysql': | 37 | case 'mysql': |
37 | $this->addSql('ALTER TABLE '.$this->getTable('oauth2_clients').' ADD name longtext COLLATE \'utf8_unicode_ci\' DEFAULT NULL'); | 38 | $this->addSql('ALTER TABLE '.$this->getTable('oauth2_clients').' ADD name longtext COLLATE \'utf8_unicode_ci\' DEFAULT NULL'); |
38 | break; | 39 | break; |
40 | |||
39 | case 'postgresql': | 41 | case 'postgresql': |
40 | $this->addSql('ALTER TABLE '.$this->getTable('oauth2_clients').' ADD name text DEFAULT NULL'); | 42 | $this->addSql('ALTER TABLE '.$this->getTable('oauth2_clients').' ADD name text DEFAULT NULL'); |
41 | } | 43 | } |
diff --git a/app/DoctrineMigrations/Version20161001072726.php b/app/DoctrineMigrations/Version20161001072726.php new file mode 100644 index 00000000..237db932 --- /dev/null +++ b/app/DoctrineMigrations/Version20161001072726.php | |||
@@ -0,0 +1,63 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Application\Migrations; | ||
4 | |||
5 | use Doctrine\DBAL\Migrations\AbstractMigration; | ||
6 | use Doctrine\DBAL\Schema\Schema; | ||
7 | use Symfony\Component\DependencyInjection\ContainerAwareInterface; | ||
8 | use Symfony\Component\DependencyInjection\ContainerInterface; | ||
9 | |||
10 | class Version20161001072726 extends AbstractMigration implements ContainerAwareInterface | ||
11 | { | ||
12 | /** | ||
13 | * @var ContainerInterface | ||
14 | */ | ||
15 | private $container; | ||
16 | |||
17 | public function setContainer(ContainerInterface $container = null) | ||
18 | { | ||
19 | $this->container = $container; | ||
20 | } | ||
21 | |||
22 | private function getTable($tableName) | ||
23 | { | ||
24 | return $this->container->getParameter('database_table_prefix') . $tableName; | ||
25 | } | ||
26 | |||
27 | /** | ||
28 | * @param Schema $schema | ||
29 | */ | ||
30 | public function up(Schema $schema) | ||
31 | { | ||
32 | $this->skipIf($this->connection->getDatabasePlatform()->getName() == 'sqlite', 'Migration can only be executed safely on \'mysql\' or \'postgresql\'.'); | ||
33 | |||
34 | // remove all FK from entry_tag | ||
35 | $query = $this->connection->query("SELECT CONSTRAINT_NAME FROM information_schema.key_column_usage WHERE TABLE_NAME = '".$this->getTable('entry_tag')."' AND CONSTRAINT_NAME LIKE 'FK_%' AND TABLE_SCHEMA = '".$this->connection->getDatabase()."'"); | ||
36 | $query->execute(); | ||
37 | |||
38 | foreach ($query->fetchAll() as $fk) { | ||
39 | $this->addSql('ALTER TABLE '.$this->getTable('entry_tag').' DROP FOREIGN KEY '.$fk['CONSTRAINT_NAME']); | ||
40 | } | ||
41 | |||
42 | $this->addSql('ALTER TABLE '.$this->getTable('entry_tag').' ADD CONSTRAINT FK_entry_tag_entry FOREIGN KEY (entry_id) REFERENCES '.$this->getTable('entry').' (id) ON DELETE CASCADE'); | ||
43 | $this->addSql('ALTER TABLE '.$this->getTable('entry_tag').' ADD CONSTRAINT FK_entry_tag_tag FOREIGN KEY (tag_id) REFERENCES '.$this->getTable('tag').' (id) ON DELETE CASCADE'); | ||
44 | |||
45 | // remove entry FK from annotation | ||
46 | $query = $this->connection->query("SELECT CONSTRAINT_NAME FROM information_schema.key_column_usage WHERE TABLE_NAME = '".$this->getTable('annotation')."' AND CONSTRAINT_NAME LIKE 'FK_%' and COLUMN_NAME = 'entry_id' AND TABLE_SCHEMA = '".$this->connection->getDatabase()."'"); | ||
47 | $query->execute(); | ||
48 | |||
49 | foreach ($query->fetchAll() as $fk) { | ||
50 | $this->addSql('ALTER TABLE '.$this->getTable('annotation').' DROP FOREIGN KEY '.$fk['CONSTRAINT_NAME']); | ||
51 | } | ||
52 | |||
53 | $this->addSql('ALTER TABLE '.$this->getTable('annotation').' ADD CONSTRAINT FK_annotation_entry FOREIGN KEY (entry_id) REFERENCES '.$this->getTable('entry').' (id) ON DELETE CASCADE'); | ||
54 | } | ||
55 | |||
56 | /** | ||
57 | * @param Schema $schema | ||
58 | */ | ||
59 | public function down(Schema $schema) | ||
60 | { | ||
61 | throw new SkipMigrationException('Too complex ...'); | ||
62 | } | ||
63 | } | ||
diff --git a/app/DoctrineMigrations/Version20161022134138.php b/app/DoctrineMigrations/Version20161022134138.php new file mode 100644 index 00000000..5cce55a5 --- /dev/null +++ b/app/DoctrineMigrations/Version20161022134138.php | |||
@@ -0,0 +1,77 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Application\Migrations; | ||
4 | |||
5 | use Doctrine\DBAL\Migrations\AbstractMigration; | ||
6 | use Doctrine\DBAL\Schema\Schema; | ||
7 | use Symfony\Component\DependencyInjection\ContainerAwareInterface; | ||
8 | use Symfony\Component\DependencyInjection\ContainerInterface; | ||
9 | |||
10 | class Version20161022134138 extends AbstractMigration implements ContainerAwareInterface | ||
11 | { | ||
12 | /** | ||
13 | * @var ContainerInterface | ||
14 | */ | ||
15 | private $container; | ||
16 | |||
17 | public function setContainer(ContainerInterface $container = null) | ||
18 | { | ||
19 | $this->container = $container; | ||
20 | } | ||
21 | |||
22 | private function getTable($tableName) | ||
23 | { | ||
24 | return $this->container->getParameter('database_table_prefix') . $tableName; | ||
25 | } | ||
26 | |||
27 | /** | ||
28 | * @param Schema $schema | ||
29 | */ | ||
30 | public function up(Schema $schema) | ||
31 | { | ||
32 | $this->skipIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'This migration only apply to MySQL'); | ||
33 | |||
34 | $this->addSql('ALTER DATABASE '.$this->container->getParameter('database_name').' CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;'); | ||
35 | |||
36 | $this->addSql('ALTER TABLE '.$this->getTable('annotation').' CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;'); | ||
37 | $this->addSql('ALTER TABLE '.$this->getTable('entry').' CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;'); | ||
38 | $this->addSql('ALTER TABLE '.$this->getTable('tag').' CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;'); | ||
39 | $this->addSql('ALTER TABLE '.$this->getTable('user').' CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;'); | ||
40 | |||
41 | $this->addSql('ALTER TABLE '.$this->getTable('annotation').' CHANGE `text` `text` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;'); | ||
42 | $this->addSql('ALTER TABLE '.$this->getTable('annotation').' CHANGE `quote` `quote` VARCHAR(191) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;'); | ||
43 | |||
44 | $this->addSql('ALTER TABLE '.$this->getTable('entry').' CHANGE `title` `title` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;'); | ||
45 | $this->addSql('ALTER TABLE '.$this->getTable('entry').' CHANGE `content` `content` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;'); | ||
46 | |||
47 | $this->addSql('ALTER TABLE '.$this->getTable('tag').' CHANGE `label` `label` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;'); | ||
48 | |||
49 | $this->addSql('ALTER TABLE '.$this->getTable('user').' CHANGE `name` `name` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;'); | ||
50 | } | ||
51 | |||
52 | /** | ||
53 | * @param Schema $schema | ||
54 | */ | ||
55 | public function down(Schema $schema) | ||
56 | { | ||
57 | $this->skipIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'This migration only apply to MySQL'); | ||
58 | |||
59 | $this->addSql('ALTER DATABASE '.$this->container->getParameter('database_name').' CHARACTER SET = utf8 COLLATE = utf8_unicode_ci;'); | ||
60 | |||
61 | $this->addSql('ALTER TABLE '.$this->getTable('annotation').' CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;'); | ||
62 | $this->addSql('ALTER TABLE '.$this->getTable('entry').' CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;'); | ||
63 | $this->addSql('ALTER TABLE '.$this->getTable('tag').' CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;'); | ||
64 | $this->addSql('ALTER TABLE '.$this->getTable('user').' CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;'); | ||
65 | |||
66 | $this->addSql('ALTER TABLE '.$this->getTable('annotation').' CHANGE `text` `text` longtext CHARACTER SET utf8 COLLATE utf8_unicode_ci;'); | ||
67 | $this->addSql('ALTER TABLE '.$this->getTable('annotation').' CHANGE `quote` `quote` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci;'); | ||
68 | |||
69 | $this->addSql('ALTER TABLE '.$this->getTable('entry').' CHANGE `title` `title` longtext CHARACTER SET utf8 COLLATE utf8_unicode_ci;'); | ||
70 | $this->addSql('ALTER TABLE '.$this->getTable('entry').' CHANGE `content` `content` longtext CHARACTER SET utf8 COLLATE utf8_unicode_ci;'); | ||
71 | |||
72 | $this->addSql('ALTER TABLE '.$this->getTable('tag').' CHANGE `label` `label` longtext CHARACTER SET utf8 COLLATE utf8_unicode_ci;'); | ||
73 | |||
74 | $this->addSql('ALTER TABLE '.$this->getTable('user').' CHANGE `name` `name` longtext CHARACTER SET utf8 COLLATE utf8_unicode_ci;'); | ||
75 | |||
76 | } | ||
77 | } | ||
diff --git a/app/config/config.yml b/app/config/config.yml index a56cbdd9..dfb0e3b2 100644 --- a/app/config/config.yml +++ b/app/config/config.yml | |||
@@ -76,7 +76,7 @@ doctrine: | |||
76 | dbname: "%database_name%" | 76 | dbname: "%database_name%" |
77 | user: "%database_user%" | 77 | user: "%database_user%" |
78 | password: "%database_password%" | 78 | password: "%database_password%" |
79 | charset: UTF8 | 79 | charset: "%database_charset%" |
80 | path: "%database_path%" | 80 | path: "%database_path%" |
81 | unix_socket: "%database_socket%" | 81 | unix_socket: "%database_socket%" |
82 | server_version: 5.6 | 82 | server_version: 5.6 |
@@ -113,12 +113,26 @@ swiftmailer: | |||
113 | fos_rest: | 113 | fos_rest: |
114 | param_fetcher_listener: true | 114 | param_fetcher_listener: true |
115 | body_listener: true | 115 | body_listener: true |
116 | format_listener: true | ||
117 | view: | 116 | view: |
117 | mime_types: | ||
118 | csv: | ||
119 | - 'text/csv' | ||
120 | - 'text/plain' | ||
121 | pdf: | ||
122 | - 'application/pdf' | ||
123 | epub: | ||
124 | - 'application/epub+zip' | ||
125 | mobi: | ||
126 | - 'application/x-mobipocket-ebook' | ||
118 | view_response_listener: 'force' | 127 | view_response_listener: 'force' |
119 | formats: | 128 | formats: |
120 | xml: true | 129 | xml: true |
121 | json : true | 130 | json: true |
131 | txt: true | ||
132 | csv: true | ||
133 | pdf: true | ||
134 | epub: true | ||
135 | mobi: true | ||
122 | templating_formats: | 136 | templating_formats: |
123 | html: true | 137 | html: true |
124 | force_redirects: | 138 | force_redirects: |
@@ -127,10 +141,21 @@ fos_rest: | |||
127 | default_engine: twig | 141 | default_engine: twig |
128 | routing_loader: | 142 | routing_loader: |
129 | default_format: json | 143 | default_format: json |
144 | format_listener: | ||
145 | enabled: true | ||
146 | rules: | ||
147 | - { path: "^/api/entries/([0-9]+)/export.(.*)", priorities: ['epub', 'mobi', 'pdf', 'txt', 'csv'], fallback_format: false, prefer_extension: false } | ||
148 | - { path: "^/api", priorities: ['json', 'xml'], fallback_format: false, prefer_extension: false } | ||
149 | - { path: "^/annotations", priorities: ['json', 'xml'], fallback_format: false, prefer_extension: false } | ||
150 | # for an unknown reason, EACH REQUEST goes to FOS\RestBundle\EventListener\FormatListener | ||
151 | # so we need to add custom rule for custom api export but also for all other routes of the application... | ||
152 | - { path: '^/', priorities: ['text/html', '*/*'], fallback_format: html, prefer_extension: false } | ||
130 | 153 | ||
131 | nelmio_api_doc: | 154 | nelmio_api_doc: |
132 | sandbox: | 155 | sandbox: |
133 | enabled: false | 156 | enabled: false |
157 | cache: | ||
158 | enabled: true | ||
134 | name: wallabag API documentation | 159 | name: wallabag API documentation |
135 | 160 | ||
136 | nelmio_cors: | 161 | nelmio_cors: |
diff --git a/app/config/config_test.yml b/app/config/config_test.yml index 3eab6fb2..f5e2c25e 100644 --- a/app/config/config_test.yml +++ b/app/config/config_test.yml | |||
@@ -28,7 +28,7 @@ doctrine: | |||
28 | dbname: "%test_database_name%" | 28 | dbname: "%test_database_name%" |
29 | user: "%test_database_user%" | 29 | user: "%test_database_user%" |
30 | password: "%test_database_password%" | 30 | password: "%test_database_password%" |
31 | charset: UTF8 | 31 | charset: "%test_database_charset%" |
32 | path: "%test_database_path%" | 32 | path: "%test_database_path%" |
33 | orm: | 33 | orm: |
34 | metadata_cache_driver: | 34 | metadata_cache_driver: |
diff --git a/app/config/parameters.yml.dist b/app/config/parameters.yml.dist index ece4903a..7a22cb98 100644 --- a/app/config/parameters.yml.dist +++ b/app/config/parameters.yml.dist | |||
@@ -19,16 +19,18 @@ parameters: | |||
19 | database_path: "%kernel.root_dir%/../data/db/wallabag.sqlite" | 19 | database_path: "%kernel.root_dir%/../data/db/wallabag.sqlite" |
20 | database_table_prefix: wallabag_ | 20 | database_table_prefix: wallabag_ |
21 | database_socket: null | 21 | database_socket: null |
22 | # with MySQL, use "utf8mb4" if you got problem with content with emojis | ||
23 | database_charset: utf8 | ||
22 | 24 | ||
23 | mailer_transport: smtp | 25 | mailer_transport: smtp |
24 | mailer_host: 127.0.0.1 | 26 | mailer_host: 127.0.0.1 |
25 | mailer_user: ~ | 27 | mailer_user: ~ |
26 | mailer_password: ~ | 28 | mailer_password: ~ |
27 | 29 | ||
28 | locale: en | 30 | locale: en |
29 | 31 | ||
30 | # A secret key that's used to generate certain security-related tokens | 32 | # A secret key that's used to generate certain security-related tokens |
31 | secret: ovmpmAWXRCabNlMgzlzFXDYmCFfzGv | 33 | secret: ovmpmAWXRCabNlMgzlzFXDYmCFfzGv |
32 | 34 | ||
33 | # two factor stuff | 35 | # two factor stuff |
34 | twofactor_auth: true | 36 | twofactor_auth: true |
diff --git a/app/config/parameters_test.yml b/app/config/parameters_test.yml index 2943b27a..5f2e25bb 100644 --- a/app/config/parameters_test.yml +++ b/app/config/parameters_test.yml | |||
@@ -6,3 +6,4 @@ parameters: | |||
6 | test_database_user: null | 6 | test_database_user: null |
7 | test_database_password: null | 7 | test_database_password: null |
8 | test_database_path: '%kernel.root_dir%/../data/db/wallabag_test.sqlite' | 8 | test_database_path: '%kernel.root_dir%/../data/db/wallabag_test.sqlite' |
9 | test_database_charset: utf8 | ||
diff --git a/app/config/routing_rest.yml b/app/config/routing_rest.yml index 52d395dd..29f4ab14 100644 --- a/app/config/routing_rest.yml +++ b/app/config/routing_rest.yml | |||
@@ -1,4 +1,3 @@ | |||
1 | Rest_Wallabag: | 1 | Rest_Wallabag: |
2 | type : rest | 2 | type : rest |
3 | resource: "@WallabagApiBundle/Resources/config/routing_rest.yml" | 3 | resource: "@WallabagApiBundle/Resources/config/routing_rest.yml" |
4 | |||
diff --git a/app/config/tests/parameters_test.mysql.yml b/app/config/tests/parameters_test.mysql.yml index d8512845..bca2d466 100644 --- a/app/config/tests/parameters_test.mysql.yml +++ b/app/config/tests/parameters_test.mysql.yml | |||
@@ -6,3 +6,4 @@ parameters: | |||
6 | test_database_user: root | 6 | test_database_user: root |
7 | test_database_password: ~ | 7 | test_database_password: ~ |
8 | test_database_path: ~ | 8 | test_database_path: ~ |
9 | test_database_charset: utf8mb4 | ||
diff --git a/app/config/tests/parameters_test.pgsql.yml b/app/config/tests/parameters_test.pgsql.yml index 41383868..3e18d4a0 100644 --- a/app/config/tests/parameters_test.pgsql.yml +++ b/app/config/tests/parameters_test.pgsql.yml | |||
@@ -6,3 +6,4 @@ parameters: | |||
6 | test_database_user: travis | 6 | test_database_user: travis |
7 | test_database_password: ~ | 7 | test_database_password: ~ |
8 | test_database_path: ~ | 8 | test_database_path: ~ |
9 | test_database_charset: utf8 | ||
diff --git a/app/config/tests/parameters_test.sqlite.yml b/app/config/tests/parameters_test.sqlite.yml index 1952e3a6..b8a5f41a 100644 --- a/app/config/tests/parameters_test.sqlite.yml +++ b/app/config/tests/parameters_test.sqlite.yml | |||
@@ -6,3 +6,4 @@ parameters: | |||
6 | test_database_user: ~ | 6 | test_database_user: ~ |
7 | test_database_password: ~ | 7 | test_database_password: ~ |
8 | test_database_path: "%kernel.root_dir%/../data/db/wallabag_test.sqlite" | 8 | test_database_path: "%kernel.root_dir%/../data/db/wallabag_test.sqlite" |
9 | test_database_charset: utf8 | ||
diff --git a/composer.json b/composer.json index 79de337b..ebc0a7dc 100644 --- a/composer.json +++ b/composer.json | |||
@@ -54,11 +54,11 @@ | |||
54 | "sensio/framework-extra-bundle": "^3.0.2", | 54 | "sensio/framework-extra-bundle": "^3.0.2", |
55 | "incenteev/composer-parameter-handler": "^2.0", | 55 | "incenteev/composer-parameter-handler": "^2.0", |
56 | "nelmio/cors-bundle": "~1.4.0", | 56 | "nelmio/cors-bundle": "~1.4.0", |
57 | "friendsofsymfony/rest-bundle": "~1.4", | 57 | "friendsofsymfony/rest-bundle": "~2.1", |
58 | "jms/serializer-bundle": "~1.0", | 58 | "jms/serializer-bundle": "~1.1", |
59 | "nelmio/api-doc-bundle": "~2.7", | 59 | "nelmio/api-doc-bundle": "~2.7", |
60 | "mgargano/simplehtmldom": "~1.5", | 60 | "mgargano/simplehtmldom": "~1.5", |
61 | "tecnickcom/tcpdf": "~6.2", | 61 | "tecnickcom/tc-lib-pdf": "dev-master", |
62 | "simplepie/simplepie": "~1.3.1", | 62 | "simplepie/simplepie": "~1.3.1", |
63 | "willdurand/hateoas-bundle": "~1.0", | 63 | "willdurand/hateoas-bundle": "~1.0", |
64 | "htmlawed/htmlawed": "~1.1.19", | 64 | "htmlawed/htmlawed": "~1.1.19", |
diff --git a/docs/en/user/configuration.rst b/docs/en/user/configuration.rst index f74924df..e7055a14 100644 --- a/docs/en/user/configuration.rst +++ b/docs/en/user/configuration.rst | |||
@@ -50,6 +50,8 @@ User information | |||
50 | 50 | ||
51 | You can change your name, your email address and enable ``Two factor authentication``. | 51 | You can change your name, your email address and enable ``Two factor authentication``. |
52 | 52 | ||
53 | If the wallabag instance has more than one enabled user, you can delete your account here. **Take care, we delete all your data**. | ||
54 | |||
53 | Two factor authentication | 55 | Two factor authentication |
54 | ~~~~~~~~~~~~~~~~~~~~~~~~~ | 56 | ~~~~~~~~~~~~~~~~~~~~~~~~~ |
55 | 57 | ||
diff --git a/docs/en/user/parameters.rst b/docs/en/user/parameters.rst index 6cbd5ae4..c35cf3b8 100644 --- a/docs/en/user/parameters.rst +++ b/docs/en/user/parameters.rst | |||
@@ -12,6 +12,7 @@ What is the meaning of the parameters? | |||
12 | "database_path", "``""%kernel.root_dir%/../data/db/wallabag.sqlite""``", "only for SQLite, define where to put the database file. Leave it empty for other database" | 12 | "database_path", "``""%kernel.root_dir%/../data/db/wallabag.sqlite""``", "only for SQLite, define where to put the database file. Leave it empty for other database" |
13 | "database_table_prefix", "wallabag_", "all wallabag's tables will be prefixed with that string. You can include a ``_`` for clarity" | 13 | "database_table_prefix", "wallabag_", "all wallabag's tables will be prefixed with that string. You can include a ``_`` for clarity" |
14 | "database_socket", "null", "If your database is using a socket instead of tcp, put the path of the socket (other connection parameters will then be ignored)" | 14 | "database_socket", "null", "If your database is using a socket instead of tcp, put the path of the socket (other connection parameters will then be ignored)" |
15 | "database_charset", "utf8mb4", "For PostgreSQL & SQLite you should use utf8, for MySQL use utf8mb4 which handle emoji" | ||
15 | 16 | ||
16 | .. csv-table:: Configuration to send emails from wallabag | 17 | .. csv-table:: Configuration to send emails from wallabag |
17 | :header: "name", "default", "description" | 18 | :header: "name", "default", "description" |
diff --git a/docs/fr/user/configuration.rst b/docs/fr/user/configuration.rst index 8bfe66f5..90eece11 100644 --- a/docs/fr/user/configuration.rst +++ b/docs/fr/user/configuration.rst | |||
@@ -51,6 +51,8 @@ Mon compte | |||
51 | 51 | ||
52 | Vous pouvez ici modifier votre nom, votre adresse email et activer la ``Double authentification``. | 52 | Vous pouvez ici modifier votre nom, votre adresse email et activer la ``Double authentification``. |
53 | 53 | ||
54 | Si l'instance de wallabag compte plus d'un utilisateur actif, vous pouvez supprimer ici votre compte. **Attention, nous supprimons toutes vos données**. | ||
55 | |||
54 | Double authentification (2FA) | 56 | Double authentification (2FA) |
55 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 57 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
56 | 58 | ||
diff --git a/src/Wallabag/AnnotationBundle/Controller/WallabagAnnotationController.php b/src/Wallabag/AnnotationBundle/Controller/WallabagAnnotationController.php index ad083e31..c13a034f 100644 --- a/src/Wallabag/AnnotationBundle/Controller/WallabagAnnotationController.php +++ b/src/Wallabag/AnnotationBundle/Controller/WallabagAnnotationController.php | |||
@@ -3,9 +3,8 @@ | |||
3 | namespace Wallabag\AnnotationBundle\Controller; | 3 | namespace Wallabag\AnnotationBundle\Controller; |
4 | 4 | ||
5 | use FOS\RestBundle\Controller\FOSRestController; | 5 | use FOS\RestBundle\Controller\FOSRestController; |
6 | use Nelmio\ApiDocBundle\Annotation\ApiDoc; | 6 | use Symfony\Component\HttpFoundation\JsonResponse; |
7 | use Symfony\Component\HttpFoundation\Request; | 7 | use Symfony\Component\HttpFoundation\Request; |
8 | use Symfony\Component\HttpFoundation\Response; | ||
9 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; | 8 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; |
10 | use Wallabag\AnnotationBundle\Entity\Annotation; | 9 | use Wallabag\AnnotationBundle\Entity\Annotation; |
11 | use Wallabag\CoreBundle\Entity\Entry; | 10 | use Wallabag\CoreBundle\Entity\Entry; |
@@ -15,42 +14,35 @@ class WallabagAnnotationController extends FOSRestController | |||
15 | /** | 14 | /** |
16 | * Retrieve annotations for an entry. | 15 | * Retrieve annotations for an entry. |
17 | * | 16 | * |
18 | * @ApiDoc( | 17 | * @param Entry $entry |
19 | * requirements={ | 18 | * |
20 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} | 19 | * @see Wallabag\ApiBundle\Controller\WallabagRestController |
21 | * } | ||
22 | * ) | ||
23 | * | 20 | * |
24 | * @return Response | 21 | * @return JsonResponse |
25 | */ | 22 | */ |
26 | public function getAnnotationsAction(Entry $entry) | 23 | public function getAnnotationsAction(Entry $entry) |
27 | { | 24 | { |
28 | $annotationRows = $this | 25 | $annotationRows = $this |
29 | ->getDoctrine() | 26 | ->getDoctrine() |
30 | ->getRepository('WallabagAnnotationBundle:Annotation') | 27 | ->getRepository('WallabagAnnotationBundle:Annotation') |
31 | ->findAnnotationsByPageId($entry->getId(), $this->getUser()->getId()); | 28 | ->findAnnotationsByPageId($entry->getId(), $this->getUser()->getId()); |
32 | $total = count($annotationRows); | 29 | $total = count($annotationRows); |
33 | $annotations = ['total' => $total, 'rows' => $annotationRows]; | 30 | $annotations = ['total' => $total, 'rows' => $annotationRows]; |
34 | 31 | ||
35 | $json = $this->get('serializer')->serialize($annotations, 'json'); | 32 | $json = $this->get('serializer')->serialize($annotations, 'json'); |
36 | 33 | ||
37 | return $this->renderJsonResponse($json); | 34 | return (new JsonResponse())->setJson($json); |
38 | } | 35 | } |
39 | 36 | ||
40 | /** | 37 | /** |
41 | * Creates a new annotation. | 38 | * Creates a new annotation. |
42 | * | 39 | * |
43 | * @param Entry $entry | 40 | * @param Request $request |
41 | * @param Entry $entry | ||
44 | * | 42 | * |
45 | * @ApiDoc( | 43 | * @return JsonResponse |
46 | * requirements={ | ||
47 | * {"name"="ranges", "dataType"="array", "requirement"="\w+", "description"="The range array for the annotation"}, | ||
48 | * {"name"="quote", "dataType"="string", "required"=false, "description"="Optional, quote for the annotation"}, | ||
49 | * {"name"="text", "dataType"="string", "required"=true, "description"=""}, | ||
50 | * } | ||
51 | * ) | ||
52 | * | 44 | * |
53 | * @return Response | 45 | * @see Wallabag\ApiBundle\Controller\WallabagRestController |
54 | */ | 46 | */ |
55 | public function postAnnotationAction(Request $request, Entry $entry) | 47 | public function postAnnotationAction(Request $request, Entry $entry) |
56 | { | 48 | { |
@@ -75,21 +67,20 @@ class WallabagAnnotationController extends FOSRestController | |||
75 | 67 | ||
76 | $json = $this->get('serializer')->serialize($annotation, 'json'); | 68 | $json = $this->get('serializer')->serialize($annotation, 'json'); |
77 | 69 | ||
78 | return $this->renderJsonResponse($json); | 70 | return (new JsonResponse())->setJson($json); |
79 | } | 71 | } |
80 | 72 | ||
81 | /** | 73 | /** |
82 | * Updates an annotation. | 74 | * Updates an annotation. |
83 | * | 75 | * |
84 | * @ApiDoc( | 76 | * @see Wallabag\ApiBundle\Controller\WallabagRestController |
85 | * requirements={ | ||
86 | * {"name"="annotation", "dataType"="string", "requirement"="\w+", "description"="The annotation ID"} | ||
87 | * } | ||
88 | * ) | ||
89 | * | 77 | * |
90 | * @ParamConverter("annotation", class="WallabagAnnotationBundle:Annotation") | 78 | * @ParamConverter("annotation", class="WallabagAnnotationBundle:Annotation") |
91 | * | 79 | * |
92 | * @return Response | 80 | * @param Annotation $annotation |
81 | * @param Request $request | ||
82 | * | ||
83 | * @return JsonResponse | ||
93 | */ | 84 | */ |
94 | public function putAnnotationAction(Annotation $annotation, Request $request) | 85 | public function putAnnotationAction(Annotation $annotation, Request $request) |
95 | { | 86 | { |
@@ -104,21 +95,19 @@ class WallabagAnnotationController extends FOSRestController | |||
104 | 95 | ||
105 | $json = $this->get('serializer')->serialize($annotation, 'json'); | 96 | $json = $this->get('serializer')->serialize($annotation, 'json'); |
106 | 97 | ||
107 | return $this->renderJsonResponse($json); | 98 | return (new JsonResponse())->setJson($json); |
108 | } | 99 | } |
109 | 100 | ||
110 | /** | 101 | /** |
111 | * Removes an annotation. | 102 | * Removes an annotation. |
112 | * | 103 | * |
113 | * @ApiDoc( | 104 | * @see Wallabag\ApiBundle\Controller\WallabagRestController |
114 | * requirements={ | ||
115 | * {"name"="annotation", "dataType"="string", "requirement"="\w+", "description"="The annotation ID"} | ||
116 | * } | ||
117 | * ) | ||
118 | * | 105 | * |
119 | * @ParamConverter("annotation", class="WallabagAnnotationBundle:Annotation") | 106 | * @ParamConverter("annotation", class="WallabagAnnotationBundle:Annotation") |
120 | * | 107 | * |
121 | * @return Response | 108 | * @param Annotation $annotation |
109 | * | ||
110 | * @return JsonResponse | ||
122 | */ | 111 | */ |
123 | public function deleteAnnotationAction(Annotation $annotation) | 112 | public function deleteAnnotationAction(Annotation $annotation) |
124 | { | 113 | { |
@@ -128,19 +117,6 @@ class WallabagAnnotationController extends FOSRestController | |||
128 | 117 | ||
129 | $json = $this->get('serializer')->serialize($annotation, 'json'); | 118 | $json = $this->get('serializer')->serialize($annotation, 'json'); |
130 | 119 | ||
131 | return $this->renderJsonResponse($json); | 120 | return (new JsonResponse())->setJson($json); |
132 | } | ||
133 | |||
134 | /** | ||
135 | * Send a JSON Response. | ||
136 | * We don't use the Symfony JsonRespone, because it takes an array as parameter instead of a JSON string. | ||
137 | * | ||
138 | * @param string $json | ||
139 | * | ||
140 | * @return Response | ||
141 | */ | ||
142 | private function renderJsonResponse($json, $code = 200) | ||
143 | { | ||
144 | return new Response($json, $code, ['application/json']); | ||
145 | } | 121 | } |
146 | } | 122 | } |
diff --git a/src/Wallabag/AnnotationBundle/Entity/Annotation.php b/src/Wallabag/AnnotationBundle/Entity/Annotation.php index c48d8731..0838f5aa 100644 --- a/src/Wallabag/AnnotationBundle/Entity/Annotation.php +++ b/src/Wallabag/AnnotationBundle/Entity/Annotation.php | |||
@@ -82,7 +82,7 @@ class Annotation | |||
82 | * @Exclude | 82 | * @Exclude |
83 | * | 83 | * |
84 | * @ORM\ManyToOne(targetEntity="Wallabag\CoreBundle\Entity\Entry", inversedBy="annotations") | 84 | * @ORM\ManyToOne(targetEntity="Wallabag\CoreBundle\Entity\Entry", inversedBy="annotations") |
85 | * @ORM\JoinColumn(name="entry_id", referencedColumnName="id") | 85 | * @ORM\JoinColumn(name="entry_id", referencedColumnName="id", onDelete="cascade") |
86 | */ | 86 | */ |
87 | private $entry; | 87 | private $entry; |
88 | 88 | ||
diff --git a/src/Wallabag/AnnotationBundle/Repository/AnnotationRepository.php b/src/Wallabag/AnnotationBundle/Repository/AnnotationRepository.php index 5f7da70e..8d3f07ee 100644 --- a/src/Wallabag/AnnotationBundle/Repository/AnnotationRepository.php +++ b/src/Wallabag/AnnotationBundle/Repository/AnnotationRepository.php | |||
@@ -50,7 +50,8 @@ class AnnotationRepository extends EntityRepository | |||
50 | { | 50 | { |
51 | return $this->createQueryBuilder('a') | 51 | return $this->createQueryBuilder('a') |
52 | ->andWhere('a.id = :annotationId')->setParameter('annotationId', $annotationId) | 52 | ->andWhere('a.id = :annotationId')->setParameter('annotationId', $annotationId) |
53 | ->getQuery()->getSingleResult() | 53 | ->getQuery() |
54 | ->getSingleResult() | ||
54 | ; | 55 | ; |
55 | } | 56 | } |
56 | 57 | ||
@@ -67,7 +68,8 @@ class AnnotationRepository extends EntityRepository | |||
67 | return $this->createQueryBuilder('a') | 68 | return $this->createQueryBuilder('a') |
68 | ->where('a.entry = :entryId')->setParameter('entryId', $entryId) | 69 | ->where('a.entry = :entryId')->setParameter('entryId', $entryId) |
69 | ->andwhere('a.user = :userId')->setParameter('userId', $userId) | 70 | ->andwhere('a.user = :userId')->setParameter('userId', $userId) |
70 | ->getQuery()->getResult() | 71 | ->getQuery() |
72 | ->getResult() | ||
71 | ; | 73 | ; |
72 | } | 74 | } |
73 | 75 | ||
@@ -106,4 +108,18 @@ class AnnotationRepository extends EntityRepository | |||
106 | ->getQuery() | 108 | ->getQuery() |
107 | ->getSingleResult(); | 109 | ->getSingleResult(); |
108 | } | 110 | } |
111 | |||
112 | /** | ||
113 | * Remove all annotations for a user id. | ||
114 | * Used when a user want to reset all informations. | ||
115 | * | ||
116 | * @param int $userId | ||
117 | */ | ||
118 | public function removeAllByUserId($userId) | ||
119 | { | ||
120 | $this->getEntityManager() | ||
121 | ->createQuery('DELETE FROM Wallabag\AnnotationBundle\Entity\Annotation a WHERE a.user = :userId') | ||
122 | ->setParameter('userId', $userId) | ||
123 | ->execute(); | ||
124 | } | ||
109 | } | 125 | } |
diff --git a/src/Wallabag/ApiBundle/Controller/WallabagRestController.php b/src/Wallabag/ApiBundle/Controller/WallabagRestController.php index 9997913d..a73d44ca 100644 --- a/src/Wallabag/ApiBundle/Controller/WallabagRestController.php +++ b/src/Wallabag/ApiBundle/Controller/WallabagRestController.php | |||
@@ -3,15 +3,17 @@ | |||
3 | namespace Wallabag\ApiBundle\Controller; | 3 | namespace Wallabag\ApiBundle\Controller; |
4 | 4 | ||
5 | use FOS\RestBundle\Controller\FOSRestController; | 5 | use FOS\RestBundle\Controller\FOSRestController; |
6 | use Hateoas\Configuration\Route; | 6 | use Hateoas\Configuration\Route as HateoasRoute; |
7 | use Hateoas\Representation\Factory\PagerfantaFactory; | 7 | use Hateoas\Representation\Factory\PagerfantaFactory; |
8 | use Nelmio\ApiDocBundle\Annotation\ApiDoc; | 8 | use Nelmio\ApiDocBundle\Annotation\ApiDoc; |
9 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; | ||
9 | use Symfony\Component\HttpFoundation\Request; | 10 | use Symfony\Component\HttpFoundation\Request; |
10 | use Symfony\Component\HttpFoundation\JsonResponse; | 11 | use Symfony\Component\HttpFoundation\JsonResponse; |
11 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; | 12 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface; |
12 | use Symfony\Component\Security\Core\Exception\AccessDeniedException; | 13 | use Symfony\Component\Security\Core\Exception\AccessDeniedException; |
13 | use Wallabag\CoreBundle\Entity\Entry; | 14 | use Wallabag\CoreBundle\Entity\Entry; |
14 | use Wallabag\CoreBundle\Entity\Tag; | 15 | use Wallabag\CoreBundle\Entity\Tag; |
16 | use Wallabag\AnnotationBundle\Entity\Annotation; | ||
15 | 17 | ||
16 | class WallabagRestController extends FOSRestController | 18 | class WallabagRestController extends FOSRestController |
17 | { | 19 | { |
@@ -115,7 +117,7 @@ class WallabagRestController extends FOSRestController | |||
115 | $pagerfantaFactory = new PagerfantaFactory('page', 'perPage'); | 117 | $pagerfantaFactory = new PagerfantaFactory('page', 'perPage'); |
116 | $paginatedCollection = $pagerfantaFactory->createRepresentation( | 118 | $paginatedCollection = $pagerfantaFactory->createRepresentation( |
117 | $pager, | 119 | $pager, |
118 | new Route( | 120 | new HateoasRoute( |
119 | 'api_get_entries', | 121 | 'api_get_entries', |
120 | [ | 122 | [ |
121 | 'archive' => $isArchived, | 123 | 'archive' => $isArchived, |
@@ -158,6 +160,28 @@ class WallabagRestController extends FOSRestController | |||
158 | } | 160 | } |
159 | 161 | ||
160 | /** | 162 | /** |
163 | * Retrieve a single entry as a predefined format. | ||
164 | * | ||
165 | * @ApiDoc( | ||
166 | * requirements={ | ||
167 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} | ||
168 | * } | ||
169 | * ) | ||
170 | * | ||
171 | * @return Response | ||
172 | */ | ||
173 | public function getEntryExportAction(Entry $entry, Request $request) | ||
174 | { | ||
175 | $this->validateAuthentication(); | ||
176 | $this->validateUserAccess($entry->getUser()->getId()); | ||
177 | |||
178 | return $this->get('wallabag_core.helper.entries_export') | ||
179 | ->setEntries($entry) | ||
180 | ->updateTitle('entry') | ||
181 | ->exportAs($request->attributes->get('_format')); | ||
182 | } | ||
183 | |||
184 | /** | ||
161 | * Create an entry. | 185 | * Create an entry. |
162 | * | 186 | * |
163 | * @ApiDoc( | 187 | * @ApiDoc( |
@@ -496,6 +520,104 @@ class WallabagRestController extends FOSRestController | |||
496 | } | 520 | } |
497 | 521 | ||
498 | /** | 522 | /** |
523 | * Retrieve annotations for an entry. | ||
524 | * | ||
525 | * @ApiDoc( | ||
526 | * requirements={ | ||
527 | * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} | ||
528 | * } | ||
529 | * ) | ||
530 | * | ||
531 | * @param Entry $entry | ||
532 | * | ||
533 | * @return JsonResponse | ||
534 | */ | ||
535 | public function getAnnotationsAction(Entry $entry) | ||
536 | { | ||
537 | $this->validateAuthentication(); | ||
538 | |||
539 | return $this->forward('WallabagAnnotationBundle:WallabagAnnotation:getAnnotations', [ | ||
540 | 'entry' => $entry, | ||
541 | ]); | ||
542 | } | ||
543 | |||
544 | /** | ||
545 | * Creates a new annotation. | ||
546 | * | ||
547 | * @ApiDoc( | ||
548 | * requirements={ | ||
549 | * {"name"="ranges", "dataType"="array", "requirement"="\w+", "description"="The range array for the annotation"}, | ||
550 | * {"name"="quote", "dataType"="string", "required"=false, "description"="Optional, quote for the annotation"}, | ||
551 | * {"name"="text", "dataType"="string", "required"=true, "description"=""}, | ||
552 | * } | ||
553 | * ) | ||
554 | * | ||
555 | * @param Request $request | ||
556 | * @param Entry $entry | ||
557 | * | ||
558 | * @return JsonResponse | ||
559 | */ | ||
560 | public function postAnnotationAction(Request $request, Entry $entry) | ||
561 | { | ||
562 | $this->validateAuthentication(); | ||
563 | |||
564 | return $this->forward('WallabagAnnotationBundle:WallabagAnnotation:postAnnotation', [ | ||
565 | 'request' => $request, | ||
566 | 'entry' => $entry, | ||
567 | ]); | ||
568 | } | ||
569 | |||
570 | /** | ||
571 | * Updates an annotation. | ||
572 | * | ||
573 | * @ApiDoc( | ||
574 | * requirements={ | ||
575 | * {"name"="annotation", "dataType"="string", "requirement"="\w+", "description"="The annotation ID"} | ||
576 | * } | ||
577 | * ) | ||
578 | * | ||
579 | * @ParamConverter("annotation", class="WallabagAnnotationBundle:Annotation") | ||
580 | * | ||
581 | * @param Annotation $annotation | ||
582 | * @param Request $request | ||
583 | * | ||
584 | * @return JsonResponse | ||
585 | */ | ||
586 | public function putAnnotationAction(Annotation $annotation, Request $request) | ||
587 | { | ||
588 | $this->validateAuthentication(); | ||
589 | |||
590 | return $this->forward('WallabagAnnotationBundle:WallabagAnnotation:putAnnotation', [ | ||
591 | 'annotation' => $annotation, | ||
592 | 'request' => $request, | ||
593 | ]); | ||
594 | } | ||
595 | |||
596 | /** | ||
597 | * Removes an annotation. | ||
598 | * | ||
599 | * @ApiDoc( | ||
600 | * requirements={ | ||
601 | * {"name"="annotation", "dataType"="string", "requirement"="\w+", "description"="The annotation ID"} | ||
602 | * } | ||
603 | * ) | ||
604 | * | ||
605 | * @ParamConverter("annotation", class="WallabagAnnotationBundle:Annotation") | ||
606 | * | ||
607 | * @param Annotation $annotation | ||
608 | * | ||
609 | * @return JsonResponse | ||
610 | */ | ||
611 | public function deleteAnnotationAction(Annotation $annotation) | ||
612 | { | ||
613 | $this->validateAuthentication(); | ||
614 | |||
615 | return $this->forward('WallabagAnnotationBundle:WallabagAnnotation:deleteAnnotation', [ | ||
616 | 'annotation' => $annotation, | ||
617 | ]); | ||
618 | } | ||
619 | |||
620 | /** | ||
499 | * Retrieve version number. | 621 | * Retrieve version number. |
500 | * | 622 | * |
501 | * @ApiDoc() | 623 | * @ApiDoc() |
diff --git a/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml b/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml index 5f43f971..35f8b2c1 100644 --- a/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml +++ b/src/Wallabag/ApiBundle/Resources/config/routing_rest.yml | |||
@@ -1,4 +1,4 @@ | |||
1 | entries: | 1 | api: |
2 | type: rest | 2 | type: rest |
3 | resource: "WallabagApiBundle:WallabagRest" | 3 | resource: "WallabagApiBundle:WallabagRest" |
4 | name_prefix: api_ | 4 | name_prefix: api_ |
diff --git a/src/Wallabag/CoreBundle/Command/InstallCommand.php b/src/Wallabag/CoreBundle/Command/InstallCommand.php index 857a8b4c..277f8524 100644 --- a/src/Wallabag/CoreBundle/Command/InstallCommand.php +++ b/src/Wallabag/CoreBundle/Command/InstallCommand.php | |||
@@ -40,7 +40,7 @@ class InstallCommand extends ContainerAwareCommand | |||
40 | { | 40 | { |
41 | $this | 41 | $this |
42 | ->setName('wallabag:install') | 42 | ->setName('wallabag:install') |
43 | ->setDescription('Wallabag installer.') | 43 | ->setDescription('wallabag installer.') |
44 | ->addOption( | 44 | ->addOption( |
45 | 'reset', | 45 | 'reset', |
46 | null, | 46 | null, |
@@ -55,7 +55,7 @@ class InstallCommand extends ContainerAwareCommand | |||
55 | $this->defaultInput = $input; | 55 | $this->defaultInput = $input; |
56 | $this->defaultOutput = $output; | 56 | $this->defaultOutput = $output; |
57 | 57 | ||
58 | $output->writeln('<info>Installing Wallabag...</info>'); | 58 | $output->writeln('<info>Installing wallabag...</info>'); |
59 | $output->writeln(''); | 59 | $output->writeln(''); |
60 | 60 | ||
61 | $this | 61 | $this |
@@ -65,7 +65,7 @@ class InstallCommand extends ContainerAwareCommand | |||
65 | ->setupConfig() | 65 | ->setupConfig() |
66 | ; | 66 | ; |
67 | 67 | ||
68 | $output->writeln('<info>Wallabag has been successfully installed.</info>'); | 68 | $output->writeln('<info>wallabag has been successfully installed.</info>'); |
69 | $output->writeln('<comment>Just execute `php bin/console server:run --env=prod` for using wallabag: http://localhost:8000</comment>'); | 69 | $output->writeln('<comment>Just execute `php bin/console server:run --env=prod` for using wallabag: http://localhost:8000</comment>'); |
70 | } | 70 | } |
71 | 71 | ||
@@ -77,7 +77,7 @@ class InstallCommand extends ContainerAwareCommand | |||
77 | 77 | ||
78 | // testing if database driver exists | 78 | // testing if database driver exists |
79 | $fulfilled = true; | 79 | $fulfilled = true; |
80 | $label = '<comment>PDO Driver</comment>'; | 80 | $label = '<comment>PDO Driver (%s)</comment>'; |
81 | $status = '<info>OK!</info>'; | 81 | $status = '<info>OK!</info>'; |
82 | $help = ''; | 82 | $help = ''; |
83 | 83 | ||
@@ -87,7 +87,7 @@ class InstallCommand extends ContainerAwareCommand | |||
87 | $help = 'Database driver "'.$this->getContainer()->getParameter('database_driver').'" is not installed.'; | 87 | $help = 'Database driver "'.$this->getContainer()->getParameter('database_driver').'" is not installed.'; |
88 | } | 88 | } |
89 | 89 | ||
90 | $rows[] = [$label, $status, $help]; | 90 | $rows[] = [sprintf($label, $this->getContainer()->getParameter('database_driver')), $status, $help]; |
91 | 91 | ||
92 | // testing if connection to the database can be etablished | 92 | // testing if connection to the database can be etablished |
93 | $label = '<comment>Database connection</comment>'; | 93 | $label = '<comment>Database connection</comment>'; |
@@ -95,7 +95,8 @@ class InstallCommand extends ContainerAwareCommand | |||
95 | $help = ''; | 95 | $help = ''; |
96 | 96 | ||
97 | try { | 97 | try { |
98 | $this->getContainer()->get('doctrine')->getManager()->getConnection()->connect(); | 98 | $conn = $this->getContainer()->get('doctrine')->getManager()->getConnection(); |
99 | $conn->connect(); | ||
99 | } catch (\Exception $e) { | 100 | } catch (\Exception $e) { |
100 | if (false === strpos($e->getMessage(), 'Unknown database') | 101 | if (false === strpos($e->getMessage(), 'Unknown database') |
101 | && false === strpos($e->getMessage(), 'database "'.$this->getContainer()->getParameter('database_name').'" does not exist')) { | 102 | && false === strpos($e->getMessage(), 'database "'.$this->getContainer()->getParameter('database_name').'" does not exist')) { |
@@ -107,6 +108,21 @@ class InstallCommand extends ContainerAwareCommand | |||
107 | 108 | ||
108 | $rows[] = [$label, $status, $help]; | 109 | $rows[] = [$label, $status, $help]; |
109 | 110 | ||
111 | // now check if MySQL isn't too old to handle utf8mb4 | ||
112 | if ($conn->isConnected() && $conn->getDatabasePlatform() instanceof \Doctrine\DBAL\Platforms\MySqlPlatform) { | ||
113 | $version = $conn->query('select version()')->fetchColumn(); | ||
114 | $minimalVersion = '5.5.4'; | ||
115 | |||
116 | if (false === version_compare($version, $minimalVersion, '>')) { | ||
117 | $fulfilled = false; | ||
118 | $rows[] = [ | ||
119 | '<comment>Database version</comment>', | ||
120 | '<error>ERROR!</error>', | ||
121 | 'Your MySQL version ('.$version.') is too old, consider upgrading ('.$minimalVersion.'+).', | ||
122 | ]; | ||
123 | } | ||
124 | } | ||
125 | |||
110 | foreach ($this->functionExists as $functionRequired) { | 126 | foreach ($this->functionExists as $functionRequired) { |
111 | $label = '<comment>'.$functionRequired.'</comment>'; | 127 | $label = '<comment>'.$functionRequired.'</comment>'; |
112 | $status = '<info>OK!</info>'; | 128 | $status = '<info>OK!</info>'; |
@@ -131,7 +147,7 @@ class InstallCommand extends ContainerAwareCommand | |||
131 | throw new \RuntimeException('Some system requirements are not fulfilled. Please check output messages and fix them.'); | 147 | throw new \RuntimeException('Some system requirements are not fulfilled. Please check output messages and fix them.'); |
132 | } | 148 | } |
133 | 149 | ||
134 | $this->defaultOutput->writeln('<info>Success! Your system can run Wallabag properly.</info>'); | 150 | $this->defaultOutput->writeln('<info>Success! Your system can run wallabag properly.</info>'); |
135 | 151 | ||
136 | $this->defaultOutput->writeln(''); | 152 | $this->defaultOutput->writeln(''); |
137 | 153 | ||
diff --git a/src/Wallabag/CoreBundle/Controller/ConfigController.php b/src/Wallabag/CoreBundle/Controller/ConfigController.php index 91cdcae5..8d391917 100644 --- a/src/Wallabag/CoreBundle/Controller/ConfigController.php +++ b/src/Wallabag/CoreBundle/Controller/ConfigController.php | |||
@@ -7,6 +7,7 @@ use Symfony\Bundle\FrameworkBundle\Controller\Controller; | |||
7 | use Symfony\Component\HttpFoundation\JsonResponse; | 7 | use Symfony\Component\HttpFoundation\JsonResponse; |
8 | use Symfony\Component\HttpFoundation\RedirectResponse; | 8 | use Symfony\Component\HttpFoundation\RedirectResponse; |
9 | use Symfony\Component\HttpFoundation\Request; | 9 | use Symfony\Component\HttpFoundation\Request; |
10 | use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; | ||
10 | use Wallabag\CoreBundle\Entity\Config; | 11 | use Wallabag\CoreBundle\Entity\Config; |
11 | use Wallabag\CoreBundle\Entity\TaggingRule; | 12 | use Wallabag\CoreBundle\Entity\TaggingRule; |
12 | use Wallabag\CoreBundle\Form\Type\ConfigType; | 13 | use Wallabag\CoreBundle\Form\Type\ConfigType; |
@@ -148,6 +149,9 @@ class ConfigController extends Controller | |||
148 | 'token' => $config->getRssToken(), | 149 | 'token' => $config->getRssToken(), |
149 | ], | 150 | ], |
150 | 'twofactor_auth' => $this->getParameter('twofactor_auth'), | 151 | 'twofactor_auth' => $this->getParameter('twofactor_auth'), |
152 | 'enabled_users' => $this->getDoctrine() | ||
153 | ->getRepository('WallabagUserBundle:User') | ||
154 | ->getSumEnabledUsers(), | ||
151 | ]); | 155 | ]); |
152 | } | 156 | } |
153 | 157 | ||
@@ -221,6 +225,80 @@ class ConfigController extends Controller | |||
221 | } | 225 | } |
222 | 226 | ||
223 | /** | 227 | /** |
228 | * Remove all annotations OR tags OR entries for the current user. | ||
229 | * | ||
230 | * @Route("/reset/{type}", requirements={"id" = "annotations|tags|entries"}, name="config_reset") | ||
231 | * | ||
232 | * @return RedirectResponse | ||
233 | */ | ||
234 | public function resetAction($type) | ||
235 | { | ||
236 | $em = $this->getDoctrine()->getManager(); | ||
237 | |||
238 | switch ($type) { | ||
239 | case 'annotations': | ||
240 | $this->getDoctrine() | ||
241 | ->getRepository('WallabagAnnotationBundle:Annotation') | ||
242 | ->removeAllByUserId($this->getUser()->getId()); | ||
243 | break; | ||
244 | |||
245 | case 'tags': | ||
246 | $this->removeAllTagsByUserId($this->getUser()->getId()); | ||
247 | break; | ||
248 | |||
249 | case 'entries': | ||
250 | // SQLite doesn't care about cascading remove, so we need to manually remove associated stuf | ||
251 | // otherwise they won't be removed ... | ||
252 | if ($this->get('doctrine')->getConnection()->getDriver() instanceof \Doctrine\DBAL\Driver\PDOSqlite\Driver) { | ||
253 | $this->getDoctrine()->getRepository('WallabagAnnotationBundle:Annotation')->removeAllByUserId($this->getUser()->getId()); | ||
254 | } | ||
255 | |||
256 | // manually remove tags to avoid orphan tag | ||
257 | $this->removeAllTagsByUserId($this->getUser()->getId()); | ||
258 | |||
259 | $this->getDoctrine() | ||
260 | ->getRepository('WallabagCoreBundle:Entry') | ||
261 | ->removeAllByUserId($this->getUser()->getId()); | ||
262 | } | ||
263 | |||
264 | $this->get('session')->getFlashBag()->add( | ||
265 | 'notice', | ||
266 | 'flashes.config.notice.'.$type.'_reset' | ||
267 | ); | ||
268 | |||
269 | return $this->redirect($this->generateUrl('config').'#set3'); | ||
270 | } | ||
271 | |||
272 | /** | ||
273 | * Remove all tags for a given user and cleanup orphan tags. | ||
274 | * | ||
275 | * @param int $userId | ||
276 | */ | ||
277 | private function removeAllTagsByUserId($userId) | ||
278 | { | ||
279 | $tags = $this->getDoctrine()->getRepository('WallabagCoreBundle:Tag')->findAllTags($userId); | ||
280 | |||
281 | if (empty($tags)) { | ||
282 | return; | ||
283 | } | ||
284 | |||
285 | $this->getDoctrine() | ||
286 | ->getRepository('WallabagCoreBundle:Entry') | ||
287 | ->removeTags($userId, $tags); | ||
288 | |||
289 | // cleanup orphan tags | ||
290 | $em = $this->getDoctrine()->getManager(); | ||
291 | |||
292 | foreach ($tags as $tag) { | ||
293 | if (count($tag->getEntries()) === 0) { | ||
294 | $em->remove($tag); | ||
295 | } | ||
296 | } | ||
297 | |||
298 | $em->flush(); | ||
299 | } | ||
300 | |||
301 | /** | ||
224 | * Validate that a rule can be edited/deleted by the current user. | 302 | * Validate that a rule can be edited/deleted by the current user. |
225 | * | 303 | * |
226 | * @param TaggingRule $rule | 304 | * @param TaggingRule $rule |
@@ -251,4 +329,37 @@ class ConfigController extends Controller | |||
251 | 329 | ||
252 | return $config; | 330 | return $config; |
253 | } | 331 | } |
332 | |||
333 | /** | ||
334 | * Delete account for current user. | ||
335 | * | ||
336 | * @Route("/account/delete", name="delete_account") | ||
337 | * | ||
338 | * @param Request $request | ||
339 | * | ||
340 | * @throws AccessDeniedHttpException | ||
341 | * | ||
342 | * @return \Symfony\Component\HttpFoundation\RedirectResponse | ||
343 | */ | ||
344 | public function deleteAccountAction(Request $request) | ||
345 | { | ||
346 | $enabledUsers = $this->getDoctrine() | ||
347 | ->getRepository('WallabagUserBundle:User') | ||
348 | ->getSumEnabledUsers(); | ||
349 | |||
350 | if ($enabledUsers <= 1) { | ||
351 | throw new AccessDeniedHttpException(); | ||
352 | } | ||
353 | |||
354 | $user = $this->getUser(); | ||
355 | |||
356 | // logout current user | ||
357 | $this->get('security.token_storage')->setToken(null); | ||
358 | $request->getSession()->invalidate(); | ||
359 | |||
360 | $em = $this->get('fos_user.user_manager'); | ||
361 | $em->deleteUser($user); | ||
362 | |||
363 | return $this->redirect($this->generateUrl('fos_user_security_login')); | ||
364 | } | ||
254 | } | 365 | } |
diff --git a/src/Wallabag/CoreBundle/Controller/TagController.php b/src/Wallabag/CoreBundle/Controller/TagController.php index 5acc6852..4542d484 100644 --- a/src/Wallabag/CoreBundle/Controller/TagController.php +++ b/src/Wallabag/CoreBundle/Controller/TagController.php | |||
@@ -90,15 +90,15 @@ class TagController extends Controller | |||
90 | 90 | ||
91 | $flatTags = []; | 91 | $flatTags = []; |
92 | 92 | ||
93 | foreach ($tags as $key => $tag) { | 93 | foreach ($tags as $tag) { |
94 | $nbEntries = $this->getDoctrine() | 94 | $nbEntries = $this->getDoctrine() |
95 | ->getRepository('WallabagCoreBundle:Entry') | 95 | ->getRepository('WallabagCoreBundle:Entry') |
96 | ->countAllEntriesByUserIdAndTagId($this->getUser()->getId(), $tag['id']); | 96 | ->countAllEntriesByUserIdAndTagId($this->getUser()->getId(), $tag->getId()); |
97 | 97 | ||
98 | $flatTags[] = [ | 98 | $flatTags[] = [ |
99 | 'id' => $tag['id'], | 99 | 'id' => $tag->getId(), |
100 | 'label' => $tag['label'], | 100 | 'label' => $tag->getLabel(), |
101 | 'slug' => $tag['slug'], | 101 | 'slug' => $tag->getSlug(), |
102 | 'nbEntries' => $nbEntries, | 102 | 'nbEntries' => $nbEntries, |
103 | ]; | 103 | ]; |
104 | } | 104 | } |
diff --git a/src/Wallabag/CoreBundle/Entity/Entry.php b/src/Wallabag/CoreBundle/Entity/Entry.php index f2da3f4d..dd0f7e67 100644 --- a/src/Wallabag/CoreBundle/Entity/Entry.php +++ b/src/Wallabag/CoreBundle/Entity/Entry.php | |||
@@ -19,7 +19,7 @@ use Wallabag\AnnotationBundle\Entity\Annotation; | |||
19 | * | 19 | * |
20 | * @XmlRoot("entry") | 20 | * @XmlRoot("entry") |
21 | * @ORM\Entity(repositoryClass="Wallabag\CoreBundle\Repository\EntryRepository") | 21 | * @ORM\Entity(repositoryClass="Wallabag\CoreBundle\Repository\EntryRepository") |
22 | * @ORM\Table(name="`entry`") | 22 | * @ORM\Table(name="`entry`", options={"collate"="utf8mb4_unicode_ci", "charset"="utf8mb4"}) |
23 | * @ORM\HasLifecycleCallbacks() | 23 | * @ORM\HasLifecycleCallbacks() |
24 | * @Hateoas\Relation("self", href = "expr('/api/entries/' ~ object.getId())") | 24 | * @Hateoas\Relation("self", href = "expr('/api/entries/' ~ object.getId())") |
25 | */ | 25 | */ |
@@ -190,10 +190,10 @@ class Entry | |||
190 | * @ORM\JoinTable( | 190 | * @ORM\JoinTable( |
191 | * name="entry_tag", | 191 | * name="entry_tag", |
192 | * joinColumns={ | 192 | * joinColumns={ |
193 | * @ORM\JoinColumn(name="entry_id", referencedColumnName="id") | 193 | * @ORM\JoinColumn(name="entry_id", referencedColumnName="id", onDelete="cascade") |
194 | * }, | 194 | * }, |
195 | * inverseJoinColumns={ | 195 | * inverseJoinColumns={ |
196 | * @ORM\JoinColumn(name="tag_id", referencedColumnName="id") | 196 | * @ORM\JoinColumn(name="tag_id", referencedColumnName="id", onDelete="cascade") |
197 | * } | 197 | * } |
198 | * ) | 198 | * ) |
199 | */ | 199 | */ |
diff --git a/src/Wallabag/CoreBundle/Repository/EntryRepository.php b/src/Wallabag/CoreBundle/Repository/EntryRepository.php index cd2b47b9..14616d88 100644 --- a/src/Wallabag/CoreBundle/Repository/EntryRepository.php +++ b/src/Wallabag/CoreBundle/Repository/EntryRepository.php | |||
@@ -329,4 +329,18 @@ class EntryRepository extends EntityRepository | |||
329 | 329 | ||
330 | return $qb->getQuery()->getSingleScalarResult(); | 330 | return $qb->getQuery()->getSingleScalarResult(); |
331 | } | 331 | } |
332 | |||
333 | /** | ||
334 | * Remove all entries for a user id. | ||
335 | * Used when a user want to reset all informations. | ||
336 | * | ||
337 | * @param int $userId | ||
338 | */ | ||
339 | public function removeAllByUserId($userId) | ||
340 | { | ||
341 | $this->getEntityManager() | ||
342 | ->createQuery('DELETE FROM Wallabag\CoreBundle\Entity\Entry e WHERE e.user = :userId') | ||
343 | ->setParameter('userId', $userId) | ||
344 | ->execute(); | ||
345 | } | ||
332 | } | 346 | } |
diff --git a/src/Wallabag/CoreBundle/Repository/TagRepository.php b/src/Wallabag/CoreBundle/Repository/TagRepository.php index e76878d4..81445989 100644 --- a/src/Wallabag/CoreBundle/Repository/TagRepository.php +++ b/src/Wallabag/CoreBundle/Repository/TagRepository.php | |||
@@ -34,6 +34,9 @@ class TagRepository extends EntityRepository | |||
34 | 34 | ||
35 | /** | 35 | /** |
36 | * Find all tags per user. | 36 | * Find all tags per user. |
37 | * Instead of just left joined on the Entry table, we select only id and group by id to avoid tag multiplication in results. | ||
38 | * Once we have all tags id, we can safely request them one by one. | ||
39 | * This'll still be fastest than the previous query. | ||
37 | * | 40 | * |
38 | * @param int $userId | 41 | * @param int $userId |
39 | * | 42 | * |
@@ -41,15 +44,20 @@ class TagRepository extends EntityRepository | |||
41 | */ | 44 | */ |
42 | public function findAllTags($userId) | 45 | public function findAllTags($userId) |
43 | { | 46 | { |
44 | return $this->createQueryBuilder('t') | 47 | $ids = $this->createQueryBuilder('t') |
45 | ->select('t.slug', 't.label', 't.id') | 48 | ->select('t.id') |
46 | ->leftJoin('t.entries', 'e') | 49 | ->leftJoin('t.entries', 'e') |
47 | ->where('e.user = :userId')->setParameter('userId', $userId) | 50 | ->where('e.user = :userId')->setParameter('userId', $userId) |
48 | ->groupBy('t.slug') | 51 | ->groupBy('t.id') |
49 | ->addGroupBy('t.label') | ||
50 | ->addGroupBy('t.id') | ||
51 | ->getQuery() | 52 | ->getQuery() |
52 | ->getArrayResult(); | 53 | ->getArrayResult(); |
54 | |||
55 | $tags = []; | ||
56 | foreach ($ids as $id) { | ||
57 | $tags[] = $this->find($id); | ||
58 | } | ||
59 | |||
60 | return $tags; | ||
53 | } | 61 | } |
54 | 62 | ||
55 | /** | 63 | /** |
diff --git a/src/Wallabag/CoreBundle/Resources/config/services.yml b/src/Wallabag/CoreBundle/Resources/config/services.yml index 614488a6..cc5f9e9a 100644 --- a/src/Wallabag/CoreBundle/Resources/config/services.yml +++ b/src/Wallabag/CoreBundle/Resources/config/services.yml | |||
@@ -129,3 +129,10 @@ services: | |||
129 | arguments: | 129 | arguments: |
130 | - '@twig' | 130 | - '@twig' |
131 | - '%kernel.debug%' | 131 | - '%kernel.debug%' |
132 | |||
133 | wallabag_core.subscriber.sqlite_cascade_delete: | ||
134 | class: Wallabag\CoreBundle\Subscriber\SQLiteCascadeDeleteSubscriber | ||
135 | arguments: | ||
136 | - "@doctrine" | ||
137 | tags: | ||
138 | - { name: doctrine.event_subscriber } | ||
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml index 714ced14..c0595583 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.da.yml | |||
@@ -88,6 +88,18 @@ config: | |||
88 | name_label: 'Navn' | 88 | name_label: 'Navn' |
89 | email_label: 'Emailadresse' | 89 | email_label: 'Emailadresse' |
90 | # twoFactorAuthentication_label: 'Two factor authentication' | 90 | # twoFactorAuthentication_label: 'Two factor authentication' |
91 | delete: | ||
92 | # title: Delete my account (a.k.a danger zone) | ||
93 | # description: If you remove your account, ALL your articles, ALL your tags, ALL your annotations and your account will be PERMANENTLY removed (it can't be UNDONE). You'll then be logged out. | ||
94 | # confirm: Are you really sure? (THIS CAN'T BE UNDONE) | ||
95 | # button: Delete my account | ||
96 | reset: | ||
97 | # title: Reset area (a.k.a danger zone) | ||
98 | # description: By hiting buttons below you'll have ability to remove some informations from your account. Be aware that these actions are IRREVERSIBLE. | ||
99 | # annotations: Remove ALL annotations | ||
100 | # tags: Remove ALL tags | ||
101 | # entries: Remove ALL entries | ||
102 | # confirm: Are you really really sure? (THIS CAN'T BE UNDONE) | ||
91 | form_password: | 103 | form_password: |
92 | old_password_label: 'Gammel adgangskode' | 104 | old_password_label: 'Gammel adgangskode' |
93 | new_password_label: 'Ny adgangskode' | 105 | new_password_label: 'Ny adgangskode' |
@@ -460,6 +472,9 @@ flashes: | |||
460 | # tagging_rules_deleted: 'Tagging rule deleted' | 472 | # tagging_rules_deleted: 'Tagging rule deleted' |
461 | # user_added: 'User "%username%" added' | 473 | # user_added: 'User "%username%" added' |
462 | # rss_token_updated: 'RSS token updated' | 474 | # rss_token_updated: 'RSS token updated' |
475 | # annotations_reset: Annotations reset | ||
476 | # tags_reset: Tags reset | ||
477 | # entries_reset: Entries reset | ||
463 | entry: | 478 | entry: |
464 | notice: | 479 | notice: |
465 | # entry_already_saved: 'Entry already saved on %date%' | 480 | # entry_already_saved: 'Entry already saved on %date%' |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml index 57e49f84..0051da2f 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.de.yml | |||
@@ -88,6 +88,18 @@ config: | |||
88 | name_label: 'Name' | 88 | name_label: 'Name' |
89 | email_label: 'E-Mail-Adresse' | 89 | email_label: 'E-Mail-Adresse' |
90 | twoFactorAuthentication_label: 'Zwei-Faktor-Authentifizierung' | 90 | twoFactorAuthentication_label: 'Zwei-Faktor-Authentifizierung' |
91 | delete: | ||
92 | # title: Delete my account (a.k.a danger zone) | ||
93 | # description: If you remove your account, ALL your articles, ALL your tags, ALL your annotations and your account will be PERMANENTLY removed (it can't be UNDONE). You'll then be logged out. | ||
94 | # confirm: Are you really sure? (THIS CAN'T BE UNDONE) | ||
95 | # button: Delete my account | ||
96 | reset: | ||
97 | # title: Reset area (a.k.a danger zone) | ||
98 | # description: By hiting buttons below you'll have ability to remove some informations from your account. Be aware that these actions are IRREVERSIBLE. | ||
99 | # annotations: Remove ALL annotations | ||
100 | # tags: Remove ALL tags | ||
101 | # entries: Remove ALL entries | ||
102 | # confirm: Are you really really sure? (THIS CAN'T BE UNDONE) | ||
91 | form_password: | 103 | form_password: |
92 | old_password_label: 'Altes Kennwort' | 104 | old_password_label: 'Altes Kennwort' |
93 | new_password_label: 'Neues Kennwort' | 105 | new_password_label: 'Neues Kennwort' |
@@ -460,6 +472,9 @@ flashes: | |||
460 | tagging_rules_deleted: 'Tagging-Regel gelöscht' | 472 | tagging_rules_deleted: 'Tagging-Regel gelöscht' |
461 | user_added: 'Benutzer "%username%" erstellt' | 473 | user_added: 'Benutzer "%username%" erstellt' |
462 | rss_token_updated: 'RSS-Token aktualisiert' | 474 | rss_token_updated: 'RSS-Token aktualisiert' |
475 | # annotations_reset: Annotations reset | ||
476 | # tags_reset: Tags reset | ||
477 | # entries_reset: Entries reset | ||
463 | entry: | 478 | entry: |
464 | notice: | 479 | notice: |
465 | entry_already_saved: 'Eintrag bereits am %date% gespeichert' | 480 | entry_already_saved: 'Eintrag bereits am %date% gespeichert' |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml index 4a59c75e..462be556 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.en.yml | |||
@@ -88,6 +88,18 @@ config: | |||
88 | name_label: 'Name' | 88 | name_label: 'Name' |
89 | email_label: 'Email' | 89 | email_label: 'Email' |
90 | twoFactorAuthentication_label: 'Two factor authentication' | 90 | twoFactorAuthentication_label: 'Two factor authentication' |
91 | delete: | ||
92 | title: Delete my account (a.k.a danger zone) | ||
93 | description: If you remove your account, ALL your articles, ALL your tags, ALL your annotations and your account will be PERMANENTLY removed (it can't be UNDONE). You'll then be logged out. | ||
94 | confirm: Are you really sure? (THIS CAN'T BE UNDONE) | ||
95 | button: Delete my account | ||
96 | reset: | ||
97 | title: Reset area (a.k.a danger zone) | ||
98 | description: By hiting buttons below you'll have ability to remove some informations from your account. Be aware that these actions are IRREVERSIBLE. | ||
99 | annotations: Remove ALL annotations | ||
100 | tags: Remove ALL tags | ||
101 | entries: Remove ALL entries | ||
102 | confirm: Are you really really sure? (THIS CAN'T BE UNDONE) | ||
91 | form_password: | 103 | form_password: |
92 | old_password_label: 'Current password' | 104 | old_password_label: 'Current password' |
93 | new_password_label: 'New password' | 105 | new_password_label: 'New password' |
@@ -459,6 +471,9 @@ flashes: | |||
459 | tagging_rules_updated: 'Tagging rules updated' | 471 | tagging_rules_updated: 'Tagging rules updated' |
460 | tagging_rules_deleted: 'Tagging rule deleted' | 472 | tagging_rules_deleted: 'Tagging rule deleted' |
461 | rss_token_updated: 'RSS token updated' | 473 | rss_token_updated: 'RSS token updated' |
474 | annotations_reset: Annotations reset | ||
475 | tags_reset: Tags reset | ||
476 | entries_reset: Entries reset | ||
462 | entry: | 477 | entry: |
463 | notice: | 478 | notice: |
464 | entry_already_saved: 'Entry already saved on %date%' | 479 | entry_already_saved: 'Entry already saved on %date%' |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml index 1b1e0cb1..cfabe09f 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.es.yml | |||
@@ -88,6 +88,18 @@ config: | |||
88 | name_label: 'Nombre' | 88 | name_label: 'Nombre' |
89 | email_label: 'Direccion e-mail' | 89 | email_label: 'Direccion e-mail' |
90 | twoFactorAuthentication_label: 'Autentificación de dos factores' | 90 | twoFactorAuthentication_label: 'Autentificación de dos factores' |
91 | delete: | ||
92 | # title: Delete my account (a.k.a danger zone) | ||
93 | # description: If you remove your account, ALL your articles, ALL your tags, ALL your annotations and your account will be PERMANENTLY removed (it can't be UNDONE). You'll then be logged out. | ||
94 | # confirm: Are you really sure? (THIS CAN'T BE UNDONE) | ||
95 | # button: Delete my account | ||
96 | reset: | ||
97 | # title: Reset area (a.k.a danger zone) | ||
98 | # description: By hiting buttons below you'll have ability to remove some informations from your account. Be aware that these actions are IRREVERSIBLE. | ||
99 | # annotations: Remove ALL annotations | ||
100 | # tags: Remove ALL tags | ||
101 | # entries: Remove ALL entries | ||
102 | # confirm: Are you really really sure? (THIS CAN'T BE UNDONE) | ||
91 | form_password: | 103 | form_password: |
92 | old_password_label: 'Contraseña actual' | 104 | old_password_label: 'Contraseña actual' |
93 | new_password_label: 'Nueva contraseña' | 105 | new_password_label: 'Nueva contraseña' |
@@ -460,6 +472,9 @@ flashes: | |||
460 | tagging_rules_deleted: 'Regla de etiquetado actualizada' | 472 | tagging_rules_deleted: 'Regla de etiquetado actualizada' |
461 | user_added: 'Usuario "%username%" añadido' | 473 | user_added: 'Usuario "%username%" añadido' |
462 | rss_token_updated: 'RSS token actualizado' | 474 | rss_token_updated: 'RSS token actualizado' |
475 | # annotations_reset: Annotations reset | ||
476 | # tags_reset: Tags reset | ||
477 | # entries_reset: Entries reset | ||
463 | entry: | 478 | entry: |
464 | notice: | 479 | notice: |
465 | entry_already_saved: 'Entrada ya guardada por %fecha%' | 480 | entry_already_saved: 'Entrada ya guardada por %fecha%' |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml index 41dc8acf..07b5bee7 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.fa.yml | |||
@@ -88,6 +88,18 @@ config: | |||
88 | name_label: 'نام' | 88 | name_label: 'نام' |
89 | email_label: 'نشانی ایمیل' | 89 | email_label: 'نشانی ایمیل' |
90 | twoFactorAuthentication_label: 'تأیید ۲مرحلهای' | 90 | twoFactorAuthentication_label: 'تأیید ۲مرحلهای' |
91 | delete: | ||
92 | # title: Delete my account (a.k.a danger zone) | ||
93 | # description: If you remove your account, ALL your articles, ALL your tags, ALL your annotations and your account will be PERMANENTLY removed (it can't be UNDONE). You'll then be logged out. | ||
94 | # confirm: Are you really sure? (THIS CAN'T BE UNDONE) | ||
95 | # button: Delete my account | ||
96 | reset: | ||
97 | # title: Reset area (a.k.a danger zone) | ||
98 | # description: By hiting buttons below you'll have ability to remove some informations from your account. Be aware that these actions are IRREVERSIBLE. | ||
99 | # annotations: Remove ALL annotations | ||
100 | # tags: Remove ALL tags | ||
101 | # entries: Remove ALL entries | ||
102 | # confirm: Are you really really sure? (THIS CAN'T BE UNDONE) | ||
91 | form_password: | 103 | form_password: |
92 | old_password_label: 'رمز قدیمی' | 104 | old_password_label: 'رمز قدیمی' |
93 | new_password_label: 'رمز تازه' | 105 | new_password_label: 'رمز تازه' |
@@ -459,6 +471,9 @@ flashes: | |||
459 | tagging_rules_deleted: 'قانون برچسبگذاری پاک شد' | 471 | tagging_rules_deleted: 'قانون برچسبگذاری پاک شد' |
460 | user_added: 'کابر "%username%" افزوده شد' | 472 | user_added: 'کابر "%username%" افزوده شد' |
461 | rss_token_updated: 'کد آر-اس-اس بهروز شد' | 473 | rss_token_updated: 'کد آر-اس-اس بهروز شد' |
474 | # annotations_reset: Annotations reset | ||
475 | # tags_reset: Tags reset | ||
476 | # entries_reset: Entries reset | ||
462 | entry: | 477 | entry: |
463 | notice: | 478 | notice: |
464 | entry_already_saved: 'این مقاله در تاریخ %date% ذخیره شده بود' | 479 | entry_already_saved: 'این مقاله در تاریخ %date% ذخیره شده بود' |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml index 7fb9681d..db6f9f7e 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.fr.yml | |||
@@ -88,6 +88,18 @@ config: | |||
88 | name_label: 'Nom' | 88 | name_label: 'Nom' |
89 | email_label: 'Adresse e-mail' | 89 | email_label: 'Adresse e-mail' |
90 | twoFactorAuthentication_label: 'Double authentification' | 90 | twoFactorAuthentication_label: 'Double authentification' |
91 | delete: | ||
92 | title: Supprimer mon compte (attention danger !) | ||
93 | description: Si vous confirmez la suppression de votre compte, TOUS les articles, TOUS les tags, TOUTES les annotations et votre compte seront DÉFINITIVEMENT supprimé (c'est IRRÉVERSIBLE). Vous serez ensuite déconnecté. | ||
94 | confirm: Vous êtes vraiment sûr ? (C'EST IRRÉVERSIBLE) | ||
95 | button: 'Supprimer mon compte' | ||
96 | reset: | ||
97 | title: Réinitialisation (attention danger !) | ||
98 | description: En cliquant sur les boutons ci-dessous vous avez la possibilité de supprimer certaines informations de votre compte. Attention, ces actions sont IRRÉVERSIBLES ! | ||
99 | annotations: Supprimer TOUTES les annotations | ||
100 | tags: Supprimer TOUS les tags | ||
101 | entries: Supprimer TOUS les articles | ||
102 | confirm: Êtes-vous vraiment vraiment sûr ? (C'EST IRRÉVERSIBLE) | ||
91 | form_password: | 103 | form_password: |
92 | old_password_label: 'Mot de passe actuel' | 104 | old_password_label: 'Mot de passe actuel' |
93 | new_password_label: 'Nouveau mot de passe' | 105 | new_password_label: 'Nouveau mot de passe' |
@@ -386,7 +398,7 @@ developer: | |||
386 | field_grant_types: 'Type de privilège accordé' | 398 | field_grant_types: 'Type de privilège accordé' |
387 | no_client: 'Aucun client pour le moment' | 399 | no_client: 'Aucun client pour le moment' |
388 | remove: | 400 | remove: |
389 | warn_message_1: 'Vous avez la possibilité de supprimer le client %name%. Cette action est IRREVERSIBLE !' | 401 | warn_message_1: 'Vous avez la possibilité de supprimer le client %name%. Cette action est IRRÉVERSIBLE !' |
390 | warn_message_2: "Si vous supprimez le client %name%, toutes les applications qui l'utilisaient ne fonctionneront plus avec votre compte wallabag." | 402 | warn_message_2: "Si vous supprimez le client %name%, toutes les applications qui l'utilisaient ne fonctionneront plus avec votre compte wallabag." |
391 | action: 'Supprimer le client %name%' | 403 | action: 'Supprimer le client %name%' |
392 | client: | 404 | client: |
@@ -460,9 +472,12 @@ flashes: | |||
460 | tagging_rules_deleted: 'Règle supprimée' | 472 | tagging_rules_deleted: 'Règle supprimée' |
461 | user_added: 'Utilisateur "%username%" ajouté' | 473 | user_added: 'Utilisateur "%username%" ajouté' |
462 | rss_token_updated: 'Jeton RSS mis à jour' | 474 | rss_token_updated: 'Jeton RSS mis à jour' |
475 | annotations_reset: Annotations supprimées | ||
476 | tags_reset: Tags supprimés | ||
477 | entries_reset: Articles supprimés | ||
463 | entry: | 478 | entry: |
464 | notice: | 479 | notice: |
465 | entry_already_saved: 'Article déjà sauvergardé le %date%' | 480 | entry_already_saved: 'Article déjà sauvegardé le %date%' |
466 | entry_saved: 'Article enregistré' | 481 | entry_saved: 'Article enregistré' |
467 | entry_saved_failed: 'Article enregistré mais impossible de récupérer le contenu' | 482 | entry_saved_failed: 'Article enregistré mais impossible de récupérer le contenu' |
468 | entry_updated: 'Article mis à jour' | 483 | entry_updated: 'Article mis à jour' |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml index b279ae40..f1aff51a 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.it.yml | |||
@@ -88,6 +88,18 @@ config: | |||
88 | name_label: 'Nome' | 88 | name_label: 'Nome' |
89 | email_label: 'E-mail' | 89 | email_label: 'E-mail' |
90 | twoFactorAuthentication_label: 'Two factor authentication' | 90 | twoFactorAuthentication_label: 'Two factor authentication' |
91 | delete: | ||
92 | # title: Delete my account (a.k.a danger zone) | ||
93 | # description: If you remove your account, ALL your articles, ALL your tags, ALL your annotations and your account will be PERMANENTLY removed (it can't be UNDONE). You'll then be logged out. | ||
94 | # confirm: Are you really sure? (THIS CAN'T BE UNDONE) | ||
95 | # button: Delete my account | ||
96 | reset: | ||
97 | # title: Reset area (a.k.a danger zone) | ||
98 | # description: By hiting buttons below you'll have ability to remove some informations from your account. Be aware that these actions are IRREVERSIBLE. | ||
99 | # annotations: Remove ALL annotations | ||
100 | # tags: Remove ALL tags | ||
101 | # entries: Remove ALL entries | ||
102 | # confirm: Are you really really sure? (THIS CAN'T BE UNDONE) | ||
91 | form_password: | 103 | form_password: |
92 | old_password_label: 'Password corrente' | 104 | old_password_label: 'Password corrente' |
93 | new_password_label: 'Nuova password' | 105 | new_password_label: 'Nuova password' |
@@ -460,6 +472,9 @@ flashes: | |||
460 | tagging_rules_deleted: 'Regola di tagging aggiornate' | 472 | tagging_rules_deleted: 'Regola di tagging aggiornate' |
461 | user_added: 'Utente "%username%" aggiunto' | 473 | user_added: 'Utente "%username%" aggiunto' |
462 | rss_token_updated: 'RSS token aggiornato' | 474 | rss_token_updated: 'RSS token aggiornato' |
475 | # annotations_reset: Annotations reset | ||
476 | # tags_reset: Tags reset | ||
477 | # entries_reset: Entries reset | ||
463 | entry: | 478 | entry: |
464 | notice: | 479 | notice: |
465 | entry_already_saved: 'Contenuto già salvato in data %date%' | 480 | entry_already_saved: 'Contenuto già salvato in data %date%' |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml index a4659620..e0567d7e 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.oc.yml | |||
@@ -25,13 +25,13 @@ menu: | |||
25 | internal_settings: 'Configuracion interna' | 25 | internal_settings: 'Configuracion interna' |
26 | import: 'Importar' | 26 | import: 'Importar' |
27 | howto: 'Ajuda' | 27 | howto: 'Ajuda' |
28 | developer: 'Desvolopador' | 28 | developer: 'Desvolopaire' |
29 | logout: 'Desconnexion' | 29 | logout: 'Desconnexion' |
30 | about: 'A prepaus' | 30 | about: 'A prepaus' |
31 | search: 'Cercar' | 31 | search: 'Cercar' |
32 | save_link: 'Enregistrar un novèl article' | 32 | save_link: 'Enregistrar un novèl article' |
33 | back_to_unread: 'Tornar als articles pas legits' | 33 | back_to_unread: 'Tornar als articles pas legits' |
34 | # users_management: 'Users management' | 34 | users_management: 'Gestion dels utilizaires' |
35 | top: | 35 | top: |
36 | add_new_entry: 'Enregistrar un novèl article' | 36 | add_new_entry: 'Enregistrar un novèl article' |
37 | search: 'Cercar' | 37 | search: 'Cercar' |
@@ -46,7 +46,7 @@ footer: | |||
46 | social: 'Social' | 46 | social: 'Social' |
47 | powered_by: 'propulsat per' | 47 | powered_by: 'propulsat per' |
48 | about: 'A prepaus' | 48 | about: 'A prepaus' |
49 | # stats: Since %user_creation% you read %nb_archives% articles. That is about %per_day% a day! | 49 | stats: "Dempuèi %user_creation% avètz legit %nb_archives% articles. Es a l'entorn de %per_day% per jorn !" |
50 | 50 | ||
51 | config: | 51 | config: |
52 | page_title: 'Configuracion' | 52 | page_title: 'Configuracion' |
@@ -88,6 +88,18 @@ config: | |||
88 | name_label: 'Nom' | 88 | name_label: 'Nom' |
89 | email_label: 'Adreça de corrièl' | 89 | email_label: 'Adreça de corrièl' |
90 | twoFactorAuthentication_label: 'Dobla autentificacion' | 90 | twoFactorAuthentication_label: 'Dobla autentificacion' |
91 | delete: | ||
92 | # title: Delete my account (a.k.a danger zone) | ||
93 | # description: If you remove your account, ALL your articles, ALL your tags, ALL your annotations and your account will be PERMANENTLY removed (it can't be UNDONE). You'll then be logged out. | ||
94 | # confirm: Are you really sure? (THIS CAN'T BE UNDONE) | ||
95 | # button: Delete my account | ||
96 | reset: | ||
97 | # title: Reset area (a.k.a danger zone) | ||
98 | # description: By hiting buttons below you'll have ability to remove some informations from your account. Be aware that these actions are IRREVERSIBLE. | ||
99 | # annotations: Remove ALL annotations | ||
100 | # tags: Remove ALL tags | ||
101 | # entries: Remove ALL entries | ||
102 | # confirm: Are you really really sure? (THIS CAN'T BE UNDONE) | ||
91 | form_password: | 103 | form_password: |
92 | old_password_label: 'Senhal actual' | 104 | old_password_label: 'Senhal actual' |
93 | new_password_label: 'Senhal novèl' | 105 | new_password_label: 'Senhal novèl' |
@@ -96,7 +108,7 @@ config: | |||
96 | if_label: 'se' | 108 | if_label: 'se' |
97 | then_tag_as_label: 'alara atribuir las etiquetas' | 109 | then_tag_as_label: 'alara atribuir las etiquetas' |
98 | delete_rule_label: 'suprimir' | 110 | delete_rule_label: 'suprimir' |
99 | # edit_rule_label: 'edit' | 111 | edit_rule_label: 'modificar' |
100 | rule_label: 'Règla' | 112 | rule_label: 'Règla' |
101 | tags_label: 'Etiquetas' | 113 | tags_label: 'Etiquetas' |
102 | faq: | 114 | faq: |
@@ -209,7 +221,7 @@ entry: | |||
209 | is_public_label: 'Public' | 221 | is_public_label: 'Public' |
210 | save_label: 'Enregistrar' | 222 | save_label: 'Enregistrar' |
211 | public: | 223 | public: |
212 | # shared_by_wallabag: "This article has been shared by <a href='%wallabag_instance%'>wallabag</a>" | 224 | shared_by_wallabag: "Aqueste article es estat partejat per <a href='%wallabag_instance%'>wallabag</a>" |
213 | 225 | ||
214 | about: | 226 | about: |
215 | page_title: 'A prepaus' | 227 | page_title: 'A prepaus' |
@@ -265,14 +277,14 @@ howto: | |||
265 | 277 | ||
266 | quickstart: | 278 | quickstart: |
267 | page_title: 'Per ben començar' | 279 | page_title: 'Per ben començar' |
268 | # more: 'More…' | 280 | more: 'Mai…' |
269 | intro: | 281 | intro: |
270 | title: 'Benvenguda sus wallabag !' | 282 | title: 'Benvenguda sus wallabag !' |
271 | paragraph_1: "Anem vos guidar per far lo torn de la proprietat e vos presentar unas fonccionalitats que vos poirián interessar per vos apropriar aquesta aisina." | 283 | paragraph_1: "Anem vos guidar per far lo torn de la proprietat e vos presentar unas fonccionalitats que vos poirián interessar per vos apropriar aquesta aisina." |
272 | paragraph_2: 'Seguètz-nos ' | 284 | paragraph_2: 'Seguètz-nos ' |
273 | configure: | 285 | configure: |
274 | title: "Configuratz l'aplicacio" | 286 | title: "Configuratz l'aplicacion" |
275 | # description: 'In order to have an application which suits you, have a look into the configuration of wallabag.' | 287 | description: "Per fin d'aver una aplicacion que vos va ben, anatz veire la configuracion de wallabag." |
276 | language: "Cambiatz la lenga e l'estil de l'aplicacion" | 288 | language: "Cambiatz la lenga e l'estil de l'aplicacion" |
277 | rss: 'Activatz los fluxes RSS' | 289 | rss: 'Activatz los fluxes RSS' |
278 | tagging_rules: 'Escrivètz de règlas per classar automaticament vòstres articles' | 290 | tagging_rules: 'Escrivètz de règlas per classar automaticament vòstres articles' |
@@ -286,7 +298,7 @@ quickstart: | |||
286 | import: 'Configurar los impòrt' | 298 | import: 'Configurar los impòrt' |
287 | first_steps: | 299 | first_steps: |
288 | title: 'Primièrs passes' | 300 | title: 'Primièrs passes' |
289 | # description: "Now wallabag is well configured, it's time to archive the web. You can click on the top right sign + to add a link." | 301 | description: "Ara wallabag es ben configurat, es lo moment d'archivar lo web. Podètz clicar sul signe + a man drecha amont per ajustar un ligam." |
290 | new_article: 'Ajustatz vòstre primièr article' | 302 | new_article: 'Ajustatz vòstre primièr article' |
291 | unread_articles: 'E racaptatz-lo !' | 303 | unread_articles: 'E racaptatz-lo !' |
292 | migrate: | 304 | migrate: |
@@ -298,14 +310,14 @@ quickstart: | |||
298 | readability: 'Migrar dempuèi Readability' | 310 | readability: 'Migrar dempuèi Readability' |
299 | instapaper: 'Migrar dempuèi Instapaper' | 311 | instapaper: 'Migrar dempuèi Instapaper' |
300 | developer: | 312 | developer: |
301 | title: 'Pels desvolopadors' | 313 | title: 'Pels desvolopaires' |
302 | # description: 'We also thought to the developers: Docker, API, translations, etc.' | 314 | description: 'Avèm tanben pensat als desvolopaires : Docker, API, traduccions, etc.' |
303 | create_application: 'Crear vòstra aplicacion tèrça' | 315 | create_application: 'Crear vòstra aplicacion tèrça' |
304 | # use_docker: 'Use Docker to install wallabag' | 316 | use_docker: 'Utilizar Docker per installar wallabag' |
305 | docs: | 317 | docs: |
306 | title: 'Documentacion complèta' | 318 | title: 'Documentacion complèta' |
307 | # description: "There are so much features in wallabag. Don't hesitate to read the manual to know them and to learn how to use them." | 319 | description: "I a un fum de fonccionalitats dins wallabag. Esitetz pas a legir lo manual per las conéisser e aprendre a las utilizar." |
308 | annotate: 'Anotatar vòstre article' | 320 | annotate: 'Anotar vòstre article' |
309 | export: 'Convertissètz vòstres articles en ePub o en PDF' | 321 | export: 'Convertissètz vòstres articles en ePub o en PDF' |
310 | search_filters: "Aprenètz a utilizar lo motor de recèrca e los filtres per retrobar l'article que vos interèssa" | 322 | search_filters: "Aprenètz a utilizar lo motor de recèrca e los filtres per retrobar l'article que vos interèssa" |
311 | fetching_errors: "Qué far se mon article es pas recuperat coma cal ?" | 323 | fetching_errors: "Qué far se mon article es pas recuperat coma cal ?" |
@@ -390,7 +402,7 @@ developer: | |||
390 | warn_message_2: "Se suprimissètz un client, totas las aplicacions que l'emplegan foncionaràn pas mai amb vòstre compte wallabag." | 402 | warn_message_2: "Se suprimissètz un client, totas las aplicacions que l'emplegan foncionaràn pas mai amb vòstre compte wallabag." |
391 | action: 'Suprimir aqueste client' | 403 | action: 'Suprimir aqueste client' |
392 | client: | 404 | client: |
393 | page_title: 'Desvlopador > Novèl client' | 405 | page_title: 'Desvolopaire > Novèl client' |
394 | page_description: "Anatz crear un novèl client. Mercés de cumplir l'url de redireccion cap a vòstra aplicacion." | 406 | page_description: "Anatz crear un novèl client. Mercés de cumplir l'url de redireccion cap a vòstra aplicacion." |
395 | form: | 407 | form: |
396 | name_label: "Nom del client" | 408 | name_label: "Nom del client" |
@@ -398,7 +410,7 @@ developer: | |||
398 | save_label: 'Crear un novèl client' | 410 | save_label: 'Crear un novèl client' |
399 | action_back: 'Retorn' | 411 | action_back: 'Retorn' |
400 | client_parameter: | 412 | client_parameter: |
401 | page_title: 'Desvolopador > Los paramètres de vòstre client' | 413 | page_title: 'Desvolopaire > Los paramètres de vòstre client' |
402 | page_description: 'Vaquí los paramètres de vòstre client' | 414 | page_description: 'Vaquí los paramètres de vòstre client' |
403 | field_name: 'Nom del client' | 415 | field_name: 'Nom del client' |
404 | field_id: 'ID Client' | 416 | field_id: 'ID Client' |
@@ -406,7 +418,7 @@ developer: | |||
406 | back: 'Retour' | 418 | back: 'Retour' |
407 | read_howto: 'Legir "cossí crear ma primièra aplicacion"' | 419 | read_howto: 'Legir "cossí crear ma primièra aplicacion"' |
408 | howto: | 420 | howto: |
409 | page_title: 'Desvolopador > Cossí crear ma primièra aplicacion' | 421 | page_title: 'Desvolopaire > Cossí crear ma primièra aplicacion' |
410 | description: | 422 | description: |
411 | paragraph_1: "Las comandas seguentas utilizan la <a href=\"https://github.com/jkbrzt/httpie\">bibliotèca HTTPie</a>. Asseguratz-vos que siasqueòu installadas abans de l'utilizar." | 423 | paragraph_1: "Las comandas seguentas utilizan la <a href=\"https://github.com/jkbrzt/httpie\">bibliotèca HTTPie</a>. Asseguratz-vos que siasqueòu installadas abans de l'utilizar." |
412 | paragraph_2: "Vos cal un geton per escambiar entre vòstra aplicacion e l'API de wallabar." | 424 | paragraph_2: "Vos cal un geton per escambiar entre vòstra aplicacion e l'API de wallabar." |
@@ -419,31 +431,31 @@ developer: | |||
419 | back: 'Retorn' | 431 | back: 'Retorn' |
420 | 432 | ||
421 | user: | 433 | user: |
422 | # page_title: Users management | 434 | page_title: 'Gestion dels utilizaires' |
423 | # new_user: Create a new user | 435 | new_user: 'Crear un novèl utilizaire' |
424 | # edit_user: Edit an existing user | 436 | edit_user: 'Modificar un utilizaire existent' |
425 | # description: "Here you can manage all users (create, edit and delete)" | 437 | description: "Aquí podètz gerir totes los utilizaires (crear, modificar e suprimir)" |
426 | # list: | 438 | list: |
427 | # actions: Actions | 439 | actions: 'Accions' |
428 | # edit_action: Edit | 440 | edit_action: 'Modificar' |
429 | # yes: Yes | 441 | yes: 'Òc' |
430 | # no: No | 442 | no: 'Non' |
431 | # create_new_one: Create a new user | 443 | create_new_one: 'Crear un novèl utilizaire' |
432 | form: | 444 | form: |
433 | username_label: "Nom d'utilizaire" | 445 | username_label: "Nom d'utilizaire" |
434 | # name_label: 'Name' | 446 | name_label: 'Nom' |
435 | password_label: 'Senhal' | 447 | password_label: 'Senhal' |
436 | repeat_new_password_label: 'Confirmatz vòstre novèl senhal' | 448 | repeat_new_password_label: 'Confirmatz vòstre novèl senhal' |
437 | plain_password_label: 'Senhal en clar' | 449 | plain_password_label: 'Senhal en clar' |
438 | email_label: 'Adreça de corrièl' | 450 | email_label: 'Adreça de corrièl' |
439 | # enabled_label: 'Enabled' | 451 | enabled_label: 'Actiu' |
440 | # locked_label: 'Locked' | 452 | locked_label: 'Varrolhat' |
441 | # last_login_label: 'Last login' | 453 | last_login_label: 'Darrièra connexion' |
442 | # twofactor_label: Two factor authentication | 454 | twofactor_label: 'Autentificacion doble-factor' |
443 | # save: Save | 455 | save: 'Enregistrar' |
444 | # delete: Delete | 456 | delete: 'Suprimir' |
445 | # delete_confirm: Are you sure? | 457 | delete_confirm: 'Sètz segur ?' |
446 | # back_to_list: Back to list | 458 | back_to_list: 'Tornar a la lista' |
447 | 459 | ||
448 | error: | 460 | error: |
449 | # page_title: An error occurred | 461 | # page_title: An error occurred |
@@ -458,8 +470,11 @@ flashes: | |||
458 | rss_updated: 'La configuracion dels fluxes RSS es ben estada mesa a jorn' | 470 | rss_updated: 'La configuracion dels fluxes RSS es ben estada mesa a jorn' |
459 | tagging_rules_updated: 'Règlas misa a jorn' | 471 | tagging_rules_updated: 'Règlas misa a jorn' |
460 | tagging_rules_deleted: 'Règla suprimida' | 472 | tagging_rules_deleted: 'Règla suprimida' |
461 | user_added: 'Utilizaire "%username%" apondut' | 473 | user_added: 'Utilizaire "%username%" ajustat' |
462 | rss_token_updated: 'Geton RSS mes a jorn' | 474 | rss_token_updated: 'Geton RSS mes a jorn' |
475 | # annotations_reset: Annotations reset | ||
476 | # tags_reset: Tags reset | ||
477 | # entries_reset: Entries reset | ||
463 | entry: | 478 | entry: |
464 | notice: | 479 | notice: |
465 | entry_already_saved: 'Article ja salvargardat lo %date%' | 480 | entry_already_saved: 'Article ja salvargardat lo %date%' |
@@ -470,12 +485,12 @@ flashes: | |||
470 | entry_reloaded_failed: "L'article es estat cargat de nòu mai la recuperacion del contengut a fracassat" | 485 | entry_reloaded_failed: "L'article es estat cargat de nòu mai la recuperacion del contengut a fracassat" |
471 | entry_archived: 'Article marcat coma legit' | 486 | entry_archived: 'Article marcat coma legit' |
472 | entry_unarchived: 'Article marcat coma pas legit' | 487 | entry_unarchived: 'Article marcat coma pas legit' |
473 | entry_starred: 'Article apondut dins los favorits' | 488 | entry_starred: 'Article ajustat dins los favorits' |
474 | entry_unstarred: 'Article quitat dels favorits' | 489 | entry_unstarred: 'Article quitat dels favorits' |
475 | entry_deleted: 'Article suprimit' | 490 | entry_deleted: 'Article suprimit' |
476 | tag: | 491 | tag: |
477 | notice: | 492 | notice: |
478 | tag_added: 'Etiqueta aponduda' | 493 | tag_added: 'Etiqueta ajustada' |
479 | import: | 494 | import: |
480 | notice: | 495 | notice: |
481 | failed: "L'importacion a fracassat, mercés de tornar ensajar" | 496 | failed: "L'importacion a fracassat, mercés de tornar ensajar" |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml index 798b39c2..8eef998b 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.pl.yml | |||
@@ -88,6 +88,18 @@ config: | |||
88 | name_label: 'Nazwa' | 88 | name_label: 'Nazwa' |
89 | email_label: 'Adres email' | 89 | email_label: 'Adres email' |
90 | twoFactorAuthentication_label: 'Autoryzacja dwuetapowa' | 90 | twoFactorAuthentication_label: 'Autoryzacja dwuetapowa' |
91 | delete: | ||
92 | title: Usuń moje konto (niebezpieczna strefa !) | ||
93 | description: Jeżeli usuniesz swoje konto, wszystkie twoje artykuły, tagi, adnotacje, oraz konto zostaną trwale usunięte (operacja jest NIEODWRACALNA). Następnie zostaniesz wylogowany. | ||
94 | confirm: Jesteś pewien? (tej operacji NIE MOŻNA cofnąć) | ||
95 | button: Usuń moje konto | ||
96 | reset: | ||
97 | # title: Reset area (a.k.a danger zone) | ||
98 | # description: By hiting buttons below you'll have ability to remove some informations from your account. Be aware that these actions are IRREVERSIBLE. | ||
99 | # annotations: Remove ALL annotations | ||
100 | # tags: Remove ALL tags | ||
101 | # entries: Remove ALL entries | ||
102 | # confirm: Are you really really sure? (THIS CAN'T BE UNDONE) | ||
91 | form_password: | 103 | form_password: |
92 | old_password_label: 'Stare hasło' | 104 | old_password_label: 'Stare hasło' |
93 | new_password_label: 'Nowe hasło' | 105 | new_password_label: 'Nowe hasło' |
@@ -460,6 +472,9 @@ flashes: | |||
460 | tagging_rules_deleted: 'Reguła tagowania usunięta' | 472 | tagging_rules_deleted: 'Reguła tagowania usunięta' |
461 | user_added: 'Użytkownik "%username%" dodany' | 473 | user_added: 'Użytkownik "%username%" dodany' |
462 | rss_token_updated: 'Token kanału RSS zaktualizowany' | 474 | rss_token_updated: 'Token kanału RSS zaktualizowany' |
475 | # annotations_reset: Annotations reset | ||
476 | # tags_reset: Tags reset | ||
477 | # entries_reset: Entries reset | ||
463 | entry: | 478 | entry: |
464 | notice: | 479 | notice: |
465 | entry_already_saved: 'Wpis już został dodany %date%' | 480 | entry_already_saved: 'Wpis już został dodany %date%' |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml index 21f27e08..6e4813e5 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.ro.yml | |||
@@ -88,6 +88,18 @@ config: | |||
88 | name_label: 'Nume' | 88 | name_label: 'Nume' |
89 | email_label: 'E-mail' | 89 | email_label: 'E-mail' |
90 | # twoFactorAuthentication_label: 'Two factor authentication' | 90 | # twoFactorAuthentication_label: 'Two factor authentication' |
91 | delete: | ||
92 | # title: Delete my account (a.k.a danger zone) | ||
93 | # description: If you remove your account, ALL your articles, ALL your tags, ALL your annotations and your account will be PERMANENTLY removed (it can't be UNDONE). You'll then be logged out. | ||
94 | # confirm: Are you really sure? (THIS CAN'T BE UNDONE) | ||
95 | # button: Delete my account | ||
96 | reset: | ||
97 | # title: Reset area (a.k.a danger zone) | ||
98 | # description: By hiting buttons below you'll have ability to remove some informations from your account. Be aware that these actions are IRREVERSIBLE. | ||
99 | # annotations: Remove ALL annotations | ||
100 | # tags: Remove ALL tags | ||
101 | # entries: Remove ALL entries | ||
102 | # confirm: Are you really really sure? (THIS CAN'T BE UNDONE) | ||
91 | form_password: | 103 | form_password: |
92 | old_password_label: 'Parola veche' | 104 | old_password_label: 'Parola veche' |
93 | new_password_label: 'Parola nouă' | 105 | new_password_label: 'Parola nouă' |
@@ -460,6 +472,9 @@ flashes: | |||
460 | # tagging_rules_deleted: 'Tagging rule deleted' | 472 | # tagging_rules_deleted: 'Tagging rule deleted' |
461 | # user_added: 'User "%username%" added' | 473 | # user_added: 'User "%username%" added' |
462 | # rss_token_updated: 'RSS token updated' | 474 | # rss_token_updated: 'RSS token updated' |
475 | # annotations_reset: Annotations reset | ||
476 | # tags_reset: Tags reset | ||
477 | # entries_reset: Entries reset | ||
463 | entry: | 478 | entry: |
464 | notice: | 479 | notice: |
465 | # entry_already_saved: 'Entry already saved on %date%' | 480 | # entry_already_saved: 'Entry already saved on %date%' |
diff --git a/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml b/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml index f137ec99..76903102 100644 --- a/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml +++ b/src/Wallabag/CoreBundle/Resources/translations/messages.tr.yml | |||
@@ -88,6 +88,18 @@ config: | |||
88 | name_label: 'İsim' | 88 | name_label: 'İsim' |
89 | email_label: 'E-posta' | 89 | email_label: 'E-posta' |
90 | twoFactorAuthentication_label: 'İki adımlı doğrulama' | 90 | twoFactorAuthentication_label: 'İki adımlı doğrulama' |
91 | delete: | ||
92 | # title: Delete my account (a.k.a danger zone) | ||
93 | # description: If you remove your account, ALL your articles, ALL your tags, ALL your annotations and your account will be PERMANENTLY removed (it can't be UNDONE). You'll then be logged out. | ||
94 | # confirm: Are you really sure? (THIS CAN'T BE UNDONE) | ||
95 | # button: Delete my account | ||
96 | reset: | ||
97 | # title: Reset area (a.k.a danger zone) | ||
98 | # description: By hiting buttons below you'll have ability to remove some informations from your account. Be aware that these actions are IRREVERSIBLE. | ||
99 | # annotations: Remove ALL annotations | ||
100 | # tags: Remove ALL tags | ||
101 | # entries: Remove ALL entries | ||
102 | # confirm: Are you really really sure? (THIS CAN'T BE UNDONE) | ||
91 | form_password: | 103 | form_password: |
92 | old_password_label: 'Eski şifre' | 104 | old_password_label: 'Eski şifre' |
93 | new_password_label: 'Yeni şifre' | 105 | new_password_label: 'Yeni şifre' |
@@ -459,6 +471,9 @@ flashes: | |||
459 | tagging_rules_deleted: 'Tagging rule deleted' | 471 | tagging_rules_deleted: 'Tagging rule deleted' |
460 | user_added: 'User "%username%" added' | 472 | user_added: 'User "%username%" added' |
461 | rss_token_updated: 'RSS token updated' | 473 | rss_token_updated: 'RSS token updated' |
474 | # annotations_reset: Annotations reset | ||
475 | # tags_reset: Tags reset | ||
476 | # entries_reset: Entries reset | ||
462 | entry: | 477 | entry: |
463 | notice: | 478 | notice: |
464 | entry_already_saved: 'Entry already saved on %date%' | 479 | entry_already_saved: 'Entry already saved on %date%' |
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Config/index.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Config/index.html.twig index ff7ef73a..455d0295 100644 --- a/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Config/index.html.twig +++ b/src/Wallabag/CoreBundle/Resources/views/themes/baggy/Config/index.html.twig | |||
@@ -146,10 +146,41 @@ | |||
146 | </fieldset> | 146 | </fieldset> |
147 | {% endif %} | 147 | {% endif %} |
148 | 148 | ||
149 | <h2>{{ 'config.reset.title'|trans }}</h2> | ||
150 | <fieldset class="w500p inline"> | ||
151 | <p>{{ 'config.reset.description'|trans }}</p> | ||
152 | <ul> | ||
153 | <li> | ||
154 | <a href="{{ path('config_reset', { type: 'annotations'}) }}" onclick="return confirm('{{ 'config.reset.confirm'|trans|escape('js') }}')" class="waves-effect waves-light btn red"> | ||
155 | {{ 'config.reset.annotations'|trans }} | ||
156 | </a> | ||
157 | </li> | ||
158 | <li> | ||
159 | <a href="{{ path('config_reset', { type: 'tags'}) }}" onclick="return confirm('{{ 'config.reset.confirm'|trans|escape('js') }}')" class="waves-effect waves-light btn red"> | ||
160 | {{ 'config.reset.tags'|trans }} | ||
161 | </a> | ||
162 | </li> | ||
163 | <li> | ||
164 | <a href="{{ path('config_reset', { type: 'entries'}) }}" onclick="return confirm('{{ 'config.reset.confirm'|trans|escape('js') }}')" class="waves-effect waves-light btn red"> | ||
165 | {{ 'config.reset.entries'|trans }} | ||
166 | </a> | ||
167 | </li> | ||
168 | </ul> | ||
169 | </fieldset> | ||
170 | |||
149 | {{ form_widget(form.user._token) }} | 171 | {{ form_widget(form.user._token) }} |
150 | {{ form_widget(form.user.save) }} | 172 | {{ form_widget(form.user.save) }} |
151 | </form> | 173 | </form> |
152 | 174 | ||
175 | {% if enabled_users > 1 %} | ||
176 | <h2>{{ 'config.form_user.delete.title'|trans }}</h2> | ||
177 | |||
178 | <p>{{ 'config.form_user.delete.description'|trans }}</p> | ||
179 | <a href="{{ path('delete_account') }}" onclick="return confirm('{{ 'config.form_user.delete.confirm'|trans|escape('js') }}')" class="waves-effect waves-light btn red delete-account"> | ||
180 | {{ 'config.form_user.delete.button'|trans }} | ||
181 | </a> | ||
182 | {% endif %} | ||
183 | |||
153 | <h2>{{ 'config.tab_menu.password'|trans }}</h2> | 184 | <h2>{{ 'config.tab_menu.password'|trans }}</h2> |
154 | 185 | ||
155 | {{ form_start(form.pwd) }} | 186 | {{ form_start(form.pwd) }} |
diff --git a/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig b/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig index 19faddc0..b53ae2fe 100644 --- a/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig +++ b/src/Wallabag/CoreBundle/Resources/views/themes/material/Config/index.html.twig | |||
@@ -167,6 +167,34 @@ | |||
167 | {{ form_widget(form.user.save, {'attr': {'class': 'btn waves-effect waves-light'}}) }} | 167 | {{ form_widget(form.user.save, {'attr': {'class': 'btn waves-effect waves-light'}}) }} |
168 | {{ form_widget(form.user._token) }} | 168 | {{ form_widget(form.user._token) }} |
169 | </form> | 169 | </form> |
170 | |||
171 | <br /><hr /><br /> | ||
172 | |||
173 | <div class="row"> | ||
174 | <h5>{{ 'config.reset.title'|trans }}</h5> | ||
175 | <p>{{ 'config.reset.description'|trans }}</p> | ||
176 | <a href="{{ path('config_reset', { type: 'annotations'}) }}" onclick="return confirm('{{ 'config.reset.confirm'|trans|escape('js') }}')" class="waves-effect waves-light btn red"> | ||
177 | {{ 'config.reset.annotations'|trans }} | ||
178 | </a> | ||
179 | <a href="{{ path('config_reset', { type: 'tags'}) }}" onclick="return confirm('{{ 'config.reset.confirm'|trans|escape('js') }}')" class="waves-effect waves-light btn red"> | ||
180 | {{ 'config.reset.tags'|trans }} | ||
181 | </a> | ||
182 | <a href="{{ path('config_reset', { type: 'entries'}) }}" onclick="return confirm('{{ 'config.reset.confirm'|trans|escape('js') }}')" class="waves-effect waves-light btn red"> | ||
183 | {{ 'config.reset.entries'|trans }} | ||
184 | </a> | ||
185 | </div> | ||
186 | |||
187 | {% if enabled_users > 1 %} | ||
188 | <br /><hr /><br /> | ||
189 | |||
190 | <div class="row"> | ||
191 | <h5>{{ 'config.form_user.delete.title'|trans }}</h5> | ||
192 | <p>{{ 'config.form_user.delete.description'|trans }}</p> | ||
193 | <a href="{{ path('delete_account') }}" onclick="return confirm('{{ 'config.form_user.delete.confirm'|trans|escape('js') }}')" class="waves-effect waves-light btn red delete-account"> | ||
194 | {{ 'config.form_user.delete.button'|trans }} | ||
195 | </a> | ||
196 | </div> | ||
197 | {% endif %} | ||
170 | </div> | 198 | </div> |
171 | 199 | ||
172 | <div id="set4" class="col s12"> | 200 | <div id="set4" class="col s12"> |
diff --git a/src/Wallabag/CoreBundle/Subscriber/SQLiteCascadeDeleteSubscriber.php b/src/Wallabag/CoreBundle/Subscriber/SQLiteCascadeDeleteSubscriber.php new file mode 100644 index 00000000..f7210bd3 --- /dev/null +++ b/src/Wallabag/CoreBundle/Subscriber/SQLiteCascadeDeleteSubscriber.php | |||
@@ -0,0 +1,70 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Wallabag\CoreBundle\Subscriber; | ||
4 | |||
5 | use Doctrine\Common\EventSubscriber; | ||
6 | use Doctrine\ORM\Event\LifecycleEventArgs; | ||
7 | use Wallabag\CoreBundle\Entity\Entry; | ||
8 | use Doctrine\Bundle\DoctrineBundle\Registry; | ||
9 | |||
10 | /** | ||
11 | * SQLite doesn't care about cascading remove, so we need to manually remove associated stuf for an Entry. | ||
12 | * Foreign Key Support can be enabled by running `PRAGMA foreign_keys = ON;` at runtime (AT RUNTIME !). | ||
13 | * But it needs a compilation flag that not all SQLite instance has ... | ||
14 | * | ||
15 | * @see https://www.sqlite.org/foreignkeys.html#fk_enable | ||
16 | */ | ||
17 | class SQLiteCascadeDeleteSubscriber implements EventSubscriber | ||
18 | { | ||
19 | private $doctrine; | ||
20 | |||
21 | /** | ||
22 | * @param \Doctrine\Bundle\DoctrineBundle\Registry $doctrine | ||
23 | */ | ||
24 | public function __construct(Registry $doctrine) | ||
25 | { | ||
26 | $this->doctrine = $doctrine; | ||
27 | } | ||
28 | |||
29 | /** | ||
30 | * @return array | ||
31 | */ | ||
32 | public function getSubscribedEvents() | ||
33 | { | ||
34 | return [ | ||
35 | 'preRemove', | ||
36 | ]; | ||
37 | } | ||
38 | |||
39 | /** | ||
40 | * We removed everything related to the upcoming removed entry because SQLite can't handle it on it own. | ||
41 | * We do it in the preRemove, because we can't retrieve tags in the postRemove (because the entry id is gone). | ||
42 | * | ||
43 | * @param LifecycleEventArgs $args | ||
44 | */ | ||
45 | public function preRemove(LifecycleEventArgs $args) | ||
46 | { | ||
47 | $entity = $args->getEntity(); | ||
48 | |||
49 | if (!$this->doctrine->getConnection()->getDriver() instanceof \Doctrine\DBAL\Driver\PDOSqlite\Driver || | ||
50 | !$entity instanceof Entry) { | ||
51 | return; | ||
52 | } | ||
53 | |||
54 | $em = $this->doctrine->getManager(); | ||
55 | |||
56 | if (null !== $entity->getTags()) { | ||
57 | foreach ($entity->getTags() as $tag) { | ||
58 | $entity->removeTag($tag); | ||
59 | } | ||
60 | } | ||
61 | |||
62 | if (null !== $entity->getAnnotations()) { | ||
63 | foreach ($entity->getAnnotations() as $annotation) { | ||
64 | $em->remove($annotation); | ||
65 | } | ||
66 | } | ||
67 | |||
68 | $em->flush(); | ||
69 | } | ||
70 | } | ||
diff --git a/src/Wallabag/UserBundle/Repository/UserRepository.php b/src/Wallabag/UserBundle/Repository/UserRepository.php index 009c4881..445edb3c 100644 --- a/src/Wallabag/UserBundle/Repository/UserRepository.php +++ b/src/Wallabag/UserBundle/Repository/UserRepository.php | |||
@@ -38,4 +38,18 @@ class UserRepository extends EntityRepository | |||
38 | ->getQuery() | 38 | ->getQuery() |
39 | ->getSingleResult(); | 39 | ->getSingleResult(); |
40 | } | 40 | } |
41 | |||
42 | /** | ||
43 | * Count how many users are enabled. | ||
44 | * | ||
45 | * @return int | ||
46 | */ | ||
47 | public function getSumEnabledUsers() | ||
48 | { | ||
49 | return $this->createQueryBuilder('u') | ||
50 | ->select('count(u)') | ||
51 | ->andWhere('u.expired = false') | ||
52 | ->getQuery() | ||
53 | ->getSingleScalarResult(); | ||
54 | } | ||
41 | } | 55 | } |
diff --git a/src/Wallabag/UserBundle/Resources/config/services.yml b/src/Wallabag/UserBundle/Resources/config/services.yml index eb9c8e67..8062e53f 100644 --- a/src/Wallabag/UserBundle/Resources/config/services.yml +++ b/src/Wallabag/UserBundle/Resources/config/services.yml | |||
@@ -21,7 +21,7 @@ services: | |||
21 | arguments: | 21 | arguments: |
22 | - WallabagUserBundle:User | 22 | - WallabagUserBundle:User |
23 | 23 | ||
24 | wallabag_user.create_config: | 24 | wallabag_user.listener.create_config: |
25 | class: Wallabag\UserBundle\EventListener\CreateConfigListener | 25 | class: Wallabag\UserBundle\EventListener\CreateConfigListener |
26 | arguments: | 26 | arguments: |
27 | - "@doctrine.orm.entity_manager" | 27 | - "@doctrine.orm.entity_manager" |
diff --git a/tests/Wallabag/AnnotationBundle/Controller/AnnotationControllerTest.php b/tests/Wallabag/AnnotationBundle/Controller/AnnotationControllerTest.php index 70849f74..cee0b847 100644 --- a/tests/Wallabag/AnnotationBundle/Controller/AnnotationControllerTest.php +++ b/tests/Wallabag/AnnotationBundle/Controller/AnnotationControllerTest.php | |||
@@ -3,35 +3,80 @@ | |||
3 | namespace Tests\AnnotationBundle\Controller; | 3 | namespace Tests\AnnotationBundle\Controller; |
4 | 4 | ||
5 | use Tests\Wallabag\AnnotationBundle\WallabagAnnotationTestCase; | 5 | use Tests\Wallabag\AnnotationBundle\WallabagAnnotationTestCase; |
6 | use Wallabag\AnnotationBundle\Entity\Annotation; | ||
7 | use Wallabag\CoreBundle\Entity\Entry; | ||
6 | 8 | ||
7 | class AnnotationControllerTest extends WallabagAnnotationTestCase | 9 | class AnnotationControllerTest extends WallabagAnnotationTestCase |
8 | { | 10 | { |
9 | public function testGetAnnotations() | 11 | /** |
12 | * This data provider allow to tests annotation from the : | ||
13 | * - API POV (when user use the api to manage annotations) | ||
14 | * - and User POV (when user use the web interface - using javascript - to manage annotations) | ||
15 | */ | ||
16 | public function dataForEachAnnotations() | ||
10 | { | 17 | { |
11 | $annotation = $this->client->getContainer() | 18 | return [ |
12 | ->get('doctrine.orm.entity_manager') | 19 | ['/api/annotations'], |
13 | ->getRepository('WallabagAnnotationBundle:Annotation') | 20 | ['annotations'], |
14 | ->findOneByUsername('admin'); | 21 | ]; |
22 | } | ||
23 | |||
24 | /** | ||
25 | * Test fetching annotations for an entry. | ||
26 | * | ||
27 | * @dataProvider dataForEachAnnotations | ||
28 | */ | ||
29 | public function testGetAnnotations($prefixUrl) | ||
30 | { | ||
31 | $em = $this->client->getContainer()->get('doctrine.orm.entity_manager'); | ||
32 | |||
33 | $user = $em | ||
34 | ->getRepository('WallabagUserBundle:User') | ||
35 | ->findOneByUserName('admin'); | ||
36 | $entry = $em | ||
37 | ->getRepository('WallabagCoreBundle:Entry') | ||
38 | ->findOneByUsernameAndNotArchived('admin'); | ||
15 | 39 | ||
16 | if (!$annotation) { | 40 | $annotation = new Annotation($user); |
17 | $this->markTestSkipped('No content found in db.'); | 41 | $annotation->setEntry($entry); |
42 | $annotation->setText('This is my annotation /o/'); | ||
43 | $annotation->setQuote('content'); | ||
44 | |||
45 | $em->persist($annotation); | ||
46 | $em->flush(); | ||
47 | |||
48 | if ('annotations' === $prefixUrl) { | ||
49 | $this->logInAs('admin'); | ||
18 | } | 50 | } |
19 | 51 | ||
20 | $this->logInAs('admin'); | 52 | $this->client->request('GET', $prefixUrl.'/'.$entry->getId().'.json'); |
21 | $crawler = $this->client->request('GET', 'annotations/'.$annotation->getEntry()->getId().'.json'); | ||
22 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | 53 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); |
23 | 54 | ||
24 | $content = json_decode($this->client->getResponse()->getContent(), true); | 55 | $content = json_decode($this->client->getResponse()->getContent(), true); |
25 | $this->assertEquals(1, $content['total']); | 56 | $this->assertGreaterThanOrEqual(1, $content['total']); |
26 | $this->assertEquals($annotation->getText(), $content['rows'][0]['text']); | 57 | $this->assertEquals($annotation->getText(), $content['rows'][0]['text']); |
58 | |||
59 | // we need to re-fetch the annotation becase after the flush, it has been "detached" from the entity manager | ||
60 | $annotation = $em->getRepository('WallabagAnnotationBundle:Annotation')->findAnnotationById($annotation->getId()); | ||
61 | $em->remove($annotation); | ||
62 | $em->flush(); | ||
27 | } | 63 | } |
28 | 64 | ||
29 | public function testSetAnnotation() | 65 | /** |
66 | * Test creating an annotation for an entry. | ||
67 | * | ||
68 | * @dataProvider dataForEachAnnotations | ||
69 | */ | ||
70 | public function testSetAnnotation($prefixUrl) | ||
30 | { | 71 | { |
31 | $this->logInAs('admin'); | 72 | $em = $this->client->getContainer()->get('doctrine.orm.entity_manager'); |
32 | 73 | ||
33 | $entry = $this->client->getContainer() | 74 | if ('annotations' === $prefixUrl) { |
34 | ->get('doctrine.orm.entity_manager') | 75 | $this->logInAs('admin'); |
76 | } | ||
77 | |||
78 | /** @var Entry $entry */ | ||
79 | $entry = $em | ||
35 | ->getRepository('WallabagCoreBundle:Entry') | 80 | ->getRepository('WallabagCoreBundle:Entry') |
36 | ->findOneByUsernameAndNotArchived('admin'); | 81 | ->findOneByUsernameAndNotArchived('admin'); |
37 | 82 | ||
@@ -41,7 +86,7 @@ class AnnotationControllerTest extends WallabagAnnotationTestCase | |||
41 | 'quote' => 'my quote', | 86 | 'quote' => 'my quote', |
42 | 'ranges' => ['start' => '', 'startOffset' => 24, 'end' => '', 'endOffset' => 31], | 87 | 'ranges' => ['start' => '', 'startOffset' => 24, 'end' => '', 'endOffset' => 31], |
43 | ]); | 88 | ]); |
44 | $crawler = $this->client->request('POST', 'annotations/'.$entry->getId().'.json', [], [], $headers, $content); | 89 | $this->client->request('POST', $prefixUrl.'/'.$entry->getId().'.json', [], [], $headers, $content); |
45 | 90 | ||
46 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | 91 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); |
47 | 92 | ||
@@ -52,6 +97,7 @@ class AnnotationControllerTest extends WallabagAnnotationTestCase | |||
52 | $this->assertEquals('my annotation', $content['text']); | 97 | $this->assertEquals('my annotation', $content['text']); |
53 | $this->assertEquals('my quote', $content['quote']); | 98 | $this->assertEquals('my quote', $content['quote']); |
54 | 99 | ||
100 | /** @var Annotation $annotation */ | ||
55 | $annotation = $this->client->getContainer() | 101 | $annotation = $this->client->getContainer() |
56 | ->get('doctrine.orm.entity_manager') | 102 | ->get('doctrine.orm.entity_manager') |
57 | ->getRepository('WallabagAnnotationBundle:Annotation') | 103 | ->getRepository('WallabagAnnotationBundle:Annotation') |
@@ -60,20 +106,35 @@ class AnnotationControllerTest extends WallabagAnnotationTestCase | |||
60 | $this->assertEquals('my annotation', $annotation->getText()); | 106 | $this->assertEquals('my annotation', $annotation->getText()); |
61 | } | 107 | } |
62 | 108 | ||
63 | public function testEditAnnotation() | 109 | /** |
110 | * Test editing an existing annotation. | ||
111 | * | ||
112 | * @dataProvider dataForEachAnnotations | ||
113 | */ | ||
114 | public function testEditAnnotation($prefixUrl) | ||
64 | { | 115 | { |
65 | $annotation = $this->client->getContainer() | 116 | $em = $this->client->getContainer()->get('doctrine.orm.entity_manager'); |
66 | ->get('doctrine.orm.entity_manager') | ||
67 | ->getRepository('WallabagAnnotationBundle:Annotation') | ||
68 | ->findOneByUsername('admin'); | ||
69 | 117 | ||
70 | $this->logInAs('admin'); | 118 | $user = $em |
119 | ->getRepository('WallabagUserBundle:User') | ||
120 | ->findOneByUserName('admin'); | ||
121 | $entry = $em | ||
122 | ->getRepository('WallabagCoreBundle:Entry') | ||
123 | ->findOneByUsernameAndNotArchived('admin'); | ||
124 | |||
125 | $annotation = new Annotation($user); | ||
126 | $annotation->setEntry($entry); | ||
127 | $annotation->setText('This is my annotation /o/'); | ||
128 | $annotation->setQuote('my quote'); | ||
129 | |||
130 | $em->persist($annotation); | ||
131 | $em->flush(); | ||
71 | 132 | ||
72 | $headers = ['CONTENT_TYPE' => 'application/json']; | 133 | $headers = ['CONTENT_TYPE' => 'application/json']; |
73 | $content = json_encode([ | 134 | $content = json_encode([ |
74 | 'text' => 'a modified annotation', | 135 | 'text' => 'a modified annotation', |
75 | ]); | 136 | ]); |
76 | $crawler = $this->client->request('PUT', 'annotations/'.$annotation->getId().'.json', [], [], $headers, $content); | 137 | $this->client->request('PUT', $prefixUrl.'/'.$annotation->getId().'.json', [], [], $headers, $content); |
77 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | 138 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); |
78 | 139 | ||
79 | $content = json_decode($this->client->getResponse()->getContent(), true); | 140 | $content = json_decode($this->client->getResponse()->getContent(), true); |
@@ -83,35 +144,56 @@ class AnnotationControllerTest extends WallabagAnnotationTestCase | |||
83 | $this->assertEquals('a modified annotation', $content['text']); | 144 | $this->assertEquals('a modified annotation', $content['text']); |
84 | $this->assertEquals('my quote', $content['quote']); | 145 | $this->assertEquals('my quote', $content['quote']); |
85 | 146 | ||
86 | $annotationUpdated = $this->client->getContainer() | 147 | /** @var Annotation $annotationUpdated */ |
87 | ->get('doctrine.orm.entity_manager') | 148 | $annotationUpdated = $em |
88 | ->getRepository('WallabagAnnotationBundle:Annotation') | 149 | ->getRepository('WallabagAnnotationBundle:Annotation') |
89 | ->findOneById($annotation->getId()); | 150 | ->findOneById($annotation->getId()); |
90 | $this->assertEquals('a modified annotation', $annotationUpdated->getText()); | 151 | $this->assertEquals('a modified annotation', $annotationUpdated->getText()); |
152 | |||
153 | $em->remove($annotationUpdated); | ||
154 | $em->flush(); | ||
91 | } | 155 | } |
92 | 156 | ||
93 | public function testDeleteAnnotation() | 157 | /** |
158 | * Test deleting an annotation. | ||
159 | * | ||
160 | * @dataProvider dataForEachAnnotations | ||
161 | */ | ||
162 | public function testDeleteAnnotation($prefixUrl) | ||
94 | { | 163 | { |
95 | $annotation = $this->client->getContainer() | 164 | $em = $this->client->getContainer()->get('doctrine.orm.entity_manager'); |
96 | ->get('doctrine.orm.entity_manager') | ||
97 | ->getRepository('WallabagAnnotationBundle:Annotation') | ||
98 | ->findOneByUsername('admin'); | ||
99 | 165 | ||
100 | $this->logInAs('admin'); | 166 | $user = $em |
167 | ->getRepository('WallabagUserBundle:User') | ||
168 | ->findOneByUserName('admin'); | ||
169 | $entry = $em | ||
170 | ->getRepository('WallabagCoreBundle:Entry') | ||
171 | ->findOneByUsernameAndNotArchived('admin'); | ||
172 | |||
173 | $annotation = new Annotation($user); | ||
174 | $annotation->setEntry($entry); | ||
175 | $annotation->setText('This is my annotation /o/'); | ||
176 | $annotation->setQuote('my quote'); | ||
177 | |||
178 | $em->persist($annotation); | ||
179 | $em->flush(); | ||
180 | |||
181 | if ('annotations' === $prefixUrl) { | ||
182 | $this->logInAs('admin'); | ||
183 | } | ||
101 | 184 | ||
102 | $headers = ['CONTENT_TYPE' => 'application/json']; | 185 | $headers = ['CONTENT_TYPE' => 'application/json']; |
103 | $content = json_encode([ | 186 | $content = json_encode([ |
104 | 'text' => 'a modified annotation', | 187 | 'text' => 'a modified annotation', |
105 | ]); | 188 | ]); |
106 | $crawler = $this->client->request('DELETE', 'annotations/'.$annotation->getId().'.json', [], [], $headers, $content); | 189 | $this->client->request('DELETE', $prefixUrl.'/'.$annotation->getId().'.json', [], [], $headers, $content); |
107 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | 190 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); |
108 | 191 | ||
109 | $content = json_decode($this->client->getResponse()->getContent(), true); | 192 | $content = json_decode($this->client->getResponse()->getContent(), true); |
110 | 193 | ||
111 | $this->assertEquals('a modified annotation', $content['text']); | 194 | $this->assertEquals('This is my annotation /o/', $content['text']); |
112 | 195 | ||
113 | $annotationDeleted = $this->client->getContainer() | 196 | $annotationDeleted = $em |
114 | ->get('doctrine.orm.entity_manager') | ||
115 | ->getRepository('WallabagAnnotationBundle:Annotation') | 197 | ->getRepository('WallabagAnnotationBundle:Annotation') |
116 | ->findOneById($annotation->getId()); | 198 | ->findOneById($annotation->getId()); |
117 | 199 | ||
diff --git a/tests/Wallabag/AnnotationBundle/WallabagAnnotationTestCase.php b/tests/Wallabag/AnnotationBundle/WallabagAnnotationTestCase.php index 82790a5c..ef3f1324 100644 --- a/tests/Wallabag/AnnotationBundle/WallabagAnnotationTestCase.php +++ b/tests/Wallabag/AnnotationBundle/WallabagAnnotationTestCase.php | |||
@@ -8,7 +8,7 @@ use Symfony\Component\BrowserKit\Cookie; | |||
8 | abstract class WallabagAnnotationTestCase extends WebTestCase | 8 | abstract class WallabagAnnotationTestCase extends WebTestCase |
9 | { | 9 | { |
10 | /** | 10 | /** |
11 | * @var Client | 11 | * @var \Symfony\Bundle\FrameworkBundle\Client |
12 | */ | 12 | */ |
13 | protected $client = null; | 13 | protected $client = null; |
14 | 14 | ||
@@ -35,7 +35,7 @@ abstract class WallabagAnnotationTestCase extends WebTestCase | |||
35 | } | 35 | } |
36 | 36 | ||
37 | /** | 37 | /** |
38 | * @return Client | 38 | * @return \Symfony\Bundle\FrameworkBundle\Client |
39 | */ | 39 | */ |
40 | protected function createAuthorizedClient() | 40 | protected function createAuthorizedClient() |
41 | { | 41 | { |
@@ -49,7 +49,7 @@ abstract class WallabagAnnotationTestCase extends WebTestCase | |||
49 | $firewallName = $container->getParameter('fos_user.firewall_name'); | 49 | $firewallName = $container->getParameter('fos_user.firewall_name'); |
50 | 50 | ||
51 | $this->user = $userManager->findUserBy(['username' => 'admin']); | 51 | $this->user = $userManager->findUserBy(['username' => 'admin']); |
52 | $loginManager->loginUser($firewallName, $this->user); | 52 | $loginManager->logInUser($firewallName, $this->user); |
53 | 53 | ||
54 | // save the login token into the session and put it in a cookie | 54 | // save the login token into the session and put it in a cookie |
55 | $container->get('session')->set('_security_'.$firewallName, serialize($container->get('security.token_storage')->getToken())); | 55 | $container->get('session')->set('_security_'.$firewallName, serialize($container->get('security.token_storage')->getToken())); |
diff --git a/tests/Wallabag/ApiBundle/Controller/WallabagRestControllerTest.php b/tests/Wallabag/ApiBundle/Controller/WallabagRestControllerTest.php index 5dcb3e00..6bca3c8b 100644 --- a/tests/Wallabag/ApiBundle/Controller/WallabagRestControllerTest.php +++ b/tests/Wallabag/ApiBundle/Controller/WallabagRestControllerTest.php | |||
@@ -32,12 +32,55 @@ class WallabagRestControllerTest extends WallabagApiTestCase | |||
32 | $this->assertEquals($entry->getUserEmail(), $content['user_email']); | 32 | $this->assertEquals($entry->getUserEmail(), $content['user_email']); |
33 | $this->assertEquals($entry->getUserId(), $content['user_id']); | 33 | $this->assertEquals($entry->getUserId(), $content['user_id']); |
34 | 34 | ||
35 | $this->assertTrue( | 35 | $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-Type')); |
36 | $this->client->getResponse()->headers->contains( | 36 | } |
37 | 'Content-Type', | 37 | |
38 | 'application/json' | 38 | public function testExportEntry() |
39 | ) | 39 | { |
40 | ); | 40 | $entry = $this->client->getContainer() |
41 | ->get('doctrine.orm.entity_manager') | ||
42 | ->getRepository('WallabagCoreBundle:Entry') | ||
43 | ->findOneBy(['user' => 1, 'isArchived' => false]); | ||
44 | |||
45 | if (!$entry) { | ||
46 | $this->markTestSkipped('No content found in db.'); | ||
47 | } | ||
48 | |||
49 | $this->client->request('GET', '/api/entries/'.$entry->getId().'/export.epub'); | ||
50 | $this->assertEquals(200, $this->client->getResponse()->getStatusCode()); | ||
51 | |||
52 | // epub format got the content type in the content | ||
53 | $this->assertContains('application/epub', $this->client->getResponse()->getContent()); | ||
54 | $this->assertEquals('application/epub+zip', $this->client->getResponse()->headers->get('Content-Type')); | ||
55 | |||
56 | // re-auth client for mobi | ||
57 | $client = $this->createAuthorizedClient(); | ||
58 | $client->request('GET', '/api/entries/'.$entry->getId().'/export.mobi'); | ||
59 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); | ||
60 | |||
61 | $this->assertEquals('application/x-mobipocket-ebook', $client->getResponse()->headers->get('Content-Type')); | ||
62 | |||
63 | // re-auth client for pdf | ||
64 | $client = $this->createAuthorizedClient(); | ||
65 | $client->request('GET', '/api/entries/'.$entry->getId().'/export.pdf'); | ||
66 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); | ||
67 | |||
68 | $this->assertContains('PDF-', $client->getResponse()->getContent()); | ||
69 | $this->assertEquals('application/pdf', $client->getResponse()->headers->get('Content-Type')); | ||
70 | |||
71 | // re-auth client for pdf | ||
72 | $client = $this->createAuthorizedClient(); | ||
73 | $client->request('GET', '/api/entries/'.$entry->getId().'/export.txt'); | ||
74 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); | ||
75 | |||
76 | $this->assertContains('text/plain', $client->getResponse()->headers->get('Content-Type')); | ||
77 | |||
78 | // re-auth client for pdf | ||
79 | $client = $this->createAuthorizedClient(); | ||
80 | $client->request('GET', '/api/entries/'.$entry->getId().'/export.csv'); | ||
81 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); | ||
82 | |||
83 | $this->assertContains('application/csv', $client->getResponse()->headers->get('Content-Type')); | ||
41 | } | 84 | } |
42 | 85 | ||
43 | public function testGetOneEntryWrongUser() | 86 | public function testGetOneEntryWrongUser() |
@@ -70,12 +113,7 @@ class WallabagRestControllerTest extends WallabagApiTestCase | |||
70 | $this->assertEquals(1, $content['page']); | 113 | $this->assertEquals(1, $content['page']); |
71 | $this->assertGreaterThanOrEqual(1, $content['pages']); | 114 | $this->assertGreaterThanOrEqual(1, $content['pages']); |
72 | 115 | ||
73 | $this->assertTrue( | 116 | $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-Type')); |
74 | $this->client->getResponse()->headers->contains( | ||
75 | 'Content-Type', | ||
76 | 'application/json' | ||
77 | ) | ||
78 | ); | ||
79 | } | 117 | } |
80 | 118 | ||
81 | public function testGetEntriesWithFullOptions() | 119 | public function testGetEntriesWithFullOptions() |
@@ -117,12 +155,7 @@ class WallabagRestControllerTest extends WallabagApiTestCase | |||
117 | $this->assertContains('since=1443274283', $content['_links'][$link]['href']); | 155 | $this->assertContains('since=1443274283', $content['_links'][$link]['href']); |
118 | } | 156 | } |
119 | 157 | ||
120 | $this->assertTrue( | 158 | $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-Type')); |
121 | $this->client->getResponse()->headers->contains( | ||
122 | 'Content-Type', | ||
123 | 'application/json' | ||
124 | ) | ||
125 | ); | ||
126 | } | 159 | } |
127 | 160 | ||
128 | public function testGetStarredEntries() | 161 | public function testGetStarredEntries() |
@@ -150,12 +183,7 @@ class WallabagRestControllerTest extends WallabagApiTestCase | |||
150 | $this->assertContains('sort=updated', $content['_links'][$link]['href']); | 183 | $this->assertContains('sort=updated', $content['_links'][$link]['href']); |
151 | } | 184 | } |
152 | 185 | ||
153 | $this->assertTrue( | 186 | $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-Type')); |
154 | $this->client->getResponse()->headers->contains( | ||
155 | 'Content-Type', | ||
156 | 'application/json' | ||
157 | ) | ||
158 | ); | ||
159 | } | 187 | } |
160 | 188 | ||
161 | public function testGetArchiveEntries() | 189 | public function testGetArchiveEntries() |
@@ -182,12 +210,7 @@ class WallabagRestControllerTest extends WallabagApiTestCase | |||
182 | $this->assertContains('archive=1', $content['_links'][$link]['href']); | 210 | $this->assertContains('archive=1', $content['_links'][$link]['href']); |
183 | } | 211 | } |
184 | 212 | ||
185 | $this->assertTrue( | 213 | $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-Type')); |
186 | $this->client->getResponse()->headers->contains( | ||
187 | 'Content-Type', | ||
188 | 'application/json' | ||
189 | ) | ||
190 | ); | ||
191 | } | 214 | } |
192 | 215 | ||
193 | public function testGetTaggedEntries() | 216 | public function testGetTaggedEntries() |
@@ -214,12 +237,7 @@ class WallabagRestControllerTest extends WallabagApiTestCase | |||
214 | $this->assertContains('tags='.urlencode('foo,bar'), $content['_links'][$link]['href']); | 237 | $this->assertContains('tags='.urlencode('foo,bar'), $content['_links'][$link]['href']); |
215 | } | 238 | } |
216 | 239 | ||
217 | $this->assertTrue( | 240 | $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-Type')); |
218 | $this->client->getResponse()->headers->contains( | ||
219 | 'Content-Type', | ||
220 | 'application/json' | ||
221 | ) | ||
222 | ); | ||
223 | } | 241 | } |
224 | 242 | ||
225 | public function testGetDatedEntries() | 243 | public function testGetDatedEntries() |
@@ -246,12 +264,7 @@ class WallabagRestControllerTest extends WallabagApiTestCase | |||
246 | $this->assertContains('since=1443274283', $content['_links'][$link]['href']); | 264 | $this->assertContains('since=1443274283', $content['_links'][$link]['href']); |
247 | } | 265 | } |
248 | 266 | ||
249 | $this->assertTrue( | 267 | $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-Type')); |
250 | $this->client->getResponse()->headers->contains( | ||
251 | 'Content-Type', | ||
252 | 'application/json' | ||
253 | ) | ||
254 | ); | ||
255 | } | 268 | } |
256 | 269 | ||
257 | public function testGetDatedSupEntries() | 270 | public function testGetDatedSupEntries() |
@@ -279,12 +292,7 @@ class WallabagRestControllerTest extends WallabagApiTestCase | |||
279 | $this->assertContains('since='.($future->getTimestamp() + 1000), $content['_links'][$link]['href']); | 292 | $this->assertContains('since='.($future->getTimestamp() + 1000), $content['_links'][$link]['href']); |
280 | } | 293 | } |
281 | 294 | ||
282 | $this->assertTrue( | 295 | $this->assertEquals('application/json', $this->client->getResponse()->headers->get('Content-Type')); |
283 | $this->client->getResponse()->headers->contains( | ||
284 | 'Content-Type', | ||
285 | 'application/json' | ||
286 | ) | ||
287 | ); | ||
288 | } | 296 | } |
289 | 297 | ||
290 | public function testDeleteEntry() | 298 | public function testDeleteEntry() |
diff --git a/tests/Wallabag/CoreBundle/Controller/ConfigControllerTest.php b/tests/Wallabag/CoreBundle/Controller/ConfigControllerTest.php index 1954c654..8d0644d1 100644 --- a/tests/Wallabag/CoreBundle/Controller/ConfigControllerTest.php +++ b/tests/Wallabag/CoreBundle/Controller/ConfigControllerTest.php | |||
@@ -3,6 +3,11 @@ | |||
3 | namespace Tests\Wallabag\CoreBundle\Controller; | 3 | namespace Tests\Wallabag\CoreBundle\Controller; |
4 | 4 | ||
5 | use Tests\Wallabag\CoreBundle\WallabagCoreTestCase; | 5 | use Tests\Wallabag\CoreBundle\WallabagCoreTestCase; |
6 | use Wallabag\CoreBundle\Entity\Config; | ||
7 | use Wallabag\UserBundle\Entity\User; | ||
8 | use Wallabag\CoreBundle\Entity\Entry; | ||
9 | use Wallabag\CoreBundle\Entity\Tag; | ||
10 | use Wallabag\AnnotationBundle\Entity\Annotation; | ||
6 | 11 | ||
7 | class ConfigControllerTest extends WallabagCoreTestCase | 12 | class ConfigControllerTest extends WallabagCoreTestCase |
8 | { | 13 | { |
@@ -570,4 +575,264 @@ class ConfigControllerTest extends WallabagCoreTestCase | |||
570 | $config->set('demo_mode_enabled', 0); | 575 | $config->set('demo_mode_enabled', 0); |
571 | $config->set('demo_mode_username', 'wallabag'); | 576 | $config->set('demo_mode_username', 'wallabag'); |
572 | } | 577 | } |
578 | |||
579 | public function testDeleteUserButtonVisibility() | ||
580 | { | ||
581 | $this->logInAs('admin'); | ||
582 | $client = $this->getClient(); | ||
583 | |||
584 | $crawler = $client->request('GET', '/config'); | ||
585 | |||
586 | $this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text'])); | ||
587 | $this->assertContains('config.form_user.delete.button', $body[0]); | ||
588 | |||
589 | $em = $client->getContainer()->get('doctrine.orm.entity_manager'); | ||
590 | |||
591 | $user = $em | ||
592 | ->getRepository('WallabagUserBundle:User') | ||
593 | ->findOneByUsername('empty'); | ||
594 | $user->setExpired(1); | ||
595 | $em->persist($user); | ||
596 | |||
597 | $user = $em | ||
598 | ->getRepository('WallabagUserBundle:User') | ||
599 | ->findOneByUsername('bob'); | ||
600 | $user->setExpired(1); | ||
601 | $em->persist($user); | ||
602 | |||
603 | $em->flush(); | ||
604 | |||
605 | $crawler = $client->request('GET', '/config'); | ||
606 | |||
607 | $this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text'])); | ||
608 | $this->assertNotContains('config.form_user.delete.button', $body[0]); | ||
609 | |||
610 | $client->request('GET', '/account/delete'); | ||
611 | $this->assertEquals(403, $client->getResponse()->getStatusCode()); | ||
612 | |||
613 | $user = $em | ||
614 | ->getRepository('WallabagUserBundle:User') | ||
615 | ->findOneByUsername('empty'); | ||
616 | $user->setExpired(0); | ||
617 | $em->persist($user); | ||
618 | |||
619 | $user = $em | ||
620 | ->getRepository('WallabagUserBundle:User') | ||
621 | ->findOneByUsername('bob'); | ||
622 | $user->setExpired(0); | ||
623 | $em->persist($user); | ||
624 | |||
625 | $em->flush(); | ||
626 | } | ||
627 | |||
628 | public function testDeleteAccount() | ||
629 | { | ||
630 | $client = $this->getClient(); | ||
631 | $em = $client->getContainer()->get('doctrine.orm.entity_manager'); | ||
632 | |||
633 | $user = new User(); | ||
634 | $user->setName('Wallace'); | ||
635 | $user->setEmail('wallace@wallabag.org'); | ||
636 | $user->setUsername('wallace'); | ||
637 | $user->setPlainPassword('wallace'); | ||
638 | $user->setEnabled(true); | ||
639 | $user->addRole('ROLE_SUPER_ADMIN'); | ||
640 | |||
641 | $em->persist($user); | ||
642 | |||
643 | $config = new Config($user); | ||
644 | |||
645 | $config->setTheme('material'); | ||
646 | $config->setItemsPerPage(30); | ||
647 | $config->setReadingSpeed(1); | ||
648 | $config->setLanguage('en'); | ||
649 | $config->setPocketConsumerKey('xxxxx'); | ||
650 | |||
651 | $em->persist($config); | ||
652 | $em->flush(); | ||
653 | |||
654 | $this->logInAs('wallace'); | ||
655 | $loggedInUserId = $this->getLoggedInUserId(); | ||
656 | |||
657 | // create entry to check after user deletion | ||
658 | // that this entry is also deleted | ||
659 | $crawler = $client->request('GET', '/new'); | ||
660 | |||
661 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); | ||
662 | |||
663 | $form = $crawler->filter('form[name=entry]')->form(); | ||
664 | $data = [ | ||
665 | 'entry[url]' => $url = 'https://github.com/wallabag/wallabag', | ||
666 | ]; | ||
667 | |||
668 | $client->submit($form, $data); | ||
669 | $this->assertEquals(302, $client->getResponse()->getStatusCode()); | ||
670 | |||
671 | $crawler = $client->request('GET', '/config'); | ||
672 | |||
673 | $deleteLink = $crawler->filter('.delete-account')->last()->link(); | ||
674 | |||
675 | $client->click($deleteLink); | ||
676 | $this->assertEquals(302, $client->getResponse()->getStatusCode()); | ||
677 | |||
678 | $em = $client->getContainer()->get('doctrine.orm.entity_manager'); | ||
679 | $user = $em | ||
680 | ->getRepository('WallabagUserBundle:User') | ||
681 | ->createQueryBuilder('u') | ||
682 | ->where('u.username = :username')->setParameter('username', 'wallace') | ||
683 | ->getQuery() | ||
684 | ->getOneOrNullResult() | ||
685 | ; | ||
686 | |||
687 | $this->assertNull($user); | ||
688 | |||
689 | $entries = $client->getContainer() | ||
690 | ->get('doctrine.orm.entity_manager') | ||
691 | ->getRepository('WallabagCoreBundle:Entry') | ||
692 | ->findByUser($loggedInUserId); | ||
693 | |||
694 | $this->assertEmpty($entries); | ||
695 | } | ||
696 | |||
697 | public function testReset() | ||
698 | { | ||
699 | $this->logInAs('empty'); | ||
700 | $client = $this->getClient(); | ||
701 | |||
702 | $em = $client->getContainer()->get('doctrine.orm.entity_manager'); | ||
703 | |||
704 | $user = static::$kernel->getContainer()->get('security.token_storage')->getToken()->getUser(); | ||
705 | |||
706 | $tag = new Tag(); | ||
707 | $tag->setLabel('super'); | ||
708 | $em->persist($tag); | ||
709 | |||
710 | $entry = new Entry($user); | ||
711 | $entry->setUrl('http://www.lemonde.fr/europe/article/2016/10/01/pour-le-psoe-chaque-election-s-est-transformee-en-une-agonie_5006476_3214.html'); | ||
712 | $entry->setContent('Youhou'); | ||
713 | $entry->setTitle('Youhou'); | ||
714 | $entry->addTag($tag); | ||
715 | $em->persist($entry); | ||
716 | |||
717 | $entry2 = new Entry($user); | ||
718 | $entry2->setUrl('http://www.lemonde.de/europe/article/2016/10/01/pour-le-psoe-chaque-election-s-est-transformee-en-une-agonie_5006476_3214.html'); | ||
719 | $entry2->setContent('Youhou'); | ||
720 | $entry2->setTitle('Youhou'); | ||
721 | $entry2->addTag($tag); | ||
722 | $em->persist($entry2); | ||
723 | |||
724 | $annotation = new Annotation($user); | ||
725 | $annotation->setText('annotated'); | ||
726 | $annotation->setQuote('annotated'); | ||
727 | $annotation->setRanges([]); | ||
728 | $annotation->setEntry($entry); | ||
729 | $em->persist($annotation); | ||
730 | |||
731 | $em->flush(); | ||
732 | |||
733 | // reset annotations | ||
734 | $crawler = $client->request('GET', '/config#set3'); | ||
735 | |||
736 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); | ||
737 | |||
738 | $crawler = $client->click($crawler->selectLink('config.reset.annotations')->link()); | ||
739 | |||
740 | $this->assertEquals(302, $client->getResponse()->getStatusCode()); | ||
741 | $this->assertContains('flashes.config.notice.annotations_reset', $client->getContainer()->get('session')->getFlashBag()->get('notice')[0]); | ||
742 | |||
743 | $annotationsReset = $em | ||
744 | ->getRepository('WallabagAnnotationBundle:Annotation') | ||
745 | ->findAnnotationsByPageId($entry->getId(), $user->getId()); | ||
746 | |||
747 | $this->assertEmpty($annotationsReset, 'Annotations were reset'); | ||
748 | |||
749 | // reset tags | ||
750 | $crawler = $client->request('GET', '/config#set3'); | ||
751 | |||
752 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); | ||
753 | |||
754 | $crawler = $client->click($crawler->selectLink('config.reset.tags')->link()); | ||
755 | |||
756 | $this->assertEquals(302, $client->getResponse()->getStatusCode()); | ||
757 | $this->assertContains('flashes.config.notice.tags_reset', $client->getContainer()->get('session')->getFlashBag()->get('notice')[0]); | ||
758 | |||
759 | $tagReset = $em | ||
760 | ->getRepository('WallabagCoreBundle:Tag') | ||
761 | ->countAllTags($user->getId()); | ||
762 | |||
763 | $this->assertEquals(0, $tagReset, 'Tags were reset'); | ||
764 | |||
765 | // reset entries | ||
766 | $crawler = $client->request('GET', '/config#set3'); | ||
767 | |||
768 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); | ||
769 | |||
770 | $crawler = $client->click($crawler->selectLink('config.reset.entries')->link()); | ||
771 | |||
772 | $this->assertEquals(302, $client->getResponse()->getStatusCode()); | ||
773 | $this->assertContains('flashes.config.notice.entries_reset', $client->getContainer()->get('session')->getFlashBag()->get('notice')[0]); | ||
774 | |||
775 | $entryReset = $em | ||
776 | ->getRepository('WallabagCoreBundle:Entry') | ||
777 | ->countAllEntriesByUsername($user->getId()); | ||
778 | |||
779 | $this->assertEquals(0, $entryReset, 'Entries were reset'); | ||
780 | } | ||
781 | |||
782 | public function testResetEntriesCascade() | ||
783 | { | ||
784 | $this->logInAs('empty'); | ||
785 | $client = $this->getClient(); | ||
786 | |||
787 | $em = $client->getContainer()->get('doctrine.orm.entity_manager'); | ||
788 | |||
789 | $user = static::$kernel->getContainer()->get('security.token_storage')->getToken()->getUser(); | ||
790 | |||
791 | $tag = new Tag(); | ||
792 | $tag->setLabel('super'); | ||
793 | $em->persist($tag); | ||
794 | |||
795 | $entry = new Entry($user); | ||
796 | $entry->setUrl('http://www.lemonde.fr/europe/article/2016/10/01/pour-le-psoe-chaque-election-s-est-transformee-en-une-agonie_5006476_3214.html'); | ||
797 | $entry->setContent('Youhou'); | ||
798 | $entry->setTitle('Youhou'); | ||
799 | $entry->addTag($tag); | ||
800 | $em->persist($entry); | ||
801 | |||
802 | $annotation = new Annotation($user); | ||
803 | $annotation->setText('annotated'); | ||
804 | $annotation->setQuote('annotated'); | ||
805 | $annotation->setRanges([]); | ||
806 | $annotation->setEntry($entry); | ||
807 | $em->persist($annotation); | ||
808 | |||
809 | $em->flush(); | ||
810 | |||
811 | $crawler = $client->request('GET', '/config#set3'); | ||
812 | |||
813 | $this->assertEquals(200, $client->getResponse()->getStatusCode()); | ||
814 | |||
815 | $crawler = $client->click($crawler->selectLink('config.reset.entries')->link()); | ||
816 | |||
817 | $this->assertEquals(302, $client->getResponse()->getStatusCode()); | ||
818 | $this->assertContains('flashes.config.notice.entries_reset', $client->getContainer()->get('session')->getFlashBag()->get('notice')[0]); | ||
819 | |||
820 | $entryReset = $em | ||
821 | ->getRepository('WallabagCoreBundle:Entry') | ||
822 | ->countAllEntriesByUsername($user->getId()); | ||
823 | |||
824 | $this->assertEquals(0, $entryReset, 'Entries were reset'); | ||
825 | |||
826 | $tagReset = $em | ||
827 | ->getRepository('WallabagCoreBundle:Tag') | ||
828 | ->countAllTags($user->getId()); | ||
829 | |||
830 | $this->assertEquals(0, $tagReset, 'Tags were reset'); | ||
831 | |||
832 | $annotationsReset = $em | ||
833 | ->getRepository('WallabagAnnotationBundle:Annotation') | ||
834 | ->findAnnotationsByPageId($entry->getId(), $user->getId()); | ||
835 | |||
836 | $this->assertEmpty($annotationsReset, 'Annotations were reset'); | ||
837 | } | ||
573 | } | 838 | } |