Compare commits

...

272 Commits

Author SHA1 Message Date
a45f644148 Release wallabag 2.0.0-alpha.2 2016-01-22 11:37:17 +01:00
6c32aaae95 Merge pull request #1614 from wallabag/v2-few-fixes
Few fixes
2016-01-22 09:36:50 +01:00
23afdf3a70 CS 2016-01-22 08:01:32 +01:00
a3cac44c78 Add for deleting rule from an other user 2016-01-22 08:01:17 +01:00
0f0e8eb82a Use FOSUserEvents instead of c/p a controller
The `resetAction` was overriden to redirect user to the homepage instead of `fos_user_profile_show`.
Instead of copying the whole method we can simply use FOSUserEvents to handle that.
2016-01-21 16:39:13 +01:00
a0d6ccc5ca Fix bad type after using findByUrlAndUserId
It returns an object since few commits this part of (untested) code still use an array.
Also add test for that part of code.
2016-01-21 16:37:25 +01:00
e56983af1f Fix bad redirection when adding a new user 2016-01-21 16:36:30 +01:00
27ea492cf7 Add tests on TagAllCommand
Some simple tests
2016-01-21 16:36:17 +01:00
7a0e6970b4 Use PHP7 random_bytes to generate RSS Token
random_bytes is a PHP 7 function wich has been ported to PHP 5 using paragonie/random_compat
2016-01-21 16:35:41 +01:00
9aa66d6244 Merge pull request #1565 from wallabag/v2-2fa-html
Use HTML email for 2FA
2016-01-21 12:27:47 +01:00
abd454c456 Update HTML 2FA template
Split paragraph in translation
Inject wallabag_url for image in HTML template
Remove username & password from config_dev.yml (null are already the default value)
2016-01-21 11:03:27 +01:00
5f736213af #1490 HTML in E-Mails 2016-01-21 11:03:27 +01:00
d1f1333f48 CS 2016-01-21 11:03:27 +01:00
7ce895bf5e Use HTML email for 2FA
Related to #1490
2016-01-21 11:03:27 +01:00
e72a943ad2 Merge pull request #1610 from wallabag/v2-composer-lock-release
Disable xdebug all the time
2016-01-21 11:02:18 +01:00
3ee1582e60 Disable xdebug all the time
Use travis_wait to avoid travis timeout on `composer up`
Jump to `memory_limit -1` for `composer up`
Also, since composer.lock is ignored, we can remove it.
2016-01-21 09:29:14 +01:00
eff6a406f3 Merge pull request #1606 from wallabag/v2-tag-cascade
Remove tag relation when removing an entry
2016-01-21 08:38:37 +01:00
ff1c7d16be Merge pull request #1607 from wallabag/v2-composer-lock
Ignore composer.lock
2016-01-21 08:28:52 +01:00
173629a400 Ignore composer.lock
Having a big composer.lock on a final project can have side effect on incoming PR that add a new vendor.
Mostly because conflict are too frequent.

By ignoring composer.lock we ease the PR submission and rebase.

BUT we need to be careful when we release a new version of wallabag. We should manually `git add -f composer.lock` to update it.

Since composer.lock will no longer be commited I switch the `composer install` to a `composer up` in the travis configuration.
2016-01-20 18:49:45 +01:00
7e80861588 Remove tag relation when removing an entry
Fix #1453
2016-01-20 18:35:57 +01:00
d481f42b7d Merge pull request #1599 from wallabag/v2-fix-pt-documentation
Add configuration for portuguese documentation
2016-01-20 13:00:43 +01:00
acc0160c29 Add configuration for portuguese documentation 2016-01-20 12:54:19 +01:00
3a59d13fd1 Merge pull request #1594 from araujo0205/patch-1
Create index.rst
2016-01-20 12:09:18 +01:00
e1101bee83 Create index.rst 2016-01-19 16:01:28 -02:00
3080a4afa4 Merge pull request #1587 from wallabag/v2-translator
Use translator interface instead of final class
2016-01-18 09:16:01 +01:00
e678c4752a Use translator interface instead of final class
Symfony use a different class when in dev mode and prod mode.

Prod loads `Symfony\Bundle\FrameworkBundle\Translation\Translator`.
Dev loads `Symfony\Component\Translation\DataCollectorTranslator` to gather information for the debug bar.

Fix #1585
2016-01-17 12:56:04 +01:00
3bcc4d4cb2 Merge pull request #1584 from wallabag/v2-cleanup
some cleanup
2016-01-15 16:29:06 +01:00
160e2d8f0b some cleanup:
* move capistrano config files
* remove useless files
2016-01-15 16:07:57 +01:00
f902d78153 Merge pull request #1564 from wallabag/v2-remove-flattr
Remove Flattr references
2016-01-15 16:01:13 +01:00
86719c63bf Merge pull request #1583 from wallabag/v2-fix-delete
Fix `findOneByUrl` side effect in tests
2016-01-15 15:47:13 +01:00
b91465c067 remove flattr on about page 2016-01-15 15:46:46 +01:00
1930c19d82 Merge pull request #1524 from wallabag/sf2.8
Upgrade to Symfony 3.0
2016-01-15 15:38:31 +01:00
7883367246 Fix findOneByUrl side effect in tests
Fix #1566
2016-01-15 15:28:32 +01:00
790573d458 Merge pull request #1560 from wallabag/v2-quickstart
Fix #1501 Quickstart for beginners
2016-01-15 15:21:43 +01:00
5c072d2b57 Quickstart for beginners 2016-01-15 15:01:30 +01:00
131eaa3e94 Update graby stuff 2016-01-15 11:05:51 +01:00
c7f622d369 Update readme & fix console permission 2016-01-15 09:36:32 +01:00
1fc8ed8794 Last deps update 2016-01-15 09:35:39 +01:00
d8dbe76bf5 Add assets config
To avoid :
> Twig_Error_Syntax: Unknown "asset" function in
2016-01-15 09:35:39 +01:00
ccf50a9fd8 Update path in build file 2016-01-15 09:35:39 +01:00
31e33fc42e Update to Symfony 3.0.x 2016-01-15 09:35:39 +01:00
73cd160bfc Switch to Symfony 3 structure 2016-01-15 09:35:39 +01:00
1d405d0e62 Remove some commented code 2016-01-15 09:35:39 +01:00
c38d27d421 Update to friendsofsymfony/oauth-server-bundle@1.5.0-BETA 2016-01-15 09:35:39 +01:00
27e475a941 Fix Pocket generateUrl parameters 2016-01-15 09:35:38 +01:00
6108b0f26c Update deps
LexikFormFilterBundle & RulerZBundle are now Symfony 3 compatible

There are still some issue with FOSOAuthServerBundle
2016-01-15 09:35:38 +01:00
1d76102a24 Fix recent update
- some missing url parameters from WallabagRestController & EntryController
- use a service for `EntryFilterType` to use fully qualified name instead (so changing class signature)
- update ImportBundle (url & form)
2016-01-15 09:35:38 +01:00
8ba854c068 TwoFactorBundle -> 2.0 2016-01-15 09:35:38 +01:00
b4faefc04d Switch to psr-4 2016-01-15 09:35:38 +01:00
5c895a7fd1 Update bundle & stock file
- update stock file (AppKernel, app.php, etc ..) from SymfonyStandard edition)
- update bundle to latest release
- remove security on profiler
2016-01-15 09:35:38 +01:00
619cc45359 Symfony Upgrade Fixer FTW
symfony-upgrade-fixer fix src/Wallabag/
2016-01-15 09:35:38 +01:00
516022d60e Improve composer.json
Following great article from Jordi: http://seld.be/notes/new-composer-patterns

Changelogs summary:

 - paragonie/random_compat updated from 1.1.1 to 1.1.4
   See changes: https://github.com/paragonie/random_compat/compare/1.1.1...1.1.4
   Release notes: https://github.com/paragonie/random_compat/releases/tag/1.1.4

 - doctrine/cache updated from v1.5.2 to v1.5.4
   See changes: https://github.com/doctrine/cache/compare/v1.5.2...v1.5.4
   Release notes: https://github.com/doctrine/cache/releases/tag/v1.5.4

 - sensio/framework-extra-bundle updated from v3.0.11 to v3.0.12
   See changes: https://github.com/sensiolabs/SensioFrameworkExtraBundle/compare/v3.0.11...v3.0.12
   Release notes: https://github.com/sensiolabs/SensioFrameworkExtraBundle/releases/tag/v3.0.12

 - nelmio/cors-bundle updated from 1.4.0 to 1.4.1
   See changes: https://github.com/nelmio/NelmioCorsBundle/compare/1.4.0...1.4.1
   Release notes: https://github.com/nelmio/NelmioCorsBundle/releases/tag/1.4.1

 - friendsofsymfony/rest-bundle updated from 1.7.4 to 1.7.6
   See changes: https://github.com/FriendsOfSymfony/FOSRestBundle/compare/1.7.4...1.7.6
   Release notes: https://github.com/FriendsOfSymfony/FOSRestBundle/releases/tag/1.7.6

 - nelmio/api-doc-bundle updated from 2.11.0 to 2.11.1
   See changes: https://github.com/nelmio/NelmioApiDocBundle/compare/2.11.0...2.11.1
   Release notes: https://github.com/nelmio/NelmioApiDocBundle/releases/tag/2.11.1

 - neitanod/forceutf8 updated from v1.5 to v2.0
   See changes: https://github.com/neitanod/forceutf8/compare/v1.5...v2.0
   Release notes: https://github.com/neitanod/forceutf8/releases/tag/v2.0

 - j0k3r/graby-site-config updated from 1.0.9 to 1.0.10
   See changes: https://github.com/j0k3r/graby-site-config/compare/1.0.9...1.0.10
   Release notes: https://github.com/j0k3r/graby-site-config/releases/tag/1.0.10

 - j0k3r/graby updated from 1.0.6 to 1.0.7
   See changes: https://github.com/j0k3r/graby/compare/1.0.6...1.0.7
   Release notes: https://github.com/j0k3r/graby/releases/tag/1.0.7

 - sebastian/diff updated from 1.4.0 to 1.4.1
   See changes: https://github.com/sebastianbergmann/diff/compare/1.4.0...1.4.1
   Release notes: https://github.com/sebastianbergmann/diff/releases/tag/1.4.1

 - phpunit/phpunit updated from 4.8.19 to 4.8.21
   See changes: https://github.com/sebastianbergmann/phpunit/compare/4.8.19...4.8.21
   Release notes: https://github.com/sebastianbergmann/phpunit/releases/tag/4.8.21

 - kphoen/rulerz updated from 0.16.1 to 0.16.2
   See changes: https://github.com/K-Phoen/rulerz/compare/0.16.1...0.16.2
   Release notes: https://github.com/K-Phoen/rulerz/releases/tag/0.16.2
2016-01-15 09:35:37 +01:00
bd2c8b4677 Wallabag now require PHP >= 5.5
See https://github.com/K-Phoen/rulerz/pull/21/files#diff-b5d0ee8c97c7abd7e3fa29b9a27d1780
2016-01-15 09:35:37 +01:00
30a40b0298 Remove weak deprecation 💥 2016-01-15 09:35:37 +01:00
e23edfd287 Update deps
Changelogs summary:

 - doctrine/cache updated from v1.5.1 to v1.5.2
   See changes: https://github.com/doctrine/cache/compare/v1.5.1...v1.5.2
   Release notes: https://github.com/doctrine/cache/releases/tag/v1.5.2

 - doctrine/common updated from v2.5.1 to v2.5.2
   See changes: https://github.com/doctrine/common/compare/v2.5.1...v2.5.2
   Release notes: https://github.com/doctrine/common/releases/tag/v2.5.2

 - symfony/polyfill-mbstring installed in version v1.0.0
   Release notes: https://github.com/symfony/polyfill-mbstring/releases/tag/v1.0.0

 - symfony/polyfill-util installed in version v1.0.0
   Release notes: https://github.com/symfony/polyfill-util/releases/tag/v1.0.0

 - paragonie/random_compat installed in version 1.1.1
   Release notes: https://github.com/paragonie/random_compat/releases/tag/1.1.1

 - symfony/polyfill-php70 installed in version v1.0.0
   Release notes: https://github.com/symfony/polyfill-php70/releases/tag/v1.0.0

 - symfony/polyfill-php56 installed in version v1.0.0
   Release notes: https://github.com/symfony/polyfill-php56/releases/tag/v1.0.0

 - ircmaxell/password-compat installed in version v1.0.4
   Release notes: https://github.com/ircmaxell/password_compat/releases/tag/v1.0.4

 - symfony/polyfill-php55 installed in version v1.0.0
   Release notes: https://github.com/symfony/polyfill-php55/releases/tag/v1.0.0

 - symfony/polyfill-php54 installed in version v1.0.0
   Release notes: https://github.com/symfony/polyfill-php54/releases/tag/v1.0.0

 - symfony/polyfill-intl-icu installed in version v1.0.0
   Release notes: https://github.com/symfony/polyfill-intl-icu/releases/tag/v1.0.0

 - symfony/symfony updated from v2.7.7 to v2.8.0
   See changes: https://github.com/symfony/symfony/compare/v2.7.7...v2.8.0
   Release notes: https://github.com/symfony/symfony/releases/tag/v2.8.0

 - symfony/security-acl installed in version v2.7.7
   Release notes: https://github.com/symfony/security-acl/releases/tag/v2.7.7

 - doctrine/orm updated from v2.5.1 to v2.5.2
   See changes: https://github.com/doctrine/doctrine2/compare/v2.5.1...v2.5.2
   Release notes: https://github.com/doctrine/doctrine2/releases/tag/v2.5.2

 - kriswallsmith/assetic updated from v1.3.1 to v1.3.2
   See changes: https://github.com/kriswallsmith/assetic/compare/v1.3.1...v1.3.2
   Release notes: https://github.com/kriswallsmith/assetic/releases/tag/v1.3.2

 - symfony/assetic-bundle updated from v2.7.0 to v2.7.1
   See changes: https://github.com/symfony/assetic-bundle/compare/v2.7.0...v2.7.1
   Release notes: https://github.com/symfony/assetic-bundle/releases/tag/v2.7.1

 - symfony/swiftmailer-bundle updated from v2.3.8 to v2.3.9
   See changes: https://github.com/symfony/swiftmailer-bundle/compare/v2.3.8...v2.3.9
   Release notes: https://github.com/symfony/swiftmailer-bundle/releases/tag/v2.3.9

 - symfony/monolog-bundle updated from 2.8.1 to v2.8.2
   See changes: https://github.com/symfony/monolog-bundle/compare/2.8.1...v2.8.2
   Release notes: https://github.com/symfony/monolog-bundle/releases/tag/v2.8.2

 - sensio/distribution-bundle updated from v3.0.33 to v3.0.34
   See changes: https://github.com/sensiolabs/SensioDistributionBundle/compare/v3.0.33...v3.0.34
   Release notes: https://github.com/sensiolabs/SensioDistributionBundle/releases/tag/v3.0.34

 - friendsofsymfony/rest-bundle updated from 1.7.2 to 1.7.4
   See changes: https://github.com/FriendsOfSymfony/FOSRestBundle/compare/1.7.2...1.7.4
   Release notes: https://github.com/FriendsOfSymfony/FOSRestBundle/releases/tag/1.7.4

 - nelmio/api-doc-bundle updated from 2.10.3 to 2.11.0
   See changes: https://github.com/nelmio/NelmioApiDocBundle/compare/2.10.3...2.11.0
   Release notes: https://github.com/nelmio/NelmioApiDocBundle/releases/tag/2.11.0

 - j0k3r/graby-site-config updated from 1.0.6 to 1.0.9
   See changes: https://github.com/j0k3r/graby-site-config/compare/1.0.6...1.0.9
   Release notes: https://github.com/j0k3r/graby-site-config/releases/tag/1.0.9

 - j0k3r/safecurl updated from v1.1.1 to 2.0.2
   See changes: https://github.com/j0k3r/safecurl/compare/v1.1.1...2.0.2
   Release notes: https://github.com/j0k3r/safecurl/releases/tag/2.0.2

 - j0k3r/graby updated from 1.0.4 to 1.0.6
   See changes: https://github.com/j0k3r/graby/compare/1.0.4...1.0.6
   Release notes: https://github.com/j0k3r/graby/releases/tag/1.0.6

 - scheb/two-factor-bundle updated from v1.4.7 to v1.5.0
   See changes: https://github.com/scheb/two-factor-bundle/compare/v1.4.7...v1.5.0
   Release notes: https://github.com/scheb/two-factor-bundle/releases/tag/v1.5.0

 - sebastian/recursion-context updated from 1.0.1 to 1.0.2
   See changes: https://github.com/sebastianbergmann/recursion-context/compare/1.0.1...1.0.2
   Release notes: https://github.com/sebastianbergmann/recursion-context/releases/tag/1.0.2

 - sebastian/environment updated from 1.3.2 to 1.3.3
   See changes: https://github.com/sebastianbergmann/environment/compare/1.3.2...1.3.3
   Release notes: https://github.com/sebastianbergmann/environment/releases/tag/1.3.3

 - sebastian/diff updated from 1.3.0 to 1.4.0
   See changes: https://github.com/sebastianbergmann/diff/compare/1.3.0...1.4.0
   Release notes: https://github.com/sebastianbergmann/diff/releases/tag/1.4.0

 - phpunit/phpunit updated from 4.8.18 to 4.8.19
   See changes: https://github.com/sebastianbergmann/phpunit/compare/4.8.18...4.8.19
   Release notes: https://github.com/sebastianbergmann/phpunit/releases/tag/4.8.19

 - symfony/phpunit-bridge updated from v2.7.7 to v2.8.0
   See changes: https://github.com/symfony/phpunit-bridge/compare/v2.7.7...v2.8.0
   Release notes: https://github.com/symfony/phpunit-bridge/releases/tag/v2.8.0

 - doctrine/doctrine-cache-bundle updated from 1.2.1 to 1.2.2
   See changes: https://github.com/doctrine/DoctrineCacheBundle/compare/1.2.1...1.2.2
   Release notes: https://github.com/doctrine/DoctrineCacheBundle/releases/tag/1.2.2

 - doctrine/doctrine-bundle updated from v1.5.2 to 1.6.1
   See changes: https://github.com/doctrine/DoctrineBundle/compare/v1.5.2...1.6.1
   Release notes: https://github.com/doctrine/DoctrineBundle/releases/tag/1.6.1

 - phpzip/phpzip updated from 2.0.7 to 2.0.8
   See changes: https://github.com/Grandt/PHPZip/compare/2.0.7...2.0.8
   Release notes: https://github.com/Grandt/PHPZip/releases/tag/2.0.8

 - kphoen/rulerz updated from 0.14.0 to 0.16.1
   See changes: https://github.com/K-Phoen/rulerz/compare/0.14.0...0.16.1
   Release notes: https://github.com/K-Phoen/rulerz/releases/tag/0.16.1

 - friendsofsymfony/user-bundle updated from dev-master to dev-master
   See changes: 45d6f40...e39b040
2016-01-15 09:35:37 +01:00
9ee44a109a Merge pull request #1574 from jjanvier/readme
Fix typo and set version in the README
2016-01-14 07:50:55 +01:00
6663d49329 Fix typo and set version in the README 2016-01-14 00:47:17 +01:00
0aafb8dfcb Merge pull request #1561 from FabienM/docker-compose
Add basic docker-compose configuration
2016-01-11 09:08:39 +01:00
8b909e7ea7 Add documentation to run docker-compose 2016-01-10 15:01:20 +01:00
1d41effebc Add Docker configuration for Postgres and MariaDB 2016-01-10 15:01:11 +01:00
540ef5e084 Add basic docker-compose configuration 2016-01-10 14:41:42 +01:00
4aa2997106 Merge pull request #1563 from wallabag/v2-fix-1394
#1394 Fixed 'mark as read' and 'mark as unread' links
2016-01-10 10:20:16 +01:00
c60b549313 Merge pull request #1562 from wallabag/v2-add-import-baggy
add import link on baggy theme
2016-01-10 10:17:27 +01:00
6c9bad13e1 Fixed 'mark as read' and 'mark as unread' links 2016-01-09 22:20:19 +01:00
da82839bf8 add import link on baggy theme 2016-01-09 20:33:09 +01:00
5ad863c63a Merge pull request #1558 from wallabag/v2-documentation
[v2] documentation cleaning
2016-01-09 17:33:27 +01:00
1caf557de7 Merge pull request #1559 from wallabag/v2-doctrine-migrations
Installation & configuration of Doctrine Migrations
2016-01-09 14:43:06 +01:00
e1b9f4fb37 Update deps
Changelogs summary:

 - doctrine/dbal updated from v2.5.3 to v2.5.4
   See changes: https://github.com/doctrine/dbal/compare/v2.5.3...v2.5.4
   Release notes: https://github.com/doctrine/dbal/releases/tag/v2.5.4

 - doctrine/orm updated from v2.5.3 to v2.5.4
   See changes: https://github.com/doctrine/doctrine2/compare/v2.5.3...v2.5.4
   Release notes: https://github.com/doctrine/doctrine2/releases/tag/v2.5.4

 - nelmio/api-doc-bundle updated from 2.11.1 to 2.11.2
   See changes: https://github.com/nelmio/NelmioApiDocBundle/compare/2.11.1...2.11.2
   Release notes: https://github.com/nelmio/NelmioApiDocBundle/releases/tag/2.11.2

 - friendsofsymfony/user-bundle updated from dev-master to dev-master
   See changes: e5e7a2b...6ccff96
2016-01-09 14:24:09 +01:00
a42938f42c documentation cleaning 2016-01-09 13:50:45 +01:00
292c1324e9 Installation & configuration of Doctrine Migrations 2016-01-08 16:27:29 +01:00
39643c6b76 Merge pull request #1493 from wallabag/v2-pocket-import
v2 – 1st draft for Pocket import via API & Wallabag v1 import
2016-01-07 22:15:08 +01:00
b88cf91fc8 updated tests 2016-01-07 21:00:04 +01:00
488a468e3e Merge pull request #1553 from wallabag/v2-hide-export-button
Hide export button if we are not on entries list
2016-01-06 21:25:41 +01:00
10b2411c10 Hide export button if we are not on entries list 2016-01-06 07:47:09 +01:00
d1af8ad4db Added french translations 2016-01-05 22:38:09 +01:00
8eedc8cfac Few phpDoc fix
And some little mistakes
2016-01-03 11:15:44 +01:00
c5c7f90a81 Fix tag related test for Pocket 2016-01-03 10:32:56 +01:00
5b2b5858fe Update deps 2016-01-02 23:31:41 +01:00
6d7c7a6e3f Remove real consumer key
We should never commit them :)
2016-01-02 23:27:41 +01:00
7019c7cf6c Add tagged services for import
- list services in /import
- add url to import service
- ImportBundle routing are now prefixed by /import
- optimize flush in each import (flushing each 20 contents)
- improve design of each import
- add more tests
2016-01-02 23:27:41 +01:00
b1d05721cf Rewrote Wallabag v1 import 2016-01-02 23:27:41 +01:00
252ebd6071 Rewrote Pocket Import
For the moment, we won't do a queue system, just a plain synchronous import.
We also use ContentProxy to grab content for each article from Pocket.
Error from Pocket are now logged using the logger.
The ImportInterface need to be simple and not related to oAuth (not all import will use that method).
2016-01-02 23:27:41 +01:00
b4b592a0c0 Fix the way to check for an existing entry
Instead of requiring more than 1 entry (> 1) we have to check for at least one entry (> 0)
2016-01-02 23:27:41 +01:00
77a7752a59 Update after previous merge
PR #1443 was merged into this branch to handle all import type in the same place.
2016-01-02 23:27:41 +01:00
7ec2897ee0 First test on PocketImport
Giving ability to define the Client add abitliy to easliy test the import.
2016-01-02 23:27:41 +01:00
0aa344dc24 Update url & service name
Prefix ur with service namel: [service]_[route name]
Add comment in Interface
2016-01-02 23:27:41 +01:00
5a4bbcc9a7 Change the way to check for an existing entry
The repository method return the entry found or false if nothing exists.
2016-01-02 23:27:41 +01:00
27a8708b67 Update deps 2016-01-02 23:27:41 +01:00
303768dfe9 - remove importers configuration
- add check on userId for findOneByURL for entries
2016-01-02 23:27:41 +01:00
dda57bb944 fix #1502 avoid duplicate entry and store pocket url in config 2016-01-02 23:24:17 +01:00
87f23b005c assign tags to entries and add lastPocketImport attribute to user 2016-01-02 23:24:17 +01:00
d51b38ed30 create ImportController to list importers 2016-01-02 23:24:17 +01:00
557e549db7 service call 2016-01-02 23:24:17 +01:00
9c9c23cf08 forgot test parameters 2016-01-02 23:24:17 +01:00
ff7b031d57 refactor pocket import 2016-01-02 23:24:17 +01:00
1f4408de9e 1st draft for Pocket import via API 2016-01-02 23:24:17 +01:00
10b40f85d6 add docs for Import feature 2016-01-02 23:24:17 +01:00
d275bdf4d3 form to upload file 2016-01-02 23:24:17 +01:00
8c3c77c1bd create controller to launch import command 2016-01-02 23:24:17 +01:00
a1bb1b3c2a userId is now set in parameters 2016-01-02 23:24:17 +01:00
56ea1de99b first draft for json import, needed for wallabag v1 migration 2016-01-02 23:24:17 +01:00
dad1c546a5 Merge pull request #1545 from wallabag/v2-user-tag
v2 – Remove user reference in tag
2016-01-02 12:46:52 +01:00
5432f61509 Merge pull request #1546 from wallabag/v2-reload-content
v2 – Ability to reload an entry
2016-01-02 12:46:09 +01:00
00a051add4 Merge pull request #1547 from wallabag/v2-mistakes
v2 – Fix wrong if when deleting an entry
2016-01-02 12:45:08 +01:00
e82160e5e9 Fix wrong if when deleting an entry
Bad condition when deleting an entry returned `true` instead of the referer url
2015-12-30 11:32:53 +01:00
0cf434c00f Backport all icomoon from v1
And add a reload icon
2015-12-30 09:53:52 +01:00
831b02aaf2 Ability to reload an entry
Could be useful when we want to update the content or when the content failed to be fetched.

Fix #1503
2015-12-30 09:41:17 +01:00
6be9750155 Removed comment
And move the SQL query inside the php doc
2015-12-29 21:59:34 +01:00
e6a228c43b Merge pull request #1544 from wallabag/2fa-email
v2 – Add custom email for 2FA
2015-12-29 20:32:58 +01:00
4059a061c0 Fix the way to remove a tag from all user entries 2015-12-29 15:43:32 +01:00
1bb1939ab7 Cleanup tests
- WallabagRestController: remove the tag deletion from the API since we can't remove a tag now, we only remove reference to entries
- RuleBasedTaggerTest: remove workaround for asserting tag are equal since problem was related to mock expects (_call instead of findOneByLabel which was removed from the tag repository)
2015-12-29 15:04:46 +01:00
01fddd0cb2 Fix parameters regarding documentation
Parameters are `star` & `archived`, not `is_starred` & `is_archived`
2015-12-29 14:54:55 +01:00
fc73222723 Remove user reference in tag
Fix #1543
2015-12-29 14:50:52 +01:00
ab64c3d9ac PHP 7.0 must not fail 2015-12-29 11:41:35 +01:00
23ff8d3619 Add custom email for 2FA
Related #1490
2015-12-29 11:17:06 +01:00
c997cfcc9c Merge pull request #1540 from wallabag/v2-fix-delete
v2 – 	Don't redirect to the content page after deletion
2015-12-28 13:51:48 +01:00
2863bf2ab5 Don't redirect to the content page after deletion
Fix #1512

We generate the url of the removed content and compare it to the referer url. If they matche, we redirect user to the homepage otherwise to the referer url.
2015-12-28 13:42:50 +01:00
71eff67f8b Entry data should have unique url 2015-12-28 13:42:50 +01:00
d25b828821 Merge pull request #1542 from wallabag/v2-avoid-duplicate-tag
v2 – 	Avoid multiple tag creation
2015-12-28 13:37:13 +01:00
e9fa8c40aa Add test on tagAllForUser
And fix multiplication of entries returned by `tagAllForUser`.
2015-12-28 13:20:48 +01:00
fc031e5706 Avoid multiple tag creation
When a new tag is created but not yet persisted, it can be duplicated.
It could happen when multiple rules match the content and at least 2 of them should attach same new tag.

Fix #1528
2015-12-28 13:20:48 +01:00
82899c0402 Merge pull request #1488 from wallabag/v2-slug-tags
Slug for tags
2015-12-28 13:12:30 +01:00
3c65dfb735 add slug for tags 2015-12-28 12:49:45 +01:00
22c1d29b89 Merge pull request #1537 from bdunogier/update_lock
Updated composer.lock
2015-12-24 08:37:27 +01:00
d4aa174367 Updated composer.lock
Had to restrict scheb/two-factor-bundle to ~1.4.0 because of a circular service reference.
See https://github.com/scheb/two-factor-bundle/issues/33.

Also updated php version requirement to 5.5.0.
2015-12-23 22:49:03 +01:00
da2240f9d4 Merge pull request #1529 from wallabag/v2-taggingrule-tablename
changed table name for tagging rule
2015-12-08 09:35:27 +01:00
347fa6beb0 php cs 2015-12-08 09:20:03 +01:00
c5d0db8b71 changed table name for tagging rule 2015-12-08 09:05:56 +01:00
a7f1921f7d Merge pull request #1478 from K-Phoen/rule-based-tags
Rule based tags
2015-12-06 14:31:26 +01:00
2e15e30bf0 Merge pull request #1523 from wallabag/tcitworld-patch-1
Typo
2015-12-04 06:43:40 +01:00
a24c1ee30a Typo 2015-12-04 00:17:07 +01:00
f4b617f2ff Merge pull request #1518 from mathbruyen/smtp-from-env
Set SMTP from environment variables
2015-12-03 08:43:40 +01:00
752b90d1f2 Fix tagging rules ordering 2015-11-29 16:19:02 +01:00
59507c5b24 SMTP configuration through environment
Configuring SMTP parameters required to be written in files which is not
convenient when using docker containers as the credentials need to be in
the filesystem.

Created environment parameters override for those parameters. A new build
script created in order to generate those parameters right before
launching the application.
2015-11-26 10:44:03 +01:00
c13eda461f Clean the tagging rule creation form 2015-11-16 14:01:43 +01:00
69edb774eb Assert that the tag has is the good one 2015-11-16 13:34:00 +01:00
958671a7ae Add a quick test 2015-11-13 22:53:28 +01:00
aeff8aa765 Document the matches operator in the FAQ 2015-11-13 20:57:46 +01:00
f27aca26f6 Add phpdoc for all Matches implementations 2015-11-13 20:50:09 +01:00
a6e27f7466 Add matches operator 2015-11-13 14:37:58 +01:00
5c514b0be3 Improve the tagging rules documentation 2015-11-11 17:06:36 +01:00
b7b2005494 Fix the creation of the repository services 2015-11-11 16:44:57 +01:00
6cbbf1481a Update rulerz-bundle 2015-11-11 16:39:28 +01:00
0c5bcd82ba Use Psr\Log\NullLogger instead of creating a mock 2015-11-11 16:27:53 +01:00
7b1648961d Fix incorrect comment. 2015-11-11 16:27:19 +01:00
8a99c7a86b Add a few functional tests for the tagging rules creation form 2015-11-11 16:27:19 +01:00
9b88658c04 Update baggy theme 2015-11-11 16:27:19 +01:00
52e423f307 Provide a way to delete tagging rules 2015-11-11 16:27:19 +01:00
625acf3352 Add a command to automatically tag all entries for a user 2015-11-11 16:27:19 +01:00
cad8cda7af Use a tagged version of kphoen/rulerz-bundle 2015-11-11 16:25:44 +01:00
c23fc05df8 Validate used operators when creating tagging rules 2015-11-11 16:23:49 +01:00
5a166c5c1a Add tests for the RuleBasedTagger class 2015-11-11 16:23:49 +01:00
1c9cd2a7f0 Errors in the automatic tagging do not prevent the entry from being added 2015-11-11 16:23:49 +01:00
1dc4e5da2e Also validate used variables when creating tagging rules 2015-11-11 16:23:49 +01:00
3447d1ee07 Add naïve validation for tagging rules (only checks the syntax) 2015-11-11 16:23:49 +01:00
71ef0ed254 Rename the « Tags » tab to « Tagging rules » 2015-11-11 16:23:49 +01:00
003fa77438 Add tests for the StringToListTransformer class 2015-11-11 16:23:49 +01:00
f530f7f5e1 Fix ContentProxyTest 2015-11-11 16:23:49 +01:00
1d7b350b25 Add missing use statement 2015-11-11 16:23:49 +01:00
e9fbd2d12e Add a table explaining the available variables 2015-11-11 16:23:49 +01:00
981cacf7b9 Display the tagging rules in the config 2015-11-11 16:23:49 +01:00
9cbb404b4a Add missing tagging rule repository 2015-11-11 16:23:49 +01:00
f19f9f62d1 Add a form to create tagging rules 2015-11-11 16:23:49 +01:00
ac9fec610a Add TaggingRule entity 2015-11-11 16:23:49 +01:00
c3510620ad PoC of rule-based tagging 2015-11-11 16:23:49 +01:00
0a0c600887 Merge pull request #1422 from wallabag/v2-ebook
V2 – Export entries
2015-11-09 16:45:48 +01:00
16bbb4aa41 Update deps 2015-11-09 16:35:09 +01:00
fba3f536a5 Fix tests 2015-11-09 16:32:48 +01:00
cceca9ea1d Fix route parameters
Improve export tests
Improve CSV export
2015-11-09 16:32:48 +01:00
268e9e7277 use JMS Serializer 2015-11-09 16:32:48 +01:00
5b7da07620 use the groups annotation instead of setIgnoredAttributes 2015-11-09 16:32:48 +01:00
8ac95cbfcc improved function 2015-11-09 16:32:48 +01:00
b3cc1a14e7 add json & xml 2015-11-09 16:32:48 +01:00
33c36f6b48 Fix tests on pgsql 2015-11-09 16:32:48 +01:00
add597bad9 Rework on export
- all export now return a `HttpFoundation\Response`
- return a 404 on unsupported format
- add tests
2015-11-09 16:32:48 +01:00
03690d1387 Start work on export
For now:
 - ebook
 - mobi
 - pdf
 - csv
2015-11-09 16:32:48 +01:00
f1eccfd63f Merge pull request #1500 from wallabag/v2-quote-pgsql
Fix quote strategy for reserved keyword in Postgres
2015-11-09 16:31:59 +01:00
53cf510689 Lock DoctrineBundle to 1.5.2
Because of a bad change, SQLite database cannot be created anymore.

➡️ https://github.com/doctrine/DoctrineBundle/pull/434
2015-11-08 10:47:56 +01:00
970e0e994f Remove duplicate comments 2015-11-07 14:18:38 +01:00
75c48e3ae0 CS 2015-11-07 14:16:13 +01:00
d502762598 Skipping PostgreSQL test that drop database
For a not-clear reason, I can't properly drop the current database. Even if Doctrine made a special case for that (https://github.com/doctrine/dbal/pull/849).

So instead of trying crazy things to achieve the test, better way is to skip test (too much wasted days ..)
2015-11-07 14:16:13 +01:00
8a493541fa Re-enable test on doctrine command
It will slow down the whole test suite (because it'll use doctrine command).
Remove unecessary `KernelTestCase`.
Also rename `AbstractControllerTest` to `WallabagApiTestCase` for consistency.
2015-11-07 14:16:13 +01:00
735068d181 Add tests on TablePrefixSubscriber 2015-11-07 14:15:33 +01:00
bd0f3d32c9 Quoted entity to avoid reserved keyword
Should fix #1498
2015-11-07 14:15:33 +01:00
54a2241e13 Fix database already exist detection
This is not the same message exception from MySQL & SQLite with Postgres.
2015-11-07 14:15:33 +01:00
4529d0f4b6 Merge pull request #1510 from wallabag/v2-sf3
v2 – Fix some deprecated for Symfony3
2015-11-07 13:39:30 +01:00
164d260c49 CS
Also force SYMFONY_DEPRECATIONS_HELPER for travis
2015-11-07 00:27:41 +01:00
b2a903feef Avoid other bundle deprecation to cancel the build 2015-11-07 00:18:46 +01:00
33fe61f92f Fix form_enctype deprecation
Use form_start instead
2015-11-07 00:18:06 +01:00
18f8f32f70 Fix security.context deprecation 2015-11-07 00:17:37 +01:00
5ead137fe6 Fix pattern deprecation
Replaced by path
2015-11-07 00:16:26 +01:00
78507d2835 Fix deprecated helper in command 2015-11-06 23:39:19 +01:00
872384b0c1 Update deps & add PHPUnit Bridge
Changelogs summary:

 - symfony/phpunit-bridge installed in version v2.7.6
   Release notes: https://github.com/symfony/phpunit-bridge/releases/tag/v2.7.6

 - twig/twig updated from v1.23.0 to v1.23.1
   See changes: https://github.com/twigphp/Twig/compare/v1.23.0...v1.23.1
   Release notes: https://github.com/twigphp/Twig/releases/tag/v1.23.1

 - doctrine/inflector updated from v1.0.1 to v1.1.0
   See changes: https://github.com/doctrine/inflector/compare/v1.0.1...v1.1.0
   Release notes: https://github.com/doctrine/inflector/releases/tag/v1.1.0

 - doctrine/cache updated from v1.5.0 to v1.5.1
   See changes: https://github.com/doctrine/cache/compare/v1.5.0...v1.5.1
   Release notes: https://github.com/doctrine/cache/releases/tag/v1.5.1

 - doctrine/doctrine-cache-bundle updated from v1.0.1 to 1.2.1
   See changes: https://github.com/doctrine/DoctrineCacheBundle/compare/v1.0.1...1.2.1
   Release notes: https://github.com/doctrine/DoctrineCacheBundle/releases/tag/1.2.1

 - doctrine/doctrine-bundle updated from v1.5.2 to 1.6.0
   See changes: https://github.com/doctrine/DoctrineBundle/compare/v1.5.2...1.6.0
   Release notes: https://github.com/doctrine/DoctrineBundle/releases/tag/1.6.0

 - friendsofsymfony/user-bundle updated from dev-master to dev-master
   See changes: d5b28c3...45d6f40
2015-11-06 23:39:14 +01:00
3d9ccf9390 Merge pull request #1505 from wallabag/v2-graby-log
Add graby log
2015-11-02 07:52:28 +01:00
386cd24663 Update deps
Changelogs summary:

 - twig/twig updated from v1.22.3 to v1.23.0
   See changes: https://github.com/twigphp/Twig/compare/v1.22.3...v1.23.0
   Release notes: https://github.com/twigphp/Twig/releases/tag/v1.23.0

 - doctrine/cache updated from v1.4.2 to v1.5.0
   See changes: https://github.com/doctrine/cache/compare/v1.4.2...v1.5.0
   Release notes: https://github.com/doctrine/cache/releases/tag/v1.5.0

 - symfony/symfony updated from v2.7.5 to v2.7.6
   See changes: https://github.com/symfony/symfony/compare/v2.7.5...v2.7.6
   Release notes: https://github.com/symfony/symfony/releases/tag/v2.7.6

 - sensio/distribution-bundle updated from v3.0.31 to v3.0.33
   See changes: https://github.com/sensiolabs/SensioDistributionBundle/compare/v3.0.31...v3.0.33
   Release notes: https://github.com/sensiolabs/SensioDistributionBundle/releases/tag/v3.0.33

 - nelmio/api-doc-bundle updated from 2.9.0 to 2.10.3
   See changes: https://github.com/nelmio/NelmioApiDocBundle/compare/2.9.0...2.10.3
   Release notes: https://github.com/nelmio/NelmioApiDocBundle/releases/tag/2.10.3

 - lexik/form-filter-bundle updated from v4.0.1 to v4.0.2
   See changes: https://github.com/lexik/LexikFormFilterBundle/compare/v4.0.1...v4.0.2
   Release notes: https://github.com/lexik/LexikFormFilterBundle/releases/tag/v4.0.2

 - j0k3r/graby-site-config updated from 1.0.5 to 1.0.6
   See changes: https://github.com/j0k3r/graby-site-config/compare/1.0.5...1.0.6
   Release notes: https://github.com/j0k3r/graby-site-config/releases/tag/1.0.6

 - j0k3r/graby updated from 1.0.2 to 1.0.4
   See changes: https://github.com/j0k3r/graby/compare/1.0.2...1.0.4
   Release notes: https://github.com/j0k3r/graby/releases/tag/1.0.4

 - phpunit/phpunit updated from 4.8.13 to 4.8.16
   See changes: https://github.com/sebastianbergmann/phpunit/compare/4.8.13...4.8.16
   Release notes: https://github.com/sebastianbergmann/phpunit/releases/tag/4.8.16
2015-10-30 08:00:43 +01:00
c929c88faf Enable graby log 2015-10-24 10:53:55 +02:00
e686b5e6fe Merge pull request #1495 from wallabag/v2-docs
add docs
2015-10-22 17:02:06 +02:00
71200c4155 Merge pull request #1496 from wallabag/v2-redirect-after-deletion
fix #1005 V2 redirect after deletion
2015-10-22 09:28:54 +02:00
18d5f4541e redirect on last page after deletion 2015-10-21 15:26:37 +02:00
59e91bc893 add docs 2015-10-21 14:14:51 +02:00
a4e1ddf2d6 Merge pull request #1491 from jlnostr/v2-german-translation
Add german translation.
2015-10-19 09:21:43 +02:00
57cf6fd76c Add language to config.yml 2015-10-18 22:04:50 +02:00
37a564a868 Add german translation. 2015-10-18 21:26:09 +02:00
fcc6949d4a Merge pull request #1446 from wallabag/v2-language-config
[WIP] language selection on config screen
2015-10-18 15:35:42 +02:00
2aac2f278f Add tests on listeners 2015-10-16 07:40:09 +02:00
c89d35e851 Language selection on config screen 2015-10-15 21:42:29 +02:00
3d3ed955f1 Merge pull request #1484 from wallabag/v2-2factor-auth
2factor authentication via email
2015-10-15 13:52:52 +02:00
e9b395ec4b forgot parameters for travis 2015-10-15 13:20:44 +02:00
18cf594f8a move 2factor activation in parameters 2015-10-15 13:17:21 +02:00
0d6a7929e1 add tests for 2factor authentication 2015-10-14 17:10:12 +02:00
f6af634aec Merge pull request #1475 from modos189/v2_material
V2 material
2015-10-14 12:01:30 +02:00
2db616b586 2factor authentication via email 2015-10-13 22:43:15 +02:00
c26a3edc0a Other improvements 2015-10-09 13:00:15 +03:00
ba873ae667 Icons in footer 2015-10-09 12:43:42 +03:00
cf0ea8f113 Merge pull request #1472 from wallabag/v2-clean-views
fix #1470: clean material theme
2015-10-08 13:44:04 +02:00
9dbcf9d418 Merge pull request #1471 from wallabag/v2-bookmarklet
implement bookmarklet
2015-10-08 13:43:53 +02:00
fa853bb603 fix #1470: clean material theme 2015-10-08 13:32:34 +02:00
880a0e1c0b implement bookmarklet 2015-10-08 13:29:41 +02:00
3bfbd22f13 Merge pull request #1468 from wallabag/v2-fix-detect-theme
fix bug on detect active theme
2015-10-06 20:57:54 +02:00
ce782c84b8 fix bug on detect active theme 2015-10-06 20:51:40 +02:00
3123b3c0c8 Merge pull request #1464 from wallabag/v2-fix-type-hint
fix type hint for User
2015-10-06 12:24:18 +02:00
1ce8f30342 fix type hint for User 2015-10-06 12:16:08 +02:00
16dabc3263 Merge pull request #1436 from wallabag/v2-register
Public registration & oAuth2 \o/
2015-10-06 09:19:06 +02:00
fdef5f4605 Merge pull request #1461 from wallabag/v2-test-for-www
fix #1433: add test for removeWww Twig Extension
2015-10-06 09:16:52 +02:00
794dc4ee8a Merge pull request #1459 from wallabag/v2-tags-chips
fix #1457 & update materialize
2015-10-06 09:09:56 +02:00
784bb4c38d add test for removeWww Twig Extension 2015-10-06 09:09:26 +02:00
8263e71192 use form widget for registration 2015-10-06 07:44:10 +02:00
68e9dcf615 restore blue color 2015-10-06 06:16:17 +02:00
e0d188809c move css 2015-10-05 22:49:46 +02:00
d30262154a last changes for baggy theme 2015-10-05 22:45:44 +02:00
ec3ce598f6 material design for register/login/recover pages 2015-10-05 22:16:18 +02:00
9c8f7af196 fix #1457 2015-10-05 21:37:17 +02:00
4c5e544183 Cleanup
- remove unecessary routing for UserBundle
- remove unused form type
2015-10-03 13:37:21 +02:00
0a878469d4 move some files to UserBundle 2015-10-03 13:31:48 +02:00
1210dae105 remove old implementation for login/register/recover 2015-10-03 13:31:48 +02:00
772d8c4b93 Add test on RegistrationConfirmedListener
And PLEASE @nicosomb, NEVER EVER inject the whole container inside a service.
2015-10-03 13:30:43 +02:00
2c13918acc add test for confirmed registration 2015-10-03 13:30:43 +02:00
359b3f43cc * rename AuthenticationListener
* add tests
2015-10-03 13:30:43 +02:00
772732531e check authentication on each API route 2015-10-03 13:30:43 +02:00
cd1298d6df allow API documentation for anonymous 2015-10-03 13:30:43 +02:00
fcb1fba5c2 * public registration
* remove WSSE implementation
* add oAuth2 implementation
2015-10-03 13:30:43 +02:00
8a60bc4cc2 Merge pull request #1447 from wallabag/v2-typo
typo
2015-10-01 19:58:58 +02:00
4b55e704ab typo 2015-10-01 18:48:38 +02:00
0e8f778199 Merge pull request #1441 from wallabag/v2-french-translation
Add french translation
2015-10-01 11:49:13 +02:00
4e9f656ecb restore footer 2015-10-01 11:38:59 +02:00
cfb28c9da0 french translation 2015-10-01 09:26:52 +02:00
8e417206d5 flash messages translation 2015-09-30 18:17:40 +02:00
4aafa7f0df french translation 2015-09-30 18:09:18 +02:00
4f0dfac6a6 add french translation 2015-09-30 17:06:41 +02:00
7c99da0c95 Merge pull request #1437 from wallabag/v2-clean-material
some cleanup on material theme
2015-09-30 09:05:03 +02:00
24429857d8 Merge pull request #1439 from wallabag/v2-fix-deploy
fix Capistrano configuration
2015-09-30 08:49:00 +02:00
d2755b1c30 Merge pull request #1440 from wallabag/v2-fix-1433
fix #1433: remove www. on entries view
2015-09-29 23:23:58 +02:00
2afdea1ccc forgot one file 2015-09-29 23:11:27 +02:00
72fcaf8a6c remove www. on entries view 2015-09-29 22:59:44 +02:00
e75b215a25 fix Capistrano configuration 2015-09-29 22:13:07 +02:00
b0b352fc8e some cleanup on material theme 2015-09-29 20:26:32 +02:00
917040d4a0 Merge pull request #1434 from wallabag/travis-db
Add multiple database tests on Travis
2015-09-28 22:57:11 +02:00
fef4124130 Enabled Tidy on travis for HHVM 2015-09-28 20:35:56 +02:00
02d17813a1 Fix tests for all 2015-09-28 20:26:37 +02:00
159986c4fb Fix Postgres tests 2015-09-28 19:35:55 +02:00
da3d4998c0 Move readingTime & domainName in ContentProxy
So, everything is centralized in one place when we save a new entry.
2015-09-28 19:35:33 +02:00
4180fddac1 Merge pull request #1435 from wallabag/v2-enabled-user
Enabled created user from Config
2015-09-26 20:19:42 +02:00
34c2cc7a1a Use tecnickcom/tcpdf
tecnick.com/tcpdf is abandonned.
2015-09-26 19:51:26 +02:00
0f30f48b93 Enabled created user from Config
By default, creating user with FOSUser are disabled by default.

Fix #1423
2015-09-26 19:45:14 +02:00
609594fa5e Fix authentication error from Github
From: http://blog.wyrihaximus.net/2015/09/github-auth-token-on-travis/
2015-09-26 19:23:20 +02:00
dc22b46bf3 Fix HHVM & PHP 7.0 2015-09-26 19:06:32 +02:00
03fb6fde5f Add multiple database tests on Travis 2015-09-26 11:56:15 +02:00
5716249455 Merge pull request #1429 from wallabag/v2-language
V2 – Handle language in entry
2015-09-23 09:13:34 +02:00
d4ebe5c5dc Entries filter on language
+ updated deps
2015-09-23 07:55:55 +02:00
0d3bafdfdf Remove tab from baggy css
And add a border left on the filter slider
2015-09-23 07:51:17 +02:00
98f0929f16 Handle entry in language
Fix #1333
2015-09-22 20:52:13 +02:00
db96045a0a Adjust preview picture 2015-09-20 22:36:51 +02:00
287 changed files with 20086 additions and 11650 deletions

30
.gitignore vendored
View File

@ -1,25 +1,20 @@
# Cache and logs (Symfony2) # Cache, logs & sessions
/app/cache/* /var/*
/app/logs/* !/var/cache
!app/cache/.gitkeep
!app/logs/.gitkeep
# Cache and logs (Symfony3)
/var/cache/* /var/cache/*
/var/logs/*
!var/cache/.gitkeep !var/cache/.gitkeep
!/var/logs
/var/logs/*
!var/logs/.gitkeep !var/logs/.gitkeep
!/var/sessions
/var/sessions/*
!var/sessions/.gitkeep
!var/SymfonyRequirements.php
# Parameters # Parameters
/app/config/parameters.yml /app/config/parameters.yml
/app/config/parameters.ini
# Managed by Composer # Managed by Composer
/app/bootstrap.php.cache
/var/bootstrap.php.cache
/bin/*
!bin/console
!bin/symfony_requirements
/vendor/ /vendor/
# Assets and user uploads # Assets and user uploads
@ -36,3 +31,10 @@
# Data for wallabag # Data for wallabag
data/assets/* data/assets/*
data/db/wallabag*.sqlite data/db/wallabag*.sqlite
# Docker container logs and data
docker/logs/
docker/data/
# To avoid crazy stuff on some PR, we must manually FORCE ADD IT on each new release
composer.lock

View File

@ -4,6 +4,7 @@ filter:
excluded_paths: excluded_paths:
- 'vendor/*' - 'vendor/*'
- 'app/*' - 'app/*'
- 'var/*'
- 'web/*' - 'web/*'
- 'src/Wallabag/*Bundle/Tests/*' - 'src/Wallabag/*Bundle/Tests/*'
- '*Test.php' - '*Test.php'

View File

@ -3,6 +3,12 @@ language: php
# faster builds on docker-container setup # faster builds on docker-container setup
sudo: false sudo: false
# used for HHVM
addons:
apt:
packages:
- tidy
# cache vendor dirs # cache vendor dirs
cache: cache:
directories: directories:
@ -10,36 +16,36 @@ cache:
- $HOME/.composer/cache - $HOME/.composer/cache
php: php:
- 5.4
- 5.5 - 5.5
- 5.6 - 5.6
- 7.0
- hhvm - hhvm
- nightly
env:
- DB=mysql
- DB=pgsql
- DB=sqlite
matrix: matrix:
fast_finish: true fast_finish: true
exclude:
- php: hhvm
env: DB=pgsql # driver for PostgreSQL currently unsupported by HHVM, requires 3rd party dependency
allow_failures: allow_failures:
- php: hhvm - php: hhvm
- php: nightly
branches: branches:
only: only:
- v2 - v2
install: before_script:
- composer self-update - if [[ $TRAVIS_PHP_VERSION != hhvm ]]; then echo "memory_limit=-1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini; fi;
- if [[ $TRAVIS_PHP_VERSION != hhvm ]]; then phpenv config-rm xdebug.ini; fi;
# build coverage only on one build, to speed up results feedbacks - composer self-update --no-progress
# before_script: - if [ -n "$GH_TOKEN" ]; then composer config github-oauth.github.com ${GH_TOKEN}; fi;
# - if [[ "$TRAVIS_PHP_VERSION" = "5.6" ]]; then PHPUNIT_FLAGS="--coverage-clover=coverage.clover"; else PHPUNIT_FLAGS=""; fi; - if [[ "$DB" = "pgsql" ]]; then psql -c 'create database wallabag;' -U postgres; fi;
script: script:
- ant prepare - travis_wait composer update --no-interaction --no-progress
- bin/phpunit --exclude-group command-doctrine --debug $PHPUNIT_FLAGS - ant prepare-$DB
- bin/phpunit -v
# after_script:
# - |
# if [ $TRAVIS_PHP_VERSION = '5.6' ]; then
# wget https://scrutinizer-ci.com/ocular.phar
# php ocular.phar code-coverage:upload --format=php-clover coverage.clover
# fi

View File

@ -1,4 +1,4 @@
Copyright (c) 2013-2015 Nicolas Lœuillet Copyright (c) 2013-2016 Nicolas Lœuillet
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

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

22
Capfile
View File

@ -1,3 +1,6 @@
set :deploy_config_path, 'app/config/capistrano/deploy.rb'
set :stage_config_path, 'app/config/capistrano/deploy'
# Load DSL and set up stages # Load DSL and set up stages
require 'capistrano/setup' require 'capistrano/setup'
@ -6,24 +9,5 @@ require 'capistrano/deploy'
require 'capistrano/symfony' require 'capistrano/symfony'
# Include tasks from other gems included in your Gemfile
#
# For documentation on these, see for example:
#
# https://github.com/capistrano/rvm
# https://github.com/capistrano/rbenv
# https://github.com/capistrano/chruby
# https://github.com/capistrano/bundler
# https://github.com/capistrano/rails
# https://github.com/capistrano/passenger
#
# require 'capistrano/rvm'
# require 'capistrano/rbenv'
# require 'capistrano/chruby'
# require 'capistrano/bundler'
# require 'capistrano/rails/assets'
# require 'capistrano/rails/migrations'
# require 'capistrano/passenger'
# Load custom tasks from `lib/capistrano/tasks` if you have any defined # Load custom tasks from `lib/capistrano/tasks` if you have any defined
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r } Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }

View File

@ -1,53 +0,0 @@
# Guidelines for wallabag
If you want to contribute to wallabag, you have some rules to respect. These rules were defined by [PHP Framework Interop Group](http://www.php-fig.org).
## Basic Coding Standard (PSR-1)
This section of the standard comprises what should be considered the standard coding elements that are required to ensure a high level of technical interoperability between shared PHP code.
* Files MUST use only `<?php` and `<?=` tags.
* Files MUST use only UTF-8 without BOM for PHP code.
* Files SHOULD either declare symbols (classes, functions, constants, etc.) or cause side-effects (e.g. generate output, change .ini settings, etc.) but SHOULD NOT do both.
* Namespaces and classes MUST follow [PSR-0](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md).
* Class names MUST be declared in `StudlyCaps`.
* Class constants MUST be declared in all upper case with underscore separators.
* Method names MUST be declared in `camelCase`.
You can read details on [PHP FIG website](http://www.php-fig.org/psr/psr-1/).
## Coding Style Guide (PSR-2)
This guide extends and expands on PSR-1, the basic coding standard.
The intent of this guide is to reduce cognitive friction when scanning code from different authors. It does so by enumerating a shared set of rules and expectations about how to format PHP code.
The style rules herein are derived from commonalities among the various member projects. When various authors collaborate across multiple projects, it helps to have one set of guidelines to be used among all those projects. Thus, the benefit of this guide is not in the rules themselves, but in the sharing of those rules.
* Code MUST follow PSR-1.
* Code MUST use 4 spaces for indenting, not tabs.
* There MUST NOT be a hard limit on line length; the soft limit MUST be 120 characters; lines SHOULD be 80 characters or less.
* There MUST be one blank line after the `namespace` declaration, and there MUST be one blank line after the block of `use` declarations.
* Opening braces for classes MUST go on the next line, and closing braces MUST go on the next line after the body.
* Opening braces for methods MUST go on the next line, and closing braces MUST go on the next line after the body.
* Visibility MUST be declared on all properties and methods; `abstract` and `final` MUST be declared before the visibility; `static` MUST be declared after the visibility.
* Control structure keywords MUST have one space after them; method and function calls MUST NOT.
* Opening braces for control structures MUST go on the same line, and closing braces MUST go on the next line after the body.
* Opening parentheses for control structures MUST NOT have a space after them, and closing parentheses for control structures MUST NOT have a space before.
You can read details on [PHP FIG website](http://www.php-fig.org/psr/psr-2/).

View File

@ -2,25 +2,25 @@
[![Code Coverage](https://scrutinizer-ci.com/g/wallabag/wallabag/badges/coverage.png?b=v2)](https://scrutinizer-ci.com/g/wallabag/wallabag/?branch=v2) [![Code Coverage](https://scrutinizer-ci.com/g/wallabag/wallabag/badges/coverage.png?b=v2)](https://scrutinizer-ci.com/g/wallabag/wallabag/?branch=v2)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/wallabag/wallabag/badges/quality-score.png?b=v2)](https://scrutinizer-ci.com/g/wallabag/wallabag/?branch=v2) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/wallabag/wallabag/badges/quality-score.png?b=v2)](https://scrutinizer-ci.com/g/wallabag/wallabag/?branch=v2)
# What is wallabag ? # What is wallabag?
wallabag is a self hostable application allowing you to not miss any content anymore. wallabag is a self hostable application allowing you to not miss any content anymore.
Click, save, read it when you can. It extracts content so that you can read it when you have time. Click, save and read it when you can. It extracts content so that you can read it when you have time.
More informations on our website: [wallabag.org](http://wallabag.org) More informations on our website: [wallabag.org](http://wallabag.org)
# Want to test the v2 ? # Want to test the v2?
Keep in mind it's an **instable** branch, everything can be broken :) Keep in mind it's an **unstable** branch, everything can be broken :)
If you don't have it yet, please [install composer](https://getcomposer.org/download/). Then you can install wallabag by executing the following commands:
``` ```
git clone https://github.com/wallabag/wallabag.git -b v2 composer create-project wallabag/wallabag wallabag 2.0.*@alpha
cd wallabag php bin/console wallabag:install
composer install php bin/console server:run
php app/console wallabag:install
php app/console server:run
``` ```
## License ## License
Copyright © 2013-2015 Nicolas Lœuillet <nicolas@loeuillet.org> Copyright © 2013-2016 Nicolas Lœuillet <nicolas@loeuillet.org>
This work is free. You can redistribute it and/or modify it under the This work is free. You can redistribute it and/or modify it under the
terms of the MIT License. See the COPYING file for more details. terms of the MIT License. See the COPYING file for more details.

View File

@ -1,67 +0,0 @@
# How to manage translations for wallabag
This guide will describe the procedure of translation management of the wallabag web application.
All translations are made using [gettext](http://en.wikipedia.org/wiki/Gettext) system and tools.
You will need the [Poedit](http://www.poedit.net/download.php) editor to update, edit and create your translation files easily. However, you can also handle translations also without it: all can be done using gettext tools and your favorite plain text editor only. This guide, however, describes editing with Poedit. If you want to use gettext only, please refer to the xgettext manual page to update po files from sources (see also how it is used by Poedit below) and use msgunfmt tool to compile .mo files manually.
You need to know, that translation phrases are stored in **".po"** files (for example: `locale/pl_PL.utf8/LC_MESSAGES/pl_PL.utf8.po`), which are then complied in **".mo"** files using **msgfmt** gettext tool or by Poedit, which will run msgfmt for you in background.
**It's assumed, that you have wallabag installed locally on your computer or on the server you have access to.**
## To change existing translation you will need to do:
### 1. Clear cache
You can do this using **http://your-wallabag-host.com/?empty-cache** link (replace http://your-wallabag-host.com/ with real url of your wallabag application)
OR
from command line:
go to root of your installation of wallabag project and run next command:
`rm -rf ./cache/*`
(this may require root privileges if you run, for example Apache web server with mod_php)
### 2. Generate php files from all twig templates
Do this using next command:
`php ./locale/tools/fillCache.php`
OR
from your browser: **http://your-wallabag-host.com/locale/tools/fillCache.php** (this may require removal of .htaccess file in locale/ directory).
### 3. Configure your Poedit
Open Poedit editor, open Edit->Preferences. Go to "Parsers" tab, click on PHP and press "Edit" button. Make sure your "Parser command:" looks like
`xgettext --no-location --force-po -o %o %C %K %F`
Usually it is required to add "--no-location" to default value.
### 4. Open .po file you want to edit in Poedit and change its settings
Open, for example `locale/pl_PL.utf8/LC_MESSAGES/pl_PL.utf8.po` file in your Poedit.
Go to "Catalog"->"Settings..." menu. Then go to "Path" tab and add path to wallabag installation in your local file system. This step can't be omitted as you will not be able to update phrases otherwise.
You can also check "project into" tab to be sure, that "Language" is set correctly (this will allow you to spell check your translation).
### 5. Update opened .po file from sources
Once you have set your path correctly, you are able to update phrases from sources. Press "Update catalog - synchronize it with sources" button or go to "Catalog"->"Update from sources" menu.
As a result you will see confirmation popup with two tabs: "New strings" and "Obsolete strings". Please review and accept changes (or press "Undo" if you see too many obsolete strings, as Poedit will remove them all - in this case please make sure all previous steps are performed w/o errors).
### 6. Translate and save your .po file
If you have any difficulties on this step, please consult with Poedit manual.
Every time you save your .po file, Poedit will also compile appropriate .mo file by default (of course, if not disabled in preferences).
You are now almost done.
### 7. Clear cache again
This step may be required if your web server runs php scripts in name of, say, www user (i.e. Apache with mod_php, not cgi).
##To create new translation
You just have to copy the folder corresponding to the language you want to translate from, change language in the project settings and for the folder and files names. Then start replacing all existing translations with your own.

71
Vagrantfile vendored
View File

@ -1,71 +0,0 @@
$script_sqlite = <<SCRIPT
apt-get update
apt-get install -y apache2 php5 php5-sqlite php5-xdebug
apt-get clean -y
echo "ServerName localhost" >> /etc/apache2/apache2.conf
service apache2 restart
rm -f /var/www/html/index.html
date > /etc/vagrant_provisioned_at
SCRIPT
$script_mysql = <<SCRIPT
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y apache2 php5 php5-mysql php5-xdebug mysql-server mysql-client
apt-get clean -y
echo "ServerName localhost" >> /etc/apache2/apache2.conf
service apache2 restart
service mysql restart
echo "create database wallabag;" | mysql -u root
rm -f /var/www/html/index.html
date > /etc/vagrant_provisioned_at
SCRIPT
$script_postgres = <<SCRIPT
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y apache2 php5 php5-pgsql php5-xdebug postgresql postgresql-contrib
apt-get clean -y
echo "ServerName localhost" >> /etc/apache2/apache2.conf
service apache2 restart
service postgresql restart
rm -f /var/www/html/index.html
date > /etc/vagrant_provisioned_at
SCRIPT
Vagrant.configure("2") do |config|
config.vm.define "sqlite" do |m|
m.vm.box = "ubuntu/trusty64"
m.vm.provision "shell", inline: $script_sqlite
m.vm.synced_folder ".", "/var/www/html", owner: "www-data", group: "www-data"
end
config.vm.define "mysql" do |m|
m.vm.box = "ubuntu/trusty64"
m.vm.provision "shell", inline: $script_mysql
m.vm.synced_folder ".", "/var/www/html", owner: "www-data", group: "www-data"
end
config.vm.define "postgres" do |m|
m.vm.box = "ubuntu/trusty64"
m.vm.provision "shell", inline: $script_postgres
m.vm.synced_folder ".", "/var/www/html", owner: "www-data", group: "www-data"
end
config.vm.define "debian7" do |m|
m.vm.box = "chef/debian-7.6"
m.vm.provision "shell", inline: $script_sqlite
m.vm.synced_folder ".", "/var/www", owner: "www-data", group: "www-data"
end
config.vm.define "debian6" do |m|
m.vm.box = "chef/debian-6.0.10"
m.vm.provision "shell", inline: $script_sqlite
m.vm.synced_folder ".", "/var/www", owner: "www-data", group: "www-data"
end
config.vm.network :forwarded_port, guest: 80, host: 8003
#config.vm.network "public_network", :bridge => "en0: Wi-Fi (AirPort)"
end

View File

@ -1,7 +1,5 @@
<?php <?php
require_once __DIR__.'/AppKernel.php';
use Symfony\Bundle\FrameworkBundle\HttpCache\HttpCache; use Symfony\Bundle\FrameworkBundle\HttpCache\HttpCache;
class AppCache extends HttpCache class AppCache extends HttpCache

View File

@ -7,7 +7,7 @@ class AppKernel extends Kernel
{ {
public function registerBundles() public function registerBundles()
{ {
$bundles = array( $bundles = [
new Symfony\Bundle\FrameworkBundle\FrameworkBundle(), new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
new Symfony\Bundle\SecurityBundle\SecurityBundle(), new Symfony\Bundle\SecurityBundle\SecurityBundle(),
new Symfony\Bundle\TwigBundle\TwigBundle(), new Symfony\Bundle\TwigBundle\TwigBundle(),
@ -26,9 +26,16 @@ class AppKernel extends Kernel
new Wallabag\ApiBundle\WallabagApiBundle(), new Wallabag\ApiBundle\WallabagApiBundle(),
new Bazinga\Bundle\HateoasBundle\BazingaHateoasBundle(), new Bazinga\Bundle\HateoasBundle\BazingaHateoasBundle(),
new Lexik\Bundle\FormFilterBundle\LexikFormFilterBundle(), new Lexik\Bundle\FormFilterBundle\LexikFormFilterBundle(),
); new FOS\OAuthServerBundle\FOSOAuthServerBundle(),
new Wallabag\UserBundle\WallabagUserBundle(),
new Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle(),
new Scheb\TwoFactorBundle\SchebTwoFactorBundle(),
new KPhoen\RulerZBundle\KPhoenRulerZBundle(),
new Wallabag\ImportBundle\WallabagImportBundle(),
new Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle(),
];
if (in_array($this->getEnvironment(), array('dev', 'test'))) { if (in_array($this->getEnvironment(), ['dev', 'test'], true)) {
$bundles[] = new Symfony\Bundle\DebugBundle\DebugBundle(); $bundles[] = new Symfony\Bundle\DebugBundle\DebugBundle();
$bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle(); $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
$bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle(); $bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle();
@ -39,8 +46,23 @@ class AppKernel extends Kernel
return $bundles; return $bundles;
} }
public function getRootDir()
{
return __DIR__;
}
public function getCacheDir()
{
return dirname(__DIR__).'/var/cache/'.$this->getEnvironment();
}
public function getLogDir()
{
return dirname(__DIR__).'/var/logs';
}
public function registerContainerConfiguration(LoaderInterface $loader) public function registerContainerConfiguration(LoaderInterface $loader)
{ {
$loader->load(__DIR__.'/config/config_'.$this->getEnvironment().'.yml'); $loader->load($this->getRootDir().'/config/config_'.$this->getEnvironment().'.yml');
} }
} }

View File

@ -8,6 +8,6 @@ use Composer\Autoload\ClassLoader;
*/ */
$loader = require __DIR__.'/../vendor/autoload.php'; $loader = require __DIR__.'/../vendor/autoload.php';
AnnotationRegistry::registerLoader(array($loader, 'loadClass')); AnnotationRegistry::registerLoader([$loader, 'loadClass']);
return $loader; return $loader;

View File

@ -0,0 +1,23 @@
# config valid only for current version of Capistrano
lock '3.4.0'
set :application, 'wallabag'
set :repo_url, 'git@github.com:wallabag/wallabag.git'
set :ssh_user, 'framasoft_bag'
server '78.46.248.87', user: fetch(:ssh_user), roles: %w{web app db}
set :scm, :git
set :format, :pretty
set :log_level, :info
# set :log_level, :debug
set :composer_install_flags, '--no-dev --prefer-dist --no-interaction --optimize-autoloader'
set :linked_files, %w{app/config/parameters.yml}
set :linked_dirs, %w{app/logs web/uploads data}
set :keep_releases, 3
after 'deploy:finishing', 'deploy:cleanup'

View File

@ -0,0 +1,2 @@
set :branch, 'v2'
set :deploy_to, '/var/www/v2.wallabag.org/web/'

View File

@ -21,9 +21,21 @@ framework:
trusted_proxies: ~ trusted_proxies: ~
session: session:
# handler_id set to null will use default session handler from php.ini # handler_id set to null will use default session handler from php.ini
handler_id: ~ handler_id: session.handler.native_file
save_path: "%kernel.root_dir%/../var/sessions/%kernel.environment%"
fragments: ~ fragments: ~
http_method_override: true http_method_override: true
assets: ~
wallabag_core:
languages:
en: 'English'
fr: 'Français'
de: 'Deutsch'
wallabag_import:
allow_mimetypes: ['application/octet-stream', 'application/json', 'text/plain']
resource_dir: "%kernel.root_dir%/../web/uploads/import"
# Twig Configuration # Twig Configuration
twig: twig:
@ -45,12 +57,11 @@ twig:
export_mobi: %export_mobi% export_mobi: %export_mobi%
export_pdf: %export_pdf% export_pdf: %export_pdf%
version: %app.version% version: %app.version%
twofactor_auth: %twofactor_auth%
warning_message: %warning_message% warning_message: %warning_message%
paypal_url: "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9UBA65LG3FX9Y&lc=gb" paypal_url: "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9UBA65LG3FX9Y&lc=gb"
flattr_url: "https://flattr.com/thing/1265480" form_themes:
form: - "LexikFormFilterBundle:Form:form_div_layout.html.twig"
resources:
- LexikFormFilterBundle:Form:form_div_layout.html.twig
# Assetic Configuration # Assetic Configuration
assetic: assetic:
@ -81,9 +92,22 @@ doctrine:
auto_generate_proxy_classes: "%kernel.debug%" auto_generate_proxy_classes: "%kernel.debug%"
entity_managers: entity_managers:
default: default:
naming_strategy: wallabag_core.doctrine.prefixed_naming_strategy
auto_mapping: true auto_mapping: true
stof_doctrine_extensions:
default_locale: "%locale%"
translation_fallback: true
orm:
default:
tree: true
sluggable: true
doctrine_migrations:
dir_name: "%kernel.root_dir%/DoctrineMigrations"
namespace: Application\Migrations
table_name: migration_versions
name: Application Migrations
# Swiftmailer Configuration # Swiftmailer Configuration
swiftmailer: swiftmailer:
transport: "%mailer_transport%" transport: "%mailer_transport%"
@ -110,7 +134,10 @@ fos_rest:
routing_loader: routing_loader:
default_format: json default_format: json
nelmio_api_doc: ~ nelmio_api_doc:
sandbox:
enabled: false
name: wallabag API documentation
nelmio_cors: nelmio_cors:
defaults: defaults:
@ -144,16 +171,39 @@ liip_theme:
autodetect_theme: wallabag_core.helper.detect_active_theme autodetect_theme: wallabag_core.helper.detect_active_theme
path_patterns: path_patterns:
# app_resource:
# - %%app_path%%/views/themes/%%current_theme%%/%%template%%
# - %%app_path%%/views/%%template%%
bundle_resource: bundle_resource:
- %%bundle_path%%/Resources/views/themes/%%current_theme%%/%%template%% - %%bundle_path%%/Resources/views/themes/%%current_theme%%/%%template%%
# bundle_resource_dir:
# - %%dir%%/views/themes/%%current_theme%%/%%bundle_name%%/%%template%%
# - %%dir%%/views/%%bundle_name%%/%%override_path%%
fos_user: fos_user:
db_driver: orm db_driver: orm
firewall_name: main firewall_name: main
user_class: Wallabag\CoreBundle\Entity\User user_class: Wallabag\UserBundle\Entity\User
registration:
confirmation:
enabled: true
fos_oauth_server:
db_driver: orm
client_class: Wallabag\ApiBundle\Entity\Client
access_token_class: Wallabag\ApiBundle\Entity\AccessToken
refresh_token_class: Wallabag\ApiBundle\Entity\RefreshToken
auth_code_class: Wallabag\ApiBundle\Entity\AuthCode
service:
user_provider: fos_user.user_manager
scheb_two_factor:
trusted_computer:
enabled: true
cookie_name: wllbg_trusted_computer
cookie_lifetime: 2592000
email:
enabled: %twofactor_auth%
sender_email: %twofactor_sender%
digits: 6
template: WallabagUserBundle:Authentication:form.html.twig
mailer: wallabag_user.auth_code_mailer
kphoen_rulerz:
executors:
doctrine: true

View File

@ -17,13 +17,14 @@ monolog:
type: stream type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log" path: "%kernel.logs_dir%/%kernel.environment%.log"
level: debug level: debug
channels: [!event]
console: console:
type: console type: console
bubble: false bubble: false
verbosity_levels: verbosity_levels:
VERBOSITY_VERBOSE: INFO VERBOSITY_VERBOSE: INFO
VERBOSITY_VERY_VERBOSE: DEBUG VERBOSITY_VERY_VERBOSE: DEBUG
channels: ["!doctrine"] channels: [!event, !doctrine]
console_very_verbose: console_very_verbose:
type: console type: console
bubble: false bubble: false
@ -31,15 +32,7 @@ monolog:
VERBOSITY_VERBOSE: NOTICE VERBOSITY_VERBOSE: NOTICE
VERBOSITY_VERY_VERBOSE: NOTICE VERBOSITY_VERY_VERBOSE: NOTICE
VERBOSITY_DEBUG: DEBUG VERBOSITY_DEBUG: DEBUG
channels: ["doctrine"] channels: [doctrine]
# uncomment to get logging in your browser
# you may have to allow bigger header sizes in your Web server configuration
#firephp:
# type: firephp
# level: info
#chromephp:
# type: chromephp
# level: info
assetic: assetic:
use_controller: true use_controller: true
@ -49,6 +42,3 @@ swiftmailer:
transport: smtp transport: smtp
host: 'localhost' host: 'localhost'
port: 1025 port: 1025
username: null
password: null

View File

@ -17,11 +17,6 @@ monolog:
type: fingers_crossed type: fingers_crossed
action_level: error action_level: error
handler: nested handler: nested
wsse:
type: stream
path: %kernel.logs_dir%/%kernel.environment%.wsse.log
level: error
channels: [wsse]
nested: nested:
type: stream type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log" path: "%kernel.logs_dir%/%kernel.environment%.log"

View File

@ -19,9 +19,14 @@ swiftmailer:
doctrine: doctrine:
dbal: dbal:
driver: pdo_sqlite driver: "%test_database_driver%"
path: %kernel.root_dir%/../data/db/wallabag_test.sqlite host: "%test_database_host%"
host: localhost port: "%test_database_port%"
dbname: "%test_database_name%"
user: "%test_database_user%"
password: "%test_database_password%"
charset: UTF8
path: "%test_database_path%"
orm: orm:
metadata_cache_driver: metadata_cache_driver:
type: service type: service

View File

@ -1,5 +1,15 @@
# This file is a "template" of what your parameters.yml file should look like # This file is a "template" of what your parameters.yml file should look like
parameters: parameters:
# Uncomment these settings or manually update your parameters.yml
# to use docker-compose
#
# database_driver: %env.database_driver%
# database_host: %env.database_host%
# database_port: %env.database_port%
# database_name: %env.database_name%
# database_user: %env.database_user%
# database_password: %env.database_password%
database_driver: pdo_sqlite database_driver: pdo_sqlite
database_host: 127.0.0.1 database_host: 127.0.0.1
database_port: ~ database_port: ~
@ -9,6 +19,14 @@ parameters:
database_path: "%kernel.root_dir%/../data/db/wallabag.sqlite" database_path: "%kernel.root_dir%/../data/db/wallabag.sqlite"
database_table_prefix: wallabag_ database_table_prefix: wallabag_
test_database_driver: pdo_sqlite
test_database_host: 127.0.0.1
test_database_port: ~
test_database_name: ~
test_database_user: ~
test_database_password: ~
test_database_path: "%kernel.root_dir%/../data/db/wallabag_test.sqlite"
mailer_transport: smtp mailer_transport: smtp
mailer_host: 127.0.0.1 mailer_host: 127.0.0.1
mailer_user: ~ mailer_user: ~
@ -21,6 +39,8 @@ parameters:
# wallabag misc # wallabag misc
app.version: 2.0.0-alpha app.version: 2.0.0-alpha
twofactor_auth: true
twofactor_sender: no-reply@wallabag.org
# message to display at the bottom of the page # message to display at the bottom of the page
warning_message: > warning_message: >
@ -41,10 +61,15 @@ parameters:
export_epub: true export_epub: true
export_mobi: true export_mobi: true
export_pdf: true export_pdf: true
wallabag_url: http://v2.wallabag.org
wallabag_support_url: 'https://www.wallabag.org/pages/support.html'
# default user config # default user config
items_on_page: 12 items_on_page: 12
theme: material theme: material
language: en_US language: en
from_email: no-reply@wallabag.org from_email: no-reply@wallabag.org
rss_limit: 50 rss_limit: 50
# pocket import
pocket_consumer_key: xxxxxxxx

View File

@ -1,32 +1,33 @@
wallabag_import:
resource: "@WallabagImportBundle/Controller/"
type: annotation
prefix: /import
wallabag_api: wallabag_api:
resource: "@WallabagApiBundle/Resources/config/routing.yml" resource: "@WallabagApiBundle/Resources/config/routing.yml"
prefix: / prefix: /
app: app:
resource: @WallabagCoreBundle/Controller/ resource: "@WallabagCoreBundle/Controller/"
type: annotation type: annotation
doc-api: doc-api:
resource: "@NelmioApiDocBundle/Resources/config/routing.yml" resource: "@NelmioApiDocBundle/Resources/config/routing.yml"
prefix: /api/doc prefix: /api/doc
login:
pattern: /login
defaults: { _controller: WallabagCoreBundle:Security:login }
login_check:
pattern: /login_check
logout:
path: /logout
rest : rest :
type : rest type : rest
resource : "routing_rest.yml" resource : "routing_rest.yml"
prefix : /api prefix : /api
homepage: homepage:
pattern: "/{page}" path: "/{page}"
defaults: { _controller: WallabagCoreBundle:Entry:showUnread, page : 1 } defaults: { _controller: WallabagCoreBundle:Entry:showUnread, page : 1 }
requirements: requirements:
page: \d+ page: \d+
fos_user:
resource: "@FOSUserBundle/Resources/config/routing/all.xml"
fos_oauth_server_token:
resource: "@FOSOAuthServerBundle/Resources/config/routing/token.xml"

View File

@ -6,10 +6,6 @@ _profiler:
resource: "@WebProfilerBundle/Resources/config/routing/profiler.xml" resource: "@WebProfilerBundle/Resources/config/routing/profiler.xml"
prefix: /_profiler prefix: /_profiler
_configurator:
resource: "@SensioDistributionBundle/Resources/config/routing/webconfigurator.xml"
prefix: /_configurator
_errors: _errors:
resource: "@TwigBundle/Resources/config/routing/errors.xml" resource: "@TwigBundle/Resources/config/routing/errors.xml"
prefix: /_error prefix: /_error

View File

@ -1,9 +1,6 @@
security: security:
encoders: encoders:
Wallabag\CoreBundle\Entity\User: FOS\UserBundle\Model\UserInterface: sha512
algorithm: sha1
encode_as_base64: false
iterations: 1
role_hierarchy: role_hierarchy:
ROLE_ADMIN: ROLE_USER ROLE_ADMIN: ROLE_USER
@ -11,18 +8,28 @@ security:
providers: providers:
administrators: administrators:
entity: { class: WallabagCoreBundle:User, property: username } entity: { class: WallabagUserBundle:User, property: username }
fos_userbundle: fos_userbundle:
id: fos_user.user_provider.username id: fos_user.user_provider.username
# the main part of the security, where you can set up firewalls # the main part of the security, where you can set up firewalls
# for specific sections of your app # for specific sections of your app
firewalls: firewalls:
wsse_secured: # disables authentication for assets and the profiler, adapt it according to your needs
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
oauth_token:
pattern: ^/oauth/v2/token
security: false
api:
pattern: /api/.* pattern: /api/.*
wsse: true fos_oauth: true
stateless: true stateless: true
anonymous: true anonymous: true
login_firewall: login_firewall:
pattern: ^/login$ pattern: ^/login$
anonymous: ~ anonymous: ~
@ -31,11 +38,11 @@ security:
pattern: ^/ pattern: ^/
form_login: form_login:
provider: fos_userbundle provider: fos_userbundle
csrf_provider: security.csrf.token_manager csrf_token_generator: security.csrf.token_manager
anonymous: true anonymous: true
remember_me: remember_me:
key: "%secret%" secret: "%secret%"
lifetime: 31536000 lifetime: 31536000
path: / path: /
domain: ~ domain: ~
@ -45,9 +52,9 @@ security:
target: / target: /
access_control: access_control:
- { path: ^/api/salt, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/doc, roles: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/api/doc, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/forgot-password, roles: IS_AUTHENTICATED_ANONYMOUSLY } - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: /(unread|starred|archive).xml$, roles: IS_AUTHENTICATED_ANONYMOUSLY } - { path: /(unread|starred|archive).xml$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, roles: ROLE_USER } - { path: ^/, roles: ROLE_USER }

View File

@ -1,9 +1,4 @@
# Learn more about services, parameters and containers at
# http://symfony.com/doc/current/book/service_container.html
parameters: parameters:
security.authentication.provider.dao.class: Wallabag\CoreBundle\Security\Authentication\Provider\WallabagAuthenticationProvider
security.encoder.digest.class: Wallabag\CoreBundle\Security\Authentication\Encoder\WallabagPasswordEncoder
security.validator.user_password.class: Wallabag\CoreBundle\Security\Validator\WallabagUserPasswordValidator
lexik_form_filter.get_filter.doctrine_orm.class: Wallabag\CoreBundle\Event\Subscriber\CustomDoctrineORMSubscriber lexik_form_filter.get_filter.doctrine_orm.class: Wallabag\CoreBundle\Event\Subscriber\CustomDoctrineORMSubscriber
services: services:
@ -17,3 +12,21 @@ services:
class: Twig_Extensions_Extension_Text class: Twig_Extensions_Extension_Text
tags: tags:
- { name: twig.extension } - { name: twig.extension }
wallabag.twig_extension:
class: Wallabag\CoreBundle\Twig\WallabagExtension
public: false
tags:
- { name: twig.extension }
wallabag.locale_listener:
class: Wallabag\CoreBundle\EventListener\LocaleListener
arguments: ["%kernel.default_locale%"]
tags:
- { name: kernel.event_subscriber }
wallabag.user_locale_listener:
class: Wallabag\CoreBundle\EventListener\UserLocaleListener
arguments: ["@session"]
tags:
- { name: kernel.event_listener, event: security.interactive_login, method: onInteractiveLogin }

View File

@ -0,0 +1,65 @@
# This file is a "template" of what your parameters.yml file should look like
parameters:
database_driver: pdo_sqlite
database_host: 127.0.0.1
database_port: ~
database_name: symfony
database_user: root
database_password: ~
database_path: "%kernel.root_dir%/../data/db/wallabag.sqlite"
database_table_prefix: wallabag_
test_database_driver: pdo_mysql
test_database_host: localhost
test_database_port: 3306
test_database_name: wallabag
test_database_user: root
test_database_password: ~
test_database_path: ~
mailer_transport: smtp
mailer_host: 127.0.0.1
mailer_user: ~
mailer_password: ~
locale: en
# A secret key that's used to generate certain security-related tokens
secret: ThisTokenIsNotSoSecretChangeIt
# wallabag misc
app.version: 2.0.0-alpha
twofactor_auth: true
twofactor_sender: no-reply@wallabag.org
# message to display at the bottom of the page
warning_message: >
You're trying wallabag v2, which is in alpha version. If you find a bug, please have a look to <a href="https://github.com/wallabag/wallabag/issues">our issues list</a> and <a href="https://github.com/wallabag/wallabag/issues/new">open a new if necessary</a>
download_pictures: false # if true, pictures will be stored into data/assets for each article
# Entry view
share_twitter: true
share_mail: true
share_shaarli: true
shaarli_url: http://myshaarli.com
share_diaspora: true
diaspora_url: http://diasporapod.com
flattr: true
carrot: true
show_printlink: true
export_epub: true
export_mobi: true
export_pdf: true
wallabag_url: http://v2.wallabag.org
wallabag_support_url: 'https://www.wallabag.org/pages/support.html'
# default user config
items_on_page: 12
theme: material
language: en_US
from_email: no-reply@wallabag.org
rss_limit: 50
# pocket import
pocket_consumer_key: xxxxxxxx

View File

@ -0,0 +1,65 @@
# This file is a "template" of what your parameters.yml file should look like
parameters:
database_driver: pdo_sqlite
database_host: 127.0.0.1
database_port: ~
database_name: symfony
database_user: root
database_password: ~
database_path: "%kernel.root_dir%/../data/db/wallabag.sqlite"
database_table_prefix: wallabag_
test_database_driver: pdo_pgsql
test_database_host: localhost
test_database_port:
test_database_name: wallabag
test_database_user: travis
test_database_password: ~
test_database_path: ~
mailer_transport: smtp
mailer_host: 127.0.0.1
mailer_user: ~
mailer_password: ~
locale: en
# A secret key that's used to generate certain security-related tokens
secret: ThisTokenIsNotSoSecretChangeIt
# wallabag misc
app.version: 2.0.0-alpha
twofactor_auth: true
twofactor_sender: no-reply@wallabag.org
# message to display at the bottom of the page
warning_message: >
You're trying wallabag v2, which is in alpha version. If you find a bug, please have a look to <a href="https://github.com/wallabag/wallabag/issues">our issues list</a> and <a href="https://github.com/wallabag/wallabag/issues/new">open a new if necessary</a>
download_pictures: false # if true, pictures will be stored into data/assets for each article
# Entry view
share_twitter: true
share_mail: true
share_shaarli: true
shaarli_url: http://myshaarli.com
share_diaspora: true
diaspora_url: http://diasporapod.com
flattr: true
carrot: true
show_printlink: true
export_epub: true
export_mobi: true
export_pdf: true
wallabag_url: http://v2.wallabag.org
wallabag_support_url: 'https://www.wallabag.org/pages/support.html'
# default user config
items_on_page: 12
theme: material
language: en_US
from_email: no-reply@wallabag.org
rss_limit: 50
# pocket import
pocket_consumer_key: xxxxxxxx

View File

@ -0,0 +1,65 @@
# This file is a "template" of what your parameters.yml file should look like
parameters:
database_driver: pdo_sqlite
database_host: 127.0.0.1
database_port: ~
database_name: symfony
database_user: root
database_password: ~
database_path: "%kernel.root_dir%/../data/db/wallabag.sqlite"
database_table_prefix: wallabag_
test_database_driver: pdo_sqlite
test_database_host: localhost
test_database_port:
test_database_name: ~
test_database_user: ~
test_database_password: ~
test_database_path: "%kernel.root_dir%/../data/db/wallabag_test.sqlite"
mailer_transport: smtp
mailer_host: 127.0.0.1
mailer_user: ~
mailer_password: ~
locale: en
# A secret key that's used to generate certain security-related tokens
secret: ThisTokenIsNotSoSecretChangeIt
# wallabag misc
app.version: 2.0.0-alpha
twofactor_auth: true
twofactor_sender: no-reply@wallabag.org
# message to display at the bottom of the page
warning_message: >
You're trying wallabag v2, which is in alpha version. If you find a bug, please have a look to <a href="https://github.com/wallabag/wallabag/issues">our issues list</a> and <a href="https://github.com/wallabag/wallabag/issues/new">open a new if necessary</a>
download_pictures: false # if true, pictures will be stored into data/assets for each article
# Entry view
share_twitter: true
share_mail: true
share_shaarli: true
shaarli_url: http://myshaarli.com
share_diaspora: true
diaspora_url: http://diasporapod.com
flattr: true
carrot: true
show_printlink: true
export_epub: true
export_mobi: true
export_pdf: true
wallabag_url: http://v2.wallabag.org
wallabag_support_url: 'https://www.wallabag.org/pages/support.html'
# default user config
items_on_page: 12
theme: material
language: en_US
from_email: no-reply@wallabag.org
rss_limit: 50
# pocket import
pocket_consumer_key: xxxxxxxx

View File

@ -1,22 +1,24 @@
#!/usr/bin/env php #!/usr/bin/env php
<?php <?php
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Debug\Debug;
// if you don't want to setup permissions the proper way, just uncomment the following PHP line // if you don't want to setup permissions the proper way, just uncomment the following PHP line
// read http://symfony.com/doc/current/book/installation.html#configuration-and-setup for more information // read http://symfony.com/doc/current/book/installation.html#configuration-and-setup for more information
//umask(0000); //umask(0000);
set_time_limit(0); set_time_limit(0);
require_once __DIR__.'/bootstrap.php.cache'; /**
require_once __DIR__.'/AppKernel.php'; * @var Composer\Autoload\ClassLoader $loader
*/
use Symfony\Bundle\FrameworkBundle\Console\Application; $loader = require __DIR__.'/../app/autoload.php';
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Debug\Debug;
$input = new ArgvInput(); $input = new ArgvInput();
$env = $input->getParameterOption(array('--env', '-e'), getenv('SYMFONY_ENV') ?: 'dev'); $env = $input->getParameterOption(['--env', '-e'], getenv('SYMFONY_ENV') ?: 'dev');
$debug = getenv('SYMFONY_DEBUG') !== '0' && !$input->hasParameterOption(array('--no-debug', '')) && $env !== 'prod'; $debug = getenv('SYMFONY_DEBUG') !== '0' && !$input->hasParameterOption(['--no-debug', '']) && $env !== 'prod';
if ($debug) { if ($debug) {
Debug::enable(); Debug::enable();

1
bin/doctrine Symbolic link
View File

@ -0,0 +1 @@
../vendor/doctrine/orm/bin/doctrine

1
bin/doctrine-dbal Symbolic link
View File

@ -0,0 +1 @@
../vendor/doctrine/dbal/bin/doctrine-dbal

1
bin/doctrine-migrations Symbolic link
View File

@ -0,0 +1 @@
../vendor/doctrine/migrations/bin/doctrine-migrations

1
bin/doctrine.php Symbolic link
View File

@ -0,0 +1 @@
../vendor/doctrine/orm/bin/doctrine.php

Binary file not shown.

1
bin/security-checker Symbolic link
View File

@ -0,0 +1 @@
../vendor/sensiolabs/security-checker/security-checker

3
app/check.php → bin/symfony_requirements Normal file → Executable file
View File

@ -1,6 +1,7 @@
#!/usr/bin/env php
<?php <?php
require_once dirname(__FILE__).'/SymfonyRequirements.php'; require_once dirname(__FILE__).'/../var/SymfonyRequirements.php';
$lineSize = 70; $lineSize = 70;
$symfonyRequirements = new SymfonyRequirements(); $symfonyRequirements = new SymfonyRequirements();

View File

@ -1,46 +1,86 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project name="wallabag" default="build"> <project name="wallabag" default="build">
<target name="build" depends="prepare"/> <target name="build" depends="clean,prepare,phpunit"/>
<target name="prepare-mysql" depends="clean,db_mysql,prepare"/>
<target name="prepare-sqlite" depends="clean,db_sqlite,prepare"/>
<target name="prepare-pgsql" depends="clean,db_pgsql,prepare"/>
<target name="clean" description="Cleanup build artifacts"> <target name="clean" description="Cleanup build artifacts">
<delete dir="${basedir}/app/cache"/> <delete dir="${basedir}/var/cache"/>
</target> </target>
<target name="prepare" depends="clean" description="Prepare for build"> <target name="prepare" description="Prepare for build">
<exec executable="composer">
<arg value="install"/>
<arg value="--no-interaction"/>
<arg value="--no-progress"/>
</exec>
<exec executable="php"> <exec executable="php">
<arg value="${basedir}/app/console"/> <arg value="${basedir}/bin/console"/>
<arg value="doctrine:database:drop"/> <arg value="doctrine:database:drop"/>
<arg value="--force"/> <arg value="--force"/>
<arg value="--env=test"/> <arg value="--env=test"/>
</exec> </exec>
<exec executable="php"> <exec executable="php">
<arg value="${basedir}/app/console"/> <arg value="${basedir}/bin/console"/>
<arg value="doctrine:database:create"/> <arg value="doctrine:database:create"/>
<arg value="--env=test"/> <arg value="--env=test"/>
</exec> </exec>
<exec executable="php"> <exec executable="php">
<arg value="${basedir}/app/console"/> <arg value="${basedir}/bin/console"/>
<arg value="doctrine:schema:create"/> <arg value="doctrine:schema:create"/>
<arg value="--env=test"/> <arg value="--env=test"/>
</exec> </exec>
<exec executable="php"> <exec executable="php">
<arg value="${basedir}/app/console"/> <arg value="${basedir}/bin/console"/>
<arg value="cache:clear"/> <arg value="cache:clear"/>
<arg value="--env=test"/> <arg value="--env=test"/>
</exec> </exec>
<exec executable="php"> <exec executable="php">
<arg value="${basedir}/app/console"/> <arg value="${basedir}/bin/console"/>
<arg value="doctrine:fixtures:load"/> <arg value="doctrine:fixtures:load"/>
<arg value="--no-interaction"/> <arg value="--no-interaction"/>
<arg value="--env=test"/> <arg value="--env=test"/>
</exec> </exec>
</target> </target>
<target name="db_mysql" description="Run test for MySQL">
<delete dir="${basedir}/app/config/parameters.yml"/>
<exec executable="cp">
<arg value="${basedir}/app/config/tests/parameters.yml.dist.mysql"/>
<arg value="${basedir}/app/config/parameters.yml"/>
</exec>
<exec executable="php">
<arg value="${basedir}/bin/console"/>
<arg value="cache:clear"/>
<arg value="--env=test"/>
</exec>
</target>
<target name="db_sqlite" description="Run test for SQLite">
<delete dir="${basedir}/app/config/parameters.yml"/>
<exec executable="cp">
<arg value="${basedir}/app/config/tests/parameters.yml.dist.sqlite"/>
<arg value="${basedir}/app/config/parameters.yml"/>
</exec>
<exec executable="php">
<arg value="${basedir}/bin/console"/>
<arg value="cache:clear"/>
<arg value="--env=test"/>
</exec>
</target>
<target name="db_pgsql" description="Run test for PostgreSQL">
<delete dir="${basedir}/app/config/parameters.yml"/>
<exec executable="cp">
<arg value="${basedir}/app/config/tests/parameters.yml.dist.pgsql"/>
<arg value="${basedir}/app/config/parameters.yml"/>
</exec>
<exec executable="php">
<arg value="${basedir}/bin/console"/>
<arg value="cache:clear"/>
<arg value="--env=test"/>
</exec>
</target>
<target name="phpunit" description="Run unit tests with PHPUnit + HTML Coverage"> <target name="phpunit" description="Run unit tests with PHPUnit + HTML Coverage">
<exec executable="phpunit" failonerror="true"> <exec executable="phpunit" failonerror="true">
<arg value="--coverage-html"/> <arg value="--coverage-html"/>

View File

@ -28,64 +28,88 @@
"issues": "https://github.com/wallabag/wallabag/issues" "issues": "https://github.com/wallabag/wallabag/issues"
}, },
"require": { "require": {
"php": ">=5.3.3", "php": ">=5.5.9",
"symfony/symfony": "~2.7.0", "symfony/symfony": "3.0.*",
"doctrine/orm": "~2.3", "doctrine/orm": "^2.5",
"doctrine/doctrine-bundle": "~1.2", "doctrine/doctrine-bundle": "^1.6",
"doctrine/doctrine-cache-bundle": "^1.2",
"twig/extensions": "~1.0", "twig/extensions": "~1.0",
"symfony/assetic-bundle": "~2.3", "symfony/assetic-bundle": "~2.3",
"symfony/swiftmailer-bundle": "~2.3", "symfony/swiftmailer-bundle": "^2.3",
"symfony/monolog-bundle": "~2.4", "symfony/monolog-bundle": "^2.8",
"sensio/distribution-bundle": "~3.0.12", "sensio/distribution-bundle": "^5.0",
"sensio/framework-extra-bundle": "~3.0", "sensio/framework-extra-bundle": "^3.0.2",
"incenteev/composer-parameter-handler": "~2.0", "incenteev/composer-parameter-handler": "^2.0",
"nelmio/cors-bundle": "~1.4.0", "nelmio/cors-bundle": "~1.4.0",
"friendsofsymfony/rest-bundle": "~1.4", "friendsofsymfony/rest-bundle": "~1.4",
"jms/serializer-bundle": "~0.13", "jms/serializer-bundle": "~1.0",
"nelmio/api-doc-bundle": "~2.7", "nelmio/api-doc-bundle": "~2.7",
"ezyang/htmlpurifier": "~4.6", "ezyang/htmlpurifier": "~4.6",
"mgargano/simplehtmldom": "~1.5", "mgargano/simplehtmldom": "~1.5",
"tecnick.com/tcpdf": "~6.2", "tecnickcom/tcpdf": "~6.2",
"simplepie/simplepie": "~1.3.1", "simplepie/simplepie": "~1.3.1",
"willdurand/hateoas-bundle": "~0.5.0", "willdurand/hateoas-bundle": "~1.0",
"htmlawed/htmlawed": "~1.1.19", "htmlawed/htmlawed": "~1.1.19",
"liip/theme-bundle": "~1.1.3", "liip/theme-bundle": "~1.1",
"pagerfanta/pagerfanta": "~1.0.3", "pagerfanta/pagerfanta": "~1.0.3",
"lexik/form-filter-bundle": "~4.0", "lexik/form-filter-bundle": "~5.0",
"j0k3r/graby": "~1.0", "j0k3r/graby": "~1.0",
"friendsofsymfony/user-bundle": "dev-master" "friendsofsymfony/user-bundle": "dev-master",
"friendsofsymfony/oauth-server-bundle": "^1.5@dev",
"stof/doctrine-extensions-bundle": "^1.2@dev",
"scheb/two-factor-bundle": "~2.0",
"grandt/phpepub": "~4.0",
"wallabag/php-mobi": "~1.0.0",
"kphoen/rulerz-bundle": "~0.10",
"guzzlehttp/guzzle": "^5.2.0",
"doctrine/doctrine-migrations-bundle": "^1.0",
"paragonie/random_compat": "~1.0"
}, },
"require-dev": { "require-dev": {
"doctrine/doctrine-fixtures-bundle": "~2.2.0", "doctrine/doctrine-fixtures-bundle": "~2.2",
"sensio/generator-bundle": "~2.5", "sensio/generator-bundle": "^3.0",
"phpunit/phpunit": "~4.4" "phpunit/phpunit": "~4.4",
"symfony/phpunit-bridge": "^2.7"
}, },
"scripts": { "scripts": {
"post-install-cmd": [ "build-parameters": [
"Incenteev\\ParameterHandler\\ScriptHandler::buildParameters", "Incenteev\\ParameterHandler\\ScriptHandler::buildParameters"
],
"post-cmd": [
"@build-parameters",
"Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap", "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap",
"Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache", "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache",
"Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets", "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets",
"Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile" "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile",
"Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::prepareDeploymentTarget"
],
"post-install-cmd": [
"@post-cmd"
], ],
"post-update-cmd": [ "post-update-cmd": [
"Incenteev\\ParameterHandler\\ScriptHandler::buildParameters", "@post-cmd"
"Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap",
"Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache",
"Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets",
"Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile"
] ]
}, },
"extra": { "extra": {
"symfony-app-dir": "app", "symfony-app-dir": "app",
"symfony-bin-dir": "bin",
"symfony-var-dir": "var",
"symfony-web-dir": "web", "symfony-web-dir": "web",
"symfony-tests-dir": "tests",
"symfony-assets-install": "relative", "symfony-assets-install": "relative",
"incenteev-parameters": { "incenteev-parameters": {
"file": "app/config/parameters.yml" "file": "app/config/parameters.yml",
"env-map": {
"mailer_host": "WALLABAG_MAILER_HOST",
"mailer_user": "WALLABAG_MAILER_USER",
"mailer_password": "WALLABAG_MAILER_PASSWORD",
"secret": "WALLABAG_SECRET"
}
} }
}, },
"autoload": { "autoload": {
"psr-0": { "": "src/" } "psr-4": { "": "src/" },
"classmap": [ "app/AppKernel.php", "app/AppCache.php" ]
}, },
"config": { "config": {
"bin-dir": "bin" "bin-dir": "bin"

3249
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,66 +0,0 @@
# config valid only for current version of Capistrano
lock '3.4.0'
set :application, 'wallabag'
set :repo_url, 'git@github.com:wallabag/wallabag.git'
set :ssh_user, 'ssh_user'
server 'server_ip', user: fetch(:ssh_user), roles: %w{web app db}
set :scm, :git
set :format, :pretty
set :log_level, :info
# set :log_level, :debug
set :composer_install_flags, '--no-dev --prefer-dist --no-interaction --optimize-autoloader'
set :linked_files, %w{app/config/parameters.yml}
set :linked_dirs, %w{app/logs web/uploads}
set :keep_releases, 3
after 'deploy:finishing', 'deploy:cleanup'
# Default branch is :master
# ask :branch, `git rev-parse --abbrev-ref HEAD`.chomp
# Default deploy_to directory is /var/www/my_app_name
# set :deploy_to, '/var/www/my_app_name'
# Default value for :scm is :git
# set :scm, :git
# Default value for :format is :pretty
# set :format, :pretty
# Default value for :log_level is :debug
# set :log_level, :debug
# Default value for :pty is false
# set :pty, true
# Default value for :linked_files is []
# set :linked_files, fetch(:linked_files, []).push('config/database.yml', 'config/secrets.yml')
# Default value for linked_dirs is []
# set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'public/system')
# Default value for default_env is {}
# set :default_env, { path: "/opt/ruby/bin:$PATH" }
# Default value for keep_releases is 5
# set :keep_releases, 5
namespace :deploy do
after :restart, :clear_cache do
on roles(:web), in: :groups, limit: 3, wait: 10 do
# Here we can do anything such as:
# within release_path do
# execute :rake, 'cache:clear'
# end
end
end
end

View File

@ -1,61 +0,0 @@
# server-based syntax
# ======================
# Defines a single server with a list of roles and multiple properties.
# You can define all roles on a single server, or split them:
# server 'example.com', user: 'deploy', roles: %w{app db web}, my_property: :my_value
# server 'example.com', user: 'deploy', roles: %w{app web}, other_property: :other_value
# server 'db.example.com', user: 'deploy', roles: %w{db}
# role-based syntax
# ==================
# Defines a role with one or multiple servers. The primary server in each
# group is considered to be the first unless any hosts have the primary
# property set. Specify the username and a domain or IP for the server.
# Don't use `:all`, it's a meta role.
# role :app, %w{deploy@example.com}, my_property: :my_value
# role :web, %w{user1@primary.com user2@additional.com}, other_property: :other_value
# role :db, %w{deploy@example.com}
# Configuration
# =============
# You can set any configuration variable like in config/deploy.rb
# These variables are then only loaded and set in this stage.
# For available Capistrano configuration variables see the documentation page.
# http://capistranorb.com/documentation/getting-started/configuration/
# Feel free to add new variables to customise your setup.
# Custom SSH Options
# ==================
# You may pass any option but keep in mind that net/ssh understands a
# limited set of options, consult the Net::SSH documentation.
# http://net-ssh.github.io/net-ssh/classes/Net/SSH.html#method-c-start
#
# Global options
# --------------
# set :ssh_options, {
# keys: %w(/home/rlisowski/.ssh/id_rsa),
# forward_agent: false,
# auth_methods: %w(password)
# }
#
# The server-based syntax can be used to override options:
# ------------------------------------
# server 'example.com',
# user: 'user_name',
# roles: %w{web app},
# ssh_options: {
# user: 'user_name', # overrides user setting above
# keys: %w(/home/user_name/.ssh/id_rsa),
# forward_agent: false,
# auth_methods: %w(publickey password)
# # password: 'please use keys'
# }

View File

@ -1,64 +0,0 @@
# server-based syntax
# ======================
# Defines a single server with a list of roles and multiple properties.
# You can define all roles on a single server, or split them:
set :branch, 'v2'
set :deploy_to, '/var/www/'
# server 'example.com', user: 'deploy', roles: %w{app db web}, my_property: :my_value
# server 'example.com', user: 'deploy', roles: %w{app web}, other_property: :other_value
# server 'db.example.com', user: 'deploy', roles: %w{db}
# role-based syntax
# ==================
# Defines a role with one or multiple servers. The primary server in each
# group is considered to be the first unless any hosts have the primary
# property set. Specify the username and a domain or IP for the server.
# Don't use `:all`, it's a meta role.
# role :app, %w{deploy@example.com}, my_property: :my_value
# role :web, %w{user1@primary.com user2@additional.com}, other_property: :other_value
# role :db, %w{deploy@example.com}
# Configuration
# =============
# You can set any configuration variable like in config/deploy.rb
# These variables are then only loaded and set in this stage.
# For available Capistrano configuration variables see the documentation page.
# http://capistranorb.com/documentation/getting-started/configuration/
# Feel free to add new variables to customise your setup.
# Custom SSH Options
# ==================
# You may pass any option but keep in mind that net/ssh understands a
# limited set of options, consult the Net::SSH documentation.
# http://net-ssh.github.io/net-ssh/classes/Net/SSH.html#method-c-start
#
# Global options
# --------------
# set :ssh_options, {
# keys: %w(/home/rlisowski/.ssh/id_rsa),
# forward_agent: false,
# auth_methods: %w(password)
# }
#
# The server-based syntax can be used to override options:
# ------------------------------------
# server 'example.com',
# user: 'user_name',
# roles: %w{web app},
# ssh_options: {
# user: 'user_name', # overrides user setting above
# keys: %w(/home/user_name/.ssh/id_rsa),
# forward_agent: false,
# auth_methods: %w(publickey password)
# # password: 'please use keys'
# }

42
docker-compose.yml Normal file
View File

@ -0,0 +1,42 @@
nginx:
image: nginx
ports:
- "8080:80"
volumes:
- ./docker/nginx/nginx.conf:/nginx.conf
- ./docker/logs/nginx:/var/log/nginx
- .:/var/www/html
links:
- php:php
command: nginx -c /nginx.conf
php:
build: docker/php
ports:
- "9000:9000"
volumes:
- .:/var/www/html
#links:
# - "postgres:rdbms"
# - "mariadb:rdbms"
env_file:
- ./docker/php/env
# Comment non-used DBMS lines
# If all DBMS are commented out, sqlite will be used as default
# - ./docker/postgres/env
# - ./docker/mariadb/env
#postgres:
# image: postgres:9
# ports:
# - "5432:5432"
# volumes:
# - ./docker/data/pgsql:/var/lib/postgresql/data
# env_file:
# - ./docker/postgres/env
#mariadb:
# image: mariadb:10
# ports:
# - "3306:3306"
# volumes:
# - ./docker/data/mariadb:/var/lib/mysql
# env_file:
# - ./docker/mariadb/env

10
docker/mariadb/env Normal file
View File

@ -0,0 +1,10 @@
MYSQL_ROOT_PASSWORD=wallaroot
MYSQL_USER=wallabag
MYSQL_PASSWORD=wallapass
MYSQL_DATABASE=wallabag
SYMFONY__ENV__DATABASE_DRIVER=pdo_mysql
SYMFONY__ENV__DATABASE_HOST=rdbms
SYMFONY__ENV__DATABASE_PORT=3306
SYMFONY__ENV__DATABASE_NAME=wallabag
SYMFONY__ENV__DATABASE_USER=wallabag
SYMFONY__ENV__DATABASE_PASSWORD=wallapass

89
docker/nginx/nginx.conf Normal file
View File

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

10
docker/php/Dockerfile Normal file
View File

@ -0,0 +1,10 @@
FROM php:fpm
RUN apt-get update && apt-get install -y \
libmcrypt-dev libicu-dev libpq-dev libxml2-dev \
&& docker-php-ext-install \
iconv mcrypt mbstring intl pdo pdo_mysql pdo_pgsql
RUN usermod -u 1000 www-data
CMD ["php-fpm"]

6
docker/php/env Normal file
View File

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

9
docker/postgres/env Normal file
View File

@ -0,0 +1,9 @@
POSTGRES_USER=wallabag
POSTGRES_PASSWORD=wallapass
POSTGRES_DB=wallabag
export SYMFONY__ENV__DATABASE_DRIVER=pdo_pgsql
export SYMFONY__ENV__DATABASE_HOST=rdbms
export SYMFONY__ENV__DATABASE_PORT=5432
export SYMFONY__ENV__DATABASE_NAME=wallabag
export SYMFONY__ENV__DATABASE_USER=wallabag
export SYMFONY__ENV__DATABASE_PASSWORD=wallapass

0
docs/README.rst Normal file
View File

55
docs/en/conf.py Normal file
View File

@ -0,0 +1,55 @@
# -*- coding: utf-8 -*-
#
# wallabag documentation build configuration file, created by
# sphinx-quickstart on Fri Oct 16 06:47:23 2015.
import sys
import os
extensions = []
templates_path = ['_templates']
source_suffix = '.rst'
master_doc = 'index'
project = u'wallabag'
copyright = u'2013-2016, Nicolas Lœuillet - MIT Licence'
version = '2.0.0'
release = version
exclude_patterns = ['_build']
pygments_style = 'sphinx'
html_theme = 'default'
html_static_path = ['_static']
htmlhelp_basename = 'wallabagdoc'
latex_elements = {
}
latex_documents = [
('index', 'wallabag.tex', u'wallabag Documentation',
u'Nicolas Lœuillet', 'manual'),
]
man_pages = [
('index', 'wallabag', u'wallabag Documentation',
[u'Nicolas Lœuillet'], 1)
]
texinfo_documents = [
('index', 'wallabag', u'wallabag Documentation',
u'Nicolas Lœuillet', 'wallabag', 'wallabag is an opensource read-it-later.',
'Miscellaneous'),
]
##### Guzzle sphinx theme
import guzzle_sphinx_theme
html_translator_class = 'guzzle_sphinx_theme.HTMLTranslator'
html_theme_path = guzzle_sphinx_theme.html_theme_path()
html_theme = 'guzzle_sphinx_theme'
# Custom sidebar templates, maps document names to template names.
html_sidebars = {
'**': ['logo-text.html', 'globaltoc.html', 'searchbox.html']
}
# Register the theme as an extension to generate a sitemap.xml
extensions.append("guzzle_sphinx_theme")

View File

@ -0,0 +1,51 @@
Run Wallabag in docker-compose
==============================
In order to run your own development instance of wallabag, you may
want to use the pre-configured docker compose files.
Requirements
------------
Make sure to have `Docker
<https://docs.docker.com/installation/ubuntulinux/>`__ and `Docker
Compose <https://docs.docker.com/compose/install/>`__ availables on
your system and up to date.
Switch DBMS
-----------
By default, Wallabag will start with a sqlite database.
Since Wallabag provide support for Postgresql and MySQL, docker
containers are also available for these ones.
In ``docker-compose.yml``, for the chosen DBMS uncomment :
- the container definition (``postgres`` or ``mariadb`` root level
block)
- the container link in the ``php`` container
- the container env file in the ``php`` container
In order to keep running Symfony commands on your host (such as
``wallabag:install``), you also should :
- source the proper env files on your command line, so variables
like ``SYMFONY__ENV__DATABASE_HOST`` will exist.
- create a ``127.0.0.1 rdbms`` on your system ``hosts`` file
Run Wallabag
------------
#. Fork and clone the project
#. Edit ``app/config/parameters.yml`` to replace ``database_*``
properties with commented ones (with values prefixed by ``env.``)
#. ``composer install`` the project dependencies
#. ``php app/console wallabag:install`` to create the schema
#. ``docker-compose up`` to run the containers
#. Finally, browse to http://localhost:8080/ to find your freshly
installed wallabag.
At various step, you'll probably run into UNIX permission problems,
bad paths in generated cache, etc…
Operations like removing cache files or changing files owners might
be frequently required, so don't be afraid !

37
docs/en/index.rst Normal file
View File

@ -0,0 +1,37 @@
wallabag documentation
======================
.. image:: ../img/wallabag.png
:alt: wallabag logo
:align: center
**wallabag** is a read-it-later application: it saves a web page by
keeping content only. Elements like navigation or ads are deleted.
The main documentation for this application is organized into a couple sections:
* :ref:`user-docs`
* :ref:`dev-docs`
.. _user-docs:
.. toctree::
:maxdepth: 2
:caption: User documentation
user/create_account
user/login
user/configuration
user/first_article
user/import
user/download_articles
user/filters
user/tags
.. _dev-docs:
.. toctree::
:maxdepth: 2
:caption: Developer documentation
developer/docker

2
docs/en/requirements.txt Normal file
View File

@ -0,0 +1,2 @@
Sphinx>=1.3.0,<1.4.0
guzzle_sphinx_theme>=0.7.0,<0.8.0

View File

@ -0,0 +1,99 @@
Configuration
=============
Now you're logged in, it's time to configure your account as you want.
Click on ``Config`` menu. You have five tabs: ``Settings``, ``RSS``, ``User information``, ``Password`` and ``Tagging rules``.
Settings
--------
Theme
~~~~~
wallabag is customizable. You can choose your prefered theme here. You can also create a new one, a chapter is dedicated for this. The default theme is ``Material``, it's the theme used in the documentation screenshots.
Items per page
~~~~~~~~~~~~~~
You can change the number of articles displayed on each page.
Language
~~~~~~~~
You can change the language of wallabag interface.
RSS
---
wallabag provides RSS feeds for each article status: unread, starred and archive.
Firstly, you need to create a personal token: click on ``Create your token``.
It's possible to change your token by clicking on ``Reset your token``.
Now you have three links, one for each status: add them into your favourite RSS reader.
You can also define how many articles you want in each RSS feed (default value: 50).
User information
----------------
You can change your name, your email address and enable ``Two factor authentication``.
Two factor authentication
~~~~~~~~~~~~~~~~~~~~~~~~~
Two-factor authentication (also known as 2FA) is a technology patented in 1984 that provides identification of users by means of the combination of two different components.
https://en.wikipedia.org/wiki/Two-factor_authentication
If you enable 2FA, each time you want to login to wallabag, you'll receive a code by email. You have to put this code on the following form.
.. image:: ../../img/user/2FA_form.png
:alt: Two factor authentication
:align: center
If you don't want to receive a code each time you want to login, you can check the ``I'm on a trusted computer`` checkbox: wallabag will remember you for 15 days.
Password
--------
You can change your password here.
Tagging rules
-------------
If you want to automatically assign a tag to new articles, this part of the configuration is for you.
What does « tagging rules » mean?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
They are rules used by wallabag to automatically tag new entries.
Each time a new entry is added, all the tagging rules will be used to add the tags you configured, thus saving you the trouble to manually classify your entries.
How do I use them?
~~~~~~~~~~~~~~~~~~
Let assume you want to tag new entries as *« short reading »* when the reading time is inferior to 3 minutes.
In that case, you should put « readingTime <= 3 » in the **Rule** field and *« short reading »* in the **Tags** field.
Several tags can added simultaneously by separating them by a comma: *« short reading, must read »*.
Complex rules can be written by using predefined operators: if *« readingTime >= 5 AND domainName = "github.com" »* then tag as *« long reading, github »*.
Which variables and operators can I use to write rules?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The following variables and operators can be used to create tagging rules:
=========== ============================================== ======== ==========
Variable Meaning Operator Meaning
----------- ---------------------------------------------- -------- ----------
title Title of the entry <= Less than…
url URL of the entry < Strictly less than…
isArchived Whether the entry is archived or not => Greater than…
isStared Whether the entry is starred or not > Strictly greater than…
content The entry's content = Equal to…
language The entry's language != Not equal to…
mimetype The entry's mime-type OR One rule or another
readingTime The estimated entry's reading time, in minutes AND One rule and another
domainName The domain name of the entry matches Tests that a subject is matches a search (case-insensitive). Example: title matches "football"
=========== ============================================== ======== ==========

View File

@ -0,0 +1,43 @@
Create an account
=================
On the login page, click on ``Register`` button.
.. image:: ../../img/user/registration_form.png
:alt: Registration form
:align: center
You have to fill the form. Please sure to type a valid email address, we'll send you an activation email.
.. image:: ../../img/user/sent_email.png
:alt: Email was sent to activate account
:align: center
Check your inbox, you now have a new mail with a link like this ``http://wallabag/register/confirm/Ba19wokGovN-DdBQNfg4YgRkUQWRP4-k2g0Bk-hBTX4``. Click on it to activate your account.
Your account is now activated.
.. image:: ../../img/user/activated_account.png
:alt: Welcome on board!
:align: center
Frequently asked questions
--------------------------
I can't valid the registration form
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Make sure that all fields are well filled:
* valid email address
* same passwords in two fields
I don't receive my activation email
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Are you sure your email address was correct? Did you check your spams folder?
When I click on the activation link, I've got this message: ``The user with confirmation token "DtrOPfbQeVkWf6N" does not exist``.
~~~~~~~~~~~~~~~
You already enabled your account or the URL of the activation email is wrong.

View File

@ -0,0 +1,16 @@
Download articles
=================
You can download each article in several formats: ePUB, MOBI, PDF, XML, JSON, CSV.
On the article view, click on this icon, in the sidebar:
.. image:: ../../img/user/download_article.png
:alt: download article
:align: center
You can also download a full category in these formats. For example, on **Unread** view, click on this icon in the top bar:
.. image:: ../../img/user/download_articles.png
:alt: download articles
:align: center

2
docs/en/user/filters.rst Normal file
View File

@ -0,0 +1,2 @@
Filters
=======

View File

@ -0,0 +1,62 @@
Save your first article
=======================
The main purpose of wallabag is to save web articles. You have many ways to do it.
.. note::
A quickstart will be displayed in the application until you save your first article.
By using a bookmarklet
----------------------
On the ``Howto`` page, you have a ``Bookmarklet`` tab. Drag and drop the ``bag it!`` link to your bookmarks bar of your browser.
Now, each time you're reading an article on the web and you want to save it, click on the ``bag it!`` link in your bookmarks bar. The article is saved.
By using the classic form
-------------------------
In the top bar of your screen, you have 3 icons. With the first one, a plus sign, you can easily save a new article.
.. image:: ../../img/user/topbar.png
:alt: Top bar
:align: center
Click on it to display a new field, paste the article URL inside and press your ``Return`` key. The article is saved.
By using a browser add-on
-------------------------
Firefox
~~~~~~~
*This addon is not yet available for wallabag v2*.
Chrome
~~~~~~
*This addon is not yet available for wallabag v2*.
By using your smarphone application
-----------------------------------
Android
~~~~~~~
*This application is not yet available for wallabag v2*.
Firefox OS
~~~~~~~~~~
*This application is not yet available for wallabag v2*.
Windows Phone
~~~~~~~~~~~~~
*This application is not yet available for wallabag v2*.
iOS
~~~
*This application is not yet available for wallabag v2*.

64
docs/en/user/import.rst Normal file
View File

@ -0,0 +1,64 @@
Migrate to wallabag
===================
From wallabag 1.x
-----------------
Export your data from your wallabag 1.x
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
On your config page, click on ``JSON export`` in the ``Export your wallabag data`` section.
.. image:: ../../img/user/export_wllbg_1.png
:alt: Export from wallabag 1.x
:align: center
You will have a ``wallabag-export-1-1970-01-01.json`` file.
Import your data into wallabag 2.x
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Click on ``Import`` link in the menu, select your export file on your computer and import it.
.. image:: ../../img/user/import_wllbg.png
:alt: Import from wallabag 1.x
:align: center
All your wallabag 1.x articles will be imported.
From Pocket
-----------
Create a new applicaton on Pocket
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To import your data from Pocket, we use the Pocket API. You need to create a new application on their developer website to continue.
* Create a new application `on the developer website <https://getpocket.com/developer/apps/new>`_
* Fill in the required fields: application name, application description, permissions (only **retrieve**), platform (**web**), accept the terms of service and submit your new application
Pocket will give you a **Consumer Key** (for example, `49961-985e4b92fe21fe4c78d682c1`). You need to configure the ``pocket_consumer_key`` into the ``app/config/parameters.yml`` file in wallabag.
Now, all is fine to migrate from Pocket.
Import your data into wallabag 2.x
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Click on ``Import`` link in the menu, on ``Import contents`` in Pocket section and then on ``Connect to Pocket and import data``.
You need to authorize wallabag to interact with your Pocket account. Your data will be imported. Data import can be a demanding process for your server (we need to work on this import to improve it).
From Instapaper
---------------
*Feature not yet implemented in wallabag v2.*
From Readability
----------------
*Feature not yet implemented in wallabag v2.*
From HTML or JSON file
----------------------
*Feature not yet implemented in wallabag v2.*

20
docs/en/user/login.rst Normal file
View File

@ -0,0 +1,20 @@
Login
=====
Your account is now enabled, congratulations!
To login to wallabag, fill the form on login page.
If you are on your personal computer and you want to stay connected, you can check the ``Keep me logged in`` checkbox: wallabag will remember you for one year.
.. image:: ../../img/user/login_form.png
:alt: Login form
:align: center
Frequently asked questions
--------------------------
I forgot my password
~~~~~~~~~~~~~~~~~~~~
You can reset your password by clicking on ``Forgot your password?`` link, on the login page. Then, fill the form with your email address

2
docs/en/user/tags.rst Normal file
View File

@ -0,0 +1,2 @@
Tags
====

55
docs/fr/conf.py Normal file
View File

@ -0,0 +1,55 @@
# -*- coding: utf-8 -*-
#
# wallabag documentation build configuration file, created by
# sphinx-quickstart on Fri Oct 16 06:47:23 2015.
import sys
import os
extensions = []
templates_path = ['_templates']
source_suffix = '.rst'
master_doc = 'index'
project = u'wallabag-fr'
copyright = u'2013-2016, Nicolas Lœuillet - MIT Licence'
version = '2.0.0'
release = version
exclude_patterns = ['_build']
pygments_style = 'sphinx'
html_theme = 'default'
html_static_path = ['_static']
htmlhelp_basename = 'wallabagfrdoc'
latex_elements = {
}
latex_documents = [
('index', 'wallabag-fr.tex', u'wallabag Documentation',
u'Nicolas Lœuillet', 'manual'),
]
man_pages = [
('index', 'wallabagfr', u'wallabag Documentation',
[u'Nicolas Lœuillet'], 1)
]
texinfo_documents = [
('index', 'wallabag', u'wallabag Documentation',
u'Nicolas Lœuillet', 'wallabag', 'wallabag is an opensource read-it-later.',
'Miscellaneous'),
]
##### Guzzle sphinx theme
import guzzle_sphinx_theme
html_translator_class = 'guzzle_sphinx_theme.HTMLTranslator'
html_theme_path = guzzle_sphinx_theme.html_theme_path()
html_theme = 'guzzle_sphinx_theme'
# Custom sidebar templates, maps document names to template names.
html_sidebars = {
'**': ['logo-text.html', 'globaltoc.html', 'searchbox.html']
}
# Register the theme as an extension to generate a sitemap.xml
extensions.append("guzzle_sphinx_theme")

17
docs/fr/index.rst Normal file
View File

@ -0,0 +1,17 @@
Documentation de wallabag
=========================
.. image:: ../img/wallabag.png
:alt: wallabag logo
:align: center
**wallabag** est une application de lecture différée : elle permet
simplement darchiver une page web en ne conservant que le contenu. Les
éléments superflus (menu, publicité, etc.) sont supprimés.
La documentation principale de cette application est découpée en plusieurs sections :
.. toctree::
:maxdepth: 2
index

2
docs/fr/requirements.txt Normal file
View File

@ -0,0 +1,2 @@
Sphinx>=1.3.0,<1.4.0
guzzle_sphinx_theme>=0.7.0,<0.8.0

BIN
docs/img/user/2FA_form.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
docs/img/user/topbar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
docs/img/wallabag.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

55
docs/pt-br/conf.py Normal file
View File

@ -0,0 +1,55 @@
# -*- coding: utf-8 -*-
#
# wallabag documentation build configuration file, created by
# sphinx-quickstart on Fri Oct 16 06:47:23 2015.
import sys
import os
extensions = []
templates_path = ['_templates']
source_suffix = '.rst'
master_doc = 'index'
project = u'wallabag-pt'
copyright = u'2013-2016, Nicolas Lœuillet - MIT Licence'
version = '2.0.0'
release = version
exclude_patterns = ['_build']
pygments_style = 'sphinx'
html_theme = 'default'
html_static_path = ['_static']
htmlhelp_basename = 'wallabagfrdoc'
latex_elements = {
}
latex_documents = [
('index', 'wallabag-pt.tex', u'wallabag Documentation',
u'Nicolas Lœuillet', 'manual'),
]
man_pages = [
('index', 'wallabagpt', u'wallabag Documentation',
[u'Nicolas Lœuillet'], 1)
]
texinfo_documents = [
('index', 'wallabag', u'wallabag Documentation',
u'Nicolas Lœuillet', 'wallabag', 'wallabag is an opensource read-it-later.',
'Miscellaneous'),
]
##### Guzzle sphinx theme
import guzzle_sphinx_theme
html_translator_class = 'guzzle_sphinx_theme.HTMLTranslator'
html_theme_path = guzzle_sphinx_theme.html_theme_path()
html_theme = 'guzzle_sphinx_theme'
# Custom sidebar templates, maps document names to template names.
html_sidebars = {
'**': ['logo-text.html', 'globaltoc.html', 'searchbox.html']
}
# Register the theme as an extension to generate a sitemap.xml
extensions.append("guzzle_sphinx_theme")

36
docs/pt-br/index.rst Normal file
View File

@ -0,0 +1,36 @@
wallabag documentation
======================
.. image:: ../img/wallabag.png
:alt: wallabag logo
:align: center
**wallabag** É uma aplicação "leia mais tarde": Ele salva páginas da web mantendo apenas o conteúdo. Elementos como ícones de navegação ou propaganda são deletedos.
A documentação principal desta aplicação é organizada em duas seções:
* :ref:`doc-usuario`
* :ref:`doc-desenvolvedor`
.. _user-docs:
.. toctree::
:maxdepth: 2
:caption: Documentação do usuário
user/criar_conta
user/login
user/configuração
user/primeiro_artigo
user/importar
user/baixar_artigos
user/filtros
user/tags
.. _dev-docs:
.. toctree::
:maxdepth: 2
:caption: Documentação do desenvolvedor
developer/docker

View File

@ -0,0 +1,2 @@
Sphinx>=1.3.0,<1.4.0
guzzle_sphinx_theme>=0.7.0,<0.8.0

View File

@ -9,27 +9,27 @@
processIsolation="false" processIsolation="false"
stopOnFailure="false" stopOnFailure="false"
syntaxCheck="false" syntaxCheck="false"
bootstrap="app/bootstrap.php.cache" bootstrap="app/autoload.php"
> >
<testsuites> <testsuites>
<testsuite name="wallabag Test Suite"> <testsuite name="wallabag Test Suite">
<directory>./src/Wallabag/*Bundle/Tests</directory> <directory>src/Wallabag/*Bundle/Tests</directory>
</testsuite> </testsuite>
</testsuites> </testsuites>
<php> <php>
<server name="KERNEL_DIR" value="./app/" /> <server name="KERNEL_DIR" value="app/" />
</php> </php>
<filter> <filter>
<whitelist> <whitelist>
<directory>./src</directory> <directory>src</directory>
<exclude> <exclude>
<directory>./vendor</directory> <directory>vendor</directory>
<directory>./src/Wallabag/*Bundle/Resources</directory> <directory>src/Wallabag/*Bundle/Resources</directory>
<directory>./src/Wallabag/*Bundle/Tests</directory> <directory>src/Wallabag/*Bundle/Tests</directory>
<directory>./src/Wallabag/*Bundle/DataFixtures</directory> <directory>src/Wallabag/*Bundle/DataFixtures</directory>
</exclude> </exclude>
</whitelist> </whitelist>
</filter> </filter>

View File

@ -2,16 +2,17 @@
namespace Wallabag\ApiBundle\Controller; namespace Wallabag\ApiBundle\Controller;
use Nelmio\ApiDocBundle\Annotation\ApiDoc; use FOS\RestBundle\Controller\FOSRestController;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Wallabag\CoreBundle\Entity\Entry;
use Wallabag\CoreBundle\Entity\Tag;
use Hateoas\Configuration\Route; use Hateoas\Configuration\Route;
use Hateoas\Representation\Factory\PagerfantaFactory; use Hateoas\Representation\Factory\PagerfantaFactory;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Wallabag\CoreBundle\Entity\Entry;
use Wallabag\CoreBundle\Entity\Tag;
class WallabagRestController extends Controller class WallabagRestController extends FOSRestController
{ {
/** /**
* @param Entry $entry * @param Entry $entry
@ -27,7 +28,7 @@ class WallabagRestController extends Controller
->findOneByLabel($label); ->findOneByLabel($label);
if (is_null($tagEntity)) { if (is_null($tagEntity)) {
$tagEntity = new Tag($this->getUser()); $tagEntity = new Tag();
$tagEntity->setLabel($label); $tagEntity->setLabel($label);
} }
@ -38,29 +39,11 @@ class WallabagRestController extends Controller
} }
} }
/** private function validateAuthentication()
* Retrieve salt for a giver user.
*
* @ApiDoc(
* parameters={
* {"name"="username", "dataType"="string", "required"=true, "description"="username"}
* }
* )
*
* @return array
*/
public function getSaltAction($username)
{ {
$user = $this if (false === $this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) {
->getDoctrine() throw new AccessDeniedException();
->getRepository('WallabagCoreBundle:User')
->findOneByUsername($username);
if (is_null($user)) {
throw $this->createNotFoundException();
} }
return array($user->getSalt() ?: null);
} }
/** /**
@ -78,10 +61,12 @@ class WallabagRestController extends Controller
* } * }
* ) * )
* *
* @return Entry * @return Response
*/ */
public function getEntriesAction(Request $request) public function getEntriesAction(Request $request)
{ {
$this->validateAuthentication();
$isArchived = $request->query->get('archive'); $isArchived = $request->query->get('archive');
$isStarred = $request->query->get('star'); $isStarred = $request->query->get('star');
$sort = $request->query->get('sort', 'created'); $sort = $request->query->get('sort', 'created');
@ -90,8 +75,7 @@ class WallabagRestController extends Controller
$perPage = (int) $request->query->get('perPage', 30); $perPage = (int) $request->query->get('perPage', 30);
$tags = $request->query->get('tags', []); $tags = $request->query->get('tags', []);
$pager = $this $pager = $this->getDoctrine()
->getDoctrine()
->getRepository('WallabagCoreBundle:Entry') ->getRepository('WallabagCoreBundle:Entry')
->findEntries($this->getUser()->getId(), $isArchived, $isStarred, $sort, $order); ->findEntries($this->getUser()->getId(), $isArchived, $isStarred, $sort, $order);
@ -101,7 +85,7 @@ class WallabagRestController extends Controller
$pagerfantaFactory = new PagerfantaFactory('page', 'perPage'); $pagerfantaFactory = new PagerfantaFactory('page', 'perPage');
$paginatedCollection = $pagerfantaFactory->createRepresentation( $paginatedCollection = $pagerfantaFactory->createRepresentation(
$pager, $pager,
new Route('api_get_entries', [], $absolute = true) new Route('api_get_entries', [], UrlGeneratorInterface::ABSOLUTE_URL)
); );
$json = $this->get('serializer')->serialize($paginatedCollection, 'json'); $json = $this->get('serializer')->serialize($paginatedCollection, 'json');
@ -118,11 +102,12 @@ class WallabagRestController extends Controller
* } * }
* ) * )
* *
* @return Entry * @return Response
*/ */
public function getEntryAction(Entry $entry) public function getEntryAction(Entry $entry)
{ {
$this->validateUserAccess($entry->getUser()->getId(), $this->getUser()->getId()); $this->validateAuthentication();
$this->validateUserAccess($entry->getUser()->getId());
$json = $this->get('serializer')->serialize($entry, 'json'); $json = $this->get('serializer')->serialize($entry, 'json');
@ -140,10 +125,12 @@ class WallabagRestController extends Controller
* } * }
* ) * )
* *
* @return Entry * @return Response
*/ */
public function postEntriesAction(Request $request) public function postEntriesAction(Request $request)
{ {
$this->validateAuthentication();
$url = $request->request->get('url'); $url = $request->request->get('url');
$entry = $this->get('wallabag_core.content_proxy')->updateEntry( $entry = $this->get('wallabag_core.content_proxy')->updateEntry(
@ -180,15 +167,16 @@ class WallabagRestController extends Controller
* } * }
* ) * )
* *
* @return Entry * @return Response
*/ */
public function patchEntriesAction(Entry $entry, Request $request) public function patchEntriesAction(Entry $entry, Request $request)
{ {
$this->validateUserAccess($entry->getUser()->getId(), $this->getUser()->getId()); $this->validateAuthentication();
$this->validateUserAccess($entry->getUser()->getId());
$title = $request->request->get('title'); $title = $request->request->get('title');
$isArchived = $request->request->get('is_archived'); $isArchived = $request->request->get('archive');
$isStarred = $request->request->get('is_starred'); $isStarred = $request->request->get('star');
if (!is_null($title)) { if (!is_null($title)) {
$entry->setTitle($title); $entry->setTitle($title);
@ -224,11 +212,12 @@ class WallabagRestController extends Controller
* } * }
* ) * )
* *
* @return Entry * @return Response
*/ */
public function deleteEntriesAction(Entry $entry) public function deleteEntriesAction(Entry $entry)
{ {
$this->validateUserAccess($entry->getUser()->getId(), $this->getUser()->getId()); $this->validateAuthentication();
$this->validateUserAccess($entry->getUser()->getId());
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
$em->remove($entry); $em->remove($entry);
@ -247,10 +236,13 @@ class WallabagRestController extends Controller
* {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
* } * }
* ) * )
*
* @return Response
*/ */
public function getEntriesTagsAction(Entry $entry) public function getEntriesTagsAction(Entry $entry)
{ {
$this->validateUserAccess($entry->getUser()->getId(), $this->getUser()->getId()); $this->validateAuthentication();
$this->validateUserAccess($entry->getUser()->getId());
$json = $this->get('serializer')->serialize($entry->getTags(), 'json'); $json = $this->get('serializer')->serialize($entry->getTags(), 'json');
@ -268,10 +260,13 @@ class WallabagRestController extends Controller
* {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."},
* } * }
* ) * )
*
* @return Response
*/ */
public function postEntriesTagsAction(Request $request, Entry $entry) public function postEntriesTagsAction(Request $request, Entry $entry)
{ {
$this->validateUserAccess($entry->getUser()->getId(), $this->getUser()->getId()); $this->validateAuthentication();
$this->validateUserAccess($entry->getUser()->getId());
$tags = $request->request->get('tags', ''); $tags = $request->request->get('tags', '');
if (!empty($tags)) { if (!empty($tags)) {
@ -296,10 +291,13 @@ class WallabagRestController extends Controller
* {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"} * {"name"="entry", "dataType"="integer", "requirement"="\w+", "description"="The entry ID"}
* } * }
* ) * )
*
* @return Response
*/ */
public function deleteEntriesTagsAction(Entry $entry, Tag $tag) public function deleteEntriesTagsAction(Entry $entry, Tag $tag)
{ {
$this->validateUserAccess($entry->getUser()->getId(), $this->getUser()->getId()); $this->validateAuthentication();
$this->validateUserAccess($entry->getUser()->getId());
$entry->removeTag($tag); $entry->removeTag($tag);
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
@ -315,10 +313,18 @@ class WallabagRestController extends Controller
* Retrieve all tags. * Retrieve all tags.
* *
* @ApiDoc() * @ApiDoc()
*
* @return Response
*/ */
public function getTagsAction() public function getTagsAction()
{ {
$json = $this->get('serializer')->serialize($this->getUser()->getTags(), 'json'); $this->validateAuthentication();
$tags = $this->getDoctrine()
->getRepository('WallabagCoreBundle:Tag')
->findAllTags($this->getUser()->getId());
$json = $this->get('serializer')->serialize($tags, 'json');
return $this->renderJsonResponse($json); return $this->renderJsonResponse($json);
} }
@ -331,14 +337,16 @@ class WallabagRestController extends Controller
* {"name"="tag", "dataType"="integer", "requirement"="\w+", "description"="The tag"} * {"name"="tag", "dataType"="integer", "requirement"="\w+", "description"="The tag"}
* } * }
* ) * )
*
* @return Response
*/ */
public function deleteTagAction(Tag $tag) public function deleteTagAction(Tag $tag)
{ {
$this->validateUserAccess($tag->getUser()->getId(), $this->getUser()->getId()); $this->validateAuthentication();
$em = $this->getDoctrine()->getManager(); $this->getDoctrine()
$em->remove($tag); ->getRepository('WallabagCoreBundle:Entry')
$em->flush(); ->removeTag($this->getUser()->getId(), $tag);
$json = $this->get('serializer')->serialize($tag, 'json'); $json = $this->get('serializer')->serialize($tag, 'json');
@ -350,12 +358,12 @@ class WallabagRestController extends Controller
* If not, throw exception. It means a user try to access information from an other user. * If not, throw exception. It means a user try to access information from an other user.
* *
* @param int $requestUserId User id from the requested source * @param int $requestUserId User id from the requested source
* @param int $currentUserId User id from the retrieved source
*/ */
private function validateUserAccess($requestUserId, $currentUserId) private function validateUserAccess($requestUserId)
{ {
if ($requestUserId != $currentUserId) { $user = $this->get('security.token_storage')->getToken()->getUser();
throw $this->createAccessDeniedException('Access forbidden. Entry user id: '.$requestUserId.', logged user id: '.$currentUserId); if ($requestUserId != $user->getId()) {
throw $this->createAccessDeniedException('Access forbidden. Entry user id: '.$requestUserId.', logged user id: '.$user->getId());
} }
} }

View File

@ -1,40 +0,0 @@
<?php
namespace Wallabag\ApiBundle\DependencyInjection\Security\Factory;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface;
class WsseFactory implements SecurityFactoryInterface
{
public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
{
$providerId = 'security.authentication.provider.wsse.'.$id;
$container
->setDefinition($providerId, new DefinitionDecorator('wsse.security.authentication.provider'))
->replaceArgument(0, new Reference($userProvider))
;
$listenerId = 'security.authentication.listener.wsse.'.$id;
$listener = $container->setDefinition($listenerId, new DefinitionDecorator('wsse.security.authentication.listener'));
return array($providerId, $listenerId, $defaultEntryPoint);
}
public function getPosition()
{
return 'pre_auth';
}
public function getKey()
{
return 'wsse';
}
public function addConfiguration(NodeDefinition $node)
{
}
}

View File

@ -3,9 +3,7 @@
namespace Wallabag\ApiBundle\DependencyInjection; namespace Wallabag\ApiBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
class WallabagApiExtension extends Extension class WallabagApiExtension extends Extension
{ {
@ -13,9 +11,6 @@ class WallabagApiExtension extends Extension
{ {
$configuration = new Configuration(); $configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs); $config = $this->processConfiguration($configuration, $configs);
$loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');
} }
public function getAlias() public function getAlias()

View File

@ -0,0 +1,31 @@
<?php
namespace Wallabag\ApiBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use FOS\OAuthServerBundle\Entity\AccessToken as BaseAccessToken;
/**
* @ORM\Table("oauth2_access_tokens")
* @ORM\Entity
*/
class AccessToken extends BaseAccessToken
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @ORM\ManyToOne(targetEntity="Client")
* @ORM\JoinColumn(nullable=false)
*/
protected $client;
/**
* @ORM\ManyToOne(targetEntity="Wallabag\UserBundle\Entity\User")
*/
protected $user;
}

View File

@ -0,0 +1,31 @@
<?php
namespace Wallabag\ApiBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use FOS\OAuthServerBundle\Entity\AuthCode as BaseAuthCode;
/**
* @ORM\Table("oauth2_auth_codes")
* @ORM\Entity
*/
class AuthCode extends BaseAuthCode
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @ORM\ManyToOne(targetEntity="Client")
* @ORM\JoinColumn(nullable=false)
*/
protected $client;
/**
* @ORM\ManyToOne(targetEntity="Wallabag\UserBundle\Entity\User")
*/
protected $user;
}

View File

@ -0,0 +1,25 @@
<?php
namespace Wallabag\ApiBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use FOS\OAuthServerBundle\Entity\Client as BaseClient;
/**
* @ORM\Table("oauth2_clients")
* @ORM\Entity
*/
class Client extends BaseClient
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
public function __construct()
{
parent::__construct();
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace Wallabag\ApiBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use FOS\OAuthServerBundle\Entity\RefreshToken as BaseRefreshToken;
/**
* @ORM\Table("oauth2_refresh_tokens")
* @ORM\Entity
*/
class RefreshToken extends BaseRefreshToken
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* @ORM\ManyToOne(targetEntity="Client")
* @ORM\JoinColumn(nullable=false)
*/
protected $client;
/**
* @ORM\ManyToOne(targetEntity="Wallabag\UserBundle\Entity\User")
*/
protected $user;
}

View File

@ -1,12 +0,0 @@
services:
wsse.security.authentication.provider:
class: Wallabag\ApiBundle\Security\Authentication\Provider\WsseProvider
public: false
arguments: ['', '%kernel.cache_dir%/security/nonces']
wsse.security.authentication.listener:
class: Wallabag\ApiBundle\Security\Firewall\WsseListener
public: false
tags:
- { name: monolog.logger, channel: wsse }
arguments: ['@security.context', '@security.authentication.manager', '@logger']

View File

@ -1,79 +0,0 @@
<?php
namespace Wallabag\ApiBundle\Security\Authentication\Provider;
use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\NonceExpiredException;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Wallabag\ApiBundle\Security\Authentication\Token\WsseUserToken;
class WsseProvider implements AuthenticationProviderInterface
{
private $userProvider;
private $cacheDir;
public function __construct(UserProviderInterface $userProvider, $cacheDir)
{
$this->userProvider = $userProvider;
$this->cacheDir = $cacheDir;
// If cache directory does not exist we create it
if (!is_dir($this->cacheDir)) {
mkdir($this->cacheDir, 0777, true);
}
}
public function authenticate(TokenInterface $token)
{
$user = $this->userProvider->loadUserByUsername($token->getUsername());
if (!$user) {
throw new AuthenticationException('Bad credentials. Did you forgot your username?');
}
if ($user && $this->validateDigest($token->digest, $token->nonce, $token->created, $user->getPassword())) {
$authenticatedToken = new WsseUserToken($user->getRoles());
$authenticatedToken->setUser($user);
return $authenticatedToken;
}
throw new AuthenticationException('The WSSE authentication failed.');
}
protected function validateDigest($digest, $nonce, $created, $secret)
{
// Check created time is not in the future
if (strtotime($created) > time()) {
throw new AuthenticationException('Back to the future...');
}
// Expire timestamp after 5 minutes
if (time() - strtotime($created) > 300) {
throw new AuthenticationException('Too late for this timestamp... Watch your watch.');
}
// Validate nonce is unique within 5 minutes
if (file_exists($this->cacheDir.'/'.$nonce) && file_get_contents($this->cacheDir.'/'.$nonce) + 300 > time()) {
throw new NonceExpiredException('Previously used nonce detected');
}
file_put_contents($this->cacheDir.'/'.$nonce, time());
// Validate Secret
$expected = base64_encode(sha1(base64_decode($nonce).$created.$secret, true));
if ($digest !== $expected) {
throw new AuthenticationException('Bad credentials ! Digest is not as expected.');
}
return $digest === $expected;
}
public function supports(TokenInterface $token)
{
return $token instanceof WsseUserToken;
}
}

View File

@ -1,24 +0,0 @@
<?php
namespace Wallabag\ApiBundle\Security\Authentication\Token;
use Symfony\Component\Security\Core\Authentication\Token\AbstractToken;
class WsseUserToken extends AbstractToken
{
public $created;
public $digest;
public $nonce;
public function __construct(array $roles = array())
{
parent::__construct($roles);
$this->setAuthenticated(count($roles) > 0);
}
public function getCredentials()
{
return '';
}
}

View File

@ -1,62 +0,0 @@
<?php
namespace Wallabag\ApiBundle\Security\Firewall;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Security\Http\Firewall\ListenerInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Wallabag\ApiBundle\Security\Authentication\Token\WsseUserToken;
use Psr\Log\LoggerInterface;
class WsseListener implements ListenerInterface
{
protected $securityContext;
protected $authenticationManager;
protected $logger;
public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, LoggerInterface $logger)
{
$this->securityContext = $securityContext;
$this->authenticationManager = $authenticationManager;
$this->logger = $logger;
}
public function handle(GetResponseEvent $event)
{
$request = $event->getRequest();
$wsseRegex = '/UsernameToken Username="([^"]+)", PasswordDigest="([^"]+)", Nonce="([^"]+)", Created="([^"]+)"/';
if (!$request->headers->has('x-wsse') || 1 !== preg_match($wsseRegex, $request->headers->get('x-wsse'), $matches)) {
return;
}
$token = new WsseUserToken();
$token->setUser($matches[1]);
$token->digest = $matches[2];
$token->nonce = $matches[3];
$token->created = $matches[4];
try {
$authToken = $this->authenticationManager->authenticate($token);
$this->securityContext->setToken($authToken);
return;
} catch (AuthenticationException $failed) {
$failedMessage = 'WSSE Login failed for '.$token->getUsername().'. Why ? '.$failed->getMessage();
$this->logger->err($failedMessage);
// Deny authentication with a '403 Forbidden' HTTP response
$response = new Response();
$response->setStatusCode(403);
$response->setContent($failedMessage);
$event->setResponse($response);
return;
}
}
}

View File

@ -2,99 +2,15 @@
namespace Wallabag\ApiBundle\Tests\Controller; namespace Wallabag\ApiBundle\Tests\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Wallabag\ApiBundle\Tests\WallabagApiTestCase;
class WallabagRestControllerTest extends WebTestCase class WallabagRestControllerTest extends WallabagApiTestCase
{ {
protected static $salt; protected static $salt;
/**
* Grab the salt once and store it to be available for all tests.
*/
public static function setUpBeforeClass()
{
$client = self::createClient();
$user = $client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:User')
->findOneByUsername('admin');
self::$salt = $user->getSalt();
}
/**
* Generate HTTP headers for authenticate user on API.
*
* @param string $username
* @param string $password
*
* @return array
*/
private function generateHeaders($username, $password)
{
$encryptedPassword = sha1($password.$username.self::$salt);
$nonce = substr(md5(uniqid('nonce_', true)), 0, 16);
$now = new \DateTime('now', new \DateTimeZone('UTC'));
$created = (string) $now->format('Y-m-d\TH:i:s\Z');
$digest = base64_encode(sha1(base64_decode($nonce).$created.$encryptedPassword, true));
return array(
'HTTP_AUTHORIZATION' => 'Authorization profile="UsernameToken"',
'HTTP_x-wsse' => 'X-WSSE: UsernameToken Username="'.$username.'", PasswordDigest="'.$digest.'", Nonce="'.$nonce.'", Created="'.$created.'"',
);
}
public function testGetSalt()
{
$client = $this->createClient();
$client->request('GET', '/api/salts/admin.json');
$user = $client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:User')
->findOneByUsername('admin');
$this->assertEquals(200, $client->getResponse()->getStatusCode());
$content = json_decode($client->getResponse()->getContent(), true);
$this->assertArrayHasKey(0, $content);
$this->assertEquals($user->getSalt(), $content[0]);
$client->request('GET', '/api/salts/notfound.json');
$this->assertEquals(404, $client->getResponse()->getStatusCode());
}
public function testWithBadHeaders()
{
$client = $this->createClient();
$entry = $client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneByIsArchived(false);
if (!$entry) {
$this->markTestSkipped('No content found in db.');
}
$badHeaders = array(
'HTTP_AUTHORIZATION' => 'Authorization profile="UsernameToken"',
'HTTP_x-wsse' => 'X-WSSE: UsernameToken Username="admin", PasswordDigest="Wr0ngDig3st", Nonce="n0Nc3", Created="2015-01-01T13:37:00Z"',
);
$client->request('GET', '/api/entries/'.$entry->getId().'.json', array(), array(), $badHeaders);
$this->assertEquals(403, $client->getResponse()->getStatusCode());
}
public function testGetOneEntry() public function testGetOneEntry()
{ {
$client = $this->createClient(); $entry = $this->client->getContainer()
$headers = $this->generateHeaders('admin', 'mypassword');
$entry = $client->getContainer()
->get('doctrine.orm.entity_manager') ->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry') ->getRepository('WallabagCoreBundle:Entry')
->findOneBy(array('user' => 1, 'isArchived' => false)); ->findOneBy(array('user' => 1, 'isArchived' => false));
@ -103,18 +19,17 @@ class WallabagRestControllerTest extends WebTestCase
$this->markTestSkipped('No content found in db.'); $this->markTestSkipped('No content found in db.');
} }
$client->request('GET', '/api/entries/'.$entry->getId().'.json', array(), array(), $headers); $this->client->request('GET', '/api/entries/'.$entry->getId().'.json');
$this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$this->assertEquals(200, $client->getResponse()->getStatusCode()); $content = json_decode($this->client->getResponse()->getContent(), true);
$content = json_decode($client->getResponse()->getContent(), true);
$this->assertEquals($entry->getTitle(), $content['title']); $this->assertEquals($entry->getTitle(), $content['title']);
$this->assertEquals($entry->getUrl(), $content['url']); $this->assertEquals($entry->getUrl(), $content['url']);
$this->assertCount(count($entry->getTags()), $content['tags']); $this->assertCount(count($entry->getTags()), $content['tags']);
$this->assertTrue( $this->assertTrue(
$client->getResponse()->headers->contains( $this->client->getResponse()->headers->contains(
'Content-Type', 'Content-Type',
'application/json' 'application/json'
) )
@ -123,10 +38,7 @@ class WallabagRestControllerTest extends WebTestCase
public function testGetOneEntryWrongUser() public function testGetOneEntryWrongUser()
{ {
$client = $this->createClient(); $entry = $this->client->getContainer()
$headers = $this->generateHeaders('admin', 'mypassword');
$entry = $client->getContainer()
->get('doctrine.orm.entity_manager') ->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry') ->getRepository('WallabagCoreBundle:Entry')
->findOneBy(array('user' => 2, 'isArchived' => false)); ->findOneBy(array('user' => 2, 'isArchived' => false));
@ -135,21 +47,18 @@ class WallabagRestControllerTest extends WebTestCase
$this->markTestSkipped('No content found in db.'); $this->markTestSkipped('No content found in db.');
} }
$client->request('GET', '/api/entries/'.$entry->getId().'.json', array(), array(), $headers); $this->client->request('GET', '/api/entries/'.$entry->getId().'.json');
$this->assertEquals(403, $client->getResponse()->getStatusCode()); $this->assertEquals(403, $this->client->getResponse()->getStatusCode());
} }
public function testGetEntries() public function testGetEntries()
{ {
$client = $this->createClient(); $this->client->request('GET', '/api/entries');
$headers = $this->generateHeaders('admin', 'mypassword');
$client->request('GET', '/api/entries', array(), array(), $headers); $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$this->assertEquals(200, $client->getResponse()->getStatusCode()); $content = json_decode($this->client->getResponse()->getContent(), true);
$content = json_decode($client->getResponse()->getContent(), true);
$this->assertGreaterThanOrEqual(1, count($content)); $this->assertGreaterThanOrEqual(1, count($content));
$this->assertNotEmpty($content['_embedded']['items']); $this->assertNotEmpty($content['_embedded']['items']);
@ -158,7 +67,7 @@ class WallabagRestControllerTest extends WebTestCase
$this->assertGreaterThanOrEqual(1, $content['pages']); $this->assertGreaterThanOrEqual(1, $content['pages']);
$this->assertTrue( $this->assertTrue(
$client->getResponse()->headers->contains( $this->client->getResponse()->headers->contains(
'Content-Type', 'Content-Type',
'application/json' 'application/json'
) )
@ -167,14 +76,11 @@ class WallabagRestControllerTest extends WebTestCase
public function testGetStarredEntries() public function testGetStarredEntries()
{ {
$client = $this->createClient(); $this->client->request('GET', '/api/entries', array('star' => 1, 'sort' => 'updated'));
$headers = $this->generateHeaders('admin', 'mypassword');
$client->request('GET', '/api/entries', array('star' => 1, 'sort' => 'updated'), array(), $headers); $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$this->assertEquals(200, $client->getResponse()->getStatusCode()); $content = json_decode($this->client->getResponse()->getContent(), true);
$content = json_decode($client->getResponse()->getContent(), true);
$this->assertGreaterThanOrEqual(1, count($content)); $this->assertGreaterThanOrEqual(1, count($content));
$this->assertNotEmpty($content['_embedded']['items']); $this->assertNotEmpty($content['_embedded']['items']);
@ -183,7 +89,7 @@ class WallabagRestControllerTest extends WebTestCase
$this->assertGreaterThanOrEqual(1, $content['pages']); $this->assertGreaterThanOrEqual(1, $content['pages']);
$this->assertTrue( $this->assertTrue(
$client->getResponse()->headers->contains( $this->client->getResponse()->headers->contains(
'Content-Type', 'Content-Type',
'application/json' 'application/json'
) )
@ -192,14 +98,11 @@ class WallabagRestControllerTest extends WebTestCase
public function testGetArchiveEntries() public function testGetArchiveEntries()
{ {
$client = $this->createClient(); $this->client->request('GET', '/api/entries', array('archive' => 1));
$headers = $this->generateHeaders('admin', 'mypassword');
$client->request('GET', '/api/entries', array('archive' => 1), array(), $headers); $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$this->assertEquals(200, $client->getResponse()->getStatusCode()); $content = json_decode($this->client->getResponse()->getContent(), true);
$content = json_decode($client->getResponse()->getContent(), true);
$this->assertGreaterThanOrEqual(1, count($content)); $this->assertGreaterThanOrEqual(1, count($content));
$this->assertNotEmpty($content['_embedded']['items']); $this->assertNotEmpty($content['_embedded']['items']);
@ -208,7 +111,7 @@ class WallabagRestControllerTest extends WebTestCase
$this->assertGreaterThanOrEqual(1, $content['pages']); $this->assertGreaterThanOrEqual(1, $content['pages']);
$this->assertTrue( $this->assertTrue(
$client->getResponse()->headers->contains( $this->client->getResponse()->headers->contains(
'Content-Type', 'Content-Type',
'application/json' 'application/json'
) )
@ -217,10 +120,7 @@ class WallabagRestControllerTest extends WebTestCase
public function testDeleteEntry() public function testDeleteEntry()
{ {
$client = $this->createClient(); $entry = $this->client->getContainer()
$headers = $this->generateHeaders('admin', 'mypassword');
$entry = $client->getContainer()
->get('doctrine.orm.entity_manager') ->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry') ->getRepository('WallabagCoreBundle:Entry')
->findOneByUser(1); ->findOneByUser(1);
@ -229,36 +129,31 @@ class WallabagRestControllerTest extends WebTestCase
$this->markTestSkipped('No content found in db.'); $this->markTestSkipped('No content found in db.');
} }
$client->request('DELETE', '/api/entries/'.$entry->getId().'.json', array(), array(), $headers); $this->client->request('DELETE', '/api/entries/'.$entry->getId().'.json');
$this->assertEquals(200, $client->getResponse()->getStatusCode()); $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($client->getResponse()->getContent(), true); $content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertEquals($entry->getTitle(), $content['title']); $this->assertEquals($entry->getTitle(), $content['title']);
$this->assertEquals($entry->getUrl(), $content['url']); $this->assertEquals($entry->getUrl(), $content['url']);
// We'll try to delete this entry again // We'll try to delete this entry again
$headers = $this->generateHeaders('admin', 'mypassword'); $this->client->request('DELETE', '/api/entries/'.$entry->getId().'.json');
$client->request('DELETE', '/api/entries/'.$entry->getId().'.json', array(), array(), $headers); $this->assertEquals(404, $this->client->getResponse()->getStatusCode());
$this->assertEquals(404, $client->getResponse()->getStatusCode());
} }
public function testPostEntry() public function testPostEntry()
{ {
$client = $this->createClient(); $this->client->request('POST', '/api/entries.json', array(
$headers = $this->generateHeaders('admin', 'mypassword');
$client->request('POST', '/api/entries.json', array(
'url' => 'http://www.lemonde.fr/pixels/article/2015/03/28/plongee-dans-l-univers-d-ingress-le-jeu-de-google-aux-frontieres-du-reel_4601155_4408996.html', 'url' => 'http://www.lemonde.fr/pixels/article/2015/03/28/plongee-dans-l-univers-d-ingress-le-jeu-de-google-aux-frontieres-du-reel_4601155_4408996.html',
'tags' => 'google', 'tags' => 'google',
), array(), $headers); ));
$this->assertEquals(200, $client->getResponse()->getStatusCode()); $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($client->getResponse()->getContent(), true); $content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThan(0, $content['id']); $this->assertGreaterThan(0, $content['id']);
$this->assertEquals('http://www.lemonde.fr/pixels/article/2015/03/28/plongee-dans-l-univers-d-ingress-le-jeu-de-google-aux-frontieres-du-reel_4601155_4408996.html', $content['url']); $this->assertEquals('http://www.lemonde.fr/pixels/article/2015/03/28/plongee-dans-l-univers-d-ingress-le-jeu-de-google-aux-frontieres-du-reel_4601155_4408996.html', $content['url']);
@ -269,10 +164,7 @@ class WallabagRestControllerTest extends WebTestCase
public function testPatchEntry() public function testPatchEntry()
{ {
$client = $this->createClient(); $entry = $this->client->getContainer()
$headers = $this->generateHeaders('admin', 'mypassword');
$entry = $client->getContainer()
->get('doctrine.orm.entity_manager') ->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry') ->getRepository('WallabagCoreBundle:Entry')
->findOneByUser(1); ->findOneByUser(1);
@ -284,16 +176,16 @@ class WallabagRestControllerTest extends WebTestCase
// hydrate the tags relations // hydrate the tags relations
$nbTags = count($entry->getTags()); $nbTags = count($entry->getTags());
$client->request('PATCH', '/api/entries/'.$entry->getId().'.json', array( $this->client->request('PATCH', '/api/entries/'.$entry->getId().'.json', array(
'title' => 'New awesome title', 'title' => 'New awesome title',
'tags' => 'new tag '.uniqid(), 'tags' => 'new tag '.uniqid(),
'star' => true, 'star' => true,
'archive' => false, 'archive' => false,
), array(), $headers); ));
$this->assertEquals(200, $client->getResponse()->getStatusCode()); $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($client->getResponse()->getContent(), true); $content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertEquals($entry->getId(), $content['id']); $this->assertEquals($entry->getId(), $content['id']);
$this->assertEquals($entry->getUrl(), $content['url']); $this->assertEquals($entry->getUrl(), $content['url']);
@ -303,10 +195,7 @@ class WallabagRestControllerTest extends WebTestCase
public function testGetTagsEntry() public function testGetTagsEntry()
{ {
$client = $this->createClient(); $entry = $this->client->getContainer()
$headers = $this->generateHeaders('admin', 'mypassword');
$entry = $client->getContainer()
->get('doctrine.orm.entity_manager') ->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry') ->getRepository('WallabagCoreBundle:Entry')
->findOneWithTags(1); ->findOneWithTags(1);
@ -319,20 +208,17 @@ class WallabagRestControllerTest extends WebTestCase
$tags = array(); $tags = array();
foreach ($entry->getTags() as $tag) { foreach ($entry->getTags() as $tag) {
$tags[] = array('id' => $tag->getId(), 'label' => $tag->getLabel()); $tags[] = array('id' => $tag->getId(), 'label' => $tag->getLabel(), 'slug' => $tag->getSlug());
} }
$client->request('GET', '/api/entries/'.$entry->getId().'/tags', array(), array(), $headers); $this->client->request('GET', '/api/entries/'.$entry->getId().'/tags');
$this->assertEquals(json_encode($tags, JSON_HEX_QUOT), $client->getResponse()->getContent()); $this->assertEquals(json_encode($tags, JSON_HEX_QUOT), $this->client->getResponse()->getContent());
} }
public function testPostTagsOnEntry() public function testPostTagsOnEntry()
{ {
$client = $this->createClient(); $entry = $this->client->getContainer()
$headers = $this->generateHeaders('admin', 'mypassword');
$entry = $client->getContainer()
->get('doctrine.orm.entity_manager') ->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry') ->getRepository('WallabagCoreBundle:Entry')
->findOneByUser(1); ->findOneByUser(1);
@ -345,16 +231,16 @@ class WallabagRestControllerTest extends WebTestCase
$newTags = 'tag1,tag2,tag3'; $newTags = 'tag1,tag2,tag3';
$client->request('POST', '/api/entries/'.$entry->getId().'/tags', array('tags' => $newTags), array(), $headers); $this->client->request('POST', '/api/entries/'.$entry->getId().'/tags', array('tags' => $newTags));
$this->assertEquals(200, $client->getResponse()->getStatusCode()); $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($client->getResponse()->getContent(), true); $content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertArrayHasKey('tags', $content); $this->assertArrayHasKey('tags', $content);
$this->assertEquals($nbTags + 3, count($content['tags'])); $this->assertEquals($nbTags + 3, count($content['tags']));
$entryDB = $client->getContainer() $entryDB = $this->client->getContainer()
->get('doctrine.orm.entity_manager') ->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry') ->getRepository('WallabagCoreBundle:Entry')
->find($entry->getId()); ->find($entry->getId());
@ -369,15 +255,13 @@ class WallabagRestControllerTest extends WebTestCase
} }
} }
public function testDeleteOneTagEntrie() public function testDeleteOneTagEntry()
{ {
$client = $this->createClient(); $entry = $this->client->getContainer()
$headers = $this->generateHeaders('admin', 'mypassword');
$entry = $client->getContainer()
->get('doctrine.orm.entity_manager') ->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry') ->getRepository('WallabagCoreBundle:Entry')
->findOneByUser(1); ->findOneWithTags(1);
$entry = $entry[0];
if (!$entry) { if (!$entry) {
$this->markTestSkipped('No content found in db.'); $this->markTestSkipped('No content found in db.');
@ -387,11 +271,11 @@ class WallabagRestControllerTest extends WebTestCase
$nbTags = count($entry->getTags()); $nbTags = count($entry->getTags());
$tag = $entry->getTags()[0]; $tag = $entry->getTags()[0];
$client->request('DELETE', '/api/entries/'.$entry->getId().'/tags/'.$tag->getId().'.json', array(), array(), $headers); $this->client->request('DELETE', '/api/entries/'.$entry->getId().'/tags/'.$tag->getId().'.json');
$this->assertEquals(200, $client->getResponse()->getStatusCode()); $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$content = json_decode($client->getResponse()->getContent(), true); $content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertArrayHasKey('tags', $content); $this->assertArrayHasKey('tags', $content);
$this->assertEquals($nbTags - 1, count($content['tags'])); $this->assertEquals($nbTags - 1, count($content['tags']));
@ -399,14 +283,11 @@ class WallabagRestControllerTest extends WebTestCase
public function testGetUserTags() public function testGetUserTags()
{ {
$client = $this->createClient(); $this->client->request('GET', '/api/tags.json');
$headers = $this->generateHeaders('admin', 'mypassword');
$client->request('GET', '/api/tags.json', array(), array(), $headers); $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$this->assertEquals(200, $client->getResponse()->getStatusCode()); $content = json_decode($this->client->getResponse()->getContent(), true);
$content = json_decode($client->getResponse()->getContent(), true);
$this->assertGreaterThan(0, $content); $this->assertGreaterThan(0, $content);
$this->assertArrayHasKey('id', $content[0]); $this->assertArrayHasKey('id', $content[0]);
@ -420,16 +301,21 @@ class WallabagRestControllerTest extends WebTestCase
*/ */
public function testDeleteUserTag($tag) public function testDeleteUserTag($tag)
{ {
$client = $this->createClient(); $this->client->request('DELETE', '/api/tags/'.$tag['id'].'.json');
$headers = $this->generateHeaders('admin', 'mypassword');
$client->request('DELETE', '/api/tags/'.$tag['id'].'.json', array(), array(), $headers); $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
$this->assertEquals(200, $client->getResponse()->getStatusCode()); $content = json_decode($this->client->getResponse()->getContent(), true);
$content = json_decode($client->getResponse()->getContent(), true);
$this->assertArrayHasKey('label', $content); $this->assertArrayHasKey('label', $content);
$this->assertEquals($tag['label'], $content['label']); $this->assertEquals($tag['label'], $content['label']);
$this->assertEquals($tag['slug'], $content['slug']);
$entries = $entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findAllByTagId($this->user->getId(), $tag['id']);
$this->assertCount(0, $entries);
} }
} }

View File

@ -0,0 +1,51 @@
<?php
namespace Wallabag\ApiBundle\Tests;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\BrowserKit\Cookie;
abstract class WallabagApiTestCase extends WebTestCase
{
/**
* @var Client
*/
protected $client = null;
/**
* @var \FOS\UserBundle\Model\UserInterface
*/
protected $user;
public function setUp()
{
$this->client = $this->createAuthorizedClient();
}
/**
* @return Client
*/
protected function createAuthorizedClient()
{
$client = static::createClient();
$container = $client->getContainer();
/** @var $userManager \FOS\UserBundle\Doctrine\UserManager */
$userManager = $container->get('fos_user.user_manager');
/** @var $loginManager \FOS\UserBundle\Security\LoginManager */
$loginManager = $container->get('fos_user.security.login_manager');
$firewallName = $container->getParameter('fos_user.firewall_name');
$this->user = $userManager->findUserBy(array('username' => 'admin'));
$loginManager->loginUser($firewallName, $this->user);
// save the login token into the session and put it in a cookie
$container->get('session')->set('_security_'.$firewallName, serialize($container->get('security.token_storage')->getToken()));
$container->get('session')->save();
$session = $container->get('session');
$client->getCookieJar()->set(new Cookie($session->getName(), $session->getId()));
return $client;
}
}

View File

@ -3,16 +3,7 @@
namespace Wallabag\ApiBundle; namespace Wallabag\ApiBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle; use Symfony\Component\HttpKernel\Bundle\Bundle;
use Wallabag\ApiBundle\DependencyInjection\Security\Factory\WsseFactory;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class WallabagApiBundle extends Bundle class WallabagApiBundle extends Bundle
{ {
public function build(ContainerBuilder $container)
{
parent::build($container);
$extension = $container->getExtension('security');
$extension->addSecurityListenerFactory(new WsseFactory());
}
} }

View File

@ -3,12 +3,14 @@
namespace Wallabag\CoreBundle\Command; namespace Wallabag\CoreBundle\Command;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand; use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Output\NullOutput; use Symfony\Component\Console\Output\NullOutput;
use Wallabag\CoreBundle\Entity\User; use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
use Wallabag\CoreBundle\Entity\Config; use Wallabag\CoreBundle\Entity\Config;
class InstallCommand extends ContainerAwareCommand class InstallCommand extends ContainerAwareCommand
@ -53,7 +55,7 @@ class InstallCommand extends ContainerAwareCommand
; ;
$output->writeln('<info>Wallabag has been successfully installed.</info>'); $output->writeln('<info>Wallabag has been successfully installed.</info>');
$output->writeln('<comment>Just execute `php app/console server:run` for using wallabag: http://localhost:8000</comment>'); $output->writeln('<comment>Just execute `php bin/console server:run` for using wallabag: http://localhost:8000</comment>');
} }
protected function checkRequirements() protected function checkRequirements()
@ -85,17 +87,18 @@ class InstallCommand extends ContainerAwareCommand
} }
$rows[] = array($label, $status, $help); $rows[] = array($label, $status, $help);
$this->getHelper('table') $table = new Table($this->defaultOutput);
$table
->setHeaders(array('Checked', 'Status', 'Recommendation')) ->setHeaders(array('Checked', 'Status', 'Recommendation'))
->setRows($rows) ->setRows($rows)
->render($this->defaultOutput); ->render();
if (!$fulfilled) { if (!$fulfilled) {
throw new \RuntimeException('Some system requirements are not fulfilled. Please check output messages and fix them.'); throw new \RuntimeException('Some system requirements are not fulfilled. Please check output messages and fix them.');
} else {
$this->defaultOutput->writeln('<info>Success! Your system can run Wallabag properly.</info>');
} }
$this->defaultOutput->writeln('<info>Success! Your system can run Wallabag properly.</info>');
$this->defaultOutput->writeln(''); $this->defaultOutput->writeln('');
return $this; return $this;
@ -107,14 +110,17 @@ class InstallCommand extends ContainerAwareCommand
// user want to reset everything? Don't care about what is already here // user want to reset everything? Don't care about what is already here
if (true === $this->defaultInput->getOption('reset')) { if (true === $this->defaultInput->getOption('reset')) {
$this->defaultOutput->writeln('Droping database, creating database and schema'); $this->defaultOutput->writeln('Droping database, creating database and schema, clearing the cache');
$this $this
->runCommand('doctrine:database:drop', array('--force' => true)) ->runCommand('doctrine:database:drop', array('--force' => true))
->runCommand('doctrine:database:create') ->runCommand('doctrine:database:create')
->runCommand('doctrine:schema:create') ->runCommand('doctrine:schema:create')
->runCommand('cache:clear')
; ;
$this->defaultOutput->writeln('');
return $this; return $this;
} }
@ -127,12 +133,15 @@ class InstallCommand extends ContainerAwareCommand
->runCommand('cache:clear') ->runCommand('cache:clear')
; ;
$this->defaultOutput->writeln('');
return $this; return $this;
} }
$dialog = $this->getHelper('dialog'); $questionHelper = $this->getHelper('question');
$question = new ConfirmationQuestion('It appears that your database already exists. Would you like to reset it? (y/N)', false);
if ($dialog->askConfirmation($this->defaultOutput, '<question>It appears that your database already exists. Would you like to reset it? (y/N)</question> ', false)) { if ($questionHelper->ask($this->defaultInput, $this->defaultOutput, $question)) {
$this->defaultOutput->writeln('Droping database, creating database and schema'); $this->defaultOutput->writeln('Droping database, creating database and schema');
$this $this
@ -141,7 +150,8 @@ class InstallCommand extends ContainerAwareCommand
->runCommand('doctrine:schema:create') ->runCommand('doctrine:schema:create')
; ;
} elseif ($this->isSchemaPresent()) { } elseif ($this->isSchemaPresent()) {
if ($dialog->askConfirmation($this->defaultOutput, '<question>Seems like your database contains schema. Do you want to reset it? (y/N)</question> ', false)) { $question = new ConfirmationQuestion('Seems like your database contains schema. Do you want to reset it? (y/N)', false);
if ($questionHelper->ask($this->defaultInput, $this->defaultOutput, $question)) {
$this->defaultOutput->writeln('Droping schema and creating schema'); $this->defaultOutput->writeln('Droping schema and creating schema');
$this $this
@ -160,17 +170,6 @@ class InstallCommand extends ContainerAwareCommand
$this->defaultOutput->writeln('Clearing the cache'); $this->defaultOutput->writeln('Clearing the cache');
$this->runCommand('cache:clear'); $this->runCommand('cache:clear');
/*
if ($this->getHelperSet()->get('dialog')->askConfirmation($this->defaultOutput, '<question>Load fixtures (Y/N)?</question>', false)) {
$doctrineConfig = $this->getContainer()->get('doctrine.orm.entity_manager')->getConnection()->getConfiguration();
$logger = $doctrineConfig->getSQLLogger();
// speed up fixture load
$doctrineConfig->setSQLLogger(null);
$this->runCommand('doctrine:fixtures:load');
$doctrineConfig->setSQLLogger($logger);
}
*/
$this->defaultOutput->writeln(''); $this->defaultOutput->writeln('');
return $this; return $this;
@ -180,18 +179,27 @@ class InstallCommand extends ContainerAwareCommand
{ {
$this->defaultOutput->writeln('<info><comment>Step 3 of 4.</comment> Administration setup.</info>'); $this->defaultOutput->writeln('<info><comment>Step 3 of 4.</comment> Administration setup.</info>');
$dialog = $this->getHelperSet()->get('dialog'); $questionHelper = $this->getHelperSet()->get('question');
$question = new ConfirmationQuestion('Would you like to create a new user ? (y/N)', false);
if (false === $dialog->askConfirmation($this->defaultOutput, '<question>Would you like to create a new user ? (y/N)</question>', true)) { if (!$questionHelper->ask($this->defaultInput, $this->defaultOutput, $question)) {
return $this; return $this;
} }
$em = $this->getContainer()->get('doctrine.orm.entity_manager'); $em = $this->getContainer()->get('doctrine.orm.entity_manager');
$user = new User(); $userManager = $this->getContainer()->get('fos_user.user_manager');
$user->setUsername($dialog->ask($this->defaultOutput, '<question>Username</question> <comment>(default: wallabag)</comment> :', 'wallabag')); $user = $userManager->createUser();
$user->setPassword($dialog->ask($this->defaultOutput, '<question>Password</question> <comment>(default: wallabag)</comment> :', 'wallabag'));
$user->setEmail($dialog->ask($this->defaultOutput, '<question>Email:</question>', '')); $question = new Question('Username (default: wallabag) :', 'wallabag');
$user->setUsername($questionHelper->ask($this->defaultInput, $this->defaultOutput, $question));
$question = new Question('Password (default: wallabag) :', 'wallabag');
$user->setPlainPassword($questionHelper->ask($this->defaultInput, $this->defaultOutput, $question));
$question = new Question('Email:', '');
$user->setEmail($questionHelper->ask($this->defaultInput, $this->defaultOutput, $question));
$user->setEnabled(true); $user->setEnabled(true);
$em->persist($user); $em->persist($user);
@ -279,10 +287,16 @@ class InstallCommand extends ContainerAwareCommand
try { try {
$schemaManager = $connection->getSchemaManager(); $schemaManager = $connection->getSchemaManager();
} catch (\Exception $exception) { } catch (\Exception $exception) {
// mysql & sqlite
if (false !== strpos($exception->getMessage(), sprintf("Unknown database '%s'", $databaseName))) { if (false !== strpos($exception->getMessage(), sprintf("Unknown database '%s'", $databaseName))) {
return false; return false;
} }
// pgsql
if (false !== strpos($exception->getMessage(), sprintf('database "%s" does not exist', $databaseName))) {
return false;
}
throw $exception; throw $exception;
} }

View File

@ -0,0 +1,66 @@
<?php
namespace Wallabag\CoreBundle\Command;
use Doctrine\ORM\NoResultException;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class TagAllCommand extends ContainerAwareCommand
{
protected function configure()
{
$this
->setName('wallabag:tag:all')
->setDescription('Tag all entries using the tagging rules.')
->addArgument(
'username',
InputArgument::REQUIRED,
'User to tag entries for.'
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
try {
$user = $this->getUser($input->getArgument('username'));
} catch (NoResultException $e) {
$output->writeln(sprintf('<error>User "%s" not found.</error>', $input->getArgument('username')));
return 1;
}
$tagger = $this->getContainer()->get('wallabag_core.rule_based_tagger');
$output->write(sprintf('Tagging entries for user « <info>%s</info> »... ', $user->getUserName()));
$entries = $tagger->tagAllForUser($user);
$em = $this->getDoctrine()->getManager();
foreach ($entries as $entry) {
$em->persist($entry);
}
$em->flush();
$output->writeln('<info>Done.</info>');
}
/**
* Fetches a user from its username.
*
* @param string $username
*
* @return \Wallabag\UserBundle\Entity\User
*/
private function getUser($username)
{
return $this->getDoctrine()->getRepository('WallabagUserBundle:User')->findOneByUserName($username);
}
private function getDoctrine()
{
return $this->getContainer()->get('doctrine');
}
}

View File

@ -4,15 +4,18 @@ namespace Wallabag\CoreBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Wallabag\CoreBundle\Entity\Config; use Wallabag\CoreBundle\Entity\Config;
use Wallabag\CoreBundle\Entity\User; use Wallabag\CoreBundle\Entity\TaggingRule;
use Wallabag\CoreBundle\Form\Type\ConfigType;
use Wallabag\CoreBundle\Form\Type\ChangePasswordType; use Wallabag\CoreBundle\Form\Type\ChangePasswordType;
use Wallabag\CoreBundle\Form\Type\UserInformationType;
use Wallabag\CoreBundle\Form\Type\NewUserType; use Wallabag\CoreBundle\Form\Type\NewUserType;
use Wallabag\CoreBundle\Form\Type\RssType; use Wallabag\CoreBundle\Form\Type\RssType;
use Wallabag\CoreBundle\Form\Type\TaggingRuleType;
use Wallabag\CoreBundle\Form\Type\UserInformationType;
use Wallabag\CoreBundle\Tools\Utils; use Wallabag\CoreBundle\Tools\Utils;
use Wallabag\UserBundle\Entity\User;
class ConfigController extends Controller class ConfigController extends Controller
{ {
@ -25,10 +28,11 @@ class ConfigController extends Controller
{ {
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
$config = $this->getConfig(); $config = $this->getConfig();
$userManager = $this->container->get('fos_user.user_manager');
$user = $this->getUser(); $user = $this->getUser();
// handle basic config detail (this form is defined as a service) // handle basic config detail (this form is defined as a service)
$configForm = $this->createForm('config', $config); $configForm = $this->createForm(ConfigType::class, $config, array('action' => $this->generateUrl('config')));
$configForm->handleRequest($request); $configForm->handleRequest($request);
if ($configForm->isValid()) { if ($configForm->isValid()) {
@ -41,20 +45,19 @@ class ConfigController extends Controller
$this->get('session')->getFlashBag()->add( $this->get('session')->getFlashBag()->add(
'notice', 'notice',
'Config saved' 'Config saved. Some parameters will be considered after disconnection.'
); );
return $this->redirect($this->generateUrl('config')); return $this->redirect($this->generateUrl('config'));
} }
// handle changing password // handle changing password
$pwdForm = $this->createForm(new ChangePasswordType()); $pwdForm = $this->createForm(ChangePasswordType::class, null, array('action' => $this->generateUrl('config').'#set4'));
$pwdForm->handleRequest($request); $pwdForm->handleRequest($request);
if ($pwdForm->isValid()) { if ($pwdForm->isValid()) {
$user->setPassword($pwdForm->get('new_password')->getData()); $user->setPlainPassword($pwdForm->get('new_password')->getData());
$em->persist($user); $userManager->updateUser($user, true);
$em->flush();
$this->get('session')->getFlashBag()->add( $this->get('session')->getFlashBag()->add(
'notice', 'notice',
@ -65,12 +68,14 @@ class ConfigController extends Controller
} }
// handle changing user information // handle changing user information
$userForm = $this->createForm(new UserInformationType(), $user, array('validation_groups' => array('Profile'))); $userForm = $this->createForm(UserInformationType::class, $user, array(
'validation_groups' => array('Profile'),
'action' => $this->generateUrl('config').'#set3',
));
$userForm->handleRequest($request); $userForm->handleRequest($request);
if ($userForm->isValid()) { if ($userForm->isValid()) {
$em->persist($user); $userManager->updateUser($user, true);
$em->flush();
$this->get('session')->getFlashBag()->add( $this->get('session')->getFlashBag()->add(
'notice', 'notice',
@ -81,7 +86,7 @@ class ConfigController extends Controller
} }
// handle rss information // handle rss information
$rssForm = $this->createForm(new RssType(), $config); $rssForm = $this->createForm(RssType::class, $config, array('action' => $this->generateUrl('config').'#set2'));
$rssForm->handleRequest($request); $rssForm->handleRequest($request);
if ($rssForm->isValid()) { if ($rssForm->isValid()) {
@ -96,13 +101,36 @@ class ConfigController extends Controller
return $this->redirect($this->generateUrl('config')); return $this->redirect($this->generateUrl('config'));
} }
// handle tagging rule
$taggingRule = new TaggingRule();
$newTaggingRule = $this->createForm(TaggingRuleType::class, $taggingRule, array('action' => $this->generateUrl('config').'#set5'));
$newTaggingRule->handleRequest($request);
if ($newTaggingRule->isValid()) {
$taggingRule->setConfig($config);
$em->persist($taggingRule);
$em->flush();
$this->get('session')->getFlashBag()->add(
'notice',
'Tagging rules updated'
);
return $this->redirect($this->generateUrl('config'));
}
// handle adding new user // handle adding new user
$newUser = new User(); $newUser = $userManager->createUser();
$newUserForm = $this->createForm(new NewUserType(), $newUser, array('validation_groups' => array('Profile'))); // enable created user by default
$newUser->setEnabled(true);
$newUserForm = $this->createForm(NewUserType::class, $newUser, array(
'validation_groups' => array('Profile'),
'action' => $this->generateUrl('config').'#set6',
));
$newUserForm->handleRequest($request); $newUserForm->handleRequest($request);
if ($newUserForm->isValid()) { if ($newUserForm->isValid() && $this->get('security.authorization_checker')->isGranted('ROLE_SUPER_ADMIN')) {
$em->persist($newUser); $userManager->updateUser($newUser, true);
$config = new Config($newUser); $config = new Config($newUser);
$config->setTheme($this->container->getParameter('theme')); $config->setTheme($this->container->getParameter('theme'));
@ -129,6 +157,7 @@ class ConfigController extends Controller
'pwd' => $pwdForm->createView(), 'pwd' => $pwdForm->createView(),
'user' => $userForm->createView(), 'user' => $userForm->createView(),
'new_user' => $newUserForm->createView(), 'new_user' => $newUserForm->createView(),
'new_tagging_rule' => $newTaggingRule->createView(),
), ),
'rss' => array( 'rss' => array(
'username' => $user->getUsername(), 'username' => $user->getUsername(),
@ -160,6 +189,33 @@ class ConfigController extends Controller
return $request->headers->get('referer') ? $this->redirect($request->headers->get('referer')) : $this->redirectToRoute('config'); return $request->headers->get('referer') ? $this->redirect($request->headers->get('referer')) : $this->redirectToRoute('config');
} }
/**
* Deletes a tagging rule and redirect to the config homepage.
*
* @param TaggingRule $rule
*
* @Route("/tagging-rule/delete/{id}", requirements={"id" = "\d+"}, name="delete_tagging_rule")
*
* @return \Symfony\Component\HttpFoundation\RedirectResponse
*/
public function deleteTaggingRule(TaggingRule $rule)
{
if ($this->getUser()->getId() != $rule->getConfig()->getUser()->getId()) {
throw $this->createAccessDeniedException('You can not access this tagging ryle.');
}
$em = $this->getDoctrine()->getManager();
$em->remove($rule);
$em->flush();
$this->get('session')->getFlashBag()->add(
'notice',
'Tagging rule deleted'
);
return $this->redirect($this->generateUrl('config'));
}
/** /**
* Retrieve config for the current user. * Retrieve config for the current user.
* If no config were found, create a new one. * If no config were found, create a new one.

View File

@ -2,18 +2,36 @@
namespace Wallabag\CoreBundle\Controller; namespace Wallabag\CoreBundle\Controller;
use Pagerfanta\Adapter\DoctrineORMAdapter;
use Pagerfanta\Pagerfanta;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Wallabag\CoreBundle\Entity\Entry; use Wallabag\CoreBundle\Entity\Entry;
use Wallabag\CoreBundle\Form\Type\NewEntryType;
use Wallabag\CoreBundle\Form\Type\EditEntryType;
use Wallabag\CoreBundle\Filter\EntryFilterType; use Wallabag\CoreBundle\Filter\EntryFilterType;
use Pagerfanta\Adapter\DoctrineORMAdapter; use Wallabag\CoreBundle\Form\Type\EditEntryType;
use Pagerfanta\Pagerfanta; use Wallabag\CoreBundle\Form\Type\NewEntryType;
class EntryController extends Controller class EntryController extends Controller
{ {
/**
* @param Entry $entry
*/
private function updateEntry(Entry $entry)
{
try {
$entry = $this->get('wallabag_core.content_proxy')->updateEntry($entry, $entry->getUrl());
$em = $this->getDoctrine()->getManager();
$em->persist($entry);
$em->flush();
} catch (\Exception $e) {
return false;
}
return true;
}
/** /**
* @param Request $request * @param Request $request
* *
@ -25,17 +43,24 @@ class EntryController extends Controller
{ {
$entry = new Entry($this->getUser()); $entry = new Entry($this->getUser());
$form = $this->createForm(new NewEntryType(), $entry); $form = $this->createForm(NewEntryType::class, $entry);
$form->handleRequest($request); $form->handleRequest($request);
if ($form->isValid()) { if ($form->isValid()) {
$entry = $this->get('wallabag_core.content_proxy')->updateEntry($entry, $entry->getUrl()); // check for existing entry, if it exists, redirect to it with a message
$existingEntry = $this->get('wallabag_core.entry_repository')->findByUrlAndUserId($entry->getUrl(), $this->getUser()->getId());
$em = $this->getDoctrine()->getManager(); if (false !== $existingEntry) {
$em->persist($entry); $this->get('session')->getFlashBag()->add(
$em->flush(); 'notice',
'Entry already saved on '.$existingEntry->getCreatedAt()->format('d-m-Y')
);
return $this->redirect($this->generateUrl('view', array('id' => $existingEntry->getId())));
}
$this->updateEntry($entry);
$this->get('session')->getFlashBag()->add( $this->get('session')->getFlashBag()->add(
'notice', 'notice',
'Entry saved' 'Entry saved'
@ -49,6 +74,22 @@ class EntryController extends Controller
)); ));
} }
/**
* @param Request $request
*
* @Route("/bookmarklet", name="bookmarklet")
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function addEntryViaBookmarklet(Request $request)
{
$entry = new Entry($this->getUser());
$entry->setUrl($request->get('url'));
$this->updateEntry($entry);
return $this->redirect($this->generateUrl('homepage'));
}
/** /**
* @param Request $request * @param Request $request
* *
@ -75,7 +116,7 @@ class EntryController extends Controller
{ {
$this->checkUserAction($entry); $this->checkUserAction($entry);
$form = $this->createForm(new EditEntryType(), $entry); $form = $this->createForm(EditEntryType::class, $entry);
$form->handleRequest($request); $form->handleRequest($request);
@ -124,6 +165,11 @@ class EntryController extends Controller
*/ */
public function showUnreadAction(Request $request, $page) public function showUnreadAction(Request $request, $page)
{ {
// load the quickstart if no entry in database
if ($page == 1 && $this->get('wallabag_core.entry_repository')->countAllEntriesByUsername($this->getUser()->getId()) == 0) {
return $this->redirect($this->generateUrl('quickstart'));
}
return $this->showEntries('unread', $request, $page); return $this->showEntries('unread', $request, $page);
} }
@ -169,7 +215,7 @@ class EntryController extends Controller
*/ */
private function showEntries($type, Request $request, $page) private function showEntries($type, Request $request, $page)
{ {
$repository = $this->getDoctrine()->getRepository('WallabagCoreBundle:Entry'); $repository = $this->get('wallabag_core.entry_repository');
switch ($type) { switch ($type) {
case 'starred': case 'starred':
@ -192,7 +238,7 @@ class EntryController extends Controller
throw new \InvalidArgumentException(sprintf('Type "%s" is not implemented.', $type)); throw new \InvalidArgumentException(sprintf('Type "%s" is not implemented.', $type));
} }
$form = $this->get('form.factory')->create(new EntryFilterType()); $form = $this->createForm(EntryFilterType::class);
if ($request->query->has($form->getName())) { if ($request->query->has($form->getName())) {
// manually bind values from the request // manually bind values from the request
@ -237,6 +283,33 @@ class EntryController extends Controller
); );
} }
/**
* Reload an entry.
* Refetch content from the website and make it readable again.
*
* @param Entry $entry
*
* @Route("/reload/{id}", requirements={"id" = "\d+"}, name="reload_entry")
*
* @return \Symfony\Component\HttpFoundation\RedirectResponse
*/
public function reloadAction(Entry $entry)
{
$this->checkUserAction($entry);
$message = 'Entry reloaded';
if (false === $this->updateEntry($entry)) {
$message = 'Failed to reload entry';
}
$this->get('session')->getFlashBag()->add(
'notice',
$message
);
return $this->redirect($this->generateUrl('view', array('id' => $entry->getId())));
}
/** /**
* Changes read status for an entry. * Changes read status for an entry.
* *
@ -288,7 +361,7 @@ class EntryController extends Controller
} }
/** /**
* Deletes entry and redirect to the homepage. * Deletes entry and redirect to the homepage or the last viewed page.
* *
* @param Entry $entry * @param Entry $entry
* *
@ -296,10 +369,18 @@ class EntryController extends Controller
* *
* @return \Symfony\Component\HttpFoundation\RedirectResponse * @return \Symfony\Component\HttpFoundation\RedirectResponse
*/ */
public function deleteEntryAction(Entry $entry) public function deleteEntryAction(Request $request, Entry $entry)
{ {
$this->checkUserAction($entry); $this->checkUserAction($entry);
// generates the view url for this entry to check for redirection later
// to avoid redirecting to the deleted entry. Ugh.
$url = $this->generateUrl(
'view',
array('id' => $entry->getId()),
UrlGeneratorInterface::ABSOLUTE_URL
);
$em = $this->getDoctrine()->getManager(); $em = $this->getDoctrine()->getManager();
$em->remove($entry); $em->remove($entry);
$em->flush(); $em->flush();
@ -309,7 +390,8 @@ class EntryController extends Controller
'Entry deleted' 'Entry deleted'
); );
return $this->redirect($this->generateUrl('homepage')); // don't redirect user to the deleted entry
return $this->redirect($url !== $request->headers->get('referer') ? $request->headers->get('referer') : $this->generateUrl('homepage'));
} }
/** /**

View File

@ -0,0 +1,65 @@
<?php
namespace Wallabag\CoreBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Wallabag\CoreBundle\Entity\Entry;
/**
* The try/catch can be removed once all formats will be implemented.
* Still need implementation: txt.
*/
class ExportController extends Controller
{
/**
* Gets one entry content.
*
* @param Entry $entry
*
* @Route("/export/{id}.{format}", name="export_entry", requirements={
* "format": "epub|mobi|pdf|json|xml|txt|csv",
* "id": "\d+"
* })
*/
public function downloadEntryAction(Entry $entry, $format)
{
try {
return $this->get('wallabag_core.helper.entries_export')
->setEntries($entry)
->updateTitle('entry')
->exportAs($format);
} catch (\InvalidArgumentException $e) {
throw new NotFoundHttpException($e->getMessage());
}
}
/**
* Export all entries for current user.
*
* @Route("/export/{category}.{format}", name="export_entries", requirements={
* "format": "epub|mobi|pdf|json|xml|txt|csv",
* "category": "all|unread|starred|archive"
* })
*/
public function downloadEntriesAction($format, $category)
{
$method = ucfirst($category);
$methodBuilder = 'getBuilderFor'.$method.'ByUser';
$entries = $this->getDoctrine()
->getRepository('WallabagCoreBundle:Entry')
->$methodBuilder($this->getUser()->getId())
->getQuery()
->getResult();
try {
return $this->get('wallabag_core.helper.entries_export')
->setEntries($entries)
->updateTitle($method)
->exportAs($format);
} catch (\InvalidArgumentException $e) {
throw new NotFoundHttpException($e->getMessage());
}
}
}

View File

@ -2,13 +2,13 @@
namespace Wallabag\CoreBundle\Controller; namespace Wallabag\CoreBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Wallabag\CoreBundle\Entity\User;
use Wallabag\CoreBundle\Entity\Entry;
use Pagerfanta\Adapter\DoctrineORMAdapter; use Pagerfanta\Adapter\DoctrineORMAdapter;
use Pagerfanta\Pagerfanta; use Pagerfanta\Pagerfanta;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Wallabag\CoreBundle\Entity\Entry;
use Wallabag\UserBundle\Entity\User;
class RssController extends Controller class RssController extends Controller
{ {
@ -16,7 +16,7 @@ class RssController extends Controller
* Shows unread entries for current user. * Shows unread entries for current user.
* *
* @Route("/{username}/{token}/unread.xml", name="unread_rss", defaults={"_format"="xml"}) * @Route("/{username}/{token}/unread.xml", name="unread_rss", defaults={"_format"="xml"})
* @ParamConverter("user", class="WallabagCoreBundle:User", converter="username_rsstoken_converter") * @ParamConverter("user", class="WallabagUserBundle:User", converter="username_rsstoken_converter")
* *
* @return \Symfony\Component\HttpFoundation\Response * @return \Symfony\Component\HttpFoundation\Response
*/ */
@ -29,7 +29,7 @@ class RssController extends Controller
* Shows read entries for current user. * Shows read entries for current user.
* *
* @Route("/{username}/{token}/archive.xml", name="archive_rss") * @Route("/{username}/{token}/archive.xml", name="archive_rss")
* @ParamConverter("user", class="WallabagCoreBundle:User", converter="username_rsstoken_converter") * @ParamConverter("user", class="WallabagUserBundle:User", converter="username_rsstoken_converter")
* *
* @return \Symfony\Component\HttpFoundation\Response * @return \Symfony\Component\HttpFoundation\Response
*/ */
@ -42,7 +42,7 @@ class RssController extends Controller
* Shows starred entries for current user. * Shows starred entries for current user.
* *
* @Route("/{username}/{token}/starred.xml", name="starred_rss") * @Route("/{username}/{token}/starred.xml", name="starred_rss")
* @ParamConverter("user", class="WallabagCoreBundle:User", converter="username_rsstoken_converter") * @ParamConverter("user", class="WallabagUserBundle:User", converter="username_rsstoken_converter")
* *
* @return \Symfony\Component\HttpFoundation\Response * @return \Symfony\Component\HttpFoundation\Response
*/ */

View File

@ -1,153 +0,0 @@
<?php
namespace Wallabag\CoreBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\SecurityContext;
use Wallabag\CoreBundle\Form\Type\ResetPasswordType;
class SecurityController extends Controller
{
public function loginAction(Request $request)
{
$session = $request->getSession();
// get the login error if there is one
if ($request->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) {
$error = $request->attributes->get(SecurityContext::AUTHENTICATION_ERROR);
} else {
$error = $session->get(SecurityContext::AUTHENTICATION_ERROR);
$session->remove(SecurityContext::AUTHENTICATION_ERROR);
}
return $this->render('WallabagCoreBundle:Security:login.html.twig', array(
// last username entered by the user
'last_username' => $session->get(SecurityContext::LAST_USERNAME),
'error' => $error,
));
}
/**
* Request forgot password: show form.
*
* @Route("/forgot-password", name="forgot_password")
*
* @Method({"GET", "POST"})
*/
public function forgotPasswordAction(Request $request)
{
$form = $this->createForm('forgot_password');
$form->handleRequest($request);
if ($form->isValid()) {
$user = $this->getDoctrine()->getRepository('WallabagCoreBundle:User')->findOneByEmail($form->get('email')->getData());
// generate "hard" token
$user->setConfirmationToken(rtrim(strtr(base64_encode(hash('sha256', uniqid(mt_rand(), true), true)), '+/', '-_'), '='));
$user->setPasswordRequestedAt(new \DateTime());
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
$message = \Swift_Message::newInstance()
->setSubject('Reset Password')
->setFrom($this->container->getParameter('from_email'))
->setTo($user->getEmail())
->setBody($this->renderView('WallabagCoreBundle:Mail:forgotPassword.txt.twig', array(
'username' => $user->getUsername(),
'confirmationUrl' => $this->generateUrl('forgot_password_reset', array('token' => $user->getConfirmationToken()), true),
)))
;
$this->get('mailer')->send($message);
return $this->redirect($this->generateUrl('forgot_password_check_email',
array('email' => $this->getObfuscatedEmail($user->getEmail()))
));
}
return $this->render('WallabagCoreBundle:Security:forgotPassword.html.twig', array(
'form' => $form->createView(),
));
}
/**
* Tell the user to check his email provider.
*
* @Route("/forgot-password/check-email", name="forgot_password_check_email")
*
* @Method({"GET"})
*/
public function checkEmailAction(Request $request)
{
$email = $request->query->get('email');
if (empty($email)) {
// the user does not come from the forgotPassword action
return $this->redirect($this->generateUrl('forgot_password'));
}
return $this->render('WallabagCoreBundle:Security:checkEmail.html.twig', array(
'email' => $email,
));
}
/**
* Reset user password.
*
* @Route("/forgot-password/{token}", name="forgot_password_reset")
*
* @Method({"GET", "POST"})
*/
public function resetAction(Request $request, $token)
{
$user = $this->getDoctrine()->getRepository('WallabagCoreBundle:User')->findOneByConfirmationToken($token);
if (null === $user) {
throw $this->createNotFoundException(sprintf('No user found with token "%s"', $token));
}
$form = $this->createForm(new ResetPasswordType());
$form->handleRequest($request);
if ($form->isValid()) {
$user->setPassword($form->get('new_password')->getData());
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
$this->get('session')->getFlashBag()->add(
'notice',
'The password has been reset successfully'
);
return $this->redirect($this->generateUrl('login'));
}
return $this->render('WallabagCoreBundle:Security:reset.html.twig', array(
'token' => $token,
'form' => $form->createView(),
));
}
/**
* Get the truncated email displayed when requesting the resetting.
*
* Keeping only the part following @ in the address.
*
* @param string $email
*
* @return string
*/
protected function getObfuscatedEmail($email)
{
if (false !== $pos = strpos($email, '@')) {
$email = '...'.substr($email, $pos);
}
return $email;
}
}

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