aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.dockerignore3
-rw-r--r--.gitattributes1
-rw-r--r--.gitignore3
-rw-r--r--.htaccess25
-rw-r--r--.travis.yml2
-rw-r--r--AUTHORS12
-rw-r--r--CHANGELOG.md39
-rw-r--r--Dockerfile.armhf47
-rw-r--r--Doxyfile2377
-rw-r--r--Makefile78
-rw-r--r--README.md4
-rw-r--r--application/ApplicationUtils.php92
-rw-r--r--application/FileUtils.php8
-rw-r--r--application/History.php12
-rw-r--r--application/Languages.php16
-rw-r--r--application/Router.php55
-rw-r--r--application/Thumbnailer.php130
-rw-r--r--application/Utils.php16
-rw-r--r--application/api/ApiMiddleware.php10
-rw-r--r--application/api/ApiUtils.php6
-rw-r--r--application/api/controllers/ApiController.php9
-rw-r--r--application/api/controllers/HistoryController.php (renamed from application/api/controllers/History.php)5
-rw-r--r--application/api/controllers/Info.php4
-rw-r--r--application/api/controllers/Tags.php1
-rw-r--r--application/api/exceptions/ApiException.php8
-rw-r--r--application/api/exceptions/ApiLinkNotFoundException.php3
-rw-r--r--application/api/exceptions/ApiTagNotFoundException.php3
-rw-r--r--application/bookmark/LinkDB.php (renamed from application/LinkDB.php)89
-rw-r--r--application/bookmark/LinkFilter.php (renamed from application/LinkFilter.php)67
-rw-r--r--application/bookmark/LinkUtils.php (renamed from application/LinkUtils.php)11
-rw-r--r--application/bookmark/exception/LinkNotFoundException.php15
-rw-r--r--application/config/ConfigJson.php2
-rw-r--r--application/config/ConfigManager.php58
-rw-r--r--application/config/ConfigPhp.php16
-rw-r--r--application/config/ConfigPlugin.php3
-rw-r--r--application/exceptions/IOException.php5
-rw-r--r--application/feed/Cache.php (renamed from application/Cache.php)0
-rw-r--r--application/feed/CachedPage.php (renamed from application/CachedPage.php)5
-rw-r--r--application/feed/FeedBuilder.php (renamed from application/FeedBuilder.php)43
-rw-r--r--application/http/Base64Url.php (renamed from application/Base64Url.php)9
-rw-r--r--application/http/HttpUtils.php (renamed from application/HttpUtils.php)34
-rw-r--r--application/http/Url.php (renamed from application/Url.php)108
-rw-r--r--application/http/UrlUtils.php88
-rw-r--r--application/netscape/NetscapeBookmarkUtils.php (renamed from application/NetscapeBookmarkUtils.php)45
-rw-r--r--application/plugin/PluginManager.php (renamed from application/PluginManager.php)42
-rw-r--r--application/plugin/exception/PluginFileNotFoundException.php23
-rw-r--r--application/render/PageBuilder.php (renamed from application/PageBuilder.php)60
-rw-r--r--application/render/ThemeUtils.php (renamed from application/ThemeUtils.php)2
-rw-r--r--application/security/LoginManager.php1
-rw-r--r--application/updater/Updater.php (renamed from application/Updater.php)168
-rw-r--r--application/updater/UpdaterUtils.php39
-rw-r--r--application/updater/exception/UpdaterException.php60
-rw-r--r--assets/common/js/picwall.js10
-rw-r--r--assets/common/js/thumbnails-update.js51
-rw-r--r--assets/common/js/thumbnails.js7
-rw-r--r--assets/default/js/base.js62
-rw-r--r--assets/default/scss/shaarli.scss87
-rw-r--r--assets/vintage/css/shaarli.css48
-rw-r--r--composer.json33
-rw-r--r--composer.lock849
-rw-r--r--doc/custom_theme/main.html23
-rw-r--r--doc/md/Community-&-Related-software.md5
-rw-r--r--doc/md/Link-structure.md18
-rw-r--r--doc/md/REST-API.md19
-rw-r--r--doc/md/Server-configuration.md11
-rw-r--r--doc/md/Sharing-content.md17
-rw-r--r--doc/md/guides/backup-restore-import-export.md (renamed from doc/md/Backup,-restore,-import-and-export.md)0
-rw-r--r--doc/md/guides/images/01-create-droplet-distro.jpgbin0 -> 20909 bytes
-rw-r--r--doc/md/guides/images/02-create-droplet-region.jpgbin0 -> 21603 bytes
-rw-r--r--doc/md/guides/images/03-create-droplet-size.jpgbin0 -> 20860 bytes
-rw-r--r--doc/md/guides/images/04-finalize.jpgbin0 -> 28233 bytes
-rw-r--r--doc/md/guides/images/05-droplet.jpgbin0 -> 11977 bytes
-rw-r--r--doc/md/guides/images/06-domain.jpgbin0 -> 4499 bytes
-rw-r--r--doc/md/guides/images/07-installation.jpgbin0 -> 42832 bytes
-rw-r--r--doc/md/guides/install-shaarli-with-debian9-and-docker.md257
-rw-r--r--doc/md/guides/various-hacks.md (renamed from doc/md/Various-hacks.md)0
-rw-r--r--doc/md/images/icon.pngbin0 -> 1266 bytes
-rw-r--r--doc/md/index.md120
-rw-r--r--docker-compose.yml61
-rw-r--r--inc/languages/fr/LC_MESSAGES/shaarli.po682
-rw-r--r--inc/web-thumbnailer.json13
-rw-r--r--index.php874
-rw-r--r--mkdocs.yml17
-rw-r--r--phpcs.xml17
-rw-r--r--phpdoc.dist.xml21
-rw-r--r--plugins/addlink_toolbar/addlink_toolbar.php2
-rw-r--r--plugins/archiveorg/archiveorg.php4
-rw-r--r--plugins/demo_plugin/demo_plugin.php14
-rw-r--r--plugins/isso/comment.pngbin0 -> 277 bytes
-rw-r--r--plugins/isso/isso.php33
-rw-r--r--plugins/isso/isso_button.html5
-rw-r--r--plugins/markdown/markdown.php28
-rw-r--r--plugins/piwik/piwik.php2
-rw-r--r--plugins/playvideos/playvideos.php3
-rw-r--r--plugins/pubsubhubbub/pubsubhubbub.php8
-rw-r--r--plugins/qrcode/qrcode.php6
-rw-r--r--plugins/wallabag/WallabagInstance.php1
-rw-r--r--plugins/wallabag/wallabag.php7
-rw-r--r--tests/ApplicationUtilsTest.php31
-rw-r--r--tests/FileUtilsTest.php12
-rw-r--r--tests/HistoryTest.php6
-rw-r--r--tests/LanguagesTest.php2
-rw-r--r--tests/PluginManagerTest.php10
-rw-r--r--tests/RouterTest.php10
-rw-r--r--tests/ThumbnailerTest.php116
-rw-r--r--tests/TimeZoneTest.php2
-rw-r--r--tests/UtilsTest.php11
-rw-r--r--tests/api/ApiMiddlewareTest.php3
-rw-r--r--tests/api/ApiUtilsTest.php7
-rw-r--r--tests/api/controllers/history/HistoryTest.php29
-rw-r--r--tests/api/controllers/info/InfoTest.php9
-rw-r--r--tests/api/controllers/links/DeleteLinkTest.php18
-rw-r--r--tests/api/controllers/links/GetLinkIdTest.php7
-rw-r--r--tests/api/controllers/links/GetLinksTest.php31
-rw-r--r--tests/api/controllers/links/PostLinkTest.php28
-rw-r--r--tests/api/controllers/links/PutLinkTest.php26
-rw-r--r--tests/api/controllers/tags/DeleteTagTest.php22
-rw-r--r--tests/api/controllers/tags/GetTagNameTest.php6
-rw-r--r--tests/api/controllers/tags/GetTagsTest.php8
-rw-r--r--tests/api/controllers/tags/PutTagTest.php18
-rw-r--r--tests/bookmark/LinkDBTest.php (renamed from tests/LinkDBTest.php)49
-rw-r--r--tests/bookmark/LinkFilterTest.php (renamed from tests/LinkFilterTest.php)33
-rw-r--r--tests/bookmark/LinkUtilsTest.php (renamed from tests/LinkUtilsTest.php)148
-rw-r--r--tests/config/ConfigJsonTest.php6
-rw-r--r--tests/config/ConfigManagerTest.php25
-rw-r--r--tests/config/ConfigPhpTest.php2
-rw-r--r--tests/config/ConfigPluginTest.php2
-rw-r--r--tests/feed/CacheTest.php (renamed from tests/CacheTest.php)25
-rw-r--r--tests/feed/CachedPageTest.php (renamed from tests/CachedPageTest.php)9
-rw-r--r--tests/feed/FeedBuilderTest.php (renamed from tests/FeedBuilderTest.php)29
-rw-r--r--tests/http/HttpUtils/ClientIpIdTest.php (renamed from tests/HttpUtils/ClientIpIdTest.php)6
-rw-r--r--tests/http/HttpUtils/GetHttpUrlTest.php (renamed from tests/HttpUtils/GetHttpUrlTest.php)10
-rw-r--r--tests/http/HttpUtils/GetIpAdressFromProxyTest.php (renamed from tests/HttpUtils/GetIpAdressFromProxyTest.php)7
-rw-r--r--tests/http/HttpUtils/IndexUrlTest.php (renamed from tests/HttpUtils/IndexUrlTest.php)6
-rw-r--r--tests/http/HttpUtils/IsHttpsTest.php (renamed from tests/HttpUtils/IsHttpsTest.php)5
-rw-r--r--tests/http/HttpUtils/PageUrlTest.php (renamed from tests/HttpUtils/PageUrlTest.php)6
-rw-r--r--tests/http/HttpUtils/ServerUrlTest.php (renamed from tests/HttpUtils/ServerUrlTest.php)6
-rw-r--r--tests/http/UrlTest.php (renamed from tests/Url/UrlTest.php)30
-rw-r--r--tests/http/UrlUtils/CleanupUrlTest.php (renamed from tests/Url/CleanupUrlTest.php)7
-rw-r--r--tests/http/UrlUtils/GetUrlSchemeTest.php (renamed from tests/Url/GetUrlSchemeTest.php)11
-rw-r--r--tests/http/UrlUtils/UnparseUrlTest.php (renamed from tests/Url/UnparseUrlTest.php)9
-rw-r--r--tests/http/UrlUtils/WhitelistProtocolsTest.php (renamed from tests/Url/WhitelistProtocolsTest.php)8
-rw-r--r--tests/languages/de/UtilsDeTest.php2
-rw-r--r--tests/languages/fr/LanguagesFrTest.php3
-rw-r--r--tests/netscape/BookmarkExportTest.php (renamed from tests/NetscapeBookmarkUtils/BookmarkExportTest.php)15
-rw-r--r--tests/netscape/BookmarkImportTest.php (renamed from tests/NetscapeBookmarkUtils/BookmarkImportTest.php)8
-rw-r--r--tests/netscape/input/empty.htm (renamed from tests/NetscapeBookmarkUtils/input/empty.htm)0
-rw-r--r--tests/netscape/input/internet_explorer_encoding.htm (renamed from tests/NetscapeBookmarkUtils/input/internet_explorer_encoding.htm)0
-rw-r--r--tests/netscape/input/lowercase_doctype.htm (renamed from tests/NetscapeBookmarkUtils/input/lowercase_doctype.htm)0
-rw-r--r--tests/netscape/input/netscape_basic.htm (renamed from tests/NetscapeBookmarkUtils/input/netscape_basic.htm)0
-rw-r--r--tests/netscape/input/netscape_nested.htm (renamed from tests/NetscapeBookmarkUtils/input/netscape_nested.htm)0
-rw-r--r--tests/netscape/input/no_doctype.htm (renamed from tests/NetscapeBookmarkUtils/input/no_doctype.htm)0
-rw-r--r--tests/netscape/input/same_date.htm (renamed from tests/NetscapeBookmarkUtils/input/same_date.htm)0
-rw-r--r--tests/plugins/PluginAddlinkTest.php10
-rw-r--r--tests/plugins/PluginArchiveorgTest.php5
-rw-r--r--tests/plugins/PluginIssoTest.php23
-rw-r--r--tests/plugins/PluginMarkdownTest.php22
-rw-r--r--tests/plugins/PluginPlayvideosTest.php7
-rw-r--r--tests/plugins/PluginPubsubhubbubTest.php7
-rw-r--r--tests/plugins/PluginQrcodeTest.php11
-rw-r--r--tests/plugins/PluginWallabagTest.php9
-rw-r--r--tests/plugins/WallabagInstanceTest.php5
-rw-r--r--tests/plugins/resources/hashtags.md10
-rw-r--r--tests/plugins/resources/hashtags.raw10
-rw-r--r--tests/plugins/resources/markdown.html8
-rw-r--r--tests/plugins/resources/markdown.md2
-rw-r--r--tests/render/ThemeUtilsTest.php (renamed from tests/ThemeUtilsTest.php)4
-rw-r--r--tests/security/LoginManagerTest.php3
-rw-r--r--tests/security/SessionManagerTest.php5
-rw-r--r--tests/updater/DummyUpdater.php (renamed from tests/Updater/DummyUpdater.php)17
-rw-r--r--tests/updater/UpdaterTest.php (renamed from tests/Updater/UpdaterTest.php)158
-rw-r--r--tests/utils/CurlUtils.php94
-rw-r--r--tests/utils/FakeApplicationUtils.php19
-rw-r--r--tests/utils/ReferenceHistory.php3
-rw-r--r--tests/utils/ReferenceLinkDB.php52
-rw-r--r--tests/utils/config/configJson.json.php71
-rw-r--r--tests/utils/config/configPhp.php4
-rw-r--r--tests/utils/config/wt.json12
-rw-r--r--tpl/default/addlink.html3
-rw-r--r--tpl/default/configure.html31
-rw-r--r--tpl/default/daily.html12
-rw-r--r--tpl/default/dailyrss.html2
-rw-r--r--tpl/default/includes.html20
-rw-r--r--tpl/default/linklist.html35
-rw-r--r--tpl/default/linklist.paging.html3
-rw-r--r--tpl/default/page.header.html28
-rw-r--r--tpl/default/picwall.html74
-rw-r--r--tpl/default/thumbnails.html48
-rw-r--r--tpl/default/tools.html36
-rw-r--r--tpl/vintage/configure.html31
-rw-r--r--tpl/vintage/daily.html9
-rw-r--r--tpl/vintage/dailyrss.html4
-rw-r--r--tpl/vintage/includes.html20
-rw-r--r--tpl/vintage/linklist.html12
-rw-r--r--tpl/vintage/picwall.html8
-rw-r--r--tpl/vintage/thumbnails.html28
-rw-r--r--tpl/vintage/tools.html32
-rw-r--r--webpack.config.js6
198 files changed, 4207 insertions, 5062 deletions
diff --git a/.dockerignore b/.dockerignore
index a0d28dc6..96fd31c5 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -4,6 +4,9 @@
4.github 4.github
5tests 5tests
6 6
7# Docker Compose resources
8docker-compose.yml
9
7# Shaarli runtime resources 10# Shaarli runtime resources
8cache/* 11cache/*
9data/* 12data/*
diff --git a/.gitattributes b/.gitattributes
index 9d22f11b..9a92bc37 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -35,6 +35,7 @@ doc/**/*.json export-ignore
35doc/**/*.md export-ignore 35doc/**/*.md export-ignore
36.docker/ export-ignore 36.docker/ export-ignore
37.dockerignore export-ignore 37.dockerignore export-ignore
38docker-compose.* export-ignore
38Dockerfile* export-ignore 39Dockerfile* export-ignore
39Doxyfile export-ignore 40Doxyfile export-ignore
40Makefile export-ignore 41Makefile export-ignore
diff --git a/.gitignore b/.gitignore
index 414ff6d5..6208e795 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,15 +22,16 @@ inc/languages/*/LC_MESSAGES/shaarli.mo
22 22
23# Development and test resources 23# Development and test resources
24coverage 24coverage
25doxygen
26sandbox 25sandbox
27phpmd.html 26phpmd.html
27phpdoc.xml
28 28
29# User plugin configuration 29# User plugin configuration
30plugins/*/config.php 30plugins/*/config.php
31 31
32# HTML documentation 32# HTML documentation
33doc/html/ 33doc/html/
34doc/phpdoc/
34 35
35# 3rd party themes 36# 3rd party themes
36tpl/* 37tpl/*
diff --git a/.htaccess b/.htaccess
index b238854c..4c004271 100644
--- a/.htaccess
+++ b/.htaccess
@@ -16,8 +16,33 @@ RewriteCond %{REQUEST_FILENAME} !-d
16RewriteRule ^ index.php [QSA,L] 16RewriteRule ^ index.php [QSA,L]
17 17
18<Limit GET POST PUT DELETE OPTIONS> 18<Limit GET POST PUT DELETE OPTIONS>
19 <IfModule version_module>
20 <IfVersion >= 2.4>
21 Require all granted
22 </IfVersion>
23 <IfVersion < 2.4>
24 Allow from all
25 Deny from none
26 </IfVersion>
27 </IfModule>
28
29 <IfModule !version_module>
19 Require all granted 30 Require all granted
31 </IfModule>
20</Limit> 32</Limit>
33
21<LimitExcept GET POST PUT DELETE OPTIONS> 34<LimitExcept GET POST PUT DELETE OPTIONS>
35 <IfModule version_module>
36 <IfVersion >= 2.4>
37 Require all denied
38 </IfVersion>
39 <IfVersion < 2.4>
40 Allow from none
41 Deny from all
42 </IfVersion>
43 </IfModule>
44
45 <IfModule !version_module>
22 Require all denied 46 Require all denied
47 </IfModule>
23</LimitExcept> 48</LimitExcept>
diff --git a/.travis.yml b/.travis.yml
index cb81846f..c6400eef 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,6 +4,8 @@ dist: trusty
4matrix: 4matrix:
5 include: 5 include:
6 - language: php 6 - language: php
7 php: 7.3
8 - language: php
7 php: 7.2 9 php: 7.2
8 - language: php 10 - language: php
9 php: 7.1 11 php: 7.1
diff --git a/AUTHORS b/AUTHORS
index 409a0308..db23ad32 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,6 +1,6 @@
1 647 ArthurHoaro <arthur@hoa.ro> 1 687 ArthurHoaro <arthur@hoa.ro>
2 327 VirtualTam <virtualtam@flibidi.net> 2 355 VirtualTam <virtualtam@flibidi.net>
3 187 nodiscc <nodiscc@gmail.com> 3 195 nodiscc <nodiscc@gmail.com>
4 56 Sébastien Sauvage <sebsauvage@sebsauvage.net> 4 56 Sébastien Sauvage <sebsauvage@sebsauvage.net>
5 15 Florian Eula <eula.florian@gmail.com> 5 15 Florian Eula <eula.florian@gmail.com>
6 13 Emilien Klein <emilien@klein.st> 6 13 Emilien Klein <emilien@klein.st>
@@ -15,7 +15,9 @@
15 4 David Sferruzza <david.sferruzza@gmail.com> 15 4 David Sferruzza <david.sferruzza@gmail.com>
16 4 Immánuel Fodor <immanuelfactor+github@gmail.com> 16 4 Immánuel Fodor <immanuelfactor+github@gmail.com>
17 3 Teromene <teromene@teromene.fr> 17 3 Teromene <teromene@teromene.fr>
18 3 llune <llune@users.noreply.github.com>
18 2 Chris Kuethe <chris.kuethe@gmail.com> 19 2 Chris Kuethe <chris.kuethe@gmail.com>
20 2 Felix Bartels <felix@host-consultants.de>
19 2 Knah Tsaeb <Knah-Tsaeb@knah-tsaeb.org> 21 2 Knah Tsaeb <Knah-Tsaeb@knah-tsaeb.org>
20 2 Mathieu Chabanon <git@matchab.fr> 22 2 Mathieu Chabanon <git@matchab.fr>
21 2 Miloš Jovanović <mjovanovic@gmail.com> 23 2 Miloš Jovanović <mjovanovic@gmail.com>
@@ -24,7 +26,9 @@
24 2 Timo Van Neerden <fire@lehollandaisvolant.net> 26 2 Timo Van Neerden <fire@lehollandaisvolant.net>
25 2 julienCXX <software@chmodplusx.eu> 27 2 julienCXX <software@chmodplusx.eu>
26 2 philipp-r <philipp-r@users.noreply.github.com> 28 2 philipp-r <philipp-r@users.noreply.github.com>
29 2 pips <pips@e5150.fr>
27 1 Adrien Oliva <adrien.oliva@yapbreak.fr> 30 1 Adrien Oliva <adrien.oliva@yapbreak.fr>
31 1 Adrien le Maire <adrien@alemaire.be>
28 1 Alexandre G.-Raymond <alex@ndre.gr> 32 1 Alexandre G.-Raymond <alex@ndre.gr>
29 1 Alexis J <alexis@effingo.be> 33 1 Alexis J <alexis@effingo.be>
30 1 Angristan <angristan@users.noreply.github.com> 34 1 Angristan <angristan@users.noreply.github.com>
@@ -36,7 +40,6 @@
36 1 Dennis Verspuij <dennisverspuij@users.noreply.github.com> 40 1 Dennis Verspuij <dennisverspuij@users.noreply.github.com>
37 1 Dimtion <zizou.xena@gmail.com> 41 1 Dimtion <zizou.xena@gmail.com>
38 1 Fanch <fanch-github@qth.fr> 42 1 Fanch <fanch-github@qth.fr>
39 1 Felix Bartels <felix@host-consultants.de>
40 1 Felix Kästner <github.com-fpunktk@fpunktk.de> 43 1 Felix Kästner <github.com-fpunktk@fpunktk.de>
41 1 Florian Voigt <flvoigt@me.com> 44 1 Florian Voigt <flvoigt@me.com>
42 1 Franck Kerbiriou <FranckKe@users.noreply.github.com> 45 1 Franck Kerbiriou <FranckKe@users.noreply.github.com>
@@ -55,3 +58,4 @@
55 1 TsT <tst2005@gmail.com> 58 1 TsT <tst2005@gmail.com>
56 1 dimtion <zizou.xena@gmail.com> 59 1 dimtion <zizou.xena@gmail.com>
57 1 durcheinandr <jochen@durcheinandr.de> 60 1 durcheinandr <jochen@durcheinandr.de>
61 1 lapineige <lapineige@users.noreply.github.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bafbd06c..aa1f0d8a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,7 +4,33 @@ All notable changes to this project will be documented in this file.
4The format is based on [Keep a Changelog](http://keepachangelog.com/) 4The format is based on [Keep a Changelog](http://keepachangelog.com/)
5and this project adheres to [Semantic Versioning](http://semver.org/). 5and this project adheres to [Semantic Versioning](http://semver.org/).
6 6
7## [v0.10.0](https://github.com/shaarli/Shaarli/releases/tag/v0.10.0) - UNPUBLISHED 7## [v0.10.2](https://github.com/shaarli/Shaarli/releases/tag/v0.10.2) - 2018-08-11
8
9### Fixed
10
11- Docker build
12
13## [v0.10.1](https://github.com/shaarli/Shaarli/releases/tag/v0.10.1) - 2018-08-11
14
15### Changed
16
17- Accessibility:
18 - Remove alt text on the logo
19 - Remove redundant title in tools page
20
21### Fixed
22
23- Fixed an error on the daily page and daily RSS
24- Fixed an issue causing 'You are not authorized to add a link' error while logged out
25- Fixed thumbnail path when Shaarli's path uses symbolic links
26- Add a `mod_version` check in Shaarli's root `.htaccess` file for Apache 2.2 syntax
27- Include assets in the release Makefile target
28
29### Removed
30
31- Firefox Social API shaare has been removed
32
33## [v0.10.0](https://github.com/shaarli/Shaarli/releases/tag/v0.10.0) - 2018-07-28
8**PHP 5.5 compatibility has been dropped.** Shaarli now requires at least PHP 5.6. 34**PHP 5.5 compatibility has been dropped.** Shaarli now requires at least PHP 5.6.
9 35
10### Added 36### Added
@@ -18,8 +44,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
18- Support redirection in cURL download callback 44- Support redirection in cURL download callback
19- Introduce multi-stage builds for Docker images 45- Introduce multi-stage builds for Docker images
20- Use Travis matrix and stages to run Javascript tests in a dedicated environment 46- Use Travis matrix and stages to run Javascript tests in a dedicated environment
47- Add tag endpoint in the REST API
48- Build the documentation in Travis builds
49- Provide a Docker Compose example
21 50
22### Changed 51### Changed
52- Use web-thumbnailer to retrieve thumbnails (see #687)
23- Use a specific page title in all pages 53- Use a specific page title in all pages
24- Daily: run hooks before creating the columns 54- Daily: run hooks before creating the columns
25- Load theme translations files automatically 55- Load theme translations files automatically
@@ -32,6 +62,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
32- Refactor server-side session management 62- Refactor server-side session management
33- Update Doxygen configuration 63- Update Doxygen configuration
34- Update Parsedown 64- Update Parsedown
65- Improve documentation
66- Docker: build the images from the local sources
67- Docker: bump alpine version to 3.7
68- Docker: expose a volume for the thumbnail cache
35 69
36### Removed 70### Removed
37- Drop support for PHP 5.5 71- Drop support for PHP 5.5
@@ -45,6 +79,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
45- Ensure user-specific CSS file is loaded 79- Ensure user-specific CSS file is loaded
46- Fix feed permalink rendering when Markdown escaping is enabled 80- Fix feed permalink rendering when Markdown escaping is enabled
47- Fix order of tags with the same number of occurrences 81- Fix order of tags with the same number of occurrences
82- Fixed the referrer meta tag in default template
83- Disable MkDocs' strict mode for ReadTheDocs builds to pass
84- fix and simplify Dockerfile for armhf
48 85
49### Security 86### Security
50- Update `.htaccess` to prevent accessing Git metadata when using a Git-based installation 87- Update `.htaccess` to prevent accessing Git metadata when using a Git-based installation
diff --git a/Dockerfile.armhf b/Dockerfile.armhf
index 5dcc34aa..1185e2df 100644
--- a/Dockerfile.armhf
+++ b/Dockerfile.armhf
@@ -1,9 +1,38 @@
1FROM lsiobase/alpine.armhf:3.6 1# Stage 1:
2# - Copy Shaarli sources
3# - Build documentation
4FROM arm32v6/alpine:3.8 as docs
5ADD . /usr/src/app/shaarli
6RUN apk --update --no-cache add py2-pip \
7 && cd /usr/src/app/shaarli \
8 && pip install --no-cache-dir mkdocs \
9 && mkdocs build --clean
10
11# Stage 2:
12# - Resolve PHP dependencies with Composer
13FROM arm32v6/alpine:3.8 as composer
14COPY --from=docs /usr/src/app/shaarli /app/shaarli
15RUN apk --update --no-cache add php7-mbstring composer \
16 && cd /app/shaarli \
17 && composer --prefer-dist --no-dev install
18
19# Stage 3:
20# - Frontend dependencies
21FROM arm32v6/alpine:3.8 as node
22COPY --from=composer /app/shaarli /shaarli
23RUN apk --update --no-cache add yarn nodejs-current python2 build-base \
24 && cd /shaarli \
25 && yarn install \
26 && yarn run build \
27 && rm -rf node_modules
28
29# Stage 4:
30# - Shaarli image
31FROM arm32v6/alpine:3.8
2LABEL maintainer="Shaarli Community" 32LABEL maintainer="Shaarli Community"
3 33
4RUN apk --update --no-cache add \ 34RUN apk --update --no-cache add \
5 ca-certificates \ 35 ca-certificates \
6 curl \
7 nginx \ 36 nginx \
8 php7 \ 37 php7 \
9 php7-ctype \ 38 php7-ctype \
@@ -15,7 +44,6 @@ RUN apk --update --no-cache add \
15 php7-json \ 44 php7-json \
16 php7-mbstring \ 45 php7-mbstring \
17 php7-openssl \ 46 php7-openssl \
18 php7-phar \
19 php7-session \ 47 php7-session \
20 php7-xml \ 48 php7-xml \
21 php7-zlib \ 49 php7-zlib \
@@ -25,22 +53,19 @@ COPY .docker/nginx.conf /etc/nginx/nginx.conf
25COPY .docker/php-fpm.conf /etc/php7/php-fpm.conf 53COPY .docker/php-fpm.conf /etc/php7/php-fpm.conf
26COPY .docker/services.d /etc/services.d 54COPY .docker/services.d /etc/services.d
27 55
28RUN curl -sS https://getcomposer.org/installer | php7 -- --install-dir=/usr/local/bin --filename=composer \ 56RUN rm -rf /etc/php7/php-fpm.d/www.conf \
29 && rm -rf /etc/php7/php-fpm.d/www.conf \
30 && sed -i 's/post_max_size.*/post_max_size = 10M/' /etc/php7/php.ini \ 57 && sed -i 's/post_max_size.*/post_max_size = 10M/' /etc/php7/php.ini \
31 && sed -i 's/upload_max_filesize.*/upload_max_filesize = 10M/' /etc/php7/php.ini 58 && sed -i 's/upload_max_filesize.*/upload_max_filesize = 10M/' /etc/php7/php.ini
32 59
33 60
34WORKDIR /var/www 61WORKDIR /var/www
35RUN curl -L https://github.com/shaarli/Shaarli/archive/master.tar.gz | tar xzf - \ 62COPY --from=node /shaarli /var/www/shaarli
36 && mv Shaarli-master shaarli \ 63
37 && cd shaarli \ 64RUN chown -R nginx:nginx . \
38 && composer --prefer-dist --no-dev install \
39 && rm -rf ~/.composer \
40 && chown -R nginx:nginx . \
41 && ln -sf /dev/stdout /var/log/nginx/shaarli.access.log \ 65 && ln -sf /dev/stdout /var/log/nginx/shaarli.access.log \
42 && ln -sf /dev/stderr /var/log/nginx/shaarli.error.log 66 && ln -sf /dev/stderr /var/log/nginx/shaarli.error.log
43 67
68VOLUME /var/www/shaarli/cache
44VOLUME /var/www/shaarli/data 69VOLUME /var/www/shaarli/data
45 70
46EXPOSE 80 71EXPOSE 80
diff --git a/Doxyfile b/Doxyfile
deleted file mode 100644
index a7f6e04f..00000000
--- a/Doxyfile
+++ /dev/null
@@ -1,2377 +0,0 @@
1# Doxyfile 1.8.9.1
2
3# This file describes the settings to be used by the documentation system
4# doxygen (www.doxygen.org) for a project.
5#
6# All text after a double hash (##) is considered a comment and is placed in
7# front of the TAG it is preceding.
8#
9# All text after a single hash (#) is considered a comment and will be ignored.
10# The format is:
11# TAG = value [value, ...]
12# For lists, items can also be appended using:
13# TAG += value [value, ...]
14# Values that contain spaces should be placed between quotes (\" \").
15
16#---------------------------------------------------------------------------
17# Project related configuration options
18#---------------------------------------------------------------------------
19
20# This tag specifies the encoding used for all characters in the config file
21# that follow. The default is UTF-8 which is also the encoding used for all text
22# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
23# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
24# for the list of possible encodings.
25# The default value is: UTF-8.
26
27DOXYFILE_ENCODING = UTF-8
28
29# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
30# double-quotes, unless you are using Doxywizard) that should identify the
31# project for which the documentation is generated. This name is used in the
32# title of most generated pages and in a few other places.
33# The default value is: My Project.
34
35PROJECT_NAME = Shaarli
36
37# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
38# could be handy for archiving the generated documentation or if some version
39# control system is used.
40
41PROJECT_NUMBER =
42
43# Using the PROJECT_BRIEF tag one can provide an optional one line description
44# for a project that appears at the top of each page and should give viewer a
45# quick idea about the purpose of the project. Keep the description short.
46
47PROJECT_BRIEF = "The personal, minimalist, super-fast, no-database delicious clone"
48
49# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
50# in the documentation. The maximum height of the logo should not exceed 55
51# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
52# the logo to the output directory.
53
54PROJECT_LOGO = doc/md/images/logo.png
55
56# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
57# into which the generated documentation will be written. If a relative path is
58# entered, it will be relative to the location where doxygen was started. If
59# left blank the current directory will be used.
60
61OUTPUT_DIRECTORY = .
62
63# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
64# directories (in 2 levels) under the output directory of each output format and
65# will distribute the generated files over these directories. Enabling this
66# option can be useful when feeding doxygen a huge amount of source files, where
67# putting all generated files in the same directory would otherwise causes
68# performance problems for the file system.
69# The default value is: NO.
70
71CREATE_SUBDIRS = NO
72
73# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
74# characters to appear in the names of generated files. If set to NO, non-ASCII
75# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
76# U+3044.
77# The default value is: NO.
78
79ALLOW_UNICODE_NAMES = NO
80
81# The OUTPUT_LANGUAGE tag is used to specify the language in which all
82# documentation generated by doxygen is written. Doxygen will use this
83# information to generate all constant output in the proper language.
84# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
85# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
86# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
87# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
88# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
89# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
90# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
91# Ukrainian and Vietnamese.
92# The default value is: English.
93
94OUTPUT_LANGUAGE = English
95
96# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
97# descriptions after the members that are listed in the file and class
98# documentation (similar to Javadoc). Set to NO to disable this.
99# The default value is: YES.
100
101BRIEF_MEMBER_DESC = YES
102
103# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
104# description of a member or function before the detailed description
105#
106# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
107# brief descriptions will be completely suppressed.
108# The default value is: YES.
109
110REPEAT_BRIEF = YES
111
112# This tag implements a quasi-intelligent brief description abbreviator that is
113# used to form the text in various listings. Each string in this list, if found
114# as the leading text of the brief description, will be stripped from the text
115# and the result, after processing the whole list, is used as the annotated
116# text. Otherwise, the brief description is used as-is. If left blank, the
117# following values are used ($name is automatically replaced with the name of
118# the entity):The $name class, The $name widget, The $name file, is, provides,
119# specifies, contains, represents, a, an and the.
120
121ABBREVIATE_BRIEF = "The $name class" \
122 "The $name widget" \
123 "The $name file" \
124 is \
125 provides \
126 specifies \
127 contains \
128 represents \
129 a \
130 an \
131 the
132
133# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
134# doxygen will generate a detailed section even if there is only a brief
135# description.
136# The default value is: NO.
137
138ALWAYS_DETAILED_SEC = NO
139
140# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
141# inherited members of a class in the documentation of that class as if those
142# members were ordinary class members. Constructors, destructors and assignment
143# operators of the base classes will not be shown.
144# The default value is: NO.
145
146INLINE_INHERITED_MEMB = NO
147
148# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
149# before files name in the file list and in the header files. If set to NO the
150# shortest path that makes the file name unique will be used
151# The default value is: YES.
152
153FULL_PATH_NAMES = YES
154
155# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
156# Stripping is only done if one of the specified strings matches the left-hand
157# part of the path. The tag can be used to show relative paths in the file list.
158# If left blank the directory from which doxygen is run is used as the path to
159# strip.
160#
161# Note that you can specify absolute paths here, but also relative paths, which
162# will be relative from the directory where doxygen is started.
163# This tag requires that the tag FULL_PATH_NAMES is set to YES.
164
165STRIP_FROM_PATH =
166
167# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
168# path mentioned in the documentation of a class, which tells the reader which
169# header file to include in order to use a class. If left blank only the name of
170# the header file containing the class definition is used. Otherwise one should
171# specify the list of include paths that are normally passed to the compiler
172# using the -I flag.
173
174STRIP_FROM_INC_PATH =
175
176# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
177# less readable) file names. This can be useful is your file systems doesn't
178# support long names like on DOS, Mac, or CD-ROM.
179# The default value is: NO.
180
181SHORT_NAMES = NO
182
183# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
184# first line (until the first dot) of a Javadoc-style comment as the brief
185# description. If set to NO, the Javadoc-style will behave just like regular Qt-
186# style comments (thus requiring an explicit @brief command for a brief
187# description.)
188# The default value is: NO.
189
190JAVADOC_AUTOBRIEF = NO
191
192# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
193# line (until the first dot) of a Qt-style comment as the brief description. If
194# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
195# requiring an explicit \brief command for a brief description.)
196# The default value is: NO.
197
198QT_AUTOBRIEF = NO
199
200# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
201# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
202# a brief description. This used to be the default behavior. The new default is
203# to treat a multi-line C++ comment block as a detailed description. Set this
204# tag to YES if you prefer the old behavior instead.
205#
206# Note that setting this tag to YES also means that rational rose comments are
207# not recognized any more.
208# The default value is: NO.
209
210MULTILINE_CPP_IS_BRIEF = NO
211
212# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
213# documentation from any documented member that it re-implements.
214# The default value is: YES.
215
216INHERIT_DOCS = YES
217
218# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
219# page for each member. If set to NO, the documentation of a member will be part
220# of the file/class/namespace that contains it.
221# The default value is: NO.
222
223SEPARATE_MEMBER_PAGES = NO
224
225# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
226# uses this value to replace tabs by spaces in code fragments.
227# Minimum value: 1, maximum value: 16, default value: 4.
228
229TAB_SIZE = 4
230
231# This tag can be used to specify a number of aliases that act as commands in
232# the documentation. An alias has the form:
233# name=value
234# For example adding
235# "sideeffect=@par Side Effects:\n"
236# will allow you to put the command \sideeffect (or @sideeffect) in the
237# documentation, which will result in a user-defined paragraph with heading
238# "Side Effects:". You can put \n's in the value part of an alias to insert
239# newlines.
240
241ALIASES =
242
243# This tag can be used to specify a number of word-keyword mappings (TCL only).
244# A mapping has the form "name=value". For example adding "class=itcl::class"
245# will allow you to use the command class in the itcl::class meaning.
246
247TCL_SUBST =
248
249# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
250# only. Doxygen will then generate output that is more tailored for C. For
251# instance, some of the names that are used will be different. The list of all
252# members will be omitted, etc.
253# The default value is: NO.
254
255OPTIMIZE_OUTPUT_FOR_C = YES
256
257# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
258# Python sources only. Doxygen will then generate output that is more tailored
259# for that language. For instance, namespaces will be presented as packages,
260# qualified scopes will look different, etc.
261# The default value is: NO.
262
263OPTIMIZE_OUTPUT_JAVA = NO
264
265# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
266# sources. Doxygen will then generate output that is tailored for Fortran.
267# The default value is: NO.
268
269OPTIMIZE_FOR_FORTRAN = NO
270
271# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
272# sources. Doxygen will then generate output that is tailored for VHDL.
273# The default value is: NO.
274
275OPTIMIZE_OUTPUT_VHDL = NO
276
277# Doxygen selects the parser to use depending on the extension of the files it
278# parses. With this tag you can assign which parser to use for a given
279# extension. Doxygen has a built-in mapping, but you can override or extend it
280# using this tag. The format is ext=language, where ext is a file extension, and
281# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
282# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
283# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
284# Fortran. In the later case the parser tries to guess whether the code is fixed
285# or free formatted code, this is the default for Fortran type files), VHDL. For
286# instance to make doxygen treat .inc files as Fortran files (default is PHP),
287# and .f files as C (default is Fortran), use: inc=Fortran f=C.
288#
289# Note: For files without extension you can use no_extension as a placeholder.
290#
291# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
292# the files are not read by doxygen.
293
294EXTENSION_MAPPING =
295
296# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
297# according to the Markdown format, which allows for more readable
298# documentation. See http://daringfireball.net/projects/markdown/ for details.
299# The output of markdown processing is further processed by doxygen, so you can
300# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
301# case of backward compatibilities issues.
302# The default value is: YES.
303
304MARKDOWN_SUPPORT = YES
305
306# When enabled doxygen tries to link words that correspond to documented
307# classes, or namespaces to their corresponding documentation. Such a link can
308# be prevented in individual cases by putting a % sign in front of the word or
309# globally by setting AUTOLINK_SUPPORT to NO.
310# The default value is: YES.
311
312AUTOLINK_SUPPORT = YES
313
314# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
315# to include (a tag file for) the STL sources as input, then you should set this
316# tag to YES in order to let doxygen match functions declarations and
317# definitions whose arguments contain STL classes (e.g. func(std::string);
318# versus func(std::string) {}). This also make the inheritance and collaboration
319# diagrams that involve STL classes more complete and accurate.
320# The default value is: NO.
321
322BUILTIN_STL_SUPPORT = NO
323
324# If you use Microsoft's C++/CLI language, you should set this option to YES to
325# enable parsing support.
326# The default value is: NO.
327
328CPP_CLI_SUPPORT = NO
329
330# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
331# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
332# will parse them like normal C++ but will assume all classes use public instead
333# of private inheritance when no explicit protection keyword is present.
334# The default value is: NO.
335
336SIP_SUPPORT = NO
337
338# For Microsoft's IDL there are propget and propput attributes to indicate
339# getter and setter methods for a property. Setting this option to YES will make
340# doxygen to replace the get and set methods by a property in the documentation.
341# This will only work if the methods are indeed getting or setting a simple
342# type. If this is not the case, or you want to show the methods anyway, you
343# should set this option to NO.
344# The default value is: YES.
345
346IDL_PROPERTY_SUPPORT = YES
347
348# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
349# tag is set to YES then doxygen will reuse the documentation of the first
350# member in the group (if any) for the other members of the group. By default
351# all members of a group must be documented explicitly.
352# The default value is: NO.
353
354DISTRIBUTE_GROUP_DOC = NO
355
356# Set the SUBGROUPING tag to YES to allow class member groups of the same type
357# (for instance a group of public functions) to be put as a subgroup of that
358# type (e.g. under the Public Functions section). Set it to NO to prevent
359# subgrouping. Alternatively, this can be done per class using the
360# \nosubgrouping command.
361# The default value is: YES.
362
363SUBGROUPING = YES
364
365# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
366# are shown inside the group in which they are included (e.g. using \ingroup)
367# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
368# and RTF).
369#
370# Note that this feature does not work in combination with
371# SEPARATE_MEMBER_PAGES.
372# The default value is: NO.
373
374INLINE_GROUPED_CLASSES = NO
375
376# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
377# with only public data fields or simple typedef fields will be shown inline in
378# the documentation of the scope in which they are defined (i.e. file,
379# namespace, or group documentation), provided this scope is documented. If set
380# to NO, structs, classes, and unions are shown on a separate page (for HTML and
381# Man pages) or section (for LaTeX and RTF).
382# The default value is: NO.
383
384INLINE_SIMPLE_STRUCTS = NO
385
386# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
387# enum is documented as struct, union, or enum with the name of the typedef. So
388# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
389# with name TypeT. When disabled the typedef will appear as a member of a file,
390# namespace, or class. And the struct will be named TypeS. This can typically be
391# useful for C code in case the coding convention dictates that all compound
392# types are typedef'ed and only the typedef is referenced, never the tag name.
393# The default value is: NO.
394
395TYPEDEF_HIDES_STRUCT = NO
396
397# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
398# cache is used to resolve symbols given their name and scope. Since this can be
399# an expensive process and often the same symbol appears multiple times in the
400# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
401# doxygen will become slower. If the cache is too large, memory is wasted. The
402# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
403# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
404# symbols. At the end of a run doxygen will report the cache usage and suggest
405# the optimal cache size from a speed point of view.
406# Minimum value: 0, maximum value: 9, default value: 0.
407
408LOOKUP_CACHE_SIZE = 0
409
410#---------------------------------------------------------------------------
411# Build related configuration options
412#---------------------------------------------------------------------------
413
414# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
415# documentation are documented, even if no documentation was available. Private
416# class members and static file members will be hidden unless the
417# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
418# Note: This will also disable the warnings about undocumented members that are
419# normally produced when WARNINGS is set to YES.
420# The default value is: NO.
421
422EXTRACT_ALL = YES
423
424# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
425# be included in the documentation.
426# The default value is: NO.
427
428EXTRACT_PRIVATE = YES
429
430# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
431# scope will be included in the documentation.
432# The default value is: NO.
433
434EXTRACT_PACKAGE = YES
435
436# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
437# included in the documentation.
438# The default value is: NO.
439
440EXTRACT_STATIC = YES
441
442# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
443# locally in source files will be included in the documentation. If set to NO,
444# only classes defined in header files are included. Does not have any effect
445# for Java sources.
446# The default value is: YES.
447
448EXTRACT_LOCAL_CLASSES = YES
449
450# This flag is only useful for Objective-C code. If set to YES, local methods,
451# which are defined in the implementation section but not in the interface are
452# included in the documentation. If set to NO, only methods in the interface are
453# included.
454# The default value is: NO.
455
456EXTRACT_LOCAL_METHODS = YES
457
458# If this flag is set to YES, the members of anonymous namespaces will be
459# extracted and appear in the documentation as a namespace called
460# 'anonymous_namespace{file}', where file will be replaced with the base name of
461# the file that contains the anonymous namespace. By default anonymous namespace
462# are hidden.
463# The default value is: NO.
464
465EXTRACT_ANON_NSPACES = YES
466
467# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
468# undocumented members inside documented classes or files. If set to NO these
469# members will be included in the various overviews, but no documentation
470# section is generated. This option has no effect if EXTRACT_ALL is enabled.
471# The default value is: NO.
472
473HIDE_UNDOC_MEMBERS = NO
474
475# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
476# undocumented classes that are normally visible in the class hierarchy. If set
477# to NO, these classes will be included in the various overviews. This option
478# has no effect if EXTRACT_ALL is enabled.
479# The default value is: NO.
480
481HIDE_UNDOC_CLASSES = NO
482
483# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
484# (class|struct|union) declarations. If set to NO, these declarations will be
485# included in the documentation.
486# The default value is: NO.
487
488HIDE_FRIEND_COMPOUNDS = NO
489
490# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
491# documentation blocks found inside the body of a function. If set to NO, these
492# blocks will be appended to the function's detailed documentation block.
493# The default value is: NO.
494
495HIDE_IN_BODY_DOCS = NO
496
497# The INTERNAL_DOCS tag determines if documentation that is typed after a
498# \internal command is included. If the tag is set to NO then the documentation
499# will be excluded. Set it to YES to include the internal documentation.
500# The default value is: NO.
501
502INTERNAL_DOCS = NO
503
504# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
505# names in lower-case letters. If set to YES, upper-case letters are also
506# allowed. This is useful if you have classes or files whose names only differ
507# in case and if your file system supports case sensitive file names. Windows
508# and Mac users are advised to set this option to NO.
509# The default value is: system dependent.
510
511CASE_SENSE_NAMES = NO
512
513# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
514# their full class and namespace scopes in the documentation. If set to YES, the
515# scope will be hidden.
516# The default value is: NO.
517
518HIDE_SCOPE_NAMES = YES
519
520# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
521# append additional text to a page's title, such as Class Reference. If set to
522# YES the compound reference will be hidden.
523# The default value is: NO.
524
525HIDE_COMPOUND_REFERENCE= NO
526
527# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
528# the files that are included by a file in the documentation of that file.
529# The default value is: YES.
530
531SHOW_INCLUDE_FILES = YES
532
533# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
534# grouped member an include statement to the documentation, telling the reader
535# which file to include in order to use the member.
536# The default value is: NO.
537
538SHOW_GROUPED_MEMB_INC = NO
539
540# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
541# files with double quotes in the documentation rather than with sharp brackets.
542# The default value is: NO.
543
544FORCE_LOCAL_INCLUDES = NO
545
546# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
547# documentation for inline members.
548# The default value is: YES.
549
550INLINE_INFO = YES
551
552# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
553# (detailed) documentation of file and class members alphabetically by member
554# name. If set to NO, the members will appear in declaration order.
555# The default value is: YES.
556
557SORT_MEMBER_DOCS = YES
558
559# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
560# descriptions of file, namespace and class members alphabetically by member
561# name. If set to NO, the members will appear in declaration order. Note that
562# this will also influence the order of the classes in the class list.
563# The default value is: NO.
564
565SORT_BRIEF_DOCS = NO
566
567# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
568# (brief and detailed) documentation of class members so that constructors and
569# destructors are listed first. If set to NO the constructors will appear in the
570# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
571# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
572# member documentation.
573# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
574# detailed member documentation.
575# The default value is: NO.
576
577SORT_MEMBERS_CTORS_1ST = NO
578
579# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
580# of group names into alphabetical order. If set to NO the group names will
581# appear in their defined order.
582# The default value is: NO.
583
584SORT_GROUP_NAMES = NO
585
586# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
587# fully-qualified names, including namespaces. If set to NO, the class list will
588# be sorted only by class name, not including the namespace part.
589# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
590# Note: This option applies only to the class list, not to the alphabetical
591# list.
592# The default value is: NO.
593
594SORT_BY_SCOPE_NAME = NO
595
596# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
597# type resolution of all parameters of a function it will reject a match between
598# the prototype and the implementation of a member function even if there is
599# only one candidate or it is obvious which candidate to choose by doing a
600# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
601# accept a match between prototype and implementation in such cases.
602# The default value is: NO.
603
604STRICT_PROTO_MATCHING = NO
605
606# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
607# list. This list is created by putting \todo commands in the documentation.
608# The default value is: YES.
609
610GENERATE_TODOLIST = YES
611
612# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
613# list. This list is created by putting \test commands in the documentation.
614# The default value is: YES.
615
616GENERATE_TESTLIST = YES
617
618# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
619# list. This list is created by putting \bug commands in the documentation.
620# The default value is: YES.
621
622GENERATE_BUGLIST = YES
623
624# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
625# the deprecated list. This list is created by putting \deprecated commands in
626# the documentation.
627# The default value is: YES.
628
629GENERATE_DEPRECATEDLIST= YES
630
631# The ENABLED_SECTIONS tag can be used to enable conditional documentation
632# sections, marked by \if <section_label> ... \endif and \cond <section_label>
633# ... \endcond blocks.
634
635ENABLED_SECTIONS =
636
637# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
638# initial value of a variable or macro / define can have for it to appear in the
639# documentation. If the initializer consists of more lines than specified here
640# it will be hidden. Use a value of 0 to hide initializers completely. The
641# appearance of the value of individual variables and macros / defines can be
642# controlled using \showinitializer or \hideinitializer command in the
643# documentation regardless of this setting.
644# Minimum value: 0, maximum value: 10000, default value: 30.
645
646MAX_INITIALIZER_LINES = 30
647
648# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
649# the bottom of the documentation of classes and structs. If set to YES, the
650# list will mention the files that were used to generate the documentation.
651# The default value is: YES.
652
653SHOW_USED_FILES = YES
654
655# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
656# will remove the Files entry from the Quick Index and from the Folder Tree View
657# (if specified).
658# The default value is: YES.
659
660SHOW_FILES = YES
661
662# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
663# page. This will remove the Namespaces entry from the Quick Index and from the
664# Folder Tree View (if specified).
665# The default value is: YES.
666
667SHOW_NAMESPACES = NO
668
669# The FILE_VERSION_FILTER tag can be used to specify a program or script that
670# doxygen should invoke to get the current version for each file (typically from
671# the version control system). Doxygen will invoke the program by executing (via
672# popen()) the command command input-file, where command is the value of the
673# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
674# by doxygen. Whatever the program writes to standard output is used as the file
675# version. For an example see the documentation.
676
677FILE_VERSION_FILTER =
678
679# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
680# by doxygen. The layout file controls the global structure of the generated
681# output files in an output format independent way. To create the layout file
682# that represents doxygen's defaults, run doxygen with the -l option. You can
683# optionally specify a file name after the option, if omitted DoxygenLayout.xml
684# will be used as the name of the layout file.
685#
686# Note that if you run doxygen from a directory containing a file called
687# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
688# tag is left empty.
689
690LAYOUT_FILE =
691
692# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
693# the reference definitions. This must be a list of .bib files. The .bib
694# extension is automatically appended if omitted. This requires the bibtex tool
695# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
696# For LaTeX the style of the bibliography can be controlled using
697# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
698# search path. See also \cite for info how to create references.
699
700CITE_BIB_FILES =
701
702#---------------------------------------------------------------------------
703# Configuration options related to warning and progress messages
704#---------------------------------------------------------------------------
705
706# The QUIET tag can be used to turn on/off the messages that are generated to
707# standard output by doxygen. If QUIET is set to YES this implies that the
708# messages are off.
709# The default value is: NO.
710
711QUIET = NO
712
713# The WARNINGS tag can be used to turn on/off the warning messages that are
714# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
715# this implies that the warnings are on.
716#
717# Tip: Turn warnings on while writing the documentation.
718# The default value is: YES.
719
720WARNINGS = YES
721
722# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
723# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
724# will automatically be disabled.
725# The default value is: YES.
726
727WARN_IF_UNDOCUMENTED = YES
728
729# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
730# potential errors in the documentation, such as not documenting some parameters
731# in a documented function, or documenting parameters that don't exist or using
732# markup commands wrongly.
733# The default value is: YES.
734
735WARN_IF_DOC_ERROR = YES
736
737# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
738# are documented, but have no documentation for their parameters or return
739# value. If set to NO, doxygen will only warn about wrong or incomplete
740# parameter documentation, but not about the absence of documentation.
741# The default value is: NO.
742
743WARN_NO_PARAMDOC = NO
744
745# The WARN_FORMAT tag determines the format of the warning messages that doxygen
746# can produce. The string should contain the $file, $line, and $text tags, which
747# will be replaced by the file and line number from which the warning originated
748# and the warning text. Optionally the format may contain $version, which will
749# be replaced by the version of the file (if it could be obtained via
750# FILE_VERSION_FILTER)
751# The default value is: $file:$line: $text.
752
753WARN_FORMAT = "$file:$line: $text"
754
755# The WARN_LOGFILE tag can be used to specify a file to which warning and error
756# messages should be written. If left blank the output is written to standard
757# error (stderr).
758
759WARN_LOGFILE =
760
761#---------------------------------------------------------------------------
762# Configuration options related to the input files
763#---------------------------------------------------------------------------
764
765# The INPUT tag is used to specify the files and/or directories that contain
766# documented source files. You may enter file names like myfile.cpp or
767# directories like /usr/src/myproject. Separate the files or directories with
768# spaces.
769# Note: If this tag is empty the current directory is searched.
770
771INPUT = .
772
773# This tag can be used to specify the character encoding of the source files
774# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
775# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
776# documentation (see: http://www.gnu.org/software/libiconv) for the list of
777# possible encodings.
778# The default value is: UTF-8.
779
780INPUT_ENCODING = UTF-8
781
782# If the value of the INPUT tag contains directories, you can use the
783# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
784# *.h) to filter out the source-files in the directories. If left blank the
785# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii,
786# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp,
787# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown,
788# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf,
789# *.qsf, *.as and *.js.
790
791FILE_PATTERNS = *.php
792
793# The RECURSIVE tag can be used to specify whether or not subdirectories should
794# be searched for input files as well.
795# The default value is: NO.
796
797RECURSIVE = YES
798
799# The EXCLUDE tag can be used to specify files and/or directories that should be
800# excluded from the INPUT source files. This way you can easily exclude a
801# subdirectory from a directory tree whose root is specified with the INPUT tag.
802#
803# Note that relative paths are relative to the directory from which doxygen is
804# run.
805
806EXCLUDE = vendor \
807 data \
808 tpl \
809 inc \
810 doc \
811 tmp
812
813# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
814# directories that are symbolic links (a Unix file system feature) are excluded
815# from the input.
816# The default value is: NO.
817
818EXCLUDE_SYMLINKS = YES
819
820# If the value of the INPUT tag contains directories, you can use the
821# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
822# certain files from those directories.
823#
824# Note that the wildcards are matched against the file with absolute path, so to
825# exclude all test directories for example use the pattern */test/*
826
827EXCLUDE_PATTERNS =
828
829# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
830# (namespaces, classes, functions, etc.) that should be excluded from the
831# output. The symbol name can be a fully qualified name, a word, or if the
832# wildcard * is used, a substring. Examples: ANamespace, AClass,
833# AClass::ANamespace, ANamespace::*Test
834#
835# Note that the wildcards are matched against the file with absolute path, so to
836# exclude all test directories use the pattern */test/*
837
838EXCLUDE_SYMBOLS =
839
840# The EXAMPLE_PATH tag can be used to specify one or more files or directories
841# that contain example code fragments that are included (see the \include
842# command).
843
844EXAMPLE_PATH =
845
846# If the value of the EXAMPLE_PATH tag contains directories, you can use the
847# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
848# *.h) to filter out the source-files in the directories. If left blank all
849# files are included.
850
851EXAMPLE_PATTERNS = *
852
853# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
854# searched for input files to be used with the \include or \dontinclude commands
855# irrespective of the value of the RECURSIVE tag.
856# The default value is: NO.
857
858EXAMPLE_RECURSIVE = NO
859
860# The IMAGE_PATH tag can be used to specify one or more files or directories
861# that contain images that are to be included in the documentation (see the
862# \image command).
863
864IMAGE_PATH =
865
866# The INPUT_FILTER tag can be used to specify a program that doxygen should
867# invoke to filter for each input file. Doxygen will invoke the filter program
868# by executing (via popen()) the command:
869#
870# <filter> <input-file>
871#
872# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
873# name of an input file. Doxygen will then use the output that the filter
874# program writes to standard output. If FILTER_PATTERNS is specified, this tag
875# will be ignored.
876#
877# Note that the filter must not add or remove lines; it is applied before the
878# code is scanned, but not when the output code is generated. If lines are added
879# or removed, the anchors will not be placed correctly.
880
881INPUT_FILTER =
882
883# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
884# basis. Doxygen will compare the file name with each pattern and apply the
885# filter if there is a match. The filters are a list of the form: pattern=filter
886# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
887# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
888# patterns match the file name, INPUT_FILTER is applied.
889
890FILTER_PATTERNS =
891
892# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
893# INPUT_FILTER) will also be used to filter the input files that are used for
894# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
895# The default value is: NO.
896
897FILTER_SOURCE_FILES = NO
898
899# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
900# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
901# it is also possible to disable source filtering for a specific pattern using
902# *.ext= (so without naming a filter).
903# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
904
905FILTER_SOURCE_PATTERNS =
906
907# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
908# is part of the input, its contents will be placed on the main page
909# (index.html). This can be useful if you have a project on for instance GitHub
910# and want to reuse the introduction page also for the doxygen output.
911
912USE_MDFILE_AS_MAINPAGE =
913
914#---------------------------------------------------------------------------
915# Configuration options related to source browsing
916#---------------------------------------------------------------------------
917
918# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
919# generated. Documented entities will be cross-referenced with these sources.
920#
921# Note: To get rid of all source code in the generated output, make sure that
922# also VERBATIM_HEADERS is set to NO.
923# The default value is: NO.
924
925SOURCE_BROWSER = YES
926
927# Setting the INLINE_SOURCES tag to YES will include the body of functions,
928# classes and enums directly into the documentation.
929# The default value is: NO.
930
931INLINE_SOURCES = NO
932
933# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
934# special comment blocks from generated source code fragments. Normal C, C++ and
935# Fortran comments will always remain visible.
936# The default value is: YES.
937
938STRIP_CODE_COMMENTS = YES
939
940# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
941# function all documented functions referencing it will be listed.
942# The default value is: NO.
943
944REFERENCED_BY_RELATION = NO
945
946# If the REFERENCES_RELATION tag is set to YES then for each documented function
947# all documented entities called/used by that function will be listed.
948# The default value is: NO.
949
950REFERENCES_RELATION = NO
951
952# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
953# to YES then the hyperlinks from functions in REFERENCES_RELATION and
954# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
955# link to the documentation.
956# The default value is: YES.
957
958REFERENCES_LINK_SOURCE = YES
959
960# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
961# source code will show a tooltip with additional information such as prototype,
962# brief description and links to the definition and documentation. Since this
963# will make the HTML file larger and loading of large files a bit slower, you
964# can opt to disable this feature.
965# The default value is: YES.
966# This tag requires that the tag SOURCE_BROWSER is set to YES.
967
968SOURCE_TOOLTIPS = YES
969
970# If the USE_HTAGS tag is set to YES then the references to source code will
971# point to the HTML generated by the htags(1) tool instead of doxygen built-in
972# source browser. The htags tool is part of GNU's global source tagging system
973# (see http://www.gnu.org/software/global/global.html). You will need version
974# 4.8.6 or higher.
975#
976# To use it do the following:
977# - Install the latest version of global
978# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
979# - Make sure the INPUT points to the root of the source tree
980# - Run doxygen as normal
981#
982# Doxygen will invoke htags (and that will in turn invoke gtags), so these
983# tools must be available from the command line (i.e. in the search path).
984#
985# The result: instead of the source browser generated by doxygen, the links to
986# source code will now point to the output of htags.
987# The default value is: NO.
988# This tag requires that the tag SOURCE_BROWSER is set to YES.
989
990USE_HTAGS = NO
991
992# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
993# verbatim copy of the header file for each class for which an include is
994# specified. Set to NO to disable this.
995# See also: Section \class.
996# The default value is: YES.
997
998VERBATIM_HEADERS = YES
999
1000#---------------------------------------------------------------------------
1001# Configuration options related to the alphabetical class index
1002#---------------------------------------------------------------------------
1003
1004# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
1005# compounds will be generated. Enable this if the project contains a lot of
1006# classes, structs, unions or interfaces.
1007# The default value is: YES.
1008
1009ALPHABETICAL_INDEX = YES
1010
1011# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
1012# which the alphabetical index list will be split.
1013# Minimum value: 1, maximum value: 20, default value: 5.
1014# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
1015
1016COLS_IN_ALPHA_INDEX = 5
1017
1018# In case all classes in a project start with a common prefix, all classes will
1019# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
1020# can be used to specify a prefix (or a list of prefixes) that should be ignored
1021# while generating the index headers.
1022# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
1023
1024IGNORE_PREFIX =
1025
1026#---------------------------------------------------------------------------
1027# Configuration options related to the HTML output
1028#---------------------------------------------------------------------------
1029
1030# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
1031# The default value is: YES.
1032
1033GENERATE_HTML = YES
1034
1035# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
1036# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
1037# it.
1038# The default directory is: html.
1039# This tag requires that the tag GENERATE_HTML is set to YES.
1040
1041HTML_OUTPUT = doxygen
1042
1043# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
1044# generated HTML page (for example: .htm, .php, .asp).
1045# The default value is: .html.
1046# This tag requires that the tag GENERATE_HTML is set to YES.
1047
1048HTML_FILE_EXTENSION = .html
1049
1050# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
1051# each generated HTML page. If the tag is left blank doxygen will generate a
1052# standard header.
1053#
1054# To get valid HTML the header file that includes any scripts and style sheets
1055# that doxygen needs, which is dependent on the configuration options used (e.g.
1056# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
1057# default header using
1058# doxygen -w html new_header.html new_footer.html new_stylesheet.css
1059# YourConfigFile
1060# and then modify the file new_header.html. See also section "Doxygen usage"
1061# for information on how to generate the default header that doxygen normally
1062# uses.
1063# Note: The header is subject to change so you typically have to regenerate the
1064# default header when upgrading to a newer version of doxygen. For a description
1065# of the possible markers and block names see the documentation.
1066# This tag requires that the tag GENERATE_HTML is set to YES.
1067
1068HTML_HEADER =
1069
1070# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
1071# generated HTML page. If the tag is left blank doxygen will generate a standard
1072# footer. See HTML_HEADER for more information on how to generate a default
1073# footer and what special commands can be used inside the footer. See also
1074# section "Doxygen usage" for information on how to generate the default footer
1075# that doxygen normally uses.
1076# This tag requires that the tag GENERATE_HTML is set to YES.
1077
1078HTML_FOOTER =
1079
1080# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
1081# sheet that is used by each HTML page. It can be used to fine-tune the look of
1082# the HTML output. If left blank doxygen will generate a default style sheet.
1083# See also section "Doxygen usage" for information on how to generate the style
1084# sheet that doxygen normally uses.
1085# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
1086# it is more robust and this tag (HTML_STYLESHEET) will in the future become
1087# obsolete.
1088# This tag requires that the tag GENERATE_HTML is set to YES.
1089
1090HTML_STYLESHEET =
1091
1092# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
1093# cascading style sheets that are included after the standard style sheets
1094# created by doxygen. Using this option one can overrule certain style aspects.
1095# This is preferred over using HTML_STYLESHEET since it does not replace the
1096# standard style sheet and is therefore more robust against future updates.
1097# Doxygen will copy the style sheet files to the output directory.
1098# Note: The order of the extra style sheet files is of importance (e.g. the last
1099# style sheet in the list overrules the setting of the previous ones in the
1100# list). For an example see the documentation.
1101# This tag requires that the tag GENERATE_HTML is set to YES.
1102
1103HTML_EXTRA_STYLESHEET =
1104
1105# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
1106# other source files which should be copied to the HTML output directory. Note
1107# that these files will be copied to the base HTML output directory. Use the
1108# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
1109# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
1110# files will be copied as-is; there are no commands or markers available.
1111# This tag requires that the tag GENERATE_HTML is set to YES.
1112
1113HTML_EXTRA_FILES =
1114
1115# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
1116# will adjust the colors in the style sheet and background images according to
1117# this color. Hue is specified as an angle on a colorwheel, see
1118# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
1119# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
1120# purple, and 360 is red again.
1121# Minimum value: 0, maximum value: 359, default value: 220.
1122# This tag requires that the tag GENERATE_HTML is set to YES.
1123
1124HTML_COLORSTYLE_HUE = 220
1125
1126# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
1127# in the HTML output. For a value of 0 the output will use grayscales only. A
1128# value of 255 will produce the most vivid colors.
1129# Minimum value: 0, maximum value: 255, default value: 100.
1130# This tag requires that the tag GENERATE_HTML is set to YES.
1131
1132HTML_COLORSTYLE_SAT = 100
1133
1134# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
1135# luminance component of the colors in the HTML output. Values below 100
1136# gradually make the output lighter, whereas values above 100 make the output
1137# darker. The value divided by 100 is the actual gamma applied, so 80 represents
1138# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
1139# change the gamma.
1140# Minimum value: 40, maximum value: 240, default value: 80.
1141# This tag requires that the tag GENERATE_HTML is set to YES.
1142
1143HTML_COLORSTYLE_GAMMA = 80
1144
1145# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
1146# page will contain the date and time when the page was generated. Setting this
1147# to NO can help when comparing the output of multiple runs.
1148# The default value is: YES.
1149# This tag requires that the tag GENERATE_HTML is set to YES.
1150
1151HTML_TIMESTAMP = YES
1152
1153# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
1154# documentation will contain sections that can be hidden and shown after the
1155# page has loaded.
1156# The default value is: NO.
1157# This tag requires that the tag GENERATE_HTML is set to YES.
1158
1159HTML_DYNAMIC_SECTIONS = NO
1160
1161# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
1162# shown in the various tree structured indices initially; the user can expand
1163# and collapse entries dynamically later on. Doxygen will expand the tree to
1164# such a level that at most the specified number of entries are visible (unless
1165# a fully collapsed tree already exceeds this amount). So setting the number of
1166# entries 1 will produce a full collapsed tree by default. 0 is a special value
1167# representing an infinite number of entries and will result in a full expanded
1168# tree by default.
1169# Minimum value: 0, maximum value: 9999, default value: 100.
1170# This tag requires that the tag GENERATE_HTML is set to YES.
1171
1172HTML_INDEX_NUM_ENTRIES = 100
1173
1174# If the GENERATE_DOCSET tag is set to YES, additional index files will be
1175# generated that can be used as input for Apple's Xcode 3 integrated development
1176# environment (see: http://developer.apple.com/tools/xcode/), introduced with
1177# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
1178# Makefile in the HTML output directory. Running make will produce the docset in
1179# that directory and running make install will install the docset in
1180# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
1181# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
1182# for more information.
1183# The default value is: NO.
1184# This tag requires that the tag GENERATE_HTML is set to YES.
1185
1186GENERATE_DOCSET = NO
1187
1188# This tag determines the name of the docset feed. A documentation feed provides
1189# an umbrella under which multiple documentation sets from a single provider
1190# (such as a company or product suite) can be grouped.
1191# The default value is: Doxygen generated docs.
1192# This tag requires that the tag GENERATE_DOCSET is set to YES.
1193
1194DOCSET_FEEDNAME = "Doxygen generated docs"
1195
1196# This tag specifies a string that should uniquely identify the documentation
1197# set bundle. This should be a reverse domain-name style string, e.g.
1198# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
1199# The default value is: org.doxygen.Project.
1200# This tag requires that the tag GENERATE_DOCSET is set to YES.
1201
1202DOCSET_BUNDLE_ID = org.doxygen.Project
1203
1204# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
1205# the documentation publisher. This should be a reverse domain-name style
1206# string, e.g. com.mycompany.MyDocSet.documentation.
1207# The default value is: org.doxygen.Publisher.
1208# This tag requires that the tag GENERATE_DOCSET is set to YES.
1209
1210DOCSET_PUBLISHER_ID = org.doxygen.Publisher
1211
1212# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
1213# The default value is: Publisher.
1214# This tag requires that the tag GENERATE_DOCSET is set to YES.
1215
1216DOCSET_PUBLISHER_NAME = Publisher
1217
1218# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
1219# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
1220# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
1221# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
1222# Windows.
1223#
1224# The HTML Help Workshop contains a compiler that can convert all HTML output
1225# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
1226# files are now used as the Windows 98 help format, and will replace the old
1227# Windows help format (.hlp) on all Windows platforms in the future. Compressed
1228# HTML files also contain an index, a table of contents, and you can search for
1229# words in the documentation. The HTML workshop also contains a viewer for
1230# compressed HTML files.
1231# The default value is: NO.
1232# This tag requires that the tag GENERATE_HTML is set to YES.
1233
1234GENERATE_HTMLHELP = NO
1235
1236# The CHM_FILE tag can be used to specify the file name of the resulting .chm
1237# file. You can add a path in front of the file if the result should not be
1238# written to the html output directory.
1239# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
1240
1241CHM_FILE =
1242
1243# The HHC_LOCATION tag can be used to specify the location (absolute path
1244# including file name) of the HTML help compiler (hhc.exe). If non-empty,
1245# doxygen will try to run the HTML help compiler on the generated index.hhp.
1246# The file has to be specified with full path.
1247# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
1248
1249HHC_LOCATION =
1250
1251# The GENERATE_CHI flag controls if a separate .chi index file is generated
1252# (YES) or that it should be included in the master .chm file (NO).
1253# The default value is: NO.
1254# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
1255
1256GENERATE_CHI = NO
1257
1258# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
1259# and project file content.
1260# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
1261
1262CHM_INDEX_ENCODING =
1263
1264# The BINARY_TOC flag controls whether a binary table of contents is generated
1265# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
1266# enables the Previous and Next buttons.
1267# The default value is: NO.
1268# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
1269
1270BINARY_TOC = NO
1271
1272# The TOC_EXPAND flag can be set to YES to add extra items for group members to
1273# the table of contents of the HTML help documentation and to the tree view.
1274# The default value is: NO.
1275# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
1276
1277TOC_EXPAND = NO
1278
1279# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
1280# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
1281# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
1282# (.qch) of the generated HTML documentation.
1283# The default value is: NO.
1284# This tag requires that the tag GENERATE_HTML is set to YES.
1285
1286GENERATE_QHP = NO
1287
1288# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
1289# the file name of the resulting .qch file. The path specified is relative to
1290# the HTML output folder.
1291# This tag requires that the tag GENERATE_QHP is set to YES.
1292
1293QCH_FILE =
1294
1295# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
1296# Project output. For more information please see Qt Help Project / Namespace
1297# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
1298# The default value is: org.doxygen.Project.
1299# This tag requires that the tag GENERATE_QHP is set to YES.
1300
1301QHP_NAMESPACE = org.doxygen.Project
1302
1303# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
1304# Help Project output. For more information please see Qt Help Project / Virtual
1305# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
1306# folders).
1307# The default value is: doc.
1308# This tag requires that the tag GENERATE_QHP is set to YES.
1309
1310QHP_VIRTUAL_FOLDER = doc
1311
1312# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
1313# filter to add. For more information please see Qt Help Project / Custom
1314# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
1315# filters).
1316# This tag requires that the tag GENERATE_QHP is set to YES.
1317
1318QHP_CUST_FILTER_NAME =
1319
1320# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
1321# custom filter to add. For more information please see Qt Help Project / Custom
1322# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
1323# filters).
1324# This tag requires that the tag GENERATE_QHP is set to YES.
1325
1326QHP_CUST_FILTER_ATTRS =
1327
1328# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
1329# project's filter section matches. Qt Help Project / Filter Attributes (see:
1330# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
1331# This tag requires that the tag GENERATE_QHP is set to YES.
1332
1333QHP_SECT_FILTER_ATTRS =
1334
1335# The QHG_LOCATION tag can be used to specify the location of Qt's
1336# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
1337# generated .qhp file.
1338# This tag requires that the tag GENERATE_QHP is set to YES.
1339
1340QHG_LOCATION =
1341
1342# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
1343# generated, together with the HTML files, they form an Eclipse help plugin. To
1344# install this plugin and make it available under the help contents menu in
1345# Eclipse, the contents of the directory containing the HTML and XML files needs
1346# to be copied into the plugins directory of eclipse. The name of the directory
1347# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
1348# After copying Eclipse needs to be restarted before the help appears.
1349# The default value is: NO.
1350# This tag requires that the tag GENERATE_HTML is set to YES.
1351
1352GENERATE_ECLIPSEHELP = NO
1353
1354# A unique identifier for the Eclipse help plugin. When installing the plugin
1355# the directory name containing the HTML and XML files should also have this
1356# name. Each documentation set should have its own identifier.
1357# The default value is: org.doxygen.Project.
1358# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
1359
1360ECLIPSE_DOC_ID = org.doxygen.Project
1361
1362# If you want full control over the layout of the generated HTML pages it might
1363# be necessary to disable the index and replace it with your own. The
1364# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
1365# of each HTML page. A value of NO enables the index and the value YES disables
1366# it. Since the tabs in the index contain the same information as the navigation
1367# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
1368# The default value is: NO.
1369# This tag requires that the tag GENERATE_HTML is set to YES.
1370
1371DISABLE_INDEX = NO
1372
1373# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
1374# structure should be generated to display hierarchical information. If the tag
1375# value is set to YES, a side panel will be generated containing a tree-like
1376# index structure (just like the one that is generated for HTML Help). For this
1377# to work a browser that supports JavaScript, DHTML, CSS and frames is required
1378# (i.e. any modern browser). Windows users are probably better off using the
1379# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
1380# further fine-tune the look of the index. As an example, the default style
1381# sheet generated by doxygen has an example that shows how to put an image at
1382# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
1383# the same information as the tab index, you could consider setting
1384# DISABLE_INDEX to YES when enabling this option.
1385# The default value is: NO.
1386# This tag requires that the tag GENERATE_HTML is set to YES.
1387
1388GENERATE_TREEVIEW = YES
1389
1390# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
1391# doxygen will group on one line in the generated HTML documentation.
1392#
1393# Note that a value of 0 will completely suppress the enum values from appearing
1394# in the overview section.
1395# Minimum value: 0, maximum value: 20, default value: 4.
1396# This tag requires that the tag GENERATE_HTML is set to YES.
1397
1398ENUM_VALUES_PER_LINE = 4
1399
1400# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
1401# to set the initial width (in pixels) of the frame in which the tree is shown.
1402# Minimum value: 0, maximum value: 1500, default value: 250.
1403# This tag requires that the tag GENERATE_HTML is set to YES.
1404
1405TREEVIEW_WIDTH = 250
1406
1407# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
1408# external symbols imported via tag files in a separate window.
1409# The default value is: NO.
1410# This tag requires that the tag GENERATE_HTML is set to YES.
1411
1412EXT_LINKS_IN_WINDOW = NO
1413
1414# Use this tag to change the font size of LaTeX formulas included as images in
1415# the HTML documentation. When you change the font size after a successful
1416# doxygen run you need to manually remove any form_*.png images from the HTML
1417# output directory to force them to be regenerated.
1418# Minimum value: 8, maximum value: 50, default value: 10.
1419# This tag requires that the tag GENERATE_HTML is set to YES.
1420
1421FORMULA_FONTSIZE = 10
1422
1423# Use the FORMULA_TRANPARENT tag to determine whether or not the images
1424# generated for formulas are transparent PNGs. Transparent PNGs are not
1425# supported properly for IE 6.0, but are supported on all modern browsers.
1426#
1427# Note that when changing this option you need to delete any form_*.png files in
1428# the HTML output directory before the changes have effect.
1429# The default value is: YES.
1430# This tag requires that the tag GENERATE_HTML is set to YES.
1431
1432FORMULA_TRANSPARENT = YES
1433
1434# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
1435# http://www.mathjax.org) which uses client side Javascript for the rendering
1436# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
1437# installed or if you want to formulas look prettier in the HTML output. When
1438# enabled you may also need to install MathJax separately and configure the path
1439# to it using the MATHJAX_RELPATH option.
1440# The default value is: NO.
1441# This tag requires that the tag GENERATE_HTML is set to YES.
1442
1443USE_MATHJAX = NO
1444
1445# When MathJax is enabled you can set the default output format to be used for
1446# the MathJax output. See the MathJax site (see:
1447# http://docs.mathjax.org/en/latest/output.html) for more details.
1448# Possible values are: HTML-CSS (which is slower, but has the best
1449# compatibility), NativeMML (i.e. MathML) and SVG.
1450# The default value is: HTML-CSS.
1451# This tag requires that the tag USE_MATHJAX is set to YES.
1452
1453MATHJAX_FORMAT = HTML-CSS
1454
1455# When MathJax is enabled you need to specify the location relative to the HTML
1456# output directory using the MATHJAX_RELPATH option. The destination directory
1457# should contain the MathJax.js script. For instance, if the mathjax directory
1458# is located at the same level as the HTML output directory, then
1459# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
1460# Content Delivery Network so you can quickly see the result without installing
1461# MathJax. However, it is strongly recommended to install a local copy of
1462# MathJax from http://www.mathjax.org before deployment.
1463# The default value is: http://cdn.mathjax.org/mathjax/latest.
1464# This tag requires that the tag USE_MATHJAX is set to YES.
1465
1466MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
1467
1468# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
1469# extension names that should be enabled during MathJax rendering. For example
1470# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
1471# This tag requires that the tag USE_MATHJAX is set to YES.
1472
1473MATHJAX_EXTENSIONS =
1474
1475# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
1476# of code that will be used on startup of the MathJax code. See the MathJax site
1477# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
1478# example see the documentation.
1479# This tag requires that the tag USE_MATHJAX is set to YES.
1480
1481MATHJAX_CODEFILE =
1482
1483# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
1484# the HTML output. The underlying search engine uses javascript and DHTML and
1485# should work on any modern browser. Note that when using HTML help
1486# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
1487# there is already a search function so this one should typically be disabled.
1488# For large projects the javascript based search engine can be slow, then
1489# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
1490# search using the keyboard; to jump to the search box use <access key> + S
1491# (what the <access key> is depends on the OS and browser, but it is typically
1492# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
1493# key> to jump into the search results window, the results can be navigated
1494# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
1495# the search. The filter options can be selected when the cursor is inside the
1496# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
1497# to select a filter and <Enter> or <escape> to activate or cancel the filter
1498# option.
1499# The default value is: YES.
1500# This tag requires that the tag GENERATE_HTML is set to YES.
1501
1502SEARCHENGINE = YES
1503
1504# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
1505# implemented using a web server instead of a web client using Javascript. There
1506# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
1507# setting. When disabled, doxygen will generate a PHP script for searching and
1508# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
1509# and searching needs to be provided by external tools. See the section
1510# "External Indexing and Searching" for details.
1511# The default value is: NO.
1512# This tag requires that the tag SEARCHENGINE is set to YES.
1513
1514SERVER_BASED_SEARCH = NO
1515
1516# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
1517# script for searching. Instead the search results are written to an XML file
1518# which needs to be processed by an external indexer. Doxygen will invoke an
1519# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
1520# search results.
1521#
1522# Doxygen ships with an example indexer (doxyindexer) and search engine
1523# (doxysearch.cgi) which are based on the open source search engine library
1524# Xapian (see: http://xapian.org/).
1525#
1526# See the section "External Indexing and Searching" for details.
1527# The default value is: NO.
1528# This tag requires that the tag SEARCHENGINE is set to YES.
1529
1530EXTERNAL_SEARCH = NO
1531
1532# The SEARCHENGINE_URL should point to a search engine hosted by a web server
1533# which will return the search results when EXTERNAL_SEARCH is enabled.
1534#
1535# Doxygen ships with an example indexer (doxyindexer) and search engine
1536# (doxysearch.cgi) which are based on the open source search engine library
1537# Xapian (see: http://xapian.org/). See the section "External Indexing and
1538# Searching" for details.
1539# This tag requires that the tag SEARCHENGINE is set to YES.
1540
1541SEARCHENGINE_URL =
1542
1543# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
1544# search data is written to a file for indexing by an external tool. With the
1545# SEARCHDATA_FILE tag the name of this file can be specified.
1546# The default file is: searchdata.xml.
1547# This tag requires that the tag SEARCHENGINE is set to YES.
1548
1549SEARCHDATA_FILE = searchdata.xml
1550
1551# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
1552# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
1553# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
1554# projects and redirect the results back to the right project.
1555# This tag requires that the tag SEARCHENGINE is set to YES.
1556
1557EXTERNAL_SEARCH_ID =
1558
1559# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
1560# projects other than the one defined by this configuration file, but that are
1561# all added to the same external search index. Each project needs to have a
1562# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
1563# to a relative location where the documentation can be found. The format is:
1564# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
1565# This tag requires that the tag SEARCHENGINE is set to YES.
1566
1567EXTRA_SEARCH_MAPPINGS =
1568
1569#---------------------------------------------------------------------------
1570# Configuration options related to the LaTeX output
1571#---------------------------------------------------------------------------
1572
1573# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
1574# The default value is: YES.
1575
1576GENERATE_LATEX = NO
1577
1578# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
1579# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
1580# it.
1581# The default directory is: latex.
1582# This tag requires that the tag GENERATE_LATEX is set to YES.
1583
1584LATEX_OUTPUT = latex
1585
1586# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
1587# invoked.
1588#
1589# Note that when enabling USE_PDFLATEX this option is only used for generating
1590# bitmaps for formulas in the HTML output, but not in the Makefile that is
1591# written to the output directory.
1592# The default file is: latex.
1593# This tag requires that the tag GENERATE_LATEX is set to YES.
1594
1595LATEX_CMD_NAME = latex
1596
1597# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
1598# index for LaTeX.
1599# The default file is: makeindex.
1600# This tag requires that the tag GENERATE_LATEX is set to YES.
1601
1602MAKEINDEX_CMD_NAME = makeindex
1603
1604# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
1605# documents. This may be useful for small projects and may help to save some
1606# trees in general.
1607# The default value is: NO.
1608# This tag requires that the tag GENERATE_LATEX is set to YES.
1609
1610COMPACT_LATEX = NO
1611
1612# The PAPER_TYPE tag can be used to set the paper type that is used by the
1613# printer.
1614# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
1615# 14 inches) and executive (7.25 x 10.5 inches).
1616# The default value is: a4.
1617# This tag requires that the tag GENERATE_LATEX is set to YES.
1618
1619PAPER_TYPE = a4
1620
1621# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
1622# that should be included in the LaTeX output. To get the times font for
1623# instance you can specify
1624# EXTRA_PACKAGES=times
1625# If left blank no extra packages will be included.
1626# This tag requires that the tag GENERATE_LATEX is set to YES.
1627
1628EXTRA_PACKAGES =
1629
1630# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
1631# generated LaTeX document. The header should contain everything until the first
1632# chapter. If it is left blank doxygen will generate a standard header. See
1633# section "Doxygen usage" for information on how to let doxygen write the
1634# default header to a separate file.
1635#
1636# Note: Only use a user-defined header if you know what you are doing! The
1637# following commands have a special meaning inside the header: $title,
1638# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
1639# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
1640# string, for the replacement values of the other commands the user is referred
1641# to HTML_HEADER.
1642# This tag requires that the tag GENERATE_LATEX is set to YES.
1643
1644LATEX_HEADER =
1645
1646# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
1647# generated LaTeX document. The footer should contain everything after the last
1648# chapter. If it is left blank doxygen will generate a standard footer. See
1649# LATEX_HEADER for more information on how to generate a default footer and what
1650# special commands can be used inside the footer.
1651#
1652# Note: Only use a user-defined footer if you know what you are doing!
1653# This tag requires that the tag GENERATE_LATEX is set to YES.
1654
1655LATEX_FOOTER =
1656
1657# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
1658# LaTeX style sheets that are included after the standard style sheets created
1659# by doxygen. Using this option one can overrule certain style aspects. Doxygen
1660# will copy the style sheet files to the output directory.
1661# Note: The order of the extra style sheet files is of importance (e.g. the last
1662# style sheet in the list overrules the setting of the previous ones in the
1663# list).
1664# This tag requires that the tag GENERATE_LATEX is set to YES.
1665
1666LATEX_EXTRA_STYLESHEET =
1667
1668# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
1669# other source files which should be copied to the LATEX_OUTPUT output
1670# directory. Note that the files will be copied as-is; there are no commands or
1671# markers available.
1672# This tag requires that the tag GENERATE_LATEX is set to YES.
1673
1674LATEX_EXTRA_FILES =
1675
1676# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
1677# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
1678# contain links (just like the HTML output) instead of page references. This
1679# makes the output suitable for online browsing using a PDF viewer.
1680# The default value is: YES.
1681# This tag requires that the tag GENERATE_LATEX is set to YES.
1682
1683PDF_HYPERLINKS = YES
1684
1685# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
1686# the PDF file directly from the LaTeX files. Set this option to YES, to get a
1687# higher quality PDF documentation.
1688# The default value is: YES.
1689# This tag requires that the tag GENERATE_LATEX is set to YES.
1690
1691USE_PDFLATEX = YES
1692
1693# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
1694# command to the generated LaTeX files. This will instruct LaTeX to keep running
1695# if errors occur, instead of asking the user for help. This option is also used
1696# when generating formulas in HTML.
1697# The default value is: NO.
1698# This tag requires that the tag GENERATE_LATEX is set to YES.
1699
1700LATEX_BATCHMODE = NO
1701
1702# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
1703# index chapters (such as File Index, Compound Index, etc.) in the output.
1704# The default value is: NO.
1705# This tag requires that the tag GENERATE_LATEX is set to YES.
1706
1707LATEX_HIDE_INDICES = NO
1708
1709# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
1710# code with syntax highlighting in the LaTeX output.
1711#
1712# Note that which sources are shown also depends on other settings such as
1713# SOURCE_BROWSER.
1714# The default value is: NO.
1715# This tag requires that the tag GENERATE_LATEX is set to YES.
1716
1717LATEX_SOURCE_CODE = NO
1718
1719# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
1720# bibliography, e.g. plainnat, or ieeetr. See
1721# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
1722# The default value is: plain.
1723# This tag requires that the tag GENERATE_LATEX is set to YES.
1724
1725LATEX_BIB_STYLE = plain
1726
1727#---------------------------------------------------------------------------
1728# Configuration options related to the RTF output
1729#---------------------------------------------------------------------------
1730
1731# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
1732# RTF output is optimized for Word 97 and may not look too pretty with other RTF
1733# readers/editors.
1734# The default value is: NO.
1735
1736GENERATE_RTF = NO
1737
1738# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
1739# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
1740# it.
1741# The default directory is: rtf.
1742# This tag requires that the tag GENERATE_RTF is set to YES.
1743
1744RTF_OUTPUT = rtf
1745
1746# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
1747# documents. This may be useful for small projects and may help to save some
1748# trees in general.
1749# The default value is: NO.
1750# This tag requires that the tag GENERATE_RTF is set to YES.
1751
1752COMPACT_RTF = NO
1753
1754# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
1755# contain hyperlink fields. The RTF file will contain links (just like the HTML
1756# output) instead of page references. This makes the output suitable for online
1757# browsing using Word or some other Word compatible readers that support those
1758# fields.
1759#
1760# Note: WordPad (write) and others do not support links.
1761# The default value is: NO.
1762# This tag requires that the tag GENERATE_RTF is set to YES.
1763
1764RTF_HYPERLINKS = NO
1765
1766# Load stylesheet definitions from file. Syntax is similar to doxygen's config
1767# file, i.e. a series of assignments. You only have to provide replacements,
1768# missing definitions are set to their default value.
1769#
1770# See also section "Doxygen usage" for information on how to generate the
1771# default style sheet that doxygen normally uses.
1772# This tag requires that the tag GENERATE_RTF is set to YES.
1773
1774RTF_STYLESHEET_FILE =
1775
1776# Set optional variables used in the generation of an RTF document. Syntax is
1777# similar to doxygen's config file. A template extensions file can be generated
1778# using doxygen -e rtf extensionFile.
1779# This tag requires that the tag GENERATE_RTF is set to YES.
1780
1781RTF_EXTENSIONS_FILE =
1782
1783# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
1784# with syntax highlighting in the RTF output.
1785#
1786# Note that which sources are shown also depends on other settings such as
1787# SOURCE_BROWSER.
1788# The default value is: NO.
1789# This tag requires that the tag GENERATE_RTF is set to YES.
1790
1791RTF_SOURCE_CODE = NO
1792
1793#---------------------------------------------------------------------------
1794# Configuration options related to the man page output
1795#---------------------------------------------------------------------------
1796
1797# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
1798# classes and files.
1799# The default value is: NO.
1800
1801GENERATE_MAN = NO
1802
1803# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
1804# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
1805# it. A directory man3 will be created inside the directory specified by
1806# MAN_OUTPUT.
1807# The default directory is: man.
1808# This tag requires that the tag GENERATE_MAN is set to YES.
1809
1810MAN_OUTPUT = man
1811
1812# The MAN_EXTENSION tag determines the extension that is added to the generated
1813# man pages. In case the manual section does not start with a number, the number
1814# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
1815# optional.
1816# The default value is: .3.
1817# This tag requires that the tag GENERATE_MAN is set to YES.
1818
1819MAN_EXTENSION = .3
1820
1821# The MAN_SUBDIR tag determines the name of the directory created within
1822# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
1823# MAN_EXTENSION with the initial . removed.
1824# This tag requires that the tag GENERATE_MAN is set to YES.
1825
1826MAN_SUBDIR =
1827
1828# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
1829# will generate one additional man file for each entity documented in the real
1830# man page(s). These additional files only source the real man page, but without
1831# them the man command would be unable to find the correct page.
1832# The default value is: NO.
1833# This tag requires that the tag GENERATE_MAN is set to YES.
1834
1835MAN_LINKS = NO
1836
1837#---------------------------------------------------------------------------
1838# Configuration options related to the XML output
1839#---------------------------------------------------------------------------
1840
1841# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
1842# captures the structure of the code including all documentation.
1843# The default value is: NO.
1844
1845GENERATE_XML = NO
1846
1847# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
1848# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
1849# it.
1850# The default directory is: xml.
1851# This tag requires that the tag GENERATE_XML is set to YES.
1852
1853XML_OUTPUT = xml
1854
1855# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
1856# listings (including syntax highlighting and cross-referencing information) to
1857# the XML output. Note that enabling this will significantly increase the size
1858# of the XML output.
1859# The default value is: YES.
1860# This tag requires that the tag GENERATE_XML is set to YES.
1861
1862XML_PROGRAMLISTING = YES
1863
1864#---------------------------------------------------------------------------
1865# Configuration options related to the DOCBOOK output
1866#---------------------------------------------------------------------------
1867
1868# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
1869# that can be used to generate PDF.
1870# The default value is: NO.
1871
1872GENERATE_DOCBOOK = NO
1873
1874# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
1875# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
1876# front of it.
1877# The default directory is: docbook.
1878# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
1879
1880DOCBOOK_OUTPUT = docbook
1881
1882# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
1883# program listings (including syntax highlighting and cross-referencing
1884# information) to the DOCBOOK output. Note that enabling this will significantly
1885# increase the size of the DOCBOOK output.
1886# The default value is: NO.
1887# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
1888
1889DOCBOOK_PROGRAMLISTING = NO
1890
1891#---------------------------------------------------------------------------
1892# Configuration options for the AutoGen Definitions output
1893#---------------------------------------------------------------------------
1894
1895# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
1896# AutoGen Definitions (see http://autogen.sf.net) file that captures the
1897# structure of the code including all documentation. Note that this feature is
1898# still experimental and incomplete at the moment.
1899# The default value is: NO.
1900
1901GENERATE_AUTOGEN_DEF = NO
1902
1903#---------------------------------------------------------------------------
1904# Configuration options related to the Perl module output
1905#---------------------------------------------------------------------------
1906
1907# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
1908# file that captures the structure of the code including all documentation.
1909#
1910# Note that this feature is still experimental and incomplete at the moment.
1911# The default value is: NO.
1912
1913GENERATE_PERLMOD = NO
1914
1915# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
1916# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
1917# output from the Perl module output.
1918# The default value is: NO.
1919# This tag requires that the tag GENERATE_PERLMOD is set to YES.
1920
1921PERLMOD_LATEX = NO
1922
1923# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
1924# formatted so it can be parsed by a human reader. This is useful if you want to
1925# understand what is going on. On the other hand, if this tag is set to NO, the
1926# size of the Perl module output will be much smaller and Perl will parse it
1927# just the same.
1928# The default value is: YES.
1929# This tag requires that the tag GENERATE_PERLMOD is set to YES.
1930
1931PERLMOD_PRETTY = YES
1932
1933# The names of the make variables in the generated doxyrules.make file are
1934# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
1935# so different doxyrules.make files included by the same Makefile don't
1936# overwrite each other's variables.
1937# This tag requires that the tag GENERATE_PERLMOD is set to YES.
1938
1939PERLMOD_MAKEVAR_PREFIX =
1940
1941#---------------------------------------------------------------------------
1942# Configuration options related to the preprocessor
1943#---------------------------------------------------------------------------
1944
1945# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
1946# C-preprocessor directives found in the sources and include files.
1947# The default value is: YES.
1948
1949ENABLE_PREPROCESSING = YES
1950
1951# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
1952# in the source code. If set to NO, only conditional compilation will be
1953# performed. Macro expansion can be done in a controlled way by setting
1954# EXPAND_ONLY_PREDEF to YES.
1955# The default value is: NO.
1956# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
1957
1958MACRO_EXPANSION = NO
1959
1960# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
1961# the macro expansion is limited to the macros specified with the PREDEFINED and
1962# EXPAND_AS_DEFINED tags.
1963# The default value is: NO.
1964# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
1965
1966EXPAND_ONLY_PREDEF = NO
1967
1968# If the SEARCH_INCLUDES tag is set to YES, the include files in the
1969# INCLUDE_PATH will be searched if a #include is found.
1970# The default value is: YES.
1971# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
1972
1973SEARCH_INCLUDES = YES
1974
1975# The INCLUDE_PATH tag can be used to specify one or more directories that
1976# contain include files that are not input files but should be processed by the
1977# preprocessor.
1978# This tag requires that the tag SEARCH_INCLUDES is set to YES.
1979
1980INCLUDE_PATH =
1981
1982# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
1983# patterns (like *.h and *.hpp) to filter out the header-files in the
1984# directories. If left blank, the patterns specified with FILE_PATTERNS will be
1985# used.
1986# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
1987
1988INCLUDE_FILE_PATTERNS =
1989
1990# The PREDEFINED tag can be used to specify one or more macro names that are
1991# defined before the preprocessor is started (similar to the -D option of e.g.
1992# gcc). The argument of the tag is a list of macros of the form: name or
1993# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
1994# is assumed. To prevent a macro definition from being undefined via #undef or
1995# recursively expanded use the := operator instead of the = operator.
1996# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
1997
1998PREDEFINED =
1999
2000# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
2001# tag can be used to specify a list of macro names that should be expanded. The
2002# macro definition that is found in the sources will be used. Use the PREDEFINED
2003# tag if you want to use a different macro definition that overrules the
2004# definition found in the source code.
2005# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
2006
2007EXPAND_AS_DEFINED =
2008
2009# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
2010# remove all references to function-like macros that are alone on a line, have
2011# an all uppercase name, and do not end with a semicolon. Such function macros
2012# are typically used for boiler-plate code, and will confuse the parser if not
2013# removed.
2014# The default value is: YES.
2015# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
2016
2017SKIP_FUNCTION_MACROS = NO
2018
2019#---------------------------------------------------------------------------
2020# Configuration options related to external references
2021#---------------------------------------------------------------------------
2022
2023# The TAGFILES tag can be used to specify one or more tag files. For each tag
2024# file the location of the external documentation should be added. The format of
2025# a tag file without this location is as follows:
2026# TAGFILES = file1 file2 ...
2027# Adding location for the tag files is done as follows:
2028# TAGFILES = file1=loc1 "file2 = loc2" ...
2029# where loc1 and loc2 can be relative or absolute paths or URLs. See the
2030# section "Linking to external documentation" for more information about the use
2031# of tag files.
2032# Note: Each tag file must have a unique name (where the name does NOT include
2033# the path). If a tag file is not located in the directory in which doxygen is
2034# run, you must also specify the path to the tagfile here.
2035
2036TAGFILES =
2037
2038# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
2039# tag file that is based on the input files it reads. See section "Linking to
2040# external documentation" for more information about the usage of tag files.
2041
2042GENERATE_TAGFILE =
2043
2044# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
2045# the class index. If set to NO, only the inherited external classes will be
2046# listed.
2047# The default value is: NO.
2048
2049ALLEXTERNALS = NO
2050
2051# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
2052# in the modules index. If set to NO, only the current project's groups will be
2053# listed.
2054# The default value is: YES.
2055
2056EXTERNAL_GROUPS = YES
2057
2058# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
2059# the related pages index. If set to NO, only the current project's pages will
2060# be listed.
2061# The default value is: YES.
2062
2063EXTERNAL_PAGES = YES
2064
2065# The PERL_PATH should be the absolute path and name of the perl script
2066# interpreter (i.e. the result of 'which perl').
2067# The default file (with absolute path) is: /usr/bin/perl.
2068
2069PERL_PATH = /usr/bin/perl
2070
2071#---------------------------------------------------------------------------
2072# Configuration options related to the dot tool
2073#---------------------------------------------------------------------------
2074
2075# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
2076# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
2077# NO turns the diagrams off. Note that this option also works with HAVE_DOT
2078# disabled, but it is recommended to install and use dot, since it yields more
2079# powerful graphs.
2080# The default value is: YES.
2081
2082CLASS_DIAGRAMS = NO
2083
2084# You can define message sequence charts within doxygen comments using the \msc
2085# command. Doxygen will then run the mscgen tool (see:
2086# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
2087# documentation. The MSCGEN_PATH tag allows you to specify the directory where
2088# the mscgen tool resides. If left empty the tool is assumed to be found in the
2089# default search path.
2090
2091MSCGEN_PATH =
2092
2093# You can include diagrams made with dia in doxygen documentation. Doxygen will
2094# then run dia to produce the diagram and insert it in the documentation. The
2095# DIA_PATH tag allows you to specify the directory where the dia binary resides.
2096# If left empty dia is assumed to be found in the default search path.
2097
2098DIA_PATH =
2099
2100# If set to YES the inheritance and collaboration graphs will hide inheritance
2101# and usage relations if the target is undocumented or is not a class.
2102# The default value is: YES.
2103
2104HIDE_UNDOC_RELATIONS = YES
2105
2106# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
2107# available from the path. This tool is part of Graphviz (see:
2108# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
2109# Bell Labs. The other options in this section have no effect if this option is
2110# set to NO
2111# The default value is: NO.
2112
2113HAVE_DOT = NO
2114
2115# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
2116# to run in parallel. When set to 0 doxygen will base this on the number of
2117# processors available in the system. You can set it explicitly to a value
2118# larger than 0 to get control over the balance between CPU load and processing
2119# speed.
2120# Minimum value: 0, maximum value: 32, default value: 0.
2121# This tag requires that the tag HAVE_DOT is set to YES.
2122
2123DOT_NUM_THREADS = 0
2124
2125# When you want a differently looking font in the dot files that doxygen
2126# generates you can specify the font name using DOT_FONTNAME. You need to make
2127# sure dot is able to find the font, which can be done by putting it in a
2128# standard location or by setting the DOTFONTPATH environment variable or by
2129# setting DOT_FONTPATH to the directory containing the font.
2130# The default value is: Helvetica.
2131# This tag requires that the tag HAVE_DOT is set to YES.
2132
2133DOT_FONTNAME = Helvetica
2134
2135# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
2136# dot graphs.
2137# Minimum value: 4, maximum value: 24, default value: 10.
2138# This tag requires that the tag HAVE_DOT is set to YES.
2139
2140DOT_FONTSIZE = 10
2141
2142# By default doxygen will tell dot to use the default font as specified with
2143# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
2144# the path where dot can find it using this tag.
2145# This tag requires that the tag HAVE_DOT is set to YES.
2146
2147DOT_FONTPATH =
2148
2149# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
2150# each documented class showing the direct and indirect inheritance relations.
2151# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
2152# The default value is: YES.
2153# This tag requires that the tag HAVE_DOT is set to YES.
2154
2155CLASS_GRAPH = YES
2156
2157# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
2158# graph for each documented class showing the direct and indirect implementation
2159# dependencies (inheritance, containment, and class references variables) of the
2160# class with other documented classes.
2161# The default value is: YES.
2162# This tag requires that the tag HAVE_DOT is set to YES.
2163
2164COLLABORATION_GRAPH = YES
2165
2166# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
2167# groups, showing the direct groups dependencies.
2168# The default value is: YES.
2169# This tag requires that the tag HAVE_DOT is set to YES.
2170
2171GROUP_GRAPHS = YES
2172
2173# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
2174# collaboration diagrams in a style similar to the OMG's Unified Modeling
2175# Language.
2176# The default value is: NO.
2177# This tag requires that the tag HAVE_DOT is set to YES.
2178
2179UML_LOOK = NO
2180
2181# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
2182# class node. If there are many fields or methods and many nodes the graph may
2183# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
2184# number of items for each type to make the size more manageable. Set this to 0
2185# for no limit. Note that the threshold may be exceeded by 50% before the limit
2186# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
2187# but if the number exceeds 15, the total amount of fields shown is limited to
2188# 10.
2189# Minimum value: 0, maximum value: 100, default value: 10.
2190# This tag requires that the tag HAVE_DOT is set to YES.
2191
2192UML_LIMIT_NUM_FIELDS = 10
2193
2194# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
2195# collaboration graphs will show the relations between templates and their
2196# instances.
2197# The default value is: NO.
2198# This tag requires that the tag HAVE_DOT is set to YES.
2199
2200TEMPLATE_RELATIONS = NO
2201
2202# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
2203# YES then doxygen will generate a graph for each documented file showing the
2204# direct and indirect include dependencies of the file with other documented
2205# files.
2206# The default value is: YES.
2207# This tag requires that the tag HAVE_DOT is set to YES.
2208
2209INCLUDE_GRAPH = YES
2210
2211# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
2212# set to YES then doxygen will generate a graph for each documented file showing
2213# the direct and indirect include dependencies of the file with other documented
2214# files.
2215# The default value is: YES.
2216# This tag requires that the tag HAVE_DOT is set to YES.
2217
2218INCLUDED_BY_GRAPH = YES
2219
2220# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
2221# dependency graph for every global function or class method.
2222#
2223# Note that enabling this option will significantly increase the time of a run.
2224# So in most cases it will be better to enable call graphs for selected
2225# functions only using the \callgraph command.
2226# The default value is: NO.
2227# This tag requires that the tag HAVE_DOT is set to YES.
2228
2229CALL_GRAPH = NO
2230
2231# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
2232# dependency graph for every global function or class method.
2233#
2234# Note that enabling this option will significantly increase the time of a run.
2235# So in most cases it will be better to enable caller graphs for selected
2236# functions only using the \callergraph command.
2237# The default value is: NO.
2238# This tag requires that the tag HAVE_DOT is set to YES.
2239
2240CALLER_GRAPH = NO
2241
2242# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
2243# hierarchy of all classes instead of a textual one.
2244# The default value is: YES.
2245# This tag requires that the tag HAVE_DOT is set to YES.
2246
2247GRAPHICAL_HIERARCHY = YES
2248
2249# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
2250# dependencies a directory has on other directories in a graphical way. The
2251# dependency relations are determined by the #include relations between the
2252# files in the directories.
2253# The default value is: YES.
2254# This tag requires that the tag HAVE_DOT is set to YES.
2255
2256DIRECTORY_GRAPH = YES
2257
2258# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
2259# generated by dot.
2260# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
2261# to make the SVG files visible in IE 9+ (other browsers do not have this
2262# requirement).
2263# Possible values are: png, jpg, gif and svg.
2264# The default value is: png.
2265# This tag requires that the tag HAVE_DOT is set to YES.
2266
2267DOT_IMAGE_FORMAT = png
2268
2269# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
2270# enable generation of interactive SVG images that allow zooming and panning.
2271#
2272# Note that this requires a modern browser other than Internet Explorer. Tested
2273# and working are Firefox, Chrome, Safari, and Opera.
2274# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
2275# the SVG files visible. Older versions of IE do not have SVG support.
2276# The default value is: NO.
2277# This tag requires that the tag HAVE_DOT is set to YES.
2278
2279INTERACTIVE_SVG = NO
2280
2281# The DOT_PATH tag can be used to specify the path where the dot tool can be
2282# found. If left blank, it is assumed the dot tool can be found in the path.
2283# This tag requires that the tag HAVE_DOT is set to YES.
2284
2285DOT_PATH =
2286
2287# The DOTFILE_DIRS tag can be used to specify one or more directories that
2288# contain dot files that are included in the documentation (see the \dotfile
2289# command).
2290# This tag requires that the tag HAVE_DOT is set to YES.
2291
2292DOTFILE_DIRS =
2293
2294# The MSCFILE_DIRS tag can be used to specify one or more directories that
2295# contain msc files that are included in the documentation (see the \mscfile
2296# command).
2297
2298MSCFILE_DIRS =
2299
2300# The DIAFILE_DIRS tag can be used to specify one or more directories that
2301# contain dia files that are included in the documentation (see the \diafile
2302# command).
2303
2304DIAFILE_DIRS =
2305
2306# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
2307# path where java can find the plantuml.jar file. If left blank, it is assumed
2308# PlantUML is not used or called during a preprocessing step. Doxygen will
2309# generate a warning when it encounters a \startuml command in this case and
2310# will not generate output for the diagram.
2311
2312PLANTUML_JAR_PATH =
2313
2314# When using plantuml, the specified paths are searched for files specified by
2315# the !include statement in a plantuml block.
2316
2317PLANTUML_INCLUDE_PATH =
2318
2319# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
2320# that will be shown in the graph. If the number of nodes in a graph becomes
2321# larger than this value, doxygen will truncate the graph, which is visualized
2322# by representing a node as a red box. Note that doxygen if the number of direct
2323# children of the root node in a graph is already larger than
2324# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
2325# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
2326# Minimum value: 0, maximum value: 10000, default value: 50.
2327# This tag requires that the tag HAVE_DOT is set to YES.
2328
2329DOT_GRAPH_MAX_NODES = 50
2330
2331# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
2332# generated by dot. A depth value of 3 means that only nodes reachable from the
2333# root by following a path via at most 3 edges will be shown. Nodes that lay
2334# further from the root node will be omitted. Note that setting this option to 1
2335# or 2 may greatly reduce the computation time needed for large code bases. Also
2336# note that the size of a graph can be further restricted by
2337# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
2338# Minimum value: 0, maximum value: 1000, default value: 0.
2339# This tag requires that the tag HAVE_DOT is set to YES.
2340
2341MAX_DOT_GRAPH_DEPTH = 0
2342
2343# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
2344# background. This is disabled by default, because dot on Windows does not seem
2345# to support this out of the box.
2346#
2347# Warning: Depending on the platform used, enabling this option may lead to
2348# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
2349# read).
2350# The default value is: NO.
2351# This tag requires that the tag HAVE_DOT is set to YES.
2352
2353DOT_TRANSPARENT = NO
2354
2355# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
2356# files in one run (i.e. multiple -o and -T options on the command line). This
2357# makes dot run faster, but since only newer versions of dot (>1.8.10) support
2358# this, this feature is disabled by default.
2359# The default value is: NO.
2360# This tag requires that the tag HAVE_DOT is set to YES.
2361
2362DOT_MULTI_TARGETS = NO
2363
2364# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
2365# explaining the meaning of the various boxes and arrows in the dot generated
2366# graphs.
2367# The default value is: YES.
2368# This tag requires that the tag HAVE_DOT is set to YES.
2369
2370GENERATE_LEGEND = YES
2371
2372# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
2373# files that are used to generate the various graphs.
2374# The default value is: YES.
2375# This tag requires that the tag HAVE_DOT is set to YES.
2376
2377DOT_CLEANUP = YES
diff --git a/Makefile b/Makefile
index b0cdadf0..286d2c90 100644
--- a/Makefile
+++ b/Makefile
@@ -2,8 +2,6 @@
2# Makefile for PHP code analysis & testing, documentation and release generation 2# Makefile for PHP code analysis & testing, documentation and release generation
3 3
4BIN = vendor/bin 4BIN = vendor/bin
5PHP_SOURCE = index.php application tests plugins
6PHP_COMMA_SOURCE = index.php,application,tests,plugins
7 5
8all: static_analysis_summary check_permissions test 6all: static_analysis_summary check_permissions test
9 7
@@ -18,84 +16,32 @@ docker_%:
18 cd ~/shaarli && make $* 16 cd ~/shaarli && make $*
19 17
20## 18##
21# Concise status of the project
22# These targets are non-blocking: || exit 0
23##
24
25static_analysis_summary: code_sniffer_source copy_paste mess_detector_summary
26 @echo
27
28##
29# PHP_CodeSniffer 19# PHP_CodeSniffer
30# Detects PHP syntax errors 20# Detects PHP syntax errors
31# Documentation (usage, output formatting): 21# Documentation (usage, output formatting):
32# - http://pear.php.net/manual/en/package.php.php-codesniffer.usage.php 22# - http://pear.php.net/manual/en/package.php.php-codesniffer.usage.php
33# - http://pear.php.net/manual/en/package.php.php-codesniffer.reporting.php 23# - http://pear.php.net/manual/en/package.php.php-codesniffer.reporting.php
34## 24##
25PHPCS := $(BIN)/phpcs
35 26
36code_sniffer: code_sniffer_full 27code_sniffer:
28 @$(PHPCS)
37 29
38### - errors filtered by coding standard: PEAR, PSR1, PSR2, Zend... 30### - errors filtered by coding standard: PEAR, PSR1, PSR2, Zend...
39PHPCS_%: 31PHPCS_%:
40 @$(BIN)/phpcs $(PHP_SOURCE) --report-full --report-width=200 --standard=$* 32 @$(PHPCS) --report-full --report-width=200 --standard=$*
41 33
42### - errors by Git author 34### - errors by Git author
43code_sniffer_blame: 35code_sniffer_blame:
44 @$(BIN)/phpcs $(PHP_SOURCE) --report-gitblame 36 @$(PHPCS) --report-gitblame
45 37
46### - all errors/warnings 38### - all errors/warnings
47code_sniffer_full: 39code_sniffer_full:
48 @$(BIN)/phpcs $(PHP_SOURCE) --report-full --report-width=200 40 @$(PHPCS) --report-full --report-width=200
49 41
50### - errors grouped by kind 42### - errors grouped by kind
51code_sniffer_source: 43code_sniffer_source:
52 @$(BIN)/phpcs $(PHP_SOURCE) --report-source || exit 0 44 @$(PHPCS) --report-source || exit 0
53
54##
55# PHP Copy/Paste Detector
56# Detects code redundancy
57# Documentation: https://github.com/sebastianbergmann/phpcpd
58##
59
60copy_paste:
61 @echo "-----------------------"
62 @echo "PHP COPY/PASTE DETECTOR"
63 @echo "-----------------------"
64 @$(BIN)/phpcpd $(PHP_SOURCE) || exit 0
65 @echo
66
67##
68# PHP Mess Detector
69# Detects PHP syntax errors, sorted by category
70# Rules documentation: http://phpmd.org/rules/index.html
71##
72MESS_DETECTOR_RULES = cleancode,codesize,controversial,design,naming,unusedcode
73
74mess_title:
75 @echo "-----------------"
76 @echo "PHP MESS DETECTOR"
77 @echo "-----------------"
78
79### - all warnings
80mess_detector: mess_title
81 @$(BIN)/phpmd $(PHP_COMMA_SOURCE) text $(MESS_DETECTOR_RULES) | sed 's_.*\/__'
82
83### - all warnings + HTML output contains links to PHPMD's documentation
84mess_detector_html:
85 @$(BIN)/phpmd $(PHP_COMMA_SOURCE) html $(MESS_DETECTOR_RULES) \
86 --reportfile phpmd.html || exit 0
87
88### - warnings grouped by message, sorted by descending frequency order
89mess_detector_grouped: mess_title
90 @$(BIN)/phpmd $(PHP_SOURCE) text $(MESS_DETECTOR_RULES) \
91 | cut -f 2 | sort | uniq -c | sort -nr
92
93### - summary: number of warnings by rule set
94mess_detector_summary: mess_title
95 @for rule in $$(echo $(MESS_DETECTOR_RULES) | tr ',' ' '); do \
96 warnings=$$($(BIN)/phpmd $(PHP_COMMA_SOURCE) text $$rule | wc -l); \
97 printf "$$warnings\t$$rule\n"; \
98 done;
99 45
100## 46##
101# Checks source file & script permissions 47# Checks source file & script permissions
@@ -170,6 +116,7 @@ release_tar: composer_dependencies htmldoc translate build_frontend
170 git archive --prefix=$(ARCHIVE_PREFIX) -o $(ARCHIVE_VERSION).tar HEAD 116 git archive --prefix=$(ARCHIVE_PREFIX) -o $(ARCHIVE_VERSION).tar HEAD
171 tar rvf $(ARCHIVE_VERSION).tar --transform "s|^vendor|$(ARCHIVE_PREFIX)vendor|" vendor/ 117 tar rvf $(ARCHIVE_VERSION).tar --transform "s|^vendor|$(ARCHIVE_PREFIX)vendor|" vendor/
172 tar rvf $(ARCHIVE_VERSION).tar --transform "s|^doc/html|$(ARCHIVE_PREFIX)doc/html|" doc/html/ 118 tar rvf $(ARCHIVE_VERSION).tar --transform "s|^doc/html|$(ARCHIVE_PREFIX)doc/html|" doc/html/
119 tar rvf $(ARCHIVE_VERSION).tar --transform "s|^tpl|$(ARCHIVE_PREFIX)tpl|" tpl/
173 gzip $(ARCHIVE_VERSION).tar 120 gzip $(ARCHIVE_VERSION).tar
174 121
175### generate a release zip and include 3rd-party dependencies and translations 122### generate a release zip and include 3rd-party dependencies and translations
@@ -180,6 +127,8 @@ release_zip: composer_dependencies htmldoc translate build_frontend
180 zip -r $(ARCHIVE_VERSION).zip $(ARCHIVE_PREFIX)doc/ 127 zip -r $(ARCHIVE_VERSION).zip $(ARCHIVE_PREFIX)doc/
181 rsync -a vendor/ $(ARCHIVE_PREFIX)vendor/ 128 rsync -a vendor/ $(ARCHIVE_PREFIX)vendor/
182 zip -r $(ARCHIVE_VERSION).zip $(ARCHIVE_PREFIX)vendor/ 129 zip -r $(ARCHIVE_VERSION).zip $(ARCHIVE_PREFIX)vendor/
130 rsync -a tpl/ $(ARCHIVE_PREFIX)tpl/
131 zip -r $(ARCHIVE_VERSION).zip $(ARCHIVE_PREFIX)tpl/
183 rm -rf $(ARCHIVE_PREFIX) 132 rm -rf $(ARCHIVE_PREFIX)
184 133
185## 134##
@@ -197,10 +146,9 @@ authors:
197 @git shortlog -sne > AUTHORS 146 @git shortlog -sne > AUTHORS
198 @rm .mailmap 147 @rm .mailmap
199 148
200### generate Doxygen documentation 149### generate phpDocumentor documentation
201doxygen: clean 150phpdoc: clean
202 @rm -rf doxygen 151 @docker run --rm -v $(PWD):/data -u `id -u`:`id -g` phpdoc/phpdoc
203 @doxygen Doxyfile
204 152
205### generate HTML documentation from Markdown pages with MkDocs 153### generate HTML documentation from Markdown pages with MkDocs
206htmldoc: 154htmldoc:
diff --git a/README.md b/README.md
index 8199e3f8..0e23e33d 100644
--- a/README.md
+++ b/README.md
@@ -6,10 +6,10 @@ _Do you want to share the links you discover?_
6_Shaarli is a minimalist link sharing service that you can install on your own server._ 6_Shaarli is a minimalist link sharing service that you can install on your own server._
7_It is designed to be personal (single-user), fast and handy._ 7_It is designed to be personal (single-user), fast and handy._
8 8
9[![](https://img.shields.io/badge/stable-v0.8.7-blue.svg)](https://github.com/shaarli/Shaarli/releases/tag/v0.8.7) 9[![](https://img.shields.io/badge/stable-v0.9.7-blue.svg)](https://github.com/shaarli/Shaarli/releases/tag/v0.9.7)
10[![](https://img.shields.io/travis/shaarli/Shaarli/stable.svg?label=stable)](https://travis-ci.org/shaarli/Shaarli) 10[![](https://img.shields.io/travis/shaarli/Shaarli/stable.svg?label=stable)](https://travis-ci.org/shaarli/Shaarli)
11&bull; 11&bull;
12[![](https://img.shields.io/badge/latest-v0.9.7-blue.svg)](https://github.com/shaarli/Shaarli/releases/tag/v0.9.7) 12[![](https://img.shields.io/badge/latest-v0.10.2-blue.svg)](https://github.com/shaarli/Shaarli/releases/tag/v0.10.2)
13[![](https://img.shields.io/travis/shaarli/Shaarli/latest.svg?label=latest)](https://travis-ci.org/shaarli/Shaarli) 13[![](https://img.shields.io/travis/shaarli/Shaarli/latest.svg?label=latest)](https://travis-ci.org/shaarli/Shaarli)
14&bull; 14&bull;
15[![](https://img.shields.io/badge/master-v0.10.x-blue.svg)](https://github.com/shaarli/Shaarli) 15[![](https://img.shields.io/badge/master-v0.10.x-blue.svg)](https://github.com/shaarli/Shaarli)
diff --git a/application/ApplicationUtils.php b/application/ApplicationUtils.php
index 911873a0..7fe3cb32 100644
--- a/application/ApplicationUtils.php
+++ b/application/ApplicationUtils.php
@@ -1,4 +1,9 @@
1<?php 1<?php
2namespace Shaarli;
3
4use Exception;
5use Shaarli\Config\ConfigManager;
6
2/** 7/**
3 * Shaarli (application) utilities 8 * Shaarli (application) utilities
4 */ 9 */
@@ -24,7 +29,7 @@ class ApplicationUtils
24 * 29 *
25 * @return mixed the version code from the repository if available, else 'false' 30 * @return mixed the version code from the repository if available, else 'false'
26 */ 31 */
27 public static function getLatestGitVersionCode($url, $timeout=2) 32 public static function getLatestGitVersionCode($url, $timeout = 2)
28 { 33 {
29 list($headers, $data) = get_http_response($url, $timeout); 34 list($headers, $data) = get_http_response($url, $timeout);
30 35
@@ -51,7 +56,7 @@ class ApplicationUtils
51 return false; 56 return false;
52 } 57 }
53 } else { 58 } else {
54 if (! is_file($remote)) { 59 if (!is_file($remote)) {
55 return false; 60 return false;
56 } 61 }
57 $data = file_get_contents($remote); 62 $data = file_get_contents($remote);
@@ -86,17 +91,18 @@ class ApplicationUtils
86 * 91 *
87 * @return mixed the new version code if available and greater, else 'false' 92 * @return mixed the new version code if available and greater, else 'false'
88 */ 93 */
89 public static function checkUpdate($currentVersion, 94 public static function checkUpdate(
90 $updateFile, 95 $currentVersion,
91 $checkInterval, 96 $updateFile,
92 $enableCheck, 97 $checkInterval,
93 $isLoggedIn, 98 $enableCheck,
94 $branch='stable') 99 $isLoggedIn,
95 { 100 $branch = 'stable'
101 ) {
96 // Do not check versions for visitors 102 // Do not check versions for visitors
97 // Do not check if the user doesn't want to 103 // Do not check if the user doesn't want to
98 // Do not check with dev version 104 // Do not check with dev version
99 if (! $isLoggedIn || empty($enableCheck) || $currentVersion === 'dev') { 105 if (!$isLoggedIn || empty($enableCheck) || $currentVersion === 'dev') {
100 return false; 106 return false;
101 } 107 }
102 108
@@ -110,7 +116,7 @@ class ApplicationUtils
110 return false; 116 return false;
111 } 117 }
112 118
113 if (! in_array($branch, self::$GIT_BRANCHES)) { 119 if (!in_array($branch, self::$GIT_BRANCHES)) {
114 throw new Exception( 120 throw new Exception(
115 'Invalid branch selected for updates: "' . $branch . '"' 121 'Invalid branch selected for updates: "' . $branch . '"'
116 ); 122 );
@@ -122,7 +128,7 @@ class ApplicationUtils
122 self::$GIT_URL . '/' . $branch . '/' . self::$VERSION_FILE 128 self::$GIT_URL . '/' . $branch . '/' . self::$VERSION_FILE
123 ); 129 );
124 130
125 if (! $latestVersion) { 131 if (!$latestVersion) {
126 // Only update the file's modification date 132 // Only update the file's modification date
127 file_put_contents($updateFile, $currentVersion); 133 file_put_contents($updateFile, $currentVersion);
128 return false; 134 return false;
@@ -151,9 +157,9 @@ class ApplicationUtils
151 if (version_compare($curVersion, $minVersion) < 0) { 157 if (version_compare($curVersion, $minVersion) < 0) {
152 $msg = t( 158 $msg = t(
153 'Your PHP version is obsolete!' 159 'Your PHP version is obsolete!'
154 . ' Shaarli requires at least PHP %s, and thus cannot run.' 160 . ' Shaarli requires at least PHP %s, and thus cannot run.'
155 . ' Your PHP version has known security vulnerabilities and should be' 161 . ' Your PHP version has known security vulnerabilities and should be'
156 . ' updated as soon as possible.' 162 . ' updated as soon as possible.'
157 ); 163 );
158 throw new Exception(sprintf($msg, $minVersion)); 164 throw new Exception(sprintf($msg, $minVersion));
159 } 165 }
@@ -173,50 +179,50 @@ class ApplicationUtils
173 179
174 // Check script and template directories are readable 180 // Check script and template directories are readable
175 foreach (array( 181 foreach (array(
176 'application', 182 'application',
177 'inc', 183 'inc',
178 'plugins', 184 'plugins',
179 $rainTplDir, 185 $rainTplDir,
180 $rainTplDir.'/'.$conf->get('resource.theme'), 186 $rainTplDir . '/' . $conf->get('resource.theme'),
181 ) as $path) { 187 ) as $path) {
182 if (! is_readable(realpath($path))) { 188 if (!is_readable(realpath($path))) {
183 $errors[] = '"'.$path.'" '. t('directory is not readable'); 189 $errors[] = '"' . $path . '" ' . t('directory is not readable');
184 } 190 }
185 } 191 }
186 192
187 // Check cache and data directories are readable and writable 193 // Check cache and data directories are readable and writable
188 foreach (array( 194 foreach (array(
189 $conf->get('resource.thumbnails_cache'), 195 $conf->get('resource.thumbnails_cache'),
190 $conf->get('resource.data_dir'), 196 $conf->get('resource.data_dir'),
191 $conf->get('resource.page_cache'), 197 $conf->get('resource.page_cache'),
192 $conf->get('resource.raintpl_tmp'), 198 $conf->get('resource.raintpl_tmp'),
193 ) as $path) { 199 ) as $path) {
194 if (! is_readable(realpath($path))) { 200 if (!is_readable(realpath($path))) {
195 $errors[] = '"'.$path.'" '. t('directory is not readable'); 201 $errors[] = '"' . $path . '" ' . t('directory is not readable');
196 } 202 }
197 if (! is_writable(realpath($path))) { 203 if (!is_writable(realpath($path))) {
198 $errors[] = '"'.$path.'" '. t('directory is not writable'); 204 $errors[] = '"' . $path . '" ' . t('directory is not writable');
199 } 205 }
200 } 206 }
201 207
202 // Check configuration files are readable and writable 208 // Check configuration files are readable and writable
203 foreach (array( 209 foreach (array(
204 $conf->getConfigFileExt(), 210 $conf->getConfigFileExt(),
205 $conf->get('resource.datastore'), 211 $conf->get('resource.datastore'),
206 $conf->get('resource.ban_file'), 212 $conf->get('resource.ban_file'),
207 $conf->get('resource.log'), 213 $conf->get('resource.log'),
208 $conf->get('resource.update_check'), 214 $conf->get('resource.update_check'),
209 ) as $path) { 215 ) as $path) {
210 if (! is_file(realpath($path))) { 216 if (!is_file(realpath($path))) {
211 # the file may not exist yet 217 # the file may not exist yet
212 continue; 218 continue;
213 } 219 }
214 220
215 if (! is_readable(realpath($path))) { 221 if (!is_readable(realpath($path))) {
216 $errors[] = '"'.$path.'" '. t('file is not readable'); 222 $errors[] = '"' . $path . '" ' . t('file is not readable');
217 } 223 }
218 if (! is_writable(realpath($path))) { 224 if (!is_writable(realpath($path))) {
219 $errors[] = '"'.$path.'" '. t('file is not writable'); 225 $errors[] = '"' . $path . '" ' . t('file is not writable');
220 } 226 }
221 } 227 }
222 228
diff --git a/application/FileUtils.php b/application/FileUtils.php
index b89ea12b..30560bfc 100644
--- a/application/FileUtils.php
+++ b/application/FileUtils.php
@@ -1,6 +1,8 @@
1<?php 1<?php
2 2
3require_once 'exceptions/IOException.php'; 3namespace Shaarli;
4
5use Shaarli\Exceptions\IOException;
4 6
5/** 7/**
6 * Class FileUtils 8 * Class FileUtils
@@ -44,7 +46,7 @@ class FileUtils
44 46
45 return file_put_contents( 47 return file_put_contents(
46 $file, 48 $file,
47 self::$phpPrefix.base64_encode(gzdeflate(serialize($content))).self::$phpSuffix 49 self::$phpPrefix . base64_encode(gzdeflate(serialize($content))) . self::$phpSuffix
48 ); 50 );
49 } 51 }
50 52
@@ -62,7 +64,7 @@ class FileUtils
62 { 64 {
63 // Note that gzinflate is faster than gzuncompress. 65 // Note that gzinflate is faster than gzuncompress.
64 // See: http://www.php.net/manual/en/function.gzdeflate.php#96439 66 // See: http://www.php.net/manual/en/function.gzdeflate.php#96439
65 if (! is_readable($file)) { 67 if (!is_readable($file)) {
66 return $default; 68 return $default;
67 } 69 }
68 70
diff --git a/application/History.php b/application/History.php
index 35ec016a..a5846652 100644
--- a/application/History.php
+++ b/application/History.php
@@ -1,4 +1,8 @@
1<?php 1<?php
2namespace Shaarli;
3
4use DateTime;
5use Exception;
2 6
3/** 7/**
4 * Class History 8 * Class History
@@ -66,7 +70,7 @@ class History
66 * History constructor. 70 * History constructor.
67 * 71 *
68 * @param string $historyFilePath History file path. 72 * @param string $historyFilePath History file path.
69 * @param int $retentionTime History content rentention time in seconds. 73 * @param int $retentionTime History content retention time in seconds.
70 * 74 *
71 * @throws Exception if something goes wrong. 75 * @throws Exception if something goes wrong.
72 */ 76 */
@@ -166,11 +170,11 @@ class History
166 */ 170 */
167 protected function check() 171 protected function check()
168 { 172 {
169 if (! is_file($this->historyFilePath)) { 173 if (!is_file($this->historyFilePath)) {
170 FileUtils::writeFlatDB($this->historyFilePath, []); 174 FileUtils::writeFlatDB($this->historyFilePath, []);
171 } 175 }
172 176
173 if (! is_writable($this->historyFilePath)) { 177 if (!is_writable($this->historyFilePath)) {
174 throw new Exception(t('History file isn\'t readable or writable')); 178 throw new Exception(t('History file isn\'t readable or writable'));
175 } 179 }
176 } 180 }
@@ -191,7 +195,7 @@ class History
191 */ 195 */
192 protected function write() 196 protected function write()
193 { 197 {
194 $comparaison = new DateTime('-'. $this->retentionTime . ' seconds'); 198 $comparaison = new DateTime('-' . $this->retentionTime . ' seconds');
195 foreach ($this->history as $key => $value) { 199 foreach ($this->history as $key => $value) {
196 if ($value['datetime'] < $comparaison) { 200 if ($value['datetime'] < $comparaison) {
197 unset($this->history[$key]); 201 unset($this->history[$key]);
diff --git a/application/Languages.php b/application/Languages.php
index 4fa32426..5cda802e 100644
--- a/application/Languages.php
+++ b/application/Languages.php
@@ -3,7 +3,6 @@
3namespace Shaarli; 3namespace Shaarli;
4 4
5use Gettext\GettextTranslator; 5use Gettext\GettextTranslator;
6use Gettext\Merge;
7use Gettext\Translations; 6use Gettext\Translations;
8use Gettext\Translator; 7use Gettext\Translator;
9use Gettext\TranslatorInterface; 8use Gettext\TranslatorInterface;
@@ -92,7 +91,7 @@ class Languages
92 /** 91 /**
93 * Initialize the translator using php gettext extension (gettext dependency act as a wrapper). 92 * Initialize the translator using php gettext extension (gettext dependency act as a wrapper).
94 */ 93 */
95 protected function initGettextTranslator () 94 protected function initGettextTranslator()
96 { 95 {
97 $this->translator = new GettextTranslator(); 96 $this->translator = new GettextTranslator();
98 $this->translator->setLanguage($this->language); 97 $this->translator->setLanguage($this->language);
@@ -125,7 +124,8 @@ class Languages
125 $translations = $translations->addFromPoFile('inc/languages/'. $this->language .'/LC_MESSAGES/shaarli.po'); 124 $translations = $translations->addFromPoFile('inc/languages/'. $this->language .'/LC_MESSAGES/shaarli.po');
126 $translations->setDomain('shaarli'); 125 $translations->setDomain('shaarli');
127 $this->translator->loadTranslations($translations); 126 $this->translator->loadTranslations($translations);
128 } catch (\InvalidArgumentException $e) {} 127 } catch (\InvalidArgumentException $e) {
128 }
129 129
130 // Default extension translation from the current theme 130 // Default extension translation from the current theme
131 $theme = $this->conf->get('theme'); 131 $theme = $this->conf->get('theme');
@@ -137,7 +137,8 @@ class Languages
137 ); 137 );
138 $translations->setDomain($theme); 138 $translations->setDomain($theme);
139 $this->translator->loadTranslations($translations); 139 $this->translator->loadTranslations($translations);
140 } catch (\InvalidArgumentException $e) {} 140 } catch (\InvalidArgumentException $e) {
141 }
141 } 142 }
142 143
143 // Extension translations (plugins, themes, etc.). 144 // Extension translations (plugins, themes, etc.).
@@ -147,10 +148,13 @@ class Languages
147 } 148 }
148 149
149 try { 150 try {
150 $extension = Translations::fromPoFile($translationPath . $this->language .'/LC_MESSAGES/'. $domain .'.po'); 151 $extension = Translations::fromPoFile(
152 $translationPath . $this->language .'/LC_MESSAGES/'. $domain .'.po'
153 );
151 $extension->setDomain($domain); 154 $extension->setDomain($domain);
152 $this->translator->loadTranslations($extension); 155 $this->translator->loadTranslations($extension);
153 } catch (\InvalidArgumentException $e) {} 156 } catch (\InvalidArgumentException $e) {
157 }
154 } 158 }
155 } 159 }
156 160
diff --git a/application/Router.php b/application/Router.php
index 4df0387c..05877acd 100644
--- a/application/Router.php
+++ b/application/Router.php
@@ -1,4 +1,5 @@
1<?php 1<?php
2namespace Shaarli;
2 3
3/** 4/**
4 * Class Router 5 * Class Router
@@ -7,6 +8,8 @@
7 */ 8 */
8class Router 9class Router
9{ 10{
11 public static $AJAX_THUMB_UPDATE = 'ajax_thumb_update';
12
10 public static $PAGE_LOGIN = 'login'; 13 public static $PAGE_LOGIN = 'login';
11 14
12 public static $PAGE_PICWALL = 'picwall'; 15 public static $PAGE_PICWALL = 'picwall';
@@ -35,6 +38,8 @@ class Router
35 38
36 public static $PAGE_DELETELINK = 'delete_link'; 39 public static $PAGE_DELETELINK = 'delete_link';
37 40
41 public static $PAGE_PINLINK = 'pin';
42
38 public static $PAGE_EXPORT = 'export'; 43 public static $PAGE_EXPORT = 'export';
39 44
40 public static $PAGE_IMPORT = 'import'; 45 public static $PAGE_IMPORT = 'import';
@@ -47,6 +52,8 @@ class Router
47 52
48 public static $PAGE_SAVE_PLUGINSADMIN = 'save_pluginadmin'; 53 public static $PAGE_SAVE_PLUGINSADMIN = 'save_pluginadmin';
49 54
55 public static $PAGE_THUMBS_UPDATE = 'thumbs_update';
56
50 public static $GET_TOKEN = 'token'; 57 public static $GET_TOKEN = 'token';
51 58
52 /** 59 /**
@@ -69,60 +76,68 @@ class Router
69 return self::$PAGE_LINKLIST; 76 return self::$PAGE_LINKLIST;
70 } 77 }
71 78
72 if (startsWith($query, 'do='. self::$PAGE_LOGIN) && $loggedIn === false) { 79 if (startsWith($query, 'do=' . self::$PAGE_LOGIN) && $loggedIn === false) {
73 return self::$PAGE_LOGIN; 80 return self::$PAGE_LOGIN;
74 } 81 }
75 82
76 if (startsWith($query, 'do='. self::$PAGE_PICWALL)) { 83 if (startsWith($query, 'do=' . self::$PAGE_PICWALL)) {
77 return self::$PAGE_PICWALL; 84 return self::$PAGE_PICWALL;
78 } 85 }
79 86
80 if (startsWith($query, 'do='. self::$PAGE_TAGCLOUD)) { 87 if (startsWith($query, 'do=' . self::$PAGE_TAGCLOUD)) {
81 return self::$PAGE_TAGCLOUD; 88 return self::$PAGE_TAGCLOUD;
82 } 89 }
83 90
84 if (startsWith($query, 'do='. self::$PAGE_TAGLIST)) { 91 if (startsWith($query, 'do=' . self::$PAGE_TAGLIST)) {
85 return self::$PAGE_TAGLIST; 92 return self::$PAGE_TAGLIST;
86 } 93 }
87 94
88 if (startsWith($query, 'do='. self::$PAGE_OPENSEARCH)) { 95 if (startsWith($query, 'do=' . self::$PAGE_OPENSEARCH)) {
89 return self::$PAGE_OPENSEARCH; 96 return self::$PAGE_OPENSEARCH;
90 } 97 }
91 98
92 if (startsWith($query, 'do='. self::$PAGE_DAILY)) { 99 if (startsWith($query, 'do=' . self::$PAGE_DAILY)) {
93 return self::$PAGE_DAILY; 100 return self::$PAGE_DAILY;
94 } 101 }
95 102
96 if (startsWith($query, 'do='. self::$PAGE_FEED_ATOM)) { 103 if (startsWith($query, 'do=' . self::$PAGE_FEED_ATOM)) {
97 return self::$PAGE_FEED_ATOM; 104 return self::$PAGE_FEED_ATOM;
98 } 105 }
99 106
100 if (startsWith($query, 'do='. self::$PAGE_FEED_RSS)) { 107 if (startsWith($query, 'do=' . self::$PAGE_FEED_RSS)) {
101 return self::$PAGE_FEED_RSS; 108 return self::$PAGE_FEED_RSS;
102 } 109 }
103 110
111 if (startsWith($query, 'do=' . self::$PAGE_THUMBS_UPDATE)) {
112 return self::$PAGE_THUMBS_UPDATE;
113 }
114
115 if (startsWith($query, 'do=' . self::$AJAX_THUMB_UPDATE)) {
116 return self::$AJAX_THUMB_UPDATE;
117 }
118
104 // At this point, only loggedin pages. 119 // At this point, only loggedin pages.
105 if (!$loggedIn) { 120 if (!$loggedIn) {
106 return self::$PAGE_LINKLIST; 121 return self::$PAGE_LINKLIST;
107 } 122 }
108 123
109 if (startsWith($query, 'do='. self::$PAGE_TOOLS)) { 124 if (startsWith($query, 'do=' . self::$PAGE_TOOLS)) {
110 return self::$PAGE_TOOLS; 125 return self::$PAGE_TOOLS;
111 } 126 }
112 127
113 if (startsWith($query, 'do='. self::$PAGE_CHANGEPASSWORD)) { 128 if (startsWith($query, 'do=' . self::$PAGE_CHANGEPASSWORD)) {
114 return self::$PAGE_CHANGEPASSWORD; 129 return self::$PAGE_CHANGEPASSWORD;
115 } 130 }
116 131
117 if (startsWith($query, 'do='. self::$PAGE_CONFIGURE)) { 132 if (startsWith($query, 'do=' . self::$PAGE_CONFIGURE)) {
118 return self::$PAGE_CONFIGURE; 133 return self::$PAGE_CONFIGURE;
119 } 134 }
120 135
121 if (startsWith($query, 'do='. self::$PAGE_CHANGETAG)) { 136 if (startsWith($query, 'do=' . self::$PAGE_CHANGETAG)) {
122 return self::$PAGE_CHANGETAG; 137 return self::$PAGE_CHANGETAG;
123 } 138 }
124 139
125 if (startsWith($query, 'do='. self::$PAGE_ADDLINK)) { 140 if (startsWith($query, 'do=' . self::$PAGE_ADDLINK)) {
126 return self::$PAGE_ADDLINK; 141 return self::$PAGE_ADDLINK;
127 } 142 }
128 143
@@ -134,23 +149,27 @@ class Router
134 return self::$PAGE_DELETELINK; 149 return self::$PAGE_DELETELINK;
135 } 150 }
136 151
137 if (startsWith($query, 'do='. self::$PAGE_EXPORT)) { 152 if (startsWith($query, 'do=' . self::$PAGE_PINLINK)) {
153 return self::$PAGE_PINLINK;
154 }
155
156 if (startsWith($query, 'do=' . self::$PAGE_EXPORT)) {
138 return self::$PAGE_EXPORT; 157 return self::$PAGE_EXPORT;
139 } 158 }
140 159
141 if (startsWith($query, 'do='. self::$PAGE_IMPORT)) { 160 if (startsWith($query, 'do=' . self::$PAGE_IMPORT)) {
142 return self::$PAGE_IMPORT; 161 return self::$PAGE_IMPORT;
143 } 162 }
144 163
145 if (startsWith($query, 'do='. self::$PAGE_PLUGINSADMIN)) { 164 if (startsWith($query, 'do=' . self::$PAGE_PLUGINSADMIN)) {
146 return self::$PAGE_PLUGINSADMIN; 165 return self::$PAGE_PLUGINSADMIN;
147 } 166 }
148 167
149 if (startsWith($query, 'do='. self::$PAGE_SAVE_PLUGINSADMIN)) { 168 if (startsWith($query, 'do=' . self::$PAGE_SAVE_PLUGINSADMIN)) {
150 return self::$PAGE_SAVE_PLUGINSADMIN; 169 return self::$PAGE_SAVE_PLUGINSADMIN;
151 } 170 }
152 171
153 if (startsWith($query, 'do='. self::$GET_TOKEN)) { 172 if (startsWith($query, 'do=' . self::$GET_TOKEN)) {
154 return self::$GET_TOKEN; 173 return self::$GET_TOKEN;
155 } 174 }
156 175
diff --git a/application/Thumbnailer.php b/application/Thumbnailer.php
new file mode 100644
index 00000000..a23f98e9
--- /dev/null
+++ b/application/Thumbnailer.php
@@ -0,0 +1,130 @@
1<?php
2
3namespace Shaarli;
4
5use Shaarli\Config\ConfigManager;
6use WebThumbnailer\Application\ConfigManager as WTConfigManager;
7use WebThumbnailer\Exception\WebThumbnailerException;
8use WebThumbnailer\WebThumbnailer;
9
10/**
11 * Class Thumbnailer
12 *
13 * Utility class used to retrieve thumbnails using web-thumbnailer dependency.
14 */
15class Thumbnailer
16{
17 const COMMON_MEDIA_DOMAINS = [
18 'imgur.com',
19 'flickr.com',
20 'youtube.com',
21 'wikimedia.org',
22 'redd.it',
23 'gfycat.com',
24 'media.giphy.com',
25 'twitter.com',
26 'twimg.com',
27 'instagram.com',
28 'pinterest.com',
29 'pinterest.fr',
30 'tumblr.com',
31 'deviantart.com',
32 ];
33
34 const MODE_ALL = 'all';
35 const MODE_COMMON = 'common';
36 const MODE_NONE = 'none';
37
38 /**
39 * @var WebThumbnailer instance.
40 */
41 protected $wt;
42
43 /**
44 * @var ConfigManager instance.
45 */
46 protected $conf;
47
48 /**
49 * Thumbnailer constructor.
50 *
51 * @param ConfigManager $conf instance.
52 */
53 public function __construct($conf)
54 {
55 $this->conf = $conf;
56
57 if (! $this->checkRequirements()) {
58 $this->conf->set('thumbnails.enabled', false);
59 $this->conf->write(true);
60 // TODO: create a proper error handling system able to catch exceptions...
61 die(t(
62 'php-gd extension must be loaded to use thumbnails. '
63 .'Thumbnails are now disabled. Please reload the page.'
64 ));
65 }
66
67 $this->wt = new WebThumbnailer();
68 WTConfigManager::addFile('inc/web-thumbnailer.json');
69 $this->wt->maxWidth($this->conf->get('thumbnails.width'))
70 ->maxHeight($this->conf->get('thumbnails.height'))
71 ->crop(true)
72 ->debug($this->conf->get('dev.debug', false));
73 }
74
75 /**
76 * Retrieve a thumbnail for given URL
77 *
78 * @param string $url where to look for a thumbnail.
79 *
80 * @return bool|string The thumbnail relative cache file path, or false if none has been found.
81 */
82 public function get($url)
83 {
84 if ($this->conf->get('thumbnails.mode') === self::MODE_COMMON
85 && ! $this->isCommonMediaOrImage($url)
86 ) {
87 return false;
88 }
89
90 try {
91 return $this->wt->thumbnail($url);
92 } catch (WebThumbnailerException $e) {
93 // Exceptions are only thrown in debug mode.
94 error_log(get_class($e) . ': ' . $e->getMessage());
95 }
96 return false;
97 }
98
99 /**
100 * We check weather the given URL is from a common media domain,
101 * or if the file extension is an image.
102 *
103 * @param string $url to check
104 *
105 * @return bool true if it's an image or from a common media domain, false otherwise.
106 */
107 public function isCommonMediaOrImage($url)
108 {
109 foreach (self::COMMON_MEDIA_DOMAINS as $domain) {
110 if (strpos($url, $domain) !== false) {
111 return true;
112 }
113 }
114
115 if (endsWith($url, '.jpg') || endsWith($url, '.png') || endsWith($url, '.jpeg')) {
116 return true;
117 }
118
119 return false;
120 }
121
122 /**
123 * Make sure that requirements are match to use thumbnails:
124 * - php-gd is loaded
125 */
126 protected function checkRequirements()
127 {
128 return extension_loaded('gd');
129 }
130}
diff --git a/application/Utils.php b/application/Utils.php
index 97b12fcf..925e1a22 100644
--- a/application/Utils.php
+++ b/application/Utils.php
@@ -97,7 +97,7 @@ function escape($input)
97 97
98 if (is_array($input)) { 98 if (is_array($input)) {
99 $out = array(); 99 $out = array();
100 foreach($input as $key => $value) { 100 foreach ($input as $key => $value) {
101 $out[$key] = escape($value); 101 $out[$key] = escape($value);
102 } 102 }
103 return $out; 103 return $out;
@@ -355,10 +355,13 @@ function return_bytes($val)
355 $val = trim($val); 355 $val = trim($val);
356 $last = strtolower($val[strlen($val)-1]); 356 $last = strtolower($val[strlen($val)-1]);
357 $val = intval(substr($val, 0, -1)); 357 $val = intval(substr($val, 0, -1));
358 switch($last) { 358 switch ($last) {
359 case 'g': $val *= 1024; 359 case 'g':
360 case 'm': $val *= 1024; 360 $val *= 1024;
361 case 'k': $val *= 1024; 361 case 'm':
362 $val *= 1024;
363 case 'k':
364 $val *= 1024;
362 } 365 }
363 return $val; 366 return $val;
364} 367}
@@ -452,6 +455,7 @@ function alphabetical_sort(&$data, $reverse = false, $byKeys = false)
452 * 455 *
453 * @return string Text translated. 456 * @return string Text translated.
454 */ 457 */
455function t($text, $nText = '', $nb = 1, $domain = 'shaarli') { 458function t($text, $nText = '', $nb = 1, $domain = 'shaarli')
459{
456 return dn__($domain, $text, $nText, $nb); 460 return dn__($domain, $text, $nText, $nb);
457} 461}
diff --git a/application/api/ApiMiddleware.php b/application/api/ApiMiddleware.php
index ff209393..5ffb8c6d 100644
--- a/application/api/ApiMiddleware.php
+++ b/application/api/ApiMiddleware.php
@@ -1,9 +1,8 @@
1<?php 1<?php
2namespace Shaarli\Api; 2namespace Shaarli\Api;
3 3
4use Shaarli\Api\Exceptions\ApiException;
5use Shaarli\Api\Exceptions\ApiAuthorizationException; 4use Shaarli\Api\Exceptions\ApiAuthorizationException;
6 5use Shaarli\Api\Exceptions\ApiException;
7use Shaarli\Config\ConfigManager; 6use Shaarli\Config\ConfigManager;
8use Slim\Container; 7use Slim\Container;
9use Slim\Http\Request; 8use Slim\Http\Request;
@@ -65,7 +64,7 @@ class ApiMiddleware
65 try { 64 try {
66 $this->checkRequest($request); 65 $this->checkRequest($request);
67 $response = $next($request, $response); 66 $response = $next($request, $response);
68 } catch(ApiException $e) { 67 } catch (ApiException $e) {
69 $e->setResponse($response); 68 $e->setResponse($response);
70 $e->setDebug($this->conf->get('dev.debug', false)); 69 $e->setDebug($this->conf->get('dev.debug', false));
71 $response = $e->getApiResponse(); 70 $response = $e->getApiResponse();
@@ -98,7 +97,8 @@ class ApiMiddleware
98 * 97 *
99 * @throws ApiAuthorizationException The token couldn't be validated. 98 * @throws ApiAuthorizationException The token couldn't be validated.
100 */ 99 */
101 protected function checkToken($request) { 100 protected function checkToken($request)
101 {
102 if (! $request->hasHeader('Authorization')) { 102 if (! $request->hasHeader('Authorization')) {
103 throw new ApiAuthorizationException('JWT token not provided'); 103 throw new ApiAuthorizationException('JWT token not provided');
104 } 104 }
@@ -126,7 +126,7 @@ class ApiMiddleware
126 */ 126 */
127 protected function setLinkDb($conf) 127 protected function setLinkDb($conf)
128 { 128 {
129 $linkDb = new \LinkDB( 129 $linkDb = new \Shaarli\Bookmark\LinkDB(
130 $conf->get('resource.datastore'), 130 $conf->get('resource.datastore'),
131 true, 131 true,
132 $conf->get('privacy.hide_public_links'), 132 $conf->get('privacy.hide_public_links'),
diff --git a/application/api/ApiUtils.php b/application/api/ApiUtils.php
index fc5ecaf1..1824b5d0 100644
--- a/application/api/ApiUtils.php
+++ b/application/api/ApiUtils.php
@@ -1,8 +1,8 @@
1<?php 1<?php
2namespace Shaarli\Api; 2namespace Shaarli\Api;
3 3
4use Shaarli\Base64Url;
5use Shaarli\Api\Exceptions\ApiAuthorizationException; 4use Shaarli\Api\Exceptions\ApiAuthorizationException;
5use Shaarli\Http\Base64Url;
6 6
7/** 7/**
8 * REST API utilities 8 * REST API utilities
@@ -12,7 +12,7 @@ class ApiUtils
12 /** 12 /**
13 * Validates a JWT token authenticity. 13 * Validates a JWT token authenticity.
14 * 14 *
15 * @param string $token JWT token extracted from the headers. 15 * @param string $token JWT token extracted from the headers.
16 * @param string $secret API secret set in the settings. 16 * @param string $secret API secret set in the settings.
17 * 17 *
18 * @throws ApiAuthorizationException the token is not valid. 18 * @throws ApiAuthorizationException the token is not valid.
@@ -50,7 +50,7 @@ class ApiUtils
50 /** 50 /**
51 * Format a Link for the REST API. 51 * Format a Link for the REST API.
52 * 52 *
53 * @param array $link Link data read from the datastore. 53 * @param array $link Link data read from the datastore.
54 * @param string $indexUrl Shaarli's index URL (used for relative URL). 54 * @param string $indexUrl Shaarli's index URL (used for relative URL).
55 * 55 *
56 * @return array Link data formatted for the REST API. 56 * @return array Link data formatted for the REST API.
diff --git a/application/api/controllers/ApiController.php b/application/api/controllers/ApiController.php
index 3be85b98..a6e7cbab 100644
--- a/application/api/controllers/ApiController.php
+++ b/application/api/controllers/ApiController.php
@@ -2,8 +2,9 @@
2 2
3namespace Shaarli\Api\Controllers; 3namespace Shaarli\Api\Controllers;
4 4
5use Shaarli\Bookmark\LinkDB;
5use Shaarli\Config\ConfigManager; 6use Shaarli\Config\ConfigManager;
6use \Slim\Container; 7use Slim\Container;
7 8
8/** 9/**
9 * Abstract Class ApiController 10 * Abstract Class ApiController
@@ -25,12 +26,12 @@ abstract class ApiController
25 protected $conf; 26 protected $conf;
26 27
27 /** 28 /**
28 * @var \LinkDB 29 * @var LinkDB
29 */ 30 */
30 protected $linkDb; 31 protected $linkDb;
31 32
32 /** 33 /**
33 * @var \History 34 * @var HistoryController
34 */ 35 */
35 protected $history; 36 protected $history;
36 37
@@ -41,7 +42,7 @@ abstract class ApiController
41 42
42 /** 43 /**
43 * ApiController constructor. 44 * ApiController constructor.
44 * 45 *
45 * Note: enabling debug mode displays JSON with readable formatting. 46 * Note: enabling debug mode displays JSON with readable formatting.
46 * 47 *
47 * @param Container $ci Slim container. 48 * @param Container $ci Slim container.
diff --git a/application/api/controllers/History.php b/application/api/controllers/HistoryController.php
index 5cc453bf..9afcfa26 100644
--- a/application/api/controllers/History.php
+++ b/application/api/controllers/HistoryController.php
@@ -14,7 +14,7 @@ use Slim\Http\Response;
14 * 14 *
15 * @package Shaarli\Api\Controllers 15 * @package Shaarli\Api\Controllers
16 */ 16 */
17class History extends ApiController 17class HistoryController extends ApiController
18{ 18{
19 /** 19 /**
20 * Service providing operation regarding Shaarli datastore and settings. 20 * Service providing operation regarding Shaarli datastore and settings.
@@ -35,8 +35,7 @@ class History extends ApiController
35 $offset = $request->getParam('offset'); 35 $offset = $request->getParam('offset');
36 if (empty($offset)) { 36 if (empty($offset)) {
37 $offset = 0; 37 $offset = 0;
38 } 38 } elseif (ctype_digit($offset)) {
39 elseif (ctype_digit($offset)) {
40 $offset = (int) $offset; 39 $offset = (int) $offset;
41 } else { 40 } else {
42 throw new ApiBadParametersException('Invalid offset'); 41 throw new ApiBadParametersException('Invalid offset');
diff --git a/application/api/controllers/Info.php b/application/api/controllers/Info.php
index 25433f72..f37dcae5 100644
--- a/application/api/controllers/Info.php
+++ b/application/api/controllers/Info.php
@@ -7,7 +7,7 @@ use Slim\Http\Response;
7 7
8/** 8/**
9 * Class Info 9 * Class Info
10 * 10 *
11 * REST API Controller: /info 11 * REST API Controller: /info
12 * 12 *
13 * @package Api\Controllers 13 * @package Api\Controllers
@@ -17,7 +17,7 @@ class Info extends ApiController
17{ 17{
18 /** 18 /**
19 * Service providing various information about Shaarli instance. 19 * Service providing various information about Shaarli instance.
20 * 20 *
21 * @param Request $request Slim request. 21 * @param Request $request Slim request.
22 * @param Response $response Slim response. 22 * @param Response $response Slim response.
23 * 23 *
diff --git a/application/api/controllers/Tags.php b/application/api/controllers/Tags.php
index 6dd78750..82f3ef74 100644
--- a/application/api/controllers/Tags.php
+++ b/application/api/controllers/Tags.php
@@ -4,7 +4,6 @@ namespace Shaarli\Api\Controllers;
4 4
5use Shaarli\Api\ApiUtils; 5use Shaarli\Api\ApiUtils;
6use Shaarli\Api\Exceptions\ApiBadParametersException; 6use Shaarli\Api\Exceptions\ApiBadParametersException;
7use Shaarli\Api\Exceptions\ApiLinkNotFoundException;
8use Shaarli\Api\Exceptions\ApiTagNotFoundException; 7use Shaarli\Api\Exceptions\ApiTagNotFoundException;
9use Slim\Http\Request; 8use Slim\Http\Request;
10use Slim\Http\Response; 9use Slim\Http\Response;
diff --git a/application/api/exceptions/ApiException.php b/application/api/exceptions/ApiException.php
index c8490e0c..d6b66323 100644
--- a/application/api/exceptions/ApiException.php
+++ b/application/api/exceptions/ApiException.php
@@ -10,7 +10,8 @@ use Slim\Http\Response;
10 * Parent Exception related to the API, able to generate a valid Response (ResponseInterface). 10 * Parent Exception related to the API, able to generate a valid Response (ResponseInterface).
11 * Also can include various information in debug mode. 11 * Also can include various information in debug mode.
12 */ 12 */
13abstract class ApiException extends \Exception { 13abstract class ApiException extends \Exception
14{
14 15
15 /** 16 /**
16 * @var Response instance from Slim. 17 * @var Response instance from Slim.
@@ -27,7 +28,7 @@ abstract class ApiException extends \Exception {
27 * 28 *
28 * @return Response Final response to give. 29 * @return Response Final response to give.
29 */ 30 */
30 public abstract function getApiResponse(); 31 abstract public function getApiResponse();
31 32
32 /** 33 /**
33 * Creates ApiResponse body. 34 * Creates ApiResponse body.
@@ -36,7 +37,8 @@ abstract class ApiException extends \Exception {
36 * 37 *
37 * @return array|string response body 38 * @return array|string response body
38 */ 39 */
39 protected function getApiResponseBody() { 40 protected function getApiResponseBody()
41 {
40 if ($this->debug !== true) { 42 if ($this->debug !== true) {
41 return $this->getMessage(); 43 return $this->getMessage();
42 } 44 }
diff --git a/application/api/exceptions/ApiLinkNotFoundException.php b/application/api/exceptions/ApiLinkNotFoundException.php
index de7e14f5..7c2bb56e 100644
--- a/application/api/exceptions/ApiLinkNotFoundException.php
+++ b/application/api/exceptions/ApiLinkNotFoundException.php
@@ -2,9 +2,6 @@
2 2
3namespace Shaarli\Api\Exceptions; 3namespace Shaarli\Api\Exceptions;
4 4
5
6use Slim\Http\Response;
7
8/** 5/**
9 * Class ApiLinkNotFoundException 6 * Class ApiLinkNotFoundException
10 * 7 *
diff --git a/application/api/exceptions/ApiTagNotFoundException.php b/application/api/exceptions/ApiTagNotFoundException.php
index eed5afa5..66ace8bf 100644
--- a/application/api/exceptions/ApiTagNotFoundException.php
+++ b/application/api/exceptions/ApiTagNotFoundException.php
@@ -2,9 +2,6 @@
2 2
3namespace Shaarli\Api\Exceptions; 3namespace Shaarli\Api\Exceptions;
4 4
5
6use Slim\Http\Response;
7
8/** 5/**
9 * Class ApiTagNotFoundException 6 * Class ApiTagNotFoundException
10 * 7 *
diff --git a/application/LinkDB.php b/application/bookmark/LinkDB.php
index cd0f2967..c13a1141 100644
--- a/application/LinkDB.php
+++ b/application/bookmark/LinkDB.php
@@ -1,4 +1,15 @@
1<?php 1<?php
2
3namespace Shaarli\Bookmark;
4
5use ArrayAccess;
6use Countable;
7use DateTime;
8use Iterator;
9use Shaarli\Bookmark\Exception\LinkNotFoundException;
10use Shaarli\Exceptions\IOException;
11use Shaarli\FileUtils;
12
2/** 13/**
3 * Data storage for links. 14 * Data storage for links.
4 * 15 *
@@ -107,8 +118,8 @@ class LinkDB implements Iterator, Countable, ArrayAccess
107 $hidePublicLinks, 118 $hidePublicLinks,
108 $redirector = '', 119 $redirector = '',
109 $redirectorEncode = true 120 $redirectorEncode = true
110 ) 121 ) {
111 { 122
112 $this->datastore = $datastore; 123 $this->datastore = $datastore;
113 $this->loggedIn = $isLoggedIn; 124 $this->loggedIn = $isLoggedIn;
114 $this->hidePublicLinks = $hidePublicLinks; 125 $this->hidePublicLinks = $hidePublicLinks;
@@ -138,7 +149,7 @@ class LinkDB implements Iterator, Countable, ArrayAccess
138 if (!isset($value['id']) || empty($value['url'])) { 149 if (!isset($value['id']) || empty($value['url'])) {
139 die(t('Internal Error: A link should always have an id and URL.')); 150 die(t('Internal Error: A link should always have an id and URL.'));
140 } 151 }
141 if (($offset !== null && ! is_int($offset)) || ! is_int($value['id'])) { 152 if (($offset !== null && !is_int($offset)) || !is_int($value['id'])) {
142 die(t('You must specify an integer as a key.')); 153 die(t('You must specify an integer as a key.'));
143 } 154 }
144 if ($offset !== null && $offset !== $value['id']) { 155 if ($offset !== null && $offset !== $value['id']) {
@@ -248,28 +259,31 @@ class LinkDB implements Iterator, Countable, ArrayAccess
248 $this->links = array(); 259 $this->links = array();
249 $link = array( 260 $link = array(
250 'id' => 1, 261 'id' => 1,
251 'title'=> t('The personal, minimalist, super-fast, database free, bookmarking service'), 262 'title' => t('The personal, minimalist, super-fast, database free, bookmarking service'),
252 'url'=>'https://shaarli.readthedocs.io', 263 'url' => 'https://shaarli.readthedocs.io',
253 'description'=>t('Welcome to Shaarli! This is your first public bookmark. To edit or delete me, you must first login. 264 'description' => t(
265 'Welcome to Shaarli! This is your first public bookmark. '
266 . 'To edit or delete me, you must first login.
254 267
255To learn how to use Shaarli, consult the link "Documentation" at the bottom of this page. 268To learn how to use Shaarli, consult the link "Documentation" at the bottom of this page.
256 269
257You use the community supported version of the original Shaarli project, by Sebastien Sauvage.'), 270You use the community supported version of the original Shaarli project, by Sebastien Sauvage.'
258 'private'=>0, 271 ),
259 'created'=> new DateTime(), 272 'private' => 0,
260 'tags'=>'opensource software' 273 'created' => new DateTime(),
274 'tags' => 'opensource software'
261 ); 275 );
262 $link['shorturl'] = link_small_hash($link['created'], $link['id']); 276 $link['shorturl'] = link_small_hash($link['created'], $link['id']);
263 $this->links[1] = $link; 277 $this->links[1] = $link;
264 278
265 $link = array( 279 $link = array(
266 'id' => 0, 280 'id' => 0,
267 'title'=> t('My secret stuff... - Pastebin.com'), 281 'title' => t('My secret stuff... - Pastebin.com'),
268 'url'=>'http://sebsauvage.net/paste/?8434b27936c09649#bR7XsXhoTiLcqCpQbmOpBi3rq2zzQUC5hBI7ZT1O3x8=', 282 'url' => 'http://sebsauvage.net/paste/?8434b27936c09649#bR7XsXhoTiLcqCpQbmOpBi3rq2zzQUC5hBI7ZT1O3x8=',
269 'description'=> t('Shhhh! I\'m a private link only YOU can see. You can delete me too.'), 283 'description' => t('Shhhh! I\'m a private link only YOU can see. You can delete me too.'),
270 'private'=>1, 284 'private' => 1,
271 'created'=> new DateTime('1 minute ago'), 285 'created' => new DateTime('1 minute ago'),
272 'tags'=>'secretstuff', 286 'tags' => 'secretstuff',
273 ); 287 );
274 $link['shorturl'] = link_small_hash($link['created'], $link['id']); 288 $link['shorturl'] = link_small_hash($link['created'], $link['id']);
275 $this->links[0] = $link; 289 $this->links[0] = $link;
@@ -295,7 +309,7 @@ You use the community supported version of the original Shaarli project, by Seba
295 309
296 $toremove = array(); 310 $toremove = array();
297 foreach ($this->links as $key => &$link) { 311 foreach ($this->links as $key => &$link) {
298 if (! $this->loggedIn && $link['private'] != 0) { 312 if (!$this->loggedIn && $link['private'] != 0) {
299 // Transition for not upgraded databases. 313 // Transition for not upgraded databases.
300 unset($this->links[$key]); 314 unset($this->links[$key]);
301 continue; 315 continue;
@@ -305,7 +319,7 @@ You use the community supported version of the original Shaarli project, by Seba
305 sanitizeLink($link); 319 sanitizeLink($link);
306 320
307 // Remove private tags if the user is not logged in. 321 // Remove private tags if the user is not logged in.
308 if (! $this->loggedIn) { 322 if (!$this->loggedIn) {
309 $link['tags'] = preg_replace('/(^|\s+)\.[^($|\s)]+\s*/', ' ', $link['tags']); 323 $link['tags'] = preg_replace('/(^|\s+)\.[^($|\s)]+\s*/', ' ', $link['tags']);
310 } 324 }
311 325
@@ -317,16 +331,15 @@ You use the community supported version of the original Shaarli project, by Seba
317 } else { 331 } else {
318 $link['real_url'] .= $link['url']; 332 $link['real_url'] .= $link['url'];
319 } 333 }
320 } 334 } else {
321 else {
322 $link['real_url'] = $link['url']; 335 $link['real_url'] = $link['url'];
323 } 336 }
324 337
325 // To be able to load links before running the update, and prepare the update 338 // To be able to load links before running the update, and prepare the update
326 if (! isset($link['created'])) { 339 if (!isset($link['created'])) {
327 $link['id'] = $link['linkdate']; 340 $link['id'] = $link['linkdate'];
328 $link['created'] = DateTime::createFromFormat(self::LINK_DATE_FORMAT, $link['linkdate']); 341 $link['created'] = DateTime::createFromFormat(self::LINK_DATE_FORMAT, $link['linkdate']);
329 if (! empty($link['updated'])) { 342 if (!empty($link['updated'])) {
330 $link['updated'] = DateTime::createFromFormat(self::LINK_DATE_FORMAT, $link['updated']); 343 $link['updated'] = DateTime::createFromFormat(self::LINK_DATE_FORMAT, $link['updated']);
331 } 344 }
332 $link['shorturl'] = smallHash($link['linkdate']); 345 $link['shorturl'] = smallHash($link['linkdate']);
@@ -403,7 +416,8 @@ You use the community supported version of the original Shaarli project, by Seba
403 * 416 *
404 * @return array list of shaare found. 417 * @return array list of shaare found.
405 */ 418 */
406 public function filterDay($request) { 419 public function filterDay($request)
420 {
407 $linkFilter = new LinkFilter($this->links); 421 $linkFilter = new LinkFilter($this->links);
408 return $linkFilter->filter(LinkFilter::$FILTER_DAY, $request); 422 return $linkFilter->filter(LinkFilter::$FILTER_DAY, $request);
409 } 423 }
@@ -411,17 +425,22 @@ You use the community supported version of the original Shaarli project, by Seba
411 /** 425 /**
412 * Filter links according to search parameters. 426 * Filter links according to search parameters.
413 * 427 *
414 * @param array $filterRequest Search request content. Supported keys: 428 * @param array $filterRequest Search request content. Supported keys:
415 * - searchtags: list of tags 429 * - searchtags: list of tags
416 * - searchterm: term search 430 * - searchterm: term search
417 * @param bool $casesensitive Optional: Perform case sensitive filter 431 * @param bool $casesensitive Optional: Perform case sensitive filter
418 * @param string $visibility return only all/private/public links 432 * @param string $visibility return only all/private/public links
419 * @param string $untaggedonly return only untagged links 433 * @param bool $untaggedonly return only untagged links
420 * 434 *
421 * @return array filtered links, all links if no suitable filter was provided. 435 * @return array filtered links, all links if no suitable filter was provided.
422 */ 436 */
423 public function filterSearch($filterRequest = array(), $casesensitive = false, $visibility = 'all', $untaggedonly = false) 437 public function filterSearch(
424 { 438 $filterRequest = array(),
439 $casesensitive = false,
440 $visibility = 'all',
441 $untaggedonly = false
442 ) {
443
425 // Filter link database according to parameters. 444 // Filter link database according to parameters.
426 $searchtags = isset($filterRequest['searchtags']) ? escape($filterRequest['searchtags']) : ''; 445 $searchtags = isset($filterRequest['searchtags']) ? escape($filterRequest['searchtags']) : '';
427 $searchterm = isset($filterRequest['searchterm']) ? escape($filterRequest['searchterm']) : ''; 446 $searchterm = isset($filterRequest['searchterm']) ? escape($filterRequest['searchterm']) : '';
@@ -437,8 +456,8 @@ You use the community supported version of the original Shaarli project, by Seba
437 /** 456 /**
438 * Returns the list tags appearing in the links with the given tags 457 * Returns the list tags appearing in the links with the given tags
439 * 458 *
440 * @param array $filteringTags tags selecting the links to consider 459 * @param array $filteringTags tags selecting the links to consider
441 * @param string $visibility process only all/private/public links 460 * @param string $visibility process only all/private/public links
442 * 461 *
443 * @return array tag => linksCount 462 * @return array tag => linksCount
444 */ 463 */
@@ -492,8 +511,7 @@ You use the community supported version of the original Shaarli project, by Seba
492 $delete = empty($to); 511 $delete = empty($to);
493 // True for case-sensitive tag search. 512 // True for case-sensitive tag search.
494 $linksToAlter = $this->filterSearch(['searchtags' => $from], true); 513 $linksToAlter = $this->filterSearch(['searchtags' => $from], true);
495 foreach($linksToAlter as $key => &$value) 514 foreach ($linksToAlter as $key => &$value) {
496 {
497 $tags = preg_split('/\s+/', trim($value['tags'])); 515 $tags = preg_split('/\s+/', trim($value['tags']));
498 if (($pos = array_search($from, $tags)) !== false) { 516 if (($pos = array_search($from, $tags)) !== false) {
499 if ($delete) { 517 if ($delete) {
@@ -536,7 +554,10 @@ You use the community supported version of the original Shaarli project, by Seba
536 { 554 {
537 $order = $order === 'ASC' ? -1 : 1; 555 $order = $order === 'ASC' ? -1 : 1;
538 // Reorder array by dates. 556 // Reorder array by dates.
539 usort($this->links, function($a, $b) use ($order) { 557 usort($this->links, function ($a, $b) use ($order) {
558 if (isset($a['sticky']) && isset($b['sticky']) && $a['sticky'] !== $b['sticky']) {
559 return $a['sticky'] ? -1 : 1;
560 }
540 return $a['created'] < $b['created'] ? 1 * $order : -1 * $order; 561 return $a['created'] < $b['created'] ? 1 * $order : -1 * $order;
541 }); 562 });
542 563
diff --git a/application/LinkFilter.php b/application/bookmark/LinkFilter.php
index e52239b8..9b966307 100644
--- a/application/LinkFilter.php
+++ b/application/bookmark/LinkFilter.php
@@ -1,5 +1,10 @@
1<?php 1<?php
2 2
3namespace Shaarli\Bookmark;
4
5use Exception;
6use Shaarli\Bookmark\Exception\LinkNotFoundException;
7
3/** 8/**
4 * Class LinkFilter. 9 * Class LinkFilter.
5 * 10 *
@@ -10,22 +15,22 @@ class LinkFilter
10 /** 15 /**
11 * @var string permalinks. 16 * @var string permalinks.
12 */ 17 */
13 public static $FILTER_HASH = 'permalink'; 18 public static $FILTER_HASH = 'permalink';
14 19
15 /** 20 /**
16 * @var string text search. 21 * @var string text search.
17 */ 22 */
18 public static $FILTER_TEXT = 'fulltext'; 23 public static $FILTER_TEXT = 'fulltext';
19 24
20 /** 25 /**
21 * @var string tag filter. 26 * @var string tag filter.
22 */ 27 */
23 public static $FILTER_TAG = 'tags'; 28 public static $FILTER_TAG = 'tags';
24 29
25 /** 30 /**
26 * @var string filter by day. 31 * @var string filter by day.
27 */ 32 */
28 public static $FILTER_DAY = 'FILTER_DAY'; 33 public static $FILTER_DAY = 'FILTER_DAY';
29 34
30 /** 35 /**
31 * @var string Allowed characters for hashtags (regex syntax). 36 * @var string Allowed characters for hashtags (regex syntax).
@@ -58,11 +63,11 @@ class LinkFilter
58 */ 63 */
59 public function filter($type, $request, $casesensitive = false, $visibility = 'all', $untaggedonly = false) 64 public function filter($type, $request, $casesensitive = false, $visibility = 'all', $untaggedonly = false)
60 { 65 {
61 if (! in_array($visibility, ['all', 'public', 'private'])) { 66 if (!in_array($visibility, ['all', 'public', 'private'])) {
62 $visibility = 'all'; 67 $visibility = 'all';
63 } 68 }
64 69
65 switch($type) { 70 switch ($type) {
66 case self::$FILTER_HASH: 71 case self::$FILTER_HASH:
67 return $this->filterSmallHash($request); 72 return $this->filterSmallHash($request);
68 case self::$FILTER_TAG | self::$FILTER_TEXT: // == "vuotext" 73 case self::$FILTER_TAG | self::$FILTER_TEXT: // == "vuotext"
@@ -117,7 +122,7 @@ class LinkFilter
117 foreach ($this->links as $key => $value) { 122 foreach ($this->links as $key => $value) {
118 if ($value['private'] && $visibility === 'private') { 123 if ($value['private'] && $visibility === 'private') {
119 $out[$key] = $value; 124 $out[$key] = $value;
120 } elseif (! $value['private'] && $visibility === 'public') { 125 } elseif (!$value['private'] && $visibility === 'public') {
121 $out[$key] = $value; 126 $out[$key] = $value;
122 } 127 }
123 } 128 }
@@ -132,7 +137,7 @@ class LinkFilter
132 * 137 *
133 * @return array $filtered array containing permalink data. 138 * @return array $filtered array containing permalink data.
134 * 139 *
135 * @throws LinkNotFoundException if the smallhash doesn't match any link. 140 * @throws \Shaarli\Bookmark\Exception\LinkNotFoundException if the smallhash doesn't match any link.
136 */ 141 */
137 private function filterSmallHash($smallHash) 142 private function filterSmallHash($smallHash)
138 { 143 {
@@ -169,7 +174,7 @@ class LinkFilter
169 * - see https://github.com/shaarli/Shaarli/issues/75 for examples 174 * - see https://github.com/shaarli/Shaarli/issues/75 for examples
170 * 175 *
171 * @param string $searchterms search query. 176 * @param string $searchterms search query.
172 * @param string $visibility Optional: return only all/private/public links. 177 * @param string $visibility Optional: return only all/private/public links.
173 * 178 *
174 * @return array search results. 179 * @return array search results.
175 */ 180 */
@@ -205,10 +210,9 @@ class LinkFilter
205 210
206 // Iterate over every stored link. 211 // Iterate over every stored link.
207 foreach ($this->links as $id => $link) { 212 foreach ($this->links as $id => $link) {
208
209 // ignore non private links when 'privatonly' is on. 213 // ignore non private links when 'privatonly' is on.
210 if ($visibility !== 'all') { 214 if ($visibility !== 'all') {
211 if (! $link['private'] && $visibility === 'private') { 215 if (!$link['private'] && $visibility === 'private') {
212 continue; 216 continue;
213 } elseif ($link['private'] && $visibility === 'public') { 217 } elseif ($link['private'] && $visibility === 'public') {
214 continue; 218 continue;
@@ -251,17 +255,19 @@ class LinkFilter
251 255
252 /** 256 /**
253 * generate a regex fragment out of a tag 257 * generate a regex fragment out of a tag
258 *
254 * @param string $tag to to generate regexs from. may start with '-' to negate, contain '*' as wildcard 259 * @param string $tag to to generate regexs from. may start with '-' to negate, contain '*' as wildcard
260 *
255 * @return string generated regex fragment 261 * @return string generated regex fragment
256 */ 262 */
257 private static function tag2regex($tag) 263 private static function tag2regex($tag)
258 { 264 {
259 $len = strlen($tag); 265 $len = strlen($tag);
260 if(!$len || $tag === "-" || $tag === "*"){ 266 if (!$len || $tag === "-" || $tag === "*") {
261 // nothing to search, return empty regex 267 // nothing to search, return empty regex
262 return ''; 268 return '';
263 } 269 }
264 if($tag[0] === "-") { 270 if ($tag[0] === "-") {
265 // query is negated 271 // query is negated
266 $i = 1; // use offset to start after '-' character 272 $i = 1; // use offset to start after '-' character
267 $regex = '(?!'; // create negative lookahead 273 $regex = '(?!'; // create negative lookahead
@@ -271,14 +277,14 @@ class LinkFilter
271 } 277 }
272 $regex .= '.*(?:^| )'; // before tag may only be a space or the beginning 278 $regex .= '.*(?:^| )'; // before tag may only be a space or the beginning
273 // iterate over string, separating it into placeholder and content 279 // iterate over string, separating it into placeholder and content
274 for(; $i < $len; $i++){ 280 for (; $i < $len; $i++) {
275 if($tag[$i] === '*'){ 281 if ($tag[$i] === '*') {
276 // placeholder found 282 // placeholder found
277 $regex .= '[^ ]*?'; 283 $regex .= '[^ ]*?';
278 } else { 284 } else {
279 // regular characters 285 // regular characters
280 $offset = strpos($tag, '*', $i); 286 $offset = strpos($tag, '*', $i);
281 if($offset === false){ 287 if ($offset === false) {
282 // no placeholder found, set offset to end of string 288 // no placeholder found, set offset to end of string
283 $offset = $len; 289 $offset = $len;
284 } 290 }
@@ -310,19 +316,19 @@ class LinkFilter
310 { 316 {
311 // get single tags (we may get passed an array, even though the docs say different) 317 // get single tags (we may get passed an array, even though the docs say different)
312 $inputTags = $tags; 318 $inputTags = $tags;
313 if(!is_array($tags)) { 319 if (!is_array($tags)) {
314 // we got an input string, split tags 320 // we got an input string, split tags
315 $inputTags = preg_split('/(?:\s+)|,/', $inputTags, -1, PREG_SPLIT_NO_EMPTY); 321 $inputTags = preg_split('/(?:\s+)|,/', $inputTags, -1, PREG_SPLIT_NO_EMPTY);
316 } 322 }
317 323
318 if(!count($inputTags)){ 324 if (!count($inputTags)) {
319 // no input tags 325 // no input tags
320 return $this->noFilter($visibility); 326 return $this->noFilter($visibility);
321 } 327 }
322 328
323 // build regex from all tags 329 // build regex from all tags
324 $re = '/^' . implode(array_map("self::tag2regex", $inputTags)) . '.*$/'; 330 $re = '/^' . implode(array_map("self::tag2regex", $inputTags)) . '.*$/';
325 if(!$casesensitive) { 331 if (!$casesensitive) {
326 // make regex case insensitive 332 // make regex case insensitive
327 $re .= 'i'; 333 $re .= 'i';
328 } 334 }
@@ -335,14 +341,14 @@ class LinkFilter
335 // check level of visibility 341 // check level of visibility
336 // ignore non private links when 'privateonly' is on. 342 // ignore non private links when 'privateonly' is on.
337 if ($visibility !== 'all') { 343 if ($visibility !== 'all') {
338 if (! $link['private'] && $visibility === 'private') { 344 if (!$link['private'] && $visibility === 'private') {
339 continue; 345 continue;
340 } elseif ($link['private'] && $visibility === 'public') { 346 } elseif ($link['private'] && $visibility === 'public') {
341 continue; 347 continue;
342 } 348 }
343 } 349 }
344 $search = $link['tags']; // build search string, start with tags of current link 350 $search = $link['tags']; // build search string, start with tags of current link
345 if(strlen(trim($link['description'])) && strpos($link['description'], '#') !== false){ 351 if (strlen(trim($link['description'])) && strpos($link['description'], '#') !== false) {
346 // description given and at least one possible tag found 352 // description given and at least one possible tag found
347 $descTags = array(); 353 $descTags = array();
348 // find all tags in the form of #tag in the description 354 // find all tags in the form of #tag in the description
@@ -351,13 +357,13 @@ class LinkFilter
351 $link['description'], 357 $link['description'],
352 $descTags 358 $descTags
353 ); 359 );
354 if(count($descTags[1])){ 360 if (count($descTags[1])) {
355 // there were some tags in the description, add them to the search string 361 // there were some tags in the description, add them to the search string
356 $search .= ' ' . implode(' ', $descTags[1]); 362 $search .= ' ' . implode(' ', $descTags[1]);
357 } 363 }
358 }; 364 };
359 // match regular expression with search string 365 // match regular expression with search string
360 if(!preg_match($re, $search)){ 366 if (!preg_match($re, $search)) {
361 // this entry does _not_ match our regex 367 // this entry does _not_ match our regex
362 continue; 368 continue;
363 } 369 }
@@ -378,7 +384,7 @@ class LinkFilter
378 $filtered = []; 384 $filtered = [];
379 foreach ($this->links as $key => $link) { 385 foreach ($this->links as $key => $link) {
380 if ($visibility !== 'all') { 386 if ($visibility !== 'all') {
381 if (! $link['private'] && $visibility === 'private') { 387 if (!$link['private'] && $visibility === 'private') {
382 continue; 388 continue;
383 } elseif ($link['private'] && $visibility === 'public') { 389 } elseif ($link['private'] && $visibility === 'public') {
384 continue; 390 continue;
@@ -407,7 +413,7 @@ class LinkFilter
407 */ 413 */
408 public function filterDay($day) 414 public function filterDay($day)
409 { 415 {
410 if (! checkDateFormat('Ymd', $day)) { 416 if (!checkDateFormat('Ymd', $day)) {
411 throw new Exception('Invalid date format'); 417 throw new Exception('Invalid date format');
412 } 418 }
413 419
@@ -441,14 +447,3 @@ class LinkFilter
441 return preg_split('/\s+/', $tagsOut, -1, PREG_SPLIT_NO_EMPTY); 447 return preg_split('/\s+/', $tagsOut, -1, PREG_SPLIT_NO_EMPTY);
442 } 448 }
443} 449}
444
445class LinkNotFoundException extends Exception
446{
447 /**
448 * LinkNotFoundException constructor.
449 */
450 public function __construct()
451 {
452 $this->message = t('The link you are trying to reach does not exist or has been deleted.');
453 }
454}
diff --git a/application/LinkUtils.php b/application/bookmark/LinkUtils.php
index 4df5c0ca..de5b61cb 100644
--- a/application/LinkUtils.php
+++ b/application/bookmark/LinkUtils.php
@@ -1,11 +1,13 @@
1<?php 1<?php
2 2
3use Shaarli\Bookmark\LinkDB;
4
3/** 5/**
4 * Get cURL callback function for CURLOPT_WRITEFUNCTION 6 * Get cURL callback function for CURLOPT_WRITEFUNCTION
5 * 7 *
6 * @param string $charset to extract from the downloaded page (reference) 8 * @param string $charset to extract from the downloaded page (reference)
7 * @param string $title to extract from the downloaded page (reference) 9 * @param string $title to extract from the downloaded page (reference)
8 * @param string $curlGetInfo Optionnaly overrides curl_getinfo function 10 * @param string $curlGetInfo Optionally overrides curl_getinfo function
9 * 11 *
10 * @return Closure 12 * @return Closure
11 */ 13 */
@@ -23,7 +25,7 @@ function get_curl_download_callback(&$charset, &$title, $curlGetInfo = 'curl_get
23 * 25 *
24 * @return int|bool length of $data or false if we need to stop the download 26 * @return int|bool length of $data or false if we need to stop the download
25 */ 27 */
26 return function(&$ch, $data) use ($curlGetInfo, &$charset, &$title, &$isRedirected) { 28 return function (&$ch, $data) use ($curlGetInfo, &$charset, &$title, &$isRedirected) {
27 $responseCode = $curlGetInfo($ch, CURLINFO_RESPONSE_CODE); 29 $responseCode = $curlGetInfo($ch, CURLINFO_RESPONSE_CODE);
28 if (!empty($responseCode) && in_array($responseCode, [301, 302])) { 30 if (!empty($responseCode) && in_array($responseCode, [301, 302])) {
29 $isRedirected = true; 31 $isRedirected = true;
@@ -196,12 +198,13 @@ function space2nbsp($text)
196 * 198 *
197 * @param string $description shaare's description. 199 * @param string $description shaare's description.
198 * @param string $redirector if a redirector is set, use it to gerenate links. 200 * @param string $redirector if a redirector is set, use it to gerenate links.
199 * @param bool $urlEncode Use `urlencode()` on the URL after the redirector or not. 201 * @param bool $urlEncode Use `urlencode()` on the URL after the redirector or not.
200 * @param string $indexUrl URL to Shaarli's index. 202 * @param string $indexUrl URL to Shaarli's index.
201 203
202 * @return string formatted description. 204 * @return string formatted description.
203 */ 205 */
204function format_description($description, $redirector = '', $urlEncode = true, $indexUrl = '') { 206function format_description($description, $redirector = '', $urlEncode = true, $indexUrl = '')
207{
205 return nl2br(space2nbsp(hashtag_autolink(text2clickable($description, $redirector, $urlEncode), $indexUrl))); 208 return nl2br(space2nbsp(hashtag_autolink(text2clickable($description, $redirector, $urlEncode), $indexUrl)));
206} 209}
207 210
diff --git a/application/bookmark/exception/LinkNotFoundException.php b/application/bookmark/exception/LinkNotFoundException.php
new file mode 100644
index 00000000..f9414428
--- /dev/null
+++ b/application/bookmark/exception/LinkNotFoundException.php
@@ -0,0 +1,15 @@
1<?php
2namespace Shaarli\Bookmark\Exception;
3
4use Exception;
5
6class LinkNotFoundException extends Exception
7{
8 /**
9 * LinkNotFoundException constructor.
10 */
11 public function __construct()
12 {
13 $this->message = t('The link you are trying to reach does not exist or has been deleted.');
14 }
15}
diff --git a/application/config/ConfigJson.php b/application/config/ConfigJson.php
index 8c8d5610..4509357c 100644
--- a/application/config/ConfigJson.php
+++ b/application/config/ConfigJson.php
@@ -47,7 +47,7 @@ class ConfigJson implements ConfigIO
47 $print = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0; 47 $print = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0;
48 $data = self::getPhpHeaders() . json_encode($conf, $print) . self::getPhpSuffix(); 48 $data = self::getPhpHeaders() . json_encode($conf, $print) . self::getPhpSuffix();
49 if (!file_put_contents($filepath, $data)) { 49 if (!file_put_contents($filepath, $data)) {
50 throw new \IOException( 50 throw new \Shaarli\Exceptions\IOException(
51 $filepath, 51 $filepath,
52 t('Shaarli could not create the config file. '. 52 t('Shaarli could not create the config file. '.
53 'Please make sure Shaarli has the right to write in the folder is it installed in.') 53 'Please make sure Shaarli has the right to write in the folder is it installed in.')
diff --git a/application/config/ConfigManager.php b/application/config/ConfigManager.php
index 82f4a368..e6c35073 100644
--- a/application/config/ConfigManager.php
+++ b/application/config/ConfigManager.php
@@ -148,6 +148,33 @@ class ConfigManager
148 } 148 }
149 149
150 /** 150 /**
151 * Remove a config element from the config file.
152 *
153 * @param string $setting Asked setting, keys separated with dots.
154 * @param bool $write Write the new setting in the config file, default false.
155 * @param bool $isLoggedIn User login state, default false.
156 *
157 * @throws \Exception Invalid
158 */
159 public function remove($setting, $write = false, $isLoggedIn = false)
160 {
161 if (empty($setting) || ! is_string($setting)) {
162 throw new \Exception(t('Invalid setting key parameter. String expected, got: '). gettype($setting));
163 }
164
165 // During the ConfigIO transition, map legacy settings to the new ones.
166 if ($this->configIO instanceof ConfigPhp && isset(ConfigPhp::$LEGACY_KEYS_MAPPING[$setting])) {
167 $setting = ConfigPhp::$LEGACY_KEYS_MAPPING[$setting];
168 }
169
170 $settings = explode('.', $setting);
171 self::removeConfig($settings, $this->loadedConfig);
172 if ($write) {
173 $this->write($isLoggedIn);
174 }
175 }
176
177 /**
151 * Check if a settings exists. 178 * Check if a settings exists.
152 * 179 *
153 * Supports nested settings with dot separated keys. 180 * Supports nested settings with dot separated keys.
@@ -180,7 +207,7 @@ class ConfigManager
180 * 207 *
181 * @throws MissingFieldConfigException: a mandatory field has not been provided in $conf. 208 * @throws MissingFieldConfigException: a mandatory field has not been provided in $conf.
182 * @throws UnauthorizedConfigException: user is not authorize to change configuration. 209 * @throws UnauthorizedConfigException: user is not authorize to change configuration.
183 * @throws \IOException: an error occurred while writing the new config file. 210 * @throws \Shaarli\Exceptions\IOException: an error occurred while writing the new config file.
184 */ 211 */
185 public function write($isLoggedIn) 212 public function write($isLoggedIn)
186 { 213 {
@@ -272,7 +299,7 @@ class ConfigManager
272 * 299 *
273 * @param array $settings Ordered array which contains keys to find. 300 * @param array $settings Ordered array which contains keys to find.
274 * @param mixed $value 301 * @param mixed $value
275 * @param array $conf Loaded settings, then sub-array. 302 * @param array $conf Loaded settings, then sub-array.
276 * 303 *
277 * @return mixed Found setting or NOT_FOUND flag. 304 * @return mixed Found setting or NOT_FOUND flag.
278 */ 305 */
@@ -290,6 +317,27 @@ class ConfigManager
290 } 317 }
291 318
292 /** 319 /**
320 * Recursive function which find asked setting in the loaded config and deletes it.
321 *
322 * @param array $settings Ordered array which contains keys to find.
323 * @param array $conf Loaded settings, then sub-array.
324 *
325 * @return mixed Found setting or NOT_FOUND flag.
326 */
327 protected static function removeConfig($settings, &$conf)
328 {
329 if (!is_array($settings) || count($settings) == 0) {
330 return self::$NOT_FOUND;
331 }
332
333 $setting = array_shift($settings);
334 if (count($settings) > 0) {
335 return self::removeConfig($settings, $conf[$setting]);
336 }
337 unset($conf[$setting]);
338 }
339
340 /**
293 * Set a bunch of default values allowing Shaarli to start without a config file. 341 * Set a bunch of default values allowing Shaarli to start without a config file.
294 */ 342 */
295 protected function setDefaultValues() 343 protected function setDefaultValues()
@@ -333,12 +381,12 @@ class ConfigManager
333 // default state of the 'remember me' checkbox of the login form 381 // default state of the 'remember me' checkbox of the login form
334 $this->setEmpty('privacy.remember_user_default', true); 382 $this->setEmpty('privacy.remember_user_default', true);
335 383
336 $this->setEmpty('thumbnail.enable_thumbnails', true);
337 $this->setEmpty('thumbnail.enable_localcache', true);
338
339 $this->setEmpty('redirector.url', ''); 384 $this->setEmpty('redirector.url', '');
340 $this->setEmpty('redirector.encode_url', true); 385 $this->setEmpty('redirector.encode_url', true);
341 386
387 $this->setEmpty('thumbnails.width', '125');
388 $this->setEmpty('thumbnails.height', '90');
389
342 $this->setEmpty('translation.language', 'auto'); 390 $this->setEmpty('translation.language', 'auto');
343 $this->setEmpty('translation.mode', 'php'); 391 $this->setEmpty('translation.mode', 'php');
344 $this->setEmpty('translation.extensions', []); 392 $this->setEmpty('translation.extensions', []);
diff --git a/application/config/ConfigPhp.php b/application/config/ConfigPhp.php
index 8add8bcd..cad34594 100644
--- a/application/config/ConfigPhp.php
+++ b/application/config/ConfigPhp.php
@@ -27,7 +27,7 @@ class ConfigPhp implements ConfigIO
27 /** 27 /**
28 * Map legacy config keys with the new ones. 28 * Map legacy config keys with the new ones.
29 * If ConfigPhp is used, getting <newkey> will actually look for <legacykey>. 29 * If ConfigPhp is used, getting <newkey> will actually look for <legacykey>.
30 * The Updater will use this array to transform keys when switching to JSON. 30 * The updater will use this array to transform keys when switching to JSON.
31 * 31 *
32 * @var array current key => legacy key. 32 * @var array current key => legacy key.
33 */ 33 */
@@ -104,19 +104,27 @@ class ConfigPhp implements ConfigIO
104 104
105 // Store all $conf['config'] 105 // Store all $conf['config']
106 foreach ($conf['config'] as $key => $value) { 106 foreach ($conf['config'] as $key => $value) {
107 $configStr .= '$GLOBALS[\'config\'][\''. $key .'\'] = '.var_export($conf['config'][$key], true).';'. PHP_EOL; 107 $configStr .= '$GLOBALS[\'config\'][\''
108 . $key
109 .'\'] = '
110 .var_export($conf['config'][$key], true).';'
111 . PHP_EOL;
108 } 112 }
109 113
110 if (isset($conf['plugins'])) { 114 if (isset($conf['plugins'])) {
111 foreach ($conf['plugins'] as $key => $value) { 115 foreach ($conf['plugins'] as $key => $value) {
112 $configStr .= '$GLOBALS[\'plugins\'][\''. $key .'\'] = '.var_export($conf['plugins'][$key], true).';'. PHP_EOL; 116 $configStr .= '$GLOBALS[\'plugins\'][\''
117 . $key
118 .'\'] = '
119 .var_export($conf['plugins'][$key], true).';'
120 . PHP_EOL;
113 } 121 }
114 } 122 }
115 123
116 if (!file_put_contents($filepath, $configStr) 124 if (!file_put_contents($filepath, $configStr)
117 || strcmp(file_get_contents($filepath), $configStr) != 0 125 || strcmp(file_get_contents($filepath), $configStr) != 0
118 ) { 126 ) {
119 throw new \IOException( 127 throw new \Shaarli\Exceptions\IOException(
120 $filepath, 128 $filepath,
121 t('Shaarli could not create the config file. '. 129 t('Shaarli could not create the config file. '.
122 'Please make sure Shaarli has the right to write in the folder is it installed in.') 130 'Please make sure Shaarli has the right to write in the folder is it installed in.')
diff --git a/application/config/ConfigPlugin.php b/application/config/ConfigPlugin.php
index b3d9752b..dbb24937 100644
--- a/application/config/ConfigPlugin.php
+++ b/application/config/ConfigPlugin.php
@@ -34,8 +34,7 @@ function save_plugin_config($formData)
34 // If there is no order, it means a disabled plugin has been enabled. 34 // If there is no order, it means a disabled plugin has been enabled.
35 if (isset($formData['order_' . $key])) { 35 if (isset($formData['order_' . $key])) {
36 $plugins[(int) $formData['order_' . $key]] = $key; 36 $plugins[(int) $formData['order_' . $key]] = $key;
37 } 37 } else {
38 else {
39 $newEnabledPlugins[] = $key; 38 $newEnabledPlugins[] = $key;
40 } 39 }
41 } 40 }
diff --git a/application/exceptions/IOException.php b/application/exceptions/IOException.php
index 18e46b77..2aa25e5c 100644
--- a/application/exceptions/IOException.php
+++ b/application/exceptions/IOException.php
@@ -1,4 +1,7 @@
1<?php 1<?php
2namespace Shaarli\Exceptions;
3
4use Exception;
2 5
3/** 6/**
4 * Exception class thrown when a filesystem access failure happens 7 * Exception class thrown when a filesystem access failure happens
@@ -17,6 +20,6 @@ class IOException extends Exception
17 { 20 {
18 $this->path = $path; 21 $this->path = $path;
19 $this->message = empty($message) ? t('Error accessing') : $message; 22 $this->message = empty($message) ? t('Error accessing') : $message;
20 $this->message .= ' "' . $this->path .'"'; 23 $this->message .= ' "' . $this->path . '"';
21 } 24 }
22} 25}
diff --git a/application/Cache.php b/application/feed/Cache.php
index e5d43e61..e5d43e61 100644
--- a/application/Cache.php
+++ b/application/feed/Cache.php
diff --git a/application/CachedPage.php b/application/feed/CachedPage.php
index e11cc52d..d809bdd9 100644
--- a/application/CachedPage.php
+++ b/application/feed/CachedPage.php
@@ -1,4 +1,7 @@
1<?php 1<?php
2
3namespace Shaarli\Feed;
4
2/** 5/**
3 * Simple cache system, mainly for the RSS/ATOM feeds 6 * Simple cache system, mainly for the RSS/ATOM feeds
4 */ 7 */
@@ -24,7 +27,7 @@ class CachedPage
24 { 27 {
25 // TODO: check write access to the cache directory 28 // TODO: check write access to the cache directory
26 $this->cacheDir = $cacheDir; 29 $this->cacheDir = $cacheDir;
27 $this->filename = $this->cacheDir.'/'.sha1($url).'.cache'; 30 $this->filename = $this->cacheDir . '/' . sha1($url) . '.cache';
28 $this->shouldBeCached = $shouldBeCached; 31 $this->shouldBeCached = $shouldBeCached;
29 } 32 }
30 33
diff --git a/application/FeedBuilder.php b/application/feed/FeedBuilder.php
index ebae18b4..b66f2f91 100644
--- a/application/FeedBuilder.php
+++ b/application/feed/FeedBuilder.php
@@ -1,4 +1,7 @@
1<?php 1<?php
2namespace Shaarli\Feed;
3
4use DateTime;
2 5
3/** 6/**
4 * FeedBuilder class. 7 * FeedBuilder class.
@@ -28,7 +31,7 @@ class FeedBuilder
28 public static $DEFAULT_NB_LINKS = 50; 31 public static $DEFAULT_NB_LINKS = 50;
29 32
30 /** 33 /**
31 * @var LinkDB instance. 34 * @var \Shaarli\Bookmark\LinkDB instance.
32 */ 35 */
33 protected $linkDB; 36 protected $linkDB;
34 37
@@ -38,12 +41,12 @@ class FeedBuilder
38 protected $feedType; 41 protected $feedType;
39 42
40 /** 43 /**
41 * @var array $_SERVER. 44 * @var array $_SERVER
42 */ 45 */
43 protected $serverInfo; 46 protected $serverInfo;
44 47
45 /** 48 /**
46 * @var array $_GET. 49 * @var array $_GET
47 */ 50 */
48 protected $userInput; 51 protected $userInput;
49 52
@@ -75,11 +78,12 @@ class FeedBuilder
75 /** 78 /**
76 * Feed constructor. 79 * Feed constructor.
77 * 80 *
78 * @param LinkDB $linkDB LinkDB instance. 81 * @param \Shaarli\Bookmark\LinkDB $linkDB LinkDB instance.
79 * @param string $feedType Type of feed. 82 * @param string $feedType Type of feed.
80 * @param array $serverInfo $_SERVER. 83 * @param array $serverInfo $_SERVER.
81 * @param array $userInput $_GET. 84 * @param array $userInput $_GET.
82 * @param boolean $isLoggedIn True if the user is currently logged in, false otherwise. 85 * @param boolean $isLoggedIn True if the user is currently logged in,
86 * false otherwise.
83 */ 87 */
84 public function __construct($linkDB, $feedType, $serverInfo, $userInput, $isLoggedIn) 88 public function __construct($linkDB, $feedType, $serverInfo, $userInput, $isLoggedIn)
85 { 89 {
@@ -124,7 +128,7 @@ class FeedBuilder
124 $data['show_dates'] = !$this->hideDates || $this->isLoggedIn; 128 $data['show_dates'] = !$this->hideDates || $this->isLoggedIn;
125 // Remove leading slash from REQUEST_URI. 129 // Remove leading slash from REQUEST_URI.
126 $data['self_link'] = escape(server_url($this->serverInfo)) 130 $data['self_link'] = escape(server_url($this->serverInfo))
127 . escape($this->serverInfo['REQUEST_URI']); 131 . escape($this->serverInfo['REQUEST_URI']);
128 $data['index_url'] = $pageaddr; 132 $data['index_url'] = $pageaddr;
129 $data['usepermalinks'] = $this->usePermalinks === true; 133 $data['usepermalinks'] = $this->usePermalinks === true;
130 $data['links'] = $linkDisplayed; 134 $data['links'] = $linkDisplayed;
@@ -142,18 +146,18 @@ class FeedBuilder
142 */ 146 */
143 protected function buildItem($link, $pageaddr) 147 protected function buildItem($link, $pageaddr)
144 { 148 {
145 $link['guid'] = $pageaddr .'?'. $link['shorturl']; 149 $link['guid'] = $pageaddr . '?' . $link['shorturl'];
146 // Check for both signs of a note: starting with ? and 7 chars long. 150 // Check for both signs of a note: starting with ? and 7 chars long.
147 if ($link['url'][0] === '?' && strlen($link['url']) === 7) { 151 if ($link['url'][0] === '?' && strlen($link['url']) === 7) {
148 $link['url'] = $pageaddr . $link['url']; 152 $link['url'] = $pageaddr . $link['url'];
149 } 153 }
150 if ($this->usePermalinks === true) { 154 if ($this->usePermalinks === true) {
151 $permalink = '<a href="'. $link['url'] .'" title="'. t('Direct link') .'">'. t('Direct link') .'</a>'; 155 $permalink = '<a href="' . $link['url'] . '" title="' . t('Direct link') . '">' . t('Direct link') . '</a>';
152 } else { 156 } else {
153 $permalink = '<a href="'. $link['guid'] .'" title="'. t('Permalink') .'">'. t('Permalink') .'</a>'; 157 $permalink = '<a href="' . $link['guid'] . '" title="' . t('Permalink') . '">' . t('Permalink') . '</a>';
154 } 158 }
155 $link['description'] = format_description($link['description'], '', false, $pageaddr); 159 $link['description'] = format_description($link['description'], '', false, $pageaddr);
156 $link['description'] .= PHP_EOL .'<br>&#8212; '. $permalink; 160 $link['description'] .= PHP_EOL . '<br>&#8212; ' . $permalink;
157 161
158 $pubDate = $link['created']; 162 $pubDate = $link['created'];
159 $link['pub_iso_date'] = $this->getIsoDate($pubDate); 163 $link['pub_iso_date'] = $this->getIsoDate($pubDate);
@@ -163,7 +167,7 @@ class FeedBuilder
163 $upDate = $link['updated']; 167 $upDate = $link['updated'];
164 $link['up_iso_date'] = $this->getIsoDate($upDate, DateTime::ATOM); 168 $link['up_iso_date'] = $this->getIsoDate($upDate, DateTime::ATOM);
165 } else { 169 } else {
166 $link['up_iso_date'] = $this->getIsoDate($pubDate, DateTime::ATOM);; 170 $link['up_iso_date'] = $this->getIsoDate($pubDate, DateTime::ATOM);
167 } 171 }
168 172
169 // Save the more recent item. 173 // Save the more recent item.
@@ -222,11 +226,11 @@ class FeedBuilder
222 public function getTypeLanguage() 226 public function getTypeLanguage()
223 { 227 {
224 // Use the locale do define the language, if available. 228 // Use the locale do define the language, if available.
225 if (! empty($this->locale) && preg_match('/^\w{2}[_\-]\w{2}/', $this->locale)) { 229 if (!empty($this->locale) && preg_match('/^\w{2}[_\-]\w{2}/', $this->locale)) {
226 $length = ($this->feedType == self::$FEED_RSS) ? 5 : 2; 230 $length = ($this->feedType === self::$FEED_RSS) ? 5 : 2;
227 return str_replace('_', '-', substr($this->locale, 0, $length)); 231 return str_replace('_', '-', substr($this->locale, 0, $length));
228 } 232 }
229 return ($this->feedType == self::$FEED_RSS) ? 'en-en' : 'en'; 233 return ($this->feedType === self::$FEED_RSS) ? 'en-en' : 'en';
230 } 234 }
231 235
232 /** 236 /**
@@ -261,7 +265,6 @@ class FeedBuilder
261 } 265 }
262 if ($this->feedType == self::$FEED_RSS) { 266 if ($this->feedType == self::$FEED_RSS) {
263 return $date->format(DateTime::RSS); 267 return $date->format(DateTime::RSS);
264
265 } 268 }
266 return $date->format(DateTime::ATOM); 269 return $date->format(DateTime::ATOM);
267 } 270 }
@@ -287,7 +290,7 @@ class FeedBuilder
287 } 290 }
288 291
289 $intNb = intval($this->userInput['nb']); 292 $intNb = intval($this->userInput['nb']);
290 if (! is_int($intNb) || $intNb == 0) { 293 if (!is_int($intNb) || $intNb == 0) {
291 return self::$DEFAULT_NB_LINKS; 294 return self::$DEFAULT_NB_LINKS;
292 } 295 }
293 296
diff --git a/application/Base64Url.php b/application/http/Base64Url.php
index 61590e43..33fa7c1f 100644
--- a/application/Base64Url.php
+++ b/application/http/Base64Url.php
@@ -1,7 +1,6 @@
1<?php 1<?php
2 2
3namespace Shaarli; 3namespace Shaarli\Http;
4
5 4
6/** 5/**
7 * URL-safe Base64 operations 6 * URL-safe Base64 operations
@@ -17,7 +16,8 @@ class Base64Url
17 * 16 *
18 * @return string Base64Url-encoded data 17 * @return string Base64Url-encoded data
19 */ 18 */
20 public static function encode($data) { 19 public static function encode($data)
20 {
21 return rtrim(strtr(base64_encode($data), '+/', '-_'), '='); 21 return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
22 } 22 }
23 23
@@ -28,7 +28,8 @@ class Base64Url
28 * 28 *
29 * @return string Decoded data 29 * @return string Decoded data
30 */ 30 */
31 public static function decode($data) { 31 public static function decode($data)
32 {
32 return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT)); 33 return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT));
33 } 34 }
34} 35}
diff --git a/application/HttpUtils.php b/application/http/HttpUtils.php
index e9282506..2ea9195d 100644
--- a/application/HttpUtils.php
+++ b/application/http/HttpUtils.php
@@ -1,4 +1,7 @@
1<?php 1<?php
2
3use Shaarli\Http\Url;
4
2/** 5/**
3 * GET an HTTP URL to retrieve its content 6 * GET an HTTP URL to retrieve its content
4 * Uses the cURL library or a fallback method 7 * Uses the cURL library or a fallback method
@@ -7,7 +10,8 @@
7 * @param int $timeout network timeout (in seconds) 10 * @param int $timeout network timeout (in seconds)
8 * @param int $maxBytes maximum downloaded bytes (default: 4 MiB) 11 * @param int $maxBytes maximum downloaded bytes (default: 4 MiB)
9 * @param callable|string $curlWriteFunction Optional callback called during the download (cURL CURLOPT_WRITEFUNCTION). 12 * @param callable|string $curlWriteFunction Optional callback called during the download (cURL CURLOPT_WRITEFUNCTION).
10 * Can be used to add download conditions on the headers (response code, content type, etc.). 13 * Can be used to add download conditions on the
14 * headers (response code, content type, etc.).
11 * 15 *
12 * @return array HTTP response headers, downloaded content 16 * @return array HTTP response headers, downloaded content
13 * 17 *
@@ -37,7 +41,7 @@ function get_http_response($url, $timeout = 30, $maxBytes = 4194304, $curlWriteF
37 $cleanUrl = $urlObj->idnToAscii(); 41 $cleanUrl = $urlObj->idnToAscii();
38 42
39 if (!filter_var($cleanUrl, FILTER_VALIDATE_URL) || !$urlObj->isHttp()) { 43 if (!filter_var($cleanUrl, FILTER_VALIDATE_URL) || !$urlObj->isHttp()) {
40 return array(array(0 => 'Invalid HTTP Url'), false); 44 return array(array(0 => 'Invalid HTTP UrlUtils'), false);
41 } 45 }
42 46
43 $userAgent = 47 $userAgent =
@@ -64,29 +68,30 @@ function get_http_response($url, $timeout = 30, $maxBytes = 4194304, $curlWriteF
64 } 68 }
65 69
66 // General cURL settings 70 // General cURL settings
67 curl_setopt($ch, CURLOPT_AUTOREFERER, true); 71 curl_setopt($ch, CURLOPT_AUTOREFERER, true);
68 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); 72 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
69 curl_setopt($ch, CURLOPT_HEADER, true); 73 curl_setopt($ch, CURLOPT_HEADER, true);
70 curl_setopt( 74 curl_setopt(
71 $ch, 75 $ch,
72 CURLOPT_HTTPHEADER, 76 CURLOPT_HTTPHEADER,
73 array('Accept-Language: ' . $acceptLanguage) 77 array('Accept-Language: ' . $acceptLanguage)
74 ); 78 );
75 curl_setopt($ch, CURLOPT_MAXREDIRS, $maxRedirs); 79 curl_setopt($ch, CURLOPT_MAXREDIRS, $maxRedirs);
76 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 80 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
77 curl_setopt($ch, CURLOPT_TIMEOUT, $timeout); 81 curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
78 curl_setopt($ch, CURLOPT_USERAGENT, $userAgent); 82 curl_setopt($ch, CURLOPT_USERAGENT, $userAgent);
79 83
80 if (is_callable($curlWriteFunction)) { 84 if (is_callable($curlWriteFunction)) {
81 curl_setopt($ch, CURLOPT_WRITEFUNCTION, $curlWriteFunction); 85 curl_setopt($ch, CURLOPT_WRITEFUNCTION, $curlWriteFunction);
82 } 86 }
83 87
84 // Max download size management 88 // Max download size management
85 curl_setopt($ch, CURLOPT_BUFFERSIZE, 1024*16); 89 curl_setopt($ch, CURLOPT_BUFFERSIZE, 1024*16);
86 curl_setopt($ch, CURLOPT_NOPROGRESS, false); 90 curl_setopt($ch, CURLOPT_NOPROGRESS, false);
87 curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, 91 curl_setopt(
88 function($arg0, $arg1, $arg2, $arg3, $arg4 = 0) use ($maxBytes) 92 $ch,
89 { 93 CURLOPT_PROGRESSFUNCTION,
94 function ($arg0, $arg1, $arg2, $arg3, $arg4 = 0) use ($maxBytes) {
90 if (version_compare(phpversion(), '5.5', '<')) { 95 if (version_compare(phpversion(), '5.5', '<')) {
91 // PHP version lower than 5.5 96 // PHP version lower than 5.5
92 // Callback has 4 arguments 97 // Callback has 4 arguments
@@ -232,7 +237,6 @@ function get_redirected_headers($url, $redirectionLimit = 3)
232 && !empty($headers) 237 && !empty($headers)
233 && (strpos($headers[0], '301') !== false || strpos($headers[0], '302') !== false) 238 && (strpos($headers[0], '301') !== false || strpos($headers[0], '302') !== false)
234 && !empty($headers['Location'])) { 239 && !empty($headers['Location'])) {
235
236 $redirection = is_array($headers['Location']) ? end($headers['Location']) : $headers['Location']; 240 $redirection = is_array($headers['Location']) ? end($headers['Location']) : $headers['Location'];
237 if ($redirection != $url) { 241 if ($redirection != $url) {
238 $redirection = getAbsoluteUrl($url, $redirection); 242 $redirection = getAbsoluteUrl($url, $redirection);
diff --git a/application/Url.php b/application/http/Url.php
index 6b9870f0..90444a2f 100644
--- a/application/Url.php
+++ b/application/http/Url.php
@@ -1,91 +1,6 @@
1<?php 1<?php
2/**
3 * Converts an array-represented URL to a string
4 *
5 * Source: http://php.net/manual/en/function.parse-url.php#106731
6 *
7 * @see http://php.net/manual/en/function.parse-url.php
8 *
9 * @param array $parsedUrl an array-represented URL
10 *
11 * @return string the string representation of the URL
12 */
13function unparse_url($parsedUrl)
14{
15 $scheme = isset($parsedUrl['scheme']) ? $parsedUrl['scheme'].'://' : '';
16 $host = isset($parsedUrl['host']) ? $parsedUrl['host'] : '';
17 $port = isset($parsedUrl['port']) ? ':'.$parsedUrl['port'] : '';
18 $user = isset($parsedUrl['user']) ? $parsedUrl['user'] : '';
19 $pass = isset($parsedUrl['pass']) ? ':'.$parsedUrl['pass'] : '';
20 $pass = ($user || $pass) ? "$pass@" : '';
21 $path = isset($parsedUrl['path']) ? $parsedUrl['path'] : '';
22 $query = isset($parsedUrl['query']) ? '?'.$parsedUrl['query'] : '';
23 $fragment = isset($parsedUrl['fragment']) ? '#'.$parsedUrl['fragment'] : '';
24
25 return "$scheme$user$pass$host$port$path$query$fragment";
26}
27
28/**
29 * Removes undesired query parameters and fragments
30 *
31 * @param string url Url to be cleaned
32 *
33 * @return string the string representation of this URL after cleanup
34 */
35function cleanup_url($url)
36{
37 $obj_url = new Url($url);
38 return $obj_url->cleanup();
39}
40 2
41/** 3namespace Shaarli\Http;
42 * Get URL scheme.
43 *
44 * @param string url Url for which the scheme is requested
45 *
46 * @return mixed the URL scheme or false if none is provided.
47 */
48function get_url_scheme($url)
49{
50 $obj_url = new Url($url);
51 return $obj_url->getScheme();
52}
53
54/**
55 * Adds a trailing slash at the end of URL if necessary.
56 *
57 * @param string $url URL to check/edit.
58 *
59 * @return string $url URL with a end trailing slash.
60 */
61function add_trailing_slash($url)
62{
63 return $url . (!endsWith($url, '/') ? '/' : '');
64}
65
66/**
67 * Replace not whitelisted protocols by 'http://' from given URL.
68 *
69 * @param string $url URL to clean
70 * @param array $protocols List of allowed protocols (aside from http(s)).
71 *
72 * @return string URL with allowed protocol
73 */
74function whitelist_protocols($url, $protocols)
75{
76 if (startsWith($url, '?') || startsWith($url, '/')) {
77 return $url;
78 }
79 $protocols = array_merge(['http', 'https'], $protocols);
80 $protocol = preg_match('#^(\w+):/?/?#', $url, $match);
81 // Protocol not allowed: we remove it and replace it with http
82 if ($protocol === 1 && ! in_array($match[1], $protocols)) {
83 $url = str_replace($match[0], 'http://', $url);
84 } elseif ($protocol !== 1) {
85 $url = 'http://' . $url;
86 }
87 return $url;
88}
89 4
90/** 5/**
91 * URL representation and cleanup utilities 6 * URL representation and cleanup utilities
@@ -182,7 +97,7 @@ class Url
182 } 97 }
183 return $input; 98 return $input;
184 } 99 }
185 100
186 /** 101 /**
187 * Returns a string representation of this URL 102 * Returns a string representation of this URL
188 */ 103 */
@@ -196,7 +111,7 @@ class Url
196 */ 111 */
197 protected function cleanupQuery() 112 protected function cleanupQuery()
198 { 113 {
199 if (! isset($this->parts['query'])) { 114 if (!isset($this->parts['query'])) {
200 return; 115 return;
201 } 116 }
202 117
@@ -217,14 +132,14 @@ class Url
217 } 132 }
218 133
219 $this->parts['query'] = implode('&', $queryParams); 134 $this->parts['query'] = implode('&', $queryParams);
220 } 135 }
221 136
222 /** 137 /**
223 * Removes undesired fragments 138 * Removes undesired fragments
224 */ 139 */
225 protected function cleanupFragment() 140 protected function cleanupFragment()
226 { 141 {
227 if (! isset($this->parts['fragment'])) { 142 if (!isset($this->parts['fragment'])) {
228 return; 143 return;
229 } 144 }
230 145
@@ -257,7 +172,7 @@ class Url
257 public function idnToAscii() 172 public function idnToAscii()
258 { 173 {
259 $out = $this->cleanup(); 174 $out = $this->cleanup();
260 if (! function_exists('idn_to_ascii') || ! isset($this->parts['host'])) { 175 if (!function_exists('idn_to_ascii') || !isset($this->parts['host'])) {
261 return $out; 176 return $out;
262 } 177 }
263 $asciiHost = idn_to_ascii($this->parts['host'], 0, INTL_IDNA_VARIANT_UTS46); 178 $asciiHost = idn_to_ascii($this->parts['host'], 0, INTL_IDNA_VARIANT_UTS46);
@@ -269,7 +184,8 @@ class Url
269 * 184 *
270 * @return string the URL scheme or false if none is provided. 185 * @return string the URL scheme or false if none is provided.
271 */ 186 */
272 public function getScheme() { 187 public function getScheme()
188 {
273 if (!isset($this->parts['scheme'])) { 189 if (!isset($this->parts['scheme'])) {
274 return false; 190 return false;
275 } 191 }
@@ -281,7 +197,8 @@ class Url
281 * 197 *
282 * @return string the URL host or false if none is provided. 198 * @return string the URL host or false if none is provided.
283 */ 199 */
284 public function getHost() { 200 public function getHost()
201 {
285 if (empty($this->parts['host'])) { 202 if (empty($this->parts['host'])) {
286 return false; 203 return false;
287 } 204 }
@@ -289,11 +206,12 @@ class Url
289 } 206 }
290 207
291 /** 208 /**
292 * Test if the Url is an HTTP one. 209 * Test if the UrlUtils is an HTTP one.
293 * 210 *
294 * @return true is HTTP, false otherwise. 211 * @return true is HTTP, false otherwise.
295 */ 212 */
296 public function isHttp() { 213 public function isHttp()
214 {
297 return strpos(strtolower($this->parts['scheme']), 'http') !== false; 215 return strpos(strtolower($this->parts['scheme']), 'http') !== false;
298 } 216 }
299} 217}
diff --git a/application/http/UrlUtils.php b/application/http/UrlUtils.php
new file mode 100644
index 00000000..4bc84b82
--- /dev/null
+++ b/application/http/UrlUtils.php
@@ -0,0 +1,88 @@
1<?php
2/**
3 * Converts an array-represented URL to a string
4 *
5 * Source: http://php.net/manual/en/function.parse-url.php#106731
6 *
7 * @see http://php.net/manual/en/function.parse-url.php
8 *
9 * @param array $parsedUrl an array-represented URL
10 *
11 * @return string the string representation of the URL
12 */
13function unparse_url($parsedUrl)
14{
15 $scheme = isset($parsedUrl['scheme']) ? $parsedUrl['scheme'].'://' : '';
16 $host = isset($parsedUrl['host']) ? $parsedUrl['host'] : '';
17 $port = isset($parsedUrl['port']) ? ':'.$parsedUrl['port'] : '';
18 $user = isset($parsedUrl['user']) ? $parsedUrl['user'] : '';
19 $pass = isset($parsedUrl['pass']) ? ':'.$parsedUrl['pass'] : '';
20 $pass = ($user || $pass) ? "$pass@" : '';
21 $path = isset($parsedUrl['path']) ? $parsedUrl['path'] : '';
22 $query = isset($parsedUrl['query']) ? '?'.$parsedUrl['query'] : '';
23 $fragment = isset($parsedUrl['fragment']) ? '#'.$parsedUrl['fragment'] : '';
24
25 return "$scheme$user$pass$host$port$path$query$fragment";
26}
27
28/**
29 * Removes undesired query parameters and fragments
30 *
31 * @param string url UrlUtils to be cleaned
32 *
33 * @return string the string representation of this URL after cleanup
34 */
35function cleanup_url($url)
36{
37 $obj_url = new \Shaarli\Http\Url($url);
38 return $obj_url->cleanup();
39}
40
41/**
42 * Get URL scheme.
43 *
44 * @param string url UrlUtils for which the scheme is requested
45 *
46 * @return mixed the URL scheme or false if none is provided.
47 */
48function get_url_scheme($url)
49{
50 $obj_url = new \Shaarli\Http\Url($url);
51 return $obj_url->getScheme();
52}
53
54/**
55 * Adds a trailing slash at the end of URL if necessary.
56 *
57 * @param string $url URL to check/edit.
58 *
59 * @return string $url URL with a end trailing slash.
60 */
61function add_trailing_slash($url)
62{
63 return $url . (!endsWith($url, '/') ? '/' : '');
64}
65
66/**
67 * Replace not whitelisted protocols by 'http://' from given URL.
68 *
69 * @param string $url URL to clean
70 * @param array $protocols List of allowed protocols (aside from http(s)).
71 *
72 * @return string URL with allowed protocol
73 */
74function whitelist_protocols($url, $protocols)
75{
76 if (startsWith($url, '?') || startsWith($url, '/')) {
77 return $url;
78 }
79 $protocols = array_merge(['http', 'https'], $protocols);
80 $protocol = preg_match('#^(\w+):/?/?#', $url, $match);
81 // Protocol not allowed: we remove it and replace it with http
82 if ($protocol === 1 && ! in_array($match[1], $protocols)) {
83 $url = str_replace($match[0], 'http://', $url);
84 } elseif ($protocol !== 1) {
85 $url = 'http://' . $url;
86 }
87 return $url;
88}
diff --git a/application/NetscapeBookmarkUtils.php b/application/netscape/NetscapeBookmarkUtils.php
index b4d16d00..2fb1a4a6 100644
--- a/application/NetscapeBookmarkUtils.php
+++ b/application/netscape/NetscapeBookmarkUtils.php
@@ -1,9 +1,16 @@
1<?php 1<?php
2 2
3namespace Shaarli\Netscape;
4
5use DateTime;
6use DateTimeZone;
7use Exception;
8use Katzgrau\KLogger\Logger;
3use Psr\Log\LogLevel; 9use Psr\Log\LogLevel;
10use Shaarli\Bookmark\LinkDB;
4use Shaarli\Config\ConfigManager; 11use Shaarli\Config\ConfigManager;
12use Shaarli\History;
5use Shaarli\NetscapeBookmarkParser\NetscapeBookmarkParser; 13use Shaarli\NetscapeBookmarkParser\NetscapeBookmarkParser;
6use Katzgrau\KLogger\Logger;
7 14
8/** 15/**
9 * Utilities to import and export bookmarks using the Netscape format 16 * Utilities to import and export bookmarks using the Netscape format
@@ -31,8 +38,8 @@ class NetscapeBookmarkUtils
31 public static function filterAndFormat($linkDb, $selection, $prependNoteUrl, $indexUrl) 38 public static function filterAndFormat($linkDb, $selection, $prependNoteUrl, $indexUrl)
32 { 39 {
33 // see tpl/export.html for possible values 40 // see tpl/export.html for possible values
34 if (! in_array($selection, array('all', 'public', 'private'))) { 41 if (!in_array($selection, array('all', 'public', 'private'))) {
35 throw new Exception(t('Invalid export selection:') .' "'.$selection.'"'); 42 throw new Exception(t('Invalid export selection:') . ' "' . $selection . '"');
36 } 43 }
37 44
38 $bookmarkLinks = array(); 45 $bookmarkLinks = array();
@@ -72,18 +79,20 @@ class NetscapeBookmarkUtils
72 private static function importStatus( 79 private static function importStatus(
73 $filename, 80 $filename,
74 $filesize, 81 $filesize,
75 $importCount=0, 82 $importCount = 0,
76 $overwriteCount=0, 83 $overwriteCount = 0,
77 $skipCount=0, 84 $skipCount = 0,
78 $duration=0 85 $duration = 0
79 ) 86 ) {
80 {
81 $status = sprintf(t('File %s (%d bytes) '), $filename, $filesize); 87 $status = sprintf(t('File %s (%d bytes) '), $filename, $filesize);
82 if ($importCount == 0 && $overwriteCount == 0 && $skipCount == 0) { 88 if ($importCount == 0 && $overwriteCount == 0 && $skipCount == 0) {
83 $status .= t('has an unknown file format. Nothing was imported.'); 89 $status .= t('has an unknown file format. Nothing was imported.');
84 } else { 90 } else {
85 $status .= vsprintf( 91 $status .= vsprintf(
86 t('was successfully processed in %d seconds: %d links imported, %d links overwritten, %d links skipped.'), 92 t(
93 'was successfully processed in %d seconds: '
94 . '%d links imported, %d links overwritten, %d links skipped.'
95 ),
87 [$duration, $importCount, $overwriteCount, $skipCount] 96 [$duration, $importCount, $overwriteCount, $skipCount]
88 ); 97 );
89 } 98 }
@@ -93,11 +102,11 @@ class NetscapeBookmarkUtils
93 /** 102 /**
94 * Imports Web bookmarks from an uploaded Netscape bookmark dump 103 * Imports Web bookmarks from an uploaded Netscape bookmark dump
95 * 104 *
96 * @param array $post Server $_POST parameters 105 * @param array $post Server $_POST parameters
97 * @param array $files Server $_FILES parameters 106 * @param array $files Server $_FILES parameters
98 * @param LinkDB $linkDb Loaded LinkDB instance 107 * @param LinkDB $linkDb Loaded LinkDB instance
99 * @param ConfigManager $conf instance 108 * @param ConfigManager $conf instance
100 * @param History $history History instance 109 * @param History $history History instance
101 * 110 *
102 * @return string Summary of the bookmark import status 111 * @return string Summary of the bookmark import status
103 */ 112 */
@@ -113,7 +122,7 @@ class NetscapeBookmarkUtils
113 } 122 }
114 123
115 // Overwrite existing links? 124 // Overwrite existing links?
116 $overwrite = ! empty($post['overwrite']); 125 $overwrite = !empty($post['overwrite']);
117 126
118 // Add tags to all imported links? 127 // Add tags to all imported links?
119 if (empty($post['default_tags'])) { 128 if (empty($post['default_tags'])) {
@@ -136,7 +145,7 @@ class NetscapeBookmarkUtils
136 ); 145 );
137 $logger = new Logger( 146 $logger = new Logger(
138 $conf->get('resource.data_dir'), 147 $conf->get('resource.data_dir'),
139 ! $conf->get('dev.debug') ? LogLevel::INFO : LogLevel::DEBUG, 148 !$conf->get('dev.debug') ? LogLevel::INFO : LogLevel::DEBUG,
140 [ 149 [
141 'prefix' => 'import.', 150 'prefix' => 'import.',
142 'extension' => 'log', 151 'extension' => 'log',
@@ -191,7 +200,7 @@ class NetscapeBookmarkUtils
191 } 200 }
192 201
193 // Add a new link - @ used for UNIX timestamps 202 // Add a new link - @ used for UNIX timestamps
194 $newLinkDate = new DateTime('@'.strval($bkm['time'])); 203 $newLinkDate = new DateTime('@' . strval($bkm['time']));
195 $newLinkDate->setTimezone(new DateTimeZone(date_default_timezone_get())); 204 $newLinkDate->setTimezone(new DateTimeZone(date_default_timezone_get()));
196 $newLink['created'] = $newLinkDate; 205 $newLink['created'] = $newLinkDate;
197 $newLink['id'] = $linkDb->getNextId(); 206 $newLink['id'] = $linkDb->getNextId();
diff --git a/application/PluginManager.php b/application/plugin/PluginManager.php
index cf603845..f7b24a8e 100644
--- a/application/PluginManager.php
+++ b/application/plugin/PluginManager.php
@@ -1,4 +1,8 @@
1<?php 1<?php
2namespace Shaarli\Plugin;
3
4use Shaarli\Config\ConfigManager;
5use Shaarli\Plugin\Exception\PluginFileNotFoundException;
2 6
3/** 7/**
4 * Class PluginManager 8 * Class PluginManager
@@ -9,12 +13,14 @@ class PluginManager
9{ 13{
10 /** 14 /**
11 * List of authorized plugins from configuration file. 15 * List of authorized plugins from configuration file.
16 *
12 * @var array $authorizedPlugins 17 * @var array $authorizedPlugins
13 */ 18 */
14 private $authorizedPlugins; 19 private $authorizedPlugins;
15 20
16 /** 21 /**
17 * List of loaded plugins. 22 * List of loaded plugins.
23 *
18 * @var array $loadedPlugins 24 * @var array $loadedPlugins
19 */ 25 */
20 private $loadedPlugins = array(); 26 private $loadedPlugins = array();
@@ -31,12 +37,14 @@ class PluginManager
31 37
32 /** 38 /**
33 * Plugins subdirectory. 39 * Plugins subdirectory.
40 *
34 * @var string $PLUGINS_PATH 41 * @var string $PLUGINS_PATH
35 */ 42 */
36 public static $PLUGINS_PATH = 'plugins'; 43 public static $PLUGINS_PATH = 'plugins';
37 44
38 /** 45 /**
39 * Plugins meta files extension. 46 * Plugins meta files extension.
47 *
40 * @var string $META_EXT 48 * @var string $META_EXT
41 */ 49 */
42 public static $META_EXT = 'meta'; 50 public static $META_EXT = 'meta';
@@ -75,8 +83,7 @@ class PluginManager
75 83
76 try { 84 try {
77 $this->loadPlugin($dirs[$index], $plugin); 85 $this->loadPlugin($dirs[$index], $plugin);
78 } 86 } catch (PluginFileNotFoundException $e) {
79 catch (PluginFileNotFoundException $e) {
80 error_log($e->getMessage()); 87 error_log($e->getMessage());
81 } 88 }
82 } 89 }
@@ -85,9 +92,9 @@ class PluginManager
85 /** 92 /**
86 * Execute all plugins registered hook. 93 * Execute all plugins registered hook.
87 * 94 *
88 * @param string $hook name of the hook to trigger. 95 * @param string $hook name of the hook to trigger.
89 * @param array $data list of data to manipulate passed by reference. 96 * @param array $data list of data to manipulate passed by reference.
90 * @param array $params additional parameters such as page target. 97 * @param array $params additional parameters such as page target.
91 * 98 *
92 * @return void 99 * @return void
93 */ 100 */
@@ -119,7 +126,7 @@ class PluginManager
119 * @param string $pluginName plugin's name. 126 * @param string $pluginName plugin's name.
120 * 127 *
121 * @return void 128 * @return void
122 * @throws PluginFileNotFoundException - plugin files not found. 129 * @throws \Shaarli\Plugin\Exception\PluginFileNotFoundException - plugin files not found.
123 */ 130 */
124 private function loadPlugin($dir, $pluginName) 131 private function loadPlugin($dir, $pluginName)
125 { 132 {
@@ -205,8 +212,8 @@ class PluginManager
205 212
206 $metaData[$plugin]['parameters'][$param]['value'] = ''; 213 $metaData[$plugin]['parameters'][$param]['value'] = '';
207 // Optional parameter description in parameter.PARAM_NAME= 214 // Optional parameter description in parameter.PARAM_NAME=
208 if (isset($metaData[$plugin]['parameter.'. $param])) { 215 if (isset($metaData[$plugin]['parameter.' . $param])) {
209 $metaData[$plugin]['parameters'][$param]['desc'] = t($metaData[$plugin]['parameter.'. $param]); 216 $metaData[$plugin]['parameters'][$param]['desc'] = t($metaData[$plugin]['parameter.' . $param]);
210 } 217 }
211 } 218 }
212 } 219 }
@@ -224,22 +231,3 @@ class PluginManager
224 return $this->errors; 231 return $this->errors;
225 } 232 }
226} 233}
227
228/**
229 * Class PluginFileNotFoundException
230 *
231 * Raise when plugin files can't be found.
232 */
233class PluginFileNotFoundException extends Exception
234{
235 /**
236 * Construct exception with plugin name.
237 * Generate message.
238 *
239 * @param string $pluginName name of the plugin not found
240 */
241 public function __construct($pluginName)
242 {
243 $this->message = sprintf(t('Plugin "%s" files not found.'), $pluginName);
244 }
245}
diff --git a/application/plugin/exception/PluginFileNotFoundException.php b/application/plugin/exception/PluginFileNotFoundException.php
new file mode 100644
index 00000000..e5386f02
--- /dev/null
+++ b/application/plugin/exception/PluginFileNotFoundException.php
@@ -0,0 +1,23 @@
1<?php
2namespace Shaarli\Plugin\Exception;
3
4use Exception;
5
6/**
7 * Class PluginFileNotFoundException
8 *
9 * Raise when plugin files can't be found.
10 */
11class PluginFileNotFoundException extends Exception
12{
13 /**
14 * Construct exception with plugin name.
15 * Generate message.
16 *
17 * @param string $pluginName name of the plugin not found
18 */
19 public function __construct($pluginName)
20 {
21 $this->message = sprintf(t('Plugin "%s" files not found.'), $pluginName);
22 }
23}
diff --git a/application/PageBuilder.php b/application/render/PageBuilder.php
index a4483870..0569b67f 100644
--- a/application/PageBuilder.php
+++ b/application/render/PageBuilder.php
@@ -1,6 +1,13 @@
1<?php 1<?php
2 2
3namespace Shaarli\Render;
4
5use Exception;
6use RainTPL;
7use Shaarli\ApplicationUtils;
8use Shaarli\Bookmark\LinkDB;
3use Shaarli\Config\ConfigManager; 9use Shaarli\Config\ConfigManager;
10use Shaarli\Thumbnailer;
4 11
5/** 12/**
6 * This class is in charge of building the final page. 13 * This class is in charge of building the final page.
@@ -22,25 +29,40 @@ class PageBuilder
22 protected $conf; 29 protected $conf;
23 30
24 /** 31 /**
32 * @var array $_SESSION
33 */
34 protected $session;
35
36 /**
25 * @var LinkDB $linkDB instance. 37 * @var LinkDB $linkDB instance.
26 */ 38 */
27 protected $linkDB; 39 protected $linkDB;
28 40
29 /** @var bool $isLoggedIn Whether the user is logged in **/ 41 /**
42 * @var null|string XSRF token
43 */
44 protected $token;
45
46 /**
47 * @var bool $isLoggedIn Whether the user is logged in
48 */
30 protected $isLoggedIn = false; 49 protected $isLoggedIn = false;
31 50
32 /** 51 /**
33 * PageBuilder constructor. 52 * PageBuilder constructor.
34 * $tpl is initialized at false for lazy loading. 53 * $tpl is initialized at false for lazy loading.
35 * 54 *
36 * @param ConfigManager $conf Configuration Manager instance (reference). 55 * @param ConfigManager $conf Configuration Manager instance (reference).
37 * @param LinkDB $linkDB instance. 56 * @param array $session $_SESSION array
38 * @param string $token Session token 57 * @param LinkDB $linkDB instance.
58 * @param string $token Session token
59 * @param bool $isLoggedIn
39 */ 60 */
40 public function __construct(&$conf, $linkDB = null, $token = null, $isLoggedIn = false) 61 public function __construct(&$conf, $session, $linkDB = null, $token = null, $isLoggedIn = false)
41 { 62 {
42 $this->tpl = false; 63 $this->tpl = false;
43 $this->conf = $conf; 64 $this->conf = $conf;
65 $this->session = $session;
44 $this->linkDB = $linkDB; 66 $this->linkDB = $linkDB;
45 $this->token = $token; 67 $this->token = $token;
46 $this->isLoggedIn = $isLoggedIn; 68 $this->isLoggedIn = $isLoggedIn;
@@ -64,7 +86,6 @@ class PageBuilder
64 ); 86 );
65 $this->tpl->assign('newVersion', escape($version)); 87 $this->tpl->assign('newVersion', escape($version));
66 $this->tpl->assign('versionError', ''); 88 $this->tpl->assign('versionError', '');
67
68 } catch (Exception $exc) { 89 } catch (Exception $exc) {
69 logm($this->conf->get('resource.log'), $_SERVER['REMOTE_ADDR'], $exc->getMessage()); 90 logm($this->conf->get('resource.log'), $_SERVER['REMOTE_ADDR'], $exc->getMessage());
70 $this->tpl->assign('newVersion', ''); 91 $this->tpl->assign('newVersion', '');
@@ -87,8 +108,8 @@ class PageBuilder
87 'version_hash', 108 'version_hash',
88 ApplicationUtils::getVersionHash(SHAARLI_VERSION, $this->conf->get('credentials.salt')) 109 ApplicationUtils::getVersionHash(SHAARLI_VERSION, $this->conf->get('credentials.salt'))
89 ); 110 );
90 $this->tpl->assign('scripturl', index_url($_SERVER)); 111 $this->tpl->assign('index_url', index_url($_SERVER));
91 $visibility = ! empty($_SESSION['visibility']) ? $_SESSION['visibility'] : ''; 112 $visibility = !empty($_SESSION['visibility']) ? $_SESSION['visibility'] : '';
92 $this->tpl->assign('visibility', $visibility); 113 $this->tpl->assign('visibility', $visibility);
93 $this->tpl->assign('untaggedonly', !empty($_SESSION['untaggedonly'])); 114 $this->tpl->assign('untaggedonly', !empty($_SESSION['untaggedonly']));
94 $this->tpl->assign('pagetitle', $this->conf->get('general.title', 'Shaarli')); 115 $this->tpl->assign('pagetitle', $this->conf->get('general.title', 'Shaarli'));
@@ -105,6 +126,19 @@ class PageBuilder
105 if ($this->linkDB !== null) { 126 if ($this->linkDB !== null) {
106 $this->tpl->assign('tags', $this->linkDB->linksCountPerTag()); 127 $this->tpl->assign('tags', $this->linkDB->linksCountPerTag());
107 } 128 }
129
130 $this->tpl->assign(
131 'thumbnails_enabled',
132 $this->conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE
133 );
134 $this->tpl->assign('thumbnails_width', $this->conf->get('thumbnails.width'));
135 $this->tpl->assign('thumbnails_height', $this->conf->get('thumbnails.height'));
136
137 if (!empty($_SESSION['warnings'])) {
138 $this->tpl->assign('global_warnings', $_SESSION['warnings']);
139 unset($_SESSION['warnings']);
140 }
141
108 // To be removed with a proper theme configuration. 142 // To be removed with a proper theme configuration.
109 $this->tpl->assign('conf', $this->conf); 143 $this->tpl->assign('conf', $this->conf);
110 } 144 }
@@ -136,7 +170,7 @@ class PageBuilder
136 $this->initialize(); 170 $this->initialize();
137 } 171 }
138 172
139 if (empty($data) || !is_array($data)){ 173 if (empty($data) || !is_array($data)) {
140 return false; 174 return false;
141 } 175 }
142 176
@@ -163,16 +197,16 @@ class PageBuilder
163 197
164 /** 198 /**
165 * Render a 404 page (uses the template : tpl/404.tpl) 199 * Render a 404 page (uses the template : tpl/404.tpl)
166 * usage : $PAGE->render404('The link was deleted') 200 * usage: $PAGE->render404('The link was deleted')
167 * 201 *
168 * @param string $message A messate to display what is not found 202 * @param string $message A message to display what is not found
169 */ 203 */
170 public function render404($message = '') 204 public function render404($message = '')
171 { 205 {
172 if (empty($message)) { 206 if (empty($message)) {
173 $message = t('The page you are trying to reach does not exist or has been deleted.'); 207 $message = t('The page you are trying to reach does not exist or has been deleted.');
174 } 208 }
175 header($_SERVER['SERVER_PROTOCOL'] .' '. t('404 Not Found')); 209 header($_SERVER['SERVER_PROTOCOL'] . ' ' . t('404 Not Found'));
176 $this->tpl->assign('error_message', $message); 210 $this->tpl->assign('error_message', $message);
177 $this->renderPage('404'); 211 $this->renderPage('404');
178 } 212 }
diff --git a/application/ThemeUtils.php b/application/render/ThemeUtils.php
index 16f2f6a2..86096c64 100644
--- a/application/ThemeUtils.php
+++ b/application/render/ThemeUtils.php
@@ -1,6 +1,6 @@
1<?php 1<?php
2 2
3namespace Shaarli; 3namespace Shaarli\Render;
4 4
5/** 5/**
6 * Class ThemeUtils 6 * Class ThemeUtils
diff --git a/application/security/LoginManager.php b/application/security/LoginManager.php
index 5a58926d..1ff3d0be 100644
--- a/application/security/LoginManager.php
+++ b/application/security/LoginManager.php
@@ -98,7 +98,6 @@ class LoginManager
98 // The user client has a valid stay-signed-in cookie 98 // The user client has a valid stay-signed-in cookie
99 // Session information is updated with the current client information 99 // Session information is updated with the current client information
100 $this->sessionManager->storeLoginInfo($clientIpId); 100 $this->sessionManager->storeLoginInfo($clientIpId);
101
102 } elseif ($this->sessionManager->hasSessionExpired() 101 } elseif ($this->sessionManager->hasSessionExpired()
103 || $this->sessionManager->hasClientIpChanged($clientIpId) 102 || $this->sessionManager->hasClientIpChanged($clientIpId)
104 ) { 103 ) {
diff --git a/application/Updater.php b/application/updater/Updater.php
index dece2c02..f12e3516 100644
--- a/application/Updater.php
+++ b/application/updater/Updater.php
@@ -1,10 +1,24 @@
1<?php 1<?php
2
3namespace Shaarli\Updater;
4
5use Exception;
6use RainTPL;
7use ReflectionClass;
8use ReflectionException;
9use ReflectionMethod;
10use Shaarli\ApplicationUtils;
11use Shaarli\Bookmark\LinkDB;
12use Shaarli\Bookmark\LinkFilter;
2use Shaarli\Config\ConfigJson; 13use Shaarli\Config\ConfigJson;
3use Shaarli\Config\ConfigPhp;
4use Shaarli\Config\ConfigManager; 14use Shaarli\Config\ConfigManager;
15use Shaarli\Config\ConfigPhp;
16use Shaarli\Exceptions\IOException;
17use Shaarli\Thumbnailer;
18use Shaarli\Updater\Exception\UpdaterException;
5 19
6/** 20/**
7 * Class Updater. 21 * Class updater.
8 * Used to update stuff when a new Shaarli's version is reached. 22 * Used to update stuff when a new Shaarli's version is reached.
9 * Update methods are ran only once, and the stored in a JSON file. 23 * Update methods are ran only once, and the stored in a JSON file.
10 */ 24 */
@@ -31,6 +45,11 @@ class Updater
31 protected $isLoggedIn; 45 protected $isLoggedIn;
32 46
33 /** 47 /**
48 * @var array $_SESSION
49 */
50 protected $session;
51
52 /**
34 * @var ReflectionMethod[] List of current class methods. 53 * @var ReflectionMethod[] List of current class methods.
35 */ 54 */
36 protected $methods; 55 protected $methods;
@@ -42,13 +61,17 @@ class Updater
42 * @param LinkDB $linkDB LinkDB instance. 61 * @param LinkDB $linkDB LinkDB instance.
43 * @param ConfigManager $conf Configuration Manager instance. 62 * @param ConfigManager $conf Configuration Manager instance.
44 * @param boolean $isLoggedIn True if the user is logged in. 63 * @param boolean $isLoggedIn True if the user is logged in.
64 * @param array $session $_SESSION (by reference)
65 *
66 * @throws ReflectionException
45 */ 67 */
46 public function __construct($doneUpdates, $linkDB, $conf, $isLoggedIn) 68 public function __construct($doneUpdates, $linkDB, $conf, $isLoggedIn, &$session = [])
47 { 69 {
48 $this->doneUpdates = $doneUpdates; 70 $this->doneUpdates = $doneUpdates;
49 $this->linkDB = $linkDB; 71 $this->linkDB = $linkDB;
50 $this->conf = $conf; 72 $this->conf = $conf;
51 $this->isLoggedIn = $isLoggedIn; 73 $this->isLoggedIn = $isLoggedIn;
74 $this->session = &$session;
52 75
53 // Retrieve all update methods. 76 // Retrieve all update methods.
54 $class = new ReflectionClass($this); 77 $class = new ReflectionClass($this);
@@ -73,12 +96,12 @@ class Updater
73 } 96 }
74 97
75 if ($this->methods === null) { 98 if ($this->methods === null) {
76 throw new UpdaterException(t('Couldn\'t retrieve Updater class methods.')); 99 throw new UpdaterException(t('Couldn\'t retrieve updater class methods.'));
77 } 100 }
78 101
79 foreach ($this->methods as $method) { 102 foreach ($this->methods as $method) {
80 // Not an update method or already done, pass. 103 // Not an update method or already done, pass.
81 if (! startsWith($method->getName(), 'updateMethod') 104 if (!startsWith($method->getName(), 'updateMethod')
82 || in_array($method->getName(), $this->doneUpdates) 105 || in_array($method->getName(), $this->doneUpdates)
83 ) { 106 ) {
84 continue; 107 continue;
@@ -129,7 +152,7 @@ class Updater
129 } 152 }
130 } 153 }
131 $this->conf->write($this->isLoggedIn); 154 $this->conf->write($this->isLoggedIn);
132 unlink($this->conf->get('resource.data_dir').'/options.php'); 155 unlink($this->conf->get('resource.data_dir') . '/options.php');
133 } 156 }
134 157
135 return true; 158 return true;
@@ -164,16 +187,16 @@ class Updater
164 $subConfig = array('config', 'plugins'); 187 $subConfig = array('config', 'plugins');
165 foreach ($subConfig as $sub) { 188 foreach ($subConfig as $sub) {
166 foreach ($oldConfig[$sub] as $key => $value) { 189 foreach ($oldConfig[$sub] as $key => $value) {
167 if (isset($legacyMap[$sub .'.'. $key])) { 190 if (isset($legacyMap[$sub . '.' . $key])) {
168 $configKey = $legacyMap[$sub .'.'. $key]; 191 $configKey = $legacyMap[$sub . '.' . $key];
169 } else { 192 } else {
170 $configKey = $sub .'.'. $key; 193 $configKey = $sub . '.' . $key;
171 } 194 }
172 $this->conf->set($configKey, $value); 195 $this->conf->set($configKey, $value);
173 } 196 }
174 } 197 }
175 198
176 try{ 199 try {
177 $this->conf->write($this->isLoggedIn); 200 $this->conf->write($this->isLoggedIn);
178 return true; 201 return true;
179 } catch (IOException $e) { 202 } catch (IOException $e) {
@@ -223,7 +246,7 @@ class Updater
223 return true; 246 return true;
224 } 247 }
225 248
226 $save = $this->conf->get('resource.data_dir') .'/datastore.'. date('YmdHis') .'.php'; 249 $save = $this->conf->get('resource.data_dir') . '/datastore.' . date('YmdHis') . '.php';
227 copy($this->conf->get('resource.datastore'), $save); 250 copy($this->conf->get('resource.datastore'), $save);
228 251
229 $links = array(); 252 $links = array();
@@ -297,7 +320,7 @@ class Updater
297 // We run the update only if this folder still contains the template files. 320 // We run the update only if this folder still contains the template files.
298 $tplDir = $this->conf->get('resource.raintpl_tpl'); 321 $tplDir = $this->conf->get('resource.raintpl_tpl');
299 $tplFile = $tplDir . '/linklist.html'; 322 $tplFile = $tplDir . '/linklist.html';
300 if (! file_exists($tplFile)) { 323 if (!file_exists($tplFile)) {
301 return true; 324 return true;
302 } 325 }
303 326
@@ -321,7 +344,7 @@ class Updater
321 */ 344 */
322 public function updateMethodMoveUserCss() 345 public function updateMethodMoveUserCss()
323 { 346 {
324 if (! is_file('inc/user.css')) { 347 if (!is_file('inc/user.css')) {
325 return true; 348 return true;
326 } 349 }
327 350
@@ -357,11 +380,11 @@ class Updater
357 */ 380 */
358 public function updateMethodPiwikUrl() 381 public function updateMethodPiwikUrl()
359 { 382 {
360 if (! $this->conf->exists('plugins.PIWIK_URL') || startsWith($this->conf->get('plugins.PIWIK_URL'), 'http')) { 383 if (!$this->conf->exists('plugins.PIWIK_URL') || startsWith($this->conf->get('plugins.PIWIK_URL'), 'http')) {
361 return true; 384 return true;
362 } 385 }
363 386
364 $this->conf->set('plugins.PIWIK_URL', 'http://'. $this->conf->get('plugins.PIWIK_URL')); 387 $this->conf->set('plugins.PIWIK_URL', 'http://' . $this->conf->get('plugins.PIWIK_URL'));
365 $this->conf->write($this->isLoggedIn); 388 $this->conf->write($this->isLoggedIn);
366 389
367 return true; 390 return true;
@@ -471,109 +494,60 @@ class Updater
471 return true; 494 return true;
472 } 495 }
473 496
474 if (! $this->conf->exists('general.download_max_size')) { 497 if (!$this->conf->exists('general.download_max_size')) {
475 $this->conf->set('general.download_max_size', 1024*1024*4); 498 $this->conf->set('general.download_max_size', 1024 * 1024 * 4);
476 } 499 }
477 500
478 if (! $this->conf->exists('general.download_timeout')) { 501 if (!$this->conf->exists('general.download_timeout')) {
479 $this->conf->set('general.download_timeout', 30); 502 $this->conf->set('general.download_timeout', 30);
480 } 503 }
481 504
482 $this->conf->write($this->isLoggedIn); 505 $this->conf->write($this->isLoggedIn);
483
484 return true; 506 return true;
485 } 507 }
486}
487
488/**
489 * Class UpdaterException.
490 */
491class UpdaterException extends Exception
492{
493 /**
494 * @var string Method where the error occurred.
495 */
496 protected $method;
497
498 /**
499 * @var Exception The parent exception.
500 */
501 protected $previous;
502
503 /**
504 * Constructor.
505 *
506 * @param string $message Force the error message if set.
507 * @param string $method Method where the error occurred.
508 * @param Exception|bool $previous Parent exception.
509 */
510 public function __construct($message = '', $method = '', $previous = false)
511 {
512 $this->method = $method;
513 $this->previous = $previous;
514 $this->message = $this->buildMessage($message);
515 }
516 508
517 /** 509 /**
518 * Build the exception error message. 510 * * Move thumbnails management to WebThumbnailer, coming with new settings.
519 *
520 * @param string $message Optional given error message.
521 *
522 * @return string The built error message.
523 */ 511 */
524 private function buildMessage($message) 512 public function updateMethodWebThumbnailer()
525 { 513 {
526 $out = ''; 514 if ($this->conf->exists('thumbnails.mode')) {
527 if (! empty($message)) { 515 return true;
528 $out .= $message . PHP_EOL;
529 } 516 }
530 517
531 if (! empty($this->method)) { 518 $thumbnailsEnabled = extension_loaded('gd') && $this->conf->get('thumbnail.enable_thumbnails', true);
532 $out .= t('An error occurred while running the update ') . $this->method . PHP_EOL; 519 $this->conf->set('thumbnails.mode', $thumbnailsEnabled ? Thumbnailer::MODE_ALL : Thumbnailer::MODE_NONE);
533 } 520 $this->conf->set('thumbnails.width', 125);
521 $this->conf->set('thumbnails.height', 90);
522 $this->conf->remove('thumbnail');
523 $this->conf->write(true);
534 524
535 if (! empty($this->previous)) { 525 if ($thumbnailsEnabled) {
536 $out .= ' '. $this->previous->getMessage(); 526 $this->session['warnings'][] = t(
527 'You have enabled or changed thumbnails mode. <a href="?do=thumbs_update">Please synchronize them</a>.'
528 );
537 } 529 }
538 530
539 return $out; 531 return true;
540 } 532 }
541}
542 533
543/** 534 /**
544 * Read the updates file, and return already done updates. 535 * Set sticky = false on all links
545 * 536 *
546 * @param string $updatesFilepath Updates file path. 537 * @return bool true if the update is successful, false otherwise.
547 * 538 */
548 * @return array Already done update methods. 539 public function updateMethodSetSticky()
549 */ 540 {
550function read_updates_file($updatesFilepath) 541 foreach ($this->linkDB as $key => $link) {
551{ 542 if (isset($link['sticky'])) {
552 if (! empty($updatesFilepath) && is_file($updatesFilepath)) { 543 return true;
553 $content = file_get_contents($updatesFilepath); 544 }
554 if (! empty($content)) { 545 $link['sticky'] = false;
555 return explode(';', $content); 546 $this->linkDB[$key] = $link;
556 } 547 }
557 }
558 return array();
559}
560 548
561/** 549 $this->linkDB->save($this->conf->get('resource.page_cache'));
562 * Write updates file.
563 *
564 * @param string $updatesFilepath Updates file path.
565 * @param array $updates Updates array to write.
566 *
567 * @throws Exception Couldn't write version number.
568 */
569function write_updates_file($updatesFilepath, $updates)
570{
571 if (empty($updatesFilepath)) {
572 throw new Exception(t('Updates file path is not set, can\'t write updates.'));
573 }
574 550
575 $res = file_put_contents($updatesFilepath, implode(';', $updates)); 551 return true;
576 if ($res === false) {
577 throw new Exception(t('Unable to write updates in '. $updatesFilepath . '.'));
578 } 552 }
579} 553}
diff --git a/application/updater/UpdaterUtils.php b/application/updater/UpdaterUtils.php
new file mode 100644
index 00000000..34d4f422
--- /dev/null
+++ b/application/updater/UpdaterUtils.php
@@ -0,0 +1,39 @@
1<?php
2
3/**
4 * Read the updates file, and return already done updates.
5 *
6 * @param string $updatesFilepath Updates file path.
7 *
8 * @return array Already done update methods.
9 */
10function read_updates_file($updatesFilepath)
11{
12 if (! empty($updatesFilepath) && is_file($updatesFilepath)) {
13 $content = file_get_contents($updatesFilepath);
14 if (! empty($content)) {
15 return explode(';', $content);
16 }
17 }
18 return array();
19}
20
21/**
22 * Write updates file.
23 *
24 * @param string $updatesFilepath Updates file path.
25 * @param array $updates Updates array to write.
26 *
27 * @throws Exception Couldn't write version number.
28 */
29function write_updates_file($updatesFilepath, $updates)
30{
31 if (empty($updatesFilepath)) {
32 throw new Exception(t('Updates file path is not set, can\'t write updates.'));
33 }
34
35 $res = file_put_contents($updatesFilepath, implode(';', $updates));
36 if ($res === false) {
37 throw new Exception(t('Unable to write updates in '. $updatesFilepath . '.'));
38 }
39}
diff --git a/application/updater/exception/UpdaterException.php b/application/updater/exception/UpdaterException.php
new file mode 100644
index 00000000..20aceccf
--- /dev/null
+++ b/application/updater/exception/UpdaterException.php
@@ -0,0 +1,60 @@
1<?php
2
3namespace Shaarli\Updater\Exception;
4
5use Exception;
6
7/**
8 * Class UpdaterException.
9 */
10class UpdaterException extends Exception
11{
12 /**
13 * @var string Method where the error occurred.
14 */
15 protected $method;
16
17 /**
18 * @var Exception The parent exception.
19 */
20 protected $previous;
21
22 /**
23 * Constructor.
24 *
25 * @param string $message Force the error message if set.
26 * @param string $method Method where the error occurred.
27 * @param Exception|bool $previous Parent exception.
28 */
29 public function __construct($message = '', $method = '', $previous = false)
30 {
31 $this->method = $method;
32 $this->previous = $previous;
33 $this->message = $this->buildMessage($message);
34 }
35
36 /**
37 * Build the exception error message.
38 *
39 * @param string $message Optional given error message.
40 *
41 * @return string The built error message.
42 */
43 private function buildMessage($message)
44 {
45 $out = '';
46 if (!empty($message)) {
47 $out .= $message . PHP_EOL;
48 }
49
50 if (!empty($this->method)) {
51 $out .= t('An error occurred while running the update ') . $this->method . PHP_EOL;
52 }
53
54 if (!empty($this->previous)) {
55 $out .= ' ' . $this->previous->getMessage();
56 }
57
58 return $out;
59 }
60}
diff --git a/assets/common/js/picwall.js b/assets/common/js/picwall.js
deleted file mode 100644
index 87a93fc3..00000000
--- a/assets/common/js/picwall.js
+++ /dev/null
@@ -1,10 +0,0 @@
1import Blazy from 'blazy';
2
3(() => {
4 const picwall = document.getElementById('picwall_container');
5 if (picwall != null) {
6 // Suppress ESLint error because that's how bLazy works
7 /* eslint-disable no-new */
8 new Blazy();
9 }
10})();
diff --git a/assets/common/js/thumbnails-update.js b/assets/common/js/thumbnails-update.js
new file mode 100644
index 00000000..b66ca3ae
--- /dev/null
+++ b/assets/common/js/thumbnails-update.js
@@ -0,0 +1,51 @@
1/**
2 * Script used in the thumbnails update page.
3 *
4 * It retrieves the list of link IDs to update, and execute AJAX requests
5 * to update their thumbnails, while updating the progress bar.
6 */
7
8/**
9 * Update the thumbnail of the link with the current i index in ids.
10 * It contains a recursive call to retrieve the thumb of the next link when it succeed.
11 * It also update the progress bar and other visual feedback elements.
12 *
13 * @param {array} ids List of LinkID to update
14 * @param {int} i Current index in ids
15 * @param {object} elements List of DOM element to avoid retrieving them at each iteration
16 */
17function updateThumb(ids, i, elements) {
18 const xhr = new XMLHttpRequest();
19 xhr.open('POST', '?do=ajax_thumb_update');
20 xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
21 xhr.responseType = 'json';
22 xhr.onload = () => {
23 if (xhr.status !== 200) {
24 alert(`An error occurred. Return code: ${xhr.status}`);
25 } else {
26 const { response } = xhr;
27 i += 1;
28 elements.progressBar.style.width = `${(i * 100) / ids.length}%`;
29 elements.current.innerHTML = i;
30 elements.title.innerHTML = response.title;
31 if (response.thumbnail !== false) {
32 elements.thumbnail.innerHTML = `<img src="${response.thumbnail}">`;
33 }
34 if (i < ids.length) {
35 updateThumb(ids, i, elements);
36 }
37 }
38 };
39 xhr.send(`id=${ids[i]}`);
40}
41
42(() => {
43 const ids = document.getElementsByName('ids')[0].value.split(',');
44 const elements = {
45 progressBar: document.querySelector('.progressbar > div'),
46 current: document.querySelector('.progress-current'),
47 thumbnail: document.querySelector('.thumbnail-placeholder'),
48 title: document.querySelector('.thumbnail-link-title'),
49 };
50 updateThumb(ids, 0, elements);
51})();
diff --git a/assets/common/js/thumbnails.js b/assets/common/js/thumbnails.js
new file mode 100644
index 00000000..c28322bb
--- /dev/null
+++ b/assets/common/js/thumbnails.js
@@ -0,0 +1,7 @@
1import Blazy from 'blazy';
2
3(() => {
4 // Suppress ESLint error because that's how bLazy works
5 /* eslint-disable no-new */
6 new Blazy();
7})();
diff --git a/assets/default/js/base.js b/assets/default/js/base.js
index 5cf037c2..99e03370 100644
--- a/assets/default/js/base.js
+++ b/assets/default/js/base.js
@@ -98,29 +98,6 @@ function htmlEntities(str) {
98 return str.replace(/[\u00A0-\u9999<>&]/gim, i => `&#${i.charCodeAt(0)};`); 98 return str.replace(/[\u00A0-\u9999<>&]/gim, i => `&#${i.charCodeAt(0)};`);
99} 99}
100 100
101function activateFirefoxSocial(node) {
102 const loc = location.href;
103 const baseURL = loc.substring(0, loc.lastIndexOf('/') + 1);
104
105 const data = {
106 name: document.title,
107 description: document.getElementById('translation-delete-link').innerHTML,
108 author: 'Shaarli',
109 version: '1.0.0',
110
111 iconURL: `${baseURL}/images/favicon.ico`,
112 icon32URL: `${baseURL}/images/favicon.ico`,
113 icon64URL: `${baseURL}/images/favicon.ico`,
114
115 shareURL: `${baseURL}?post=%{url}&title=%{title}&description=%{text}&source=firefoxsocialapi`,
116 homepageURL: baseURL,
117 };
118 node.setAttribute('data-service', JSON.stringify(data));
119
120 const activate = new CustomEvent('ActivateSocialFeature');
121 node.dispatchEvent(activate);
122}
123
124/** 101/**
125 * Add the class 'hidden' to city options not attached to the current selected continent. 102 * Add the class 'hidden' to city options not attached to the current selected continent.
126 * 103 *
@@ -433,16 +410,6 @@ function init(description) {
433 }); 410 });
434 }); 411 });
435 412
436 /**
437 * Firefox Social
438 */
439 const ffButton = document.getElementById('ff-social-button');
440 if (ffButton != null) {
441 ffButton.addEventListener('click', (event) => {
442 activateFirefoxSocial(event.target);
443 });
444 }
445
446 const continent = document.getElementById('continent'); 413 const continent = document.getElementById('continent');
447 const city = document.getElementById('city'); 414 const city = document.getElementById('city');
448 if (continent != null && city != null) { 415 if (continent != null && city != null) {
@@ -455,12 +422,12 @@ function init(description) {
455 /** 422 /**
456 * Bulk actions 423 * Bulk actions
457 */ 424 */
458 const linkCheckboxes = document.querySelectorAll('.delete-checkbox'); 425 const linkCheckboxes = document.querySelectorAll('.link-checkbox');
459 const bar = document.getElementById('actions'); 426 const bar = document.getElementById('actions');
460 [...linkCheckboxes].forEach((checkbox) => { 427 [...linkCheckboxes].forEach((checkbox) => {
461 checkbox.style.display = 'inline-block'; 428 checkbox.style.display = 'inline-block';
462 checkbox.addEventListener('click', () => { 429 checkbox.addEventListener('change', () => {
463 const linkCheckedCheckboxes = document.querySelectorAll('.delete-checkbox:checked'); 430 const linkCheckedCheckboxes = document.querySelectorAll('.link-checkbox:checked');
464 const count = [...linkCheckedCheckboxes].length; 431 const count = [...linkCheckedCheckboxes].length;
465 if (count === 0 && bar.classList.contains('open')) { 432 if (count === 0 && bar.classList.contains('open')) {
466 bar.classList.toggle('open'); 433 bar.classList.toggle('open');
@@ -477,7 +444,7 @@ function init(description) {
477 event.preventDefault(); 444 event.preventDefault();
478 445
479 const links = []; 446 const links = [];
480 const linkCheckedCheckboxes = document.querySelectorAll('.delete-checkbox:checked'); 447 const linkCheckedCheckboxes = document.querySelectorAll('.link-checkbox:checked');
481 [...linkCheckedCheckboxes].forEach((checkbox) => { 448 [...linkCheckedCheckboxes].forEach((checkbox) => {
482 links.push({ 449 links.push({
483 id: checkbox.value, 450 id: checkbox.value,
@@ -500,6 +467,25 @@ function init(description) {
500 } 467 }
501 468
502 /** 469 /**
470 * Select all button
471 */
472 const selectAllButtons = document.querySelectorAll('.select-all-button');
473 [...selectAllButtons].forEach((selectAllButton) => {
474 selectAllButton.addEventListener('click', (e) => {
475 e.preventDefault();
476 const checked = selectAllButton.classList.contains('filter-off');
477 [...selectAllButtons].forEach((selectAllButton2) => {
478 selectAllButton2.classList.toggle('filter-off');
479 selectAllButton2.classList.toggle('filter-on');
480 });
481 [...linkCheckboxes].forEach((linkCheckbox) => {
482 linkCheckbox.checked = checked;
483 linkCheckbox.dispatchEvent(new Event('change'));
484 });
485 });
486 });
487
488 /**
503 * Tag list operations 489 * Tag list operations
504 * 490 *
505 * TODO: support error code in the backend for AJAX requests 491 * TODO: support error code in the backend for AJAX requests
@@ -581,7 +567,7 @@ function init(description) {
581 event.preventDefault(); 567 event.preventDefault();
582 const block = findParent(event.target, 'div', { class: 'tag-list-item' }); 568 const block = findParent(event.target, 'div', { class: 'tag-list-item' });
583 const tag = block.getAttribute('data-tag'); 569 const tag = block.getAttribute('data-tag');
584 const refreshedToken = document.getElementById('token'); 570 const refreshedToken = document.getElementById('token').value;
585 571
586 if (confirm(`Are you sure you want to delete the tag "${tag}"?`)) { 572 if (confirm(`Are you sure you want to delete the tag "${tag}"?`)) {
587 const xhr = new XMLHttpRequest(); 573 const xhr = new XMLHttpRequest();
diff --git a/assets/default/scss/shaarli.scss b/assets/default/scss/shaarli.scss
index 09d5efbe..760d8d6a 100644
--- a/assets/default/scss/shaarli.scss
+++ b/assets/default/scss/shaarli.scss
@@ -146,6 +146,17 @@ body,
146 background-color: $main-green; 146 background-color: $main-green;
147} 147}
148 148
149.pure-alert-warning {
150 a {
151 color: $warning-text;
152 font-weight: bold;
153 }
154}
155
156.page-single-alert {
157 margin-top: 100px;
158}
159
149.anchor { 160.anchor {
150 &:target { 161 &:target {
151 padding-top: 40px; 162 padding-top: 40px;
@@ -370,8 +381,6 @@ body,
370 box-shadow: 0 1px 0 $light-shadow, 0 1px 4px $dark-shadow inset; 381 box-shadow: 0 1px 0 $light-shadow, 0 1px 4px $dark-shadow inset;
371 background: $almost-white; 382 background: $almost-white;
372 padding: 5px 5px 3px 15px; 383 padding: 5px 5px 3px 15px;
373 width: 20%;
374 height: 20px;
375 color: $dark-grey; 384 color: $dark-grey;
376} 385}
377 386
@@ -625,23 +634,22 @@ body,
625} 634}
626 635
627.linklist-item { 636.linklist-item {
637 position: relative;
628 margin: 0 0 10px; 638 margin: 0 0 10px;
629 box-shadow: 1px 1px 3px $light-grey; 639 box-shadow: 1px 1px 3px $light-grey;
630 background: $almost-white; 640 background: $almost-white;
631 641
632 &.private { 642 &.private {
633 .linklist-item-title { 643 &::before {
634 &::before { 644 display: block;
635 @extend %private-border; 645 position: absolute;
636 margin-top: 3px; 646 top: 0;
637 } 647 left: 0;
638 } 648 z-index: 1;
639 649 background: $orange;
640 .linklist-item-description { 650 width: 2px;
641 &::before { 651 height: 100%;
642 @extend %private-border; 652 content: '';
643 height: 100%;
644 }
645 } 653 }
646 } 654 }
647} 655}
@@ -732,7 +740,7 @@ body,
732 font-size: 1em; 740 font-size: 1em;
733 } 741 }
734 742
735 .delete-checkbox { 743 .link-checkbox {
736 display: none; 744 display: none;
737 } 745 }
738} 746}
@@ -747,6 +755,14 @@ body,
747 font-size: 1.3em; 755 font-size: 1.3em;
748} 756}
749 757
758.pin-link {
759 font-size: 1.3em;
760}
761
762.pinned-link {
763 color: $blue !important;
764}
765
750.linklist-item-description { 766.linklist-item-description {
751 position: relative; 767 position: relative;
752 padding: 0 10px; 768 padding: 0 10px;
@@ -840,6 +856,10 @@ body,
840 margin: 0 7px; 856 margin: 0 7px;
841} 857}
842 858
859.ctrl-delete {
860 margin: 0 7px 0 0;
861}
862
843// 64em -> lg 863// 64em -> lg
844@media screen and (max-width: 64em) { 864@media screen and (max-width: 64em) {
845 .linklist-item-infos-url { 865 .linklist-item-infos-url {
@@ -1543,3 +1563,40 @@ form {
1543.pure-button-shaarli { 1563.pure-button-shaarli {
1544 background-color: $main-green; 1564 background-color: $main-green;
1545} 1565}
1566
1567.progressbar {
1568 border-radius: 6px;
1569 background-color: $main-green;
1570 padding: 1px;
1571
1572 > div {
1573 border-radius: 10px;
1574 background: repeating-linear-gradient(
1575 -45deg,
1576 $almost-white,
1577 $almost-white 6px,
1578 $background-color 6px,
1579 $background-color 12px
1580 );
1581 width: 0%;
1582 height: 10px;
1583 }
1584}
1585
1586.thumbnails-page-container {
1587 .progress-counter {
1588 padding: 10px 0 20px;
1589 }
1590
1591 .thumbnail-placeholder {
1592 margin: 10px auto;
1593 background-color: $light-grey;
1594 }
1595
1596 .thumbnail-link-title {
1597 padding-bottom: 20px;
1598 overflow: hidden;
1599 text-overflow: ellipsis;
1600 white-space: nowrap;
1601 }
1602}
diff --git a/assets/vintage/css/shaarli.css b/assets/vintage/css/shaarli.css
index c919339b..87c440c8 100644
--- a/assets/vintage/css/shaarli.css
+++ b/assets/vintage/css/shaarli.css
@@ -701,8 +701,8 @@ a.bigbutton, #pageheader a.bigbutton {
701 position: relative; 701 position: relative;
702 display: table-cell; 702 display: table-cell;
703 vertical-align: middle; 703 vertical-align: middle;
704 width: 90px; 704 width: 120px;
705 height: 90px; 705 height: 120px;
706 overflow: hidden; 706 overflow: hidden;
707 text-align: center; 707 text-align: center;
708 float: left; 708 float: left;
@@ -739,9 +739,9 @@ a.bigbutton, #pageheader a.bigbutton {
739 position: absolute; 739 position: absolute;
740 top: 0; 740 top: 0;
741 left: 0; 741 left: 0;
742 width: 90px; 742 width: 120px;
743 font-weight: bold; 743 font-weight: bold;
744 font-size: 8pt; 744 font-size: 9pt;
745 color: #fff; 745 color: #fff;
746 text-align: left; 746 text-align: left;
747 background-color: transparent; 747 background-color: transparent;
@@ -1210,3 +1210,43 @@ ul.errors {
1210 width: 13px; 1210 width: 13px;
1211 height: 13px; 1211 height: 13px;
1212} 1212}
1213
1214.thumbnails-update-container {
1215 padding: 20px 0;
1216 width: 50%;
1217 margin: auto;
1218}
1219
1220.thumbnails-update-container .thumbnail-placeholder {
1221 background: grey;
1222 margin: auto;
1223}
1224
1225.thumbnails-update-container .thumbnail-link-title {
1226 width: 75%;
1227 margin: auto;
1228
1229 padding-bottom: 20px;
1230 overflow: hidden;
1231 text-overflow: ellipsis;
1232 white-space: nowrap;
1233}
1234
1235.progressbar {
1236 border-radius: 6px;
1237 background-color: #111;
1238 padding: 1px;
1239}
1240
1241.progressbar > div {
1242 border-radius: 10px;
1243 background: repeating-linear-gradient(
1244 -45deg,
1245 #f5f5f5,
1246 #f5f5f5 6px,
1247 #d0d0d0 6px,
1248 #d0d0d0 12px
1249 );
1250 width: 0%;
1251 height: 10px;
1252}
diff --git a/composer.json b/composer.json
index 0d4c623c..c23b8252 100644
--- a/composer.json
+++ b/composer.json
@@ -16,18 +16,27 @@
16 }, 16 },
17 "require": { 17 "require": {
18 "php": ">=5.6", 18 "php": ">=5.6",
19 "shaarli/netscape-bookmark-parser": "^2.0", 19 "ext-json": "*",
20 "ext-zlib": "*",
21 "shaarli/netscape-bookmark-parser": "^2.1",
20 "erusev/parsedown": "^1.6", 22 "erusev/parsedown": "^1.6",
21 "slim/slim": "^3.0", 23 "slim/slim": "^3.0",
24 "arthurhoaro/web-thumbnailer": "^1.1",
22 "pubsubhubbub/publisher": "dev-master", 25 "pubsubhubbub/publisher": "dev-master",
23 "gettext/gettext": "^4.4" 26 "gettext/gettext": "^4.4"
24 }, 27 },
25 "require-dev": { 28 "require-dev": {
26 "phpmd/phpmd" : "@stable", 29 "roave/security-advisories": "dev-master",
30 "phpunit/phpcov": "*",
27 "phpunit/phpunit": "^5.0", 31 "phpunit/phpunit": "^5.0",
28 "sebastian/phpcpd": "*", 32 "squizlabs/php_codesniffer": "2.*"
29 "squizlabs/php_codesniffer": "2.*", 33 },
30 "phpunit/phpcov": "*" 34 "suggest": {
35 "ext-curl": "Allows fetching web pages and thumbnails in a more robust way",
36 "ext-gd": "Required for thumbnail generation",
37 "ext-gettext": "Enables faster translation system in gettext mode",
38 "ext-intl": "Provides localized text sorting",
39 "ext-mbstring": "Provides multibyte (Unicode) string support"
31 }, 40 },
32 "autoload": { 41 "autoload": {
33 "psr-4": { 42 "psr-4": {
@@ -35,9 +44,21 @@
35 "Shaarli\\Api\\": "application/api/", 44 "Shaarli\\Api\\": "application/api/",
36 "Shaarli\\Api\\Controllers\\": "application/api/controllers", 45 "Shaarli\\Api\\Controllers\\": "application/api/controllers",
37 "Shaarli\\Api\\Exceptions\\": "application/api/exceptions", 46 "Shaarli\\Api\\Exceptions\\": "application/api/exceptions",
47 "Shaarli\\Bookmark\\": "application/bookmark",
48 "Shaarli\\Bookmark\\Exception\\": "application/bookmark/exception",
38 "Shaarli\\Config\\": "application/config/", 49 "Shaarli\\Config\\": "application/config/",
39 "Shaarli\\Config\\Exception\\": "application/config/exception", 50 "Shaarli\\Config\\Exception\\": "application/config/exception",
40 "Shaarli\\Security\\": "application/security" 51 "Shaarli\\Exceptions\\": "application/exceptions",
52 "Shaarli\\Feed\\": "application/feed",
53 "Shaarli\\Http\\": "application/http",
54 "Shaarli\\Netscape\\": "application/netscape",
55 "Shaarli\\Plugin\\": "application/plugin",
56 "Shaarli\\Plugin\\Exception\\": "application/plugin/exception",
57 "Shaarli\\Plugin\\Wallabag\\": "plugins/wallabag",
58 "Shaarli\\Render\\": "application/render",
59 "Shaarli\\Security\\": "application/security",
60 "Shaarli\\Updater\\": "application/updater",
61 "Shaarli\\Updater\\Exception\\": "application/updater/exception"
41 } 62 }
42 } 63 }
43} 64}
diff --git a/composer.lock b/composer.lock
index ee762c0e..5cbcbc4a 100644
--- a/composer.lock
+++ b/composer.lock
@@ -1,12 +1,60 @@
1{ 1{
2 "_readme": [ 2 "_readme": [
3 "This file locks the dependencies of your project to a known state", 3 "This file locks the dependencies of your project to a known state",
4 "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 4 "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
5 "This file is @generated automatically" 5 "This file is @generated automatically"
6 ], 6 ],
7 "content-hash": "308a35eab91602fbb449f2c669c445ed", 7 "content-hash": "432005c9db3e890f42fde27036d2a70f",
8 "packages": [ 8 "packages": [
9 { 9 {
10 "name": "arthurhoaro/web-thumbnailer",
11 "version": "v1.3.1",
12 "source": {
13 "type": "git",
14 "url": "https://github.com/ArthurHoaro/web-thumbnailer.git",
15 "reference": "7142bd94ec93719a756a7012ebb8e1c5813c6860"
16 },
17 "dist": {
18 "type": "zip",
19 "url": "https://api.github.com/repos/ArthurHoaro/web-thumbnailer/zipball/7142bd94ec93719a756a7012ebb8e1c5813c6860",
20 "reference": "7142bd94ec93719a756a7012ebb8e1c5813c6860",
21 "shasum": ""
22 },
23 "require": {
24 "php": ">=5.6",
25 "phpunit/php-text-template": "^1.2"
26 },
27 "conflict": {
28 "phpunit/php-timer": ">=2"
29 },
30 "require-dev": {
31 "php-coveralls/php-coveralls": "^2.0",
32 "phpunit/phpunit": "5.2.*",
33 "squizlabs/php_codesniffer": "^3.2"
34 },
35 "type": "library",
36 "autoload": {
37 "psr-0": {
38 "WebThumbnailer\\": [
39 "src/",
40 "tests/"
41 ]
42 }
43 },
44 "notification-url": "https://packagist.org/downloads/",
45 "license": [
46 "MIT"
47 ],
48 "authors": [
49 {
50 "name": "Arthur Hoaro",
51 "homepage": "http://hoa.ro"
52 }
53 ],
54 "description": "PHP library which will retrieve a thumbnail for any given URL",
55 "time": "2018-08-11T12:21:52+00:00"
56 },
57 {
10 "name": "container-interop/container-interop", 58 "name": "container-interop/container-interop",
11 "version": "1.2.0", 59 "version": "1.2.0",
12 "source": { 60 "source": {
@@ -85,16 +133,16 @@
85 }, 133 },
86 { 134 {
87 "name": "gettext/gettext", 135 "name": "gettext/gettext",
88 "version": "v4.4.4", 136 "version": "v4.6.2",
89 "source": { 137 "source": {
90 "type": "git", 138 "type": "git",
91 "url": "https://github.com/oscarotero/Gettext.git", 139 "url": "https://github.com/oscarotero/Gettext.git",
92 "reference": "ab5e863de2f60806d02e6e6081e21efd45249168" 140 "reference": "93176b272d61fb58a9767be71c50d19149cb1e48"
93 }, 141 },
94 "dist": { 142 "dist": {
95 "type": "zip", 143 "type": "zip",
96 "url": "https://api.github.com/repos/oscarotero/Gettext/zipball/ab5e863de2f60806d02e6e6081e21efd45249168", 144 "url": "https://api.github.com/repos/oscarotero/Gettext/zipball/93176b272d61fb58a9767be71c50d19149cb1e48",
97 "reference": "ab5e863de2f60806d02e6e6081e21efd45249168", 145 "reference": "93176b272d61fb58a9767be71c50d19149cb1e48",
98 "shasum": "" 146 "shasum": ""
99 }, 147 },
100 "require": { 148 "require": {
@@ -103,7 +151,7 @@
103 }, 151 },
104 "require-dev": { 152 "require-dev": {
105 "illuminate/view": "*", 153 "illuminate/view": "*",
106 "phpunit/phpunit": "^4.8|^5.7", 154 "phpunit/phpunit": "^4.8|^5.7|^6.5",
107 "squizlabs/php_codesniffer": "^3.0", 155 "squizlabs/php_codesniffer": "^3.0",
108 "symfony/yaml": "~2", 156 "symfony/yaml": "~2",
109 "twig/extensions": "*", 157 "twig/extensions": "*",
@@ -143,20 +191,20 @@
143 "po", 191 "po",
144 "translation" 192 "translation"
145 ], 193 ],
146 "time": "2018-02-21T18:49:59+00:00" 194 "time": "2019-01-12T18:40:56+00:00"
147 }, 195 },
148 { 196 {
149 "name": "gettext/languages", 197 "name": "gettext/languages",
150 "version": "2.3.0", 198 "version": "2.5.0",
151 "source": { 199 "source": {
152 "type": "git", 200 "type": "git",
153 "url": "https://github.com/mlocati/cldr-to-gettext-plural-rules.git", 201 "url": "https://github.com/mlocati/cldr-to-gettext-plural-rules.git",
154 "reference": "49c39e51569963cc917a924b489e7025bfb9d8c7" 202 "reference": "78db2d17933f0765a102f368a6663f057162ddbd"
155 }, 203 },
156 "dist": { 204 "dist": {
157 "type": "zip", 205 "type": "zip",
158 "url": "https://api.github.com/repos/mlocati/cldr-to-gettext-plural-rules/zipball/49c39e51569963cc917a924b489e7025bfb9d8c7", 206 "url": "https://api.github.com/repos/mlocati/cldr-to-gettext-plural-rules/zipball/78db2d17933f0765a102f368a6663f057162ddbd",
159 "reference": "49c39e51569963cc917a924b489e7025bfb9d8c7", 207 "reference": "78db2d17933f0765a102f368a6663f057162ddbd",
160 "shasum": "" 208 "shasum": ""
161 }, 209 },
162 "require": { 210 "require": {
@@ -204,7 +252,7 @@
204 "translations", 252 "translations",
205 "unicode" 253 "unicode"
206 ], 254 ],
207 "time": "2017-03-23T17:02:28+00:00" 255 "time": "2018-11-13T22:06:07+00:00"
208 }, 256 },
209 { 257 {
210 "name": "katzgrau/klogger", 258 "name": "katzgrau/klogger",
@@ -303,6 +351,47 @@
303 "time": "2018-02-13T20:26:39+00:00" 351 "time": "2018-02-13T20:26:39+00:00"
304 }, 352 },
305 { 353 {
354 "name": "phpunit/php-text-template",
355 "version": "1.2.1",
356 "source": {
357 "type": "git",
358 "url": "https://github.com/sebastianbergmann/php-text-template.git",
359 "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686"
360 },
361 "dist": {
362 "type": "zip",
363 "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
364 "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
365 "shasum": ""
366 },
367 "require": {
368 "php": ">=5.3.3"
369 },
370 "type": "library",
371 "autoload": {
372 "classmap": [
373 "src/"
374 ]
375 },
376 "notification-url": "https://packagist.org/downloads/",
377 "license": [
378 "BSD-3-Clause"
379 ],
380 "authors": [
381 {
382 "name": "Sebastian Bergmann",
383 "email": "sebastian@phpunit.de",
384 "role": "lead"
385 }
386 ],
387 "description": "Simple template engine.",
388 "homepage": "https://github.com/sebastianbergmann/php-text-template/",
389 "keywords": [
390 "template"
391 ],
392 "time": "2015-06-21T13:50:34+00:00"
393 },
394 {
306 "name": "pimple/pimple", 395 "name": "pimple/pimple",
307 "version": "v3.2.3", 396 "version": "v3.2.3",
308 "source": { 397 "source": {
@@ -453,16 +542,16 @@
453 }, 542 },
454 { 543 {
455 "name": "psr/log", 544 "name": "psr/log",
456 "version": "1.0.2", 545 "version": "1.1.0",
457 "source": { 546 "source": {
458 "type": "git", 547 "type": "git",
459 "url": "https://github.com/php-fig/log.git", 548 "url": "https://github.com/php-fig/log.git",
460 "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" 549 "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd"
461 }, 550 },
462 "dist": { 551 "dist": {
463 "type": "zip", 552 "type": "zip",
464 "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", 553 "url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd",
465 "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", 554 "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd",
466 "shasum": "" 555 "shasum": ""
467 }, 556 },
468 "require": { 557 "require": {
@@ -496,7 +585,7 @@
496 "psr", 585 "psr",
497 "psr-3" 586 "psr-3"
498 ], 587 ],
499 "time": "2016-10-10T12:19:37+00:00" 588 "time": "2018-11-20T15:27:04+00:00"
500 }, 589 },
501 { 590 {
502 "name": "pubsubhubbub/publisher", 591 "name": "pubsubhubbub/publisher",
@@ -504,15 +593,16 @@
504 "source": { 593 "source": {
505 "type": "git", 594 "type": "git",
506 "url": "https://github.com/pubsubhubbub/php-publisher.git", 595 "url": "https://github.com/pubsubhubbub/php-publisher.git",
507 "reference": "0d224daebd504ab61c22fee4db58f8d1fc18945f" 596 "reference": "047b0faf6219071527a45942d6fef4dbc6d1d884"
508 }, 597 },
509 "dist": { 598 "dist": {
510 "type": "zip", 599 "type": "zip",
511 "url": "https://api.github.com/repos/pubsubhubbub/php-publisher/zipball/0d224daebd504ab61c22fee4db58f8d1fc18945f", 600 "url": "https://api.github.com/repos/pubsubhubbub/php-publisher/zipball/047b0faf6219071527a45942d6fef4dbc6d1d884",
512 "reference": "0d224daebd504ab61c22fee4db58f8d1fc18945f", 601 "reference": "047b0faf6219071527a45942d6fef4dbc6d1d884",
513 "shasum": "" 602 "shasum": ""
514 }, 603 },
515 "require": { 604 "require": {
605 "ext-curl": "*",
516 "php": "~5.4 || ~7.0" 606 "php": "~5.4 || ~7.0"
517 }, 607 },
518 "type": "library", 608 "type": "library",
@@ -537,30 +627,31 @@
537 "data", 627 "data",
538 "feeds", 628 "feeds",
539 "publishers", 629 "publishers",
540 "pubsubhubbub" 630 "pubsubhubbub",
631 "websub"
541 ], 632 ],
542 "time": "2017-10-08T10:59:41+00:00" 633 "time": "2018-10-09T05:20:28+00:00"
543 }, 634 },
544 { 635 {
545 "name": "shaarli/netscape-bookmark-parser", 636 "name": "shaarli/netscape-bookmark-parser",
546 "version": "v2.0.5", 637 "version": "v2.1.0",
547 "source": { 638 "source": {
548 "type": "git", 639 "type": "git",
549 "url": "https://github.com/shaarli/netscape-bookmark-parser.git", 640 "url": "https://github.com/shaarli/netscape-bookmark-parser.git",
550 "reference": "ea6911a0ea3dd372fa7002593c5aef9c15a49315" 641 "reference": "819008ee42c4dd7e45d988176a4a22d6ed689577"
551 }, 642 },
552 "dist": { 643 "dist": {
553 "type": "zip", 644 "type": "zip",
554 "url": "https://api.github.com/repos/shaarli/netscape-bookmark-parser/zipball/ea6911a0ea3dd372fa7002593c5aef9c15a49315", 645 "url": "https://api.github.com/repos/shaarli/netscape-bookmark-parser/zipball/819008ee42c4dd7e45d988176a4a22d6ed689577",
555 "reference": "ea6911a0ea3dd372fa7002593c5aef9c15a49315", 646 "reference": "819008ee42c4dd7e45d988176a4a22d6ed689577",
556 "shasum": "" 647 "shasum": ""
557 }, 648 },
558 "require": { 649 "require": {
559 "katzgrau/klogger": "~1.0", 650 "katzgrau/klogger": "~1.0",
560 "php": ">=5.3.4" 651 "php": ">=5.6"
561 }, 652 },
562 "require-dev": { 653 "require-dev": {
563 "phpunit/phpunit": "4.8.*" 654 "phpunit/phpunit": "^5.0"
564 }, 655 },
565 "type": "library", 656 "type": "library",
566 "autoload": { 657 "autoload": {
@@ -592,22 +683,22 @@
592 "bookmark", 683 "bookmark",
593 "link", 684 "link",
594 "netscape", 685 "netscape",
595 "parse" 686 "parser"
596 ], 687 ],
597 "time": "2018-01-30T17:34:48+00:00" 688 "time": "2018-10-06T14:43:38+00:00"
598 }, 689 },
599 { 690 {
600 "name": "slim/slim", 691 "name": "slim/slim",
601 "version": "3.9.2", 692 "version": "3.12.0",
602 "source": { 693 "source": {
603 "type": "git", 694 "type": "git",
604 "url": "https://github.com/slimphp/Slim.git", 695 "url": "https://github.com/slimphp/Slim.git",
605 "reference": "4086d0106cf5a7135c69fce4161fe355a8feb118" 696 "reference": "f4947cc900b6e51cbfda58b9f1247bca2f76f9f0"
606 }, 697 },
607 "dist": { 698 "dist": {
608 "type": "zip", 699 "type": "zip",
609 "url": "https://api.github.com/repos/slimphp/Slim/zipball/4086d0106cf5a7135c69fce4161fe355a8feb118", 700 "url": "https://api.github.com/repos/slimphp/Slim/zipball/f4947cc900b6e51cbfda58b9f1247bca2f76f9f0",
610 "reference": "4086d0106cf5a7135c69fce4161fe355a8feb118", 701 "reference": "f4947cc900b6e51cbfda58b9f1247bca2f76f9f0",
611 "shasum": "" 702 "shasum": ""
612 }, 703 },
613 "require": { 704 "require": {
@@ -665,7 +756,7 @@
665 "micro", 756 "micro",
666 "router" 757 "router"
667 ], 758 ],
668 "time": "2017-11-26T19:13:09+00:00" 759 "time": "2019-01-15T13:21:25+00:00"
669 } 760 }
670 ], 761 ],
671 "packages-dev": [ 762 "packages-dev": [
@@ -769,46 +860,6 @@
769 "time": "2017-10-19T19:58:43+00:00" 860 "time": "2017-10-19T19:58:43+00:00"
770 }, 861 },
771 { 862 {
772 "name": "pdepend/pdepend",
773 "version": "2.5.2",
774 "source": {
775 "type": "git",
776 "url": "https://github.com/pdepend/pdepend.git",
777 "reference": "9daf26d0368d4a12bed1cacae1a9f3a6f0adf239"
778 },
779 "dist": {
780 "type": "zip",
781 "url": "https://api.github.com/repos/pdepend/pdepend/zipball/9daf26d0368d4a12bed1cacae1a9f3a6f0adf239",
782 "reference": "9daf26d0368d4a12bed1cacae1a9f3a6f0adf239",
783 "shasum": ""
784 },
785 "require": {
786 "php": ">=5.3.7",
787 "symfony/config": "^2.3.0|^3|^4",
788 "symfony/dependency-injection": "^2.3.0|^3|^4",
789 "symfony/filesystem": "^2.3.0|^3|^4"
790 },
791 "require-dev": {
792 "phpunit/phpunit": "^4.8|^5.7",
793 "squizlabs/php_codesniffer": "^2.0.0"
794 },
795 "bin": [
796 "src/bin/pdepend"
797 ],
798 "type": "library",
799 "autoload": {
800 "psr-4": {
801 "PDepend\\": "src/main/php/PDepend"
802 }
803 },
804 "notification-url": "https://packagist.org/downloads/",
805 "license": [
806 "BSD-3-Clause"
807 ],
808 "description": "Official version of pdepend to be handled with Composer",
809 "time": "2017-12-13T13:21:38+00:00"
810 },
811 {
812 "name": "phpdocumentor/reflection-common", 863 "name": "phpdocumentor/reflection-common",
813 "version": "1.0.1", 864 "version": "1.0.1",
814 "source": { 865 "source": {
@@ -955,100 +1006,34 @@
955 "time": "2017-07-14T14:27:02+00:00" 1006 "time": "2017-07-14T14:27:02+00:00"
956 }, 1007 },
957 { 1008 {
958 "name": "phpmd/phpmd",
959 "version": "2.6.0",
960 "source": {
961 "type": "git",
962 "url": "https://github.com/phpmd/phpmd.git",
963 "reference": "4e9924b2c157a3eb64395460fcf56b31badc8374"
964 },
965 "dist": {
966 "type": "zip",
967 "url": "https://api.github.com/repos/phpmd/phpmd/zipball/4e9924b2c157a3eb64395460fcf56b31badc8374",
968 "reference": "4e9924b2c157a3eb64395460fcf56b31badc8374",
969 "shasum": ""
970 },
971 "require": {
972 "ext-xml": "*",
973 "pdepend/pdepend": "^2.5",
974 "php": ">=5.3.9"
975 },
976 "require-dev": {
977 "phpunit/phpunit": "^4.0",
978 "squizlabs/php_codesniffer": "^2.0"
979 },
980 "bin": [
981 "src/bin/phpmd"
982 ],
983 "type": "project",
984 "autoload": {
985 "psr-0": {
986 "PHPMD\\": "src/main/php"
987 }
988 },
989 "notification-url": "https://packagist.org/downloads/",
990 "license": [
991 "BSD-3-Clause"
992 ],
993 "authors": [
994 {
995 "name": "Manuel Pichler",
996 "email": "github@manuel-pichler.de",
997 "homepage": "https://github.com/manuelpichler",
998 "role": "Project Founder"
999 },
1000 {
1001 "name": "Other contributors",
1002 "homepage": "https://github.com/phpmd/phpmd/graphs/contributors",
1003 "role": "Contributors"
1004 },
1005 {
1006 "name": "Marc Würth",
1007 "email": "ravage@bluewin.ch",
1008 "homepage": "https://github.com/ravage84",
1009 "role": "Project Maintainer"
1010 }
1011 ],
1012 "description": "PHPMD is a spin-off project of PHP Depend and aims to be a PHP equivalent of the well known Java tool PMD.",
1013 "homepage": "http://phpmd.org/",
1014 "keywords": [
1015 "mess detection",
1016 "mess detector",
1017 "pdepend",
1018 "phpmd",
1019 "pmd"
1020 ],
1021 "time": "2017-01-20T14:41:10+00:00"
1022 },
1023 {
1024 "name": "phpspec/prophecy", 1009 "name": "phpspec/prophecy",
1025 "version": "1.7.5", 1010 "version": "1.8.0",
1026 "source": { 1011 "source": {
1027 "type": "git", 1012 "type": "git",
1028 "url": "https://github.com/phpspec/prophecy.git", 1013 "url": "https://github.com/phpspec/prophecy.git",
1029 "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401" 1014 "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06"
1030 }, 1015 },
1031 "dist": { 1016 "dist": {
1032 "type": "zip", 1017 "type": "zip",
1033 "url": "https://api.github.com/repos/phpspec/prophecy/zipball/dfd6be44111a7c41c2e884a336cc4f461b3b2401", 1018 "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4ba436b55987b4bf311cb7c6ba82aa528aac0a06",
1034 "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401", 1019 "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06",
1035 "shasum": "" 1020 "shasum": ""
1036 }, 1021 },
1037 "require": { 1022 "require": {
1038 "doctrine/instantiator": "^1.0.2", 1023 "doctrine/instantiator": "^1.0.2",
1039 "php": "^5.3|^7.0", 1024 "php": "^5.3|^7.0",
1040 "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", 1025 "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0",
1041 "sebastian/comparator": "^1.1|^2.0", 1026 "sebastian/comparator": "^1.1|^2.0|^3.0",
1042 "sebastian/recursion-context": "^1.0|^2.0|^3.0" 1027 "sebastian/recursion-context": "^1.0|^2.0|^3.0"
1043 }, 1028 },
1044 "require-dev": { 1029 "require-dev": {
1045 "phpspec/phpspec": "^2.5|^3.2", 1030 "phpspec/phpspec": "^2.5|^3.2",
1046 "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" 1031 "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1"
1047 }, 1032 },
1048 "type": "library", 1033 "type": "library",
1049 "extra": { 1034 "extra": {
1050 "branch-alias": { 1035 "branch-alias": {
1051 "dev-master": "1.7.x-dev" 1036 "dev-master": "1.8.x-dev"
1052 } 1037 }
1053 }, 1038 },
1054 "autoload": { 1039 "autoload": {
@@ -1081,7 +1066,7 @@
1081 "spy", 1066 "spy",
1082 "stub" 1067 "stub"
1083 ], 1068 ],
1084 "time": "2018-02-19T10:16:54+00:00" 1069 "time": "2018-08-05T17:53:17+00:00"
1085 }, 1070 },
1086 { 1071 {
1087 "name": "phpunit/php-code-coverage", 1072 "name": "phpunit/php-code-coverage",
@@ -1194,47 +1179,6 @@
1194 "time": "2017-11-27T13:52:08+00:00" 1179 "time": "2017-11-27T13:52:08+00:00"
1195 }, 1180 },
1196 { 1181 {
1197 "name": "phpunit/php-text-template",
1198 "version": "1.2.1",
1199 "source": {
1200 "type": "git",
1201 "url": "https://github.com/sebastianbergmann/php-text-template.git",
1202 "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686"
1203 },
1204 "dist": {
1205 "type": "zip",
1206 "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
1207 "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
1208 "shasum": ""
1209 },
1210 "require": {
1211 "php": ">=5.3.3"
1212 },
1213 "type": "library",
1214 "autoload": {
1215 "classmap": [
1216 "src/"
1217 ]
1218 },
1219 "notification-url": "https://packagist.org/downloads/",
1220 "license": [
1221 "BSD-3-Clause"
1222 ],
1223 "authors": [
1224 {
1225 "name": "Sebastian Bergmann",
1226 "email": "sebastian@phpunit.de",
1227 "role": "lead"
1228 }
1229 ],
1230 "description": "Simple template engine.",
1231 "homepage": "https://github.com/sebastianbergmann/php-text-template/",
1232 "keywords": [
1233 "template"
1234 ],
1235 "time": "2015-06-21T13:50:34+00:00"
1236 },
1237 {
1238 "name": "phpunit/php-timer", 1182 "name": "phpunit/php-timer",
1239 "version": "1.0.9", 1183 "version": "1.0.9",
1240 "source": { 1184 "source": {
@@ -1526,6 +1470,210 @@
1526 "time": "2017-06-30T09:13:00+00:00" 1470 "time": "2017-06-30T09:13:00+00:00"
1527 }, 1471 },
1528 { 1472 {
1473 "name": "roave/security-advisories",
1474 "version": "dev-master",
1475 "source": {
1476 "type": "git",
1477 "url": "https://github.com/Roave/SecurityAdvisories.git",
1478 "reference": "d155baccb43ba2542941fbcba258b85ce7786419"
1479 },
1480 "dist": {
1481 "type": "zip",
1482 "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/d155baccb43ba2542941fbcba258b85ce7786419",
1483 "reference": "d155baccb43ba2542941fbcba258b85ce7786419",
1484 "shasum": ""
1485 },
1486 "conflict": {
1487 "3f/pygmentize": "<1.2",
1488 "adodb/adodb-php": "<5.20.12",
1489 "alterphp/easyadmin-extension-bundle": ">=1.2,<1.2.11|>=1.3,<1.3.1",
1490 "amphp/artax": "<1.0.6|>=2,<2.0.6",
1491 "amphp/http": "<1.0.1",
1492 "api-platform/core": ">=2.2,<2.2.10|>=2.3,<2.3.6",
1493 "asymmetricrypt/asymmetricrypt": ">=0,<9.9.99",
1494 "aws/aws-sdk-php": ">=3,<3.2.1",
1495 "brightlocal/phpwhois": "<=4.2.5",
1496 "bugsnag/bugsnag-laravel": ">=2,<2.0.2",
1497 "cakephp/cakephp": ">=1.3,<1.3.18|>=2,<2.4.99|>=2.5,<2.5.99|>=2.6,<2.6.12|>=2.7,<2.7.6|>=3,<3.0.15|>=3.1,<3.1.4|>=3.4,<3.4.14|>=3.5,<3.5.17|>=3.6,<3.6.4",
1498 "cart2quote/module-quotation": ">=4.1.6,<=4.4.5|>=5,<5.4.4",
1499 "cartalyst/sentry": "<=2.1.6",
1500 "codeigniter/framework": "<=3.0.6",
1501 "composer/composer": "<=1.0.0-alpha11",
1502 "contao-components/mediaelement": ">=2.14.2,<2.21.1",
1503 "contao/core": ">=2,<3.5.35",
1504 "contao/core-bundle": ">=4,<4.4.18|>=4.5,<4.5.8",
1505 "contao/listing-bundle": ">=4,<4.4.8",
1506 "contao/newsletter-bundle": ">=4,<4.1",
1507 "david-garcia/phpwhois": "<=4.3.1",
1508 "doctrine/annotations": ">=1,<1.2.7",
1509 "doctrine/cache": ">=1,<1.3.2|>=1.4,<1.4.2",
1510 "doctrine/common": ">=2,<2.4.3|>=2.5,<2.5.1",
1511 "doctrine/dbal": ">=2,<2.0.8|>=2.1,<2.1.2",
1512 "doctrine/doctrine-bundle": "<1.5.2",
1513 "doctrine/doctrine-module": "<=0.7.1",
1514 "doctrine/mongodb-odm": ">=1,<1.0.2",
1515 "doctrine/mongodb-odm-bundle": ">=2,<3.0.1",
1516 "doctrine/orm": ">=2,<2.4.8|>=2.5,<2.5.1",
1517 "dompdf/dompdf": ">=0.6,<0.6.2",
1518 "drupal/core": ">=7,<7.60|>=8,<8.5.8|>=8.6,<8.6.2",
1519 "drupal/drupal": ">=7,<7.60|>=8,<8.5.8|>=8.6,<8.6.2",
1520 "erusev/parsedown": "<1.7",
1521 "ezsystems/ezpublish-kernel": ">=5.3,<5.3.12.1|>=5.4,<5.4.13.1|>=6,<6.7.9.1|>=6.8,<6.13.5.1|>=7,<7.2.4.1|>=7.3,<7.3.2.1",
1522 "ezsystems/ezpublish-legacy": ">=5.3,<5.3.12.6|>=5.4,<5.4.12.3|>=2011,<2017.12.4.3|>=2018.6,<2018.6.1.4|>=2018.9,<2018.9.1.3",
1523 "ezsystems/repository-forms": ">=2.3,<2.3.2.1",
1524 "ezyang/htmlpurifier": "<4.1.1",
1525 "firebase/php-jwt": "<2",
1526 "fooman/tcpdf": "<6.2.22",
1527 "fossar/tcpdf-parser": "<6.2.22",
1528 "friendsofsymfony/rest-bundle": ">=1.2,<1.2.2",
1529 "friendsofsymfony/user-bundle": ">=1.2,<1.3.5",
1530 "fuel/core": "<1.8.1",
1531 "gree/jose": "<=2.2",
1532 "gregwar/rst": "<1.0.3",
1533 "guzzlehttp/guzzle": ">=6,<6.2.1|>=4.0.0-rc2,<4.2.4|>=5,<5.3.1",
1534 "illuminate/auth": ">=4,<4.0.99|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.10",
1535 "illuminate/cookie": ">=4,<=4.0.11|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.42|>=5.6,<5.6.30",
1536 "illuminate/database": ">=4,<4.0.99|>=4.1,<4.1.29",
1537 "illuminate/encryption": ">=4,<=4.0.11|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.40|>=5.6,<5.6.15",
1538 "ivankristianto/phpwhois": "<=4.3",
1539 "james-heinrich/getid3": "<1.9.9",
1540 "joomla/session": "<1.3.1",
1541 "jsmitty12/phpwhois": "<5.1",
1542 "kazist/phpwhois": "<=4.2.6",
1543 "kreait/firebase-php": ">=3.2,<3.8.1",
1544 "la-haute-societe/tcpdf": "<6.2.22",
1545 "laravel/framework": ">=4,<4.0.99|>=4.1,<=4.1.31|>=4.2,<=4.2.22|>=5,<=5.0.35|>=5.1,<=5.1.46|>=5.2,<=5.2.45|>=5.3,<=5.3.31|>=5.4,<=5.4.36|>=5.5,<5.5.42|>=5.6,<5.6.30",
1546 "laravel/socialite": ">=1,<1.0.99|>=2,<2.0.10",
1547 "league/commonmark": ">=0.15.6,<0.18.1",
1548 "magento/magento1ce": "<1.9.4",
1549 "magento/magento1ee": ">=1.9,<1.14.4",
1550 "magento/product-community-edition": ">=2,<2.2.7",
1551 "monolog/monolog": ">=1.8,<1.12",
1552 "namshi/jose": "<2.2",
1553 "onelogin/php-saml": "<2.10.4",
1554 "openid/php-openid": "<2.3",
1555 "oro/crm": ">=1.7,<1.7.4",
1556 "oro/platform": ">=1.7,<1.7.4",
1557 "padraic/humbug_get_contents": "<1.1.2",
1558 "pagarme/pagarme-php": ">=0,<3",
1559 "paragonie/random_compat": "<2",
1560 "paypal/merchant-sdk-php": "<3.12",
1561 "pear/archive_tar": "<1.4.4",
1562 "phpmailer/phpmailer": ">=5,<5.2.27|>=6,<6.0.6",
1563 "phpoffice/phpexcel": "<=1.8.1",
1564 "phpoffice/phpspreadsheet": "<=1.5",
1565 "phpunit/phpunit": ">=4.8.19,<4.8.28|>=5.0.10,<5.6.3",
1566 "phpwhois/phpwhois": "<=4.2.5",
1567 "phpxmlrpc/extras": "<0.6.1",
1568 "propel/propel": ">=2.0.0-alpha1,<=2.0.0-alpha7",
1569 "propel/propel1": ">=1,<=1.7.1",
1570 "pusher/pusher-php-server": "<2.2.1",
1571 "robrichards/xmlseclibs": ">=1,<3.0.2",
1572 "sabre/dav": ">=1.6,<1.6.99|>=1.7,<1.7.11|>=1.8,<1.8.9",
1573 "sensiolabs/connect": "<4.2.3",
1574 "serluck/phpwhois": "<=4.2.6",
1575 "shopware/shopware": "<5.3.7",
1576 "silverstripe/cms": ">=3,<=3.0.11|>=3.1,<3.1.11",
1577 "silverstripe/forum": "<=0.6.1|>=0.7,<=0.7.3",
1578 "silverstripe/framework": ">=3,<3.3",
1579 "silverstripe/userforms": "<3",
1580 "simple-updates/phpwhois": "<=1",
1581 "simplesamlphp/saml2": "<1.10.6|>=2,<2.3.8|>=3,<3.1.4",
1582 "simplesamlphp/simplesamlphp": "<1.16.3",
1583 "simplesamlphp/simplesamlphp-module-infocard": "<1.0.1",
1584 "slim/slim": "<2.6",
1585 "smarty/smarty": "<3.1.33",
1586 "socalnick/scn-social-auth": "<1.15.2",
1587 "spoonity/tcpdf": "<6.2.22",
1588 "squizlabs/php_codesniffer": ">=1,<2.8.1|>=3,<3.0.1",
1589 "stormpath/sdk": ">=0,<9.9.99",
1590 "swiftmailer/swiftmailer": ">=4,<5.4.5",
1591 "sylius/admin-bundle": ">=1,<1.0.17|>=1.1,<1.1.9|>=1.2,<1.2.2",
1592 "sylius/sylius": ">=1,<1.0.17|>=1.1,<1.1.9|>=1.2,<1.2.2",
1593 "symfony/dependency-injection": ">=2,<2.0.17",
1594 "symfony/form": ">=2.3,<2.3.35|>=2.4,<2.6.12|>=2.7,<2.7.50|>=2.8,<2.8.49|>=3,<3.4.20|>=4,<4.0.15|>=4.1,<4.1.9|>=4.2,<4.2.1",
1595 "symfony/framework-bundle": ">=2,<2.3.18|>=2.4,<2.4.8|>=2.5,<2.5.2",
1596 "symfony/http-foundation": ">=2,<2.7.49|>=2.8,<2.8.44|>=3,<3.3.18|>=3.4,<3.4.14|>=4,<4.0.14|>=4.1,<4.1.3",
1597 "symfony/http-kernel": ">=2,<2.3.29|>=2.4,<2.5.12|>=2.6,<2.6.8",
1598 "symfony/intl": ">=2.7,<2.7.38|>=2.8,<2.8.31|>=3,<3.2.14|>=3.3,<3.3.13",
1599 "symfony/polyfill": ">=1,<1.10",
1600 "symfony/polyfill-php55": ">=1,<1.10",
1601 "symfony/routing": ">=2,<2.0.19",
1602 "symfony/security": ">=2,<2.7.50|>=2.8,<2.8.49|>=3,<3.4.19|>=4,<4.0.15|>=4.1,<4.1.9|>=4.2,<4.2.1",
1603 "symfony/security-bundle": ">=2,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11",
1604 "symfony/security-core": ">=2.4,<2.6.13|>=2.7,<2.7.9|>=2.7.30,<2.7.32|>=2.8,<2.8.37|>=3,<3.3.17|>=3.4,<3.4.7|>=4,<4.0.7",
1605 "symfony/security-csrf": ">=2.4,<2.7.48|>=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11",
1606 "symfony/security-guard": ">=2.8,<2.8.41|>=3,<3.3.17|>=3.4,<3.4.11|>=4,<4.0.11",
1607 "symfony/security-http": ">=2.3,<2.3.41|>=2.4,<2.7.50|>=2.8,<2.8.49|>=3,<3.4.20|>=4,<4.0.15|>=4.1,<4.1.9|>=4.2,<4.2.1",
1608 "symfony/serializer": ">=2,<2.0.11",
1609 "symfony/symfony": ">=2,<2.7.50|>=2.8,<2.8.49|>=3,<3.4.20|>=4,<4.0.15|>=4.1,<4.1.9|>=4.2,<4.2.1",
1610 "symfony/translation": ">=2,<2.0.17",
1611 "symfony/validator": ">=2,<2.0.24|>=2.1,<2.1.12|>=2.2,<2.2.5|>=2.3,<2.3.3",
1612 "symfony/web-profiler-bundle": ">=2,<2.3.19|>=2.4,<2.4.9|>=2.5,<2.5.4",
1613 "symfony/yaml": ">=2,<2.0.22|>=2.1,<2.1.7",
1614 "tecnickcom/tcpdf": "<6.2.22",
1615 "thelia/backoffice-default-template": ">=2.1,<2.1.2",
1616 "thelia/thelia": ">=2.1.0-beta1,<2.1.3|>=2.1,<2.1.2",
1617 "theonedemon/phpwhois": "<=4.2.5",
1618 "titon/framework": ">=0,<9.9.99",
1619 "truckersmp/phpwhois": "<=4.3.1",
1620 "twig/twig": "<1.20",
1621 "typo3/cms": ">=6.2,<6.2.30|>=7,<7.6.32|>=8,<8.7.21|>=9,<9.5.2",
1622 "typo3/cms-core": ">=8,<8.7.21|>=9,<9.5.2",
1623 "typo3/flow": ">=1,<1.0.4|>=1.1,<1.1.1|>=2,<2.0.1|>=2.3,<2.3.16|>=3,<3.0.10|>=3.1,<3.1.7|>=3.2,<3.2.7|>=3.3,<3.3.5",
1624 "typo3/neos": ">=1.1,<1.1.3|>=1.2,<1.2.13|>=2,<2.0.4",
1625 "ua-parser/uap-php": "<3.8",
1626 "wallabag/tcpdf": "<6.2.22",
1627 "willdurand/js-translation-bundle": "<2.1.1",
1628 "yiisoft/yii": ">=1.1.14,<1.1.15",
1629 "yiisoft/yii2": "<2.0.15",
1630 "yiisoft/yii2-bootstrap": "<2.0.4",
1631 "yiisoft/yii2-dev": "<2.0.15",
1632 "yiisoft/yii2-elasticsearch": "<2.0.5",
1633 "yiisoft/yii2-gii": "<2.0.4",
1634 "yiisoft/yii2-jui": "<2.0.4",
1635 "yiisoft/yii2-redis": "<2.0.8",
1636 "zendframework/zend-cache": ">=2.4,<2.4.8|>=2.5,<2.5.3",
1637 "zendframework/zend-captcha": ">=2,<2.4.9|>=2.5,<2.5.2",
1638 "zendframework/zend-crypt": ">=2,<2.4.9|>=2.5,<2.5.2",
1639 "zendframework/zend-db": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.10|>=2.3,<2.3.5",
1640 "zendframework/zend-diactoros": ">=1,<1.8.4",
1641 "zendframework/zend-feed": ">=1,<2.10.3",
1642 "zendframework/zend-form": ">=2,<2.2.7|>=2.3,<2.3.1",
1643 "zendframework/zend-http": ">=1,<2.8.1",
1644 "zendframework/zend-json": ">=2.1,<2.1.6|>=2.2,<2.2.6",
1645 "zendframework/zend-ldap": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.8|>=2.3,<2.3.3",
1646 "zendframework/zend-mail": ">=2,<2.4.11|>=2.5,<2.7.2",
1647 "zendframework/zend-navigation": ">=2,<2.2.7|>=2.3,<2.3.1",
1648 "zendframework/zend-session": ">=2,<2.0.99|>=2.1,<2.1.99|>=2.2,<2.2.9|>=2.3,<2.3.4",
1649 "zendframework/zend-validator": ">=2.3,<2.3.6",
1650 "zendframework/zend-view": ">=2,<2.2.7|>=2.3,<2.3.1",
1651 "zendframework/zend-xmlrpc": ">=2.1,<2.1.6|>=2.2,<2.2.6",
1652 "zendframework/zendframework": "<2.5.1",
1653 "zendframework/zendframework1": "<1.12.20",
1654 "zendframework/zendopenid": ">=2,<2.0.2",
1655 "zendframework/zendxml": ">=1,<1.0.1",
1656 "zetacomponents/mail": "<1.8.2",
1657 "zf-commons/zfc-user": "<1.2.2",
1658 "zfcampus/zf-apigility-doctrine": ">=1,<1.0.3",
1659 "zfr/zfr-oauth2-server-module": "<0.1.2"
1660 },
1661 "type": "metapackage",
1662 "notification-url": "https://packagist.org/downloads/",
1663 "license": [
1664 "MIT"
1665 ],
1666 "authors": [
1667 {
1668 "name": "Marco Pivetta",
1669 "email": "ocramius@gmail.com",
1670 "role": "maintainer"
1671 }
1672 ],
1673 "description": "Prevents installation of composer packages with known security vulnerabilities: no API, simply require it",
1674 "time": "2019-01-15T19:39:37+00:00"
1675 },
1676 {
1529 "name": "sebastian/code-unit-reverse-lookup", 1677 "name": "sebastian/code-unit-reverse-lookup",
1530 "version": "1.0.1", 1678 "version": "1.0.1",
1531 "source": { 1679 "source": {
@@ -1940,56 +2088,6 @@
1940 "time": "2017-02-18T15:18:39+00:00" 2088 "time": "2017-02-18T15:18:39+00:00"
1941 }, 2089 },
1942 { 2090 {
1943 "name": "sebastian/phpcpd",
1944 "version": "3.0.1",
1945 "source": {
1946 "type": "git",
1947 "url": "https://github.com/sebastianbergmann/phpcpd.git",
1948 "reference": "dfed51c1288790fc957c9433e2f49ab152e8a564"
1949 },
1950 "dist": {
1951 "type": "zip",
1952 "url": "https://api.github.com/repos/sebastianbergmann/phpcpd/zipball/dfed51c1288790fc957c9433e2f49ab152e8a564",
1953 "reference": "dfed51c1288790fc957c9433e2f49ab152e8a564",
1954 "shasum": ""
1955 },
1956 "require": {
1957 "php": "^5.6|^7.0",
1958 "phpunit/php-timer": "^1.0.6",
1959 "sebastian/finder-facade": "^1.1",
1960 "sebastian/version": "^1.0|^2.0",
1961 "symfony/console": "^2.7|^3.0|^4.0"
1962 },
1963 "bin": [
1964 "phpcpd"
1965 ],
1966 "type": "library",
1967 "extra": {
1968 "branch-alias": {
1969 "dev-master": "3.0-dev"
1970 }
1971 },
1972 "autoload": {
1973 "classmap": [
1974 "src/"
1975 ]
1976 },
1977 "notification-url": "https://packagist.org/downloads/",
1978 "license": [
1979 "BSD-3-Clause"
1980 ],
1981 "authors": [
1982 {
1983 "name": "Sebastian Bergmann",
1984 "email": "sebastian@phpunit.de",
1985 "role": "lead"
1986 }
1987 ],
1988 "description": "Copy/Paste Detector (CPD) for PHP code.",
1989 "homepage": "https://github.com/sebastianbergmann/phpcpd",
1990 "time": "2017-11-16T08:49:28+00:00"
1991 },
1992 {
1993 "name": "sebastian/recursion-context", 2091 "name": "sebastian/recursion-context",
1994 "version": "2.0.0", 2092 "version": "2.0.0",
1995 "source": { 2093 "source": {
@@ -2129,16 +2227,16 @@
2129 }, 2227 },
2130 { 2228 {
2131 "name": "squizlabs/php_codesniffer", 2229 "name": "squizlabs/php_codesniffer",
2132 "version": "2.9.1", 2230 "version": "2.9.2",
2133 "source": { 2231 "source": {
2134 "type": "git", 2232 "type": "git",
2135 "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", 2233 "url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
2136 "reference": "dcbed1074f8244661eecddfc2a675430d8d33f62" 2234 "reference": "2acf168de78487db620ab4bc524135a13cfe6745"
2137 }, 2235 },
2138 "dist": { 2236 "dist": {
2139 "type": "zip", 2237 "type": "zip",
2140 "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/dcbed1074f8244661eecddfc2a675430d8d33f62", 2238 "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/2acf168de78487db620ab4bc524135a13cfe6745",
2141 "reference": "dcbed1074f8244661eecddfc2a675430d8d33f62", 2239 "reference": "2acf168de78487db620ab4bc524135a13cfe6745",
2142 "shasum": "" 2240 "shasum": ""
2143 }, 2241 },
2144 "require": { 2242 "require": {
@@ -2203,83 +2301,20 @@
2203 "phpcs", 2301 "phpcs",
2204 "standards" 2302 "standards"
2205 ], 2303 ],
2206 "time": "2017-05-22T02:43:20+00:00" 2304 "time": "2018-11-07T22:31:41+00:00"
2207 },
2208 {
2209 "name": "symfony/config",
2210 "version": "v3.4.6",
2211 "source": {
2212 "type": "git",
2213 "url": "https://github.com/symfony/config.git",
2214 "reference": "05e10567b529476a006b00746c5f538f1636810e"
2215 },
2216 "dist": {
2217 "type": "zip",
2218 "url": "https://api.github.com/repos/symfony/config/zipball/05e10567b529476a006b00746c5f538f1636810e",
2219 "reference": "05e10567b529476a006b00746c5f538f1636810e",
2220 "shasum": ""
2221 },
2222 "require": {
2223 "php": "^5.5.9|>=7.0.8",
2224 "symfony/filesystem": "~2.8|~3.0|~4.0"
2225 },
2226 "conflict": {
2227 "symfony/dependency-injection": "<3.3",
2228 "symfony/finder": "<3.3"
2229 },
2230 "require-dev": {
2231 "symfony/dependency-injection": "~3.3|~4.0",
2232 "symfony/event-dispatcher": "~3.3|~4.0",
2233 "symfony/finder": "~3.3|~4.0",
2234 "symfony/yaml": "~3.0|~4.0"
2235 },
2236 "suggest": {
2237 "symfony/yaml": "To use the yaml reference dumper"
2238 },
2239 "type": "library",
2240 "extra": {
2241 "branch-alias": {
2242 "dev-master": "3.4-dev"
2243 }
2244 },
2245 "autoload": {
2246 "psr-4": {
2247 "Symfony\\Component\\Config\\": ""
2248 },
2249 "exclude-from-classmap": [
2250 "/Tests/"
2251 ]
2252 },
2253 "notification-url": "https://packagist.org/downloads/",
2254 "license": [
2255 "MIT"
2256 ],
2257 "authors": [
2258 {
2259 "name": "Fabien Potencier",
2260 "email": "fabien@symfony.com"
2261 },
2262 {
2263 "name": "Symfony Community",
2264 "homepage": "https://symfony.com/contributors"
2265 }
2266 ],
2267 "description": "Symfony Config Component",
2268 "homepage": "https://symfony.com",
2269 "time": "2018-02-14T10:03:57+00:00"
2270 }, 2305 },
2271 { 2306 {
2272 "name": "symfony/console", 2307 "name": "symfony/console",
2273 "version": "v3.4.6", 2308 "version": "v3.4.21",
2274 "source": { 2309 "source": {
2275 "type": "git", 2310 "type": "git",
2276 "url": "https://github.com/symfony/console.git", 2311 "url": "https://github.com/symfony/console.git",
2277 "reference": "067339e9b8ec30d5f19f5950208893ff026b94f7" 2312 "reference": "a700b874d3692bc8342199adfb6d3b99f62cc61a"
2278 }, 2313 },
2279 "dist": { 2314 "dist": {
2280 "type": "zip", 2315 "type": "zip",
2281 "url": "https://api.github.com/repos/symfony/console/zipball/067339e9b8ec30d5f19f5950208893ff026b94f7", 2316 "url": "https://api.github.com/repos/symfony/console/zipball/a700b874d3692bc8342199adfb6d3b99f62cc61a",
2282 "reference": "067339e9b8ec30d5f19f5950208893ff026b94f7", 2317 "reference": "a700b874d3692bc8342199adfb6d3b99f62cc61a",
2283 "shasum": "" 2318 "shasum": ""
2284 }, 2319 },
2285 "require": { 2320 "require": {
@@ -2300,7 +2335,7 @@
2300 "symfony/process": "~3.3|~4.0" 2335 "symfony/process": "~3.3|~4.0"
2301 }, 2336 },
2302 "suggest": { 2337 "suggest": {
2303 "psr/log": "For using the console logger", 2338 "psr/log-implementation": "For using the console logger",
2304 "symfony/event-dispatcher": "", 2339 "symfony/event-dispatcher": "",
2305 "symfony/lock": "", 2340 "symfony/lock": "",
2306 "symfony/process": "" 2341 "symfony/process": ""
@@ -2335,20 +2370,20 @@
2335 ], 2370 ],
2336 "description": "Symfony Console Component", 2371 "description": "Symfony Console Component",
2337 "homepage": "https://symfony.com", 2372 "homepage": "https://symfony.com",
2338 "time": "2018-02-26T15:46:28+00:00" 2373 "time": "2019-01-04T04:42:43+00:00"
2339 }, 2374 },
2340 { 2375 {
2341 "name": "symfony/debug", 2376 "name": "symfony/debug",
2342 "version": "v3.4.6", 2377 "version": "v3.4.21",
2343 "source": { 2378 "source": {
2344 "type": "git", 2379 "type": "git",
2345 "url": "https://github.com/symfony/debug.git", 2380 "url": "https://github.com/symfony/debug.git",
2346 "reference": "9b1071f86e79e1999b3d3675d2e0e7684268b9bc" 2381 "reference": "26d7f23b9bd0b93bee5583e4d6ca5cb1ab31b186"
2347 }, 2382 },
2348 "dist": { 2383 "dist": {
2349 "type": "zip", 2384 "type": "zip",
2350 "url": "https://api.github.com/repos/symfony/debug/zipball/9b1071f86e79e1999b3d3675d2e0e7684268b9bc", 2385 "url": "https://api.github.com/repos/symfony/debug/zipball/26d7f23b9bd0b93bee5583e4d6ca5cb1ab31b186",
2351 "reference": "9b1071f86e79e1999b3d3675d2e0e7684268b9bc", 2386 "reference": "26d7f23b9bd0b93bee5583e4d6ca5cb1ab31b186",
2352 "shasum": "" 2387 "shasum": ""
2353 }, 2388 },
2354 "require": { 2389 "require": {
@@ -2391,46 +2426,24 @@
2391 ], 2426 ],
2392 "description": "Symfony Debug Component", 2427 "description": "Symfony Debug Component",
2393 "homepage": "https://symfony.com", 2428 "homepage": "https://symfony.com",
2394 "time": "2018-02-28T21:49:22+00:00" 2429 "time": "2019-01-01T13:45:19+00:00"
2395 }, 2430 },
2396 { 2431 {
2397 "name": "symfony/dependency-injection", 2432 "name": "symfony/finder",
2398 "version": "v3.4.6", 2433 "version": "v3.4.21",
2399 "source": { 2434 "source": {
2400 "type": "git", 2435 "type": "git",
2401 "url": "https://github.com/symfony/dependency-injection.git", 2436 "url": "https://github.com/symfony/finder.git",
2402 "reference": "12e901abc1cb0d637a0e5abe9923471361d96b07" 2437 "reference": "3f2a2ab6315dd7682d4c16dcae1e7b95c8b8555e"
2403 }, 2438 },
2404 "dist": { 2439 "dist": {
2405 "type": "zip", 2440 "type": "zip",
2406 "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/12e901abc1cb0d637a0e5abe9923471361d96b07", 2441 "url": "https://api.github.com/repos/symfony/finder/zipball/3f2a2ab6315dd7682d4c16dcae1e7b95c8b8555e",
2407 "reference": "12e901abc1cb0d637a0e5abe9923471361d96b07", 2442 "reference": "3f2a2ab6315dd7682d4c16dcae1e7b95c8b8555e",
2408 "shasum": "" 2443 "shasum": ""
2409 }, 2444 },
2410 "require": { 2445 "require": {
2411 "php": "^5.5.9|>=7.0.8", 2446 "php": "^5.5.9|>=7.0.8"
2412 "psr/container": "^1.0"
2413 },
2414 "conflict": {
2415 "symfony/config": "<3.3.7",
2416 "symfony/finder": "<3.3",
2417 "symfony/proxy-manager-bridge": "<3.4",
2418 "symfony/yaml": "<3.4"
2419 },
2420 "provide": {
2421 "psr/container-implementation": "1.0"
2422 },
2423 "require-dev": {
2424 "symfony/config": "~3.3|~4.0",
2425 "symfony/expression-language": "~2.8|~3.0|~4.0",
2426 "symfony/yaml": "~3.4|~4.0"
2427 },
2428 "suggest": {
2429 "symfony/config": "",
2430 "symfony/expression-language": "For using expressions in service container configuration",
2431 "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required",
2432 "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them",
2433 "symfony/yaml": ""
2434 }, 2447 },
2435 "type": "library", 2448 "type": "library",
2436 "extra": { 2449 "extra": {
@@ -2440,7 +2453,7 @@
2440 }, 2453 },
2441 "autoload": { 2454 "autoload": {
2442 "psr-4": { 2455 "psr-4": {
2443 "Symfony\\Component\\DependencyInjection\\": "" 2456 "Symfony\\Component\\Finder\\": ""
2444 }, 2457 },
2445 "exclude-from-classmap": [ 2458 "exclude-from-classmap": [
2446 "/Tests/" 2459 "/Tests/"
@@ -2460,39 +2473,42 @@
2460 "homepage": "https://symfony.com/contributors" 2473 "homepage": "https://symfony.com/contributors"
2461 } 2474 }
2462 ], 2475 ],
2463 "description": "Symfony DependencyInjection Component", 2476 "description": "Symfony Finder Component",
2464 "homepage": "https://symfony.com", 2477 "homepage": "https://symfony.com",
2465 "time": "2018-03-04T03:54:53+00:00" 2478 "time": "2019-01-01T13:45:19+00:00"
2466 }, 2479 },
2467 { 2480 {
2468 "name": "symfony/filesystem", 2481 "name": "symfony/polyfill-ctype",
2469 "version": "v3.4.6", 2482 "version": "v1.10.0",
2470 "source": { 2483 "source": {
2471 "type": "git", 2484 "type": "git",
2472 "url": "https://github.com/symfony/filesystem.git", 2485 "url": "https://github.com/symfony/polyfill-ctype.git",
2473 "reference": "253a4490b528597aa14d2bf5aeded6f5e5e4a541" 2486 "reference": "e3d826245268269cd66f8326bd8bc066687b4a19"
2474 }, 2487 },
2475 "dist": { 2488 "dist": {
2476 "type": "zip", 2489 "type": "zip",
2477 "url": "https://api.github.com/repos/symfony/filesystem/zipball/253a4490b528597aa14d2bf5aeded6f5e5e4a541", 2490 "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e3d826245268269cd66f8326bd8bc066687b4a19",
2478 "reference": "253a4490b528597aa14d2bf5aeded6f5e5e4a541", 2491 "reference": "e3d826245268269cd66f8326bd8bc066687b4a19",
2479 "shasum": "" 2492 "shasum": ""
2480 }, 2493 },
2481 "require": { 2494 "require": {
2482 "php": "^5.5.9|>=7.0.8" 2495 "php": ">=5.3.3"
2496 },
2497 "suggest": {
2498 "ext-ctype": "For best performance"
2483 }, 2499 },
2484 "type": "library", 2500 "type": "library",
2485 "extra": { 2501 "extra": {
2486 "branch-alias": { 2502 "branch-alias": {
2487 "dev-master": "3.4-dev" 2503 "dev-master": "1.9-dev"
2488 } 2504 }
2489 }, 2505 },
2490 "autoload": { 2506 "autoload": {
2491 "psr-4": { 2507 "psr-4": {
2492 "Symfony\\Component\\Filesystem\\": "" 2508 "Symfony\\Polyfill\\Ctype\\": ""
2493 }, 2509 },
2494 "exclude-from-classmap": [ 2510 "files": [
2495 "/Tests/" 2511 "bootstrap.php"
2496 ] 2512 ]
2497 }, 2513 },
2498 "notification-url": "https://packagist.org/downloads/", 2514 "notification-url": "https://packagist.org/downloads/",
@@ -2501,79 +2517,36 @@
2501 ], 2517 ],
2502 "authors": [ 2518 "authors": [
2503 { 2519 {
2504 "name": "Fabien Potencier",
2505 "email": "fabien@symfony.com"
2506 },
2507 {
2508 "name": "Symfony Community", 2520 "name": "Symfony Community",
2509 "homepage": "https://symfony.com/contributors" 2521 "homepage": "https://symfony.com/contributors"
2510 }
2511 ],
2512 "description": "Symfony Filesystem Component",
2513 "homepage": "https://symfony.com",
2514 "time": "2018-02-22T10:48:49+00:00"
2515 },
2516 {
2517 "name": "symfony/finder",
2518 "version": "v3.4.6",
2519 "source": {
2520 "type": "git",
2521 "url": "https://github.com/symfony/finder.git",
2522 "reference": "a479817ce0a9e4adfd7d39c6407c95d97c254625"
2523 },
2524 "dist": {
2525 "type": "zip",
2526 "url": "https://api.github.com/repos/symfony/finder/zipball/a479817ce0a9e4adfd7d39c6407c95d97c254625",
2527 "reference": "a479817ce0a9e4adfd7d39c6407c95d97c254625",
2528 "shasum": ""
2529 },
2530 "require": {
2531 "php": "^5.5.9|>=7.0.8"
2532 },
2533 "type": "library",
2534 "extra": {
2535 "branch-alias": {
2536 "dev-master": "3.4-dev"
2537 }
2538 },
2539 "autoload": {
2540 "psr-4": {
2541 "Symfony\\Component\\Finder\\": ""
2542 },
2543 "exclude-from-classmap": [
2544 "/Tests/"
2545 ]
2546 },
2547 "notification-url": "https://packagist.org/downloads/",
2548 "license": [
2549 "MIT"
2550 ],
2551 "authors": [
2552 {
2553 "name": "Fabien Potencier",
2554 "email": "fabien@symfony.com"
2555 }, 2522 },
2556 { 2523 {
2557 "name": "Symfony Community", 2524 "name": "Gert de Pagter",
2558 "homepage": "https://symfony.com/contributors" 2525 "email": "BackEndTea@gmail.com"
2559 } 2526 }
2560 ], 2527 ],
2561 "description": "Symfony Finder Component", 2528 "description": "Symfony polyfill for ctype functions",
2562 "homepage": "https://symfony.com", 2529 "homepage": "https://symfony.com",
2563 "time": "2018-03-05T18:28:11+00:00" 2530 "keywords": [
2531 "compatibility",
2532 "ctype",
2533 "polyfill",
2534 "portable"
2535 ],
2536 "time": "2018-08-06T14:22:27+00:00"
2564 }, 2537 },
2565 { 2538 {
2566 "name": "symfony/polyfill-mbstring", 2539 "name": "symfony/polyfill-mbstring",
2567 "version": "v1.7.0", 2540 "version": "v1.10.0",
2568 "source": { 2541 "source": {
2569 "type": "git", 2542 "type": "git",
2570 "url": "https://github.com/symfony/polyfill-mbstring.git", 2543 "url": "https://github.com/symfony/polyfill-mbstring.git",
2571 "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b" 2544 "reference": "c79c051f5b3a46be09205c73b80b346e4153e494"
2572 }, 2545 },
2573 "dist": { 2546 "dist": {
2574 "type": "zip", 2547 "type": "zip",
2575 "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/78be803ce01e55d3491c1397cf1c64beb9c1b63b", 2548 "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/c79c051f5b3a46be09205c73b80b346e4153e494",
2576 "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b", 2549 "reference": "c79c051f5b3a46be09205c73b80b346e4153e494",
2577 "shasum": "" 2550 "shasum": ""
2578 }, 2551 },
2579 "require": { 2552 "require": {
@@ -2585,7 +2558,7 @@
2585 "type": "library", 2558 "type": "library",
2586 "extra": { 2559 "extra": {
2587 "branch-alias": { 2560 "branch-alias": {
2588 "dev-master": "1.7-dev" 2561 "dev-master": "1.9-dev"
2589 } 2562 }
2590 }, 2563 },
2591 "autoload": { 2564 "autoload": {
@@ -2619,24 +2592,25 @@
2619 "portable", 2592 "portable",
2620 "shim" 2593 "shim"
2621 ], 2594 ],
2622 "time": "2018-01-30T19:27:44+00:00" 2595 "time": "2018-09-21T13:07:52+00:00"
2623 }, 2596 },
2624 { 2597 {
2625 "name": "symfony/yaml", 2598 "name": "symfony/yaml",
2626 "version": "v3.4.6", 2599 "version": "v3.4.21",
2627 "source": { 2600 "source": {
2628 "type": "git", 2601 "type": "git",
2629 "url": "https://github.com/symfony/yaml.git", 2602 "url": "https://github.com/symfony/yaml.git",
2630 "reference": "6af42631dcf89e9c616242c900d6c52bd53bd1bb" 2603 "reference": "554a59a1ccbaac238a89b19c8e551a556fd0e2ea"
2631 }, 2604 },
2632 "dist": { 2605 "dist": {
2633 "type": "zip", 2606 "type": "zip",
2634 "url": "https://api.github.com/repos/symfony/yaml/zipball/6af42631dcf89e9c616242c900d6c52bd53bd1bb", 2607 "url": "https://api.github.com/repos/symfony/yaml/zipball/554a59a1ccbaac238a89b19c8e551a556fd0e2ea",
2635 "reference": "6af42631dcf89e9c616242c900d6c52bd53bd1bb", 2608 "reference": "554a59a1ccbaac238a89b19c8e551a556fd0e2ea",
2636 "shasum": "" 2609 "shasum": ""
2637 }, 2610 },
2638 "require": { 2611 "require": {
2639 "php": "^5.5.9|>=7.0.8" 2612 "php": "^5.5.9|>=7.0.8",
2613 "symfony/polyfill-ctype": "~1.8"
2640 }, 2614 },
2641 "conflict": { 2615 "conflict": {
2642 "symfony/console": "<3.4" 2616 "symfony/console": "<3.4"
@@ -2677,7 +2651,7 @@
2677 ], 2651 ],
2678 "description": "Symfony Yaml Component", 2652 "description": "Symfony Yaml Component",
2679 "homepage": "https://symfony.com", 2653 "homepage": "https://symfony.com",
2680 "time": "2018-02-16T09:50:28+00:00" 2654 "time": "2019-01-01T13:45:19+00:00"
2681 }, 2655 },
2682 { 2656 {
2683 "name": "theseer/fdomdocument", 2657 "name": "theseer/fdomdocument",
@@ -2721,20 +2695,21 @@
2721 }, 2695 },
2722 { 2696 {
2723 "name": "webmozart/assert", 2697 "name": "webmozart/assert",
2724 "version": "1.3.0", 2698 "version": "1.4.0",
2725 "source": { 2699 "source": {
2726 "type": "git", 2700 "type": "git",
2727 "url": "https://github.com/webmozart/assert.git", 2701 "url": "https://github.com/webmozart/assert.git",
2728 "reference": "0df1908962e7a3071564e857d86874dad1ef204a" 2702 "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9"
2729 }, 2703 },
2730 "dist": { 2704 "dist": {
2731 "type": "zip", 2705 "type": "zip",
2732 "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a", 2706 "url": "https://api.github.com/repos/webmozart/assert/zipball/83e253c8e0be5b0257b881e1827274667c5c17a9",
2733 "reference": "0df1908962e7a3071564e857d86874dad1ef204a", 2707 "reference": "83e253c8e0be5b0257b881e1827274667c5c17a9",
2734 "shasum": "" 2708 "shasum": ""
2735 }, 2709 },
2736 "require": { 2710 "require": {
2737 "php": "^5.3.3 || ^7.0" 2711 "php": "^5.3.3 || ^7.0",
2712 "symfony/polyfill-ctype": "^1.8"
2738 }, 2713 },
2739 "require-dev": { 2714 "require-dev": {
2740 "phpunit/phpunit": "^4.6", 2715 "phpunit/phpunit": "^4.6",
@@ -2767,19 +2742,21 @@
2767 "check", 2742 "check",
2768 "validate" 2743 "validate"
2769 ], 2744 ],
2770 "time": "2018-01-29T19:49:41+00:00" 2745 "time": "2018-12-25T11:19:39+00:00"
2771 } 2746 }
2772 ], 2747 ],
2773 "aliases": [], 2748 "aliases": [],
2774 "minimum-stability": "stable", 2749 "minimum-stability": "stable",
2775 "stability-flags": { 2750 "stability-flags": {
2776 "pubsubhubbub/publisher": 20, 2751 "pubsubhubbub/publisher": 20,
2777 "phpmd/phpmd": 0 2752 "roave/security-advisories": 20
2778 }, 2753 },
2779 "prefer-stable": false, 2754 "prefer-stable": false,
2780 "prefer-lowest": false, 2755 "prefer-lowest": false,
2781 "platform": { 2756 "platform": {
2782 "php": ">=5.6" 2757 "php": ">=5.6",
2758 "ext-json": "*",
2759 "ext-zlib": "*"
2783 }, 2760 },
2784 "platform-dev": [], 2761 "platform-dev": [],
2785 "platform-overrides": { 2762 "platform-overrides": {
diff --git a/doc/custom_theme/main.html b/doc/custom_theme/main.html
new file mode 100644
index 00000000..cc2a703e
--- /dev/null
+++ b/doc/custom_theme/main.html
@@ -0,0 +1,23 @@
1{% extends "base.html" %}
2
3{#
4The entry point for the ReadTheDocs Theme.
5
6Any theme customisations should override this file to redefine blocks defined in
7the various templates. The custom theme should only need to define a main.html
8which `{% extends "base.html" %}` and defines various blocks which will replace
9the blocks defined in base.html and its included child templates.
10#}
11
12{%- block site_meta %}
13<meta charset="utf-8">
14<meta http-equiv="X-UA-Compatible" content="IE=edge">
15<meta name="viewport" content="width=device-width, initial-scale=1.0">
16
17{%- if 'media.readthedocs.org' not in config.extra_css[0] %}
18<meta name="robots" content="noindex, nofollow">
19{%- endif %}
20
21{% if page and page.is_homepage %}<meta name="description" content="{{ config.site_description }}">{% endif %}
22{% if config.site_author %}<meta name="author" content="{{ config.site_author }}">{% endif %}
23{%- endblock %}
diff --git a/doc/md/Community-&-Related-software.md b/doc/md/Community-&-Related-software.md
index 49c20c9c..c66d1c70 100644
--- a/doc/md/Community-&-Related-software.md
+++ b/doc/md/Community-&-Related-software.md
@@ -22,7 +22,8 @@ See [REST API](REST-API) for a list of official and community clients.
22- [shaarli2twitter](https://github.com/ArthurHoaro/shaarli2twitter) by [@ArthurHoaro](https://github.com/ArthurHoaro) - Automatically tweet your shared links from Shaarli 22- [shaarli2twitter](https://github.com/ArthurHoaro/shaarli2twitter) by [@ArthurHoaro](https://github.com/ArthurHoaro) - Automatically tweet your shared links from Shaarli
23- [shaarli2mastodon](https://github.com/kalvn/shaarli2mastodon) by [@kalvn](https://github.com/kalvn) - This Shaarli plugin allows you to automatically publish links you post on your Mastodon timeline. 23- [shaarli2mastodon](https://github.com/kalvn/shaarli2mastodon) by [@kalvn](https://github.com/kalvn) - This Shaarli plugin allows you to automatically publish links you post on your Mastodon timeline.
24- [shaarli-descriptor](https://github.com/immanuelfodor/shaarli-descriptor) by [@immanuelfodor](https://github.com/immanuelfodor) - Customize the default height/number of rows of the Description field when editing a link. 24- [shaarli-descriptor](https://github.com/immanuelfodor/shaarli-descriptor) by [@immanuelfodor](https://github.com/immanuelfodor) - Customize the default height/number of rows of the Description field when editing a link.
25 25- [urlextern](https://github.com/trailjeep/shaarli-urlextern) by [@trailjeep](https://github.com/trailjeep) - Shaarli plugin to open external links in a new tab/window.
26- [favicons](https://github.com/trailjeep/shaarli-favicons) by [@trailjeep](https://github.com/trailjeep) - Shaarli plugin to add favicon/filetype icons to links.
26 27
27### Third-party themes 28### Third-party themes
28See [Theming](Theming) for a list of community-contributed themes, and an installation guide. 29See [Theming](Theming) for a list of community-contributed themes, and an installation guide.
@@ -51,7 +52,7 @@ See [Theming](Theming) for a list of community-contributed themes, and an instal
51- [Shaarlo](https://github.com/DMeloni/shaarlo) - An aggregator for shaarlis with many features (a very popular running instance among French shaarliers: [shaarli.fr](http://shaarli.fr/)) 52- [Shaarlo](https://github.com/DMeloni/shaarlo) - An aggregator for shaarlis with many features (a very popular running instance among French shaarliers: [shaarli.fr](http://shaarli.fr/))
52- [Shaarlimages](https://github.com/BoboTiG/shaarlimages) - An image-oriented aggregator for Shaarlis 53- [Shaarlimages](https://github.com/BoboTiG/shaarlimages) - An image-oriented aggregator for Shaarlis
53- [mknexen/shaarli-api](https://github.com/mknexen/shaarli-api) - A REST API for Shaarli 54- [mknexen/shaarli-api](https://github.com/mknexen/shaarli-api) - A REST API for Shaarli
54- [Self dead link](https://github.com/qwertygc/shaarli-dev-code/blob/master/self-dead-link.php) - Detect dead links on shaarli. This version use the database of shaarli. [Another version](https://github.com/qwertygc/shaarli-dev-code/blob/master/dead-link.php), can be used for other shaarli instances (but is more resource consuming). 55- [Self dead link](https://framagit.org/qwertygc/shaarli-dev-code/blob/master/self-dead-link.php) - Detect dead links on shaarli. This version use the database of shaarli. [Another version](https://framagit.org/qwertygc/shaarli-dev-code/blob/master/dead-link.php), can be used for other shaarli instances (but is more resource consuming).
55- [Bookmark Archiver](https://github.com/pirate/bookmark-archiver) - Save an archived copy of all websites starred using browser bookmarks/Shaarli/Delicious/Instapaper/Unmark.it/Pocket/Pinboard. Outputs browseable html. 56- [Bookmark Archiver](https://github.com/pirate/bookmark-archiver) - Save an archived copy of all websites starred using browser bookmarks/Shaarli/Delicious/Instapaper/Unmark.it/Pocket/Pinboard. Outputs browseable html.
56 57
57## Alternatives to Shaarli 58## Alternatives to Shaarli
diff --git a/doc/md/Link-structure.md b/doc/md/Link-structure.md
new file mode 100644
index 00000000..0a2d0f88
--- /dev/null
+++ b/doc/md/Link-structure.md
@@ -0,0 +1,18 @@
1## Link structure
2
3Every link available through the `LinkDB` object is represented as an array
4containing the following fields:
5
6 * `id` (integer): Unique identifier.
7 * `title` (string): Title of the link.
8 * `url` (string): URL of the link. Used for displayable links (without redirector, url encoding, etc.).
9 Can be absolute or relative for Notes.
10 * `real_url` (string): Real destination URL, can be redirected, encoded, etc.
11 * `shorturl` (string): Permalink small hash.
12 * `description` (string): Link text description.
13 * `private` (boolean): whether the link is private or not.
14 * `tags` (string): all link tags separated by a single space
15 * `thumbnail` (string|boolean): relative path of the thumbnail cache file, or false if there isn't any.
16 * `created` (DateTime): link creation date time.
17 * `updated` (DateTime): last modification date time.
18 \ No newline at end of file
diff --git a/doc/md/REST-API.md b/doc/md/REST-API.md
index c016de56..11bd1cd2 100644
--- a/doc/md/REST-API.md
+++ b/doc/md/REST-API.md
@@ -152,3 +152,22 @@ See the reference API client:
152 152
153- [Documentation](http://python-shaarli-client.readthedocs.io/en/latest/) on ReadTheDocs 153- [Documentation](http://python-shaarli-client.readthedocs.io/en/latest/) on ReadTheDocs
154- [python-shaarli-client](https://github.com/shaarli/python-shaarli-client) on Github 154- [python-shaarli-client](https://github.com/shaarli/python-shaarli-client) on Github
155
156## Troubleshooting
157
158### Debug mode
159
160> This should never be used in a production environment.
161
162For security reasons, authentication issues will always return an `HTTP 401` error code without any detail.
163
164It is possible to enable the debug mode in `config.json.php`
165to get the actual error message in the HTTP response body with:
166
167```json
168{
169 "dev": {
170 "debug": true
171 }
172}
173```
diff --git a/doc/md/Server-configuration.md b/doc/md/Server-configuration.md
index ca82b2ec..78083a46 100644
--- a/doc/md/Server-configuration.md
+++ b/doc/md/Server-configuration.md
@@ -18,7 +18,7 @@ Version | Status | Shaarli compatibility
187.2 | Supported | Yes 187.2 | Supported | Yes
197.1 | Supported | Yes 197.1 | Supported | Yes
207.0 | Supported | Yes 207.0 | Supported | Yes
215.6 | Supported | Yes 215.6 | EOL: 2018-12-31 | Yes (up to Shaarli 0.10.x)
225.5 | EOL: 2016-07-10 | Yes 225.5 | EOL: 2016-07-10 | Yes
235.4 | EOL: 2015-09-14 | Yes (up to Shaarli 0.8.x) 235.4 | EOL: 2015-09-14 | Yes (up to Shaarli 0.8.x)
245.3 | EOL: 2014-08-14 | Yes (up to Shaarli 0.8.x) 245.3 | EOL: 2014-08-14 | Yes (up to Shaarli 0.8.x)
@@ -29,7 +29,7 @@ Extension | Required? | Usage
29---|:---:|--- 29---|:---:|---
30[`openssl`](http://php.net/manual/en/book.openssl.php) | All | OpenSSL, HTTPS 30[`openssl`](http://php.net/manual/en/book.openssl.php) | All | OpenSSL, HTTPS
31[`php-mbstring`](http://php.net/manual/en/book.mbstring.php) | CentOS, Fedora, RHEL, Windows, some hosting providers | multibyte (Unicode) string support 31[`php-mbstring`](http://php.net/manual/en/book.mbstring.php) | CentOS, Fedora, RHEL, Windows, some hosting providers | multibyte (Unicode) string support
32[`php-gd`](http://php.net/manual/en/book.image.php) | optional | thumbnail resizing 32[`php-gd`](http://php.net/manual/en/book.image.php) | optional | required to use thumbnails
33[`php-intl`](http://php.net/manual/en/book.intl.php) | optional | localized text sorting (e.g. `e->è->f`) 33[`php-intl`](http://php.net/manual/en/book.intl.php) | optional | localized text sorting (e.g. `e->è->f`)
34[`php-curl`](http://php.net/manual/en/book.curl.php) | optional | using cURL for fetching webpages and thumbnails in a more robust way 34[`php-curl`](http://php.net/manual/en/book.curl.php) | optional | using cURL for fetching webpages and thumbnails in a more robust way
35[`php-gettext`](http://php.net/manual/en/book.gettext.php) | optional | Use the translation system in gettext mode (faster) 35[`php-gettext`](http://php.net/manual/en/book.gettext.php) | optional | Use the translation system in gettext mode (faster)
@@ -397,6 +397,7 @@ http {
397``` 397```
398 398
399## Proxies 399## Proxies
400
400If Shaarli is served behind a proxy (i.e. there is a proxy server between clients and the web server hosting Shaarli), please refer to the proxy server documentation for proper configuration. In particular, you have to ensure that the following server variables are properly set: 401If Shaarli is served behind a proxy (i.e. there is a proxy server between clients and the web server hosting Shaarli), please refer to the proxy server documentation for proper configuration. In particular, you have to ensure that the following server variables are properly set:
401 402
402- `X-Forwarded-Proto` 403- `X-Forwarded-Proto`
@@ -405,6 +406,12 @@ If Shaarli is served behind a proxy (i.e. there is a proxy server between client
405 406
406See also [proxy-related](https://github.com/shaarli/Shaarli/issues?utf8=%E2%9C%93&q=label%3Aproxy+) issues. 407See also [proxy-related](https://github.com/shaarli/Shaarli/issues?utf8=%E2%9C%93&q=label%3Aproxy+) issues.
407 408
409## Robots and crawlers
410
411Shaarli disallows indexing and crawling of your local documentation pages by search engines, using `<meta name="robots">` HTML tags.
412Your Shaarli instance and other pages you host may still be indexed by various robots on the public Internet.
413You may want to setup a robots.txt file or other crawler control mechanism on your server.
414See [[1]](https://en.wikipedia.org/wiki/Robots_exclusion_standard), [[2]](https://support.google.com/webmasters/answer/6062608?hl=en) and [[3]](https://developers.google.com/search/reference/robots_meta_tag)
408 415
409## See also 416## See also
410 417
diff --git a/doc/md/Sharing-content.md b/doc/md/Sharing-content.md
index 4910ff6c..9a16fc62 100644
--- a/doc/md/Sharing-content.md
+++ b/doc/md/Sharing-content.md
@@ -15,7 +15,6 @@ While logged in to your Shaarli, you can add new Shaares in several ways:
15 15
16 * [+Shaare button](#shaare-button) 16 * [+Shaare button](#shaare-button)
17 * [Bookmarklet](#bookmarklet) 17 * [Bookmarklet](#bookmarklet)
18 * [Firefox Share](#firefox-share)
19 * Third-party [apps and browser addons](Community-&-Related-software.md#mobile-apps) 18 * Third-party [apps and browser addons](Community-&-Related-software.md#mobile-apps)
20 * [REST API](https://shaarli.github.io/api-documentation/) 19 * [REST API](https://shaarli.github.io/api-documentation/)
21 20
@@ -52,22 +51,6 @@ bookmarklet in your browser! The same `New Shaare` dialog as above is displayed.
52![](images/bookmarklet.png) 51![](images/bookmarklet.png)
53 52
54 53
55### Firefox Share
56
57Before using Firefox Share, you must first add Shaarli as a sharing provider:
58
59- Click the `Tools` button in the top bar
60- Click the `✚Add to Firefox social` button and accept the activation.
61
62Once this is done, you can share any URL you are visiting by clicking the Firefox
63_Share_ button ![images/firefoxshare.png](images/firefoxshare.png)
64
65| Note | Firefox Share is no longer available for Firefox 57 and later versions. |
66|---------|---------|
67
68| Note | Your Shaarli instance must be hosted on an HTTPS (SSL/TLS secure connection) enabled server for Firefox Share to work. Firefox Share will not work over plaintext HTTP connections. |
69|---------|---------|
70
71-------------------------------------------------------------------------------- 54--------------------------------------------------------------------------------
72 55
73## Editing Shaares 56## Editing Shaares
diff --git a/doc/md/Backup,-restore,-import-and-export.md b/doc/md/guides/backup-restore-import-export.md
index bb790074..bb790074 100644
--- a/doc/md/Backup,-restore,-import-and-export.md
+++ b/doc/md/guides/backup-restore-import-export.md
diff --git a/doc/md/guides/images/01-create-droplet-distro.jpg b/doc/md/guides/images/01-create-droplet-distro.jpg
new file mode 100644
index 00000000..63682ba8
--- /dev/null
+++ b/doc/md/guides/images/01-create-droplet-distro.jpg
Binary files differ
diff --git a/doc/md/guides/images/02-create-droplet-region.jpg b/doc/md/guides/images/02-create-droplet-region.jpg
new file mode 100644
index 00000000..135a78be
--- /dev/null
+++ b/doc/md/guides/images/02-create-droplet-region.jpg
Binary files differ
diff --git a/doc/md/guides/images/03-create-droplet-size.jpg b/doc/md/guides/images/03-create-droplet-size.jpg
new file mode 100644
index 00000000..aa5b2fd2
--- /dev/null
+++ b/doc/md/guides/images/03-create-droplet-size.jpg
Binary files differ
diff --git a/doc/md/guides/images/04-finalize.jpg b/doc/md/guides/images/04-finalize.jpg
new file mode 100644
index 00000000..68ec0dc5
--- /dev/null
+++ b/doc/md/guides/images/04-finalize.jpg
Binary files differ
diff --git a/doc/md/guides/images/05-droplet.jpg b/doc/md/guides/images/05-droplet.jpg
new file mode 100644
index 00000000..44e93a1e
--- /dev/null
+++ b/doc/md/guides/images/05-droplet.jpg
Binary files differ
diff --git a/doc/md/guides/images/06-domain.jpg b/doc/md/guides/images/06-domain.jpg
new file mode 100644
index 00000000..5827dd93
--- /dev/null
+++ b/doc/md/guides/images/06-domain.jpg
Binary files differ
diff --git a/doc/md/guides/images/07-installation.jpg b/doc/md/guides/images/07-installation.jpg
new file mode 100644
index 00000000..42cc9f10
--- /dev/null
+++ b/doc/md/guides/images/07-installation.jpg
Binary files differ
diff --git a/doc/md/guides/install-shaarli-with-debian9-and-docker.md b/doc/md/guides/install-shaarli-with-debian9-and-docker.md
new file mode 100644
index 00000000..f1b26d47
--- /dev/null
+++ b/doc/md/guides/install-shaarli-with-debian9-and-docker.md
@@ -0,0 +1,257 @@
1_Last updated on 2018-07-01._
2
3## Goals
4- Getting a Virtual Private Server (VPS)
5- Running Shaarli:
6 - as a Docker container,
7 - using the Træfik reverse proxy,
8 - securized with TLS certificates from Let's Encrypt.
9
10
11The following components and tools will be used:
12
13- [Debian](https://www.debian.org/), a GNU/Linux distribution widely used in
14 server environments;
15- [Docker](https://docs.docker.com/engine/docker-overview/), an open platform
16 for developing, shipping, and running applications;
17- [Docker Compose](https://docs.docker.com/compose/), a tool for defining and
18 running multi-container Docker applications.
19
20
21More information can be found in the [Resources](#resources) section at the
22bottom of the guide.
23
24## Getting a Virtual Private Server
25For this guide, I went for the smallest VPS available from DigitalOcean,
26a Droplet with 1 CPU, 1 GiB RAM and 25 GiB SSD storage, which costs
27$5/month ($0.007/hour):
28
29- [Droplets Overview](https://www.digitalocean.com/docs/droplets/overview/)
30- [Pricing](https://www.digitalocean.com/pricing/)
31- [How to Create a Droplet from the DigitalOcean Control Panel](https://www.digitalocean.com/docs/droplets/how-to/create/)
32- [How to Add SSH Keys to Droplets](https://www.digitalocean.com/docs/droplets/how-to/add-ssh-keys/)
33- [Initial Server Setup with Debian 8](https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-8) (also applies to Debian 9)
34- [An Introduction to Securing your Linux VPS](https://www.digitalocean.com/community/tutorials/an-introduction-to-securing-your-linux-vps)
35
36### Creating a Droplet
37Select `Debian 9` as the Droplet distribution:
38
39<img src="../images/01-create-droplet-distro.jpg"
40 width="500px"
41 alt="Droplet distribution" />
42
43Choose a region that is geographically close to you:
44
45<img src="../images/02-create-droplet-region.jpg"
46 width="500px"
47 alt="Droplet region" />
48
49Choose a Droplet size that corresponds to your usage and budget:
50
51<img src="../images/03-create-droplet-size.jpg"
52 width="500px"
53 alt="Droplet size" />
54
55Finalize the Droplet creation:
56
57<img src="../images/04-finalize.jpg"
58 width="500px"
59 alt="Droplet finalization" />
60
61Droplet information is displayed on the Control Panel:
62
63<img src="../images/05-droplet.jpg"
64 width="500px"
65 alt="Droplet summary" />
66
67Once your VPS has been created, you will receive an e-mail with connection
68instructions.
69
70## Obtaining a domain name
71After creating your VPS, it will be reachable using its IP address; some hosting
72providers also create a DNS record, e.g. `ns4853142.ip-01-47-127.eu`.
73
74A domain name (DNS record) is required to obtain a certificate and setup HTTPS
75(HTTP with TLS encryption).
76
77Domain names can be obtained from registrars through hosting providers such as
78[Gandi](https://www.gandi.net/en/domain).
79
80Once you have your own domain, you need to create a new DNS record that points
81to your VPS' IP address:
82
83<img src="../images/06-domain.jpg"
84 width="650px"
85 alt="Domain configuration" />
86
87## Host setup
88Now's the time to connect to your freshly created VPS!
89
90```shell
91$ ssh root@188.166.85.8
92
93Linux stretch-shaarli-02 4.9.0-6-amd64 #1 SMP Debian 4.9.88-1+deb9u1 (2018-05-07) x86_64
94
95The programs included with the Debian GNU/Linux system are free software;
96the exact distribution terms for each program are described in the
97individual files in /usr/share/doc/*/copyright.
98
99Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
100permitted by applicable law.
101Last login: Sun Jul 1 11:20:18 2018 from <REDACTED>
102
103root@stretch-shaarli-02:~$
104```
105
106### Updating the system
107```shell
108root@stretch-shaarli-02:~$ apt update && apt upgrade -y
109```
110
111### Setting up Docker
112_The following instructions are from the
113[Get Docker CE for Debian](https://docs.docker.com/install/linux/docker-ce/debian/)
114guide._
115
116Install package dependencies:
117
118```shell
119root@stretch-shaarli-02:~$ apt install -y apt-transport-https ca-certificates curl gnupg2 software-properties-common
120```
121
122Add Docker's package repository GPG key:
123
124```shell
125root@stretch-shaarli-02:~$ curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -
126```
127
128Add Docker's package repository:
129
130```shell
131root@stretch-shaarli-02:~$ add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian stretch stable"
132```
133
134Update package lists and install Docker:
135
136```shell
137root@stretch-shaarli-02:~$ apt update && apt install -y docker-ce
138```
139
140Verify Docker is properly configured by running the `hello-world` image:
141
142```shell
143root@stretch-shaarli-02:~$ docker run hello-world
144```
145
146### Setting up Docker Compose
147_The following instructions are from the
148[Install Docker Compose](https://docs.docker.com/compose/install/)
149guide._
150
151Download the current version from the release page:
152
153```shell
154root@stretch-shaarli-02:~$ curl -L https://github.com/docker/compose/releases/download/1.21.2/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
155root@stretch-shaarli-02:~$ chmod +x /usr/local/bin/docker-compose
156```
157
158## Running Shaarli
159Shaarli comes with a configuration file for Docker Compose, that will setup:
160
161- a local Docker network
162- a Docker [volume](https://docs.docker.com/storage/volumes/) to store Shaarli data
163- a Docker [volume](https://docs.docker.com/storage/volumes/) to store Træfik TLS configuration and certificates
164- a [Shaarli](https://hub.docker.com/r/shaarli/shaarli/) instance
165- a [Træfik](https://hub.docker.com/_/traefik/) instance
166
167[Træfik](https://docs.traefik.io/) is a modern HTTP reverse proxy, with native
168support for Docker and [Let's Encrypt](https://letsencrypt.org/).
169
170### Compose configuration
171Create a new directory to store the configuration:
172
173```shell
174root@stretch-shaarli-02:~$ mkdir shaarli && cd shaarli
175root@stretch-shaarli-02:~/shaarli$
176```
177
178Download the current version of Shaarli's `docker-compose.yml`:
179
180```shell
181root@stretch-shaarli-02:~/shaarli$ curl -L https://raw.githubusercontent.com/shaarli/Shaarli/master/docker-compose.yml -o docker-compose.yml
182```
183
184Create the `.env` file and fill in your VPS and domain information (replace
185`<MY_SHAARLI_DOMAIN>` and `<MY_CONTACT_EMAIL>` with your actual information):
186
187```shell
188root@stretch-shaarli-02:~/shaarli$ vim .env
189```
190
191```shell
192SHAARLI_VIRTUAL_HOST=<MY_SHAARLI_DOMAIN>
193SHAARLI_LETSENCRYPT_EMAIL=<MY_CONTACT_EMAIL>
194```
195
196### Pull the Docker images
197```shell
198root@stretch-shaarli-02:~/shaarli$ docker-compose pull
199Pulling shaarli ... done
200Pulling traefik ... done
201```
202
203### Run!
204```shell
205root@stretch-shaarli-02:~/shaarli$ docker-compose up -d
206Creating network "shaarli_http-proxy" with the default driver
207Creating volume "shaarli_traefik-acme" with default driver
208Creating volume "shaarli_shaarli-data" with default driver
209Creating shaarli_shaarli_1 ... done
210Creating shaarli_traefik_1 ... done
211```
212
213## Conclusion
214Congratulations! Your Shaarli instance should be up and running, and available
215at `https://<MY_SHAARLI_DOMAIN>`.
216
217<img src="../images/07-installation.jpg"
218 width="500px"
219 alt="Shaarli installation page" />
220
221## Resources
222### Related Shaarli documentation
223- [Docker 101](../docker/docker-101.md)
224- [Shaarli images](../docker/shaarli-images.md)
225
226### Hosting providers
227- [DigitalOcean](https://www.digitalocean.com/)
228- [Gandi](https://www.gandi.net/en)
229- [OVH](https://www.ovh.co.uk/)
230- [RackSpace](https://www.rackspace.com/)
231- etc.
232
233### Domain Names and Registrars
234- [Introduction to the Domain Name System (DNS)](https://opensource.com/article/17/4/introduction-domain-name-system-dns)
235- [ICANN](https://www.icann.org/)
236- [Domain name registrar](https://en.wikipedia.org/wiki/Domain_name_registrar)
237- [OVH Domain Registration](https://www.ovh.co.uk/domains/)
238- [Gandi Domain Registration](https://www.gandi.net/en/domain)
239
240### HTTPS and Security
241- [Transport Layer Security](https://en.wikipedia.org/wiki/Transport_Layer_Security)
242- [Let's Encrypt](https://letsencrypt.org/)
243
244### Docker
245- [Docker Overview](https://docs.docker.com/engine/docker-overview/)
246- [Docker Documentation](https://docs.docker.com/)
247- [Get Docker CE for Debian](https://docs.docker.com/install/linux/docker-ce/debian/)
248- [docker logs](https://docs.docker.com/engine/reference/commandline/logs/)
249- [Volumes](https://docs.docker.com/storage/volumes/)
250- [Install Docker Compose](https://docs.docker.com/compose/install/)
251- [docker-compose logs](https://docs.docker.com/compose/reference/logs/)
252
253### Træfik
254- [Getting Started](https://docs.traefik.io/)
255- [Docker backend](https://docs.traefik.io/configuration/backends/docker/)
256- [Let's Encrypt and Docker](https://docs.traefik.io/user-guide/docker-and-lets-encrypt/)
257- [traefik](https://hub.docker.com/_/traefik/) Docker image
diff --git a/doc/md/Various-hacks.md b/doc/md/guides/various-hacks.md
index 0074ae9f..0074ae9f 100644
--- a/doc/md/Various-hacks.md
+++ b/doc/md/guides/various-hacks.md
diff --git a/doc/md/images/icon.png b/doc/md/images/icon.png
new file mode 100644
index 00000000..530d7469
--- /dev/null
+++ b/doc/md/images/icon.png
Binary files differ
diff --git a/doc/md/index.md b/doc/md/index.md
index c18332b4..220eeec1 100644
--- a/doc/md/index.md
+++ b/doc/md/index.md
@@ -1,25 +1,19 @@
1# [Shaarli](https://github.com/shaarli/Shaarli/) documentation 1# <img src="images/icon.png" width="20px" height="20px"> Shaarli
2 2
3The personal, minimalist, super-fast, database free, bookmarking service. 3The personal, minimalist, super-fast, database free, bookmarking service.
4 4
5Do you want to share the links you discover? 5Do you want to share the links you discover?
6Shaarli is a minimalist link sharing service that you can install on your own server. 6Shaarli is a minimalist bookmark manager and link sharing service that you can install on your own server.
7It is designed to be personal (single-user), fast and handy. 7It is designed to be personal (single-user), fast and handy.
8 8
9<!-- TODO screenshots --> 9<!-- TODO screenshots -->
10 10
11Here you can find some info on how to use, configure, tweak and solve problems with your Shaarli. 11Visit the pages in the sidebar to find information on how to setup, use, configure, tweak and troubleshoot Shaarli.
12For general information, read the [README](https://github.com/shaarli/Shaarli/blob/master/README.md).
13 12
14If you have any questions or ideas, please join the [chat](https://gitter.im/shaarli/Shaarli) (also reachable via [IRC](https://irc.gitter.im/)), post them in our [general discussion](https://github.com/shaarli/Shaarli/issues/308) or read the current [issues](https://github.com/shaarli/Shaarli/issues).
15
16If you've found a bug, please create a [new issue](https://github.com/shaarli/Shaarli/issues/new).
17
18If you would like a feature added to Shaarli, check the issues labeled [`feature`](https://github.com/shaarli/Shaarli/labels/feature), [`enhancement`](https://github.com/shaarli/Shaarli/labels/enhancement), and [`plugin`](https://github.com/shaarli/Shaarli/labels/plugin).
19 13
20* [GitHub project page](https://github.com/shaarli/Shaarli) 14* [GitHub project page](https://github.com/shaarli/Shaarli)
21* [Online documentation](https://shaarli.readthedocs.io/) (this page) 15* [Online documentation](https://shaarli.readthedocs.io/)
22* [Latest Shaarli releases](https://github.com/shaarli/Shaarli/releases) 16* [Latest releases](https://github.com/shaarli/Shaarli/releases)
23* [Changelog](https://github.com/shaarli/Shaarli/blob/master/CHANGELOG.md) 17* [Changelog](https://github.com/shaarli/Shaarli/blob/master/CHANGELOG.md)
24 18
25 19
@@ -30,87 +24,70 @@ It runs the latest development version of Shaarli and is updated/reset daily.
30 24
31Login: `demo`; Password: `demo` 25Login: `demo`; Password: `demo`
32 26
33<!-- TODO review everything below this point -->
34
35
36## Features 27## Features
37 28
38Shaarli can be used: 29Shaarli can be used:
39 30
40- to share, comment and save interesting links and news. 31- to share, comment and save interesting links and news
41- to bookmark useful/frequent personal links (as private links) and share them between computers. 32- to bookmark useful/frequent links and share them between computers
42- as a minimal blog/microblog/writing platform (no character limit). 33- as a minimal blog/microblog/writing platform
43- as a read-it-later list (for example items tagged `readlater`). 34- as a read-it-later list
44- to draft and save articles/posts/ideas. 35- to draft and save articles/posts/ideas
45- to keep code snippets. 36- to keep notes, documentation and code snippets
46- to keep notes and documentation. 37- as a shared clipboard/notepad/pastebin between machines
47- as a shared clipboard/notepad/pastebin between machines. 38- as a todo list
48- as a todo list. 39- to store media playlists
49- to store playlists (e.g. with the `music` or `video` tags).
50- to keep extracts/comments from webpages that may disappear. 40- to keep extracts/comments from webpages that may disappear.
51- to keep track of ongoing discussions (for example items tagged `discussion`). 41- to keep track of ongoing discussions
52- [to feed RSS aggregators](http://shaarli.chassegnouf.net/?9Efeiw) (planets) with specific tags. 42- to feed other blogs, aggregators, social networks... using RSS feeds
53- to feed other social networks, blogs... using RSS feeds and external services (dlvr.it, ifttt.com ...).
54 43
55### Interface 44### Edit, view and search your links
56 45
57- minimalist design (simple is beautiful) 46- Minimalist design
58- FAST 47- FAST
59- ATOM and RSS feeds 48- Customizable link titles and descriptions
60- views: 49- Tags to organize your links (features tag autocompletion, renaming, merging and deletion)
61 - paginated link list (with image and video thumbnails) 50- Search by tag or using the full-text search
62 - tag cloud 51- Public and private links (visible only to logged-in users)
63 - picture wall: image and video thumbnails (with lazy loading) 52- Unique permalinks for easy reference
64 - daily: newspaper-like daily digest 53- Paginated link list (with image and video thumbnails)
65 - daily RSS feed 54- Tag cloud and list views
66- permalinks for easy reference 55- Picture wall: image and video thumbnails view (with lazy loading)
67- links can be public or private 56- ATOM and RSS feeds (can also be filtered using tags or text search)
68- thumbnail generation for images and video services 57- Daily: newspaper-like daily digest (and daily RSS feed)
69- URL cleanup: automatic removal of `?utm_source=...`, `fb=...` 58- URL cleanup: automatic removal of `?utm_source=...`, `fb=...`
70- extensible through [plugins](https://shaarli.readthedocs.io/en/master/Plugins/#plugin-usage) 59- Extensible through [plugins](https://shaarli.readthedocs.io/en/master/Plugins/#plugin-usage)
71
72### Tag, view and search your links
73
74- add a custom title and description to archived links
75- add tags to classify and search links
76 - features tag autocompletion, renaming, merging and deletion
77- full-text and tag search
78 60
79### Easy setup 61### Easy setup
80 62
81- dead-simple installation: drop the files, open the page 63- Dead-simple installation: drop the files, open the page
82- links are stored in a file 64- Links are stored in a file (no database required, easy backup: simply copy the datastore file)
83 - compact storage 65- Import and export links as Netscape bookmarks compatible with most Web browsers
84 - no database required
85 - easy backup: simply copy the datastore file
86- import and export links as Netscape bookmarks
87 66
88### Accessibility 67### Accessibility
89 68
90- bookmarlet to share links in one click 69- Bookmarklet and other tools to share links in one click
91- support for mobile browsers 70- Support for mobile browsers
92- degrades gracefully with Javascript disabled 71- Degrades gracefully with Javascript disabled
93- easy page customization through HTML/CSS/RainTPL 72- Easy page customization through HTML/CSS/RainTPL
94 73
95### Security 74### Security
96 75
97- discreet pop-up notification when a new release is available 76- Discreet pop-up notification when a new release is available
98- bruteforce protection on the login form 77- Bruteforce protection on the login form
99- protected against [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) and session cookie hijacking 78- Protected against [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) and session cookie hijacking
100 79
101<!-- TODO Limitations --> 80<!-- TODO Limitations -->
102 81
103### REST API 82### REST API
104 83
105Easily extensible by any client using the REST API exposed by Shaarli. 84- Easily extensible by any client using the REST API exposed by Shaarli ([API documentation](http://shaarli.github.io/api-documentation/)).
106
107See the [API documentation](http://shaarli.github.io/api-documentation/).
108 85
109## About 86## About
110 87
111### Shaarli community fork 88### Shaarli community fork
112 89
113This friendly fork is maintained by the Shaarli community at https://github.com/shaarli/Shaarli 90This friendly fork is maintained by the Shaarli community at <https://github.com/shaarli/Shaarli>
114 91
115This is a community fork of the original [Shaarli](https://github.com/sebsauvage/Shaarli/) project by [Sébastien Sauvage](http://sebsauvage.net/). 92This is a community fork of the original [Shaarli](https://github.com/sebsauvage/Shaarli/) project by [Sébastien Sauvage](http://sebsauvage.net/).
116 93
@@ -123,16 +100,15 @@ in this repository, and will keep maintaining the project for the foreseeable
123future, while keeping Shaarli simple and efficient. 100future, while keeping Shaarli simple and efficient.
124 101
125 102
126### Contributing 103### Contributing and getting help
127 104
128If you'd like to help, please: 105Feedback is very appreciated!
129 106
130- have a look at the open [issues](https://github.com/shaarli/Shaarli/issues) 107- If you have any questions or ideas, please join the [chat](https://gitter.im/shaarli/Shaarli) (also reachable via [IRC](https://irc.gitter.im/)), post them in our [general discussion](https://github.com/shaarli/Shaarli/issues/308) or read the current [issues](https://github.com/shaarli/Shaarli/issues).
131and [pull requests](https://github.com/shaarli/Shaarli/pulls) 108- Have a look at the open [issues](https://github.com/shaarli/Shaarli/issues) and [pull requests](https://github.com/shaarli/Shaarli/pulls)
132- feel free to report bugs (feedback is much appreciated) 109- If you would like a feature added to Shaarli, check the issues labeled [`feature`](https://github.com/shaarli/Shaarli/labels/feature), [`enhancement`](https://github.com/shaarli/Shaarli/labels/enhancement), and [`plugin`](https://github.com/shaarli/Shaarli/labels/plugin).
133- suggest new features and improvements to both code and [documentation](https://github.com/shaarli/Shaarli/tree/master/doc/md/) 110- If you've found a bug, please create a [new issue](https://github.com/shaarli/Shaarli/issues/new).
134- propose solutions to existing problems 111- Feel free to propose solutions to existing problems, help us improve the documentation and translations, and submit pull requests :-)
135- submit pull requests :-)
136 112
137 113
138### License 114### License
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 00000000..e8ea4271
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,61 @@
1---
2# Shaarli - Docker Compose example configuration
3#
4# See:
5# - https://shaarli.readthedocs.io/en/master/docker/shaarli-images/
6# - https://shaarli.readthedocs.io/en/master/guides/install-shaarli-with-debian9-and-docker/
7#
8# Environment variables:
9# - SHAARLI_VIRTUAL_HOST Fully Qualified Domain Name for the Shaarli instance
10# - SHAARLI_LETSENCRYPT_EMAIL Contact email for certificate renewal
11version: '3'
12
13networks:
14 http-proxy:
15
16volumes:
17 traefik-acme:
18 shaarli-cache:
19 shaarli-data:
20
21services:
22 shaarli:
23 image: shaarli/shaarli:master
24 build: ./
25 networks:
26 - http-proxy
27 volumes:
28 - shaarli-cache:/var/www/shaarli/cache
29 - shaarli-data:/var/www/shaarli/data
30 labels:
31 traefik.domain: "${SHAARLI_VIRTUAL_HOST}"
32 traefik.backend: shaarli
33 traefik.frontend.rule: "Host:${SHAARLI_VIRTUAL_HOST}"
34
35 traefik:
36 image: traefik
37 command:
38 - "--defaultentrypoints=http,https"
39 - "--entrypoints=Name:http Address::80 Redirect.EntryPoint:https"
40 - "--entrypoints=Name:https Address::443 TLS"
41 - "--retry"
42 - "--docker"
43 - "--docker.domain=docker.localhost"
44 - "--docker.exposedbydefault=true"
45 - "--docker.watch=true"
46 - "--acme"
47 - "--acme.domains=${SHAARLI_VIRTUAL_HOST}"
48 - "--acme.email=${SHAARLI_LETSENCRYPT_EMAIL}"
49 - "--acme.entrypoint=https"
50 - "--acme.onhostrule=true"
51 - "--acme.storage=/acme/acme.json"
52 - "--acme.httpchallenge"
53 - "--acme.httpchallenge.entrypoint=http"
54 networks:
55 - http-proxy
56 ports:
57 - 80:80
58 - 443:443
59 volumes:
60 - /var/run/docker.sock:/var/run/docker.sock:ro
61 - traefik-acme:/acme
diff --git a/inc/languages/fr/LC_MESSAGES/shaarli.po b/inc/languages/fr/LC_MESSAGES/shaarli.po
index 2ebeccbc..102c80da 100644
--- a/inc/languages/fr/LC_MESSAGES/shaarli.po
+++ b/inc/languages/fr/LC_MESSAGES/shaarli.po
@@ -1,15 +1,15 @@
1msgid "" 1msgid ""
2msgstr "" 2msgstr ""
3"Project-Id-Version: Shaarli\n" 3"Project-Id-Version: Shaarli\n"
4"POT-Creation-Date: 2018-01-24 18:43+0100\n" 4"POT-Creation-Date: 2018-10-06 13:08+0200\n"
5"PO-Revision-Date: 2018-03-06 18:44+0100\n" 5"PO-Revision-Date: 2018-10-06 13:08+0200\n"
6"Last-Translator: \n" 6"Last-Translator: \n"
7"Language-Team: Shaarli\n" 7"Language-Team: Shaarli\n"
8"Language: fr_FR\n" 8"Language: fr_FR\n"
9"MIME-Version: 1.0\n" 9"MIME-Version: 1.0\n"
10"Content-Type: text/plain; charset=UTF-8\n" 10"Content-Type: text/plain; charset=UTF-8\n"
11"Content-Transfer-Encoding: 8bit\n" 11"Content-Transfer-Encoding: 8bit\n"
12"X-Generator: Poedit 2.0.6\n" 12"X-Generator: Poedit 2.1.1\n"
13"X-Poedit-Basepath: ../../../..\n" 13"X-Poedit-Basepath: ../../../..\n"
14"Plural-Forms: nplurals=2; plural=(n > 1);\n" 14"Plural-Forms: nplurals=2; plural=(n > 1);\n"
15"X-Poedit-SourceCharset: UTF-8\n" 15"X-Poedit-SourceCharset: UTF-8\n"
@@ -48,7 +48,7 @@ msgstr "le fichier n'est pas accessible en écriture"
48#: application/Cache.php:16 48#: application/Cache.php:16
49#, php-format 49#, php-format
50msgid "Cannot purge %s: no directory" 50msgid "Cannot purge %s: no directory"
51msgstr "Impossible de purger %s: le répertoire n'existe pas" 51msgstr "Impossible de purger %s : le répertoire n'existe pas"
52 52
53#: application/FeedBuilder.php:151 53#: application/FeedBuilder.php:151
54msgid "Direct link" 54msgid "Direct link"
@@ -56,7 +56,7 @@ msgstr "Liens directs"
56 56
57#: application/FeedBuilder.php:153 57#: application/FeedBuilder.php:153
58#: tmp/daily.b91ef64efc3688266305ea9b42e5017e.rtpl.php:88 58#: tmp/daily.b91ef64efc3688266305ea9b42e5017e.rtpl.php:88
59#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:178 59#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:177
60msgid "Permalink" 60msgid "Permalink"
61msgstr "Permalien" 61msgstr "Permalien"
62 62
@@ -68,18 +68,22 @@ msgstr "Le fichier d'historique n'est pas accessible en lecture ou en écriture"
68msgid "Could not parse history file" 68msgid "Could not parse history file"
69msgstr "Format incorrect pour le fichier d'historique" 69msgstr "Format incorrect pour le fichier d'historique"
70 70
71#: application/Languages.php:161 71#: application/Languages.php:177
72msgid "Automatic" 72msgid "Automatic"
73msgstr "Automatique" 73msgstr "Automatique"
74 74
75#: application/Languages.php:162 75#: application/Languages.php:178
76msgid "English" 76msgid "English"
77msgstr "Anglais" 77msgstr "Anglais"
78 78
79#: application/Languages.php:163 79#: application/Languages.php:179
80msgid "French" 80msgid "French"
81msgstr "Français" 81msgstr "Français"
82 82
83#: application/Languages.php:180
84msgid "German"
85msgstr "Allemand"
86
83#: application/LinkDB.php:136 87#: application/LinkDB.php:136
84msgid "You are not authorized to add a link." 88msgid "You are not authorized to add a link."
85msgstr "Vous n'êtes pas autorisé à ajouter un lien." 89msgstr "Vous n'êtes pas autorisé à ajouter un lien."
@@ -94,17 +98,15 @@ msgstr "Vous devez utiliser un entier comme clé."
94 98
95#: application/LinkDB.php:145 99#: application/LinkDB.php:145
96msgid "Array offset and link ID must be equal." 100msgid "Array offset and link ID must be equal."
97msgstr "La clé du tableau et l'ID du lien doivent être égaux." 101msgstr "La clé du tableau et l'ID du lien doivent être identiques."
98 102
99#: application/LinkDB.php:251 103#: application/LinkDB.php:251
100#: tmp/page.footer.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14
101#: tmp/page.footer.b91ef64efc3688266305ea9b42e5017e.rtpl.php:48
102#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:14 104#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:14
103#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:48 105#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:48
104msgid "" 106msgid ""
105"The personal, minimalist, super-fast, database free, bookmarking service" 107"The personal, minimalist, super-fast, database free, bookmarking service"
106msgstr "" 108msgstr ""
107"Le gestionnaire de marque-page personnel, minimaliste, et sans base de " 109"Le gestionnaire de marque-pages personnel, minimaliste, et sans base de "
108"données" 110"données"
109 111
110#: application/LinkDB.php:253 112#: application/LinkDB.php:253
@@ -121,11 +123,11 @@ msgstr ""
121"Bienvenue sur Shaarli ! Ceci est votre premier marque-page public. Pour me " 123"Bienvenue sur Shaarli ! Ceci est votre premier marque-page public. Pour me "
122"modifier ou me supprimer, vous devez d'abord vous connecter.\n" 124"modifier ou me supprimer, vous devez d'abord vous connecter.\n"
123"\n" 125"\n"
124"Pour apprendre comment utiliser Shaarli, consultez le lien « Documentation » " 126"Pour apprendre à utiliser Shaarli, consultez le lien « Documentation » en "
125"en bas de page.\n" 127"bas de page.\n"
126"\n" 128"\n"
127"Vous utilisez la version supportée par la communauté du projet original " 129"Vous utilisez la version supportée par la communauté du projet original "
128"Shaarli, de Sébastien Sauvage." 130"Shaarli de Sébastien Sauvage."
129 131
130#: application/LinkDB.php:267 132#: application/LinkDB.php:267
131msgid "My secret stuff... - Pastebin.com" 133msgid "My secret stuff... - Pastebin.com"
@@ -163,11 +165,11 @@ msgstr ""
163"a été importé avec succès en %d secondes : %d liens importés, %d liens " 165"a été importé avec succès en %d secondes : %d liens importés, %d liens "
164"écrasés, %d liens ignorés." 166"écrasés, %d liens ignorés."
165 167
166#: application/PageBuilder.php:168 168#: application/PageBuilder.php:200
167msgid "The page you are trying to reach does not exist or has been deleted." 169msgid "The page you are trying to reach does not exist or has been deleted."
168msgstr "La page que vous essayez de consulter n'existe pas ou a été supprimée." 170msgstr "La page que vous essayez de consulter n'existe pas ou a été supprimée."
169 171
170#: application/PageBuilder.php:170 172#: application/PageBuilder.php:202
171msgid "404 Not Found" 173msgid "404 Not Found"
172msgstr "404 Introuvable" 174msgstr "404 Introuvable"
173 175
@@ -176,21 +178,37 @@ msgstr "404 Introuvable"
176msgid "Plugin \"%s\" files not found." 178msgid "Plugin \"%s\" files not found."
177msgstr "Les fichiers de l'extension \"%s\" sont introuvables." 179msgstr "Les fichiers de l'extension \"%s\" sont introuvables."
178 180
179#: application/Updater.php:76 181#: application/Thumbnailer.php:61
182msgid ""
183"php-gd extension must be loaded to use thumbnails. Thumbnails are now "
184"disabled. Please reload the page."
185msgstr ""
186"l'extension php-gd doit être chargée pour utiliser les miniatures. Les "
187"miniatures sont désormais désactivées. Rechargez la page."
188
189#: application/Updater.php:86
180msgid "Couldn't retrieve Updater class methods." 190msgid "Couldn't retrieve Updater class methods."
181msgstr "Impossible de récupérer les méthodes de la classe Updater." 191msgstr "Impossible de récupérer les méthodes de la classe Updater."
182 192
183#: application/Updater.php:506 193#: application/Updater.php:514 index.php:1022
194msgid ""
195"You have enabled or changed thumbnails mode. <a href=\"?do=thumbs_update"
196"\">Please synchronize them</a>."
197msgstr ""
198"Vous avez activé ou changé le mode de miniatures. <a href=\"?do=thumbs_update"
199"\">Merci de les synchroniser</a>."
200
201#: application/Updater.php:586
184msgid "An error occurred while running the update " 202msgid "An error occurred while running the update "
185msgstr "Une erreur s'est produite lors de l'exécution de la mise à jour " 203msgstr "Une erreur s'est produite lors de l'exécution de la mise à jour "
186 204
187#: application/Updater.php:546 205#: application/Updater.php:626
188msgid "Updates file path is not set, can't write updates." 206msgid "Updates file path is not set, can't write updates."
189msgstr "" 207msgstr ""
190"Le chemin vers le fichier de mise à jour n'est pas défini, impossible " 208"Le chemin vers le fichier de mise à jour n'est pas défini, impossible "
191"d'écrire les mises à jour." 209"d'écrire les mises à jour."
192 210
193#: application/Updater.php:551 211#: application/Updater.php:631
194msgid "Unable to write updates in " 212msgid "Unable to write updates in "
195msgstr "Impossible d'écrire les mises à jour dans " 213msgstr "Impossible d'écrire les mises à jour dans "
196 214
@@ -230,6 +248,7 @@ msgstr ""
230"Shaarli a les droits d'écriture dans le dossier dans lequel il est installé." 248"Shaarli a les droits d'écriture dans le dossier dans lequel il est installé."
231 249
232#: application/config/ConfigManager.php:135 250#: application/config/ConfigManager.php:135
251#: application/config/ConfigManager.php:162
233msgid "Invalid setting key parameter. String expected, got: " 252msgid "Invalid setting key parameter. String expected, got: "
234msgstr "Clé de paramétrage invalide. Chaîne de caractères obtenue, attendu : " 253msgstr "Clé de paramétrage invalide. Chaîne de caractères obtenue, attendu : "
235 254
@@ -251,135 +270,128 @@ msgstr "Vous n'êtes pas autorisé à modifier la configuration."
251msgid "Error accessing" 270msgid "Error accessing"
252msgstr "Une erreur s'est produite en accédant à" 271msgstr "Une erreur s'est produite en accédant à"
253 272
254#: index.php:142 273#: index.php:143
255msgid "Shared links on " 274msgid "Shared links on "
256msgstr "Liens partagés sur " 275msgstr "Liens partagés sur "
257 276
258#: index.php:164 277#: index.php:165
259msgid "Insufficient permissions:" 278msgid "Insufficient permissions:"
260msgstr "Permissions insuffisantes :" 279msgstr "Permissions insuffisantes :"
261 280
262#: index.php:303 281#: index.php:201
263msgid "I said: NO. You are banned for the moment. Go away." 282msgid "I said: NO. You are banned for the moment. Go away."
264msgstr "NON. Vous êtes banni pour le moment. Revenez plus tard." 283msgstr "NON. Vous êtes banni pour le moment. Revenez plus tard."
265 284
266#: index.php:368 285#: index.php:273
267msgid "Wrong login/password." 286msgid "Wrong login/password."
268msgstr "Nom d'utilisateur ou mot de passe incorrects." 287msgstr "Nom d'utilisateur ou mot de passe incorrect(s)."
269 288
270#: index.php:576 tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:42 289#: index.php:482 tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:46
271#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:42
272msgid "Daily" 290msgid "Daily"
273msgstr "Quotidien" 291msgstr "Quotidien"
274 292
275#: index.php:681 tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:28 293#: index.php:588 tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:28
276#: tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:44 294#: tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:44
277#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:71 295#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:75
278#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:95 296#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:99
279#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:71
280#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:95
281msgid "Login" 297msgid "Login"
282msgstr "Connexion" 298msgstr "Connexion"
283 299
284#: index.php:722 tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:39 300#: index.php:605 tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:41
285#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:39
286msgid "Picture wall" 301msgid "Picture wall"
287msgstr "Mur d'images" 302msgstr "Mur d'images"
288 303
289#: index.php:770 tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36 304#: index.php:682 tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:36
290#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:36
291#: tmp/tag.cloud.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19 305#: tmp/tag.cloud.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19
292msgid "Tag cloud" 306msgid "Tag cloud"
293msgstr "Nuage de tags" 307msgstr "Nuage de tags"
294 308
295#: index.php:803 tmp/tag.list.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19 309#: index.php:715 tmp/tag.list.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19
296msgid "Tag list" 310msgid "Tag list"
297msgstr "Liste des tags" 311msgstr "Liste des tags"
298 312
299#: index.php:1028 tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:31 313#: index.php:940 tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:31
300#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:31
301msgid "Tools" 314msgid "Tools"
302msgstr "Outils" 315msgstr "Outils"
303 316
304#: index.php:1037 317#: index.php:949
305msgid "You are not supposed to change a password on an Open Shaarli." 318msgid "You are not supposed to change a password on an Open Shaarli."
306msgstr "" 319msgstr ""
307"Vous n'êtes pas censé modifier le mot de passe d'un Shaarli en mode ouvert." 320"Vous n'êtes pas censé modifier le mot de passe d'un Shaarli en mode ouvert."
308 321
309#: index.php:1042 index.php:1084 index.php:1162 index.php:1193 index.php:1293 322#: index.php:954 index.php:996 index.php:1084 index.php:1116 index.php:1221
310msgid "Wrong token." 323msgid "Wrong token."
311msgstr "Jeton invalide." 324msgstr "Jeton invalide."
312 325
313#: index.php:1047 326#: index.php:959
314msgid "The old password is not correct." 327msgid "The old password is not correct."
315msgstr "L'ancien mot de passe est incorrect." 328msgstr "L'ancien mot de passe est incorrect."
316 329
317#: index.php:1067 330#: index.php:979
318msgid "Your password has been changed" 331msgid "Your password has been changed"
319msgstr "Votre mot de passe a été modifié" 332msgstr "Votre mot de passe a été modifié"
320 333
321#: index.php:1072 334#: index.php:984 tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:29
322#: tmp/changepassword.b91ef64efc3688266305ea9b42e5017e.rtpl.php:13
323#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:29
324msgid "Change password" 335msgid "Change password"
325msgstr "Modification du mot de passe" 336msgstr "Modifier le mot de passe"
326 337
327#: index.php:1121 338#: index.php:1042
328msgid "Configuration was saved." 339msgid "Configuration was saved."
329msgstr "La configuration a été sauvegardé." 340msgstr "La configuration a été sauvegardée."
330 341
331#: index.php:1145 tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:24 342#: index.php:1067 tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:24
332msgid "Configure" 343msgid "Configure"
333msgstr "Configurer" 344msgstr "Configurer"
334 345
335#: index.php:1156 tmp/changetag.b91ef64efc3688266305ea9b42e5017e.rtpl.php:13 346#: index.php:1078 tmp/changetag.b91ef64efc3688266305ea9b42e5017e.rtpl.php:13
336#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36 347#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36
337msgid "Manage tags" 348msgid "Manage tags"
338msgstr "Gérer les tags" 349msgstr "Gérer les tags"
339 350
340#: index.php:1174 351#: index.php:1097
341#, php-format 352#, php-format
342msgid "The tag was removed from %d link." 353msgid "The tag was removed from %d link."
343msgid_plural "The tag was removed from %d links." 354msgid_plural "The tag was removed from %d links."
344msgstr[0] "Le tag a été supprimé de %d lien." 355msgstr[0] "Le tag a été supprimé de %d lien."
345msgstr[1] "Le tag a été supprimé de %d liens." 356msgstr[1] "Le tag a été supprimé de %d liens."
346 357
347#: index.php:1175 358#: index.php:1098
348#, php-format 359#, php-format
349msgid "The tag was renamed in %d link." 360msgid "The tag was renamed in %d link."
350msgid_plural "The tag was renamed in %d links." 361msgid_plural "The tag was renamed in %d links."
351msgstr[0] "Le tag a été renommé dans %d lien." 362msgstr[0] "Le tag a été renommé dans %d lien."
352msgstr[1] "Le tag a été renommé dans %d liens." 363msgstr[1] "Le tag a été renommé dans %d liens."
353 364
354#: index.php:1183 tmp/addlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:13 365#: index.php:1106 tmp/addlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:13
355msgid "Shaare a new link" 366msgid "Shaare a new link"
356msgstr "Partager un nouveau lien" 367msgstr "Partager un nouveau lien"
357 368
358#: index.php:1353 tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14 369#: index.php:1281 tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:169
359#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:170
360msgid "Edit" 370msgid "Edit"
361msgstr "Modifier" 371msgstr "Modifier"
362 372
363#: index.php:1353 index.php:1418 373#: index.php:1281 index.php:1351
364#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
365#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:26
366#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:26 374#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:26
367msgid "Shaare" 375msgid "Shaare"
368msgstr "Shaare" 376msgstr "Shaare"
369 377
370#: index.php:1387 378#: index.php:1320
371msgid "Note: " 379msgid "Note: "
372msgstr "Note : " 380msgstr "Note : "
373 381
374#: index.php:1427 tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:65 382#: index.php:1359
383msgid "Invalid link ID provided"
384msgstr ""
385
386#: index.php:1379
375msgid "Export" 387msgid "Export"
376msgstr "Exporter" 388msgstr "Exporter"
377 389
378#: index.php:1489 tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:83 390#: index.php:1441
379msgid "Import" 391msgid "Import"
380msgstr "Importer" 392msgstr "Importer"
381 393
382#: index.php:1499 394#: index.php:1451
383#, php-format 395#, php-format
384msgid "" 396msgid ""
385"The file you are trying to upload is probably bigger than what this " 397"The file you are trying to upload is probably bigger than what this "
@@ -389,16 +401,20 @@ msgstr ""
389"le serveur web peut accepter (%s). Merci de l'envoyer en parties plus " 401"le serveur web peut accepter (%s). Merci de l'envoyer en parties plus "
390"légères." 402"légères."
391 403
392#: index.php:1538 tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:26 404#: index.php:1490 tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:26
393#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:22 405#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:22
394msgid "Plugin administration" 406msgid "Plugin administration"
395msgstr "Administration des extensions" 407msgstr "Administration des plugins"
396 408
397#: index.php:1703 409#: index.php:1542 tmp/thumbnails.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14
410msgid "Thumbnails update"
411msgstr "Mise à jour des miniatures"
412
413#: index.php:1714
398msgid "Search: " 414msgid "Search: "
399msgstr "Recherche : " 415msgstr "Recherche : "
400 416
401#: index.php:1930 417#: index.php:1754
402#, php-format 418#, php-format
403msgid "" 419msgid ""
404"<pre>Sessions do not seem to work correctly on your server.<br>Make sure the " 420"<pre>Sessions do not seem to work correctly on your server.<br>Make sure the "
@@ -408,16 +424,16 @@ msgid ""
408"custom hostname without a dot causes cookie storage to fail. We recommend " 424"custom hostname without a dot causes cookie storage to fail. We recommend "
409"accessing your server via it's IP address or Fully Qualified Domain Name.<br>" 425"accessing your server via it's IP address or Fully Qualified Domain Name.<br>"
410msgstr "" 426msgstr ""
411"<pre>Les sesssions ne semble pas fonctionner sur ce serveur.<br>Assurez vous " 427"<pre>Les sesssions ne semblent pas fonctionner sur ce serveur.<br>Assurez "
412"que la variable « session.save_path » est correctement définie dans votre " 428"vous que la variable « session.save_path » est correctement définie dans "
413"fichier de configuration PHP, et que vous y avez les droits d'écriture." 429"votre fichier de configuration PHP, et que vous avez les droits d'écriture "
414"<br>Ce paramètre pointe actuellement sur %s.<br>Sur certains navigateurs, " 430"dessus.<br>Ce paramètre pointe actuellement sur %s.<br>Sur certains "
415"accéder à votre serveur depuis un nom d'hôte comme « localhost » ou autre " 431"navigateurs, accéder à votre serveur depuis un nom d'hôte comme « localhost "
416"nom personnalisé sans point '.' entraine l'échec de la sauvegarde des " 432"» ou autre nom personnalisé sans point '.' entraine l'échec de la sauvegarde "
417"cookies. Nous vous recommandons d'accéder à votre serveur depuis son adresse " 433"des cookies. Nous vous recommandons d'accéder à votre serveur depuis son "
418"IP ou un <em>Fully Qualified Domain Name</em>.<br>" 434"adresse IP ou un <em>Fully Qualified Domain Name</em>.<br>"
419 435
420#: index.php:1940 436#: index.php:1764
421msgid "Click to try again." 437msgid "Click to try again."
422msgstr "Cliquer ici pour réessayer." 438msgstr "Cliquer ici pour réessayer."
423 439
@@ -432,7 +448,7 @@ msgstr "Shaare"
432 448
433#: plugins/addlink_toolbar/addlink_toolbar.php:50 449#: plugins/addlink_toolbar/addlink_toolbar.php:50
434msgid "Adds the addlink input on the linklist page." 450msgid "Adds the addlink input on the linklist page."
435msgstr "Ajout le formulaire d'ajout de liens sur la page principale." 451msgstr "Ajoute le formulaire d'ajout de liens sur la page principale."
436 452
437#: plugins/archiveorg/archiveorg.php:23 453#: plugins/archiveorg/archiveorg.php:23
438msgid "View on archive.org" 454msgid "View on archive.org"
@@ -448,7 +464,7 @@ msgid ""
448"developers." 464"developers."
449msgstr "" 465msgstr ""
450"Une extension de démonstration couvrant tous les cas d'utilisation pour les " 466"Une extension de démonstration couvrant tous les cas d'utilisation pour les "
451"designers et les développeurs." 467"designers de thèmes et les développeurs d'extensions."
452 468
453#: plugins/isso/isso.php:20 469#: plugins/isso/isso.php:20
454msgid "" 470msgid ""
@@ -458,28 +474,29 @@ msgstr ""
458"Erreur de l'extension Isso : Merci de définir le paramètre « ISSO_SERVER » " 474"Erreur de l'extension Isso : Merci de définir le paramètre « ISSO_SERVER » "
459"dans la page d'administration des extensions." 475"dans la page d'administration des extensions."
460 476
461#: plugins/isso/isso.php:63 477#: plugins/isso/isso.php:90
462msgid "Let visitor comment your shaares on permalinks with Isso." 478msgid "Let visitor comment your shaares on permalinks with Isso."
463msgstr "" 479msgstr ""
464"Permet aux visiteurs de commenter vos shaares sur les permaliens avec Isso." 480"Permettre aux visiteurs de commenter vos shaares sur les permaliens avec "
481"Isso."
465 482
466#: plugins/isso/isso.php:64 483#: plugins/isso/isso.php:91
467msgid "Isso server URL (without 'http://')" 484msgid "Isso server URL (without 'http://')"
468msgstr "URL du serveur Isso (sans 'http://')" 485msgstr "URL du serveur Isso (sans 'http://')"
469 486
470#: plugins/markdown/markdown.php:158 487#: plugins/markdown/markdown.php:161
471msgid "Description will be rendered with" 488msgid "Description will be rendered with"
472msgstr "La description sera générée avec" 489msgstr "La description sera générée avec"
473 490
474#: plugins/markdown/markdown.php:159 491#: plugins/markdown/markdown.php:162
475msgid "Markdown syntax documentation" 492msgid "Markdown syntax documentation"
476msgstr "Documentation sur la syntaxe Markdown" 493msgstr "Documentation sur la syntaxe Markdown"
477 494
478#: plugins/markdown/markdown.php:160 495#: plugins/markdown/markdown.php:163
479msgid "Markdown syntax" 496msgid "Markdown syntax"
480msgstr "la syntaxe Markdown" 497msgstr "la syntaxe Markdown"
481 498
482#: plugins/markdown/markdown.php:339 499#: plugins/markdown/markdown.php:347
483msgid "" 500msgid ""
484"Render shaare description with Markdown syntax.<br><strong>Warning</" 501"Render shaare description with Markdown syntax.<br><strong>Warning</"
485"strong>:\n" 502"strong>:\n"
@@ -555,7 +572,7 @@ msgstr "Active la publication de flux vers PubSubHubbub."
555 572
556#: plugins/qrcode/qrcode.php:69 plugins/wallabag/wallabag.php:68 573#: plugins/qrcode/qrcode.php:69 plugins/wallabag/wallabag.php:68
557msgid "For each link, add a QRCode icon." 574msgid "For each link, add a QRCode icon."
558msgstr "Pour chaque liens, ajouter une icône de QRCode." 575msgstr "Pour chaque lien, ajouter une icône de QRCode."
559 576
560#: plugins/wallabag/wallabag.php:21 577#: plugins/wallabag/wallabag.php:21
561msgid "" 578msgid ""
@@ -577,38 +594,20 @@ msgstr "URL de l'API Wallabag"
577msgid "Wallabag API version (1 or 2)" 594msgid "Wallabag API version (1 or 2)"
578msgstr "Version de l'API Wallabag (1 ou 2)" 595msgstr "Version de l'API Wallabag (1 ou 2)"
579 596
580#: tests/LanguagesTest.php:188 tests/LanguagesTest.php:201 597#: tests/LanguagesTest.php:214 tests/LanguagesTest.php:227
581#: tests/languages/fr/LanguagesFrTest.php:160 598#: tests/languages/fr/LanguagesFrTest.php:160
582#: tests/languages/fr/LanguagesFrTest.php:173 599#: tests/languages/fr/LanguagesFrTest.php:173
583#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:81 600#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:85
584#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:81
585msgid "Search" 601msgid "Search"
586msgid_plural "Search" 602msgid_plural "Search"
587msgstr[0] "Rechercher" 603msgstr[0] "Rechercher"
588msgstr[1] "Rechercher" 604msgstr[1] "Rechercher"
589 605
590#: tmp/404.b91ef64efc3688266305ea9b42e5017e.rtpl.php:12
591msgid "Sorry, nothing to see here."
592msgstr "Désolé, il y a rien à voir ici."
593
594#: tmp/addlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16 606#: tmp/addlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
595msgid "URL or leave empty to post a note" 607msgid "URL or leave empty to post a note"
596msgstr "URL ou laisser vide pour créer une note" 608msgstr "URL ou laisser vide pour créer une note"
597 609
598#: tmp/changepassword.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
599msgid "Current password"
600msgstr "Mot de passe actuel"
601
602#: tmp/changepassword.b91ef64efc3688266305ea9b42e5017e.rtpl.php:19
603msgid "New password"
604msgstr "Nouveau mot de passe"
605
606#: tmp/changepassword.b91ef64efc3688266305ea9b42e5017e.rtpl.php:23
607msgid "Change"
608msgstr "Changer"
609
610#: tmp/changetag.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16 610#: tmp/changetag.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
611#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:77
612msgid "Tag" 611msgid "Tag"
613msgstr "Tag" 612msgstr "Tag"
614 613
@@ -625,8 +624,8 @@ msgid "Rename"
625msgstr "Renommer" 624msgstr "Renommer"
626 625
627#: tmp/changetag.b91ef64efc3688266305ea9b42e5017e.rtpl.php:35 626#: tmp/changetag.b91ef64efc3688266305ea9b42e5017e.rtpl.php:35
628#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:79 627#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:77
629#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:172 628#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:171
630msgid "Delete" 629msgid "Delete"
631msgstr "Supprimer" 630msgstr "Supprimer"
632 631
@@ -638,6 +637,34 @@ msgstr "Vous pouvez aussi modifier les tags dans la"
638msgid "tag list" 637msgid "tag list"
639msgstr "liste des tags" 638msgstr "liste des tags"
640 639
640#: tmp/configure.90100d2eaf5d3705e14b9b4f78ecddc9.rtpl.php:143
641#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:296
642msgid "All"
643msgstr "Tous"
644
645#: tmp/configure.90100d2eaf5d3705e14b9b4f78ecddc9.rtpl.php:147
646#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:300
647msgid "Only common media hosts"
648msgstr "Seulement les hébergeurs de média connus"
649
650#: tmp/configure.90100d2eaf5d3705e14b9b4f78ecddc9.rtpl.php:151
651#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:304
652msgid "None"
653msgstr "Aucune"
654
655#: tmp/configure.90100d2eaf5d3705e14b9b4f78ecddc9.rtpl.php:158
656#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:281
657msgid "You need to enable the extension <code>php-gd</code> to use thumbnails."
658msgstr ""
659"Vous devez activer l'extension <code>php-gd</code> pour utiliser les "
660"miniatures."
661
662#: tmp/configure.90100d2eaf5d3705e14b9b4f78ecddc9.rtpl.php:162
663#, fuzzy
664#| msgid "Enable thumbnails"
665msgid "Synchonize thumbnails"
666msgstr "Activer les miniatures"
667
641#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:29 668#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:29
642msgid "title" 669msgid "title"
643msgstr "titre" 670msgstr "titre"
@@ -655,22 +682,18 @@ msgid "Theme"
655msgstr "Thème" 682msgstr "Thème"
656 683
657#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:87 684#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:87
658#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:78
659msgid "Language" 685msgid "Language"
660msgstr "Langue" 686msgstr "Langue"
661 687
662#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:116 688#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:116
663#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:102
664msgid "Timezone" 689msgid "Timezone"
665msgstr "Fuseau horaire" 690msgstr "Fuseau horaire"
666 691
667#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:117 692#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:117
668#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:103
669msgid "Continent" 693msgid "Continent"
670msgstr "Continent" 694msgstr "Continent"
671 695
672#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:117 696#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:117
673#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:103
674msgid "City" 697msgid "City"
675msgstr "Ville" 698msgstr "Ville"
676 699
@@ -711,33 +734,38 @@ msgid "Do not show any links if the user is not logged in"
711msgstr "N'afficher aucun lien sans être connecté" 734msgstr "N'afficher aucun lien sans être connecté"
712 735
713#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:231 736#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:231
714#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:150
715msgid "Check updates" 737msgid "Check updates"
716msgstr "Vérifier les mises à jour" 738msgstr "Vérifier les mises à jour"
717 739
718#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:232 740#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:232
719#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:152
720msgid "Notify me when a new release is ready" 741msgid "Notify me when a new release is ready"
721msgstr "Me notifier lorsqu'une nouvelle version est disponible" 742msgstr "Me notifier lorsqu'une nouvelle version est disponible"
722 743
723#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:247 744#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:247
724#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:169
725msgid "Enable REST API" 745msgid "Enable REST API"
726msgstr "Activer l'API REST" 746msgstr "Activer l'API REST"
727 747
728#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:248 748#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:248
729#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:170
730msgid "Allow third party software to use Shaarli such as mobile application" 749msgid "Allow third party software to use Shaarli such as mobile application"
731msgstr "" 750msgstr ""
732"Permets aux applications tierces d'utiliser Shaarli, par exemple les " 751"Permet aux applications tierces d'utiliser Shaarli, par exemple les "
733"applications mobiles" 752"applications mobiles"
734 753
735#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:263 754#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:263
736msgid "API secret" 755msgid "API secret"
737msgstr "Clé d'API secrète" 756msgstr "Clé d'API secrète"
738 757
739#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:274 758#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:277
740#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:74 759msgid "Enable thumbnails"
760msgstr "Activer les miniatures"
761
762#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:285
763#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:56
764msgid "Synchronize thumbnails"
765msgstr "Synchroniser les miniatures"
766
767#: tmp/configure.b91ef64efc3688266305ea9b42e5017e.rtpl.php:312
768#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:72
741#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:139 769#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:139
742#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:199 770#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:199
743msgid "Save" 771msgid "Save"
@@ -763,25 +791,27 @@ msgstr "Tous les liens d'un jour sur une page."
763msgid "Next day" 791msgid "Next day"
764msgstr "Jour suivant" 792msgstr "Jour suivant"
765 793
766#: tpl/editlink.html 794#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14
767msgid "Edit Shaare" 795msgid "Edit Shaare"
768msgstr "Modifier le Shaare" 796msgstr "Modifier le Shaare"
797
798#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14
769msgid "New Shaare" 799msgid "New Shaare"
770msgstr "Nouveau Shaare" 800msgstr "Nouveau Shaare"
771 801
772#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:25 802#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:23
773msgid "Created:" 803msgid "Created:"
774msgstr "Création :" 804msgstr "Création :"
775 805
776#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:28 806#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:26
777msgid "URL" 807msgid "URL"
778msgstr "URL" 808msgstr "URL"
779 809
780#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:34 810#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:32
781msgid "Title" 811msgid "Title"
782msgstr "Titre" 812msgstr "Titre"
783 813
784#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:40 814#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:38
785#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:42 815#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:42
786#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:75 816#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:75
787#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:99 817#: tmp/pluginsadmin.b91ef64efc3688266305ea9b42e5017e.rtpl.php:99
@@ -789,140 +819,40 @@ msgstr "Titre"
789msgid "Description" 819msgid "Description"
790msgstr "Description" 820msgstr "Description"
791 821
792#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:46 822#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:44
793msgid "Tags" 823msgid "Tags"
794msgstr "Tags" 824msgstr "Tags"
795 825
796#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:59 826#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:57
797#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36 827#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:167
798#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:168
799msgid "Private" 828msgid "Private"
800msgstr "Privé" 829msgstr "Privé"
801 830
802#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:74 831#: tmp/editlink.b91ef64efc3688266305ea9b42e5017e.rtpl.php:72
803msgid "Apply Changes" 832msgid "Apply Changes"
804msgstr "Appliquer" 833msgstr "Appliquer les changements"
805
806#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
807msgid "Export Database"
808msgstr "Exporter les données"
809
810#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:24
811msgid "Selection"
812msgstr "Choisir"
813
814#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:31
815msgid "All"
816msgstr "Tous"
817
818#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:41
819msgid "Public"
820msgstr "Publics"
821
822#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:52
823msgid "Prepend note permalinks with this Shaarli instance's URL"
824msgstr "Préfixer les liens de notes avec l'URL de l'instance de Shaarli"
825
826#: tmp/export.b91ef64efc3688266305ea9b42e5017e.rtpl.php:53
827msgid "Useful to import bookmarks in a web browser"
828msgstr "Utile pour importer les marques-pages dans un navigateur"
829
830#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16
831msgid "Import Database"
832msgstr "Importer des données"
833
834#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:23
835msgid "Maximum size allowed:"
836msgstr "Taille maximum autorisée :"
837
838#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:29
839msgid "Visibility"
840msgstr "Visibilité"
841
842#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36
843msgid "Use values from the imported file, default to public"
844msgstr ""
845"Utiliser les valeurs présentes dans le fichier d'import, public par défaut"
846
847#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:41
848msgid "Import all bookmarks as private"
849msgstr "Importer tous les liens comme privés"
850
851#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:46
852msgid "Import all bookmarks as public"
853msgstr "Importer tous les liens comme publics"
854
855#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:57
856msgid "Overwrite existing bookmarks"
857msgstr "Remplacer les liens existants"
858
859#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:58
860msgid "Duplicates based on URL"
861msgstr "Les doublons s'appuient sur les URL"
862
863#: tmp/import.b91ef64efc3688266305ea9b42e5017e.rtpl.php:72
864msgid "Add default tags"
865msgstr "Ajouter des tags par défaut"
866
867#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:22
868msgid "Install Shaarli"
869msgstr "Installation de Shaarli"
870
871#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:25
872msgid "It looks like it's the first time you run Shaarli. Please configure it."
873msgstr ""
874"Il semblerait que ça soit la première fois que vous lancez Shaarli. Merci de "
875"le configurer."
876
877#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:33
878#: tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:30
879#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:147
880#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:147
881msgid "Username"
882msgstr "Nom d'utilisateur"
883
884#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:48
885#: tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:34
886#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:148
887#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:148
888msgid "Password"
889msgstr "Mot de passe"
890
891#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:63
892msgid "Shaarli title"
893msgstr "Titre du Shaarli"
894
895#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:69
896msgid "My links"
897msgstr "Mes liens"
898
899#: tmp/install.b91ef64efc3688266305ea9b42e5017e.rtpl.php:182
900msgid "Install"
901msgstr "Installer"
902 834
903#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14 835#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14
904#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:80 836#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:79
905msgid "shaare" 837msgid "shaare"
906msgid_plural "shaares" 838msgid_plural "shaares"
907msgstr[0] "shaare" 839msgstr[0] "shaare"
908msgstr[1] "shaares" 840msgstr[1] "shaares"
909 841
910#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:18 842#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:18
911#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:84 843#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:83
912msgid "private link" 844msgid "private link"
913msgid_plural "private links" 845msgid_plural "private links"
914msgstr[0] "lien privé" 846msgstr[0] "lien privé"
915msgstr[1] "liens privés" 847msgstr[1] "liens privés"
916 848
917#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:31 849#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:30
918#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:117 850#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:121
919#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:117
920msgid "Search text" 851msgid "Search text"
921msgstr "Recherche texte" 852msgstr "Recherche texte"
922 853
923#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:38 854#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:37
924#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:124 855#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:128
925#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:124
926#: tmp/tag.cloud.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36 856#: tmp/tag.cloud.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36
927#: tmp/tag.cloud.b91ef64efc3688266305ea9b42e5017e.rtpl.php:64 857#: tmp/tag.cloud.b91ef64efc3688266305ea9b42e5017e.rtpl.php:64
928#: tmp/tag.list.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36 858#: tmp/tag.list.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36
@@ -930,85 +860,84 @@ msgstr "Recherche texte"
930msgid "Filter by tag" 860msgid "Filter by tag"
931msgstr "Filtrer par tag" 861msgstr "Filtrer par tag"
932 862
933#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:111 863#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:110
934msgid "Nothing found." 864msgid "Nothing found."
935msgstr "Aucun résultat." 865msgstr "Aucun résultat."
936 866
937#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:119 867#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:118
938#, php-format 868#, php-format
939msgid "%s result" 869msgid "%s result"
940msgid_plural "%s results" 870msgid_plural "%s results"
941msgstr[0] "%s résultat" 871msgstr[0] "%s résultat"
942msgstr[1] "%s résultats" 872msgstr[1] "%s résultats"
943 873
944#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:123 874#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:122
945msgid "for" 875msgid "for"
946msgstr "pour" 876msgstr "pour"
947 877
948#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:130 878#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:129
949msgid "tagged" 879msgid "tagged"
950msgstr "taggé" 880msgstr "taggé"
951 881
952#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:134 882#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:133
953msgid "Remove tag" 883msgid "Remove tag"
954msgstr "Retirer le tag" 884msgstr "Retirer le tag"
955 885
956#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:143 886#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:142
957msgid "with status" 887msgid "with status"
958msgstr "avec le statut" 888msgstr "avec le statut"
959 889
960#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:154 890#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:153
961msgid "without any tag" 891msgid "without any tag"
962msgstr "sans tag" 892msgstr "sans tag"
963 893
964#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:174 894#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:173
965#: tmp/page.footer.b91ef64efc3688266305ea9b42e5017e.rtpl.php:42
966#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:42 895#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:42
967msgid "Fold" 896msgid "Fold"
968msgstr "Replier" 897msgstr "Replier"
969 898
970#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:176 899#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:175
971msgid "Edited: " 900msgid "Edited: "
972msgstr "Modifié : " 901msgstr "Modifié : "
973 902
974#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:180 903#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:179
975msgid "permalink" 904msgid "permalink"
976msgstr "permalien" 905msgstr "permalien"
977 906
978#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:182 907#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:181
979msgid "Add tag" 908msgid "Add tag"
980msgstr "Ajouter un tag" 909msgstr "Ajouter un tag"
981 910
982#: tmp/linklist.paging.b91ef64efc3688266305ea9b42e5017e.rtpl.php:7 911#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:183
912msgid "Toggle sticky"
913msgstr "Changer statut épinglé"
914
915#: tmp/linklist.b91ef64efc3688266305ea9b42e5017e.rtpl.php:185
916msgid "Sticky"
917msgstr "Épinglé"
918
983#: tmp/linklist.paging.cedf684561d925457130839629000a81.rtpl.php:7 919#: tmp/linklist.paging.cedf684561d925457130839629000a81.rtpl.php:7
984msgid "Filters" 920msgid "Filters"
985msgstr "Filtres" 921msgstr "Filtres"
986 922
987#: tmp/linklist.paging.b91ef64efc3688266305ea9b42e5017e.rtpl.php:12
988#: tmp/linklist.paging.cedf684561d925457130839629000a81.rtpl.php:12 923#: tmp/linklist.paging.cedf684561d925457130839629000a81.rtpl.php:12
989msgid "Only display private links" 924msgid "Only display private links"
990msgstr "Afficher uniquement les liens privés" 925msgstr "Afficher uniquement les liens privés"
991 926
992#: tmp/linklist.paging.b91ef64efc3688266305ea9b42e5017e.rtpl.php:15
993#: tmp/linklist.paging.cedf684561d925457130839629000a81.rtpl.php:15 927#: tmp/linklist.paging.cedf684561d925457130839629000a81.rtpl.php:15
994msgid "Only display public links" 928msgid "Only display public links"
995msgstr "Afficher uniquement les liens publics" 929msgstr "Afficher uniquement les liens publics"
996 930
997#: tmp/linklist.paging.b91ef64efc3688266305ea9b42e5017e.rtpl.php:20
998#: tmp/linklist.paging.cedf684561d925457130839629000a81.rtpl.php:20 931#: tmp/linklist.paging.cedf684561d925457130839629000a81.rtpl.php:20
999msgid "Filter untagged links" 932msgid "Filter untagged links"
1000msgstr "Filtrer par liens privés" 933msgstr "Filtrer par liens privés"
1001 934
1002#: tmp/linklist.paging.b91ef64efc3688266305ea9b42e5017e.rtpl.php:24
1003#: tmp/linklist.paging.b91ef64efc3688266305ea9b42e5017e.rtpl.php:76
1004#: tmp/linklist.paging.cedf684561d925457130839629000a81.rtpl.php:24 935#: tmp/linklist.paging.cedf684561d925457130839629000a81.rtpl.php:24
1005#: tmp/linklist.paging.cedf684561d925457130839629000a81.rtpl.php:76 936#: tmp/linklist.paging.cedf684561d925457130839629000a81.rtpl.php:76
1006#: tmp/page.footer.b91ef64efc3688266305ea9b42e5017e.rtpl.php:43
1007#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:43 937#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:43
1008msgid "Fold all" 938msgid "Fold all"
1009msgstr "Replier tout" 939msgstr "Replier tout"
1010 940
1011#: tmp/linklist.paging.b91ef64efc3688266305ea9b42e5017e.rtpl.php:69
1012#: tmp/linklist.paging.cedf684561d925457130839629000a81.rtpl.php:69 941#: tmp/linklist.paging.cedf684561d925457130839629000a81.rtpl.php:69
1013msgid "Links per page" 942msgid "Links per page"
1014msgstr "Liens par page" 943msgstr "Liens par page"
@@ -1017,71 +946,85 @@ msgstr "Liens par page"
1017msgid "" 946msgid ""
1018"You have been banned after too many failed login attempts. Try again later." 947"You have been banned after too many failed login attempts. Try again later."
1019msgstr "" 948msgstr ""
1020"Vous avez été banni après trop d'échec d'authentification. Merci de " 949"Vous avez été banni après trop d'échecs d'authentification. Merci de "
1021"réessayer plus tard." 950"réessayer plus tard."
1022 951
1023#: tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:41 952#: tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:30
1024#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:151
1025#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:151 953#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:151
954msgid "Username"
955msgstr "Nom d'utilisateur"
956
957#: tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:34
958#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:152
959msgid "Password"
960msgstr "Mot de passe"
961
962#: tmp/loginform.b91ef64efc3688266305ea9b42e5017e.rtpl.php:41
963#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:155
1026msgid "Remember me" 964msgid "Remember me"
1027msgstr "Rester connecté" 965msgstr "Rester connecté"
1028 966
1029#: tmp/page.footer.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14
1030#: tmp/page.footer.b91ef64efc3688266305ea9b42e5017e.rtpl.php:48
1031#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:14 967#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:14
1032#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:48 968#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:48
1033msgid "by the Shaarli community" 969msgid "by the Shaarli community"
1034msgstr "par la communauté Shaarli" 970msgstr "par la communauté Shaarli"
1035 971
1036#: tmp/page.footer.b91ef64efc3688266305ea9b42e5017e.rtpl.php:15
1037#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:15 972#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:15
1038msgid "Documentation" 973msgid "Documentation"
1039msgstr "Documentation" 974msgstr "Documentation"
1040 975
1041#: tmp/page.footer.b91ef64efc3688266305ea9b42e5017e.rtpl.php:44
1042#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:44 976#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:44
1043msgid "Expand" 977msgid "Expand"
1044msgstr "Déplier" 978msgstr "Déplier"
1045 979
1046#: tmp/page.footer.b91ef64efc3688266305ea9b42e5017e.rtpl.php:45
1047#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:45 980#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:45
1048msgid "Expand all" 981msgid "Expand all"
1049msgstr "Déplier tout" 982msgstr "Déplier tout"
1050 983
1051#: tmp/page.footer.b91ef64efc3688266305ea9b42e5017e.rtpl.php:46
1052#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:46 984#: tmp/page.footer.cedf684561d925457130839629000a81.rtpl.php:46
1053msgid "Are you sure you want to delete this link?" 985msgid "Are you sure you want to delete this link?"
1054msgstr "Êtes-vous sûr de vouloir supprimer ce lien ?" 986msgstr "Êtes-vous sûr de vouloir supprimer ce lien ?"
1055 987
1056#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:61 988#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:65
1057#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:86 989#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:90
1058#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:61
1059#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:86
1060msgid "RSS Feed" 990msgid "RSS Feed"
1061msgstr "Flux RSS" 991msgstr "Flux RSS"
1062 992
1063#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:66 993#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:70
1064#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:102 994#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:106
1065#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:66
1066#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:102
1067msgid "Logout" 995msgid "Logout"
1068msgstr "Déconnexion" 996msgstr "Déconnexion"
1069 997
1070#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:169 998#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:173
1071#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:169
1072msgid "is available" 999msgid "is available"
1073msgstr "est disponible" 1000msgstr "est disponible"
1074 1001
1075#: tmp/page.header.b91ef64efc3688266305ea9b42e5017e.rtpl.php:176 1002#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:180
1076#: tmp/page.header.cedf684561d925457130839629000a81.rtpl.php:176
1077msgid "Error" 1003msgid "Error"
1078msgstr "Erreur" 1004msgstr "Erreur"
1079 1005
1080#: tmp/picwall.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16 1006#: tmp/picwall.b91ef64efc3688266305ea9b42e5017e.rtpl.php:14
1007msgid "Picture wall unavailable (thumbnails are disabled)."
1008msgstr ""
1009"Le mur d'images n'est pas disponible (les miniatures sont désactivées)."
1010
1011#: tmp/picwall.b91ef64efc3688266305ea9b42e5017e.rtpl.php:24
1012#, fuzzy
1013#| msgid ""
1014#| "You don't have any cached thumbnail. Try to <a href=\"?do=thumbs_update"
1015#| "\">synchronize them</a>."
1016msgid ""
1017"There is no cached thumbnail. Try to <a href=\"?do=thumbs_update"
1018"\">synchronize them</a>."
1019msgstr ""
1020"Il n'y a aucune miniature en cache. Essayer de <a href=\"?do=thumbs_update"
1021"\">les synchroniser</a>."
1022
1023#: tmp/picwall.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36
1081msgid "Picture Wall" 1024msgid "Picture Wall"
1082msgstr "Mur d'images" 1025msgstr "Mur d'images"
1083 1026
1084#: tmp/picwall.b91ef64efc3688266305ea9b42e5017e.rtpl.php:16 1027#: tmp/picwall.b91ef64efc3688266305ea9b42e5017e.rtpl.php:36
1085msgid "pics" 1028msgid "pics"
1086msgstr "images" 1029msgstr "images"
1087 1030
@@ -1155,22 +1098,18 @@ msgstr "tags"
1155msgid "List all links with those tags" 1098msgid "List all links with those tags"
1156msgstr "Lister tous les liens avec ces tags" 1099msgstr "Lister tous les liens avec ces tags"
1157 1100
1158#: tmp/tag.sort.b91ef64efc3688266305ea9b42e5017e.rtpl.php:3
1159#: tmp/tag.sort.cedf684561d925457130839629000a81.rtpl.php:3 1101#: tmp/tag.sort.cedf684561d925457130839629000a81.rtpl.php:3
1160msgid "Sort by:" 1102msgid "Sort by:"
1161msgstr "Trier par :" 1103msgstr "Trier par :"
1162 1104
1163#: tmp/tag.sort.b91ef64efc3688266305ea9b42e5017e.rtpl.php:5
1164#: tmp/tag.sort.cedf684561d925457130839629000a81.rtpl.php:5 1105#: tmp/tag.sort.cedf684561d925457130839629000a81.rtpl.php:5
1165msgid "Cloud" 1106msgid "Cloud"
1166msgstr "Nuage" 1107msgstr "Nuage"
1167 1108
1168#: tmp/tag.sort.b91ef64efc3688266305ea9b42e5017e.rtpl.php:6
1169#: tmp/tag.sort.cedf684561d925457130839629000a81.rtpl.php:6 1109#: tmp/tag.sort.cedf684561d925457130839629000a81.rtpl.php:6
1170msgid "Most used" 1110msgid "Most used"
1171msgstr "Plus utilisés" 1111msgstr "Plus utilisés"
1172 1112
1173#: tmp/tag.sort.b91ef64efc3688266305ea9b42e5017e.rtpl.php:7
1174#: tmp/tag.sort.cedf684561d925457130839629000a81.rtpl.php:7 1113#: tmp/tag.sort.cedf684561d925457130839629000a81.rtpl.php:7
1175msgid "Alphabetical" 1114msgid "Alphabetical"
1176msgstr "Alphabétique" 1115msgstr "Alphabétique"
@@ -1185,7 +1124,7 @@ msgstr "Changer les paramètres de Shaarli : titre, fuseau horaire, etc."
1185 1124
1186#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:17 1125#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:17
1187msgid "Configure your Shaarli" 1126msgid "Configure your Shaarli"
1188msgstr "Conguration de Shaarli" 1127msgstr "Configurer Shaarli"
1189 1128
1190#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:21 1129#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:21
1191msgid "Enable, disable and configure plugins" 1130msgid "Enable, disable and configure plugins"
@@ -1193,51 +1132,63 @@ msgstr "Activer, désactiver et configurer les extensions"
1193 1132
1194#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:28 1133#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:28
1195msgid "Change your password" 1134msgid "Change your password"
1196msgstr "Modification du mot de passe" 1135msgstr "Modifier le mot de passe"
1197 1136
1198#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:35 1137#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:35
1199msgid "Rename or delete a tag in all links" 1138msgid "Rename or delete a tag in all links"
1200msgstr "Rename or delete a tag in all links" 1139msgstr "Renommer ou supprimer un tag dans tous les liens"
1201 1140
1202#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:41 1141#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:41
1142#, fuzzy
1143#| msgid ""
1144#| "Import Netscape HTML bookmarks (as exported from Firefox, Chrome, Opera, "
1145#| "delicious…)"
1203msgid "" 1146msgid ""
1204"Import Netscape HTML bookmarks (as exported from Firefox, Chrome, Opera, " 1147"Import Netscape HTML bookmarks (as exported from Firefox, Chrome, Opera, "
1205"delicious...)" 1148"delicious...)"
1206msgstr "" 1149msgstr ""
1207"Importer des marques pages au format Netscape HTML (comme exportés depuis " 1150"Importer des marques pages au format Netscape HTML (comme exportés depuis "
1208"Firefox, Chrome, Opera, delicious...)" 1151"Firefox, Chrome, Opera, delicious)"
1209 1152
1210#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:42 1153#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:42
1211msgid "Import links" 1154msgid "Import links"
1212msgstr "Importer des liens" 1155msgstr "Importer des liens"
1213 1156
1214#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:47 1157#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:47
1158#, fuzzy
1159#| msgid ""
1160#| "Export Netscape HTML bookmarks (which can be imported in Firefox, Chrome, "
1161#| "Opera, delicious…)"
1215msgid "" 1162msgid ""
1216"Export Netscape HTML bookmarks (which can be imported in Firefox, Chrome, " 1163"Export Netscape HTML bookmarks (which can be imported in Firefox, Chrome, "
1217"Opera, delicious...)" 1164"Opera, delicious...)"
1218msgstr "" 1165msgstr ""
1219"Exporter les marques pages au format Netscape HTML (comme exportés depuis " 1166"Exporter les marques pages au format Netscape HTML (comme exportés depuis "
1220"Firefox, Chrome, Opera, delicious...)" 1167"Firefox, Chrome, Opera, delicious)"
1221 1168
1222#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:48 1169#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:48
1223msgid "Export database" 1170msgid "Export database"
1224msgstr "Exporter les données" 1171msgstr "Exporter les données"
1225 1172
1226#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:71 1173#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:55
1174msgid "Synchronize all link thumbnails"
1175msgstr "Synchroniser toutes les miniatures"
1176
1177#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:81
1227msgid "" 1178msgid ""
1228"Drag one of these button to your bookmarks toolbar or right-click it and " 1179"Drag one of these button to your bookmarks toolbar or right-click it and "
1229"\"Bookmark This Link\"" 1180"\"Bookmark This Link\""
1230msgstr "" 1181msgstr ""
1231"Glisser un de ces bouttons dans votre barre de favoris ou cliquer droit " 1182"Glisser un de ces boutons dans votre barre de favoris ou cliquer droit "
1232"dessus et « Ajouter aux favoris »" 1183"dessus et « Ajouter aux favoris »"
1233 1184
1234#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:72 1185#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:82
1235msgid "then click on the bookmarklet in any page you want to share." 1186msgid "then click on the bookmarklet in any page you want to share."
1236msgstr "" 1187msgstr ""
1237"puis cliquer sur le marque page depuis un site que vous souhaitez partager." 1188"puis cliquer sur le marque-page depuis un site que vous souhaitez partager."
1238 1189
1239#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:76 1190#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:86
1240#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:100 1191#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:110
1241msgid "" 1192msgid ""
1242"Drag this link to your bookmarks toolbar or right-click it and Bookmark This " 1193"Drag this link to your bookmarks toolbar or right-click it and Bookmark This "
1243"Link" 1194"Link"
@@ -1245,57 +1196,40 @@ msgstr ""
1245"Glisser ce lien dans votre barre de favoris ou cliquer droit dessus et « " 1196"Glisser ce lien dans votre barre de favoris ou cliquer droit dessus et « "
1246"Ajouter aux favoris »" 1197"Ajouter aux favoris »"
1247 1198
1248#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:77 1199#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:87
1249msgid "then click ✚Shaare link button in any page you want to share" 1200msgid "then click ✚Shaare link button in any page you want to share"
1250msgstr "puis cliquer sur ✚Shaare depuis un site que vous souhaitez partager" 1201msgstr "puis cliquer sur ✚Shaare depuis un site que vous souhaitez partager"
1251 1202
1252#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:86 1203#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:96
1253#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:108 1204#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:118
1254msgid "The selected text is too long, it will be truncated." 1205msgid "The selected text is too long, it will be truncated."
1255msgstr "Le texte sélectionné est trop long, il sera tronqué." 1206msgstr "Le texte sélectionné est trop long, il sera tronqué."
1256 1207
1257#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:96 1208#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:106
1258msgid "Shaare link" 1209msgid "Shaare link"
1259msgstr "Shaare" 1210msgstr "Shaare"
1260 1211
1261#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:101 1212#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:111
1262msgid "" 1213msgid ""
1263"Then click ✚Add Note button anytime to start composing a private Note (text " 1214"Then click ✚Add Note button anytime to start composing a private Note (text "
1264"post) to your Shaarli" 1215"post) to your Shaarli"
1265msgstr "" 1216msgstr ""
1266"Puis cliquer sur ✚Add Note pour commencer à rédiger une Note sur Shaarli" 1217"Puis cliquer sur ✚Add Note pour commencer à rédiger une Note sur Shaarli"
1267 1218
1268#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:117 1219#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:127
1269msgid "Add Note" 1220msgid "Add Note"
1270msgstr "Ajouter une Note" 1221msgstr "Ajouter une Note"
1271 1222
1272#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:129 1223#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:136
1273msgid ""
1274"You need to browse your Shaarli over <strong>HTTPS</strong> to use this "
1275"functionality."
1276msgstr ""
1277"Vous devez utiliser Shaarli en <strong>HTTPS</strong> pour utiliser cette "
1278"fonctionalité."
1279
1280#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:134
1281msgid "Add to"
1282msgstr "Ajouter à"
1283
1284#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:145
1285msgid "3rd party" 1224msgid "3rd party"
1286msgstr "Applications tierces" 1225msgstr "Applications tierces"
1287 1226
1288#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:147 1227#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:139
1289#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:153 1228#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:144
1290msgid "Plugin"
1291msgstr "Extension"
1292
1293#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:148
1294#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:154
1295msgid "plugin" 1229msgid "plugin"
1296msgstr "extension" 1230msgstr "extension"
1297 1231
1298#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:175 1232#: tmp/tools.b91ef64efc3688266305ea9b42e5017e.rtpl.php:169
1299msgid "" 1233msgid ""
1300"Drag this link to your bookmarks toolbar, or right-click it and choose " 1234"Drag this link to your bookmarks toolbar, or right-click it and choose "
1301"Bookmark This Link" 1235"Bookmark This Link"
@@ -1303,6 +1237,107 @@ msgstr ""
1303"Glisser ce lien dans votre barre de favoris ou cliquer droit dessus et « " 1237"Glisser ce lien dans votre barre de favoris ou cliquer droit dessus et « "
1304"Ajouter aux favoris »" 1238"Ajouter aux favoris »"
1305 1239
1240#~ msgid "Sorry, nothing to see here."
1241#~ msgstr "Désolé, il y a rien à voir ici."
1242
1243#~ msgid "Current password"
1244#~ msgstr "Mot de passe actuel"
1245
1246#~ msgid "New password"
1247#~ msgstr "Nouveau mot de passe"
1248
1249#~ msgid "Change"
1250#~ msgstr "Changer"
1251
1252#~ msgid "Export Database"
1253#~ msgstr "Exporter les données"
1254
1255#~ msgid "Selection"
1256#~ msgstr "Choisir"
1257
1258#~ msgid "Public"
1259#~ msgstr "Publics"
1260
1261#~ msgid "Prepend note permalinks with this Shaarli instance's URL"
1262#~ msgstr "Préfixer les liens de note avec l'URL de l'instance de Shaarli"
1263
1264#~ msgid "Useful to import bookmarks in a web browser"
1265#~ msgstr "Utile pour importer les marques-pages dans un navigateur"
1266
1267#~ msgid "Import Database"
1268#~ msgstr "Importer des données"
1269
1270#~ msgid "Maximum size allowed:"
1271#~ msgstr "Taille maximum autorisée :"
1272
1273#~ msgid "Visibility"
1274#~ msgstr "Visibilité"
1275
1276#~ msgid "Use values from the imported file, default to public"
1277#~ msgstr ""
1278#~ "Utiliser les valeurs présentes dans le fichier d'import, public par défaut"
1279
1280#~ msgid "Import all bookmarks as private"
1281#~ msgstr "Importer tous les liens comme privés"
1282
1283#~ msgid "Import all bookmarks as public"
1284#~ msgstr "Importer tous les liens comme publics"
1285
1286#~ msgid "Overwrite existing bookmarks"
1287#~ msgstr "Remplacer les liens existants"
1288
1289#~ msgid "Duplicates based on URL"
1290#~ msgstr "Les doublons s'appuient sur les URL"
1291
1292#~ msgid "Add default tags"
1293#~ msgstr "Ajouter des tags par défaut"
1294
1295#~ msgid "Install Shaarli"
1296#~ msgstr "Installation de Shaarli"
1297
1298#~ msgid ""
1299#~ "It looks like it's the first time you run Shaarli. Please configure it."
1300#~ msgstr ""
1301#~ "Il semblerait que ça soit la première fois que vous lancez Shaarli. Merci "
1302#~ "de le configurer."
1303
1304#~ msgid "Shaarli title"
1305#~ msgstr "Titre du Shaarli"
1306
1307#~ msgid "My links"
1308#~ msgstr "Mes liens"
1309
1310#~ msgid "Install"
1311#~ msgstr "Installer"
1312
1313#~ msgid ""
1314#~ "You need to browse your Shaarli over <strong>HTTPS</strong> to use this "
1315#~ "functionality."
1316#~ msgstr ""
1317#~ "Vous devez utiliser Shaarli en <strong>HTTPS</strong> pour utiliser cette "
1318#~ "fonctionalité."
1319
1320#~ msgid "Add to"
1321#~ msgstr "Ajouter à"
1322
1323#~ msgid "Plugin"
1324#~ msgstr "Extension"
1325
1326#~ msgid "Warning: "
1327#~ msgstr "Attention : "
1328
1329#~ msgid ""
1330#~ "It's recommended to visit the picture wall after enabling this feature."
1331#~ msgstr ""
1332#~ "Il est recommandé de visiter le Mur d'images après avoir activé cette "
1333#~ "fonctionnalité."
1334
1335#~ msgid ""
1336#~ "If you have a large database, the first retrieval may take a few minutes."
1337#~ msgstr ""
1338#~ "Si vous avez beaucoup de liens, la première récupération peut prendre "
1339#~ "plusieurs minutes."
1340
1306#, fuzzy 1341#, fuzzy
1307#~| msgid "Change" 1342#~| msgid "Change"
1308#~ msgid "range" 1343#~ msgid "range"
@@ -1360,7 +1395,8 @@ msgstr ""
1360#~ "\n" 1395#~ "\n"
1361 1396
1362#~ msgid "Sessions do not seem to work correctly on your server." 1397#~ msgid "Sessions do not seem to work correctly on your server."
1363#~ msgstr "Les sessions ne semblent " 1398#~ msgstr ""
1399#~ "Les sessions ne semblent pas fonctionner correctement sur votre serveur."
1364 1400
1365#~ msgid "Tag was renamed in " 1401#~ msgid "Tag was renamed in "
1366#~ msgstr "Le tag a été renommé dans " 1402#~ msgstr "Le tag a été renommé dans "
diff --git a/inc/web-thumbnailer.json b/inc/web-thumbnailer.json
new file mode 100644
index 00000000..dcaa149e
--- /dev/null
+++ b/inc/web-thumbnailer.json
@@ -0,0 +1,13 @@
1{
2 "settings": {
3 "default": {
4 "download_mode": "DOWNLOAD",
5 "_comment": "infinite cache",
6 "cache_duration": -1,
7 "timeout": 10
8 },
9 "path": {
10 "cache": "cache/"
11 }
12 }
13}
diff --git a/index.php b/index.php
index 29d67f62..633ab89e 100644
--- a/index.php
+++ b/index.php
@@ -28,7 +28,7 @@ if (date_default_timezone_get() == '') {
28define('WEB_PATH', substr($_SERVER['REQUEST_URI'], 0, 1+strrpos($_SERVER['REQUEST_URI'], '/', 0))); 28define('WEB_PATH', substr($_SERVER['REQUEST_URI'], 0, 1+strrpos($_SERVER['REQUEST_URI'], '/', 0)));
29 29
30// High execution time in case of problematic imports/exports. 30// High execution time in case of problematic imports/exports.
31ini_set('max_input_time','60'); 31ini_set('max_input_time', '60');
32 32
33// Try to set max upload file size and read 33// Try to set max upload file size and read
34ini_set('memory_limit', '128M'); 34ini_set('memory_limit', '128M');
@@ -56,35 +56,38 @@ require_once 'inc/rain.tpl.class.php';
56require_once __DIR__ . '/vendor/autoload.php'; 56require_once __DIR__ . '/vendor/autoload.php';
57 57
58// Shaarli library 58// Shaarli library
59require_once 'application/ApplicationUtils.php'; 59require_once 'application/bookmark/LinkUtils.php';
60require_once 'application/Cache.php';
61require_once 'application/CachedPage.php';
62require_once 'application/config/ConfigPlugin.php'; 60require_once 'application/config/ConfigPlugin.php';
63require_once 'application/FeedBuilder.php'; 61require_once 'application/feed/Cache.php';
62require_once 'application/http/HttpUtils.php';
63require_once 'application/http/UrlUtils.php';
64require_once 'application/updater/UpdaterUtils.php';
64require_once 'application/FileUtils.php'; 65require_once 'application/FileUtils.php';
65require_once 'application/History.php';
66require_once 'application/HttpUtils.php';
67require_once 'application/LinkDB.php';
68require_once 'application/LinkFilter.php';
69require_once 'application/LinkUtils.php';
70require_once 'application/NetscapeBookmarkUtils.php';
71require_once 'application/PageBuilder.php';
72require_once 'application/TimeZone.php'; 66require_once 'application/TimeZone.php';
73require_once 'application/Url.php';
74require_once 'application/Utils.php'; 67require_once 'application/Utils.php';
75require_once 'application/PluginManager.php'; 68
76require_once 'application/Router.php'; 69use \Shaarli\ApplicationUtils;
77require_once 'application/Updater.php'; 70use \Shaarli\Bookmark\Exception\LinkNotFoundException;
78use \Shaarli\Languages; 71use \Shaarli\Bookmark\LinkDB;
79use \Shaarli\ThemeUtils;
80use \Shaarli\Config\ConfigManager; 72use \Shaarli\Config\ConfigManager;
73use \Shaarli\Feed\CachedPage;
74use \Shaarli\Feed\FeedBuilder;
75use \Shaarli\History;
76use \Shaarli\Languages;
77use \Shaarli\Netscape\NetscapeBookmarkUtils;
78use \Shaarli\Plugin\PluginManager;
79use \Shaarli\Render\PageBuilder;
80use \Shaarli\Render\ThemeUtils;
81use \Shaarli\Router;
81use \Shaarli\Security\LoginManager; 82use \Shaarli\Security\LoginManager;
82use \Shaarli\Security\SessionManager; 83use \Shaarli\Security\SessionManager;
84use \Shaarli\Thumbnailer;
85use \Shaarli\Updater\Updater;
83 86
84// Ensure the PHP version is supported 87// Ensure the PHP version is supported
85try { 88try {
86 ApplicationUtils::checkPHPVersion('5.5', PHP_VERSION); 89 ApplicationUtils::checkPHPVersion('5.5', PHP_VERSION);
87} catch(Exception $exc) { 90} catch (Exception $exc) {
88 header('Content-Type: text/plain; charset=utf-8'); 91 header('Content-Type: text/plain; charset=utf-8');
89 echo $exc->getMessage(); 92 echo $exc->getMessage();
90 exit; 93 exit;
@@ -110,7 +113,7 @@ ini_set('session.use_trans_sid', false);
110 113
111session_name('shaarli'); 114session_name('shaarli');
112// Start session if needed (Some server auto-start sessions). 115// Start session if needed (Some server auto-start sessions).
113if (session_id() == '') { 116if (session_status() == PHP_SESSION_NONE) {
114 session_start(); 117 session_start();
115} 118}
116 119
@@ -222,7 +225,6 @@ if (isset($_POST['login'])) {
222 $expirationTime, 225 $expirationTime,
223 WEB_PATH 226 WEB_PATH
224 ); 227 );
225
226 } else { 228 } else {
227 // Standard session expiration (=when browser closes) 229 // Standard session expiration (=when browser closes)
228 $expirationTime = 0; 230 $expirationTime = 0;
@@ -256,7 +258,8 @@ if (isset($_POST['login'])) {
256 exit; 258 exit;
257 } 259 }
258 } 260 }
259 header('Location: ?'); exit; 261 header('Location: ?');
262 exit;
260 } else { 263 } else {
261 $loginManager->handleFailedLogin($_SERVER); 264 $loginManager->handleFailedLogin($_SERVER);
262 $redir = '&username='. urlencode($_POST['login']); 265 $redir = '&username='. urlencode($_POST['login']);
@@ -277,7 +280,9 @@ if (isset($_POST['login'])) {
277// ------------------------------------------------------------------------------------------ 280// ------------------------------------------------------------------------------------------
278// Token management for XSRF protection 281// Token management for XSRF protection
279// Token should be used in any form which acts on data (create,update,delete,import...). 282// Token should be used in any form which acts on data (create,update,delete,import...).
280if (!isset($_SESSION['tokens'])) $_SESSION['tokens']=array(); // Token are attached to the session. 283if (!isset($_SESSION['tokens'])) {
284 $_SESSION['tokens']=array(); // Token are attached to the session.
285}
281 286
282/** 287/**
283 * Daily RSS feed: 1 RSS entry per day giving all the links on that day. 288 * Daily RSS feed: 1 RSS entry per day giving all the links on that day.
@@ -287,13 +292,14 @@ if (!isset($_SESSION['tokens'])) $_SESSION['tokens']=array(); // Token are atta
287 * @param ConfigManager $conf Configuration Manager instance 292 * @param ConfigManager $conf Configuration Manager instance
288 * @param LoginManager $loginManager LoginManager instance 293 * @param LoginManager $loginManager LoginManager instance
289 */ 294 */
290function showDailyRSS($conf, $loginManager) { 295function showDailyRSS($conf, $loginManager)
296{
291 // Cache system 297 // Cache system
292 $query = $_SERVER['QUERY_STRING']; 298 $query = $_SERVER['QUERY_STRING'];
293 $cache = new CachedPage( 299 $cache = new CachedPage(
294 $conf->get('config.PAGE_CACHE'), 300 $conf->get('config.PAGE_CACHE'),
295 page_url($_SERVER), 301 page_url($_SERVER),
296 startsWith($query,'do=dailyrss') && !$loginManager->isLoggedIn() 302 startsWith($query, 'do=dailyrss') && !$loginManager->isLoggedIn()
297 ); 303 );
298 $cached = $cache->cachedVersion(); 304 $cached = $cache->cachedVersion();
299 if (!empty($cached)) { 305 if (!empty($cached)) {
@@ -355,7 +361,6 @@ function showDailyRSS($conf, $loginManager) {
355 $conf->get('redirector.url'), 361 $conf->get('redirector.url'),
356 $conf->get('redirector.encode_url') 362 $conf->get('redirector.encode_url')
357 ); 363 );
358 $link['thumbnail'] = thumbnail($conf, $link['url']);
359 $link['timestamp'] = $link['created']->getTimestamp(); 364 $link['timestamp'] = $link['created']->getTimestamp();
360 if (startsWith($link['url'], '?')) { 365 if (startsWith($link['url'], '?')) {
361 $link['url'] = index_url($_SERVER) . $link['url']; // make permalink URL absolute 366 $link['url'] = index_url($_SERVER) . $link['url']; // make permalink URL absolute
@@ -370,6 +375,7 @@ function showDailyRSS($conf, $loginManager) {
370 $tpl->assign('links', $links); 375 $tpl->assign('links', $links);
371 $tpl->assign('rssdate', escape($dayDate->format(DateTime::RSS))); 376 $tpl->assign('rssdate', escape($dayDate->format(DateTime::RSS)));
372 $tpl->assign('hide_timestamps', $conf->get('privacy.hide_timestamps', false)); 377 $tpl->assign('hide_timestamps', $conf->get('privacy.hide_timestamps', false));
378 $tpl->assign('index_url', $pageaddr);
373 $html = $tpl->draw('dailyrss', true); 379 $html = $tpl->draw('dailyrss', true);
374 380
375 echo $html . PHP_EOL; 381 echo $html . PHP_EOL;
@@ -394,7 +400,7 @@ function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager, $loginManager)
394{ 400{
395 $day = date('Ymd', strtotime('-1 day')); // Yesterday, in format YYYYMMDD. 401 $day = date('Ymd', strtotime('-1 day')); // Yesterday, in format YYYYMMDD.
396 if (isset($_GET['day'])) { 402 if (isset($_GET['day'])) {
397 $day = $_GET['day']; 403 $day = $_GET['day'];
398 } 404 }
399 405
400 $days = $LINKSDB->days(); 406 $days = $LINKSDB->days();
@@ -412,7 +418,7 @@ function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager, $loginManager)
412 $previousday=$days[$i - 1]; 418 $previousday=$days[$i - 1];
413 } 419 }
414 if ($i < count($days) - 1) { 420 if ($i < count($days) - 1) {
415 $nextday = $days[$i + 1]; 421 $nextday = $days[$i + 1];
416 } 422 }
417 } 423 }
418 try { 424 try {
@@ -423,8 +429,8 @@ function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager, $loginManager)
423 } 429 }
424 430
425 // We pre-format some fields for proper output. 431 // We pre-format some fields for proper output.
426 foreach($linksToDisplay as $key => $link) { 432 foreach ($linksToDisplay as $key => $link) {
427 $taglist = explode(' ',$link['tags']); 433 $taglist = explode(' ', $link['tags']);
428 uasort($taglist, 'strcasecmp'); 434 uasort($taglist, 'strcasecmp');
429 $linksToDisplay[$key]['taglist']=$taglist; 435 $linksToDisplay[$key]['taglist']=$taglist;
430 $linksToDisplay[$key]['formatedDescription'] = format_description( 436 $linksToDisplay[$key]['formatedDescription'] = format_description(
@@ -432,7 +438,6 @@ function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager, $loginManager)
432 $conf->get('redirector.url'), 438 $conf->get('redirector.url'),
433 $conf->get('redirector.encode_url') 439 $conf->get('redirector.encode_url')
434 ); 440 );
435 $linksToDisplay[$key]['thumbnail'] = thumbnail($conf, $link['url']);
436 $linksToDisplay[$key]['timestamp'] = $link['created']->getTimestamp(); 441 $linksToDisplay[$key]['timestamp'] = $link['created']->getTimestamp();
437 } 442 }
438 443
@@ -457,14 +462,14 @@ function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager, $loginManager)
457 */ 462 */
458 $columns = array(array(), array(), array()); // Entries to display, for each column. 463 $columns = array(array(), array(), array()); // Entries to display, for each column.
459 $fill = array(0, 0, 0); // Rough estimate of columns fill. 464 $fill = array(0, 0, 0); // Rough estimate of columns fill.
460 foreach($data['linksToDisplay'] as $key => $link) { 465 foreach ($data['linksToDisplay'] as $key => $link) {
461 // Roughly estimate length of entry (by counting characters) 466 // Roughly estimate length of entry (by counting characters)
462 // Title: 30 chars = 1 line. 1 line is 30 pixels height. 467 // Title: 30 chars = 1 line. 1 line is 30 pixels height.
463 // Description: 836 characters gives roughly 342 pixel height. 468 // Description: 836 characters gives roughly 342 pixel height.
464 // This is not perfect, but it's usually OK. 469 // This is not perfect, but it's usually OK.
465 $length = strlen($link['title']) + (342 * strlen($link['description'])) / 836; 470 $length = strlen($link['title']) + (342 * strlen($link['description'])) / 836;
466 if ($link['thumbnail']) { 471 if ($link['thumbnail']) {
467 $length += 100; // 1 thumbnails roughly takes 100 pixels height. 472 $length += 100; // 1 thumbnails roughly takes 100 pixels height.
468 } 473 }
469 // Then put in column which is the less filled: 474 // Then put in column which is the less filled:
470 $smallest = min($fill); // find smallest value in array. 475 $smallest = min($fill); // find smallest value in array.
@@ -492,8 +497,9 @@ function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager, $loginManager)
492 * @param ConfigManager $conf Configuration Manager instance. 497 * @param ConfigManager $conf Configuration Manager instance.
493 * @param PluginManager $pluginManager Plugin Manager instance. 498 * @param PluginManager $pluginManager Plugin Manager instance.
494 */ 499 */
495function showLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager) { 500function showLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager)
496 buildLinkList($PAGE,$LINKSDB, $conf, $pluginManager, $loginManager); 501{
502 buildLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager);
497 $PAGE->renderPage('linklist'); 503 $PAGE->renderPage('linklist');
498} 504}
499 505
@@ -513,7 +519,8 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
513 read_updates_file($conf->get('resource.updates')), 519 read_updates_file($conf->get('resource.updates')),
514 $LINKSDB, 520 $LINKSDB,
515 $conf, 521 $conf,
516 $loginManager->isLoggedIn() 522 $loginManager->isLoggedIn(),
523 $_SESSION
517 ); 524 );
518 try { 525 try {
519 $newUpdates = $updater->update(); 526 $newUpdates = $updater->update();
@@ -523,12 +530,11 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
523 $updater->getDoneUpdates() 530 $updater->getDoneUpdates()
524 ); 531 );
525 } 532 }
526 } 533 } catch (Exception $e) {
527 catch(Exception $e) {
528 die($e->getMessage()); 534 die($e->getMessage());
529 } 535 }
530 536
531 $PAGE = new PageBuilder($conf, $LINKSDB, $sessionManager->generateToken(), $loginManager->isLoggedIn()); 537 $PAGE = new PageBuilder($conf, $_SESSION, $LINKSDB, $sessionManager->generateToken(), $loginManager->isLoggedIn());
532 $PAGE->assign('linkcount', count($LINKSDB)); 538 $PAGE->assign('linkcount', count($LINKSDB));
533 $PAGE->assign('privateLinkcount', count_private($LINKSDB)); 539 $PAGE->assign('privateLinkcount', count_private($LINKSDB));
534 $PAGE->assign('plugin_errors', $pluginManager->getErrors()); 540 $PAGE->assign('plugin_errors', $pluginManager->getErrors());
@@ -537,8 +543,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
537 $query = (isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : ''; 543 $query = (isset($_SERVER['QUERY_STRING'])) ? $_SERVER['QUERY_STRING'] : '';
538 $targetPage = Router::findPage($query, $_GET, $loginManager->isLoggedIn()); 544 $targetPage = Router::findPage($query, $_GET, $loginManager->isLoggedIn());
539 545
540 if ( 546 if (// if the user isn't logged in
541 // if the user isn't logged in
542 !$loginManager->isLoggedIn() && 547 !$loginManager->isLoggedIn() &&
543 // and Shaarli doesn't have public content... 548 // and Shaarli doesn't have public content...
544 $conf->get('privacy.hide_public_links') && 549 $conf->get('privacy.hide_public_links') &&
@@ -562,9 +567,11 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
562 'footer', 567 'footer',
563 ); 568 );
564 569
565 foreach($common_hooks as $name) { 570 foreach ($common_hooks as $name) {
566 $plugin_data = array(); 571 $plugin_data = array();
567 $pluginManager->executeHooks('render_' . $name, $plugin_data, 572 $pluginManager->executeHooks(
573 'render_' . $name,
574 $plugin_data,
568 array( 575 array(
569 'target' => $targetPage, 576 'target' => $targetPage,
570 'loggedin' => $loginManager->isLoggedIn() 577 'loggedin' => $loginManager->isLoggedIn()
@@ -574,13 +581,15 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
574 } 581 }
575 582
576 // -------- Display login form. 583 // -------- Display login form.
577 if ($targetPage == Router::$PAGE_LOGIN) 584 if ($targetPage == Router::$PAGE_LOGIN) {
578 { 585 if ($conf->get('security.open_shaarli')) {
579 if ($conf->get('security.open_shaarli')) { header('Location: ?'); exit; } // No need to login for open Shaarli 586 header('Location: ?');
587 exit;
588 } // No need to login for open Shaarli
580 if (isset($_GET['username'])) { 589 if (isset($_GET['username'])) {
581 $PAGE->assign('username', escape($_GET['username'])); 590 $PAGE->assign('username', escape($_GET['username']));
582 } 591 }
583 $PAGE->assign('returnurl',(isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']):'')); 592 $PAGE->assign('returnurl', (isset($_SERVER['HTTP_REFERER']) ? escape($_SERVER['HTTP_REFERER']):''));
584 // add default state of the 'remember me' checkbox 593 // add default state of the 'remember me' checkbox
585 $PAGE->assign('remember_user_default', $conf->get('privacy.remember_user_default')); 594 $PAGE->assign('remember_user_default', $conf->get('privacy.remember_user_default'));
586 $PAGE->assign('user_can_login', $loginManager->canLogin($_SERVER)); 595 $PAGE->assign('user_can_login', $loginManager->canLogin($_SERVER));
@@ -589,8 +598,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
589 exit; 598 exit;
590 } 599 }
591 // -------- User wants to logout. 600 // -------- User wants to logout.
592 if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=logout')) 601 if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=logout')) {
593 {
594 invalidateCaches($conf->get('resource.page_cache')); 602 invalidateCaches($conf->get('resource.page_cache'));
595 $sessionManager->logout(); 603 $sessionManager->logout();
596 setcookie(LoginManager::$STAY_SIGNED_IN_COOKIE, 'false', 0, WEB_PATH); 604 setcookie(LoginManager::$STAY_SIGNED_IN_COOKIE, 'false', 0, WEB_PATH);
@@ -599,21 +607,23 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
599 } 607 }
600 608
601 // -------- Picture wall 609 // -------- Picture wall
602 if ($targetPage == Router::$PAGE_PICWALL) 610 if ($targetPage == Router::$PAGE_PICWALL) {
603 { 611 $PAGE->assign('pagetitle', t('Picture wall') .' - '. $conf->get('general.title', 'Shaarli'));
612 if (! $conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) === Thumbnailer::MODE_NONE) {
613 $PAGE->assign('linksToDisplay', []);
614 $PAGE->renderPage('picwall');
615 exit;
616 }
617
604 // Optionally filter the results: 618 // Optionally filter the results:
605 $links = $LINKSDB->filterSearch($_GET); 619 $links = $LINKSDB->filterSearch($_GET);
606 $linksToDisplay = array(); 620 $linksToDisplay = array();
607 621
608 // Get only links which have a thumbnail. 622 // Get only links which have a thumbnail.
609 foreach($links as $link) 623 // Note: we do not retrieve thumbnails here, the request is too heavy.
610 { 624 foreach ($links as $key => $link) {
611 $permalink='?'.$link['shorturl']; 625 if (isset($link['thumbnail']) && $link['thumbnail'] !== false) {
612 $thumb=lazyThumbnail($conf, $link['url'],$permalink); 626 $linksToDisplay[] = $link; // Add to array.
613 if ($thumb!='') // Only output links which have a thumbnail.
614 {
615 $link['thumbnail']=$thumb; // Thumbnail HTML code.
616 $linksToDisplay[]=$link; // Add to array.
617 } 627 }
618 } 628 }
619 629
@@ -626,14 +636,13 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
626 $PAGE->assign($key, $value); 636 $PAGE->assign($key, $value);
627 } 637 }
628 638
629 $PAGE->assign('pagetitle', t('Picture wall') .' - '. $conf->get('general.title', 'Shaarli')); 639
630 $PAGE->renderPage('picwall'); 640 $PAGE->renderPage('picwall');
631 exit; 641 exit;
632 } 642 }
633 643
634 // -------- Tag cloud 644 // -------- Tag cloud
635 if ($targetPage == Router::$PAGE_TAGCLOUD) 645 if ($targetPage == Router::$PAGE_TAGCLOUD) {
636 {
637 $visibility = ! empty($_SESSION['visibility']) ? $_SESSION['visibility'] : ''; 646 $visibility = ! empty($_SESSION['visibility']) ? $_SESSION['visibility'] : '';
638 $filteringTags = isset($_GET['searchtags']) ? explode(' ', $_GET['searchtags']) : []; 647 $filteringTags = isset($_GET['searchtags']) ? explode(' ', $_GET['searchtags']) : [];
639 $tags = $LINKSDB->linksCountPerTag($filteringTags, $visibility); 648 $tags = $LINKSDB->linksCountPerTag($filteringTags, $visibility);
@@ -648,7 +657,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
648 alphabetical_sort($tags, false, true); 657 alphabetical_sort($tags, false, true);
649 658
650 $tagList = array(); 659 $tagList = array();
651 foreach($tags as $key => $value) { 660 foreach ($tags as $key => $value) {
652 if (in_array($key, $filteringTags)) { 661 if (in_array($key, $filteringTags)) {
653 continue; 662 continue;
654 } 663 }
@@ -680,8 +689,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
680 } 689 }
681 690
682 // -------- Tag list 691 // -------- Tag list
683 if ($targetPage == Router::$PAGE_TAGLIST) 692 if ($targetPage == Router::$PAGE_TAGLIST) {
684 {
685 $visibility = ! empty($_SESSION['visibility']) ? $_SESSION['visibility'] : ''; 693 $visibility = ! empty($_SESSION['visibility']) ? $_SESSION['visibility'] : '';
686 $filteringTags = isset($_GET['searchtags']) ? explode(' ', $_GET['searchtags']) : []; 694 $filteringTags = isset($_GET['searchtags']) ? explode(' ', $_GET['searchtags']) : [];
687 $tags = $LINKSDB->linksCountPerTag($filteringTags, $visibility); 695 $tags = $LINKSDB->linksCountPerTag($filteringTags, $visibility);
@@ -727,7 +735,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
727 $cache = new CachedPage( 735 $cache = new CachedPage(
728 $conf->get('resource.page_cache'), 736 $conf->get('resource.page_cache'),
729 page_url($_SERVER), 737 page_url($_SERVER),
730 startsWith($query,'do='. $targetPage) && !$loginManager->isLoggedIn() 738 startsWith($query, 'do='. $targetPage) && !$loginManager->isLoggedIn()
731 ); 739 );
732 $cached = $cache->cachedVersion(); 740 $cached = $cache->cachedVersion();
733 if (!empty($cached)) { 741 if (!empty($cached)) {
@@ -765,11 +773,14 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
765 } 773 }
766 774
767 // -------- User clicks on a tag in a link: The tag is added to the list of searched tags (searchtags=...) 775 // -------- User clicks on a tag in a link: The tag is added to the list of searched tags (searchtags=...)
768 if (isset($_GET['addtag'])) 776 if (isset($_GET['addtag'])) {
769 {
770 // Get previous URL (http_referer) and add the tag to the searchtags parameters in query. 777 // Get previous URL (http_referer) and add the tag to the searchtags parameters in query.
771 if (empty($_SERVER['HTTP_REFERER'])) { header('Location: ?searchtags='.urlencode($_GET['addtag'])); exit; } // In case browser does not send HTTP_REFERER 778 if (empty($_SERVER['HTTP_REFERER'])) {
772 parse_str(parse_url($_SERVER['HTTP_REFERER'],PHP_URL_QUERY), $params); 779 // In case browser does not send HTTP_REFERER
780 header('Location: ?searchtags='.urlencode($_GET['addtag']));
781 exit;
782 }
783 parse_str(parse_url($_SERVER['HTTP_REFERER'], PHP_URL_QUERY), $params);
773 784
774 // Prevent redirection loop 785 // Prevent redirection loop
775 if (isset($params['addtag'])) { 786 if (isset($params['addtag'])) {
@@ -793,12 +804,14 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
793 // Append the tag if necessary 804 // Append the tag if necessary
794 if (empty($params['searchtags'])) { 805 if (empty($params['searchtags'])) {
795 $params['searchtags'] = trim($_GET['addtag']); 806 $params['searchtags'] = trim($_GET['addtag']);
796 } 807 } elseif ($addtag) {
797 elseif ($addtag) {
798 $params['searchtags'] = trim($params['searchtags']).' '.trim($_GET['addtag']); 808 $params['searchtags'] = trim($params['searchtags']).' '.trim($_GET['addtag']);
799 } 809 }
800 810
801 unset($params['page']); // We also remove page (keeping the same page has no sense, since the results are different) 811 // We also remove page (keeping the same page has no sense, since the
812 // results are different)
813 unset($params['page']);
814
802 header('Location: ?'.http_build_query($params)); 815 header('Location: ?'.http_build_query($params));
803 exit; 816 exit;
804 } 817 }
@@ -823,13 +836,15 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
823 $tags = explode(' ', $params['searchtags']); 836 $tags = explode(' ', $params['searchtags']);
824 // Remove value from array $tags. 837 // Remove value from array $tags.
825 $tags = array_diff($tags, array($_GET['removetag'])); 838 $tags = array_diff($tags, array($_GET['removetag']));
826 $params['searchtags'] = implode(' ',$tags); 839 $params['searchtags'] = implode(' ', $tags);
827 840
828 if (empty($params['searchtags'])) { 841 if (empty($params['searchtags'])) {
829 unset($params['searchtags']); 842 unset($params['searchtags']);
830 } 843 }
831 844
832 unset($params['page']); // We also remove page (keeping the same page has no sense, since the results are different) 845 // We also remove page (keeping the same page has no sense, since
846 // the results are different)
847 unset($params['page']);
833 } 848 }
834 header('Location: ?'.http_build_query($params)); 849 header('Location: ?'.http_build_query($params));
835 exit; 850 exit;
@@ -892,12 +907,10 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
892 } 907 }
893 908
894 // -------- Handle other actions allowed for non-logged in users: 909 // -------- Handle other actions allowed for non-logged in users:
895 if (!$loginManager->isLoggedIn()) 910 if (!$loginManager->isLoggedIn()) {
896 {
897 // User tries to post new link but is not logged in: 911 // User tries to post new link but is not logged in:
898 // Show login screen, then redirect to ?post=... 912 // Show login screen, then redirect to ?post=...
899 if (isset($_GET['post'])) 913 if (isset($_GET['post'])) {
900 {
901 header( // Redirect to login page, then back to post link. 914 header( // Redirect to login page, then back to post link.
902 'Location: ?do=login&post='.urlencode($_GET['post']). 915 'Location: ?do=login&post='.urlencode($_GET['post']).
903 (!empty($_GET['title'])?'&title='.urlencode($_GET['title']):''). 916 (!empty($_GET['title'])?'&title='.urlencode($_GET['title']):'').
@@ -920,8 +933,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
920 // -------- All other functions are reserved for the registered user: 933 // -------- All other functions are reserved for the registered user:
921 934
922 // -------- Display the Tools menu if requested (import/export/bookmarklet...) 935 // -------- Display the Tools menu if requested (import/export/bookmarklet...)
923 if ($targetPage == Router::$PAGE_TOOLS) 936 if ($targetPage == Router::$PAGE_TOOLS) {
924 {
925 $data = [ 937 $data = [
926 'pageabsaddr' => index_url($_SERVER), 938 'pageabsaddr' => index_url($_SERVER),
927 'sslenabled' => is_https($_SERVER), 939 'sslenabled' => is_https($_SERVER),
@@ -938,30 +950,40 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
938 } 950 }
939 951
940 // -------- User wants to change his/her password. 952 // -------- User wants to change his/her password.
941 if ($targetPage == Router::$PAGE_CHANGEPASSWORD) 953 if ($targetPage == Router::$PAGE_CHANGEPASSWORD) {
942 {
943 if ($conf->get('security.open_shaarli')) { 954 if ($conf->get('security.open_shaarli')) {
944 die(t('You are not supposed to change a password on an Open Shaarli.')); 955 die(t('You are not supposed to change a password on an Open Shaarli.'));
945 } 956 }
946 957
947 if (!empty($_POST['setpassword']) && !empty($_POST['oldpassword'])) 958 if (!empty($_POST['setpassword']) && !empty($_POST['oldpassword'])) {
948 { 959 if (!$sessionManager->checkToken($_POST['token'])) {
949 if (!$sessionManager->checkToken($_POST['token'])) die(t('Wrong token.')); // Go away! 960 die(t('Wrong token.')); // Go away!
961 }
950 962
951 // Make sure old password is correct. 963 // Make sure old password is correct.
952 $oldhash = sha1($_POST['oldpassword'].$conf->get('credentials.login').$conf->get('credentials.salt')); 964 $oldhash = sha1(
953 if ($oldhash!= $conf->get('credentials.hash')) { 965 $_POST['oldpassword'].$conf->get('credentials.login').$conf->get('credentials.salt')
954 echo '<script>alert("'. t('The old password is not correct.') .'");document.location=\'?do=changepasswd\';</script>'; 966 );
967 if ($oldhash != $conf->get('credentials.hash')) {
968 echo '<script>alert("'
969 . t('The old password is not correct.')
970 .'");document.location=\'?do=changepasswd\';</script>';
955 exit; 971 exit;
956 } 972 }
957 // Save new password 973 // Save new password
958 // Salt renders rainbow-tables attacks useless. 974 // Salt renders rainbow-tables attacks useless.
959 $conf->set('credentials.salt', sha1(uniqid('', true) .'_'. mt_rand())); 975 $conf->set('credentials.salt', sha1(uniqid('', true) .'_'. mt_rand()));
960 $conf->set('credentials.hash', sha1($_POST['setpassword'] . $conf->get('credentials.login') . $conf->get('credentials.salt'))); 976 $conf->set(
977 'credentials.hash',
978 sha1(
979 $_POST['setpassword']
980 . $conf->get('credentials.login')
981 . $conf->get('credentials.salt')
982 )
983 );
961 try { 984 try {
962 $conf->write($loginManager->isLoggedIn()); 985 $conf->write($loginManager->isLoggedIn());
963 } 986 } catch (Exception $e) {
964 catch(Exception $e) {
965 error_log( 987 error_log(
966 'ERROR while writing config file after changing password.' . PHP_EOL . 988 'ERROR while writing config file after changing password.' . PHP_EOL .
967 $e->getMessage() 989 $e->getMessage()
@@ -973,9 +995,8 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
973 } 995 }
974 echo '<script>alert("'. t('Your password has been changed') .'");document.location=\'?do=tools\';</script>'; 996 echo '<script>alert("'. t('Your password has been changed') .'");document.location=\'?do=tools\';</script>';
975 exit; 997 exit;
976 } 998 } else {
977 else // show the change password form. 999 // show the change password form.
978 {
979 $PAGE->assign('pagetitle', t('Change password') .' - '. $conf->get('general.title', 'Shaarli')); 1000 $PAGE->assign('pagetitle', t('Change password') .' - '. $conf->get('general.title', 'Shaarli'));
980 $PAGE->renderPage('changepassword'); 1001 $PAGE->renderPage('changepassword');
981 exit; 1002 exit;
@@ -983,10 +1004,8 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
983 } 1004 }
984 1005
985 // -------- User wants to change configuration 1006 // -------- User wants to change configuration
986 if ($targetPage == Router::$PAGE_CONFIGURE) 1007 if ($targetPage == Router::$PAGE_CONFIGURE) {
987 { 1008 if (!empty($_POST['title'])) {
988 if (!empty($_POST['title']) )
989 {
990 if (!$sessionManager->checkToken($_POST['token'])) { 1009 if (!$sessionManager->checkToken($_POST['token'])) {
991 die(t('Wrong token.')); // Go away! 1010 die(t('Wrong token.')); // Go away!
992 } 1011 }
@@ -1009,12 +1028,22 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1009 $conf->set('api.secret', escape($_POST['apiSecret'])); 1028 $conf->set('api.secret', escape($_POST['apiSecret']));
1010 $conf->set('translation.language', escape($_POST['language'])); 1029 $conf->set('translation.language', escape($_POST['language']));
1011 1030
1031 $thumbnailsMode = extension_loaded('gd') ? $_POST['enableThumbnails'] : Thumbnailer::MODE_NONE;
1032 if ($thumbnailsMode !== Thumbnailer::MODE_NONE
1033 && $thumbnailsMode !== $conf->get('thumbnails.mode', Thumbnailer::MODE_NONE)
1034 ) {
1035 $_SESSION['warnings'][] = t(
1036 'You have enabled or changed thumbnails mode. '
1037 .'<a href="?do=thumbs_update">Please synchronize them</a>.'
1038 );
1039 }
1040 $conf->set('thumbnails.mode', $thumbnailsMode);
1041
1012 try { 1042 try {
1013 $conf->write($loginManager->isLoggedIn()); 1043 $conf->write($loginManager->isLoggedIn());
1014 $history->updateSettings(); 1044 $history->updateSettings();
1015 invalidateCaches($conf->get('resource.page_cache')); 1045 invalidateCaches($conf->get('resource.page_cache'));
1016 } 1046 } catch (Exception $e) {
1017 catch(Exception $e) {
1018 error_log( 1047 error_log(
1019 'ERROR while writing config file after configuration update.' . PHP_EOL . 1048 'ERROR while writing config file after configuration update.' . PHP_EOL .
1020 $e->getMessage() 1049 $e->getMessage()
@@ -1026,9 +1055,8 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1026 } 1055 }
1027 echo '<script>alert("'. t('Configuration was saved.') .'");document.location=\'?do=configure\';</script>'; 1056 echo '<script>alert("'. t('Configuration was saved.') .'");document.location=\'?do=configure\';</script>';
1028 exit; 1057 exit;
1029 } 1058 } else {
1030 else // Show the configuration form. 1059 // Show the configuration form.
1031 {
1032 $PAGE->assign('title', $conf->get('general.title')); 1060 $PAGE->assign('title', $conf->get('general.title'));
1033 $PAGE->assign('theme', $conf->get('resource.theme')); 1061 $PAGE->assign('theme', $conf->get('resource.theme'));
1034 $PAGE->assign('theme_available', ThemeUtils::getThemes($conf->get('resource.raintpl_tpl'))); 1062 $PAGE->assign('theme_available', ThemeUtils::getThemes($conf->get('resource.raintpl_tpl')));
@@ -1047,6 +1075,8 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1047 $PAGE->assign('api_secret', $conf->get('api.secret')); 1075 $PAGE->assign('api_secret', $conf->get('api.secret'));
1048 $PAGE->assign('languages', Languages::getAvailableLanguages()); 1076 $PAGE->assign('languages', Languages::getAvailableLanguages());
1049 $PAGE->assign('language', $conf->get('translation.language')); 1077 $PAGE->assign('language', $conf->get('translation.language'));
1078 $PAGE->assign('gd_enabled', extension_loaded('gd'));
1079 $PAGE->assign('thumbnails_mode', $conf->get('thumbnails.mode', Thumbnailer::MODE_NONE));
1050 $PAGE->assign('pagetitle', t('Configure') .' - '. $conf->get('general.title', 'Shaarli')); 1080 $PAGE->assign('pagetitle', t('Configure') .' - '. $conf->get('general.title', 'Shaarli'));
1051 $PAGE->renderPage('configure'); 1081 $PAGE->renderPage('configure');
1052 exit; 1082 exit;
@@ -1054,8 +1084,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1054 } 1084 }
1055 1085
1056 // -------- User wants to rename a tag or delete it 1086 // -------- User wants to rename a tag or delete it
1057 if ($targetPage == Router::$PAGE_CHANGETAG) 1087 if ($targetPage == Router::$PAGE_CHANGETAG) {
1058 {
1059 if (empty($_POST['fromtag']) || (empty($_POST['totag']) && isset($_POST['renametag']))) { 1088 if (empty($_POST['fromtag']) || (empty($_POST['totag']) && isset($_POST['renametag']))) {
1060 $PAGE->assign('fromtag', ! empty($_GET['fromtag']) ? escape($_GET['fromtag']) : ''); 1089 $PAGE->assign('fromtag', ! empty($_GET['fromtag']) ? escape($_GET['fromtag']) : '');
1061 $PAGE->assign('pagetitle', t('Manage tags') .' - '. $conf->get('general.title', 'Shaarli')); 1090 $PAGE->assign('pagetitle', t('Manage tags') .' - '. $conf->get('general.title', 'Shaarli'));
@@ -1067,7 +1096,8 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1067 die(t('Wrong token.')); 1096 die(t('Wrong token.'));
1068 } 1097 }
1069 1098
1070 $alteredLinks = $LINKSDB->renameTag(escape($_POST['fromtag']), escape($_POST['totag'])); 1099 $toTag = isset($_POST['totag']) ? escape($_POST['totag']) : null;
1100 $alteredLinks = $LINKSDB->renameTag(escape($_POST['fromtag']), $toTag);
1071 $LINKSDB->save($conf->get('resource.page_cache')); 1101 $LINKSDB->save($conf->get('resource.page_cache'));
1072 foreach ($alteredLinks as $link) { 1102 foreach ($alteredLinks as $link) {
1073 $history->updateLink($link); 1103 $history->updateLink($link);
@@ -1083,16 +1113,14 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1083 } 1113 }
1084 1114
1085 // -------- User wants to add a link without using the bookmarklet: Show form. 1115 // -------- User wants to add a link without using the bookmarklet: Show form.
1086 if ($targetPage == Router::$PAGE_ADDLINK) 1116 if ($targetPage == Router::$PAGE_ADDLINK) {
1087 {
1088 $PAGE->assign('pagetitle', t('Shaare a new link') .' - '. $conf->get('general.title', 'Shaarli')); 1117 $PAGE->assign('pagetitle', t('Shaare a new link') .' - '. $conf->get('general.title', 'Shaarli'));
1089 $PAGE->renderPage('addlink'); 1118 $PAGE->renderPage('addlink');
1090 exit; 1119 exit;
1091 } 1120 }
1092 1121
1093 // -------- User clicked the "Save" button when editing a link: Save link to database. 1122 // -------- User clicked the "Save" button when editing a link: Save link to database.
1094 if (isset($_POST['save_edit'])) 1123 if (isset($_POST['save_edit'])) {
1095 {
1096 // Go away! 1124 // Go away!
1097 if (! $sessionManager->checkToken($_POST['token'])) { 1125 if (! $sessionManager->checkToken($_POST['token'])) {
1098 die(t('Wrong token.')); 1126 die(t('Wrong token.'));
@@ -1103,7 +1131,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1103 // Linkdate is kept here to: 1131 // Linkdate is kept here to:
1104 // - use the same permalink for notes as they're displayed when creating them 1132 // - use the same permalink for notes as they're displayed when creating them
1105 // - let users hack creation date of their posts 1133 // - let users hack creation date of their posts
1106 // See: https://shaarli.readthedocs.io/en/master/Various-hacks/#changing-the-timestamp-for-a-shaare 1134 // See: https://shaarli.readthedocs.io/en/master/guides/various-hacks/#changing-the-timestamp-for-a-shaare
1107 $linkdate = escape($_POST['lf_linkdate']); 1135 $linkdate = escape($_POST['lf_linkdate']);
1108 if (isset($LINKSDB[$id])) { 1136 if (isset($LINKSDB[$id])) {
1109 // Edit 1137 // Edit
@@ -1148,6 +1176,11 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1148 $link['title'] = $link['url']; 1176 $link['title'] = $link['url'];
1149 } 1177 }
1150 1178
1179 if ($conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE) {
1180 $thumbnailer = new Thumbnailer($conf);
1181 $link['thumbnail'] = $thumbnailer->get($url);
1182 }
1183
1151 $pluginManager->executeHooks('save_link', $link); 1184 $pluginManager->executeHooks('save_link', $link);
1152 1185
1153 $LINKSDB[$id] = $link; 1186 $LINKSDB[$id] = $link;
@@ -1174,14 +1207,16 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1174 } 1207 }
1175 1208
1176 // -------- User clicked the "Cancel" button when editing a link. 1209 // -------- User clicked the "Cancel" button when editing a link.
1177 if (isset($_POST['cancel_edit'])) 1210 if (isset($_POST['cancel_edit'])) {
1178 {
1179 $id = isset($_POST['lf_id']) ? (int) escape($_POST['lf_id']) : false; 1211 $id = isset($_POST['lf_id']) ? (int) escape($_POST['lf_id']) : false;
1180 if (! isset($LINKSDB[$id])) { 1212 if (! isset($LINKSDB[$id])) {
1181 header('Location: ?'); 1213 header('Location: ?');
1182 } 1214 }
1183 // If we are called from the bookmarklet, we must close the popup: 1215 // If we are called from the bookmarklet, we must close the popup:
1184 if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { echo '<script>self.close();</script>'; exit; } 1216 if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) {
1217 echo '<script>self.close();</script>';
1218 exit;
1219 }
1185 $link = $LINKSDB[$id]; 1220 $link = $LINKSDB[$id];
1186 $returnurl = ( isset($_POST['returnurl']) ? $_POST['returnurl'] : '?' ); 1221 $returnurl = ( isset($_POST['returnurl']) ? $_POST['returnurl'] : '?' );
1187 // Scroll to the link which has been edited. 1222 // Scroll to the link which has been edited.
@@ -1192,8 +1227,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1192 } 1227 }
1193 1228
1194 // -------- User clicked the "Delete" button when editing a link: Delete link from database. 1229 // -------- User clicked the "Delete" button when editing a link: Delete link from database.
1195 if ($targetPage == Router::$PAGE_DELETELINK) 1230 if ($targetPage == Router::$PAGE_DELETELINK) {
1196 {
1197 if (! $sessionManager->checkToken($_GET['token'])) { 1231 if (! $sessionManager->checkToken($_GET['token'])) {
1198 die(t('Wrong token.')); 1232 die(t('Wrong token.'));
1199 } 1233 }
@@ -1207,28 +1241,31 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1207 $ids = [$ids]; 1241 $ids = [$ids];
1208 } 1242 }
1209 // assert at least one id is given 1243 // assert at least one id is given
1210 if(!count($ids)){ 1244 if (!count($ids)) {
1211 die('no id provided'); 1245 die('no id provided');
1212 } 1246 }
1213 foreach ($ids as $id) { 1247 foreach ($ids as $id) {
1214 $id = (int) escape($id); 1248 $id = (int) escape($id);
1215 $link = $LINKSDB[$id]; 1249 $link = $LINKSDB[$id];
1216 $pluginManager->executeHooks('delete_link', $link); 1250 $pluginManager->executeHooks('delete_link', $link);
1251 $history->deleteLink($link);
1217 unset($LINKSDB[$id]); 1252 unset($LINKSDB[$id]);
1218 } 1253 }
1219 $LINKSDB->save($conf->get('resource.page_cache')); // save to disk 1254 $LINKSDB->save($conf->get('resource.page_cache')); // save to disk
1220 $history->deleteLink($link);
1221 1255
1222 // If we are called from the bookmarklet, we must close the popup: 1256 // If we are called from the bookmarklet, we must close the popup:
1223 if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) { echo '<script>self.close();</script>'; exit; } 1257 if (isset($_GET['source']) && ($_GET['source']=='bookmarklet' || $_GET['source']=='firefoxsocialapi')) {
1258 echo '<script>self.close();</script>';
1259 exit;
1260 }
1224 1261
1225 $location = '?'; 1262 $location = '?';
1226 if (isset($_SERVER['HTTP_REFERER'])) { 1263 if (isset($_SERVER['HTTP_REFERER'])) {
1227 // Don't redirect to where we were previously if it was a permalink or an edit_link, because it would 404. 1264 // Don't redirect to where we were previously if it was a permalink or an edit_link, because it would 404.
1228 $location = generateLocation( 1265 $location = generateLocation(
1229 $_SERVER['HTTP_REFERER'], 1266 $_SERVER['HTTP_REFERER'],
1230 $_SERVER['HTTP_HOST'], 1267 $_SERVER['HTTP_HOST'],
1231 ['delete_link', 'edit_link', $link['shorturl']] 1268 ['delete_link', 'edit_link', $link['shorturl']]
1232 ); 1269 );
1233 } 1270 }
1234 1271
@@ -1237,11 +1274,13 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1237 } 1274 }
1238 1275
1239 // -------- User clicked the "EDIT" button on a link: Display link edit form. 1276 // -------- User clicked the "EDIT" button on a link: Display link edit form.
1240 if (isset($_GET['edit_link'])) 1277 if (isset($_GET['edit_link'])) {
1241 {
1242 $id = (int) escape($_GET['edit_link']); 1278 $id = (int) escape($_GET['edit_link']);
1243 $link = $LINKSDB[$id]; // Read database 1279 $link = $LINKSDB[$id]; // Read database
1244 if (!$link) { header('Location: ?'); exit; } // Link not found in database. 1280 if (!$link) {
1281 header('Location: ?');
1282 exit;
1283 } // Link not found in database.
1245 $link['linkdate'] = $link['created']->format(LinkDB::LINK_DATE_FORMAT); 1284 $link['linkdate'] = $link['created']->format(LinkDB::LINK_DATE_FORMAT);
1246 $data = array( 1285 $data = array(
1247 'link' => $link, 1286 'link' => $link,
@@ -1267,8 +1306,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1267 $link_is_new = false; 1306 $link_is_new = false;
1268 // Check if URL is not already in database (in this case, we will edit the existing link) 1307 // Check if URL is not already in database (in this case, we will edit the existing link)
1269 $link = $LINKSDB->getLinkFromUrl($url); 1308 $link = $LINKSDB->getLinkFromUrl($url);
1270 if (! $link) 1309 if (! $link) {
1271 {
1272 $link_is_new = true; 1310 $link_is_new = true;
1273 $linkdate = strval(date(LinkDB::LINK_DATE_FORMAT)); 1311 $linkdate = strval(date(LinkDB::LINK_DATE_FORMAT));
1274 // Get title if it was provided in URL (by the bookmarklet). 1312 // Get title if it was provided in URL (by the bookmarklet).
@@ -1277,7 +1315,9 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1277 $description = empty($_GET['description']) ? '' : escape($_GET['description']); 1315 $description = empty($_GET['description']) ? '' : escape($_GET['description']);
1278 $tags = empty($_GET['tags']) ? '' : escape($_GET['tags']); 1316 $tags = empty($_GET['tags']) ? '' : escape($_GET['tags']);
1279 $private = !empty($_GET['private']) && $_GET['private'] === "1" ? 1 : 0; 1317 $private = !empty($_GET['private']) && $_GET['private'] === "1" ? 1 : 0;
1280 // If this is an HTTP(S) link, we try go get the page to extract the title (otherwise we will to straight to the edit form.) 1318
1319 // If this is an HTTP(S) link, we try go get the page to extract
1320 // the title (otherwise we will to straight to the edit form.)
1281 if (empty($title) && strpos(get_url_scheme($url), 'http') !== false) { 1321 if (empty($title) && strpos(get_url_scheme($url), 'http') !== false) {
1282 // Short timeout to keep the application responsive 1322 // Short timeout to keep the application responsive
1283 // The callback will fill $charset and $title with data from the downloaded page. 1323 // The callback will fill $charset and $title with data from the downloaded page.
@@ -1330,6 +1370,25 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1330 exit; 1370 exit;
1331 } 1371 }
1332 1372
1373 if ($targetPage == Router::$PAGE_PINLINK) {
1374 if (! isset($_GET['id']) || empty($LINKSDB[$_GET['id']])) {
1375 // FIXME! Use a proper error system.
1376 $msg = t('Invalid link ID provided');
1377 echo '<script>alert("'. $msg .'");document.location=\''. index_url($_SERVER) .'\';</script>';
1378 exit;
1379 }
1380 if (! $sessionManager->checkToken($_GET['token'])) {
1381 die('Wrong token.');
1382 }
1383
1384 $link = $LINKSDB[$_GET['id']];
1385 $link['sticky'] = ! $link['sticky'];
1386 $LINKSDB[(int) $_GET['id']] = $link;
1387 $LINKSDB->save($conf->get('resource.page_cache'));
1388 header('Location: '.index_url($_SERVER));
1389 exit;
1390 }
1391
1333 if ($targetPage == Router::$PAGE_EXPORT) { 1392 if ($targetPage == Router::$PAGE_EXPORT) {
1334 // Export links as a Netscape Bookmarks file 1393 // Export links as a Netscape Bookmarks file
1335 1394
@@ -1366,7 +1425,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1366 header('Content-Type: text/html; charset=utf-8'); 1425 header('Content-Type: text/html; charset=utf-8');
1367 header( 1426 header(
1368 'Content-disposition: attachment; filename=bookmarks_' 1427 'Content-disposition: attachment; filename=bookmarks_'
1369 .$selection.'_'.$now->format(LinkDB::LINK_DATE_FORMAT).'.html' 1428 .$selection.'_'.$now->format(LinkDB::LINK_DATE_FORMAT).'.html'
1370 ); 1429 );
1371 $PAGE->assign('date', $now->format(DateTime::RFC822)); 1430 $PAGE->assign('date', $now->format(DateTime::RFC822));
1372 $PAGE->assign('eol', PHP_EOL); 1431 $PAGE->assign('eol', PHP_EOL);
@@ -1434,14 +1493,20 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1434 $pluginMeta = $pluginManager->getPluginsMeta(); 1493 $pluginMeta = $pluginManager->getPluginsMeta();
1435 1494
1436 // Split plugins into 2 arrays: ordered enabled plugins and disabled. 1495 // Split plugins into 2 arrays: ordered enabled plugins and disabled.
1437 $enabledPlugins = array_filter($pluginMeta, function($v) { return $v['order'] !== false; }); 1496 $enabledPlugins = array_filter($pluginMeta, function ($v) {
1497 return $v['order'] !== false;
1498 });
1438 // Load parameters. 1499 // Load parameters.
1439 $enabledPlugins = load_plugin_parameter_values($enabledPlugins, $conf->get('plugins', array())); 1500 $enabledPlugins = load_plugin_parameter_values($enabledPlugins, $conf->get('plugins', array()));
1440 uasort( 1501 uasort(
1441 $enabledPlugins, 1502 $enabledPlugins,
1442 function($a, $b) { return $a['order'] - $b['order']; } 1503 function ($a, $b) {
1504 return $a['order'] - $b['order'];
1505 }
1443 ); 1506 );
1444 $disabledPlugins = array_filter($pluginMeta, function($v) { return $v['order'] === false; }); 1507 $disabledPlugins = array_filter($pluginMeta, function ($v) {
1508 return $v['order'] === false;
1509 });
1445 1510
1446 $PAGE->assign('enabledPlugins', $enabledPlugins); 1511 $PAGE->assign('enabledPlugins', $enabledPlugins);
1447 $PAGE->assign('disabledPlugins', $disabledPlugins); 1512 $PAGE->assign('disabledPlugins', $disabledPlugins);
@@ -1458,21 +1523,23 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1458 foreach ($_POST as $param => $value) { 1523 foreach ($_POST as $param => $value) {
1459 $conf->set('plugins.'. $param, escape($value)); 1524 $conf->set('plugins.'. $param, escape($value));
1460 } 1525 }
1461 } 1526 } else {
1462 else {
1463 $conf->set('general.enabled_plugins', save_plugin_config($_POST)); 1527 $conf->set('general.enabled_plugins', save_plugin_config($_POST));
1464 } 1528 }
1465 $conf->write($loginManager->isLoggedIn()); 1529 $conf->write($loginManager->isLoggedIn());
1466 $history->updateSettings(); 1530 $history->updateSettings();
1467 } 1531 } catch (Exception $e) {
1468 catch (Exception $e) {
1469 error_log( 1532 error_log(
1470 'ERROR while saving plugin configuration:.' . PHP_EOL . 1533 'ERROR while saving plugin configuration:.' . PHP_EOL .
1471 $e->getMessage() 1534 $e->getMessage()
1472 ); 1535 );
1473 1536
1474 // TODO: do not handle exceptions/errors in JS. 1537 // TODO: do not handle exceptions/errors in JS.
1475 echo '<script>alert("'. $e->getMessage() .'");document.location=\'?do='. Router::$PAGE_PLUGINSADMIN .'\';</script>'; 1538 echo '<script>alert("'
1539 . $e->getMessage()
1540 .'");document.location=\'?do='
1541 . Router::$PAGE_PLUGINSADMIN
1542 .'\';</script>';
1476 exit; 1543 exit;
1477 } 1544 }
1478 header('Location: ?do='. Router::$PAGE_PLUGINSADMIN); 1545 header('Location: ?do='. Router::$PAGE_PLUGINSADMIN);
@@ -1486,6 +1553,43 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager,
1486 exit; 1553 exit;
1487 } 1554 }
1488 1555
1556 // -------- Thumbnails Update
1557 if ($targetPage == Router::$PAGE_THUMBS_UPDATE) {
1558 $ids = [];
1559 foreach ($LINKSDB as $link) {
1560 // A note or not HTTP(S)
1561 if ($link['url'][0] === '?' || ! startsWith(strtolower($link['url']), 'http')) {
1562 continue;
1563 }
1564 $ids[] = $link['id'];
1565 }
1566 $PAGE->assign('ids', $ids);
1567 $PAGE->assign('pagetitle', t('Thumbnails update') .' - '. $conf->get('general.title', 'Shaarli'));
1568 $PAGE->renderPage('thumbnails');
1569 exit;
1570 }
1571
1572 // -------- Single Thumbnail Update
1573 if ($targetPage == Router::$AJAX_THUMB_UPDATE) {
1574 if (! isset($_POST['id']) || ! ctype_digit($_POST['id'])) {
1575 http_response_code(400);
1576 exit;
1577 }
1578 $id = (int) $_POST['id'];
1579 if (empty($LINKSDB[$id])) {
1580 http_response_code(404);
1581 exit;
1582 }
1583 $thumbnailer = new Thumbnailer($conf);
1584 $link = $LINKSDB[$id];
1585 $link['thumbnail'] = $thumbnailer->get($link['url']);
1586 $LINKSDB[$id] = $link;
1587 $LINKSDB->save($conf->get('resource.page_cache'));
1588
1589 echo json_encode($link);
1590 exit;
1591 }
1592
1489 // -------- Otherwise, simply display search form and links: 1593 // -------- Otherwise, simply display search form and links:
1490 showLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager); 1594 showLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager);
1491 exit; 1595 exit;
@@ -1549,9 +1653,14 @@ function buildLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager)
1549 // Start index. 1653 // Start index.
1550 $i = ($page-1) * $_SESSION['LINKS_PER_PAGE']; 1654 $i = ($page-1) * $_SESSION['LINKS_PER_PAGE'];
1551 $end = $i + $_SESSION['LINKS_PER_PAGE']; 1655 $end = $i + $_SESSION['LINKS_PER_PAGE'];
1656
1657 $thumbnailsEnabled = $conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE;
1658 if ($thumbnailsEnabled) {
1659 $thumbnailer = new Thumbnailer($conf);
1660 }
1661
1552 $linkDisp = array(); 1662 $linkDisp = array();
1553 while ($i<$end && $i<count($keys)) 1663 while ($i<$end && $i<count($keys)) {
1554 {
1555 $link = $linksToDisplay[$keys[$i]]; 1664 $link = $linksToDisplay[$keys[$i]];
1556 $link['description'] = format_description( 1665 $link['description'] = format_description(
1557 $link['description'], 1666 $link['description'],
@@ -1569,9 +1678,21 @@ function buildLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager)
1569 $taglist = preg_split('/\s+/', $link['tags'], -1, PREG_SPLIT_NO_EMPTY); 1678 $taglist = preg_split('/\s+/', $link['tags'], -1, PREG_SPLIT_NO_EMPTY);
1570 uasort($taglist, 'strcasecmp'); 1679 uasort($taglist, 'strcasecmp');
1571 $link['taglist'] = $taglist; 1680 $link['taglist'] = $taglist;
1681
1682 // Logged in, thumbnails enabled, not a note,
1683 // and (never retrieved yet or no valid cache file)
1684 if ($loginManager->isLoggedIn() && $thumbnailsEnabled && $link['url'][0] != '?'
1685 && (! isset($link['thumbnail']) || ($link['thumbnail'] !== false && ! is_file($link['thumbnail'])))
1686 ) {
1687 $elem = $LINKSDB[$keys[$i]];
1688 $elem['thumbnail'] = $thumbnailer->get($link['url']);
1689 $LINKSDB[$keys[$i]] = $elem;
1690 $updateDB = true;
1691 $link['thumbnail'] = $elem['thumbnail'];
1692 }
1693
1572 // Check for both signs of a note: starting with ? and 7 chars long. 1694 // Check for both signs of a note: starting with ? and 7 chars long.
1573 if ($link['url'][0] === '?' && 1695 if ($link['url'][0] === '?' && strlen($link['url']) === 7) {
1574 strlen($link['url']) === 7) {
1575 $link['url'] = index_url($_SERVER) . $link['url']; 1696 $link['url'] = index_url($_SERVER) . $link['url'];
1576 } 1697 }
1577 1698
@@ -1579,6 +1700,11 @@ function buildLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager)
1579 $i++; 1700 $i++;
1580 } 1701 }
1581 1702
1703 // If we retrieved new thumbnails, we update the database.
1704 if (!empty($updateDB)) {
1705 $LINKSDB->save($conf->get('resource.page_cache'));
1706 }
1707
1582 // Compute paging navigation 1708 // Compute paging navigation
1583 $searchtagsUrl = $searchtags === '' ? '' : '&searchtags=' . urlencode($searchtags); 1709 $searchtagsUrl = $searchtags === '' ? '' : '&searchtags=' . urlencode($searchtags);
1584 $searchtermUrl = empty($searchterm) ? '' : '&searchterm=' . urlencode($searchterm); 1710 $searchtermUrl = empty($searchterm) ? '' : '&searchterm=' . urlencode($searchterm);
@@ -1630,194 +1756,6 @@ function buildLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager)
1630} 1756}
1631 1757
1632/** 1758/**
1633 * Compute the thumbnail for a link.
1634 *
1635 * With a link to the original URL.
1636 * Understands various services (youtube.com...)
1637 * Input: $url = URL for which the thumbnail must be found.
1638 * $href = if provided, this URL will be followed instead of $url
1639 * Returns an associative array with thumbnail attributes (src,href,width,height,style,alt)
1640 * Some of them may be missing.
1641 * Return an empty array if no thumbnail available.
1642 *
1643 * @param ConfigManager $conf Configuration Manager instance.
1644 * @param string $url
1645 * @param string|bool $href
1646 *
1647 * @return array
1648 */
1649function computeThumbnail($conf, $url, $href = false)
1650{
1651 if (!$conf->get('thumbnail.enable_thumbnails')) return array();
1652 if ($href==false) $href=$url;
1653
1654 // For most hosts, the URL of the thumbnail can be easily deduced from the URL of the link.
1655 // (e.g. http://www.youtube.com/watch?v=spVypYk4kto ---> http://img.youtube.com/vi/spVypYk4kto/default.jpg )
1656 // ^^^^^^^^^^^ ^^^^^^^^^^^
1657 $domain = parse_url($url,PHP_URL_HOST);
1658 if ($domain=='youtube.com' || $domain=='www.youtube.com')
1659 {
1660 parse_str(parse_url($url,PHP_URL_QUERY), $params); // Extract video ID and get thumbnail
1661 if (!empty($params['v'])) return array('src'=>'https://img.youtube.com/vi/'.$params['v'].'/default.jpg',
1662 'href'=>$href,'width'=>'120','height'=>'90','alt'=>'YouTube thumbnail');
1663 }
1664 if ($domain=='youtu.be') // Youtube short links
1665 {
1666 $path = parse_url($url,PHP_URL_PATH);
1667 return array('src'=>'https://img.youtube.com/vi'.$path.'/default.jpg',
1668 'href'=>$href,'width'=>'120','height'=>'90','alt'=>'YouTube thumbnail');
1669 }
1670 if ($domain=='pix.toile-libre.org') // pix.toile-libre.org image hosting
1671 {
1672 parse_str(parse_url($url,PHP_URL_QUERY), $params); // Extract image filename.
1673 if (!empty($params) && !empty($params['img'])) return array('src'=>'http://pix.toile-libre.org/upload/thumb/'.urlencode($params['img']),
1674 'href'=>$href,'style'=>'max-width:120px; max-height:150px','alt'=>'pix.toile-libre.org thumbnail');
1675 }
1676
1677 if ($domain=='imgur.com')
1678 {
1679 $path = parse_url($url,PHP_URL_PATH);
1680 if (startsWith($path,'/a/')) return array(); // Thumbnails for albums are not available.
1681 if (startsWith($path,'/r/')) return array('src'=>'https://i.imgur.com/'.basename($path).'s.jpg',
1682 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail');
1683 if (startsWith($path,'/gallery/')) return array('src'=>'https://i.imgur.com'.substr($path,8).'s.jpg',
1684 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail');
1685
1686 if (substr_count($path,'/')==1) return array('src'=>'https://i.imgur.com/'.substr($path,1).'s.jpg',
1687 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail');
1688 }
1689 if ($domain=='i.imgur.com')
1690 {
1691 $pi = pathinfo(parse_url($url,PHP_URL_PATH));
1692 if (!empty($pi['filename'])) return array('src'=>'https://i.imgur.com/'.$pi['filename'].'s.jpg',
1693 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail');
1694 }
1695 if ($domain=='dailymotion.com' || $domain=='www.dailymotion.com')
1696 {
1697 if (strpos($url,'dailymotion.com/video/')!==false)
1698 {
1699 $thumburl=str_replace('dailymotion.com/video/','dailymotion.com/thumbnail/video/',$url);
1700 return array('src'=>$thumburl,
1701 'href'=>$href,'width'=>'120','style'=>'height:auto;','alt'=>'DailyMotion thumbnail');
1702 }
1703 }
1704 if (endsWith($domain,'.imageshack.us'))
1705 {
1706 $ext=strtolower(pathinfo($url,PATHINFO_EXTENSION));
1707 if ($ext=='jpg' || $ext=='jpeg' || $ext=='png' || $ext=='gif')
1708 {
1709 $thumburl = substr($url,0,strlen($url)-strlen($ext)).'th.'.$ext;
1710 return array('src'=>$thumburl,
1711 'href'=>$href,'width'=>'120','style'=>'height:auto;','alt'=>'imageshack.us thumbnail');
1712 }
1713 }
1714
1715 // Some other hosts are SLOW AS HELL and usually require an extra HTTP request to get the thumbnail URL.
1716 // So we deport the thumbnail generation in order not to slow down page generation
1717 // (and we also cache the thumbnail)
1718
1719 if (! $conf->get('thumbnail.enable_localcache')) return array(); // If local cache is disabled, no thumbnails for services which require the use a local cache.
1720
1721 if ($domain=='flickr.com' || endsWith($domain,'.flickr.com')
1722 || $domain=='vimeo.com'
1723 || $domain=='ted.com' || endsWith($domain,'.ted.com')
1724 || $domain=='xkcd.com' || endsWith($domain,'.xkcd.com')
1725 )
1726 {
1727 if ($domain=='vimeo.com')
1728 { // Make sure this vimeo URL points to a video (/xxx... where xxx is numeric)
1729 $path = parse_url($url,PHP_URL_PATH);
1730 if (!preg_match('!/\d+.+?!',$path)) return array(); // This is not a single video URL.
1731 }
1732 if ($domain=='xkcd.com' || endsWith($domain,'.xkcd.com'))
1733 { // Make sure this URL points to a single comic (/xxx... where xxx is numeric)
1734 $path = parse_url($url,PHP_URL_PATH);
1735 if (!preg_match('!/\d+.+?!',$path)) return array();
1736 }
1737 if ($domain=='ted.com' || endsWith($domain,'.ted.com'))
1738 { // Make sure this TED URL points to a video (/talks/...)
1739 $path = parse_url($url,PHP_URL_PATH);
1740 if ("/talks/" !== substr($path,0,7)) return array(); // This is not a single video URL.
1741 }
1742 $sign = hash_hmac('sha256', $url, $conf->get('credentials.salt')); // We use the salt to sign data (it's random, secret, and specific to each installation)
1743 return array('src'=>index_url($_SERVER).'?do=genthumbnail&hmac='.$sign.'&url='.urlencode($url),
1744 'href'=>$href,'width'=>'120','style'=>'height:auto;','alt'=>'thumbnail');
1745 }
1746
1747 // For all other, we try to make a thumbnail of links ending with .jpg/jpeg/png/gif
1748 // Technically speaking, we should download ALL links and check their Content-Type to see if they are images.
1749 // But using the extension will do.
1750 $ext=strtolower(pathinfo($url,PATHINFO_EXTENSION));
1751 if ($ext=='jpg' || $ext=='jpeg' || $ext=='png' || $ext=='gif')
1752 {
1753 $sign = hash_hmac('sha256', $url, $conf->get('credentials.salt')); // We use the salt to sign data (it's random, secret, and specific to each installation)
1754 return array('src'=>index_url($_SERVER).'?do=genthumbnail&hmac='.$sign.'&url='.urlencode($url),
1755 'href'=>$href,'width'=>'120','style'=>'height:auto;','alt'=>'thumbnail');
1756 }
1757 return array(); // No thumbnail.
1758
1759}
1760
1761
1762// Returns the HTML code to display a thumbnail for a link
1763// with a link to the original URL.
1764// Understands various services (youtube.com...)
1765// Input: $url = URL for which the thumbnail must be found.
1766// $href = if provided, this URL will be followed instead of $url
1767// Returns '' if no thumbnail available.
1768function thumbnail($url,$href=false)
1769{
1770 // FIXME!
1771 global $conf;
1772 $t = computeThumbnail($conf, $url,$href);
1773 if (count($t)==0) return ''; // Empty array = no thumbnail for this URL.
1774
1775 $html='<a href="'.escape($t['href']).'"><img src="'.escape($t['src']).'"';
1776 if (!empty($t['width'])) $html.=' width="'.escape($t['width']).'"';
1777 if (!empty($t['height'])) $html.=' height="'.escape($t['height']).'"';
1778 if (!empty($t['style'])) $html.=' style="'.escape($t['style']).'"';
1779 if (!empty($t['alt'])) $html.=' alt="'.escape($t['alt']).'"';
1780 $html.='></a>';
1781 return $html;
1782}
1783
1784// Returns the HTML code to display a thumbnail for a link
1785// for the picture wall (using lazy image loading)
1786// Understands various services (youtube.com...)
1787// Input: $url = URL for which the thumbnail must be found.
1788// $href = if provided, this URL will be followed instead of $url
1789// Returns '' if no thumbnail available.
1790function lazyThumbnail($conf, $url,$href=false)
1791{
1792 // FIXME!
1793 global $conf;
1794 $t = computeThumbnail($conf, $url,$href);
1795 if (count($t)==0) return ''; // Empty array = no thumbnail for this URL.
1796
1797 $html='<a href="'.escape($t['href']).'">';
1798
1799 // Lazy image
1800 $html.='<img class="b-lazy" src="#" data-src="'.escape($t['src']).'"';
1801
1802 if (!empty($t['width'])) $html.=' width="'.escape($t['width']).'"';
1803 if (!empty($t['height'])) $html.=' height="'.escape($t['height']).'"';
1804 if (!empty($t['style'])) $html.=' style="'.escape($t['style']).'"';
1805 if (!empty($t['alt'])) $html.=' alt="'.escape($t['alt']).'"';
1806 $html.='>';
1807
1808 // No-JavaScript fallback.
1809 $html.='<noscript><img src="'.escape($t['src']).'"';
1810 if (!empty($t['width'])) $html.=' width="'.escape($t['width']).'"';
1811 if (!empty($t['height'])) $html.=' height="'.escape($t['height']).'"';
1812 if (!empty($t['style'])) $html.=' style="'.escape($t['style']).'"';
1813 if (!empty($t['alt'])) $html.=' alt="'.escape($t['alt']).'"';
1814 $html.='></noscript></a>';
1815
1816 return $html;
1817}
1818
1819
1820/**
1821 * Installation 1759 * Installation
1822 * This function should NEVER be called if the file data/config.php exists. 1760 * This function should NEVER be called if the file data/config.php exists.
1823 * 1761 *
@@ -1825,16 +1763,19 @@ function lazyThumbnail($conf, $url,$href=false)
1825 * @param SessionManager $sessionManager SessionManager instance 1763 * @param SessionManager $sessionManager SessionManager instance
1826 * @param LoginManager $loginManager LoginManager instance 1764 * @param LoginManager $loginManager LoginManager instance
1827 */ 1765 */
1828function install($conf, $sessionManager, $loginManager) { 1766function install($conf, $sessionManager, $loginManager)
1767{
1829 // On free.fr host, make sure the /sessions directory exists, otherwise login will not work. 1768 // On free.fr host, make sure the /sessions directory exists, otherwise login will not work.
1830 if (endsWith($_SERVER['HTTP_HOST'],'.free.fr') && !is_dir($_SERVER['DOCUMENT_ROOT'].'/sessions')) mkdir($_SERVER['DOCUMENT_ROOT'].'/sessions',0705); 1769 if (endsWith($_SERVER['HTTP_HOST'], '.free.fr') && !is_dir($_SERVER['DOCUMENT_ROOT'].'/sessions')) {
1770 mkdir($_SERVER['DOCUMENT_ROOT'].'/sessions', 0705);
1771 }
1831 1772
1832 1773
1833 // This part makes sure sessions works correctly. 1774 // This part makes sure sessions works correctly.
1834 // (Because on some hosts, session.save_path may not be set correctly, 1775 // (Because on some hosts, session.save_path may not be set correctly,
1835 // or we may not have write access to it.) 1776 // or we may not have write access to it.)
1836 if (isset($_GET['test_session']) && ( !isset($_SESSION) || !isset($_SESSION['session_tested']) || $_SESSION['session_tested']!='Working')) 1777 if (isset($_GET['test_session'])
1837 { 1778 && ( !isset($_SESSION) || !isset($_SESSION['session_tested']) || $_SESSION['session_tested']!='Working')) {
1838 // Step 2: Check if data in session is correct. 1779 // Step 2: Check if data in session is correct.
1839 $msg = t( 1780 $msg = t(
1840 '<pre>Sessions do not seem to work correctly on your server.<br>'. 1781 '<pre>Sessions do not seem to work correctly on your server.<br>'.
@@ -1850,19 +1791,18 @@ function install($conf, $sessionManager, $loginManager) {
1850 echo '<br><a href="?">'. t('Click to try again.') .'</a></pre>'; 1791 echo '<br><a href="?">'. t('Click to try again.') .'</a></pre>';
1851 die; 1792 die;
1852 } 1793 }
1853 if (!isset($_SESSION['session_tested'])) 1794 if (!isset($_SESSION['session_tested'])) {
1854 { // Step 1 : Try to store data in session and reload page. 1795 // Step 1 : Try to store data in session and reload page.
1855 $_SESSION['session_tested'] = 'Working'; // Try to set a variable in session. 1796 $_SESSION['session_tested'] = 'Working'; // Try to set a variable in session.
1856 header('Location: '.index_url($_SERVER).'?test_session'); // Redirect to check stored data. 1797 header('Location: '.index_url($_SERVER).'?test_session'); // Redirect to check stored data.
1857 } 1798 }
1858 if (isset($_GET['test_session'])) 1799 if (isset($_GET['test_session'])) {
1859 { // Step 3: Sessions are OK. Remove test parameter from URL. 1800 // Step 3: Sessions are OK. Remove test parameter from URL.
1860 header('Location: '.index_url($_SERVER)); 1801 header('Location: '.index_url($_SERVER));
1861 } 1802 }
1862 1803
1863 1804
1864 if (!empty($_POST['setlogin']) && !empty($_POST['setpassword'])) 1805 if (!empty($_POST['setlogin']) && !empty($_POST['setpassword'])) {
1865 {
1866 $tz = 'UTC'; 1806 $tz = 'UTC';
1867 if (!empty($_POST['continent']) && !empty($_POST['city']) 1807 if (!empty($_POST['continent']) && !empty($_POST['city'])
1868 && isTimeZoneValid($_POST['continent'], $_POST['city']) 1808 && isTimeZoneValid($_POST['continent'], $_POST['city'])
@@ -1893,22 +1833,24 @@ function install($conf, $sessionManager, $loginManager) {
1893 try { 1833 try {
1894 // Everything is ok, let's create config file. 1834 // Everything is ok, let's create config file.
1895 $conf->write($loginManager->isLoggedIn()); 1835 $conf->write($loginManager->isLoggedIn());
1896 } 1836 } catch (Exception $e) {
1897 catch(Exception $e) {
1898 error_log( 1837 error_log(
1899 'ERROR while writing config file after installation.' . PHP_EOL . 1838 'ERROR while writing config file after installation.' . PHP_EOL .
1900 $e->getMessage() 1839 $e->getMessage()
1901 ); 1840 );
1902 1841
1903 // TODO: do not handle exceptions/errors in JS. 1842 // TODO: do not handle exceptions/errors in JS.
1904 echo '<script>alert("'. $e->getMessage() .'");document.location=\'?\';</script>'; 1843 echo '<script>alert("'. $e->getMessage() .'");document.location=\'?\';</script>';
1905 exit; 1844 exit;
1906 } 1845 }
1907 echo '<script>alert("Shaarli is now configured. Please enter your login/password and start shaaring your links!");document.location=\'?do=login\';</script>'; 1846 echo '<script>alert('
1847 .'"Shaarli is now configured. '
1848 .'Please enter your login/password and start shaaring your links!"'
1849 .');document.location=\'?do=login\';</script>';
1908 exit; 1850 exit;
1909 } 1851 }
1910 1852
1911 $PAGE = new PageBuilder($conf, null, $sessionManager->generateToken()); 1853 $PAGE = new PageBuilder($conf, $_SESSION, null, $sessionManager->generateToken());
1912 list($continents, $cities) = generateTimeZoneData(timezone_identifiers_list(), date_default_timezone_get()); 1854 list($continents, $cities) = generateTimeZoneData(timezone_identifiers_list(), date_default_timezone_get());
1913 $PAGE->assign('continents', $continents); 1855 $PAGE->assign('continents', $continents);
1914 $PAGE->assign('cities', $cities); 1856 $PAGE->assign('cities', $cities);
@@ -1917,240 +1859,18 @@ function install($conf, $sessionManager, $loginManager) {
1917 exit; 1859 exit;
1918} 1860}
1919 1861
1920/** 1862if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=dailyrss')) {
1921 * Because some f*cking services like flickr require an extra HTTP request to get the thumbnail URL, 1863 showDailyRSS($conf, $loginManager);
1922 * I have deported the thumbnail URL code generation here, otherwise this would slow down page generation. 1864 exit;
1923 * The following function takes the URL a link (e.g. a flickr page) and return the proper thumbnail.
1924 * This function is called by passing the URL:
1925 * http://mywebsite.com/shaarli/?do=genthumbnail&hmac=[HMAC]&url=[URL]
1926 * [URL] is the URL of the link (e.g. a flickr page)
1927 * [HMAC] is the signature for the [URL] (so that these URL cannot be forged).
1928 * The function below will fetch the image from the webservice and store it in the cache.
1929 *
1930 * @param ConfigManager $conf Configuration Manager instance,
1931 */
1932function genThumbnail($conf)
1933{
1934 // Make sure the parameters in the URL were generated by us.
1935 $sign = hash_hmac('sha256', $_GET['url'], $conf->get('credentials.salt'));
1936 if ($sign!=$_GET['hmac']) die('Naughty boy!');
1937
1938 $cacheDir = $conf->get('resource.thumbnails_cache', 'cache');
1939 // Let's see if we don't already have the image for this URL in the cache.
1940 $thumbname=hash('sha1',$_GET['url']).'.jpg';
1941 if (is_file($cacheDir .'/'. $thumbname))
1942 { // We have the thumbnail, just serve it:
1943 header('Content-Type: image/jpeg');
1944 echo file_get_contents($cacheDir .'/'. $thumbname);
1945 return;
1946 }
1947 // We may also serve a blank image (if service did not respond)
1948 $blankname=hash('sha1',$_GET['url']).'.gif';
1949 if (is_file($cacheDir .'/'. $blankname))
1950 {
1951 header('Content-Type: image/gif');
1952 echo file_get_contents($cacheDir .'/'. $blankname);
1953 return;
1954 }
1955
1956 // Otherwise, generate the thumbnail.
1957 $url = $_GET['url'];
1958 $domain = parse_url($url,PHP_URL_HOST);
1959
1960 if ($domain=='flickr.com' || endsWith($domain,'.flickr.com'))
1961 {
1962 // Crude replacement to handle new flickr domain policy (They prefer www. now)
1963 $url = str_replace('http://flickr.com/','http://www.flickr.com/',$url);
1964
1965 // Is this a link to an image, or to a flickr page ?
1966 $imageurl='';
1967 if (endsWith(parse_url($url, PHP_URL_PATH), '.jpg'))
1968 { // This is a direct link to an image. e.g. http://farm1.staticflickr.com/5/5921913_ac83ed27bd_o.jpg
1969 preg_match('!(http://farm\d+\.staticflickr\.com/\d+/\d+_\w+_)\w.jpg!',$url,$matches);
1970 if (!empty($matches[1])) $imageurl=$matches[1].'m.jpg';
1971 }
1972 else // This is a flickr page (html)
1973 {
1974 // Get the flickr html page.
1975 list($headers, $content) = get_http_response($url, 20);
1976 if (strpos($headers[0], '200 OK') !== false)
1977 {
1978 // flickr now nicely provides the URL of the thumbnail in each flickr page.
1979 preg_match('!<link rel=\"image_src\" href=\"(.+?)\"!', $content, $matches);
1980 if (!empty($matches[1])) $imageurl=$matches[1];
1981
1982 // In albums (and some other pages), the link rel="image_src" is not provided,
1983 // but flickr provides:
1984 // <meta property="og:image" content="http://farm4.staticflickr.com/3398/3239339068_25d13535ff_z.jpg" />
1985 if ($imageurl=='')
1986 {
1987 preg_match('!<meta property=\"og:image\" content=\"(.+?)\"!', $content, $matches);
1988 if (!empty($matches[1])) $imageurl=$matches[1];
1989 }
1990 }
1991 }
1992
1993 if ($imageurl!='')
1994 { // Let's download the image.
1995 // Image is 240x120, so 10 seconds to download should be enough.
1996 list($headers, $content) = get_http_response($imageurl, 10);
1997 if (strpos($headers[0], '200 OK') !== false) {
1998 // Save image to cache.
1999 file_put_contents($cacheDir .'/'. $thumbname, $content);
2000 header('Content-Type: image/jpeg');
2001 echo $content;
2002 return;
2003 }
2004 }
2005 }
2006
2007 elseif ($domain=='vimeo.com' )
2008 {
2009 // This is more complex: we have to perform a HTTP request, then parse the result.
2010 // Maybe we should deport this to JavaScript ? Example: http://stackoverflow.com/questions/1361149/get-img-thumbnails-from-vimeo/4285098#4285098
2011 $vid = substr(parse_url($url,PHP_URL_PATH),1);
2012 list($headers, $content) = get_http_response('https://vimeo.com/api/v2/video/'.escape($vid).'.php', 5);
2013 if (strpos($headers[0], '200 OK') !== false) {
2014 $t = unserialize($content);
2015 $imageurl = $t[0]['thumbnail_medium'];
2016 // Then we download the image and serve it to our client.
2017 list($headers, $content) = get_http_response($imageurl, 10);
2018 if (strpos($headers[0], '200 OK') !== false) {
2019 // Save image to cache.
2020 file_put_contents($cacheDir .'/'. $thumbname, $content);
2021 header('Content-Type: image/jpeg');
2022 echo $content;
2023 return;
2024 }
2025 }
2026 }
2027
2028 elseif ($domain=='ted.com' || endsWith($domain,'.ted.com'))
2029 {
2030 // The thumbnail for TED talks is located in the <link rel="image_src" [...]> tag on that page
2031 // http://www.ted.com/talks/mikko_hypponen_fighting_viruses_defending_the_net.html
2032 // <link rel="image_src" href="http://images.ted.com/images/ted/28bced335898ba54d4441809c5b1112ffaf36781_389x292.jpg" />
2033 list($headers, $content) = get_http_response($url, 5);
2034 if (strpos($headers[0], '200 OK') !== false) {
2035 // Extract the link to the thumbnail
2036 preg_match('!link rel="image_src" href="(http://images.ted.com/images/ted/.+_\d+x\d+\.jpg)"!', $content, $matches);
2037 if (!empty($matches[1]))
2038 { // Let's download the image.
2039 $imageurl=$matches[1];
2040 // No control on image size, so wait long enough
2041 list($headers, $content) = get_http_response($imageurl, 20);
2042 if (strpos($headers[0], '200 OK') !== false) {
2043 $filepath = $cacheDir .'/'. $thumbname;
2044 file_put_contents($filepath, $content); // Save image to cache.
2045 if (resizeImage($filepath))
2046 {
2047 header('Content-Type: image/jpeg');
2048 echo file_get_contents($filepath);
2049 return;
2050 }
2051 }
2052 }
2053 }
2054 }
2055
2056 elseif ($domain=='xkcd.com' || endsWith($domain,'.xkcd.com'))
2057 {
2058 // There is no thumbnail available for xkcd comics, so download the whole image and resize it.
2059 // http://xkcd.com/327/
2060 // <img src="http://imgs.xkcd.com/comics/exploits_of_a_mom.png" title="<BLABLA>" alt="<BLABLA>" />
2061 list($headers, $content) = get_http_response($url, 5);
2062 if (strpos($headers[0], '200 OK') !== false) {
2063 // Extract the link to the thumbnail
2064 preg_match('!<img src="(http://imgs.xkcd.com/comics/.*)" title="[^s]!', $content, $matches);
2065 if (!empty($matches[1]))
2066 { // Let's download the image.
2067 $imageurl=$matches[1];
2068 // No control on image size, so wait long enough
2069 list($headers, $content) = get_http_response($imageurl, 20);
2070 if (strpos($headers[0], '200 OK') !== false) {
2071 $filepath = $cacheDir.'/'.$thumbname;
2072 // Save image to cache.
2073 file_put_contents($filepath, $content);
2074 if (resizeImage($filepath))
2075 {
2076 header('Content-Type: image/jpeg');
2077 echo file_get_contents($filepath);
2078 return;
2079 }
2080 }
2081 }
2082 }
2083 }
2084
2085 else
2086 {
2087 // For all other domains, we try to download the image and make a thumbnail.
2088 // We allow 30 seconds max to download (and downloads are limited to 4 Mb)
2089 list($headers, $content) = get_http_response($url, 30);
2090 if (strpos($headers[0], '200 OK') !== false) {
2091 $filepath = $cacheDir .'/'.$thumbname;
2092 // Save image to cache.
2093 file_put_contents($filepath, $content);
2094 if (resizeImage($filepath))
2095 {
2096 header('Content-Type: image/jpeg');
2097 echo file_get_contents($filepath);
2098 return;
2099 }
2100 }
2101 }
2102
2103
2104 // Otherwise, return an empty image (8x8 transparent gif)
2105 $blankgif = base64_decode('R0lGODlhCAAIAIAAAP///////yH5BAEKAAEALAAAAAAIAAgAAAIHjI+py+1dAAA7');
2106 // Also put something in cache so that this URL is not requested twice.
2107 file_put_contents($cacheDir .'/'. $blankname, $blankgif);
2108 header('Content-Type: image/gif');
2109 echo $blankgif;
2110}
2111
2112// Make a thumbnail of the image (to width: 120 pixels)
2113// Returns true if success, false otherwise.
2114function resizeImage($filepath)
2115{
2116 if (!function_exists('imagecreatefromjpeg')) return false; // GD not present: no thumbnail possible.
2117
2118 // Trick: some stupid people rename GIF as JPEG... or else.
2119 // So we really try to open each image type whatever the extension is.
2120 $header=file_get_contents($filepath,false,NULL,0,256); // Read first 256 bytes and try to sniff file type.
2121 $im=false;
2122 $i=strpos($header,'GIF8'); if (($i!==false) && ($i==0)) $im = imagecreatefromgif($filepath); // Well this is crude, but it should be enough.
2123 $i=strpos($header,'PNG'); if (($i!==false) && ($i==1)) $im = imagecreatefrompng($filepath);
2124 $i=strpos($header,'JFIF'); if ($i!==false) $im = imagecreatefromjpeg($filepath);
2125 if (!$im) return false; // Unable to open image (corrupted or not an image)
2126 $w = imagesx($im);
2127 $h = imagesy($im);
2128 $ystart = 0; $yheight=$h;
2129 if ($h>$w) { $ystart= ($h/2)-($w/2); $yheight=$w/2; }
2130 $nw = 120; // Desired width
2131 $nh = min(floor(($h*$nw)/$w),120); // Compute new width/height, but maximum 120 pixels height.
2132 // Resize image:
2133 $im2 = imagecreatetruecolor($nw,$nh);
2134 imagecopyresampled($im2, $im, 0, 0, 0, $ystart, $nw, $nh, $w, $yheight);
2135 imageinterlace($im2,true); // For progressive JPEG.
2136 $tempname=$filepath.'_TEMP.jpg';
2137 imagejpeg($im2, $tempname, 90);
2138 imagedestroy($im);
2139 imagedestroy($im2);
2140 unlink($filepath);
2141 rename($tempname,$filepath); // Overwrite original picture with thumbnail.
2142 return true;
2143} 1865}
2144 1866
2145if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=genthumbnail')) { genThumbnail($conf); exit; } // Thumbnail generation/cache does not need the link database.
2146if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=dailyrss')) { showDailyRSS($conf); exit; }
2147if (!isset($_SESSION['LINKS_PER_PAGE'])) { 1867if (!isset($_SESSION['LINKS_PER_PAGE'])) {
2148 $_SESSION['LINKS_PER_PAGE'] = $conf->get('general.links_per_page', 20); 1868 $_SESSION['LINKS_PER_PAGE'] = $conf->get('general.links_per_page', 20);
2149} 1869}
2150 1870
2151try { 1871try {
2152 $history = new History($conf->get('resource.history')); 1872 $history = new History($conf->get('resource.history'));
2153} catch(Exception $e) { 1873} catch (Exception $e) {
2154 die($e->getMessage()); 1874 die($e->getMessage());
2155} 1875}
2156 1876
@@ -2169,7 +1889,7 @@ $container['history'] = $history;
2169$app = new \Slim\App($container); 1889$app = new \Slim\App($container);
2170 1890
2171// REST API routes 1891// REST API routes
2172$app->group('/api/v1', function() { 1892$app->group('/api/v1', function () {
2173 $this->get('/info', '\Shaarli\Api\Controllers\Info:getInfo')->setName('getInfo'); 1893 $this->get('/info', '\Shaarli\Api\Controllers\Info:getInfo')->setName('getInfo');
2174 $this->get('/links', '\Shaarli\Api\Controllers\Links:getLinks')->setName('getLinks'); 1894 $this->get('/links', '\Shaarli\Api\Controllers\Links:getLinks')->setName('getLinks');
2175 $this->get('/links/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:getLink')->setName('getLink'); 1895 $this->get('/links/{id:[\d]+}', '\Shaarli\Api\Controllers\Links:getLink')->setName('getLink');
@@ -2186,6 +1906,7 @@ $app->group('/api/v1', function() {
2186})->add('\Shaarli\Api\ApiMiddleware'); 1906})->add('\Shaarli\Api\ApiMiddleware');
2187 1907
2188$response = $app->run(true); 1908$response = $app->run(true);
1909
2189// Hack to make Slim and Shaarli router work together: 1910// Hack to make Slim and Shaarli router work together:
2190// If a Slim route isn't found and NOT API call, we call renderPage(). 1911// If a Slim route isn't found and NOT API call, we call renderPage().
2191if ($response->getStatusCode() == 404 && strpos($_SERVER['REQUEST_URI'], '/api/v1') === false) { 1912if ($response->getStatusCode() == 404 && strpos($_SERVER['REQUEST_URI'], '/api/v1') === false) {
@@ -2193,5 +1914,12 @@ if ($response->getStatusCode() == 404 && strpos($_SERVER['REQUEST_URI'], '/api/v
2193 header('Content-Type: text/html; charset=utf-8'); 1914 header('Content-Type: text/html; charset=utf-8');
2194 renderPage($conf, $pluginManager, $linkDb, $history, $sessionManager, $loginManager); 1915 renderPage($conf, $pluginManager, $linkDb, $history, $sessionManager, $loginManager);
2195} else { 1916} else {
1917 $response = $response
1918 ->withHeader('Access-Control-Allow-Origin', '*')
1919 ->withHeader(
1920 'Access-Control-Allow-Headers',
1921 'X-Requested-With, Content-Type, Accept, Origin, Authorization'
1922 )
1923 ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
2196 $app->respond($response); 1924 $app->respond($response);
2197} 1925}
diff --git a/mkdocs.yml b/mkdocs.yml
index a1226e8f..248fdbfe 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -2,10 +2,15 @@ site_name: Shaarli Documentation
2repo_url: https://github.com/shaarli/Shaarli 2repo_url: https://github.com/shaarli/Shaarli
3edit_uri: edit/master/doc/md 3edit_uri: edit/master/doc/md
4site_description: The personal, minimalist, super-fast, database free, bookmarking service 4site_description: The personal, minimalist, super-fast, database free, bookmarking service
5theme: readthedocs 5theme:
6 name: readthedocs
7 custom_dir: doc/custom_theme/
6docs_dir: doc/md 8docs_dir: doc/md
7site_dir: doc/html 9site_dir: doc/html
8strict: true 10# Disable strict mode until ReadTheDocs provides up-to-date MkDocs settings:
11# - https://github.com/shaarli/Shaarli/issues/1179
12# - https://github.com/rtfd/readthedocs.org/issues/4314
13# strict: true
9 14
10pages: 15pages:
11- Home: index.md 16- Home: index.md
@@ -27,14 +32,16 @@ pages:
27 - RSS feeds: RSS-feeds.md 32 - RSS feeds: RSS-feeds.md
28 - REST API: REST-API.md 33 - REST API: REST-API.md
29 - Community & Related software: Community-&-Related-software.md 34 - Community & Related software: Community-&-Related-software.md
30- How To: 35- Guides:
31 - Backup, restore, import and export: Backup,-restore,-import-and-export.md 36 - Install Shaarli on Debian 9 with Docker: guides/install-shaarli-with-debian9-and-docker.md
32 - Various hacks: Various-hacks.md 37 - Backup, restore, import and export: guides/backup-restore-import-export.md
38 - Various hacks: guides/various-hacks.md
33- Development: 39- Development:
34 - Development guidelines: Development-guidelines.md 40 - Development guidelines: Development-guidelines.md
35 - Continuous integration tools: Continuous-integration-tools.md 41 - Continuous integration tools: Continuous-integration-tools.md
36 - GnuPG signature: GnuPG-signature.md 42 - GnuPG signature: GnuPG-signature.md
37 - Directory structure: Directory-structure.md 43 - Directory structure: Directory-structure.md
44 - Link Structure: Link-structure.md
38 - 3rd party libraries: 3rd-party-libraries.md 45 - 3rd party libraries: 3rd-party-libraries.md
39 - Plugin System: Plugin-System.md 46 - Plugin System: Plugin-System.md
40 - Release Shaarli: Release-Shaarli.md 47 - Release Shaarli: Release-Shaarli.md
diff --git a/phpcs.xml b/phpcs.xml
new file mode 100644
index 00000000..29b95d56
--- /dev/null
+++ b/phpcs.xml
@@ -0,0 +1,17 @@
1<?xml version="1.0"?>
2<ruleset name="Shaarli">
3 <description>The Shaarli coding standards</description>
4
5 <file>index.php</file>
6 <file>application</file>
7 <file>plugins</file>
8 <file>tests</file>
9
10 <exclude-pattern>*/*.css</exclude-pattern>
11 <exclude-pattern>*/*.js</exclude-pattern>
12
13 <arg name="colors"/>
14
15 <rule ref="PSR1"/>
16 <rule ref="PSR2"/>
17</ruleset>
diff --git a/phpdoc.dist.xml b/phpdoc.dist.xml
new file mode 100644
index 00000000..3878550e
--- /dev/null
+++ b/phpdoc.dist.xml
@@ -0,0 +1,21 @@
1<?xml version="1.0" encoding="UTF-8" ?>
2<phpdoc>
3 <title><![CDATA[Shaarli API Reference]]></title>
4 <parser>
5 <encoding>utf8</encoding>
6 <markers>
7 <item>FIXME</item>
8 <item>TODO</item>
9 <item>WIP</item>
10 </markers>
11 <target>doc/phpdoc</target>
12 </parser>
13 <transformer>
14 <target>doc/phpdoc</target>
15 </transformer>
16 <files>
17 <file>index.php</file>
18 <directory>application</directory>
19 <directory>plugins</directory>
20 </files>
21</phpdoc>
diff --git a/plugins/addlink_toolbar/addlink_toolbar.php b/plugins/addlink_toolbar/addlink_toolbar.php
index 8c05a231..8bf4ed46 100644
--- a/plugins/addlink_toolbar/addlink_toolbar.php
+++ b/plugins/addlink_toolbar/addlink_toolbar.php
@@ -5,6 +5,8 @@
5 * Adds the addlink input on the linklist page. 5 * Adds the addlink input on the linklist page.
6 */ 6 */
7 7
8use Shaarli\Router;
9
8/** 10/**
9 * When linklist is displayed, add play videos to header's toolbar. 11 * When linklist is displayed, add play videos to header's toolbar.
10 * 12 *
diff --git a/plugins/archiveorg/archiveorg.php b/plugins/archiveorg/archiveorg.php
index cda35751..0ee1c73c 100644
--- a/plugins/archiveorg/archiveorg.php
+++ b/plugins/archiveorg/archiveorg.php
@@ -5,6 +5,8 @@
5 * Add an icon in the link list for archive.org. 5 * Add an icon in the link list for archive.org.
6 */ 6 */
7 7
8use Shaarli\Plugin\PluginManager;
9
8/** 10/**
9 * Add archive.org icon to link_plugin when rendering linklist. 11 * Add archive.org icon to link_plugin when rendering linklist.
10 * 12 *
@@ -17,7 +19,7 @@ function hook_archiveorg_render_linklist($data)
17 $archive_html = file_get_contents(PluginManager::$PLUGINS_PATH . '/archiveorg/archiveorg.html'); 19 $archive_html = file_get_contents(PluginManager::$PLUGINS_PATH . '/archiveorg/archiveorg.html');
18 20
19 foreach ($data['links'] as &$value) { 21 foreach ($data['links'] as &$value) {
20 if($value['private'] && preg_match('/^\?[a-zA-Z0-9-_@]{6}($|&|#)/', $value['real_url'])) { 22 if ($value['private'] && preg_match('/^\?[a-zA-Z0-9-_@]{6}($|&|#)/', $value['real_url'])) {
21 continue; 23 continue;
22 } 24 }
23 $archive = sprintf($archive_html, $value['url'], t('View on archive.org')); 25 $archive = sprintf($archive_html, $value['url'], t('View on archive.org'));
diff --git a/plugins/demo_plugin/demo_plugin.php b/plugins/demo_plugin/demo_plugin.php
index f3a63b6a..95ea7fe2 100644
--- a/plugins/demo_plugin/demo_plugin.php
+++ b/plugins/demo_plugin/demo_plugin.php
@@ -15,6 +15,8 @@
15 */ 15 */
16 16
17use Shaarli\Config\ConfigManager; 17use Shaarli\Config\ConfigManager;
18use Shaarli\Plugin\PluginManager;
19use Shaarli\Router;
18 20
19/** 21/**
20 * In the footer hook, there is a working example of a translation extension for Shaarli. 22 * In the footer hook, there is a working example of a translation extension for Shaarli.
@@ -73,7 +75,6 @@ function hook_demo_plugin_render_header($data)
73{ 75{
74 // Only execute when linklist is rendered. 76 // Only execute when linklist is rendered.
75 if ($data['_PAGE_'] == Router::$PAGE_LINKLIST) { 77 if ($data['_PAGE_'] == Router::$PAGE_LINKLIST) {
76
77 // If loggedin 78 // If loggedin
78 if ($data['_LOGGEDIN_'] === true) { 79 if ($data['_LOGGEDIN_'] === true) {
79 /* 80 /*
@@ -109,10 +110,10 @@ function hook_demo_plugin_render_header($data)
109 * ], 110 * ],
110 * ] 111 * ]
111 * This example renders as: 112 * This example renders as:
112 * <form form-attribute-1="form attribute 1 value" form-attribute-2="form attribute 2 value"> 113 * <form form-attribute-1="form attribute 1 value" form-attribute-2="form attribute 2 value">
113 * <input input-1-attribute-1="input 1 attribute 1 value" input-1-attribute-2="input 1 attribute 2 value"> 114 * <input input-1-attribute-1="input 1 attribute 1 value" input-1-attribute-2="input 1 attribute 2 value">
114 * <input input-2-attribute-1="input 2 attribute 1 value"> 115 * <input input-2-attribute-1="input 2 attribute 1 value">
115 * </form> 116 * </form>
116 */ 117 */
117 $form = array( 118 $form = array(
118 'attr' => array( 119 'attr' => array(
@@ -448,8 +449,7 @@ function hook_demo_plugin_render_feed($data)
448 foreach ($data['links'] as &$link) { 449 foreach ($data['links'] as &$link) {
449 if ($data['_PAGE_'] == Router::$PAGE_FEED_ATOM) { 450 if ($data['_PAGE_'] == Router::$PAGE_FEED_ATOM) {
450 $link['description'] .= ' - ATOM Feed' ; 451 $link['description'] .= ' - ATOM Feed' ;
451 } 452 } elseif ($data['_PAGE_'] == Router::$PAGE_FEED_RSS) {
452 elseif ($data['_PAGE_'] == Router::$PAGE_FEED_RSS) {
453 $link['description'] .= ' - RSS Feed'; 453 $link['description'] .= ' - RSS Feed';
454 } 454 }
455 } 455 }
diff --git a/plugins/isso/comment.png b/plugins/isso/comment.png
new file mode 100644
index 00000000..0158c03b
--- /dev/null
+++ b/plugins/isso/comment.png
Binary files differ
diff --git a/plugins/isso/isso.php b/plugins/isso/isso.php
index 5bc1cce2..dab75dd5 100644
--- a/plugins/isso/isso.php
+++ b/plugins/isso/isso.php
@@ -5,6 +5,8 @@
5 */ 5 */
6 6
7use Shaarli\Config\ConfigManager; 7use Shaarli\Config\ConfigManager;
8use Shaarli\Plugin\PluginManager;
9use Shaarli\Router;
8 10
9/** 11/**
10 * Display an error everywhere if the plugin is enabled without configuration. 12 * Display an error everywhere if the plugin is enabled without configuration.
@@ -46,9 +48,36 @@ function hook_isso_render_linklist($data, $conf)
46 48
47 $isso = sprintf($issoHtml, $issoUrl, $issoUrl, $link['id'], $link['id']); 49 $isso = sprintf($issoHtml, $issoUrl, $issoUrl, $link['id'], $link['id']);
48 $data['plugin_end_zone'][] = $isso; 50 $data['plugin_end_zone'][] = $isso;
51 } else {
52 $button = '<span><a href="?%s#isso-thread">';
53 // For the default theme we use a FontAwesome icon which is better than an image
54 if ($conf->get('resource.theme') === 'default') {
55 $button .= '<i class="linklist-plugin-icon fa fa-comment"></i>';
56 } else {
57 $button .= '<img class="linklist-plugin-icon" src="plugins/isso/comment.png" ';
58 $button .= 'title="Comment on this shaare" alt="Comments" />';
59 }
60 $button .= '</a></span>';
61 foreach ($data['links'] as &$value) {
62 $commentLink = sprintf($button, $value['shorturl']);
63 $value['link_plugin'][] = $commentLink;
64 }
65 }
49 66
50 // Hackish way to include this CSS file only when necessary. 67 return $data;
51 $data['plugins_includes']['css_files'][] = PluginManager::$PLUGINS_PATH . '/isso/isso.css'; 68}
69
70/**
71 * When linklist is displayed, include isso CSS file.
72 *
73 * @param array $data - header data.
74 *
75 * @return mixed - header data with isso CSS file added.
76 */
77function hook_isso_render_includes($data)
78{
79 if ($data['_PAGE_'] == Router::$PAGE_LINKLIST) {
80 $data['css_files'][] = PluginManager::$PLUGINS_PATH . '/isso/isso.css';
52 } 81 }
53 82
54 return $data; 83 return $data;
diff --git a/plugins/isso/isso_button.html b/plugins/isso/isso_button.html
new file mode 100644
index 00000000..3f828480
--- /dev/null
+++ b/plugins/isso/isso_button.html
@@ -0,0 +1,5 @@
1<span>
2 <a href="?%s#isso-thread">
3 <img class="linklist-plugin-icon" src="plugins/archiveorg/internetarchive.png" title="%s" alt="archive.org" />
4 </a>
5</span>
diff --git a/plugins/markdown/markdown.php b/plugins/markdown/markdown.php
index 821bb125..628970d6 100644
--- a/plugins/markdown/markdown.php
+++ b/plugins/markdown/markdown.php
@@ -7,6 +7,8 @@
7 */ 7 */
8 8
9use Shaarli\Config\ConfigManager; 9use Shaarli\Config\ConfigManager;
10use Shaarli\Plugin\PluginManager;
11use Shaarli\Router;
10 12
11/* 13/*
12 * If this tag is used on a shaare, the description won't be processed by Parsedown. 14 * If this tag is used on a shaare, the description won't be processed by Parsedown.
@@ -28,6 +30,7 @@ function hook_markdown_render_linklist($data, $conf)
28 $value = stripNoMarkdownTag($value); 30 $value = stripNoMarkdownTag($value);
29 continue; 31 continue;
30 } 32 }
33 $value['description_src'] = $value['description'];
31 $value['description'] = process_markdown( 34 $value['description'] = process_markdown(
32 $value['description'], 35 $value['description'],
33 $conf->get('security.markdown_escape', true), 36 $conf->get('security.markdown_escape', true),
@@ -138,7 +141,6 @@ function hook_markdown_render_includes($data)
138 || $data['_PAGE_'] == Router::$PAGE_DAILY 141 || $data['_PAGE_'] == Router::$PAGE_DAILY
139 || $data['_PAGE_'] == Router::$PAGE_EDITLINK 142 || $data['_PAGE_'] == Router::$PAGE_EDITLINK
140 ) { 143 ) {
141
142 $data['css_files'][] = PluginManager::$PLUGINS_PATH . '/markdown/markdown.css'; 144 $data['css_files'][] = PluginManager::$PLUGINS_PATH . '/markdown/markdown.css';
143 } 145 }
144 146
@@ -194,8 +196,7 @@ function reverse_text2clickable($description)
194 // Detect and toggle block of code 196 // Detect and toggle block of code
195 if (!$codeBlockOn) { 197 if (!$codeBlockOn) {
196 $codeBlockOn = preg_match('/^```/', $descriptionLine) > 0; 198 $codeBlockOn = preg_match('/^```/', $descriptionLine) > 0;
197 } 199 } elseif (preg_match('/^```/', $descriptionLine) > 0) {
198 elseif (preg_match('/^```/', $descriptionLine) > 0) {
199 $codeBlockOn = false; 200 $codeBlockOn = false;
200 } 201 }
201 202
@@ -215,6 +216,15 @@ function reverse_text2clickable($description)
215 $descriptionLine 216 $descriptionLine
216 ); 217 );
217 218
219 // Make hashtag links markdown ready, otherwise the links will be ignored with escape set to true
220 if (!$codeBlockOn && !$codeLineOn) {
221 $descriptionLine = preg_replace(
222 '#<a href="([^ ]*)"'. $hashtagTitle .'>([^<]+)</a>#m',
223 '[$2]($1)',
224 $descriptionLine
225 );
226 }
227
218 $descriptionOut .= $descriptionLine; 228 $descriptionOut .= $descriptionLine;
219 if ($lineCount++ < count($descriptionLines) - 1) { 229 if ($lineCount++ < count($descriptionLines) - 1) {
220 $descriptionOut .= PHP_EOL; 230 $descriptionOut .= PHP_EOL;
@@ -292,13 +302,17 @@ function sanitize_html($description)
292 foreach ($escapeTags as $tag) { 302 foreach ($escapeTags as $tag) {
293 $description = preg_replace_callback( 303 $description = preg_replace_callback(
294 '#<\s*'. $tag .'[^>]*>(.*</\s*'. $tag .'[^>]*>)?#is', 304 '#<\s*'. $tag .'[^>]*>(.*</\s*'. $tag .'[^>]*>)?#is',
295 function ($match) { return escape($match[0]); }, 305 function ($match) {
296 $description); 306 return escape($match[0]);
307 },
308 $description
309 );
297 } 310 }
298 $description = preg_replace( 311 $description = preg_replace(
299 '#(<[^>]+\s)on[a-z]*="?[^ "]*"?#is', 312 '#(<[^>]+\s)on[a-z]*="?[^ "]*"?#is',
300 '$1', 313 '$1',
301 $description); 314 $description
315 );
302 return $description; 316 return $description;
303} 317}
304 318
@@ -331,7 +345,7 @@ function process_markdown($description, $escape = true, $allowedProtocols = [])
331 ->text($processedDescription); 345 ->text($processedDescription);
332 $processedDescription = sanitize_html($processedDescription); 346 $processedDescription = sanitize_html($processedDescription);
333 347
334 if(!empty($processedDescription)){ 348 if (!empty($processedDescription)) {
335 $processedDescription = '<div class="markdown">'. $processedDescription . '</div>'; 349 $processedDescription = '<div class="markdown">'. $processedDescription . '</div>';
336 } 350 }
337 351
diff --git a/plugins/piwik/piwik.php b/plugins/piwik/piwik.php
index ca00c2be..17b1aecc 100644
--- a/plugins/piwik/piwik.php
+++ b/plugins/piwik/piwik.php
@@ -4,6 +4,8 @@
4 * Adds tracking code on each page. 4 * Adds tracking code on each page.
5 */ 5 */
6 6
7use Shaarli\Plugin\PluginManager;
8
7/** 9/**
8 * Initialization function. 10 * Initialization function.
9 * It will be called when the plugin is loaded. 11 * It will be called when the plugin is loaded.
diff --git a/plugins/playvideos/playvideos.php b/plugins/playvideos/playvideos.php
index c6d6b0cc..0341ed59 100644
--- a/plugins/playvideos/playvideos.php
+++ b/plugins/playvideos/playvideos.php
@@ -6,6 +6,9 @@
6 * Note: this plugin adds jQuery. 6 * Note: this plugin adds jQuery.
7 */ 7 */
8 8
9use Shaarli\Plugin\PluginManager;
10use Shaarli\Router;
11
9/** 12/**
10 * When linklist is displayed, add play videos to header's toolbar. 13 * When linklist is displayed, add play videos to header's toolbar.
11 * 14 *
diff --git a/plugins/pubsubhubbub/pubsubhubbub.php b/plugins/pubsubhubbub/pubsubhubbub.php
index 184b588b..2878c050 100644
--- a/plugins/pubsubhubbub/pubsubhubbub.php
+++ b/plugins/pubsubhubbub/pubsubhubbub.php
@@ -6,11 +6,14 @@
6 * PubSub is a protocol which fasten up RSS fetching: 6 * PubSub is a protocol which fasten up RSS fetching:
7 * - Every time a new link is posted, Shaarli notify the hub. 7 * - Every time a new link is posted, Shaarli notify the hub.
8 * - The hub notify all feed subscribers that a new link has been posted. 8 * - The hub notify all feed subscribers that a new link has been posted.
9 * - Subscribers retrieve the new link. 9 * - Subscribers retrieve the new link.
10 */ 10 */
11 11
12use pubsubhubbub\publisher\Publisher; 12use pubsubhubbub\publisher\Publisher;
13use Shaarli\Config\ConfigManager; 13use Shaarli\Config\ConfigManager;
14use Shaarli\Feed\FeedBuilder;
15use Shaarli\Plugin\PluginManager;
16use Shaarli\Router;
14 17
15/** 18/**
16 * Plugin init function - set the hub to the default appspot one. 19 * Plugin init function - set the hub to the default appspot one.
@@ -82,7 +85,8 @@ function hook_pubsubhubbub_save_link($data, $conf)
82 * 85 *
83 * @throws Exception An error occurred. 86 * @throws Exception An error occurred.
84 */ 87 */
85function nocurl_http_post($url, $postString) { 88function nocurl_http_post($url, $postString)
89{
86 $params = array('http' => array( 90 $params = array('http' => array(
87 'method' => 'POST', 91 'method' => 'POST',
88 'content' => $postString, 92 'content' => $postString,
diff --git a/plugins/qrcode/qrcode.php b/plugins/qrcode/qrcode.php
index 0f96a106..34eef8be 100644
--- a/plugins/qrcode/qrcode.php
+++ b/plugins/qrcode/qrcode.php
@@ -5,6 +5,9 @@
5 * Display a QRCode icon in link list. 5 * Display a QRCode icon in link list.
6 */ 6 */
7 7
8use Shaarli\Plugin\PluginManager;
9use Shaarli\Router;
10
8/** 11/**
9 * Add qrcode icon to link_plugin when rendering linklist. 12 * Add qrcode icon to link_plugin when rendering linklist.
10 * 13 *
@@ -17,7 +20,8 @@ function hook_qrcode_render_linklist($data)
17 $qrcode_html = file_get_contents(PluginManager::$PLUGINS_PATH . '/qrcode/qrcode.html'); 20 $qrcode_html = file_get_contents(PluginManager::$PLUGINS_PATH . '/qrcode/qrcode.html');
18 21
19 foreach ($data['links'] as &$value) { 22 foreach ($data['links'] as &$value) {
20 $qrcode = sprintf($qrcode_html, 23 $qrcode = sprintf(
24 $qrcode_html,
21 urlencode($value['url']), 25 urlencode($value['url']),
22 $value['url'], 26 $value['url'],
23 PluginManager::$PLUGINS_PATH 27 PluginManager::$PLUGINS_PATH
diff --git a/plugins/wallabag/WallabagInstance.php b/plugins/wallabag/WallabagInstance.php
index eb8ab618..f4a0a92b 100644
--- a/plugins/wallabag/WallabagInstance.php
+++ b/plugins/wallabag/WallabagInstance.php
@@ -1,4 +1,5 @@
1<?php 1<?php
2namespace Shaarli\Plugin\Wallabag;
2 3
3/** 4/**
4 * Class WallabagInstance. 5 * Class WallabagInstance.
diff --git a/plugins/wallabag/wallabag.php b/plugins/wallabag/wallabag.php
index 9dfd079e..bc35df08 100644
--- a/plugins/wallabag/wallabag.php
+++ b/plugins/wallabag/wallabag.php
@@ -1,11 +1,11 @@
1<?php 1<?php
2
3/** 2/**
4 * Plugin Wallabag. 3 * Wallabag plugin
5 */ 4 */
6 5
7require_once 'WallabagInstance.php';
8use Shaarli\Config\ConfigManager; 6use Shaarli\Config\ConfigManager;
7use Shaarli\Plugin\PluginManager;
8use Shaarli\Plugin\Wallabag\WallabagInstance;
9 9
10/** 10/**
11 * Init function, return an error if the server is not set. 11 * Init function, return an error if the server is not set.
@@ -69,4 +69,3 @@ function wallabag_dummy_translation()
69 t('Wallabag API URL'); 69 t('Wallabag API URL');
70 t('Wallabag API version (1 or 2)'); 70 t('Wallabag API version (1 or 2)');
71} 71}
72
diff --git a/tests/ApplicationUtilsTest.php b/tests/ApplicationUtilsTest.php
index ff4c9e17..82f8804d 100644
--- a/tests/ApplicationUtilsTest.php
+++ b/tests/ApplicationUtilsTest.php
@@ -1,33 +1,14 @@
1<?php 1<?php
2use Shaarli\Config\ConfigManager; 2namespace Shaarli;
3
4/**
5 * ApplicationUtils' tests
6 */
7 3
8require_once 'application/ApplicationUtils.php'; 4use Shaarli\Config\ConfigManager;
9
10/**
11 * Fake ApplicationUtils class to avoid HTTP requests
12 */
13class FakeApplicationUtils extends ApplicationUtils
14{
15 public static $VERSION_CODE = '';
16
17 /**
18 * Toggle HTTP requests, allow overriding the version code
19 */
20 public static function getVersion($url, $timeout=0)
21 {
22 return self::$VERSION_CODE;
23 }
24}
25 5
6require_once 'tests/utils/FakeApplicationUtils.php';
26 7
27/** 8/**
28 * Unitary tests for Shaarli utilities 9 * Unitary tests for Shaarli utilities
29 */ 10 */
30class ApplicationUtilsTest extends PHPUnit_Framework_TestCase 11class ApplicationUtilsTest extends \PHPUnit\Framework\TestCase
31{ 12{
32 protected static $testUpdateFile = 'sandbox/update.txt'; 13 protected static $testUpdateFile = 'sandbox/update.txt';
33 protected static $testVersion = '0.5.0'; 14 protected static $testVersion = '0.5.0';
@@ -67,7 +48,7 @@ class ApplicationUtilsTest extends PHPUnit_Framework_TestCase
67 '0.5.4', 48 '0.5.4',
68 ApplicationUtils::getVersion( 49 ApplicationUtils::getVersion(
69 'https://raw.githubusercontent.com/shaarli/Shaarli/' 50 'https://raw.githubusercontent.com/shaarli/Shaarli/'
70 .'v0.5.4/shaarli_version.php', 51 .'v0.5.4/shaarli_version.php',
71 $testTimeout 52 $testTimeout
72 ) 53 )
73 ); 54 );
@@ -75,7 +56,7 @@ class ApplicationUtilsTest extends PHPUnit_Framework_TestCase
75 self::$versionPattern, 56 self::$versionPattern,
76 ApplicationUtils::getVersion( 57 ApplicationUtils::getVersion(
77 'https://raw.githubusercontent.com/shaarli/Shaarli/' 58 'https://raw.githubusercontent.com/shaarli/Shaarli/'
78 .'latest/shaarli_version.php', 59 .'latest/shaarli_version.php',
79 $testTimeout 60 $testTimeout
80 ) 61 )
81 ); 62 );
diff --git a/tests/FileUtilsTest.php b/tests/FileUtilsTest.php
index d764e495..57719175 100644
--- a/tests/FileUtilsTest.php
+++ b/tests/FileUtilsTest.php
@@ -1,13 +1,15 @@
1<?php 1<?php
2 2
3require_once 'application/FileUtils.php'; 3namespace Shaarli;
4
5use Exception;
4 6
5/** 7/**
6 * Class FileUtilsTest 8 * Class FileUtilsTest
7 * 9 *
8 * Test file utility class. 10 * Test file utility class.
9 */ 11 */
10class FileUtilsTest extends PHPUnit_Framework_TestCase 12class FileUtilsTest extends \PHPUnit\Framework\TestCase
11{ 13{
12 /** 14 /**
13 * @var string Test file path. 15 * @var string Test file path.
@@ -48,7 +50,7 @@ class FileUtilsTest extends PHPUnit_Framework_TestCase
48 /** 50 /**
49 * File not writable: raise an exception. 51 * File not writable: raise an exception.
50 * 52 *
51 * @expectedException IOException 53 * @expectedException Shaarli\Exceptions\IOException
52 * @expectedExceptionMessage Error accessing "sandbox/flat.db" 54 * @expectedExceptionMessage Error accessing "sandbox/flat.db"
53 */ 55 */
54 public function testWriteWithoutPermission() 56 public function testWriteWithoutPermission()
@@ -61,7 +63,7 @@ class FileUtilsTest extends PHPUnit_Framework_TestCase
61 /** 63 /**
62 * Folder non existent: raise an exception. 64 * Folder non existent: raise an exception.
63 * 65 *
64 * @expectedException IOException 66 * @expectedException Shaarli\Exceptions\IOException
65 * @expectedExceptionMessage Error accessing "nopefolder" 67 * @expectedExceptionMessage Error accessing "nopefolder"
66 */ 68 */
67 public function testWriteFolderDoesNotExist() 69 public function testWriteFolderDoesNotExist()
@@ -72,7 +74,7 @@ class FileUtilsTest extends PHPUnit_Framework_TestCase
72 /** 74 /**
73 * Folder non writable: raise an exception. 75 * Folder non writable: raise an exception.
74 * 76 *
75 * @expectedException IOException 77 * @expectedException Shaarli\Exceptions\IOException
76 * @expectedExceptionMessage Error accessing "sandbox" 78 * @expectedExceptionMessage Error accessing "sandbox"
77 */ 79 */
78 public function testWriteFolderPermission() 80 public function testWriteFolderPermission()
diff --git a/tests/HistoryTest.php b/tests/HistoryTest.php
index d3bef5a3..8303e53a 100644
--- a/tests/HistoryTest.php
+++ b/tests/HistoryTest.php
@@ -1,9 +1,11 @@
1<?php 1<?php
2 2
3require_once 'application/History.php'; 3namespace Shaarli;
4 4
5use DateTime;
6use Exception;
5 7
6class HistoryTest extends PHPUnit_Framework_TestCase 8class HistoryTest extends \PHPUnit\Framework\TestCase
7{ 9{
8 /** 10 /**
9 * @var string History file path 11 * @var string History file path
diff --git a/tests/LanguagesTest.php b/tests/LanguagesTest.php
index 4951e09a..de83f291 100644
--- a/tests/LanguagesTest.php
+++ b/tests/LanguagesTest.php
@@ -7,7 +7,7 @@ use Shaarli\Config\ConfigManager;
7/** 7/**
8 * Class LanguagesTest. 8 * Class LanguagesTest.
9 */ 9 */
10class LanguagesTest extends \PHPUnit_Framework_TestCase 10class LanguagesTest extends \PHPUnit\Framework\TestCase
11{ 11{
12 /** 12 /**
13 * @var string Config file path (without extension). 13 * @var string Config file path (without extension).
diff --git a/tests/PluginManagerTest.php b/tests/PluginManagerTest.php
index 01de959c..71761ac1 100644
--- a/tests/PluginManagerTest.php
+++ b/tests/PluginManagerTest.php
@@ -1,16 +1,12 @@
1<?php 1<?php
2use Shaarli\Config\ConfigManager; 2namespace Shaarli\Plugin;
3 3
4/** 4use Shaarli\Config\ConfigManager;
5 * Plugin Manager tests
6 */
7
8require_once 'application/PluginManager.php';
9 5
10/** 6/**
11 * Unit tests for Plugins 7 * Unit tests for Plugins
12 */ 8 */
13class PluginManagerTest extends PHPUnit_Framework_TestCase 9class PluginManagerTest extends \PHPUnit\Framework\TestCase
14{ 10{
15 /** 11 /**
16 * Path to tests plugin. 12 * Path to tests plugin.
diff --git a/tests/RouterTest.php b/tests/RouterTest.php
index 544bcf9c..0cd49bb8 100644
--- a/tests/RouterTest.php
+++ b/tests/RouterTest.php
@@ -1,15 +1,10 @@
1<?php 1<?php
2 2namespace Shaarli;
3/**
4 * Router tests
5 */
6
7require_once 'application/Router.php';
8 3
9/** 4/**
10 * Unit tests for Router 5 * Unit tests for Router
11 */ 6 */
12class RouterTest extends PHPUnit_Framework_TestCase 7class RouterTest extends \PHPUnit\Framework\TestCase
13{ 8{
14 /** 9 /**
15 * Test findPage: login page output. 10 * Test findPage: login page output.
@@ -218,7 +213,6 @@ class RouterTest extends PHPUnit_Framework_TestCase
218 Router::$PAGE_CHANGEPASSWORD, 213 Router::$PAGE_CHANGEPASSWORD,
219 Router::findPage('do=changepasswd&stuff', array(), true) 214 Router::findPage('do=changepasswd&stuff', array(), true)
220 ); 215 );
221
222 } 216 }
223 217
224 /** 218 /**
diff --git a/tests/ThumbnailerTest.php b/tests/ThumbnailerTest.php
new file mode 100644
index 00000000..c01849f7
--- /dev/null
+++ b/tests/ThumbnailerTest.php
@@ -0,0 +1,116 @@
1<?php
2
3namespace Shaarli;
4
5use PHPUnit\Framework\TestCase;
6use Shaarli\Config\ConfigManager;
7use WebThumbnailer\Application\ConfigManager as WTConfigManager;
8
9/**
10 * Class ThumbnailerTest
11 *
12 * We only make 1 thumb test because:
13 *
14 * 1. the thumbnailer library is itself tested
15 * 2. we don't want to make too many external requests during the tests
16 */
17class ThumbnailerTest extends TestCase
18{
19 const WIDTH = 190;
20
21 const HEIGHT = 210;
22
23 /**
24 * @var Thumbnailer;
25 */
26 protected $thumbnailer;
27
28 /**
29 * @var ConfigManager
30 */
31 protected $conf;
32
33 public function setUp()
34 {
35 $this->conf = new ConfigManager('tests/utils/config/configJson');
36 $this->conf->set('thumbnails.mode', Thumbnailer::MODE_ALL);
37 $this->conf->set('thumbnails.width', self::WIDTH);
38 $this->conf->set('thumbnails.height', self::HEIGHT);
39 $this->conf->set('dev.debug', true);
40
41 $this->thumbnailer = new Thumbnailer($this->conf);
42 // cache files in the sandbox
43 WTConfigManager::addFile('tests/utils/config/wt.json');
44 }
45
46 public function tearDown()
47 {
48 $this->rrmdirContent('sandbox/');
49 }
50
51 /**
52 * Test a thumbnail with a custom size in 'all' mode.
53 */
54 public function testThumbnailAllValid()
55 {
56 $thumb = $this->thumbnailer->get('https://github.com/shaarli/Shaarli/');
57 $this->assertNotFalse($thumb);
58 $image = imagecreatefromstring(file_get_contents($thumb));
59 $this->assertEquals(self::WIDTH, imagesx($image));
60 $this->assertEquals(self::HEIGHT, imagesy($image));
61 }
62
63 /**
64 * Test a thumbnail with a custom size in 'common' mode.
65 */
66 public function testThumbnailCommonValid()
67 {
68 $this->conf->set('thumbnails.mode', Thumbnailer::MODE_COMMON);
69 $thumb = $this->thumbnailer->get('https://imgur.com/jlFgGpe');
70 $this->assertNotFalse($thumb);
71 $image = imagecreatefromstring(file_get_contents($thumb));
72 $this->assertEquals(self::WIDTH, imagesx($image));
73 $this->assertEquals(self::HEIGHT, imagesy($image));
74 }
75
76 /**
77 * Test a thumbnail in 'common' mode which isn't include in common websites.
78 */
79 public function testThumbnailCommonInvalid()
80 {
81 $this->conf->set('thumbnails.mode', Thumbnailer::MODE_COMMON);
82 $thumb = $this->thumbnailer->get('https://github.com/shaarli/Shaarli/');
83 $this->assertFalse($thumb);
84 }
85
86 /**
87 * Test a thumbnail that can't be retrieved.
88 */
89 public function testThumbnailNotValid()
90 {
91 $oldlog = ini_get('error_log');
92 ini_set('error_log', '/dev/null');
93
94 $thumbnailer = new Thumbnailer(new ConfigManager());
95 $thumb = $thumbnailer->get('nope');
96 $this->assertFalse($thumb);
97
98 ini_set('error_log', $oldlog);
99 }
100
101 protected function rrmdirContent($dir)
102 {
103 if (is_dir($dir)) {
104 $objects = scandir($dir);
105 foreach ($objects as $object) {
106 if ($object != "." && $object != "..") {
107 if (is_dir($dir."/".$object)) {
108 $this->rrmdirContent($dir."/".$object);
109 } else {
110 unlink($dir."/".$object);
111 }
112 }
113 }
114 }
115 }
116}
diff --git a/tests/TimeZoneTest.php b/tests/TimeZoneTest.php
index 127fdc19..02bf060f 100644
--- a/tests/TimeZoneTest.php
+++ b/tests/TimeZoneTest.php
@@ -8,7 +8,7 @@ require_once 'application/TimeZone.php';
8/** 8/**
9 * Unitary tests for timezone utilities 9 * Unitary tests for timezone utilities
10 */ 10 */
11class TimeZoneTest extends PHPUnit_Framework_TestCase 11class TimeZoneTest extends PHPUnit\Framework\TestCase
12{ 12{
13 /** 13 /**
14 * @var array of timezones 14 * @var array of timezones
diff --git a/tests/UtilsTest.php b/tests/UtilsTest.php
index 6cd37a7a..8225d95a 100644
--- a/tests/UtilsTest.php
+++ b/tests/UtilsTest.php
@@ -10,7 +10,7 @@ require_once 'application/Languages.php';
10/** 10/**
11 * Unitary tests for Shaarli utilities 11 * Unitary tests for Shaarli utilities
12 */ 12 */
13class UtilsTest extends PHPUnit_Framework_TestCase 13class UtilsTest extends PHPUnit\Framework\TestCase
14{ 14{
15 // Log file 15 // Log file
16 protected static $testLogFile = 'tests.log'; 16 protected static $testLogFile = 'tests.log';
@@ -187,7 +187,8 @@ class UtilsTest extends PHPUnit_Framework_TestCase
187 /** 187 /**
188 * Test generate location with valid data. 188 * Test generate location with valid data.
189 */ 189 */
190 public function testGenerateLocation() { 190 public function testGenerateLocation()
191 {
191 $ref = 'http://localhost/?test'; 192 $ref = 'http://localhost/?test';
192 $this->assertEquals($ref, generateLocation($ref, 'localhost')); 193 $this->assertEquals($ref, generateLocation($ref, 'localhost'));
193 $ref = 'http://localhost:8080/?test'; 194 $ref = 'http://localhost:8080/?test';
@@ -199,7 +200,8 @@ class UtilsTest extends PHPUnit_Framework_TestCase
199 /** 200 /**
200 * Test generate location - anti loop. 201 * Test generate location - anti loop.
201 */ 202 */
202 public function testGenerateLocationLoop() { 203 public function testGenerateLocationLoop()
204 {
203 $ref = 'http://localhost/?test'; 205 $ref = 'http://localhost/?test';
204 $this->assertEquals('?', generateLocation($ref, 'localhost', array('test'))); 206 $this->assertEquals('?', generateLocation($ref, 'localhost', array('test')));
205 } 207 }
@@ -207,7 +209,8 @@ class UtilsTest extends PHPUnit_Framework_TestCase
207 /** 209 /**
208 * Test generate location - from other domain. 210 * Test generate location - from other domain.
209 */ 211 */
210 public function testGenerateLocationOut() { 212 public function testGenerateLocationOut()
213 {
211 $ref = 'http://somewebsite.com/?test'; 214 $ref = 'http://somewebsite.com/?test';
212 $this->assertEquals('?', generateLocation($ref, 'localhost')); 215 $this->assertEquals('?', generateLocation($ref, 'localhost'));
213 } 216 }
diff --git a/tests/api/ApiMiddlewareTest.php b/tests/api/ApiMiddlewareTest.php
index 23a56b1c..0b9b03f2 100644
--- a/tests/api/ApiMiddlewareTest.php
+++ b/tests/api/ApiMiddlewareTest.php
@@ -2,7 +2,6 @@
2namespace Shaarli\Api; 2namespace Shaarli\Api;
3 3
4use Shaarli\Config\ConfigManager; 4use Shaarli\Config\ConfigManager;
5
6use Slim\Container; 5use Slim\Container;
7use Slim\Http\Environment; 6use Slim\Http\Environment;
8use Slim\Http\Request; 7use Slim\Http\Request;
@@ -18,7 +17,7 @@ use Slim\Http\Response;
18 * 17 *
19 * @package Api 18 * @package Api
20 */ 19 */
21class ApiMiddlewareTest extends \PHPUnit_Framework_TestCase 20class ApiMiddlewareTest extends \PHPUnit\Framework\TestCase
22{ 21{
23 /** 22 /**
24 * @var string datastore to test write operations 23 * @var string datastore to test write operations
diff --git a/tests/api/ApiUtilsTest.php b/tests/api/ApiUtilsTest.php
index 62baf4c5..ea0ae500 100644
--- a/tests/api/ApiUtilsTest.php
+++ b/tests/api/ApiUtilsTest.php
@@ -2,13 +2,12 @@
2 2
3namespace Shaarli\Api; 3namespace Shaarli\Api;
4 4
5use Shaarli\Base64Url; 5use Shaarli\Http\Base64Url;
6
7 6
8/** 7/**
9 * Class ApiUtilsTest 8 * Class ApiUtilsTest
10 */ 9 */
11class ApiUtilsTest extends \PHPUnit_Framework_TestCase 10class ApiUtilsTest extends \PHPUnit\Framework\TestCase
12{ 11{
13 /** 12 /**
14 * Force the timezone for ISO datetimes. 13 * Force the timezone for ISO datetimes.
@@ -34,7 +33,7 @@ class ApiUtilsTest extends \PHPUnit_Framework_TestCase
34 $payload = Base64Url::encode('{ 33 $payload = Base64Url::encode('{
35 "iat": '. time() .' 34 "iat": '. time() .'
36 }'); 35 }');
37 $signature = Base64Url::encode(hash_hmac('sha512', $header .'.'. $payload , $secret, true)); 36 $signature = Base64Url::encode(hash_hmac('sha512', $header .'.'. $payload, $secret, true));
38 return $header .'.'. $payload .'.'. $signature; 37 return $header .'.'. $payload .'.'. $signature;
39 } 38 }
40 39
diff --git a/tests/api/controllers/history/HistoryTest.php b/tests/api/controllers/history/HistoryTest.php
index 61046d97..e287f239 100644
--- a/tests/api/controllers/history/HistoryTest.php
+++ b/tests/api/controllers/history/HistoryTest.php
@@ -1,10 +1,9 @@
1<?php 1<?php
2 2
3
4namespace Shaarli\Api\Controllers; 3namespace Shaarli\Api\Controllers;
5 4
6
7use Shaarli\Config\ConfigManager; 5use Shaarli\Config\ConfigManager;
6use Shaarli\History;
8use Slim\Container; 7use Slim\Container;
9use Slim\Http\Environment; 8use Slim\Http\Environment;
10use Slim\Http\Request; 9use Slim\Http\Request;
@@ -12,7 +11,7 @@ use Slim\Http\Response;
12 11
13require_once 'tests/utils/ReferenceHistory.php'; 12require_once 'tests/utils/ReferenceHistory.php';
14 13
15class HistoryTest extends \PHPUnit_Framework_TestCase 14class HistoryTest extends \PHPUnit\Framework\TestCase
16{ 15{
17 /** 16 /**
18 * @var string datastore to test write operations 17 * @var string datastore to test write operations
@@ -35,7 +34,7 @@ class HistoryTest extends \PHPUnit_Framework_TestCase
35 protected $container; 34 protected $container;
36 35
37 /** 36 /**
38 * @var History controller instance. 37 * @var HistoryController controller instance.
39 */ 38 */
40 protected $controller; 39 protected $controller;
41 40
@@ -50,9 +49,9 @@ class HistoryTest extends \PHPUnit_Framework_TestCase
50 $this->container = new Container(); 49 $this->container = new Container();
51 $this->container['conf'] = $this->conf; 50 $this->container['conf'] = $this->conf;
52 $this->container['db'] = true; 51 $this->container['db'] = true;
53 $this->container['history'] = new \History(self::$testHistory); 52 $this->container['history'] = new History(self::$testHistory);
54 53
55 $this->controller = new History($this->container); 54 $this->controller = new HistoryController($this->container);
56 } 55 }
57 56
58 /** 57 /**
@@ -79,35 +78,35 @@ class HistoryTest extends \PHPUnit_Framework_TestCase
79 78
80 $this->assertEquals($this->refHistory->count(), count($data)); 79 $this->assertEquals($this->refHistory->count(), count($data));
81 80
82 $this->assertEquals(\History::DELETED, $data[0]['event']); 81 $this->assertEquals(History::DELETED, $data[0]['event']);
83 $this->assertEquals( 82 $this->assertEquals(
84 \DateTime::createFromFormat('Ymd_His', '20170303_121216')->format(\DateTime::ATOM), 83 \DateTime::createFromFormat('Ymd_His', '20170303_121216')->format(\DateTime::ATOM),
85 $data[0]['datetime'] 84 $data[0]['datetime']
86 ); 85 );
87 $this->assertEquals(124, $data[0]['id']); 86 $this->assertEquals(124, $data[0]['id']);
88 87
89 $this->assertEquals(\History::SETTINGS, $data[1]['event']); 88 $this->assertEquals(History::SETTINGS, $data[1]['event']);
90 $this->assertEquals( 89 $this->assertEquals(
91 \DateTime::createFromFormat('Ymd_His', '20170302_121215')->format(\DateTime::ATOM), 90 \DateTime::createFromFormat('Ymd_His', '20170302_121215')->format(\DateTime::ATOM),
92 $data[1]['datetime'] 91 $data[1]['datetime']
93 ); 92 );
94 $this->assertNull($data[1]['id']); 93 $this->assertNull($data[1]['id']);
95 94
96 $this->assertEquals(\History::UPDATED, $data[2]['event']); 95 $this->assertEquals(History::UPDATED, $data[2]['event']);
97 $this->assertEquals( 96 $this->assertEquals(
98 \DateTime::createFromFormat('Ymd_His', '20170301_121214')->format(\DateTime::ATOM), 97 \DateTime::createFromFormat('Ymd_His', '20170301_121214')->format(\DateTime::ATOM),
99 $data[2]['datetime'] 98 $data[2]['datetime']
100 ); 99 );
101 $this->assertEquals(123, $data[2]['id']); 100 $this->assertEquals(123, $data[2]['id']);
102 101
103 $this->assertEquals(\History::CREATED, $data[3]['event']); 102 $this->assertEquals(History::CREATED, $data[3]['event']);
104 $this->assertEquals( 103 $this->assertEquals(
105 \DateTime::createFromFormat('Ymd_His', '20170201_121214')->format(\DateTime::ATOM), 104 \DateTime::createFromFormat('Ymd_His', '20170201_121214')->format(\DateTime::ATOM),
106 $data[3]['datetime'] 105 $data[3]['datetime']
107 ); 106 );
108 $this->assertEquals(124, $data[3]['id']); 107 $this->assertEquals(124, $data[3]['id']);
109 108
110 $this->assertEquals(\History::CREATED, $data[4]['event']); 109 $this->assertEquals(History::CREATED, $data[4]['event']);
111 $this->assertEquals( 110 $this->assertEquals(
112 \DateTime::createFromFormat('Ymd_His', '20170101_121212')->format(\DateTime::ATOM), 111 \DateTime::createFromFormat('Ymd_His', '20170101_121212')->format(\DateTime::ATOM),
113 $data[4]['datetime'] 112 $data[4]['datetime']
@@ -132,7 +131,7 @@ class HistoryTest extends \PHPUnit_Framework_TestCase
132 131
133 $this->assertEquals(1, count($data)); 132 $this->assertEquals(1, count($data));
134 133
135 $this->assertEquals(\History::DELETED, $data[0]['event']); 134 $this->assertEquals(History::DELETED, $data[0]['event']);
136 $this->assertEquals( 135 $this->assertEquals(
137 \DateTime::createFromFormat('Ymd_His', '20170303_121216')->format(\DateTime::ATOM), 136 \DateTime::createFromFormat('Ymd_His', '20170303_121216')->format(\DateTime::ATOM),
138 $data[0]['datetime'] 137 $data[0]['datetime']
@@ -157,7 +156,7 @@ class HistoryTest extends \PHPUnit_Framework_TestCase
157 156
158 $this->assertEquals(1, count($data)); 157 $this->assertEquals(1, count($data));
159 158
160 $this->assertEquals(\History::CREATED, $data[0]['event']); 159 $this->assertEquals(History::CREATED, $data[0]['event']);
161 $this->assertEquals( 160 $this->assertEquals(
162 \DateTime::createFromFormat('Ymd_His', '20170101_121212')->format(\DateTime::ATOM), 161 \DateTime::createFromFormat('Ymd_His', '20170101_121212')->format(\DateTime::ATOM),
163 $data[0]['datetime'] 162 $data[0]['datetime']
@@ -182,7 +181,7 @@ class HistoryTest extends \PHPUnit_Framework_TestCase
182 181
183 $this->assertEquals(1, count($data)); 182 $this->assertEquals(1, count($data));
184 183
185 $this->assertEquals(\History::DELETED, $data[0]['event']); 184 $this->assertEquals(History::DELETED, $data[0]['event']);
186 $this->assertEquals( 185 $this->assertEquals(
187 \DateTime::createFromFormat('Ymd_His', '20170303_121216')->format(\DateTime::ATOM), 186 \DateTime::createFromFormat('Ymd_His', '20170303_121216')->format(\DateTime::ATOM),
188 $data[0]['datetime'] 187 $data[0]['datetime']
@@ -207,7 +206,7 @@ class HistoryTest extends \PHPUnit_Framework_TestCase
207 206
208 $this->assertEquals(1, count($data)); 207 $this->assertEquals(1, count($data));
209 208
210 $this->assertEquals(\History::SETTINGS, $data[0]['event']); 209 $this->assertEquals(History::SETTINGS, $data[0]['event']);
211 $this->assertEquals( 210 $this->assertEquals(
212 \DateTime::createFromFormat('Ymd_His', '20170302_121215')->format(\DateTime::ATOM), 211 \DateTime::createFromFormat('Ymd_His', '20170302_121215')->format(\DateTime::ATOM),
213 $data[0]['datetime'] 212 $data[0]['datetime']
diff --git a/tests/api/controllers/info/InfoTest.php b/tests/api/controllers/info/InfoTest.php
index f7e63bfa..e70d371b 100644
--- a/tests/api/controllers/info/InfoTest.php
+++ b/tests/api/controllers/info/InfoTest.php
@@ -2,7 +2,6 @@
2namespace Shaarli\Api\Controllers; 2namespace Shaarli\Api\Controllers;
3 3
4use Shaarli\Config\ConfigManager; 4use Shaarli\Config\ConfigManager;
5
6use Slim\Container; 5use Slim\Container;
7use Slim\Http\Environment; 6use Slim\Http\Environment;
8use Slim\Http\Request; 7use Slim\Http\Request;
@@ -10,12 +9,12 @@ use Slim\Http\Response;
10 9
11/** 10/**
12 * Class InfoTest 11 * Class InfoTest
13 * 12 *
14 * Test REST API controller Info. 13 * Test REST API controller Info.
15 * 14 *
16 * @package Api\Controllers 15 * @package Api\Controllers
17 */ 16 */
18class InfoTest extends \PHPUnit_Framework_TestCase 17class InfoTest extends \PHPUnit\Framework\TestCase
19{ 18{
20 /** 19 /**
21 * @var string datastore to test write operations 20 * @var string datastore to test write operations
@@ -53,7 +52,7 @@ class InfoTest extends \PHPUnit_Framework_TestCase
53 52
54 $this->container = new Container(); 53 $this->container = new Container();
55 $this->container['conf'] = $this->conf; 54 $this->container['conf'] = $this->conf;
56 $this->container['db'] = new \LinkDB(self::$testDatastore, true, false); 55 $this->container['db'] = new \Shaarli\Bookmark\LinkDB(self::$testDatastore, true, false);
57 $this->container['history'] = null; 56 $this->container['history'] = null;
58 57
59 $this->controller = new Info($this->container); 58 $this->controller = new Info($this->container);
diff --git a/tests/api/controllers/links/DeleteLinkTest.php b/tests/api/controllers/links/DeleteLinkTest.php
index 7d797137..90193e28 100644
--- a/tests/api/controllers/links/DeleteLinkTest.php
+++ b/tests/api/controllers/links/DeleteLinkTest.php
@@ -3,13 +3,15 @@
3 3
4namespace Shaarli\Api\Controllers; 4namespace Shaarli\Api\Controllers;
5 5
6use Shaarli\Bookmark\LinkDB;
6use Shaarli\Config\ConfigManager; 7use Shaarli\Config\ConfigManager;
8use Shaarli\History;
7use Slim\Container; 9use Slim\Container;
8use Slim\Http\Environment; 10use Slim\Http\Environment;
9use Slim\Http\Request; 11use Slim\Http\Request;
10use Slim\Http\Response; 12use Slim\Http\Response;
11 13
12class DeleteLinkTest extends \PHPUnit_Framework_TestCase 14class DeleteLinkTest extends \PHPUnit\Framework\TestCase
13{ 15{
14 /** 16 /**
15 * @var string datastore to test write operations 17 * @var string datastore to test write operations
@@ -32,12 +34,12 @@ class DeleteLinkTest extends \PHPUnit_Framework_TestCase
32 protected $refDB = null; 34 protected $refDB = null;
33 35
34 /** 36 /**
35 * @var \LinkDB instance. 37 * @var LinkDB instance.
36 */ 38 */
37 protected $linkDB; 39 protected $linkDB;
38 40
39 /** 41 /**
40 * @var \History instance. 42 * @var HistoryController instance.
41 */ 43 */
42 protected $history; 44 protected $history;
43 45
@@ -59,10 +61,10 @@ class DeleteLinkTest extends \PHPUnit_Framework_TestCase
59 $this->conf = new ConfigManager('tests/utils/config/configJson'); 61 $this->conf = new ConfigManager('tests/utils/config/configJson');
60 $this->refDB = new \ReferenceLinkDB(); 62 $this->refDB = new \ReferenceLinkDB();
61 $this->refDB->write(self::$testDatastore); 63 $this->refDB->write(self::$testDatastore);
62 $this->linkDB = new \LinkDB(self::$testDatastore, true, false); 64 $this->linkDB = new LinkDB(self::$testDatastore, true, false);
63 $refHistory = new \ReferenceHistory(); 65 $refHistory = new \ReferenceHistory();
64 $refHistory->write(self::$testHistory); 66 $refHistory->write(self::$testHistory);
65 $this->history = new \History(self::$testHistory); 67 $this->history = new History(self::$testHistory);
66 $this->container = new Container(); 68 $this->container = new Container();
67 $this->container['conf'] = $this->conf; 69 $this->container['conf'] = $this->conf;
68 $this->container['db'] = $this->linkDB; 70 $this->container['db'] = $this->linkDB;
@@ -96,11 +98,11 @@ class DeleteLinkTest extends \PHPUnit_Framework_TestCase
96 $this->assertEquals(204, $response->getStatusCode()); 98 $this->assertEquals(204, $response->getStatusCode());
97 $this->assertEmpty((string) $response->getBody()); 99 $this->assertEmpty((string) $response->getBody());
98 100
99 $this->linkDB = new \LinkDB(self::$testDatastore, true, false); 101 $this->linkDB = new LinkDB(self::$testDatastore, true, false);
100 $this->assertFalse(isset($this->linkDB[$id])); 102 $this->assertFalse(isset($this->linkDB[$id]));
101 103
102 $historyEntry = $this->history->getHistory()[0]; 104 $historyEntry = $this->history->getHistory()[0];
103 $this->assertEquals(\History::DELETED, $historyEntry['event']); 105 $this->assertEquals(History::DELETED, $historyEntry['event']);
104 $this->assertTrue( 106 $this->assertTrue(
105 (new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime'] 107 (new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime']
106 ); 108 );
@@ -110,7 +112,7 @@ class DeleteLinkTest extends \PHPUnit_Framework_TestCase
110 /** 112 /**
111 * Test DELETE link endpoint: reach not existing ID. 113 * Test DELETE link endpoint: reach not existing ID.
112 * 114 *
113 * @expectedException Shaarli\Api\Exceptions\ApiLinkNotFoundException 115 * @expectedException \Shaarli\Api\Exceptions\ApiLinkNotFoundException
114 */ 116 */
115 public function testDeleteLink404() 117 public function testDeleteLink404()
116 { 118 {
diff --git a/tests/api/controllers/links/GetLinkIdTest.php b/tests/api/controllers/links/GetLinkIdTest.php
index 57528d5a..cb9b7f6a 100644
--- a/tests/api/controllers/links/GetLinkIdTest.php
+++ b/tests/api/controllers/links/GetLinkIdTest.php
@@ -3,7 +3,6 @@
3namespace Shaarli\Api\Controllers; 3namespace Shaarli\Api\Controllers;
4 4
5use Shaarli\Config\ConfigManager; 5use Shaarli\Config\ConfigManager;
6
7use Slim\Container; 6use Slim\Container;
8use Slim\Http\Environment; 7use Slim\Http\Environment;
9use Slim\Http\Request; 8use Slim\Http\Request;
@@ -18,7 +17,7 @@ use Slim\Http\Response;
18 * 17 *
19 * @package Shaarli\Api\Controllers 18 * @package Shaarli\Api\Controllers
20 */ 19 */
21class GetLinkIdTest extends \PHPUnit_Framework_TestCase 20class GetLinkIdTest extends \PHPUnit\Framework\TestCase
22{ 21{
23 /** 22 /**
24 * @var string datastore to test write operations 23 * @var string datastore to test write operations
@@ -61,7 +60,7 @@ class GetLinkIdTest extends \PHPUnit_Framework_TestCase
61 60
62 $this->container = new Container(); 61 $this->container = new Container();
63 $this->container['conf'] = $this->conf; 62 $this->container['conf'] = $this->conf;
64 $this->container['db'] = new \LinkDB(self::$testDatastore, true, false); 63 $this->container['db'] = new \Shaarli\Bookmark\LinkDB(self::$testDatastore, true, false);
65 $this->container['history'] = null; 64 $this->container['history'] = null;
66 65
67 $this->controller = new Links($this->container); 66 $this->controller = new Links($this->container);
@@ -108,7 +107,7 @@ class GetLinkIdTest extends \PHPUnit_Framework_TestCase
108 $this->assertEquals('sTuff', $data['tags'][0]); 107 $this->assertEquals('sTuff', $data['tags'][0]);
109 $this->assertEquals(false, $data['private']); 108 $this->assertEquals(false, $data['private']);
110 $this->assertEquals( 109 $this->assertEquals(
111 \DateTime::createFromFormat(\LinkDB::LINK_DATE_FORMAT, '20150310_114651')->format(\DateTime::ATOM), 110 \DateTime::createFromFormat(\Shaarli\Bookmark\LinkDB::LINK_DATE_FORMAT, '20150310_114651')->format(\DateTime::ATOM),
112 $data['created'] 111 $data['created']
113 ); 112 );
114 $this->assertEmpty($data['updated']); 113 $this->assertEmpty($data['updated']);
diff --git a/tests/api/controllers/links/GetLinksTest.php b/tests/api/controllers/links/GetLinksTest.php
index d22ed3bf..711a3152 100644
--- a/tests/api/controllers/links/GetLinksTest.php
+++ b/tests/api/controllers/links/GetLinksTest.php
@@ -1,8 +1,8 @@
1<?php 1<?php
2namespace Shaarli\Api\Controllers; 2namespace Shaarli\Api\Controllers;
3 3
4use Shaarli\Bookmark\LinkDB;
4use Shaarli\Config\ConfigManager; 5use Shaarli\Config\ConfigManager;
5
6use Slim\Container; 6use Slim\Container;
7use Slim\Http\Environment; 7use Slim\Http\Environment;
8use Slim\Http\Request; 8use Slim\Http\Request;
@@ -17,7 +17,7 @@ use Slim\Http\Response;
17 * 17 *
18 * @package Shaarli\Api\Controllers 18 * @package Shaarli\Api\Controllers
19 */ 19 */
20class GetLinksTest extends \PHPUnit_Framework_TestCase 20class GetLinksTest extends \PHPUnit\Framework\TestCase
21{ 21{
22 /** 22 /**
23 * @var string datastore to test write operations 23 * @var string datastore to test write operations
@@ -60,7 +60,7 @@ class GetLinksTest extends \PHPUnit_Framework_TestCase
60 60
61 $this->container = new Container(); 61 $this->container = new Container();
62 $this->container['conf'] = $this->conf; 62 $this->container['conf'] = $this->conf;
63 $this->container['db'] = new \LinkDB(self::$testDatastore, true, false); 63 $this->container['db'] = new LinkDB(self::$testDatastore, true, false);
64 $this->container['history'] = null; 64 $this->container['history'] = null;
65 65
66 $this->controller = new Links($this->container); 66 $this->controller = new Links($this->container);
@@ -95,7 +95,7 @@ class GetLinksTest extends \PHPUnit_Framework_TestCase
95 $this->assertEquals($this->refDB->countLinks(), count($data)); 95 $this->assertEquals($this->refDB->countLinks(), count($data));
96 96
97 // Check order 97 // Check order
98 $order = [41, 8, 6, 7, 0, 1, 9, 4, 42]; 98 $order = [10, 11, 41, 8, 6, 7, 0, 1, 9, 4, 42];
99 $cpt = 0; 99 $cpt = 0;
100 foreach ($data as $link) { 100 foreach ($data as $link) {
101 $this->assertEquals(self::NB_FIELDS_LINK, count($link)); 101 $this->assertEquals(self::NB_FIELDS_LINK, count($link));
@@ -103,7 +103,7 @@ class GetLinksTest extends \PHPUnit_Framework_TestCase
103 } 103 }
104 104
105 // Check first element fields 105 // Check first element fields
106 $first = $data[0]; 106 $first = $data[2];
107 $this->assertEquals('http://domain.tld/?WDWyig', $first['url']); 107 $this->assertEquals('http://domain.tld/?WDWyig', $first['url']);
108 $this->assertEquals('WDWyig', $first['shorturl']); 108 $this->assertEquals('WDWyig', $first['shorturl']);
109 $this->assertEquals('Link title: @website', $first['title']); 109 $this->assertEquals('Link title: @website', $first['title']);
@@ -114,18 +114,18 @@ class GetLinksTest extends \PHPUnit_Framework_TestCase
114 $this->assertEquals('sTuff', $first['tags'][0]); 114 $this->assertEquals('sTuff', $first['tags'][0]);
115 $this->assertEquals(false, $first['private']); 115 $this->assertEquals(false, $first['private']);
116 $this->assertEquals( 116 $this->assertEquals(
117 \DateTime::createFromFormat(\LinkDB::LINK_DATE_FORMAT, '20150310_114651')->format(\DateTime::ATOM), 117 \DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114651')->format(\DateTime::ATOM),
118 $first['created'] 118 $first['created']
119 ); 119 );
120 $this->assertEmpty($first['updated']); 120 $this->assertEmpty($first['updated']);
121 121
122 // Multi tags 122 // Multi tags
123 $link = $data[1]; 123 $link = $data[3];
124 $this->assertEquals(7, count($link['tags'])); 124 $this->assertEquals(7, count($link['tags']));
125 125
126 // Update date 126 // Update date
127 $this->assertEquals( 127 $this->assertEquals(
128 \DateTime::createFromFormat(\LinkDB::LINK_DATE_FORMAT, '20160803_093033')->format(\DateTime::ATOM), 128 \DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160803_093033')->format(\DateTime::ATOM),
129 $link['updated'] 129 $link['updated']
130 ); 130 );
131 } 131 }
@@ -138,7 +138,7 @@ class GetLinksTest extends \PHPUnit_Framework_TestCase
138 { 138 {
139 $env = Environment::mock([ 139 $env = Environment::mock([
140 'REQUEST_METHOD' => 'GET', 140 'REQUEST_METHOD' => 'GET',
141 'QUERY_STRING' => 'offset=1&limit=1' 141 'QUERY_STRING' => 'offset=3&limit=1'
142 ]); 142 ]);
143 $request = Request::createFromEnvironment($env); 143 $request = Request::createFromEnvironment($env);
144 $response = $this->controller->getLinks($request, new Response()); 144 $response = $this->controller->getLinks($request, new Response());
@@ -164,7 +164,7 @@ class GetLinksTest extends \PHPUnit_Framework_TestCase
164 $data = json_decode((string) $response->getBody(), true); 164 $data = json_decode((string) $response->getBody(), true);
165 $this->assertEquals($this->refDB->countLinks(), count($data)); 165 $this->assertEquals($this->refDB->countLinks(), count($data));
166 // Check order 166 // Check order
167 $order = [41, 8, 6, 7, 0, 1, 9, 4, 42]; 167 $order = [10, 11, 41, 8, 6, 7, 0, 1, 9, 4, 42];
168 $cpt = 0; 168 $cpt = 0;
169 foreach ($data as $link) { 169 foreach ($data as $link) {
170 $this->assertEquals(self::NB_FIELDS_LINK, count($link)); 170 $this->assertEquals(self::NB_FIELDS_LINK, count($link));
@@ -205,7 +205,8 @@ class GetLinksTest extends \PHPUnit_Framework_TestCase
205 $this->assertEquals(200, $response->getStatusCode()); 205 $this->assertEquals(200, $response->getStatusCode());
206 $data = json_decode((string)$response->getBody(), true); 206 $data = json_decode((string)$response->getBody(), true);
207 $this->assertEquals($this->refDB->countLinks(), count($data)); 207 $this->assertEquals($this->refDB->countLinks(), count($data));
208 $this->assertEquals(41, $data[0]['id']); 208 $this->assertEquals(10, $data[0]['id']);
209 $this->assertEquals(41, $data[2]['id']);
209 $this->assertEquals(self::NB_FIELDS_LINK, count($data[0])); 210 $this->assertEquals(self::NB_FIELDS_LINK, count($data[0]));
210 } 211 }
211 212
@@ -243,7 +244,8 @@ class GetLinksTest extends \PHPUnit_Framework_TestCase
243 $this->assertEquals(200, $response->getStatusCode()); 244 $this->assertEquals(200, $response->getStatusCode());
244 $data = json_decode((string)$response->getBody(), true); 245 $data = json_decode((string)$response->getBody(), true);
245 $this->assertEquals($this->refDB->countPublicLinks(), count($data)); 246 $this->assertEquals($this->refDB->countPublicLinks(), count($data));
246 $this->assertEquals(41, $data[0]['id']); 247 $this->assertEquals(10, $data[0]['id']);
248 $this->assertEquals(41, $data[2]['id']);
247 $this->assertEquals(self::NB_FIELDS_LINK, count($data[0])); 249 $this->assertEquals(self::NB_FIELDS_LINK, count($data[0]));
248 } 250 }
249 251
@@ -413,8 +415,9 @@ class GetLinksTest extends \PHPUnit_Framework_TestCase
413 $response = $this->controller->getLinks($request, new Response()); 415 $response = $this->controller->getLinks($request, new Response());
414 $this->assertEquals(200, $response->getStatusCode()); 416 $this->assertEquals(200, $response->getStatusCode());
415 $data = json_decode((string) $response->getBody(), true); 417 $data = json_decode((string) $response->getBody(), true);
416 $this->assertEquals(9, count($data)); 418 $this->assertEquals(\ReferenceLinkDB::$NB_LINKS_TOTAL, count($data));
417 $this->assertEquals(41, $data[0]['id']); 419 $this->assertEquals(10, $data[0]['id']);
420 $this->assertEquals(41, $data[2]['id']);
418 421
419 // wildcard: optional ('*' does not need to expand) 422 // wildcard: optional ('*' does not need to expand)
420 $env = Environment::mock([ 423 $env = Environment::mock([
diff --git a/tests/api/controllers/links/PostLinkTest.php b/tests/api/controllers/links/PostLinkTest.php
index 100a9170..d683a984 100644
--- a/tests/api/controllers/links/PostLinkTest.php
+++ b/tests/api/controllers/links/PostLinkTest.php
@@ -2,9 +2,9 @@
2 2
3namespace Shaarli\Api\Controllers; 3namespace Shaarli\Api\Controllers;
4 4
5
6use PHPUnit\Framework\TestCase; 5use PHPUnit\Framework\TestCase;
7use Shaarli\Config\ConfigManager; 6use Shaarli\Config\ConfigManager;
7use Shaarli\History;
8use Slim\Container; 8use Slim\Container;
9use Slim\Http\Environment; 9use Slim\Http\Environment;
10use Slim\Http\Request; 10use Slim\Http\Request;
@@ -41,7 +41,7 @@ class PostLinkTest extends TestCase
41 protected $refDB = null; 41 protected $refDB = null;
42 42
43 /** 43 /**
44 * @var \History instance. 44 * @var HistoryController instance.
45 */ 45 */
46 protected $history; 46 protected $history;
47 47
@@ -71,12 +71,12 @@ class PostLinkTest extends TestCase
71 71
72 $refHistory = new \ReferenceHistory(); 72 $refHistory = new \ReferenceHistory();
73 $refHistory->write(self::$testHistory); 73 $refHistory->write(self::$testHistory);
74 $this->history = new \History(self::$testHistory); 74 $this->history = new History(self::$testHistory);
75 75
76 $this->container = new Container(); 76 $this->container = new Container();
77 $this->container['conf'] = $this->conf; 77 $this->container['conf'] = $this->conf;
78 $this->container['db'] = new \LinkDB(self::$testDatastore, true, false); 78 $this->container['db'] = new \Shaarli\Bookmark\LinkDB(self::$testDatastore, true, false);
79 $this->container['history'] = new \History(self::$testHistory); 79 $this->container['history'] = new History(self::$testHistory);
80 80
81 $this->controller = new Links($this->container); 81 $this->controller = new Links($this->container);
82 82
@@ -122,17 +122,19 @@ class PostLinkTest extends TestCase
122 $data = json_decode((string) $response->getBody(), true); 122 $data = json_decode((string) $response->getBody(), true);
123 $this->assertEquals(self::NB_FIELDS_LINK, count($data)); 123 $this->assertEquals(self::NB_FIELDS_LINK, count($data));
124 $this->assertEquals(43, $data['id']); 124 $this->assertEquals(43, $data['id']);
125 $this->assertRegExp('/[\w-_]{6}/', $data['shorturl']); 125 $this->assertRegExp('/[\w_-]{6}/', $data['shorturl']);
126 $this->assertEquals('http://domain.tld/?' . $data['shorturl'], $data['url']); 126 $this->assertEquals('http://domain.tld/?' . $data['shorturl'], $data['url']);
127 $this->assertEquals('?' . $data['shorturl'], $data['title']); 127 $this->assertEquals('?' . $data['shorturl'], $data['title']);
128 $this->assertEquals('', $data['description']); 128 $this->assertEquals('', $data['description']);
129 $this->assertEquals([], $data['tags']); 129 $this->assertEquals([], $data['tags']);
130 $this->assertEquals(false, $data['private']); 130 $this->assertEquals(false, $data['private']);
131 $this->assertTrue(new \DateTime('5 seconds ago') < \DateTime::createFromFormat(\DateTime::ATOM, $data['created'])); 131 $this->assertTrue(
132 new \DateTime('5 seconds ago') < \DateTime::createFromFormat(\DateTime::ATOM, $data['created'])
133 );
132 $this->assertEquals('', $data['updated']); 134 $this->assertEquals('', $data['updated']);
133 135
134 $historyEntry = $this->history->getHistory()[0]; 136 $historyEntry = $this->history->getHistory()[0];
135 $this->assertEquals(\History::CREATED, $historyEntry['event']); 137 $this->assertEquals(History::CREATED, $historyEntry['event']);
136 $this->assertTrue( 138 $this->assertTrue(
137 (new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime'] 139 (new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime']
138 ); 140 );
@@ -165,13 +167,15 @@ class PostLinkTest extends TestCase
165 $data = json_decode((string) $response->getBody(), true); 167 $data = json_decode((string) $response->getBody(), true);
166 $this->assertEquals(self::NB_FIELDS_LINK, count($data)); 168 $this->assertEquals(self::NB_FIELDS_LINK, count($data));
167 $this->assertEquals(43, $data['id']); 169 $this->assertEquals(43, $data['id']);
168 $this->assertRegExp('/[\w-_]{6}/', $data['shorturl']); 170 $this->assertRegExp('/[\w_-]{6}/', $data['shorturl']);
169 $this->assertEquals('http://' . $link['url'], $data['url']); 171 $this->assertEquals('http://' . $link['url'], $data['url']);
170 $this->assertEquals($link['title'], $data['title']); 172 $this->assertEquals($link['title'], $data['title']);
171 $this->assertEquals($link['description'], $data['description']); 173 $this->assertEquals($link['description'], $data['description']);
172 $this->assertEquals($link['tags'], $data['tags']); 174 $this->assertEquals($link['tags'], $data['tags']);
173 $this->assertEquals(true, $data['private']); 175 $this->assertEquals(true, $data['private']);
174 $this->assertTrue(new \DateTime('2 seconds ago') < \DateTime::createFromFormat(\DateTime::ATOM, $data['created'])); 176 $this->assertTrue(
177 new \DateTime('2 seconds ago') < \DateTime::createFromFormat(\DateTime::ATOM, $data['created'])
178 );
175 $this->assertEquals('', $data['updated']); 179 $this->assertEquals('', $data['updated']);
176 } 180 }
177 181
@@ -207,11 +211,11 @@ class PostLinkTest extends TestCase
207 $this->assertEquals(['gnu', 'media', 'web', '.hidden', 'hashtag'], $data['tags']); 211 $this->assertEquals(['gnu', 'media', 'web', '.hidden', 'hashtag'], $data['tags']);
208 $this->assertEquals(false, $data['private']); 212 $this->assertEquals(false, $data['private']);
209 $this->assertEquals( 213 $this->assertEquals(
210 \DateTime::createFromFormat(\LinkDB::LINK_DATE_FORMAT, '20130614_184135'), 214 \DateTime::createFromFormat(\Shaarli\Bookmark\LinkDB::LINK_DATE_FORMAT, '20130614_184135'),
211 \DateTime::createFromFormat(\DateTime::ATOM, $data['created']) 215 \DateTime::createFromFormat(\DateTime::ATOM, $data['created'])
212 ); 216 );
213 $this->assertEquals( 217 $this->assertEquals(
214 \DateTime::createFromFormat(\LinkDB::LINK_DATE_FORMAT, '20130615_184230'), 218 \DateTime::createFromFormat(\Shaarli\Bookmark\LinkDB::LINK_DATE_FORMAT, '20130615_184230'),
215 \DateTime::createFromFormat(\DateTime::ATOM, $data['updated']) 219 \DateTime::createFromFormat(\DateTime::ATOM, $data['updated'])
216 ); 220 );
217 } 221 }
diff --git a/tests/api/controllers/links/PutLinkTest.php b/tests/api/controllers/links/PutLinkTest.php
index 8a562571..cd815b66 100644
--- a/tests/api/controllers/links/PutLinkTest.php
+++ b/tests/api/controllers/links/PutLinkTest.php
@@ -3,14 +3,14 @@
3 3
4namespace Shaarli\Api\Controllers; 4namespace Shaarli\Api\Controllers;
5 5
6
7use Shaarli\Config\ConfigManager; 6use Shaarli\Config\ConfigManager;
7use Shaarli\History;
8use Slim\Container; 8use Slim\Container;
9use Slim\Http\Environment; 9use Slim\Http\Environment;
10use Slim\Http\Request; 10use Slim\Http\Request;
11use Slim\Http\Response; 11use Slim\Http\Response;
12 12
13class PutLinkTest extends \PHPUnit_Framework_TestCase 13class PutLinkTest extends \PHPUnit\Framework\TestCase
14{ 14{
15 /** 15 /**
16 * @var string datastore to test write operations 16 * @var string datastore to test write operations
@@ -33,7 +33,7 @@ class PutLinkTest extends \PHPUnit_Framework_TestCase
33 protected $refDB = null; 33 protected $refDB = null;
34 34
35 /** 35 /**
36 * @var \History instance. 36 * @var HistoryController instance.
37 */ 37 */
38 protected $history; 38 protected $history;
39 39
@@ -63,12 +63,12 @@ class PutLinkTest extends \PHPUnit_Framework_TestCase
63 63
64 $refHistory = new \ReferenceHistory(); 64 $refHistory = new \ReferenceHistory();
65 $refHistory->write(self::$testHistory); 65 $refHistory->write(self::$testHistory);
66 $this->history = new \History(self::$testHistory); 66 $this->history = new History(self::$testHistory);
67 67
68 $this->container = new Container(); 68 $this->container = new Container();
69 $this->container['conf'] = $this->conf; 69 $this->container['conf'] = $this->conf;
70 $this->container['db'] = new \LinkDB(self::$testDatastore, true, false); 70 $this->container['db'] = new \Shaarli\Bookmark\LinkDB(self::$testDatastore, true, false);
71 $this->container['history'] = new \History(self::$testHistory); 71 $this->container['history'] = new History(self::$testHistory);
72 72
73 $this->controller = new Links($this->container); 73 $this->controller = new Links($this->container);
74 74
@@ -115,10 +115,12 @@ class PutLinkTest extends \PHPUnit_Framework_TestCase
115 \DateTime::createFromFormat('Ymd_His', '20150310_114651'), 115 \DateTime::createFromFormat('Ymd_His', '20150310_114651'),
116 \DateTime::createFromFormat(\DateTime::ATOM, $data['created']) 116 \DateTime::createFromFormat(\DateTime::ATOM, $data['created'])
117 ); 117 );
118 $this->assertTrue(new \DateTime('5 seconds ago') < \DateTime::createFromFormat(\DateTime::ATOM, $data['updated'])); 118 $this->assertTrue(
119 new \DateTime('5 seconds ago') < \DateTime::createFromFormat(\DateTime::ATOM, $data['updated'])
120 );
119 121
120 $historyEntry = $this->history->getHistory()[0]; 122 $historyEntry = $this->history->getHistory()[0];
121 $this->assertEquals(\History::UPDATED, $historyEntry['event']); 123 $this->assertEquals(History::UPDATED, $historyEntry['event']);
122 $this->assertTrue( 124 $this->assertTrue(
123 (new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime'] 125 (new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime']
124 ); 126 );
@@ -160,7 +162,9 @@ class PutLinkTest extends \PHPUnit_Framework_TestCase
160 \DateTime::createFromFormat('Ymd_His', '20150310_114651'), 162 \DateTime::createFromFormat('Ymd_His', '20150310_114651'),
161 \DateTime::createFromFormat(\DateTime::ATOM, $data['created']) 163 \DateTime::createFromFormat(\DateTime::ATOM, $data['created'])
162 ); 164 );
163 $this->assertTrue(new \DateTime('5 seconds ago') < \DateTime::createFromFormat(\DateTime::ATOM, $data['updated'])); 165 $this->assertTrue(
166 new \DateTime('5 seconds ago') < \DateTime::createFromFormat(\DateTime::ATOM, $data['updated'])
167 );
164 } 168 }
165 169
166 /** 170 /**
@@ -195,11 +199,11 @@ class PutLinkTest extends \PHPUnit_Framework_TestCase
195 $this->assertEquals(['gnu', 'media', 'web', '.hidden', 'hashtag'], $data['tags']); 199 $this->assertEquals(['gnu', 'media', 'web', '.hidden', 'hashtag'], $data['tags']);
196 $this->assertEquals(false, $data['private']); 200 $this->assertEquals(false, $data['private']);
197 $this->assertEquals( 201 $this->assertEquals(
198 \DateTime::createFromFormat(\LinkDB::LINK_DATE_FORMAT, '20130614_184135'), 202 \DateTime::createFromFormat(\Shaarli\Bookmark\LinkDB::LINK_DATE_FORMAT, '20130614_184135'),
199 \DateTime::createFromFormat(\DateTime::ATOM, $data['created']) 203 \DateTime::createFromFormat(\DateTime::ATOM, $data['created'])
200 ); 204 );
201 $this->assertEquals( 205 $this->assertEquals(
202 \DateTime::createFromFormat(\LinkDB::LINK_DATE_FORMAT, '20130615_184230'), 206 \DateTime::createFromFormat(\Shaarli\Bookmark\LinkDB::LINK_DATE_FORMAT, '20130615_184230'),
203 \DateTime::createFromFormat(\DateTime::ATOM, $data['updated']) 207 \DateTime::createFromFormat(\DateTime::ATOM, $data['updated'])
204 ); 208 );
205 } 209 }
diff --git a/tests/api/controllers/tags/DeleteTagTest.php b/tests/api/controllers/tags/DeleteTagTest.php
index e0787ce2..84e1d56e 100644
--- a/tests/api/controllers/tags/DeleteTagTest.php
+++ b/tests/api/controllers/tags/DeleteTagTest.php
@@ -3,13 +3,15 @@
3 3
4namespace Shaarli\Api\Controllers; 4namespace Shaarli\Api\Controllers;
5 5
6use Shaarli\Bookmark\LinkDB;
6use Shaarli\Config\ConfigManager; 7use Shaarli\Config\ConfigManager;
8use Shaarli\History;
7use Slim\Container; 9use Slim\Container;
8use Slim\Http\Environment; 10use Slim\Http\Environment;
9use Slim\Http\Request; 11use Slim\Http\Request;
10use Slim\Http\Response; 12use Slim\Http\Response;
11 13
12class DeleteTagTest extends \PHPUnit_Framework_TestCase 14class DeleteTagTest extends \PHPUnit\Framework\TestCase
13{ 15{
14 /** 16 /**
15 * @var string datastore to test write operations 17 * @var string datastore to test write operations
@@ -32,12 +34,12 @@ class DeleteTagTest extends \PHPUnit_Framework_TestCase
32 protected $refDB = null; 34 protected $refDB = null;
33 35
34 /** 36 /**
35 * @var \LinkDB instance. 37 * @var LinkDB instance.
36 */ 38 */
37 protected $linkDB; 39 protected $linkDB;
38 40
39 /** 41 /**
40 * @var \History instance. 42 * @var HistoryController instance.
41 */ 43 */
42 protected $history; 44 protected $history;
43 45
@@ -59,10 +61,10 @@ class DeleteTagTest extends \PHPUnit_Framework_TestCase
59 $this->conf = new ConfigManager('tests/utils/config/configJson'); 61 $this->conf = new ConfigManager('tests/utils/config/configJson');
60 $this->refDB = new \ReferenceLinkDB(); 62 $this->refDB = new \ReferenceLinkDB();
61 $this->refDB->write(self::$testDatastore); 63 $this->refDB->write(self::$testDatastore);
62 $this->linkDB = new \LinkDB(self::$testDatastore, true, false); 64 $this->linkDB = new LinkDB(self::$testDatastore, true, false);
63 $refHistory = new \ReferenceHistory(); 65 $refHistory = new \ReferenceHistory();
64 $refHistory->write(self::$testHistory); 66 $refHistory->write(self::$testHistory);
65 $this->history = new \History(self::$testHistory); 67 $this->history = new History(self::$testHistory);
66 $this->container = new Container(); 68 $this->container = new Container();
67 $this->container['conf'] = $this->conf; 69 $this->container['conf'] = $this->conf;
68 $this->container['db'] = $this->linkDB; 70 $this->container['db'] = $this->linkDB;
@@ -97,18 +99,18 @@ class DeleteTagTest extends \PHPUnit_Framework_TestCase
97 $this->assertEquals(204, $response->getStatusCode()); 99 $this->assertEquals(204, $response->getStatusCode());
98 $this->assertEmpty((string) $response->getBody()); 100 $this->assertEmpty((string) $response->getBody());
99 101
100 $this->linkDB = new \LinkDB(self::$testDatastore, true, false); 102 $this->linkDB = new LinkDB(self::$testDatastore, true, false);
101 $tags = $this->linkDB->linksCountPerTag(); 103 $tags = $this->linkDB->linksCountPerTag();
102 $this->assertFalse(isset($tags[$tagName])); 104 $this->assertFalse(isset($tags[$tagName]));
103 105
104 // 2 links affected 106 // 2 links affected
105 $historyEntry = $this->history->getHistory()[0]; 107 $historyEntry = $this->history->getHistory()[0];
106 $this->assertEquals(\History::UPDATED, $historyEntry['event']); 108 $this->assertEquals(History::UPDATED, $historyEntry['event']);
107 $this->assertTrue( 109 $this->assertTrue(
108 (new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime'] 110 (new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime']
109 ); 111 );
110 $historyEntry = $this->history->getHistory()[1]; 112 $historyEntry = $this->history->getHistory()[1];
111 $this->assertEquals(\History::UPDATED, $historyEntry['event']); 113 $this->assertEquals(History::UPDATED, $historyEntry['event']);
112 $this->assertTrue( 114 $this->assertTrue(
113 (new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime'] 115 (new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime']
114 ); 116 );
@@ -131,13 +133,13 @@ class DeleteTagTest extends \PHPUnit_Framework_TestCase
131 $this->assertEquals(204, $response->getStatusCode()); 133 $this->assertEquals(204, $response->getStatusCode());
132 $this->assertEmpty((string) $response->getBody()); 134 $this->assertEmpty((string) $response->getBody());
133 135
134 $this->linkDB = new \LinkDB(self::$testDatastore, true, false); 136 $this->linkDB = new LinkDB(self::$testDatastore, true, false);
135 $tags = $this->linkDB->linksCountPerTag(); 137 $tags = $this->linkDB->linksCountPerTag();
136 $this->assertFalse(isset($tags[$tagName])); 138 $this->assertFalse(isset($tags[$tagName]));
137 $this->assertTrue($tags[strtolower($tagName)] > 0); 139 $this->assertTrue($tags[strtolower($tagName)] > 0);
138 140
139 $historyEntry = $this->history->getHistory()[0]; 141 $historyEntry = $this->history->getHistory()[0];
140 $this->assertEquals(\History::UPDATED, $historyEntry['event']); 142 $this->assertEquals(History::UPDATED, $historyEntry['event']);
141 $this->assertTrue( 143 $this->assertTrue(
142 (new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime'] 144 (new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime']
143 ); 145 );
diff --git a/tests/api/controllers/tags/GetTagNameTest.php b/tests/api/controllers/tags/GetTagNameTest.php
index afac228e..a2525c17 100644
--- a/tests/api/controllers/tags/GetTagNameTest.php
+++ b/tests/api/controllers/tags/GetTagNameTest.php
@@ -2,8 +2,8 @@
2 2
3namespace Shaarli\Api\Controllers; 3namespace Shaarli\Api\Controllers;
4 4
5use Shaarli\Bookmark\LinkDB;
5use Shaarli\Config\ConfigManager; 6use Shaarli\Config\ConfigManager;
6
7use Slim\Container; 7use Slim\Container;
8use Slim\Http\Environment; 8use Slim\Http\Environment;
9use Slim\Http\Request; 9use Slim\Http\Request;
@@ -16,7 +16,7 @@ use Slim\Http\Response;
16 * 16 *
17 * @package Shaarli\Api\Controllers 17 * @package Shaarli\Api\Controllers
18 */ 18 */
19class GetTagNameTest extends \PHPUnit_Framework_TestCase 19class GetTagNameTest extends \PHPUnit\Framework\TestCase
20{ 20{
21 /** 21 /**
22 * @var string datastore to test write operations 22 * @var string datastore to test write operations
@@ -59,7 +59,7 @@ class GetTagNameTest extends \PHPUnit_Framework_TestCase
59 59
60 $this->container = new Container(); 60 $this->container = new Container();
61 $this->container['conf'] = $this->conf; 61 $this->container['conf'] = $this->conf;
62 $this->container['db'] = new \LinkDB(self::$testDatastore, true, false); 62 $this->container['db'] = new LinkDB(self::$testDatastore, true, false);
63 $this->container['history'] = null; 63 $this->container['history'] = null;
64 64
65 $this->controller = new Tags($this->container); 65 $this->controller = new Tags($this->container);
diff --git a/tests/api/controllers/tags/GetTagsTest.php b/tests/api/controllers/tags/GetTagsTest.php
index 3fab31b0..98628c98 100644
--- a/tests/api/controllers/tags/GetTagsTest.php
+++ b/tests/api/controllers/tags/GetTagsTest.php
@@ -1,8 +1,8 @@
1<?php 1<?php
2namespace Shaarli\Api\Controllers; 2namespace Shaarli\Api\Controllers;
3 3
4use Shaarli\Bookmark\LinkDB;
4use Shaarli\Config\ConfigManager; 5use Shaarli\Config\ConfigManager;
5
6use Slim\Container; 6use Slim\Container;
7use Slim\Http\Environment; 7use Slim\Http\Environment;
8use Slim\Http\Request; 8use Slim\Http\Request;
@@ -15,7 +15,7 @@ use Slim\Http\Response;
15 * 15 *
16 * @package Shaarli\Api\Controllers 16 * @package Shaarli\Api\Controllers
17 */ 17 */
18class GetTagsTest extends \PHPUnit_Framework_TestCase 18class GetTagsTest extends \PHPUnit\Framework\TestCase
19{ 19{
20 /** 20 /**
21 * @var string datastore to test write operations 21 * @var string datastore to test write operations
@@ -38,7 +38,7 @@ class GetTagsTest extends \PHPUnit_Framework_TestCase
38 protected $container; 38 protected $container;
39 39
40 /** 40 /**
41 * @var \LinkDB instance. 41 * @var LinkDB instance.
42 */ 42 */
43 protected $linkDB; 43 protected $linkDB;
44 44
@@ -63,7 +63,7 @@ class GetTagsTest extends \PHPUnit_Framework_TestCase
63 63
64 $this->container = new Container(); 64 $this->container = new Container();
65 $this->container['conf'] = $this->conf; 65 $this->container['conf'] = $this->conf;
66 $this->linkDB = new \LinkDB(self::$testDatastore, true, false); 66 $this->linkDB = new LinkDB(self::$testDatastore, true, false);
67 $this->container['db'] = $this->linkDB; 67 $this->container['db'] = $this->linkDB;
68 $this->container['history'] = null; 68 $this->container['history'] = null;
69 69
diff --git a/tests/api/controllers/tags/PutTagTest.php b/tests/api/controllers/tags/PutTagTest.php
index 6f7dec22..86106fc7 100644
--- a/tests/api/controllers/tags/PutTagTest.php
+++ b/tests/api/controllers/tags/PutTagTest.php
@@ -1,17 +1,17 @@
1<?php 1<?php
2 2
3
4namespace Shaarli\Api\Controllers; 3namespace Shaarli\Api\Controllers;
5 4
6
7use Shaarli\Api\Exceptions\ApiBadParametersException; 5use Shaarli\Api\Exceptions\ApiBadParametersException;
6use Shaarli\Bookmark\LinkDB;
8use Shaarli\Config\ConfigManager; 7use Shaarli\Config\ConfigManager;
8use Shaarli\History;
9use Slim\Container; 9use Slim\Container;
10use Slim\Http\Environment; 10use Slim\Http\Environment;
11use Slim\Http\Request; 11use Slim\Http\Request;
12use Slim\Http\Response; 12use Slim\Http\Response;
13 13
14class PutTagTest extends \PHPUnit_Framework_TestCase 14class PutTagTest extends \PHPUnit\Framework\TestCase
15{ 15{
16 /** 16 /**
17 * @var string datastore to test write operations 17 * @var string datastore to test write operations
@@ -34,7 +34,7 @@ class PutTagTest extends \PHPUnit_Framework_TestCase
34 protected $refDB = null; 34 protected $refDB = null;
35 35
36 /** 36 /**
37 * @var \History instance. 37 * @var HistoryController instance.
38 */ 38 */
39 protected $history; 39 protected $history;
40 40
@@ -44,7 +44,7 @@ class PutTagTest extends \PHPUnit_Framework_TestCase
44 protected $container; 44 protected $container;
45 45
46 /** 46 /**
47 * @var \LinkDB instance. 47 * @var LinkDB instance.
48 */ 48 */
49 protected $linkDB; 49 protected $linkDB;
50 50
@@ -69,11 +69,11 @@ class PutTagTest extends \PHPUnit_Framework_TestCase
69 69
70 $refHistory = new \ReferenceHistory(); 70 $refHistory = new \ReferenceHistory();
71 $refHistory->write(self::$testHistory); 71 $refHistory->write(self::$testHistory);
72 $this->history = new \History(self::$testHistory); 72 $this->history = new History(self::$testHistory);
73 73
74 $this->container = new Container(); 74 $this->container = new Container();
75 $this->container['conf'] = $this->conf; 75 $this->container['conf'] = $this->conf;
76 $this->linkDB = new \LinkDB(self::$testDatastore, true, false); 76 $this->linkDB = new LinkDB(self::$testDatastore, true, false);
77 $this->container['db'] = $this->linkDB; 77 $this->container['db'] = $this->linkDB;
78 $this->container['history'] = $this->history; 78 $this->container['history'] = $this->history;
79 79
@@ -114,12 +114,12 @@ class PutTagTest extends \PHPUnit_Framework_TestCase
114 $this->assertEquals(2, $tags[$newName]); 114 $this->assertEquals(2, $tags[$newName]);
115 115
116 $historyEntry = $this->history->getHistory()[0]; 116 $historyEntry = $this->history->getHistory()[0];
117 $this->assertEquals(\History::UPDATED, $historyEntry['event']); 117 $this->assertEquals(History::UPDATED, $historyEntry['event']);
118 $this->assertTrue( 118 $this->assertTrue(
119 (new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime'] 119 (new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime']
120 ); 120 );
121 $historyEntry = $this->history->getHistory()[1]; 121 $historyEntry = $this->history->getHistory()[1];
122 $this->assertEquals(\History::UPDATED, $historyEntry['event']); 122 $this->assertEquals(History::UPDATED, $historyEntry['event']);
123 $this->assertTrue( 123 $this->assertTrue(
124 (new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime'] 124 (new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime']
125 ); 125 );
diff --git a/tests/LinkDBTest.php b/tests/bookmark/LinkDBTest.php
index 3b980878..ff5c0b97 100644
--- a/tests/LinkDBTest.php
+++ b/tests/bookmark/LinkDBTest.php
@@ -3,9 +3,14 @@
3 * Link datastore tests 3 * Link datastore tests
4 */ 4 */
5 5
6require_once 'application/Cache.php'; 6namespace Shaarli\Bookmark;
7require_once 'application/FileUtils.php'; 7
8require_once 'application/LinkDB.php'; 8use DateTime;
9use ReferenceLinkDB;
10use ReflectionClass;
11use Shaarli;
12
13require_once 'application/feed/Cache.php';
9require_once 'application/Utils.php'; 14require_once 'application/Utils.php';
10require_once 'tests/utils/ReferenceLinkDB.php'; 15require_once 'tests/utils/ReferenceLinkDB.php';
11 16
@@ -13,7 +18,7 @@ require_once 'tests/utils/ReferenceLinkDB.php';
13/** 18/**
14 * Unitary tests for LinkDB 19 * Unitary tests for LinkDB
15 */ 20 */
16class LinkDBTest extends PHPUnit_Framework_TestCase 21class LinkDBTest extends \PHPUnit\Framework\TestCase
17{ 22{
18 // datastore to test write operations 23 // datastore to test write operations
19 protected static $testDatastore = 'sandbox/datastore.php'; 24 protected static $testDatastore = 'sandbox/datastore.php';
@@ -73,7 +78,7 @@ class LinkDBTest extends PHPUnit_Framework_TestCase
73 */ 78 */
74 protected static function getMethod($name) 79 protected static function getMethod($name)
75 { 80 {
76 $class = new ReflectionClass('LinkDB'); 81 $class = new ReflectionClass('Shaarli\Bookmark\LinkDB');
77 $method = $class->getMethod($name); 82 $method = $class->getMethod($name);
78 $method->setAccessible(true); 83 $method->setAccessible(true);
79 return $method; 84 return $method;
@@ -100,7 +105,7 @@ class LinkDBTest extends PHPUnit_Framework_TestCase
100 /** 105 /**
101 * Attempt to instantiate a LinkDB whereas the datastore is not writable 106 * Attempt to instantiate a LinkDB whereas the datastore is not writable
102 * 107 *
103 * @expectedException IOException 108 * @expectedException Shaarli\Exceptions\IOException
104 * @expectedExceptionMessageRegExp /Error accessing "null"/ 109 * @expectedExceptionMessageRegExp /Error accessing "null"/
105 */ 110 */
106 public function testConstructDatastoreNotWriteable() 111 public function testConstructDatastoreNotWriteable()
@@ -187,12 +192,12 @@ class LinkDBTest extends PHPUnit_Framework_TestCase
187 192
188 $link = array( 193 $link = array(
189 'id' => 42, 194 'id' => 42,
190 'title'=>'an additional link', 195 'title' => 'an additional link',
191 'url'=>'http://dum.my', 196 'url' => 'http://dum.my',
192 'description'=>'One more', 197 'description' => 'One more',
193 'private'=>0, 198 'private' => 0,
194 'created'=> DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150518_190000'), 199 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150518_190000'),
195 'tags'=>'unit test' 200 'tags' => 'unit test'
196 ); 201 );
197 $testDB[$link['id']] = $link; 202 $testDB[$link['id']] = $link;
198 $testDB->save('tests'); 203 $testDB->save('tests');
@@ -239,12 +244,12 @@ class LinkDBTest extends PHPUnit_Framework_TestCase
239 public function testDays() 244 public function testDays()
240 { 245 {
241 $this->assertEquals( 246 $this->assertEquals(
242 array('20100310', '20121206', '20130614', '20150310'), 247 array('20100309', '20100310', '20121206', '20121207', '20130614', '20150310'),
243 self::$publicLinkDB->days() 248 self::$publicLinkDB->days()
244 ); 249 );
245 250
246 $this->assertEquals( 251 $this->assertEquals(
247 array('20100310', '20121206', '20130614', '20141125', '20150310'), 252 array('20100309', '20100310', '20121206', '20121207', '20130614', '20141125', '20150310'),
248 self::$privateLinkDB->days() 253 self::$privateLinkDB->days()
249 ); 254 );
250 } 255 }
@@ -362,7 +367,7 @@ class LinkDBTest extends PHPUnit_Framework_TestCase
362 public function testLinkRealUrlWithoutRedirector() 367 public function testLinkRealUrlWithoutRedirector()
363 { 368 {
364 $db = new LinkDB(self::$testDatastore, false, false); 369 $db = new LinkDB(self::$testDatastore, false, false);
365 foreach($db as $link) { 370 foreach ($db as $link) {
366 $this->assertEquals($link['url'], $link['real_url']); 371 $this->assertEquals($link['url'], $link['real_url']);
367 } 372 }
368 } 373 }
@@ -374,13 +379,13 @@ class LinkDBTest extends PHPUnit_Framework_TestCase
374 { 379 {
375 $redirector = 'http://redirector.to?'; 380 $redirector = 'http://redirector.to?';
376 $db = new LinkDB(self::$testDatastore, false, false, $redirector); 381 $db = new LinkDB(self::$testDatastore, false, false, $redirector);
377 foreach($db as $link) { 382 foreach ($db as $link) {
378 $this->assertStringStartsWith($redirector, $link['real_url']); 383 $this->assertStringStartsWith($redirector, $link['real_url']);
379 $this->assertNotFalse(strpos($link['real_url'], urlencode('://'))); 384 $this->assertNotFalse(strpos($link['real_url'], urlencode('://')));
380 } 385 }
381 386
382 $db = new LinkDB(self::$testDatastore, false, false, $redirector, false); 387 $db = new LinkDB(self::$testDatastore, false, false, $redirector, false);
383 foreach($db as $link) { 388 foreach ($db as $link) {
384 $this->assertStringStartsWith($redirector, $link['real_url']); 389 $this->assertStringStartsWith($redirector, $link['real_url']);
385 $this->assertFalse(strpos($link['real_url'], urlencode('://'))); 390 $this->assertFalse(strpos($link['real_url'], urlencode('://')));
386 } 391 }
@@ -451,7 +456,7 @@ class LinkDBTest extends PHPUnit_Framework_TestCase
451 /** 456 /**
452 * Test filterHash() with an invalid smallhash. 457 * Test filterHash() with an invalid smallhash.
453 * 458 *
454 * @expectedException LinkNotFoundException 459 * @expectedException \Shaarli\Bookmark\Exception\LinkNotFoundException
455 */ 460 */
456 public function testFilterHashInValid1() 461 public function testFilterHashInValid1()
457 { 462 {
@@ -462,7 +467,7 @@ class LinkDBTest extends PHPUnit_Framework_TestCase
462 /** 467 /**
463 * Test filterHash() with an empty smallhash. 468 * Test filterHash() with an empty smallhash.
464 * 469 *
465 * @expectedException LinkNotFoundException 470 * @expectedException \Shaarli\Bookmark\Exception\LinkNotFoundException
466 */ 471 */
467 public function testFilterHashInValid() 472 public function testFilterHashInValid()
468 { 473 {
@@ -475,13 +480,15 @@ class LinkDBTest extends PHPUnit_Framework_TestCase
475 public function testReorderLinksDesc() 480 public function testReorderLinksDesc()
476 { 481 {
477 self::$privateLinkDB->reorder('ASC'); 482 self::$privateLinkDB->reorder('ASC');
478 $linkIds = array(42, 4, 9, 1, 0, 7, 6, 8, 41); 483 $stickyIds = [11, 10];
484 $standardIds = [42, 4, 9, 1, 0, 7, 6, 8, 41];
485 $linkIds = array_merge($stickyIds, $standardIds);
479 $cpt = 0; 486 $cpt = 0;
480 foreach (self::$privateLinkDB as $key => $value) { 487 foreach (self::$privateLinkDB as $key => $value) {
481 $this->assertEquals($linkIds[$cpt++], $key); 488 $this->assertEquals($linkIds[$cpt++], $key);
482 } 489 }
483 self::$privateLinkDB->reorder('DESC'); 490 self::$privateLinkDB->reorder('DESC');
484 $linkIds = array_reverse($linkIds); 491 $linkIds = array_merge(array_reverse($stickyIds), array_reverse($standardIds));
485 $cpt = 0; 492 $cpt = 0;
486 foreach (self::$privateLinkDB as $key => $value) { 493 foreach (self::$privateLinkDB as $key => $value) {
487 $this->assertEquals($linkIds[$cpt++], $key); 494 $this->assertEquals($linkIds[$cpt++], $key);
diff --git a/tests/LinkFilterTest.php b/tests/bookmark/LinkFilterTest.php
index 9cd6dbd4..808f8122 100644
--- a/tests/LinkFilterTest.php
+++ b/tests/bookmark/LinkFilterTest.php
@@ -1,11 +1,14 @@
1<?php 1<?php
2 2
3require_once 'application/LinkFilter.php'; 3namespace Shaarli\Bookmark;
4
5use Exception;
6use ReferenceLinkDB;
4 7
5/** 8/**
6 * Class LinkFilterTest. 9 * Class LinkFilterTest.
7 */ 10 */
8class LinkFilterTest extends PHPUnit_Framework_TestCase 11class LinkFilterTest extends \PHPUnit\Framework\TestCase
9{ 12{
10 /** 13 /**
11 * @var string Test datastore path. 14 * @var string Test datastore path.
@@ -27,7 +30,7 @@ class LinkFilterTest extends PHPUnit_Framework_TestCase
27 protected static $linkDB; 30 protected static $linkDB;
28 31
29 /** 32 /**
30 * Instanciate linkFilter with ReferenceLinkDB data. 33 * Instantiate linkFilter with ReferenceLinkDB data.
31 */ 34 */
32 public static function setUpBeforeClass() 35 public static function setUpBeforeClass()
33 { 36 {
@@ -76,7 +79,19 @@ class LinkFilterTest extends PHPUnit_Framework_TestCase
76 79
77 $this->assertEquals( 80 $this->assertEquals(
78 self::$refDB->countUntaggedLinks(), 81 self::$refDB->countUntaggedLinks(),
79 count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, /*$request=*/'', /*$casesensitive=*/false, /*$visibility=*/'all', /*$untaggedonly=*/true)) 82 count(
83 self::$linkFilter->filter(
84 LinkFilter::$FILTER_TAG,
85 /*$request=*/
86 '',
87 /*$casesensitive=*/
88 false,
89 /*$visibility=*/
90 'all',
91 /*$untaggedonly=*/
92 true
93 )
94 )
80 ); 95 );
81 96
82 $this->assertEquals( 97 $this->assertEquals(
@@ -219,7 +234,7 @@ class LinkFilterTest extends PHPUnit_Framework_TestCase
219 /** 234 /**
220 * No link for this hash 235 * No link for this hash
221 * 236 *
222 * @expectedException LinkNotFoundException 237 * @expectedException \Shaarli\Bookmark\Exception\LinkNotFoundException
223 */ 238 */
224 public function testFilterUnknownSmallHash() 239 public function testFilterUnknownSmallHash()
225 { 240 {
@@ -246,7 +261,7 @@ class LinkFilterTest extends PHPUnit_Framework_TestCase
246 2, 261 2,
247 count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'ars.userfriendly.org')) 262 count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'ars.userfriendly.org'))
248 ); 263 );
249 264
250 $this->assertEquals( 265 $this->assertEquals(
251 2, 266 2,
252 count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'ars org')) 267 count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'ars org'))
@@ -288,16 +303,16 @@ class LinkFilterTest extends PHPUnit_Framework_TestCase
288 1, 303 1,
289 count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'publishing media')) 304 count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'publishing media'))
290 ); 305 );
291 306
292 $this->assertEquals( 307 $this->assertEquals(
293 1, 308 1,
294 count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'mercurial w3c')) 309 count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'mercurial w3c'))
295 ); 310 );
296 311
297 $this->assertEquals( 312 $this->assertEquals(
298 3, 313 3,
299 count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, '"free software"')) 314 count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, '"free software"'))
300 ); 315 );
301 } 316 }
302 317
303 /** 318 /**
diff --git a/tests/LinkUtilsTest.php b/tests/bookmark/LinkUtilsTest.php
index 7fbd59b0..1b8688e6 100644
--- a/tests/LinkUtilsTest.php
+++ b/tests/bookmark/LinkUtilsTest.php
@@ -1,11 +1,15 @@
1<?php 1<?php
2 2
3require_once 'application/LinkUtils.php'; 3namespace Shaarli\Bookmark;
4
5use ReferenceLinkDB;
6
7require_once 'tests/utils/CurlUtils.php';
4 8
5/** 9/**
6* Class LinkUtilsTest. 10 * Class LinkUtilsTest.
7*/ 11 */
8class LinkUtilsTest extends PHPUnit_Framework_TestCase 12class LinkUtilsTest extends \PHPUnit\Framework\TestCase
9{ 13{
10 /** 14 /**
11 * Test html_extract_title() when the title is found. 15 * Test html_extract_title() when the title is found.
@@ -13,9 +17,9 @@ class LinkUtilsTest extends PHPUnit_Framework_TestCase
13 public function testHtmlExtractExistentTitle() 17 public function testHtmlExtractExistentTitle()
14 { 18 {
15 $title = 'Read me please.'; 19 $title = 'Read me please.';
16 $html = '<html><meta>stuff</meta><title>'. $title .'</title></html>'; 20 $html = '<html><meta>stuff</meta><title>' . $title . '</title></html>';
17 $this->assertEquals($title, html_extract_title($html)); 21 $this->assertEquals($title, html_extract_title($html));
18 $html = '<html><title>'. $title .'</title>blabla<title>another</title></html>'; 22 $html = '<html><title>' . $title . '</title>blabla<title>another</title></html>';
19 $this->assertEquals($title, html_extract_title($html)); 23 $this->assertEquals($title, html_extract_title($html));
20 } 24 }
21 25
@@ -34,7 +38,7 @@ class LinkUtilsTest extends PHPUnit_Framework_TestCase
34 public function testHeadersExtractExistentCharset() 38 public function testHeadersExtractExistentCharset()
35 { 39 {
36 $charset = 'x-MacCroatian'; 40 $charset = 'x-MacCroatian';
37 $headers = 'text/html; charset='. $charset; 41 $headers = 'text/html; charset=' . $charset;
38 $this->assertEquals(strtolower($charset), header_extract_charset($headers)); 42 $this->assertEquals(strtolower($charset), header_extract_charset($headers));
39 } 43 }
40 44
@@ -56,7 +60,7 @@ class LinkUtilsTest extends PHPUnit_Framework_TestCase
56 public function testHtmlExtractExistentCharset() 60 public function testHtmlExtractExistentCharset()
57 { 61 {
58 $charset = 'x-MacCroatian'; 62 $charset = 'x-MacCroatian';
59 $html = '<html><meta>stuff2</meta><meta charset="'. $charset .'"/></html>'; 63 $html = '<html><meta>stuff2</meta><meta charset="' . $charset . '"/></html>';
60 $this->assertEquals(strtolower($charset), html_extract_charset($html)); 64 $this->assertEquals(strtolower($charset), html_extract_charset($html));
61 } 65 }
62 66
@@ -83,7 +87,9 @@ class LinkUtilsTest extends PHPUnit_Framework_TestCase
83 'Date: Sat, 28 Oct 2017 12:01:33 GMT', 87 'Date: Sat, 28 Oct 2017 12:01:33 GMT',
84 'Content-Type: text/html; charset=utf-8', 88 'Content-Type: text/html; charset=utf-8',
85 'Status: 200 OK', 89 'Status: 200 OK',
86 'end' => 'th=device-width"><title>Refactoring · GitHub</title><link rel="search" type="application/opensea', 90 'end' => 'th=device-width">'
91 . '<title>Refactoring · GitHub</title>'
92 . '<link rel="search" type="application/opensea',
87 '<title>ignored</title>', 93 '<title>ignored</title>',
88 ]; 94 ];
89 foreach ($data as $key => $line) { 95 foreach ($data as $key => $line) {
@@ -106,7 +112,9 @@ class LinkUtilsTest extends PHPUnit_Framework_TestCase
106 $callback = get_curl_download_callback($charset, $title, 'ut_curl_getinfo_no_charset'); 112 $callback = get_curl_download_callback($charset, $title, 'ut_curl_getinfo_no_charset');
107 $data = [ 113 $data = [
108 'HTTP/1.1 200 OK', 114 'HTTP/1.1 200 OK',
109 'end' => 'th=device-width"><title>Refactoring · GitHub</title><link rel="search" type="application/opensea', 115 'end' => 'th=device-width">'
116 . '<title>Refactoring · GitHub</title>'
117 . '<link rel="search" type="application/opensea',
110 '<title>ignored</title>', 118 '<title>ignored</title>',
111 ]; 119 ];
112 foreach ($data as $key => $line) { 120 foreach ($data as $key => $line) {
@@ -126,7 +134,9 @@ class LinkUtilsTest extends PHPUnit_Framework_TestCase
126 $data = [ 134 $data = [
127 'HTTP/1.1 200 OK', 135 'HTTP/1.1 200 OK',
128 '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />', 136 '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />',
129 'end' => 'th=device-width"><title>Refactoring · GitHub</title><link rel="search" type="application/opensea', 137 'end' => 'th=device-width">'
138 . '<title>Refactoring · GitHub</title>'
139 . '<link rel="search" type="application/opensea',
130 '<title>ignored</title>', 140 '<title>ignored</title>',
131 ]; 141 ];
132 foreach ($data as $key => $line) { 142 foreach ($data as $key => $line) {
@@ -211,29 +221,32 @@ class LinkUtilsTest extends PHPUnit_Framework_TestCase
211 public function testText2clickableWithoutRedirector() 221 public function testText2clickableWithoutRedirector()
212 { 222 {
213 $text = 'stuff http://hello.there/is=someone#here otherstuff'; 223 $text = 'stuff http://hello.there/is=someone#here otherstuff';
214 $expectedText = 'stuff <a href="http://hello.there/is=someone#here">http://hello.there/is=someone#here</a> otherstuff'; 224 $expectedText = 'stuff <a href="http://hello.there/is=someone#here">'
225 . 'http://hello.there/is=someone#here</a> otherstuff';
215 $processedText = text2clickable($text, ''); 226 $processedText = text2clickable($text, '');
216 $this->assertEquals($expectedText, $processedText); 227 $this->assertEquals($expectedText, $processedText);
217 228
218 $text = 'stuff http://hello.there/is=someone#here(please) otherstuff'; 229 $text = 'stuff http://hello.there/is=someone#here(please) otherstuff';
219 $expectedText = 'stuff <a href="http://hello.there/is=someone#here(please)">http://hello.there/is=someone#here(please)</a> otherstuff'; 230 $expectedText = 'stuff <a href="http://hello.there/is=someone#here(please)">'
231 . 'http://hello.there/is=someone#here(please)</a> otherstuff';
220 $processedText = text2clickable($text, ''); 232 $processedText = text2clickable($text, '');
221 $this->assertEquals($expectedText, $processedText); 233 $this->assertEquals($expectedText, $processedText);
222 234
223 $text = 'stuff http://hello.there/is=someone#here(please)&no otherstuff'; 235 $text = 'stuff http://hello.there/is=someone#here(please)&no otherstuff';
224 $expectedText = 'stuff <a href="http://hello.there/is=someone#here(please)&no">http://hello.there/is=someone#here(please)&no</a> otherstuff'; 236 $expectedText = 'stuff <a href="http://hello.there/is=someone#here(please)&no">'
237 . 'http://hello.there/is=someone#here(please)&no</a> otherstuff';
225 $processedText = text2clickable($text, ''); 238 $processedText = text2clickable($text, '');
226 $this->assertEquals($expectedText, $processedText); 239 $this->assertEquals($expectedText, $processedText);
227 } 240 }
228 241
229 /** 242 /**
230 * Test text2clickable a redirector set. 243 * Test text2clickable with a redirector set.
231 */ 244 */
232 public function testText2clickableWithRedirector() 245 public function testText2clickableWithRedirector()
233 { 246 {
234 $text = 'stuff http://hello.there/is=someone#here otherstuff'; 247 $text = 'stuff http://hello.there/is=someone#here otherstuff';
235 $redirector = 'http://redirector.to'; 248 $redirector = 'http://redirector.to';
236 $expectedText = 'stuff <a href="'. 249 $expectedText = 'stuff <a href="' .
237 $redirector . 250 $redirector .
238 urlencode('http://hello.there/is=someone#here') . 251 urlencode('http://hello.there/is=someone#here') .
239 '">http://hello.there/is=someone#here</a> otherstuff'; 252 '">http://hello.there/is=someone#here</a> otherstuff';
@@ -248,7 +261,7 @@ class LinkUtilsTest extends PHPUnit_Framework_TestCase
248 { 261 {
249 $text = 'stuff http://hello.there/?is=someone&or=something#here otherstuff'; 262 $text = 'stuff http://hello.there/?is=someone&or=something#here otherstuff';
250 $redirector = 'http://redirector.to'; 263 $redirector = 'http://redirector.to';
251 $expectedText = 'stuff <a href="'. 264 $expectedText = 'stuff <a href="' .
252 $redirector . 265 $redirector .
253 'http://hello.there/?is=someone&or=something#here' . 266 'http://hello.there/?is=someone&or=something#here' .
254 '">http://hello.there/?is=someone&or=something#here</a> otherstuff'; 267 '">http://hello.there/?is=someone&or=something#here</a> otherstuff';
@@ -261,8 +274,8 @@ class LinkUtilsTest extends PHPUnit_Framework_TestCase
261 */ 274 */
262 public function testSpace2nbsp() 275 public function testSpace2nbsp()
263 { 276 {
264 $text = ' Are you thrilled by flags ?'. PHP_EOL .' Really?'; 277 $text = ' Are you thrilled by flags ?' . PHP_EOL . ' Really?';
265 $expectedText = '&nbsp; Are you &nbsp; thrilled &nbsp;by flags &nbsp; ?'. PHP_EOL .'&nbsp;Really?'; 278 $expectedText = '&nbsp; Are you &nbsp; thrilled &nbsp;by flags &nbsp; ?' . PHP_EOL . '&nbsp;Really?';
266 $processedText = space2nbsp($text); 279 $processedText = space2nbsp($text);
267 $this->assertEquals($expectedText, $processedText); 280 $this->assertEquals($expectedText, $processedText);
268 } 281 }
@@ -308,106 +321,13 @@ class LinkUtilsTest extends PHPUnit_Framework_TestCase
308 * Util function to build an hashtag link. 321 * Util function to build an hashtag link.
309 * 322 *
310 * @param string $hashtag Hashtag name. 323 * @param string $hashtag Hashtag name.
311 * @param string $index Index URL. 324 * @param string $index Index URL.
312 * 325 *
313 * @return string HTML hashtag link. 326 * @return string HTML hashtag link.
314 */ 327 */
315 private function getHashtagLink($hashtag, $index = '') 328 private function getHashtagLink($hashtag, $index = '')
316 { 329 {
317 $hashtagLink = '<a href="'. $index .'?addtag=$1" title="Hashtag $1">#$1</a>'; 330 $hashtagLink = '<a href="' . $index . '?addtag=$1" title="Hashtag $1">#$1</a>';
318 return str_replace('$1', $hashtag, $hashtagLink); 331 return str_replace('$1', $hashtag, $hashtagLink);
319 } 332 }
320} 333}
321
322// old style mock: PHPUnit doesn't allow function mock
323
324/**
325 * Returns code 200 or html content type.
326 *
327 * @param resource $ch cURL resource
328 * @param int $type cURL info type
329 *
330 * @return int|string 200 or 'text/html'
331 */
332function ut_curl_getinfo_ok($ch, $type)
333{
334 switch ($type) {
335 case CURLINFO_RESPONSE_CODE:
336 return 200;
337 case CURLINFO_CONTENT_TYPE:
338 return 'text/html; charset=utf-8';
339 }
340}
341
342/**
343 * Returns code 200 or html content type without charset.
344 *
345 * @param resource $ch cURL resource
346 * @param int $type cURL info type
347 *
348 * @return int|string 200 or 'text/html'
349 */
350function ut_curl_getinfo_no_charset($ch, $type)
351{
352 switch ($type) {
353 case CURLINFO_RESPONSE_CODE:
354 return 200;
355 case CURLINFO_CONTENT_TYPE:
356 return 'text/html';
357 }
358}
359
360/**
361 * Invalid response code.
362 *
363 * @param resource $ch cURL resource
364 * @param int $type cURL info type
365 *
366 * @return int|string 404 or 'text/html'
367 */
368function ut_curl_getinfo_rc_ko($ch, $type)
369{
370 switch ($type) {
371 case CURLINFO_RESPONSE_CODE:
372 return 404;
373 case CURLINFO_CONTENT_TYPE:
374 return 'text/html; charset=utf-8';
375 }
376}
377
378/**
379 * Invalid content type.
380 *
381 * @param resource $ch cURL resource
382 * @param int $type cURL info type
383 *
384 * @return int|string 200 or 'text/plain'
385 */
386function ut_curl_getinfo_ct_ko($ch, $type)
387{
388 switch ($type) {
389 case CURLINFO_RESPONSE_CODE:
390 return 200;
391 case CURLINFO_CONTENT_TYPE:
392 return 'text/plain';
393 }
394}
395
396/**
397 * Invalid response code and content type.
398 *
399 * @param resource $ch cURL resource
400 * @param int $type cURL info type
401 *
402 * @return int|string 404 or 'text/plain'
403 */
404function ut_curl_getinfo_rs_ct_ko($ch, $type)
405{
406 switch ($type) {
407 case CURLINFO_RESPONSE_CODE:
408 return 404;
409 case CURLINFO_CONTENT_TYPE:
410 return 'text/plain';
411 }
412}
413
diff --git a/tests/config/ConfigJsonTest.php b/tests/config/ConfigJsonTest.php
index d237bc80..95ad060b 100644
--- a/tests/config/ConfigJsonTest.php
+++ b/tests/config/ConfigJsonTest.php
@@ -4,7 +4,7 @@ namespace Shaarli\Config;
4/** 4/**
5 * Class ConfigJsonTest 5 * Class ConfigJsonTest
6 */ 6 */
7class ConfigJsonTest extends \PHPUnit_Framework_TestCase 7class ConfigJsonTest extends \PHPUnit\Framework\TestCase
8{ 8{
9 /** 9 /**
10 * @var ConfigJson 10 * @var ConfigJson
@@ -111,7 +111,7 @@ class ConfigJsonTest extends \PHPUnit_Framework_TestCase
111 /** 111 /**
112 * Write to invalid path. 112 * Write to invalid path.
113 * 113 *
114 * @expectedException \IOException 114 * @expectedException \Shaarli\Exceptions\IOException
115 */ 115 */
116 public function testWriteInvalidArray() 116 public function testWriteInvalidArray()
117 { 117 {
@@ -122,7 +122,7 @@ class ConfigJsonTest extends \PHPUnit_Framework_TestCase
122 /** 122 /**
123 * Write to invalid path. 123 * Write to invalid path.
124 * 124 *
125 * @expectedException \IOException 125 * @expectedException \Shaarli\Exceptions\IOException
126 */ 126 */
127 public function testWriteInvalidBlank() 127 public function testWriteInvalidBlank()
128 { 128 {
diff --git a/tests/config/ConfigManagerTest.php b/tests/config/ConfigManagerTest.php
index 1ec447b2..33830bc9 100644
--- a/tests/config/ConfigManagerTest.php
+++ b/tests/config/ConfigManagerTest.php
@@ -7,7 +7,7 @@ namespace Shaarli\Config;
7 * Note: it only test the manager with ConfigJson, 7 * Note: it only test the manager with ConfigJson,
8 * ConfigPhp is only a workaround to handle the transition to JSON type. 8 * ConfigPhp is only a workaround to handle the transition to JSON type.
9 */ 9 */
10class ConfigManagerTest extends \PHPUnit_Framework_TestCase 10class ConfigManagerTest extends \PHPUnit\Framework\TestCase
11{ 11{
12 /** 12 /**
13 * @var ConfigManager 13 * @var ConfigManager
@@ -81,6 +81,18 @@ class ConfigManagerTest extends \PHPUnit_Framework_TestCase
81 $this->assertEquals('testSetWriteGetNested', $this->conf->get('foo.bar.key.stuff')); 81 $this->assertEquals('testSetWriteGetNested', $this->conf->get('foo.bar.key.stuff'));
82 } 82 }
83 83
84 public function testSetDeleteNested()
85 {
86 $this->conf->set('foo.bar.key.stuff', 'testSetDeleteNested');
87 $this->assertTrue($this->conf->exists('foo.bar'));
88 $this->assertTrue($this->conf->exists('foo.bar.key.stuff'));
89 $this->assertEquals('testSetDeleteNested', $this->conf->get('foo.bar.key.stuff'));
90
91 $this->conf->remove('foo.bar');
92 $this->assertFalse($this->conf->exists('foo.bar.key.stuff'));
93 $this->assertFalse($this->conf->exists('foo.bar'));
94 }
95
84 /** 96 /**
85 * Set with an empty key. 97 * Set with an empty key.
86 * 98 *
@@ -104,6 +116,17 @@ class ConfigManagerTest extends \PHPUnit_Framework_TestCase
104 } 116 }
105 117
106 /** 118 /**
119 * Remove with an empty key.
120 *
121 * @expectedException \Exception
122 * @expectedExceptionMessageRegExp #^Invalid setting key parameter. String expected, got.*#
123 */
124 public function testRmoveEmptyKey()
125 {
126 $this->conf->remove('');
127 }
128
129 /**
107 * Try to write the config without mandatory parameter (e.g. 'login'). 130 * Try to write the config without mandatory parameter (e.g. 'login').
108 * 131 *
109 * @expectedException Shaarli\Config\Exception\MissingFieldConfigException 132 * @expectedException Shaarli\Config\Exception\MissingFieldConfigException
diff --git a/tests/config/ConfigPhpTest.php b/tests/config/ConfigPhpTest.php
index be23eea1..67d878ce 100644
--- a/tests/config/ConfigPhpTest.php
+++ b/tests/config/ConfigPhpTest.php
@@ -4,7 +4,7 @@ namespace Shaarli\Config;
4/** 4/**
5 * Class ConfigPhpTest 5 * Class ConfigPhpTest
6 */ 6 */
7class ConfigPhpTest extends \PHPUnit_Framework_TestCase 7class ConfigPhpTest extends \PHPUnit\Framework\TestCase
8{ 8{
9 /** 9 /**
10 * @var ConfigPhp 10 * @var ConfigPhp
diff --git a/tests/config/ConfigPluginTest.php b/tests/config/ConfigPluginTest.php
index deb02c9e..d7a70e68 100644
--- a/tests/config/ConfigPluginTest.php
+++ b/tests/config/ConfigPluginTest.php
@@ -8,7 +8,7 @@ require_once 'application/config/ConfigPlugin.php';
8/** 8/**
9 * Unitary tests for Shaarli config related functions 9 * Unitary tests for Shaarli config related functions
10 */ 10 */
11class ConfigPluginTest extends \PHPUnit_Framework_TestCase 11class ConfigPluginTest extends \PHPUnit\Framework\TestCase
12{ 12{
13 /** 13 /**
14 * Test save_plugin_config with valid data. 14 * Test save_plugin_config with valid data.
diff --git a/tests/CacheTest.php b/tests/feed/CacheTest.php
index 992e26a5..c0a9f26f 100644
--- a/tests/CacheTest.php
+++ b/tests/feed/CacheTest.php
@@ -2,16 +2,17 @@
2/** 2/**
3 * Cache tests 3 * Cache tests
4 */ 4 */
5namespace Shaarli\Feed;
5 6
6// required to access $_SESSION array 7// required to access $_SESSION array
7session_start(); 8session_start();
8 9
9require_once 'application/Cache.php'; 10require_once 'application/feed/Cache.php';
10 11
11/** 12/**
12 * Unitary tests for cached pages 13 * Unitary tests for cached pages
13 */ 14 */
14class CacheTest extends PHPUnit_Framework_TestCase 15class CacheTest extends \PHPUnit\Framework\TestCase
15{ 16{
16 // test cache directory 17 // test cache directory
17 protected static $testCacheDir = 'sandbox/dummycache'; 18 protected static $testCacheDir = 'sandbox/dummycache';
@@ -25,16 +26,16 @@ class CacheTest extends PHPUnit_Framework_TestCase
25 */ 26 */
26 public function setUp() 27 public function setUp()
27 { 28 {
28 if (! is_dir(self::$testCacheDir)) { 29 if (!is_dir(self::$testCacheDir)) {
29 mkdir(self::$testCacheDir); 30 mkdir(self::$testCacheDir);
30 } else { 31 } else {
31 array_map('unlink', glob(self::$testCacheDir.'/*')); 32 array_map('unlink', glob(self::$testCacheDir . '/*'));
32 } 33 }
33 34
34 foreach (self::$pages as $page) { 35 foreach (self::$pages as $page) {
35 file_put_contents(self::$testCacheDir.'/'.$page.'.cache', $page); 36 file_put_contents(self::$testCacheDir . '/' . $page . '.cache', $page);
36 } 37 }
37 file_put_contents(self::$testCacheDir.'/intru.der', 'ShouldNotBeThere'); 38 file_put_contents(self::$testCacheDir . '/intru.der', 'ShouldNotBeThere');
38 } 39 }
39 40
40 /** 41 /**
@@ -42,7 +43,7 @@ class CacheTest extends PHPUnit_Framework_TestCase
42 */ 43 */
43 public function tearDown() 44 public function tearDown()
44 { 45 {
45 array_map('unlink', glob(self::$testCacheDir.'/*')); 46 array_map('unlink', glob(self::$testCacheDir . '/*'));
46 rmdir(self::$testCacheDir); 47 rmdir(self::$testCacheDir);
47 } 48 }
48 49
@@ -53,10 +54,10 @@ class CacheTest extends PHPUnit_Framework_TestCase
53 { 54 {
54 purgeCachedPages(self::$testCacheDir); 55 purgeCachedPages(self::$testCacheDir);
55 foreach (self::$pages as $page) { 56 foreach (self::$pages as $page) {
56 $this->assertFileNotExists(self::$testCacheDir.'/'.$page.'.cache'); 57 $this->assertFileNotExists(self::$testCacheDir . '/' . $page . '.cache');
57 } 58 }
58 59
59 $this->assertFileExists(self::$testCacheDir.'/intru.der'); 60 $this->assertFileExists(self::$testCacheDir . '/intru.der');
60 } 61 }
61 62
62 /** 63 /**
@@ -68,7 +69,7 @@ class CacheTest extends PHPUnit_Framework_TestCase
68 ini_set('error_log', '/dev/null'); 69 ini_set('error_log', '/dev/null');
69 $this->assertEquals( 70 $this->assertEquals(
70 'Cannot purge sandbox/dummycache_missing: no directory', 71 'Cannot purge sandbox/dummycache_missing: no directory',
71 purgeCachedPages(self::$testCacheDir.'_missing') 72 purgeCachedPages(self::$testCacheDir . '_missing')
72 ); 73 );
73 ini_set('error_log', $oldlog); 74 ini_set('error_log', $oldlog);
74 } 75 }
@@ -83,8 +84,8 @@ class CacheTest extends PHPUnit_Framework_TestCase
83 84
84 invalidateCaches(self::$testCacheDir); 85 invalidateCaches(self::$testCacheDir);
85 foreach (self::$pages as $page) { 86 foreach (self::$pages as $page) {
86 $this->assertFileNotExists(self::$testCacheDir.'/'.$page.'.cache'); 87 $this->assertFileNotExists(self::$testCacheDir . '/' . $page . '.cache');
87 } 88 }
88 89
89 $this->assertArrayNotHasKey('tags', $_SESSION); 90 $this->assertArrayNotHasKey('tags', $_SESSION);
90 } 91 }
diff --git a/tests/CachedPageTest.php b/tests/feed/CachedPageTest.php
index 51565cd6..0bcc1442 100644
--- a/tests/CachedPageTest.php
+++ b/tests/feed/CachedPageTest.php
@@ -2,13 +2,12 @@
2/** 2/**
3 * PageCache tests 3 * PageCache tests
4 */ 4 */
5 5namespace Shaarli\Feed;
6require_once 'application/CachedPage.php';
7 6
8/** 7/**
9 * Unitary tests for cached pages 8 * Unitary tests for cached pages
10 */ 9 */
11class CachedPageTest extends PHPUnit_Framework_TestCase 10class CachedPageTest extends \PHPUnit\Framework\TestCase
12{ 11{
13 // test cache directory 12 // test cache directory
14 protected static $testCacheDir = 'sandbox/pagecache'; 13 protected static $testCacheDir = 'sandbox/pagecache';
@@ -20,10 +19,10 @@ class CachedPageTest extends PHPUnit_Framework_TestCase
20 */ 19 */
21 public static function setUpBeforeClass() 20 public static function setUpBeforeClass()
22 { 21 {
23 if (! is_dir(self::$testCacheDir)) { 22 if (!is_dir(self::$testCacheDir)) {
24 mkdir(self::$testCacheDir); 23 mkdir(self::$testCacheDir);
25 } 24 }
26 self::$filename = self::$testCacheDir.'/'.sha1(self::$url).'.cache'; 25 self::$filename = self::$testCacheDir . '/' . sha1(self::$url) . '.cache';
27 } 26 }
28 27
29 /** 28 /**
diff --git a/tests/FeedBuilderTest.php b/tests/feed/FeedBuilderTest.php
index a590306d..b496cb4c 100644
--- a/tests/FeedBuilderTest.php
+++ b/tests/feed/FeedBuilderTest.php
@@ -1,14 +1,17 @@
1<?php 1<?php
2 2
3require_once 'application/FeedBuilder.php'; 3namespace Shaarli\Feed;
4require_once 'application/LinkDB.php'; 4
5use DateTime;
6use ReferenceLinkDB;
7use Shaarli\Bookmark\LinkDB;
5 8
6/** 9/**
7 * FeedBuilderTest class. 10 * FeedBuilderTest class.
8 * 11 *
9 * Unit tests for FeedBuilder. 12 * Unit tests for FeedBuilder.
10 */ 13 */
11class FeedBuilderTest extends PHPUnit_Framework_TestCase 14class FeedBuilderTest extends \PHPUnit\Framework\TestCase
12{ 15{
13 /** 16 /**
14 * @var string locale Basque (Spain). 17 * @var string locale Basque (Spain).
@@ -82,15 +85,15 @@ class FeedBuilderTest extends PHPUnit_Framework_TestCase
82 $this->assertFalse($data['usepermalinks']); 85 $this->assertFalse($data['usepermalinks']);
83 $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links'])); 86 $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links']));
84 87
85 // Test first link (note link) 88 // Test first not pinned link (note link)
86 $link = reset($data['links']); 89 $link = $data['links'][array_keys($data['links'])[2]];
87 $this->assertEquals(41, $link['id']); 90 $this->assertEquals(41, $link['id']);
88 $this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114651'), $link['created']); 91 $this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114651'), $link['created']);
89 $this->assertEquals('http://host.tld/?WDWyig', $link['guid']); 92 $this->assertEquals('http://host.tld/?WDWyig', $link['guid']);
90 $this->assertEquals('http://host.tld/?WDWyig', $link['url']); 93 $this->assertEquals('http://host.tld/?WDWyig', $link['url']);
91 $this->assertRegExp('/Tue, 10 Mar 2015 11:46:51 \+\d{4}/', $link['pub_iso_date']); 94 $this->assertRegExp('/Tue, 10 Mar 2015 11:46:51 \+\d{4}/', $link['pub_iso_date']);
92 $pub = DateTime::createFromFormat(DateTime::RSS, $link['pub_iso_date']); 95 $pub = DateTime::createFromFormat(DateTime::RSS, $link['pub_iso_date']);
93 $up = DateTime::createFromFormat(DateTime::ATOM, $link['up_iso_date']); 96 $up = DateTime::createFromFormat(DateTime::ATOM, $link['up_iso_date']);
94 $this->assertEquals($pub, $up); 97 $this->assertEquals($pub, $up);
95 $this->assertContains('Stallman has a beard', $link['description']); 98 $this->assertContains('Stallman has a beard', $link['description']);
96 $this->assertContains('Permalink', $link['description']); 99 $this->assertContains('Permalink', $link['description']);
@@ -119,7 +122,7 @@ class FeedBuilderTest extends PHPUnit_Framework_TestCase
119 $data = $feedBuilder->buildData(); 122 $data = $feedBuilder->buildData();
120 $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links'])); 123 $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links']));
121 $this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['last_update']); 124 $this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['last_update']);
122 $link = reset($data['links']); 125 $link = $data['links'][array_keys($data['links'])[2]];
123 $this->assertRegExp('/2015-03-10T11:46:51\+\d{2}:\d{2}/', $link['pub_iso_date']); 126 $this->assertRegExp('/2015-03-10T11:46:51\+\d{2}:\d{2}/', $link['pub_iso_date']);
124 $this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['links'][8]['up_iso_date']); 127 $this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['links'][8]['up_iso_date']);
125 } 128 }
@@ -148,13 +151,13 @@ class FeedBuilderTest extends PHPUnit_Framework_TestCase
148 public function testBuildDataCount() 151 public function testBuildDataCount()
149 { 152 {
150 $criteria = array( 153 $criteria = array(
151 'nb' => '1', 154 'nb' => '3',
152 ); 155 );
153 $feedBuilder = new FeedBuilder(self::$linkDB, FeedBuilder::$FEED_ATOM, self::$serverInfo, $criteria, false); 156 $feedBuilder = new FeedBuilder(self::$linkDB, FeedBuilder::$FEED_ATOM, self::$serverInfo, $criteria, false);
154 $feedBuilder->setLocale(self::$LOCALE); 157 $feedBuilder->setLocale(self::$LOCALE);
155 $data = $feedBuilder->buildData(); 158 $data = $feedBuilder->buildData();
156 $this->assertEquals(1, count($data['links'])); 159 $this->assertEquals(3, count($data['links']));
157 $link = array_shift($data['links']); 160 $link = $data['links'][array_keys($data['links'])[2]];
158 $this->assertEquals(41, $link['id']); 161 $this->assertEquals(41, $link['id']);
159 $this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114651'), $link['created']); 162 $this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114651'), $link['created']);
160 } 163 }
@@ -171,7 +174,7 @@ class FeedBuilderTest extends PHPUnit_Framework_TestCase
171 $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links'])); 174 $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links']));
172 $this->assertTrue($data['usepermalinks']); 175 $this->assertTrue($data['usepermalinks']);
173 // First link is a permalink 176 // First link is a permalink
174 $link = array_shift($data['links']); 177 $link = $data['links'][array_keys($data['links'])[2]];
175 $this->assertEquals(41, $link['id']); 178 $this->assertEquals(41, $link['id']);
176 $this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114651'), $link['created']); 179 $this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114651'), $link['created']);
177 $this->assertEquals('http://host.tld/?WDWyig', $link['guid']); 180 $this->assertEquals('http://host.tld/?WDWyig', $link['guid']);
@@ -179,7 +182,7 @@ class FeedBuilderTest extends PHPUnit_Framework_TestCase
179 $this->assertContains('Direct link', $link['description']); 182 $this->assertContains('Direct link', $link['description']);
180 $this->assertContains('http://host.tld/?WDWyig', $link['description']); 183 $this->assertContains('http://host.tld/?WDWyig', $link['description']);
181 // Second link is a direct link 184 // Second link is a direct link
182 $link = array_shift($data['links']); 185 $link = $data['links'][array_keys($data['links'])[3]];
183 $this->assertEquals(8, $link['id']); 186 $this->assertEquals(8, $link['id']);
184 $this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114633'), $link['created']); 187 $this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114633'), $link['created']);
185 $this->assertEquals('http://host.tld/?RttfEw', $link['guid']); 188 $this->assertEquals('http://host.tld/?RttfEw', $link['guid']);
@@ -237,7 +240,7 @@ class FeedBuilderTest extends PHPUnit_Framework_TestCase
237 ); 240 );
238 241
239 // Test first link (note link) 242 // Test first link (note link)
240 $link = array_shift($data['links']); 243 $link = $data['links'][array_keys($data['links'])[2]];
241 $this->assertEquals('http://host.tld:8080/~user/shaarli/?WDWyig', $link['guid']); 244 $this->assertEquals('http://host.tld:8080/~user/shaarli/?WDWyig', $link['guid']);
242 $this->assertEquals('http://host.tld:8080/~user/shaarli/?WDWyig', $link['url']); 245 $this->assertEquals('http://host.tld:8080/~user/shaarli/?WDWyig', $link['url']);
243 $this->assertContains('http://host.tld:8080/~user/shaarli/?addtag=hashtag', $link['description']); 246 $this->assertContains('http://host.tld:8080/~user/shaarli/?addtag=hashtag', $link['description']);
diff --git a/tests/HttpUtils/ClientIpIdTest.php b/tests/http/HttpUtils/ClientIpIdTest.php
index c15ac5cc..982e57e0 100644
--- a/tests/HttpUtils/ClientIpIdTest.php
+++ b/tests/http/HttpUtils/ClientIpIdTest.php
@@ -3,12 +3,14 @@
3 * HttpUtils' tests 3 * HttpUtils' tests
4 */ 4 */
5 5
6require_once 'application/HttpUtils.php'; 6namespace Shaarli\Http;
7
8require_once 'application/http/HttpUtils.php';
7 9
8/** 10/**
9 * Unitary tests for client_ip_id() 11 * Unitary tests for client_ip_id()
10 */ 12 */
11class ClientIpIdTest extends PHPUnit_Framework_TestCase 13class ClientIpIdTest extends \PHPUnit\Framework\TestCase
12{ 14{
13 /** 15 /**
14 * Get a remote client ID based on its IP 16 * Get a remote client ID based on its IP
diff --git a/tests/HttpUtils/GetHttpUrlTest.php b/tests/http/HttpUtils/GetHttpUrlTest.php
index ea53de5f..3dc5bc9b 100644
--- a/tests/HttpUtils/GetHttpUrlTest.php
+++ b/tests/http/HttpUtils/GetHttpUrlTest.php
@@ -3,12 +3,14 @@
3 * HttpUtils' tests 3 * HttpUtils' tests
4 */ 4 */
5 5
6require_once 'application/HttpUtils.php'; 6namespace Shaarli\Http;
7
8require_once 'application/http/HttpUtils.php';
7 9
8/** 10/**
9 * Unitary tests for get_http_response() 11 * Unitary tests for get_http_response()
10 */ 12 */
11class GetHttpUrlTest extends PHPUnit_Framework_TestCase 13class GetHttpUrlTest extends \PHPUnit\Framework\TestCase
12{ 14{
13 /** 15 /**
14 * Get an invalid local URL 16 * Get an invalid local URL
@@ -17,12 +19,12 @@ class GetHttpUrlTest extends PHPUnit_Framework_TestCase
17 { 19 {
18 // Local 20 // Local
19 list($headers, $content) = get_http_response('/non/existent', 1); 21 list($headers, $content) = get_http_response('/non/existent', 1);
20 $this->assertEquals('Invalid HTTP Url', $headers[0]); 22 $this->assertEquals('Invalid HTTP UrlUtils', $headers[0]);
21 $this->assertFalse($content); 23 $this->assertFalse($content);
22 24
23 // Non HTTP 25 // Non HTTP
24 list($headers, $content) = get_http_response('ftp://save.tld/mysave', 1); 26 list($headers, $content) = get_http_response('ftp://save.tld/mysave', 1);
25 $this->assertEquals('Invalid HTTP Url', $headers[0]); 27 $this->assertEquals('Invalid HTTP UrlUtils', $headers[0]);
26 $this->assertFalse($content); 28 $this->assertFalse($content);
27 } 29 }
28 30
diff --git a/tests/HttpUtils/GetIpAdressFromProxyTest.php b/tests/http/HttpUtils/GetIpAdressFromProxyTest.php
index 6a74a45a..fe3a639e 100644
--- a/tests/HttpUtils/GetIpAdressFromProxyTest.php
+++ b/tests/http/HttpUtils/GetIpAdressFromProxyTest.php
@@ -1,11 +1,14 @@
1<?php 1<?php
2 2
3require_once 'application/HttpUtils.php'; 3namespace Shaarli\Http;
4
5require_once 'application/http/HttpUtils.php';
4 6
5/** 7/**
6 * Unitary tests for getIpAddressFromProxy() 8 * Unitary tests for getIpAddressFromProxy()
7 */ 9 */
8class GetIpAdressFromProxyTest extends PHPUnit_Framework_TestCase { 10class GetIpAdressFromProxyTest extends \PHPUnit\Framework\TestCase
11{
9 12
10 /** 13 /**
11 * Test without proxy 14 * Test without proxy
diff --git a/tests/HttpUtils/IndexUrlTest.php b/tests/http/HttpUtils/IndexUrlTest.php
index 337dcab0..bcbe59cb 100644
--- a/tests/HttpUtils/IndexUrlTest.php
+++ b/tests/http/HttpUtils/IndexUrlTest.php
@@ -3,12 +3,14 @@
3 * HttpUtils' tests 3 * HttpUtils' tests
4 */ 4 */
5 5
6require_once 'application/HttpUtils.php'; 6namespace Shaarli\Http;
7
8require_once 'application/http/HttpUtils.php';
7 9
8/** 10/**
9 * Unitary tests for index_url() 11 * Unitary tests for index_url()
10 */ 12 */
11class IndexUrlTest extends PHPUnit_Framework_TestCase 13class IndexUrlTest extends \PHPUnit\Framework\TestCase
12{ 14{
13 /** 15 /**
14 * If on the main page, remove "index.php" from the URL resource 16 * If on the main page, remove "index.php" from the URL resource
diff --git a/tests/HttpUtils/IsHttpsTest.php b/tests/http/HttpUtils/IsHttpsTest.php
index 097f2bcf..348956c6 100644
--- a/tests/HttpUtils/IsHttpsTest.php
+++ b/tests/http/HttpUtils/IsHttpsTest.php
@@ -1,12 +1,15 @@
1<?php 1<?php
2 2
3namespace Shaarli\Http;
4
5require_once 'application/http/HttpUtils.php';
3 6
4/** 7/**
5 * Class IsHttpsTest 8 * Class IsHttpsTest
6 * 9 *
7 * Test class for is_https() function. 10 * Test class for is_https() function.
8 */ 11 */
9class IsHttpsTest extends PHPUnit_Framework_TestCase 12class IsHttpsTest extends \PHPUnit\Framework\TestCase
10{ 13{
11 14
12 /** 15 /**
diff --git a/tests/HttpUtils/PageUrlTest.php b/tests/http/HttpUtils/PageUrlTest.php
index 4dbbe9cf..f1991716 100644
--- a/tests/HttpUtils/PageUrlTest.php
+++ b/tests/http/HttpUtils/PageUrlTest.php
@@ -3,12 +3,14 @@
3 * HttpUtils' tests 3 * HttpUtils' tests
4 */ 4 */
5 5
6require_once 'application/HttpUtils.php'; 6namespace Shaarli\Http;
7
8require_once 'application/http/HttpUtils.php';
7 9
8/** 10/**
9 * Unitary tests for page_url() 11 * Unitary tests for page_url()
10 */ 12 */
11class PageUrlTest extends PHPUnit_Framework_TestCase 13class PageUrlTest extends \PHPUnit\Framework\TestCase
12{ 14{
13 /** 15 /**
14 * If on the main page, remove "index.php" from the URL resource 16 * If on the main page, remove "index.php" from the URL resource
diff --git a/tests/HttpUtils/ServerUrlTest.php b/tests/http/HttpUtils/ServerUrlTest.php
index 324b827a..9caf1049 100644
--- a/tests/HttpUtils/ServerUrlTest.php
+++ b/tests/http/HttpUtils/ServerUrlTest.php
@@ -3,12 +3,14 @@
3 * HttpUtils' tests 3 * HttpUtils' tests
4 */ 4 */
5 5
6require_once 'application/HttpUtils.php'; 6namespace Shaarli\Http;
7
8require_once 'application/http/HttpUtils.php';
7 9
8/** 10/**
9 * Unitary tests for server_url() 11 * Unitary tests for server_url()
10 */ 12 */
11class ServerUrlTest extends PHPUnit_Framework_TestCase 13class ServerUrlTest extends \PHPUnit\Framework\TestCase
12{ 14{
13 /** 15 /**
14 * Detect if the server uses SSL 16 * Detect if the server uses SSL
diff --git a/tests/Url/UrlTest.php b/tests/http/UrlTest.php
index aa2f2234..ae92f73a 100644
--- a/tests/Url/UrlTest.php
+++ b/tests/http/UrlTest.php
@@ -1,14 +1,14 @@
1<?php 1<?php
2/** 2/**
3 * Url's tests 3 * UrlUtils's tests
4 */ 4 */
5 5
6require_once 'application/Url.php'; 6namespace Shaarli\Http;
7 7
8/** 8/**
9 * Unitary tests for URL utilities 9 * Unitary tests for URL utilities
10 */ 10 */
11class UrlTest extends PHPUnit_Framework_TestCase 11class UrlTest extends \PHPUnit\Framework\TestCase
12{ 12{
13 // base URL for tests 13 // base URL for tests
14 protected static $baseUrl = 'http://domain.tld:3000'; 14 protected static $baseUrl = 'http://domain.tld:3000';
@@ -16,9 +16,9 @@ class UrlTest extends PHPUnit_Framework_TestCase
16 /** 16 /**
17 * Helper method 17 * Helper method
18 */ 18 */
19 private function assertUrlIsCleaned($query='', $fragment='') 19 private function assertUrlIsCleaned($query = '', $fragment = '')
20 { 20 {
21 $url = new Url(self::$baseUrl.$query.$fragment); 21 $url = new Url(self::$baseUrl . $query . $fragment);
22 $url->cleanup(); 22 $url->cleanup();
23 $this->assertEquals(self::$baseUrl, $url->toString()); 23 $this->assertEquals(self::$baseUrl, $url->toString());
24 } 24 }
@@ -38,7 +38,7 @@ class UrlTest extends PHPUnit_Framework_TestCase
38 public function testConstruct() 38 public function testConstruct()
39 { 39 {
40 $ref = 'http://username:password@hostname:9090/path' 40 $ref = 'http://username:password@hostname:9090/path'
41 .'?arg1=value1&arg2=value2#anchor'; 41 . '?arg1=value1&arg2=value2#anchor';
42 $url = new Url($ref); 42 $url = new Url($ref);
43 $this->assertEquals($ref, $url->toString()); 43 $this->assertEquals($ref, $url->toString());
44 } 44 }
@@ -52,7 +52,7 @@ class UrlTest extends PHPUnit_Framework_TestCase
52 $this->assertUrlIsCleaned(); 52 $this->assertUrlIsCleaned();
53 53
54 // URL with no annoying elements 54 // URL with no annoying elements
55 $ref = self::$baseUrl.'?p1=val1&p2=1234#edit'; 55 $ref = self::$baseUrl . '?p1=val1&p2=1234#edit';
56 $url = new Url($ref); 56 $url = new Url($ref);
57 $this->assertEquals($ref, $url->cleanup()); 57 $this->assertEquals($ref, $url->cleanup());
58 } 58 }
@@ -115,33 +115,33 @@ class UrlTest extends PHPUnit_Framework_TestCase
115 // ditch annoying query params and fragment, keep useful params 115 // ditch annoying query params and fragment, keep useful params
116 $url = new Url( 116 $url = new Url(
117 self::$baseUrl 117 self::$baseUrl
118 .'?fb=zomg&my=stuff&utm_medium=numnum&is=kept#tk.rss_all' 118 . '?fb=zomg&my=stuff&utm_medium=numnum&is=kept#tk.rss_all'
119 ); 119 );
120 $this->assertEquals(self::$baseUrl.'?my=stuff&is=kept', $url->cleanup()); 120 $this->assertEquals(self::$baseUrl . '?my=stuff&is=kept', $url->cleanup());
121 121
122 122
123 // ditch annoying query params, keep useful params and fragment 123 // ditch annoying query params, keep useful params and fragment
124 $url = new Url( 124 $url = new Url(
125 self::$baseUrl 125 self::$baseUrl
126 .'?fb=zomg&my=stuff&utm_medium=numnum&is=kept#again' 126 . '?fb=zomg&my=stuff&utm_medium=numnum&is=kept#again'
127 ); 127 );
128 $this->assertEquals( 128 $this->assertEquals(
129 self::$baseUrl.'?my=stuff&is=kept#again', 129 self::$baseUrl . '?my=stuff&is=kept#again',
130 $url->cleanup() 130 $url->cleanup()
131 ); 131 );
132 132
133 // test firefox reader url 133 // test firefox reader url
134 $url = new Url( 134 $url = new Url(
135 'about://reader?url=' . urlencode(self::$baseUrl .'?my=stuff&is=kept') 135 'about://reader?url=' . urlencode(self::$baseUrl . '?my=stuff&is=kept')
136 ); 136 );
137 $this->assertEquals(self::$baseUrl.'?my=stuff&is=kept', $url->cleanup()); 137 $this->assertEquals(self::$baseUrl . '?my=stuff&is=kept', $url->cleanup());
138
139 } 138 }
140 139
141 /** 140 /**
142 * Test default http scheme. 141 * Test default http scheme.
143 */ 142 */
144 public function testDefaultScheme() { 143 public function testDefaultScheme()
144 {
145 $url = new Url(self::$baseUrl); 145 $url = new Url(self::$baseUrl);
146 $this->assertEquals('http', $url->getScheme()); 146 $this->assertEquals('http', $url->getScheme());
147 $url = new Url('domain.tld'); 147 $url = new Url('domain.tld');
diff --git a/tests/Url/CleanupUrlTest.php b/tests/http/UrlUtils/CleanupUrlTest.php
index 1407d7d2..6c4d124b 100644
--- a/tests/Url/CleanupUrlTest.php
+++ b/tests/http/UrlUtils/CleanupUrlTest.php
@@ -3,9 +3,11 @@
3 * Unitary tests for cleanup_url() 3 * Unitary tests for cleanup_url()
4 */ 4 */
5 5
6require_once 'application/Url.php'; 6namespace Shaarli\Http;
7 7
8class CleanupUrlTest extends PHPUnit_Framework_TestCase 8require_once 'application/http/UrlUtils.php';
9
10class CleanupUrlTest extends \PHPUnit\Framework\TestCase
9{ 11{
10 /** 12 /**
11 * @var string reference URL 13 * @var string reference URL
@@ -107,4 +109,3 @@ class CleanupUrlTest extends PHPUnit_Framework_TestCase
107 ); 109 );
108 } 110 }
109} 111}
110
diff --git a/tests/Url/GetUrlSchemeTest.php b/tests/http/UrlUtils/GetUrlSchemeTest.php
index 72d80b30..2b97f7be 100644
--- a/tests/Url/GetUrlSchemeTest.php
+++ b/tests/http/UrlUtils/GetUrlSchemeTest.php
@@ -3,12 +3,14 @@
3 * Unitary tests for get_url_scheme() 3 * Unitary tests for get_url_scheme()
4 */ 4 */
5 5
6require_once 'application/Url.php'; 6namespace Shaarli\Http;
7 7
8class GetUrlSchemeTest extends PHPUnit_Framework_TestCase 8require_once 'application/http/UrlUtils.php';
9
10class GetUrlSchemeTest extends \PHPUnit\Framework\TestCase
9{ 11{
10 /** 12 /**
11 * Get empty scheme string for empty Url 13 * Get empty scheme string for empty UrlUtils
12 */ 14 */
13 public function testGetUrlSchemeEmpty() 15 public function testGetUrlSchemeEmpty()
14 { 16 {
@@ -16,7 +18,7 @@ class GetUrlSchemeTest extends PHPUnit_Framework_TestCase
16 } 18 }
17 19
18 /** 20 /**
19 * Get normal scheme of Url 21 * Get normal scheme of UrlUtils
20 */ 22 */
21 public function testGetUrlScheme() 23 public function testGetUrlScheme()
22 { 24 {
@@ -28,4 +30,3 @@ class GetUrlSchemeTest extends PHPUnit_Framework_TestCase
28 $this->assertEquals('git', get_url_scheme('git://domain.tld/push?pull=clone#checkout')); 30 $this->assertEquals('git', get_url_scheme('git://domain.tld/push?pull=clone#checkout'));
29 } 31 }
30} 32}
31
diff --git a/tests/Url/UnparseUrlTest.php b/tests/http/UrlUtils/UnparseUrlTest.php
index edde73e4..040d8c54 100644
--- a/tests/Url/UnparseUrlTest.php
+++ b/tests/http/UrlUtils/UnparseUrlTest.php
@@ -1,14 +1,16 @@
1<?php 1<?php
2/** 2/**
3 * Unpares Url's tests 3 * Unpares UrlUtils's tests
4 */ 4 */
5 5
6require_once 'application/Url.php'; 6namespace Shaarli\Http;
7
8require_once 'application/http/UrlUtils.php';
7 9
8/** 10/**
9 * Unitary tests for unparse_url() 11 * Unitary tests for unparse_url()
10 */ 12 */
11class UnparseUrlTest extends PHPUnit_Framework_TestCase 13class UnparseUrlTest extends \PHPUnit\Framework\TestCase
12{ 14{
13 /** 15 /**
14 * Thanks for building nothing 16 * Thanks for building nothing
@@ -28,4 +30,3 @@ class UnparseUrlTest extends PHPUnit_Framework_TestCase
28 $this->assertEquals($ref, unparse_url(parse_url($ref))); 30 $this->assertEquals($ref, unparse_url(parse_url($ref)));
29 } 31 }
30} 32}
31
diff --git a/tests/Url/WhitelistProtocolsTest.php b/tests/http/UrlUtils/WhitelistProtocolsTest.php
index a3156804..69512dbd 100644
--- a/tests/Url/WhitelistProtocolsTest.php
+++ b/tests/http/UrlUtils/WhitelistProtocolsTest.php
@@ -1,15 +1,15 @@
1<?php 1<?php
2 2
3require_once 'application/Url.php'; 3namespace Shaarli\Http;
4 4
5use Shaarli\Config\ConfigManager; 5require_once 'application/http/UrlUtils.php';
6 6
7/** 7/**
8 * Class WhitelistProtocolsTest 8 * Class WhitelistProtocolsTest
9 * 9 *
10 * Test whitelist_protocols() function of Url. 10 * Test whitelist_protocols() function of UrlUtils.
11 */ 11 */
12class WhitelistProtocolsTest extends PHPUnit_Framework_TestCase 12class WhitelistProtocolsTest extends \PHPUnit\Framework\TestCase
13{ 13{
14 /** 14 /**
15 * Test whitelist_protocols() on a note (relative URL). 15 * Test whitelist_protocols() on a note (relative URL).
diff --git a/tests/languages/de/UtilsDeTest.php b/tests/languages/de/UtilsDeTest.php
index 4569c923..588c9fd6 100644
--- a/tests/languages/de/UtilsDeTest.php
+++ b/tests/languages/de/UtilsDeTest.php
@@ -20,7 +20,7 @@ class UtilsDeTest extends UtilsTest
20 public function testDateFormatNoTime() 20 public function testDateFormatNoTime()
21 { 21 {
22 $date = DateTime::createFromFormat('Ymd_His', '20170101_101112'); 22 $date = DateTime::createFromFormat('Ymd_His', '20170101_101112');
23 $this->assertRegExp('/1\. Januar 2017/', format_date($date, false,true)); 23 $this->assertRegExp('/1\. Januar 2017/', format_date($date, false, true));
24 } 24 }
25 25
26 /** 26 /**
diff --git a/tests/languages/fr/LanguagesFrTest.php b/tests/languages/fr/LanguagesFrTest.php
index 0cf74891..b8b7ca3a 100644
--- a/tests/languages/fr/LanguagesFrTest.php
+++ b/tests/languages/fr/LanguagesFrTest.php
@@ -3,7 +3,6 @@
3 3
4namespace Shaarli; 4namespace Shaarli;
5 5
6
7use Shaarli\Config\ConfigManager; 6use Shaarli\Config\ConfigManager;
8 7
9/** 8/**
@@ -13,7 +12,7 @@ use Shaarli\Config\ConfigManager;
13 * 12 *
14 * @package Shaarli 13 * @package Shaarli
15 */ 14 */
16class LanguagesFrTest extends \PHPUnit_Framework_TestCase 15class LanguagesFrTest extends \PHPUnit\Framework\TestCase
17{ 16{
18 /** 17 /**
19 * @var string Config file path (without extension). 18 * @var string Config file path (without extension).
diff --git a/tests/NetscapeBookmarkUtils/BookmarkExportTest.php b/tests/netscape/BookmarkExportTest.php
index 6a47bbb9..6de9876d 100644
--- a/tests/NetscapeBookmarkUtils/BookmarkExportTest.php
+++ b/tests/netscape/BookmarkExportTest.php
@@ -1,11 +1,14 @@
1<?php 1<?php
2namespace Shaarli\Netscape;
2 3
3require_once 'application/NetscapeBookmarkUtils.php'; 4use Shaarli\Bookmark\LinkDB;
5
6require_once 'tests/utils/ReferenceLinkDB.php';
4 7
5/** 8/**
6 * Netscape bookmark export 9 * Netscape bookmark export
7 */ 10 */
8class BookmarkExportTest extends PHPUnit_Framework_TestCase 11class BookmarkExportTest extends \PHPUnit\Framework\TestCase
9{ 12{
10 /** 13 /**
11 * @var string datastore to test write operations 14 * @var string datastore to test write operations
@@ -13,7 +16,7 @@ class BookmarkExportTest extends PHPUnit_Framework_TestCase
13 protected static $testDatastore = 'sandbox/datastore.php'; 16 protected static $testDatastore = 'sandbox/datastore.php';
14 17
15 /** 18 /**
16 * @var ReferenceLinkDB instance. 19 * @var \ReferenceLinkDB instance.
17 */ 20 */
18 protected static $refDb = null; 21 protected static $refDb = null;
19 22
@@ -27,7 +30,7 @@ class BookmarkExportTest extends PHPUnit_Framework_TestCase
27 */ 30 */
28 public static function setUpBeforeClass() 31 public static function setUpBeforeClass()
29 { 32 {
30 self::$refDb = new ReferenceLinkDB(); 33 self::$refDb = new \ReferenceLinkDB();
31 self::$refDb->write(self::$testDatastore); 34 self::$refDb->write(self::$testDatastore);
32 self::$linkDb = new LinkDB(self::$testDatastore, true, false); 35 self::$linkDb = new LinkDB(self::$testDatastore, true, false);
33 } 36 }
@@ -110,7 +113,7 @@ class BookmarkExportTest extends PHPUnit_Framework_TestCase
110 $links = NetscapeBookmarkUtils::filterAndFormat(self::$linkDb, 'public', false, ''); 113 $links = NetscapeBookmarkUtils::filterAndFormat(self::$linkDb, 'public', false, '');
111 $this->assertEquals( 114 $this->assertEquals(
112 '?WDWyig', 115 '?WDWyig',
113 $links[0]['url'] 116 $links[2]['url']
114 ); 117 );
115 } 118 }
116 119
@@ -128,7 +131,7 @@ class BookmarkExportTest extends PHPUnit_Framework_TestCase
128 ); 131 );
129 $this->assertEquals( 132 $this->assertEquals(
130 $indexUrl . '?WDWyig', 133 $indexUrl . '?WDWyig',
131 $links[0]['url'] 134 $links[2]['url']
132 ); 135 );
133 } 136 }
134} 137}
diff --git a/tests/NetscapeBookmarkUtils/BookmarkImportTest.php b/tests/netscape/BookmarkImportTest.php
index f0a958cb..ccafc161 100644
--- a/tests/NetscapeBookmarkUtils/BookmarkImportTest.php
+++ b/tests/netscape/BookmarkImportTest.php
@@ -1,8 +1,10 @@
1<?php 1<?php
2namespace Shaarli\Netscape;
2 3
3require_once 'application/NetscapeBookmarkUtils.php'; 4use DateTime;
4 5use Shaarli\Bookmark\LinkDB;
5use Shaarli\Config\ConfigManager; 6use Shaarli\Config\ConfigManager;
7use Shaarli\History;
6 8
7/** 9/**
8 * Utility function to load a file's metadata in a $_FILES-like array 10 * Utility function to load a file's metadata in a $_FILES-like array
@@ -26,7 +28,7 @@ function file2array($filename)
26/** 28/**
27 * Netscape bookmark import 29 * Netscape bookmark import
28 */ 30 */
29class BookmarkImportTest extends PHPUnit_Framework_TestCase 31class BookmarkImportTest extends \PHPUnit\Framework\TestCase
30{ 32{
31 /** 33 /**
32 * @var string datastore to test write operations 34 * @var string datastore to test write operations
diff --git a/tests/NetscapeBookmarkUtils/input/empty.htm b/tests/netscape/input/empty.htm
index e69de29b..e69de29b 100644
--- a/tests/NetscapeBookmarkUtils/input/empty.htm
+++ b/tests/netscape/input/empty.htm
diff --git a/tests/NetscapeBookmarkUtils/input/internet_explorer_encoding.htm b/tests/netscape/input/internet_explorer_encoding.htm
index 18703cf6..18703cf6 100644
--- a/tests/NetscapeBookmarkUtils/input/internet_explorer_encoding.htm
+++ b/tests/netscape/input/internet_explorer_encoding.htm
diff --git a/tests/NetscapeBookmarkUtils/input/lowercase_doctype.htm b/tests/netscape/input/lowercase_doctype.htm
index 8911ad19..8911ad19 100644
--- a/tests/NetscapeBookmarkUtils/input/lowercase_doctype.htm
+++ b/tests/netscape/input/lowercase_doctype.htm
diff --git a/tests/NetscapeBookmarkUtils/input/netscape_basic.htm b/tests/netscape/input/netscape_basic.htm
index affe0cf8..affe0cf8 100644
--- a/tests/NetscapeBookmarkUtils/input/netscape_basic.htm
+++ b/tests/netscape/input/netscape_basic.htm
diff --git a/tests/NetscapeBookmarkUtils/input/netscape_nested.htm b/tests/netscape/input/netscape_nested.htm
index b486fe18..b486fe18 100644
--- a/tests/NetscapeBookmarkUtils/input/netscape_nested.htm
+++ b/tests/netscape/input/netscape_nested.htm
diff --git a/tests/NetscapeBookmarkUtils/input/no_doctype.htm b/tests/netscape/input/no_doctype.htm
index 766d398b..766d398b 100644
--- a/tests/NetscapeBookmarkUtils/input/no_doctype.htm
+++ b/tests/netscape/input/no_doctype.htm
diff --git a/tests/NetscapeBookmarkUtils/input/same_date.htm b/tests/netscape/input/same_date.htm
index 9d58a582..9d58a582 100644
--- a/tests/NetscapeBookmarkUtils/input/same_date.htm
+++ b/tests/netscape/input/same_date.htm
diff --git a/tests/plugins/PluginAddlinkTest.php b/tests/plugins/PluginAddlinkTest.php
index b6239e7f..d052f8b9 100644
--- a/tests/plugins/PluginAddlinkTest.php
+++ b/tests/plugins/PluginAddlinkTest.php
@@ -1,17 +1,15 @@
1<?php 1<?php
2namespace Shaarli\Plugin\Addlink;
2 3
3/** 4use Shaarli\Plugin\PluginManager;
4 * PluginPlayvideosTest.php 5use Shaarli\Router;
5 */
6 6
7require_once 'plugins/addlink_toolbar/addlink_toolbar.php'; 7require_once 'plugins/addlink_toolbar/addlink_toolbar.php';
8require_once 'application/Router.php';
9 8
10/** 9/**
11 * Class PluginAddlinkTest
12 * Unit test for the Addlink toolbar plugin 10 * Unit test for the Addlink toolbar plugin
13 */ 11 */
14class PluginAddlinkTest extends PHPUnit_Framework_TestCase 12class PluginAddlinkTest extends \PHPUnit\Framework\TestCase
15{ 13{
16 /** 14 /**
17 * Reset plugin path. 15 * Reset plugin path.
diff --git a/tests/plugins/PluginArchiveorgTest.php b/tests/plugins/PluginArchiveorgTest.php
index fecd5f2c..510288bb 100644
--- a/tests/plugins/PluginArchiveorgTest.php
+++ b/tests/plugins/PluginArchiveorgTest.php
@@ -1,16 +1,19 @@
1<?php 1<?php
2namespace Shaarli\Plugin\Archiveorg;
2 3
3/** 4/**
4 * PluginArchiveorgTest.php 5 * PluginArchiveorgTest.php
5 */ 6 */
6 7
8use Shaarli\Plugin\PluginManager;
9
7require_once 'plugins/archiveorg/archiveorg.php'; 10require_once 'plugins/archiveorg/archiveorg.php';
8 11
9/** 12/**
10 * Class PluginArchiveorgTest 13 * Class PluginArchiveorgTest
11 * Unit test for the archiveorg plugin 14 * Unit test for the archiveorg plugin
12 */ 15 */
13class PluginArchiveorgTest extends PHPUnit_Framework_TestCase 16class PluginArchiveorgTest extends \PHPUnit\Framework\TestCase
14{ 17{
15 /** 18 /**
16 * Reset plugin path 19 * Reset plugin path
diff --git a/tests/plugins/PluginIssoTest.php b/tests/plugins/PluginIssoTest.php
index 0ae73183..bdfab439 100644
--- a/tests/plugins/PluginIssoTest.php
+++ b/tests/plugins/PluginIssoTest.php
@@ -1,5 +1,10 @@
1<?php 1<?php
2namespace Shaarli\Plugin\Isso;
3
4use DateTime;
5use Shaarli\Bookmark\LinkDB;
2use Shaarli\Config\ConfigManager; 6use Shaarli\Config\ConfigManager;
7use Shaarli\Plugin\PluginManager;
3 8
4require_once 'plugins/isso/isso.php'; 9require_once 'plugins/isso/isso.php';
5 10
@@ -8,7 +13,7 @@ require_once 'plugins/isso/isso.php';
8 * 13 *
9 * Test the Isso plugin (comment system). 14 * Test the Isso plugin (comment system).
10 */ 15 */
11class PluginIssoTest extends PHPUnit_Framework_TestCase 16class PluginIssoTest extends \PHPUnit\Framework\TestCase
12{ 17{
13 /** 18 /**
14 * Reset plugin path 19 * Reset plugin path
@@ -21,7 +26,7 @@ class PluginIssoTest extends PHPUnit_Framework_TestCase
21 /** 26 /**
22 * Test Isso init without errors. 27 * Test Isso init without errors.
23 */ 28 */
24 public function testWallabagInitNoError() 29 public function testIssoInitNoError()
25 { 30 {
26 $conf = new ConfigManager(''); 31 $conf = new ConfigManager('');
27 $conf->set('plugins.ISSO_SERVER', 'value'); 32 $conf->set('plugins.ISSO_SERVER', 'value');
@@ -32,7 +37,7 @@ class PluginIssoTest extends PHPUnit_Framework_TestCase
32 /** 37 /**
33 * Test Isso init with errors. 38 * Test Isso init with errors.
34 */ 39 */
35 public function testWallabagInitError() 40 public function testIssoInitError()
36 { 41 {
37 $conf = new ConfigManager(''); 42 $conf = new ConfigManager('');
38 $errors = isso_init($conf); 43 $errors = isso_init($conf);
@@ -96,19 +101,22 @@ class PluginIssoTest extends PHPUnit_Framework_TestCase
96 array( 101 array(
97 'id' => 12, 102 'id' => 12,
98 'url' => $str, 103 'url' => $str,
104 'shorturl' => $short1 = 'abcd',
99 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $date1), 105 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $date1),
100 ), 106 ),
101 array( 107 array(
102 'id' => 13, 108 'id' => 13,
103 'url' => $str . '2', 109 'url' => $str . '2',
110 'shorturl' => $short2 = 'efgh',
104 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $date2), 111 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $date2),
105 ), 112 ),
106 ) 113 )
107 ); 114 );
108 115
109 $processed = hook_isso_render_linklist($data, $conf); 116 $processed = hook_isso_render_linklist($data, $conf);
110 // data shouldn't be altered 117 // link_plugin should be added for the icon
111 $this->assertEquals($data, $processed); 118 $this->assertContains('<a href="?'. $short1 .'#isso-thread">', $processed['links'][0]['link_plugin'][0]);
119 $this->assertContains('<a href="?'. $short2 .'#isso-thread">', $processed['links'][1]['link_plugin'][0]);
112 } 120 }
113 121
114 /** 122 /**
@@ -127,6 +135,7 @@ class PluginIssoTest extends PHPUnit_Framework_TestCase
127 array( 135 array(
128 'id' => 12, 136 'id' => 12,
129 'url' => $str, 137 'url' => $str,
138 'shorturl' => $short1 = 'abcd',
130 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $date), 139 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $date),
131 ) 140 )
132 ), 141 ),
@@ -135,8 +144,8 @@ class PluginIssoTest extends PHPUnit_Framework_TestCase
135 144
136 $processed = hook_isso_render_linklist($data, $conf); 145 $processed = hook_isso_render_linklist($data, $conf);
137 146
138 // data shouldn't be altered 147 // link_plugin should be added for the icon
139 $this->assertEquals($data, $processed); 148 $this->assertContains('<a href="?'. $short1 .'#isso-thread">', $processed['links'][0]['link_plugin'][0]);
140 } 149 }
141 150
142 /** 151 /**
diff --git a/tests/plugins/PluginMarkdownTest.php b/tests/plugins/PluginMarkdownTest.php
index b31e817f..5e7c02b0 100644
--- a/tests/plugins/PluginMarkdownTest.php
+++ b/tests/plugins/PluginMarkdownTest.php
@@ -1,10 +1,14 @@
1<?php 1<?php
2namespace Shaarli\Plugin\Markdown;
3
2use Shaarli\Config\ConfigManager; 4use Shaarli\Config\ConfigManager;
5use Shaarli\Plugin\PluginManager;
3 6
4/** 7/**
5 * PluginMarkdownTest.php 8 * PluginMarkdownTest.php
6 */ 9 */
7 10
11require_once 'application/bookmark/LinkUtils.php';
8require_once 'application/Utils.php'; 12require_once 'application/Utils.php';
9require_once 'plugins/markdown/markdown.php'; 13require_once 'plugins/markdown/markdown.php';
10 14
@@ -12,7 +16,7 @@ require_once 'plugins/markdown/markdown.php';
12 * Class PluginMarkdownTest 16 * Class PluginMarkdownTest
13 * Unit test for the Markdown plugin 17 * Unit test for the Markdown plugin
14 */ 18 */
15class PluginMarkdownTest extends PHPUnit_Framework_TestCase 19class PluginMarkdownTest extends \PHPUnit\Framework\TestCase
16{ 20{
17 /** 21 /**
18 * @var ConfigManager instance. 22 * @var ConfigManager instance.
@@ -47,6 +51,8 @@ class PluginMarkdownTest extends PHPUnit_Framework_TestCase
47 $data = hook_markdown_render_linklist($data, $this->conf); 51 $data = hook_markdown_render_linklist($data, $this->conf);
48 $this->assertNotFalse(strpos($data['links'][0]['description'], '<h1>')); 52 $this->assertNotFalse(strpos($data['links'][0]['description'], '<h1>'));
49 $this->assertNotFalse(strpos($data['links'][0]['description'], '<p>')); 53 $this->assertNotFalse(strpos($data['links'][0]['description'], '<p>'));
54
55 $this->assertEquals($markdown, $data['links'][0]['description_src']);
50 } 56 }
51 57
52 /** 58 /**
@@ -107,6 +113,18 @@ class PluginMarkdownTest extends PHPUnit_Framework_TestCase
107 } 113 }
108 114
109 /** 115 /**
116 * Test reverse_text2clickable().
117 */
118 public function testReverseText2clickableHashtags()
119 {
120 $text = file_get_contents('tests/plugins/resources/hashtags.raw');
121 $md = file_get_contents('tests/plugins/resources/hashtags.md');
122 $clickableText = hashtag_autolink($text);
123 $reversedText = reverse_text2clickable($clickableText);
124 $this->assertEquals($md, $reversedText);
125 }
126
127 /**
110 * Test reverse_nl2br(). 128 * Test reverse_nl2br().
111 */ 129 */
112 public function testReverseNl2br() 130 public function testReverseNl2br()
@@ -246,7 +264,7 @@ class PluginMarkdownTest extends PHPUnit_Framework_TestCase
246 $this->conf->get('security.markdown_escape', true), 264 $this->conf->get('security.markdown_escape', true),
247 $this->conf->get('security.allowed_protocols') 265 $this->conf->get('security.allowed_protocols')
248 ); 266 );
249 $this->assertEquals($html, $data); 267 $this->assertEquals($html, $data . PHP_EOL);
250 } 268 }
251 269
252 /** 270 /**
diff --git a/tests/plugins/PluginPlayvideosTest.php b/tests/plugins/PluginPlayvideosTest.php
index 29ad047f..51472617 100644
--- a/tests/plugins/PluginPlayvideosTest.php
+++ b/tests/plugins/PluginPlayvideosTest.php
@@ -1,17 +1,20 @@
1<?php 1<?php
2namespace Shaarli\Plugin\Playvideos;
2 3
3/** 4/**
4 * PluginPlayvideosTest.php 5 * PluginPlayvideosTest.php
5 */ 6 */
6 7
8use Shaarli\Plugin\PluginManager;
9use Shaarli\Router;
10
7require_once 'plugins/playvideos/playvideos.php'; 11require_once 'plugins/playvideos/playvideos.php';
8require_once 'application/Router.php';
9 12
10/** 13/**
11 * Class PluginPlayvideosTest 14 * Class PluginPlayvideosTest
12 * Unit test for the PlayVideos plugin 15 * Unit test for the PlayVideos plugin
13 */ 16 */
14class PluginPlayvideosTest extends PHPUnit_Framework_TestCase 17class PluginPlayvideosTest extends \PHPUnit\Framework\TestCase
15{ 18{
16 /** 19 /**
17 * Reset plugin path 20 * Reset plugin path
diff --git a/tests/plugins/PluginPubsubhubbubTest.php b/tests/plugins/PluginPubsubhubbubTest.php
index 69d00936..a7bd8fc9 100644
--- a/tests/plugins/PluginPubsubhubbubTest.php
+++ b/tests/plugins/PluginPubsubhubbubTest.php
@@ -1,14 +1,17 @@
1<?php 1<?php
2namespace Shaarli\Plugin\Pubsubhubbub;
3
2use Shaarli\Config\ConfigManager; 4use Shaarli\Config\ConfigManager;
5use Shaarli\Plugin\PluginManager;
6use Shaarli\Router;
3 7
4require_once 'plugins/pubsubhubbub/pubsubhubbub.php'; 8require_once 'plugins/pubsubhubbub/pubsubhubbub.php';
5require_once 'application/Router.php';
6 9
7/** 10/**
8 * Class PluginPubsubhubbubTest 11 * Class PluginPubsubhubbubTest
9 * Unit test for the pubsubhubbub plugin 12 * Unit test for the pubsubhubbub plugin
10 */ 13 */
11class PluginPubsubhubbubTest extends PHPUnit_Framework_TestCase 14class PluginPubsubhubbubTest extends \PHPUnit\Framework\TestCase
12{ 15{
13 /** 16 /**
14 * @var string Config file path (without extension). 17 * @var string Config file path (without extension).
diff --git a/tests/plugins/PluginQrcodeTest.php b/tests/plugins/PluginQrcodeTest.php
index ebfadddf..0c61e14a 100644
--- a/tests/plugins/PluginQrcodeTest.php
+++ b/tests/plugins/PluginQrcodeTest.php
@@ -1,21 +1,26 @@
1<?php 1<?php
2namespace Shaarli\Plugin\Qrcode;
3
2/** 4/**
3 * PluginQrcodeTest.php 5 * PluginQrcodeTest.php
4 */ 6 */
5 7
8use Shaarli\Plugin\PluginManager;
9use Shaarli\Router;
10
6require_once 'plugins/qrcode/qrcode.php'; 11require_once 'plugins/qrcode/qrcode.php';
7require_once 'application/Router.php';
8 12
9/** 13/**
10 * Class PluginQrcodeTest 14 * Class PluginQrcodeTest
11 * Unit test for the QR-Code plugin 15 * Unit test for the QR-Code plugin
12 */ 16 */
13class PluginQrcodeTest extends PHPUnit_Framework_TestCase 17class PluginQrcodeTest extends \PHPUnit\Framework\TestCase
14{ 18{
15 /** 19 /**
16 * Reset plugin path 20 * Reset plugin path
17 */ 21 */
18 public function setUp() { 22 public function setUp()
23 {
19 PluginManager::$PLUGINS_PATH = 'plugins'; 24 PluginManager::$PLUGINS_PATH = 'plugins';
20 } 25 }
21 26
diff --git a/tests/plugins/PluginWallabagTest.php b/tests/plugins/PluginWallabagTest.php
index 76b7887e..79751921 100644
--- a/tests/plugins/PluginWallabagTest.php
+++ b/tests/plugins/PluginWallabagTest.php
@@ -1,9 +1,8 @@
1<?php 1<?php
2use Shaarli\Config\ConfigManager; 2namespace Shaarli\Plugin\Wallabag;
3 3
4/** 4use Shaarli\Config\ConfigManager;
5 * PluginWallabagTest.php.php 5use Shaarli\Plugin\PluginManager;
6 */
7 6
8require_once 'plugins/wallabag/wallabag.php'; 7require_once 'plugins/wallabag/wallabag.php';
9 8
@@ -11,7 +10,7 @@ require_once 'plugins/wallabag/wallabag.php';
11 * Class PluginWallabagTest 10 * Class PluginWallabagTest
12 * Unit test for the Wallabag plugin 11 * Unit test for the Wallabag plugin
13 */ 12 */
14class PluginWallabagTest extends PHPUnit_Framework_TestCase 13class PluginWallabagTest extends \PHPUnit\Framework\TestCase
15{ 14{
16 /** 15 /**
17 * Reset plugin path 16 * Reset plugin path
diff --git a/tests/plugins/WallabagInstanceTest.php b/tests/plugins/WallabagInstanceTest.php
index 2c466871..a3cd9076 100644
--- a/tests/plugins/WallabagInstanceTest.php
+++ b/tests/plugins/WallabagInstanceTest.php
@@ -1,11 +1,10 @@
1<?php 1<?php
2 2namespace Shaarli\Plugin\Wallabag;
3require_once 'plugins/wallabag/WallabagInstance.php';
4 3
5/** 4/**
6 * Class WallabagInstanceTest 5 * Class WallabagInstanceTest
7 */ 6 */
8class WallabagInstanceTest extends PHPUnit_Framework_TestCase 7class WallabagInstanceTest extends \PHPUnit\Framework\TestCase
9{ 8{
10 /** 9 /**
11 * @var string wallabag url. 10 * @var string wallabag url.
diff --git a/tests/plugins/resources/hashtags.md b/tests/plugins/resources/hashtags.md
new file mode 100644
index 00000000..46326de3
--- /dev/null
+++ b/tests/plugins/resources/hashtags.md
@@ -0,0 +1,10 @@
1[#lol](?addtag=lol)
2
3 #test
4
5`#test2`
6
7```
8bla #bli blo
9#bla
10```
diff --git a/tests/plugins/resources/hashtags.raw b/tests/plugins/resources/hashtags.raw
new file mode 100644
index 00000000..9d2dc98a
--- /dev/null
+++ b/tests/plugins/resources/hashtags.raw
@@ -0,0 +1,10 @@
1#lol
2
3 #test
4
5`#test2`
6
7```
8bla #bli blo
9#bla
10```
diff --git a/tests/plugins/resources/markdown.html b/tests/plugins/resources/markdown.html
index f1df4e7e..c3460bf7 100644
--- a/tests/plugins/resources/markdown.html
+++ b/tests/plugins/resources/markdown.html
@@ -12,11 +12,11 @@
12<li><a href="http://link.tld">two</a></li> 12<li><a href="http://link.tld">two</a></li>
13<li><a href="http://link.tld">three</a></li> 13<li><a href="http://link.tld">three</a></li>
14<li><a href="http://link.tld">four</a></li> 14<li><a href="http://link.tld">four</a></li>
15<li>foo &lt;a href=&quot;?addtag=foobar&quot; title=&quot;Hashtag foobar&quot;&gt;#foobar&lt;/a&gt;</li> 15<li>foo <a href="?addtag=foobar">#foobar</a></li>
16</ol></li> 16</ol></li>
17</ol> 17</ol>
18<p>&lt;a href=&quot;?addtag=foobar&quot; title=&quot;Hashtag foobar&quot;&gt;#foobar&lt;/a&gt; foo <code>lol #foo</code> &lt;a href=&quot;?addtag=bar&quot; title=&quot;Hashtag bar&quot;&gt;#bar&lt;/a&gt;</p> 18<p><a href="?addtag=foobar">#foobar</a> foo <code>lol #foo</code> <a href="?addtag=bar">#bar</a></p>
19<p>fsdfs <a href="http://link.tld">http://link.tld</a> &lt;a href=&quot;?addtag=foobar&quot; title=&quot;Hashtag foobar&quot;&gt;#foobar&lt;/a&gt; <code>http://link.tld</code></p> 19<p>fsdfs <a href="http://link.tld">http://link.tld</a> <a href="?addtag=foobar">#foobar</a> <code>http://link.tld</code></p>
20<pre><code>http://link.tld #foobar 20<pre><code>http://link.tld #foobar
21next #foo</code></pre> 21next #foo</code></pre>
22<p>Block:</p> 22<p>Block:</p>
@@ -30,4 +30,4 @@ next #foo</code></pre>
30<a href="ftp://test.tld/path/?query=value#hash">link</a><br /> 30<a href="ftp://test.tld/path/?query=value#hash">link</a><br />
31<a href="magnet:test.tld/path/?query=value#hash">link</a><br /> 31<a href="magnet:test.tld/path/?query=value#hash">link</a><br />
32<a href="http://alert(&#039;xss&#039;)">link</a><br /> 32<a href="http://alert(&#039;xss&#039;)">link</a><br />
33<a href="http://test.tld/path/?query=value#hash">link</a></p></div> \ No newline at end of file 33<a href="http://test.tld/path/?query=value#hash">link</a></p></div>
diff --git a/tests/plugins/resources/markdown.md b/tests/plugins/resources/markdown.md
index b8ebd934..9350a8c7 100644
--- a/tests/plugins/resources/markdown.md
+++ b/tests/plugins/resources/markdown.md
@@ -31,4 +31,4 @@ lorem ipsum #foobar http://link.tld
31[link](ftp://test.tld/path/?query=value#hash) 31[link](ftp://test.tld/path/?query=value#hash)
32[link](magnet:test.tld/path/?query=value#hash) 32[link](magnet:test.tld/path/?query=value#hash)
33[link](javascript:alert('xss')) 33[link](javascript:alert('xss'))
34[link](other://test.tld/path/?query=value#hash) \ No newline at end of file 34[link](other://test.tld/path/?query=value#hash)
diff --git a/tests/ThemeUtilsTest.php b/tests/render/ThemeUtilsTest.php
index e44564be..58e3426b 100644
--- a/tests/ThemeUtilsTest.php
+++ b/tests/render/ThemeUtilsTest.php
@@ -1,13 +1,13 @@
1<?php 1<?php
2 2
3namespace Shaarli; 3namespace Shaarli\Render;
4 4
5/** 5/**
6 * Class ThemeUtilsTest 6 * Class ThemeUtilsTest
7 * 7 *
8 * @package Shaarli 8 * @package Shaarli
9 */ 9 */
10class ThemeUtilsTest extends \PHPUnit_Framework_TestCase 10class ThemeUtilsTest extends \PHPUnit\Framework\TestCase
11{ 11{
12 /** 12 /**
13 * Test getThemes() with existing theme directories. 13 * Test getThemes() with existing theme directories.
diff --git a/tests/security/LoginManagerTest.php b/tests/security/LoginManagerTest.php
index b9ab5ec4..7b0262b3 100644
--- a/tests/security/LoginManagerTest.php
+++ b/tests/security/LoginManagerTest.php
@@ -2,7 +2,8 @@
2namespace Shaarli\Security; 2namespace Shaarli\Security;
3 3
4require_once 'tests/utils/FakeConfigManager.php'; 4require_once 'tests/utils/FakeConfigManager.php';
5use \PHPUnit\Framework\TestCase; 5
6use PHPUnit\Framework\TestCase;
6 7
7/** 8/**
8 * Test coverage for LoginManager 9 * Test coverage for LoginManager
diff --git a/tests/security/SessionManagerTest.php b/tests/security/SessionManagerTest.php
index 9bd868f8..f264505e 100644
--- a/tests/security/SessionManagerTest.php
+++ b/tests/security/SessionManagerTest.php
@@ -5,9 +5,8 @@ require_once 'tests/utils/FakeConfigManager.php';
5require_once 'tests/utils/ReferenceSessionIdHashes.php'; 5require_once 'tests/utils/ReferenceSessionIdHashes.php';
6ReferenceSessionIdHashes::genAllHashes(); 6ReferenceSessionIdHashes::genAllHashes();
7 7
8use \Shaarli\Security\SessionManager; 8use PHPUnit\Framework\TestCase;
9use \PHPUnit\Framework\TestCase; 9use Shaarli\Security\SessionManager;
10
11 10
12/** 11/**
13 * Test coverage for SessionManager 12 * Test coverage for SessionManager
diff --git a/tests/Updater/DummyUpdater.php b/tests/updater/DummyUpdater.php
index a0be4413..9e866f1f 100644
--- a/tests/Updater/DummyUpdater.php
+++ b/tests/updater/DummyUpdater.php
@@ -1,10 +1,15 @@
1<?php 1<?php
2namespace Shaarli\Updater;
2 3
3require_once 'application/Updater.php'; 4use Exception;
5use ReflectionClass;
6use ReflectionMethod;
7use Shaarli\Bookmark\LinkDB;
8use Shaarli\Config\ConfigManager;
4 9
5/** 10/**
6 * Class DummyUpdater. 11 * Class DummyUpdater.
7 * Extends Updater to add update method designed for unit tests. 12 * Extends updater to add update method designed for unit tests.
8 */ 13 */
9class DummyUpdater extends Updater 14class DummyUpdater extends Updater
10{ 15{
@@ -31,7 +36,7 @@ class DummyUpdater extends Updater
31 * 36 *
32 * @return bool true. 37 * @return bool true.
33 */ 38 */
34 private final function updateMethodDummy1() 39 final private function updateMethodDummy1()
35 { 40 {
36 return true; 41 return true;
37 } 42 }
@@ -41,7 +46,7 @@ class DummyUpdater extends Updater
41 * 46 *
42 * @return bool true. 47 * @return bool true.
43 */ 48 */
44 private final function updateMethodDummy2() 49 final private function updateMethodDummy2()
45 { 50 {
46 return true; 51 return true;
47 } 52 }
@@ -51,7 +56,7 @@ class DummyUpdater extends Updater
51 * 56 *
52 * @return bool true. 57 * @return bool true.
53 */ 58 */
54 private final function updateMethodDummy3() 59 final private function updateMethodDummy3()
55 { 60 {
56 return true; 61 return true;
57 } 62 }
@@ -61,7 +66,7 @@ class DummyUpdater extends Updater
61 * 66 *
62 * @throws Exception error. 67 * @throws Exception error.
63 */ 68 */
64 private final function updateMethodException() 69 final private function updateMethodException()
65 { 70 {
66 throw new Exception('whatever'); 71 throw new Exception('whatever');
67 } 72 }
diff --git a/tests/Updater/UpdaterTest.php b/tests/updater/UpdaterTest.php
index 94e3c7d3..d7df5963 100644
--- a/tests/Updater/UpdaterTest.php
+++ b/tests/updater/UpdaterTest.php
@@ -1,16 +1,24 @@
1<?php 1<?php
2namespace Shaarli\Updater;
3
4use DateTime;
5use Exception;
6use Shaarli\Bookmark\LinkDB;
2use Shaarli\Config\ConfigJson; 7use Shaarli\Config\ConfigJson;
3use Shaarli\Config\ConfigManager; 8use Shaarli\Config\ConfigManager;
4use Shaarli\Config\ConfigPhp; 9use Shaarli\Config\ConfigPhp;
10use Shaarli\Thumbnailer;
5 11
6require_once 'tests/Updater/DummyUpdater.php'; 12require_once 'application/updater/UpdaterUtils.php';
13require_once 'tests/updater/DummyUpdater.php';
14require_once 'tests/utils/ReferenceLinkDB.php';
7require_once 'inc/rain.tpl.class.php'; 15require_once 'inc/rain.tpl.class.php';
8 16
9/** 17/**
10 * Class UpdaterTest. 18 * Class UpdaterTest.
11 * Runs unit tests against the Updater class. 19 * Runs unit tests against the updater class.
12 */ 20 */
13class UpdaterTest extends PHPUnit_Framework_TestCase 21class UpdaterTest extends \PHPUnit\Framework\TestCase
14{ 22{
15 /** 23 /**
16 * @var string Path to test datastore. 24 * @var string Path to test datastore.
@@ -20,7 +28,7 @@ class UpdaterTest extends PHPUnit_Framework_TestCase
20 /** 28 /**
21 * @var string Config file path (without extension). 29 * @var string Config file path (without extension).
22 */ 30 */
23 protected static $configFile = 'tests/utils/config/configJson'; 31 protected static $configFile = 'sandbox/config';
24 32
25 /** 33 /**
26 * @var ConfigManager 34 * @var ConfigManager
@@ -32,6 +40,7 @@ class UpdaterTest extends PHPUnit_Framework_TestCase
32 */ 40 */
33 public function setUp() 41 public function setUp()
34 { 42 {
43 copy('tests/utils/config/configJson.json.php', self::$configFile .'.json.php');
35 $this->conf = new ConfigManager(self::$configFile); 44 $this->conf = new ConfigManager(self::$configFile);
36 } 45 }
37 46
@@ -151,7 +160,7 @@ class UpdaterTest extends PHPUnit_Framework_TestCase
151 /** 160 /**
152 * Test Update failed. 161 * Test Update failed.
153 * 162 *
154 * @expectedException UpdaterException 163 * @expectedException \Exception
155 */ 164 */
156 public function testUpdateFailed() 165 public function testUpdateFailed()
157 { 166 {
@@ -177,17 +186,17 @@ class UpdaterTest extends PHPUnit_Framework_TestCase
177 $this->conf->setConfigFile('tests/utils/config/configPhp'); 186 $this->conf->setConfigFile('tests/utils/config/configPhp');
178 $this->conf->reset(); 187 $this->conf->reset();
179 188
180 $optionsFile = 'tests/Updater/options.php'; 189 $optionsFile = 'tests/updater/options.php';
181 $options = '<?php 190 $options = '<?php
182$GLOBALS[\'privateLinkByDefault\'] = true;'; 191$GLOBALS[\'privateLinkByDefault\'] = true;';
183 file_put_contents($optionsFile, $options); 192 file_put_contents($optionsFile, $options);
184 193
185 // tmp config file. 194 // tmp config file.
186 $this->conf->setConfigFile('tests/Updater/config'); 195 $this->conf->setConfigFile('tests/updater/config');
187 196
188 // merge configs 197 // merge configs
189 $updater = new Updater(array(), array(), $this->conf, true); 198 $updater = new Updater(array(), array(), $this->conf, true);
190 // This writes a new config file in tests/Updater/config.php 199 // This writes a new config file in tests/updater/config.php
191 $updater->updateMethodMergeDeprecatedConfigFile(); 200 $updater->updateMethodMergeDeprecatedConfigFile();
192 201
193 // make sure updated field is changed 202 // make sure updated field is changed
@@ -214,7 +223,7 @@ $GLOBALS[\'privateLinkByDefault\'] = true;';
214 */ 223 */
215 public function testRenameDashTags() 224 public function testRenameDashTags()
216 { 225 {
217 $refDB = new ReferenceLinkDB(); 226 $refDB = new \ReferenceLinkDB();
218 $refDB->write(self::$testDatastore); 227 $refDB->write(self::$testDatastore);
219 $linkDB = new LinkDB(self::$testDatastore, true, false); 228 $linkDB = new LinkDB(self::$testDatastore, true, false);
220 229
@@ -360,7 +369,7 @@ $GLOBALS[\'privateLinkByDefault\'] = true;';
360 'private' => true, 369 'private' => true,
361 ), 370 ),
362 ); 371 );
363 $refDB = new ReferenceLinkDB(); 372 $refDB = new \ReferenceLinkDB();
364 $refDB->setLinks($links); 373 $refDB->setLinks($links);
365 $refDB->write(self::$testDatastore); 374 $refDB->write(self::$testDatastore);
366 $linkDB = new LinkDB(self::$testDatastore, true, false); 375 $linkDB = new LinkDB(self::$testDatastore, true, false);
@@ -391,20 +400,32 @@ $GLOBALS[\'privateLinkByDefault\'] = true;';
391 $this->assertEquals('Naming conventions... #private', $linkDB[0]['description']); 400 $this->assertEquals('Naming conventions... #private', $linkDB[0]['description']);
392 $this->assertEquals('samba cartoon web', $linkDB[0]['tags']); 401 $this->assertEquals('samba cartoon web', $linkDB[0]['tags']);
393 $this->assertTrue($linkDB[0]['private']); 402 $this->assertTrue($linkDB[0]['private']);
394 $this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_142300'), $linkDB[0]['created']); 403 $this->assertEquals(
404 DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_142300'),
405 $linkDB[0]['created']
406 );
395 407
396 $this->assertTrue(isset($linkDB[1])); 408 $this->assertTrue(isset($linkDB[1]));
397 $this->assertFalse(isset($linkDB[1]['linkdate'])); 409 $this->assertFalse(isset($linkDB[1]['linkdate']));
398 $this->assertEquals(1, $linkDB[1]['id']); 410 $this->assertEquals(1, $linkDB[1]['id']);
399 $this->assertEquals('UserFriendly - Samba', $linkDB[1]['title']); 411 $this->assertEquals('UserFriendly - Samba', $linkDB[1]['title']);
400 $this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_172539'), $linkDB[1]['created']); 412 $this->assertEquals(
413 DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_172539'),
414 $linkDB[1]['created']
415 );
401 416
402 $this->assertTrue(isset($linkDB[2])); 417 $this->assertTrue(isset($linkDB[2]));
403 $this->assertFalse(isset($linkDB[2]['linkdate'])); 418 $this->assertFalse(isset($linkDB[2]['linkdate']));
404 $this->assertEquals(2, $linkDB[2]['id']); 419 $this->assertEquals(2, $linkDB[2]['id']);
405 $this->assertEquals('Geek and Poke', $linkDB[2]['title']); 420 $this->assertEquals('Geek and Poke', $linkDB[2]['title']);
406 $this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_182539'), $linkDB[2]['created']); 421 $this->assertEquals(
407 $this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_190301'), $linkDB[2]['updated']); 422 DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_182539'),
423 $linkDB[2]['created']
424 );
425 $this->assertEquals(
426 DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_190301'),
427 $linkDB[2]['updated']
428 );
408 } 429 }
409 430
410 /** 431 /**
@@ -412,7 +433,7 @@ $GLOBALS[\'privateLinkByDefault\'] = true;';
412 */ 433 */
413 public function testDatastoreIdsNothingToDo() 434 public function testDatastoreIdsNothingToDo()
414 { 435 {
415 $refDB = new ReferenceLinkDB(); 436 $refDB = new \ReferenceLinkDB();
416 $refDB->write(self::$testDatastore); 437 $refDB->write(self::$testDatastore);
417 $linkDB = new LinkDB(self::$testDatastore, true, false); 438 $linkDB = new LinkDB(self::$testDatastore, true, false);
418 439
@@ -684,4 +705,111 @@ $GLOBALS[\'privateLinkByDefault\'] = true;';
684 $this->assertEquals(4194304, $this->conf->get('general.download_max_size')); 705 $this->assertEquals(4194304, $this->conf->get('general.download_max_size'));
685 $this->assertEquals(3, $this->conf->get('general.download_timeout')); 706 $this->assertEquals(3, $this->conf->get('general.download_timeout'));
686 } 707 }
708
709 /**
710<<<<<<< HEAD
711 * Test updateMethodWebThumbnailer with thumbnails enabled.
712 */
713 public function testUpdateMethodWebThumbnailerEnabled()
714 {
715 $this->conf->remove('thumbnails');
716 $this->conf->set('thumbnail.enable_thumbnails', true);
717 $updater = new Updater([], [], $this->conf, true, $_SESSION);
718 $this->assertTrue($updater->updateMethodWebThumbnailer());
719 $this->assertFalse($this->conf->exists('thumbnail'));
720 $this->assertEquals(\Shaarli\Thumbnailer::MODE_ALL, $this->conf->get('thumbnails.mode'));
721 $this->assertEquals(125, $this->conf->get('thumbnails.width'));
722 $this->assertEquals(90, $this->conf->get('thumbnails.height'));
723 $this->assertContains('You have enabled or changed thumbnails', $_SESSION['warnings'][0]);
724 }
725
726 /**
727 * Test updateMethodWebThumbnailer with thumbnails disabled.
728 */
729 public function testUpdateMethodWebThumbnailerDisabled()
730 {
731 $this->conf->remove('thumbnails');
732 $this->conf->set('thumbnail.enable_thumbnails', false);
733 $updater = new Updater([], [], $this->conf, true, $_SESSION);
734 $this->assertTrue($updater->updateMethodWebThumbnailer());
735 $this->assertFalse($this->conf->exists('thumbnail'));
736 $this->assertEquals(Thumbnailer::MODE_NONE, $this->conf->get('thumbnails.mode'));
737 $this->assertEquals(125, $this->conf->get('thumbnails.width'));
738 $this->assertEquals(90, $this->conf->get('thumbnails.height'));
739 $this->assertTrue(empty($_SESSION['warnings']));
740 }
741
742 /**
743 * Test updateMethodWebThumbnailer with thumbnails disabled.
744 */
745 public function testUpdateMethodWebThumbnailerNothingToDo()
746 {
747 $updater = new Updater([], [], $this->conf, true, $_SESSION);
748 $this->assertTrue($updater->updateMethodWebThumbnailer());
749 $this->assertFalse($this->conf->exists('thumbnail'));
750 $this->assertEquals(Thumbnailer::MODE_COMMON, $this->conf->get('thumbnails.mode'));
751 $this->assertEquals(90, $this->conf->get('thumbnails.width'));
752 $this->assertEquals(53, $this->conf->get('thumbnails.height'));
753 $this->assertTrue(empty($_SESSION['warnings']));
754 }
755
756 /**
757 * Test updateMethodSetSticky().
758 */
759 public function testUpdateStickyValid()
760 {
761 $blank = [
762 'id' => 1,
763 'url' => 'z',
764 'title' => '',
765 'description' => '',
766 'tags' => '',
767 'created' => new DateTime(),
768 ];
769 $links = [
770 1 => ['id' => 1] + $blank,
771 2 => ['id' => 2] + $blank,
772 ];
773 $refDB = new \ReferenceLinkDB();
774 $refDB->setLinks($links);
775 $refDB->write(self::$testDatastore);
776 $linkDB = new LinkDB(self::$testDatastore, true, false);
777
778 $updater = new Updater(array(), $linkDB, $this->conf, true);
779 $this->assertTrue($updater->updateMethodSetSticky());
780
781 $linkDB = new LinkDB(self::$testDatastore, true, false);
782 foreach ($linkDB as $link) {
783 $this->assertFalse($link['sticky']);
784 }
785 }
786
787 /**
788 * Test updateMethodSetSticky().
789 */
790 public function testUpdateStickyNothingToDo()
791 {
792 $blank = [
793 'id' => 1,
794 'url' => 'z',
795 'title' => '',
796 'description' => '',
797 'tags' => '',
798 'created' => new DateTime(),
799 ];
800 $links = [
801 1 => ['id' => 1, 'sticky' => true] + $blank,
802 2 => ['id' => 2] + $blank,
803 ];
804 $refDB = new \ReferenceLinkDB();
805 $refDB->setLinks($links);
806 $refDB->write(self::$testDatastore);
807 $linkDB = new LinkDB(self::$testDatastore, true, false);
808
809 $updater = new Updater(array(), $linkDB, $this->conf, true);
810 $this->assertTrue($updater->updateMethodSetSticky());
811
812 $linkDB = new LinkDB(self::$testDatastore, true, false);
813 $this->assertTrue($linkDB[1]['sticky']);
814 }
687} 815}
diff --git a/tests/utils/CurlUtils.php b/tests/utils/CurlUtils.php
new file mode 100644
index 00000000..1cc4907e
--- /dev/null
+++ b/tests/utils/CurlUtils.php
@@ -0,0 +1,94 @@
1<?php
2/**
3 * Old-style mock for cURL, as PHPUnit doesn't allow to mock global functions
4 */
5
6/**
7 * Returns code 200 or html content type.
8 *
9 * @param resource $ch cURL resource
10 * @param int $type cURL info type
11 *
12 * @return int|string 200 or 'text/html'
13 */
14function ut_curl_getinfo_ok($ch, $type)
15{
16 switch ($type) {
17 case CURLINFO_RESPONSE_CODE:
18 return 200;
19 case CURLINFO_CONTENT_TYPE:
20 return 'text/html; charset=utf-8';
21 }
22}
23
24/**
25 * Returns code 200 or html content type without charset.
26 *
27 * @param resource $ch cURL resource
28 * @param int $type cURL info type
29 *
30 * @return int|string 200 or 'text/html'
31 */
32function ut_curl_getinfo_no_charset($ch, $type)
33{
34 switch ($type) {
35 case CURLINFO_RESPONSE_CODE:
36 return 200;
37 case CURLINFO_CONTENT_TYPE:
38 return 'text/html';
39 }
40}
41
42/**
43 * Invalid response code.
44 *
45 * @param resource $ch cURL resource
46 * @param int $type cURL info type
47 *
48 * @return int|string 404 or 'text/html'
49 */
50function ut_curl_getinfo_rc_ko($ch, $type)
51{
52 switch ($type) {
53 case CURLINFO_RESPONSE_CODE:
54 return 404;
55 case CURLINFO_CONTENT_TYPE:
56 return 'text/html; charset=utf-8';
57 }
58}
59
60/**
61 * Invalid content type.
62 *
63 * @param resource $ch cURL resource
64 * @param int $type cURL info type
65 *
66 * @return int|string 200 or 'text/plain'
67 */
68function ut_curl_getinfo_ct_ko($ch, $type)
69{
70 switch ($type) {
71 case CURLINFO_RESPONSE_CODE:
72 return 200;
73 case CURLINFO_CONTENT_TYPE:
74 return 'text/plain';
75 }
76}
77
78/**
79 * Invalid response code and content type.
80 *
81 * @param resource $ch cURL resource
82 * @param int $type cURL info type
83 *
84 * @return int|string 404 or 'text/plain'
85 */
86function ut_curl_getinfo_rs_ct_ko($ch, $type)
87{
88 switch ($type) {
89 case CURLINFO_RESPONSE_CODE:
90 return 404;
91 case CURLINFO_CONTENT_TYPE:
92 return 'text/plain';
93 }
94}
diff --git a/tests/utils/FakeApplicationUtils.php b/tests/utils/FakeApplicationUtils.php
new file mode 100644
index 00000000..de83d598
--- /dev/null
+++ b/tests/utils/FakeApplicationUtils.php
@@ -0,0 +1,19 @@
1<?php
2
3namespace Shaarli;
4
5/**
6 * Fake ApplicationUtils class to avoid HTTP requests
7 */
8class FakeApplicationUtils extends ApplicationUtils
9{
10 public static $VERSION_CODE = '';
11
12 /**
13 * Toggle HTTP requests, allow overriding the version code
14 */
15 public static function getVersion($url, $timeout = 0)
16 {
17 return self::$VERSION_CODE;
18 }
19}
diff --git a/tests/utils/ReferenceHistory.php b/tests/utils/ReferenceHistory.php
index 75cbb326..e411c417 100644
--- a/tests/utils/ReferenceHistory.php
+++ b/tests/utils/ReferenceHistory.php
@@ -1,5 +1,8 @@
1<?php 1<?php
2 2
3use Shaarli\FileUtils;
4use Shaarli\History;
5
3/** 6/**
4 * Populates a reference history 7 * Populates a reference history
5 */ 8 */
diff --git a/tests/utils/ReferenceLinkDB.php b/tests/utils/ReferenceLinkDB.php
index e887aa78..c12bcb67 100644
--- a/tests/utils/ReferenceLinkDB.php
+++ b/tests/utils/ReferenceLinkDB.php
@@ -1,10 +1,13 @@
1<?php 1<?php
2
3use Shaarli\Bookmark\LinkDB;
4
2/** 5/**
3 * Populates a reference datastore to test LinkDB 6 * Populates a reference datastore to test LinkDB
4 */ 7 */
5class ReferenceLinkDB 8class ReferenceLinkDB
6{ 9{
7 public static $NB_LINKS_TOTAL = 9; 10 public static $NB_LINKS_TOTAL = 11;
8 11
9 private $_links = array(); 12 private $_links = array();
10 private $_publicCount = 0; 13 private $_publicCount = 0;
@@ -16,6 +19,32 @@ class ReferenceLinkDB
16 public function __construct() 19 public function __construct()
17 { 20 {
18 $this->addLink( 21 $this->addLink(
22 11,
23 'Pined older',
24 '?PCRizQ',
25 'This is an older pinned link',
26 0,
27 DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20100309_101010'),
28 '',
29 null,
30 'PCRizQ',
31 true
32 );
33
34 $this->addLink(
35 10,
36 'Pined',
37 '?0gCTjQ',
38 'This is a pinned link',
39 0,
40 DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121207_152312'),
41 '',
42 null,
43 '0gCTjQ',
44 true
45 );
46
47 $this->addLink(
19 41, 48 41,
20 'Link title: @website', 49 'Link title: @website',
21 '?WDWyig', 50 '?WDWyig',
@@ -114,8 +143,18 @@ class ReferenceLinkDB
114 /** 143 /**
115 * Adds a new link 144 * Adds a new link
116 */ 145 */
117 protected function addLink($id, $title, $url, $description, $private, $date, $tags, $updated = '', $shorturl = '') 146 protected function addLink(
118 { 147 $id,
148 $title,
149 $url,
150 $description,
151 $private,
152 $date,
153 $tags,
154 $updated = '',
155 $shorturl = '',
156 $pinned = false
157 ) {
119 $link = array( 158 $link = array(
120 'id' => $id, 159 'id' => $id,
121 'title' => $title, 160 'title' => $title,
@@ -126,6 +165,7 @@ class ReferenceLinkDB
126 'created' => $date, 165 'created' => $date,
127 'updated' => $updated, 166 'updated' => $updated,
128 'shorturl' => $shorturl ? $shorturl : smallHash($date->format(LinkDB::LINK_DATE_FORMAT) . $id), 167 'shorturl' => $shorturl ? $shorturl : smallHash($date->format(LinkDB::LINK_DATE_FORMAT) . $id),
168 'sticky' => $pinned
129 ); 169 );
130 $this->_links[$id] = $link; 170 $this->_links[$id] = $link;
131 171
@@ -164,7 +204,11 @@ class ReferenceLinkDB
164 204
165 $order = $order === 'ASC' ? -1 : 1; 205 $order = $order === 'ASC' ? -1 : 1;
166 // Reorder array by dates. 206 // Reorder array by dates.
167 usort($this->_links, function($a, $b) use ($order) { 207 usort($this->_links, function ($a, $b) use ($order) {
208 if (isset($a['sticky']) && isset($b['sticky']) && $a['sticky'] !== $b['sticky']) {
209 return $a['sticky'] ? -1 : 1;
210 }
211
168 return $a['created'] < $b['created'] ? 1 * $order : -1 * $order; 212 return $a['created'] < $b['created'] ? 1 * $order : -1 * $order;
169 }); 213 });
170 } 214 }
diff --git a/tests/utils/config/configJson.json.php b/tests/utils/config/configJson.json.php
index 9c9288f3..1549ddfc 100644
--- a/tests/utils/config/configJson.json.php
+++ b/tests/utils/config/configJson.json.php
@@ -1,35 +1,84 @@
1<?php /* 1<?php /*
2{ 2{
3 "credentials": { 3 "credentials": {
4 "login":"root", 4 "login": "root",
5 "hash":"hash", 5 "hash": "hash",
6 "salt":"salt" 6 "salt": "salt"
7 }, 7 },
8 "security": { 8 "security": {
9 "session_protection_disabled":false 9 "session_protection_disabled": false,
10 "ban_after": 4,
11 "ban_duration": 1800,
12 "open_shaarli": false,
13 "allowed_protocols": [
14 "ftp",
15 "ftps",
16 "magnet"
17 ]
10 }, 18 },
11 "general": { 19 "general": {
12 "timezone":"Europe\/Paris", 20 "timezone": "Europe\/Paris",
13 "title": "Shaarli", 21 "title": "Shaarli",
14 "header_link": "?" 22 "header_link": "?",
23 "links_per_page": 20,
24 "enabled_plugins": [
25 "qrcode"
26 ],
27 "default_note_title": "Note: "
15 }, 28 },
16 "privacy": { 29 "privacy": {
17 "default_private_links":true 30 "default_private_links": true,
31 "hide_public_links": false,
32 "force_login": false,
33 "hide_timestamps": false,
34 "remember_user_default": true
18 }, 35 },
19 "redirector": { 36 "redirector": {
20 "url":"lala" 37 "url": "lala",
38 "encode_url": true
21 }, 39 },
22 "config": { 40 "config": {
23 "foo": "bar" 41 "foo": "bar"
24 }, 42 },
25 "resource": { 43 "resource": {
26 "datastore": "tests\/utils\/config\/datastore.php", 44 "datastore": "tests\/utils\/config\/datastore.php",
27 "data_dir": "sandbox/", 45 "data_dir": "sandbox\/",
28 "raintpl_tpl": "tpl/" 46 "raintpl_tpl": "tpl\/",
47 "config": "data\/config.php",
48 "ban_file": "data\/ipbans.php",
49 "updates": "data\/updates.txt",
50 "log": "data\/log.txt",
51 "update_check": "data\/lastupdatecheck.txt",
52 "history": "data\/history.php",
53 "theme": "default",
54 "raintpl_tmp": "tmp\/",
55 "thumbnails_cache": "cache",
56 "page_cache": "pagecache"
29 }, 57 },
30 "plugins": { 58 "plugins": {
31 "WALLABAG_VERSION": 1 59 "WALLABAG_VERSION": 1
60 },
61 "dev": {
62 "debug": true
63 },
64 "updates": {
65 "check_updates": false,
66 "check_updates_branch": "stable",
67 "check_updates_interval": 86400
68 },
69 "feed": {
70 "rss_permalinks": true,
71 "show_atom": true
72 },
73 "translation": {
74 "language": "auto",
75 "mode": "php",
76 "extensions": []
77 },
78 "thumbnails": {
79 "mode": "common",
80 "width": 90,
81 "height": 53
32 } 82 }
33} 83}
34*/ ?> 84*/ ?>
35
diff --git a/tests/utils/config/configPhp.php b/tests/utils/config/configPhp.php
index 0e034175..7dc81e22 100644
--- a/tests/utils/config/configPhp.php
+++ b/tests/utils/config/configPhp.php
@@ -1,4 +1,4 @@
1<?php 1<?php
2$GLOBALS['login'] = 'root'; 2$GLOBALS['login'] = 'root';
3$GLOBALS['hash'] = 'hash'; 3$GLOBALS['hash'] = 'hash';
4$GLOBALS['salt'] = 'salt'; 4$GLOBALS['salt'] = 'salt';
@@ -8,7 +8,7 @@ $GLOBALS['titleLink'] = 'titleLink';
8$GLOBALS['redirector'] = 'lala'; 8$GLOBALS['redirector'] = 'lala';
9$GLOBALS['disablesessionprotection'] = false; 9$GLOBALS['disablesessionprotection'] = false;
10$GLOBALS['privateLinkByDefault'] = false; 10$GLOBALS['privateLinkByDefault'] = false;
11$GLOBALS['config']['DATADIR'] = 'tests/Updater'; 11$GLOBALS['config']['DATADIR'] = 'tests/updater';
12$GLOBALS['config']['PAGECACHE'] = 'sandbox/pagecache'; 12$GLOBALS['config']['PAGECACHE'] = 'sandbox/pagecache';
13$GLOBALS['config']['DATASTORE'] = 'data/datastore.php'; 13$GLOBALS['config']['DATASTORE'] = 'data/datastore.php';
14$GLOBALS['plugins']['WALLABAG_VERSION'] = '1'; 14$GLOBALS['plugins']['WALLABAG_VERSION'] = '1';
diff --git a/tests/utils/config/wt.json b/tests/utils/config/wt.json
new file mode 100644
index 00000000..69ce49a6
--- /dev/null
+++ b/tests/utils/config/wt.json
@@ -0,0 +1,12 @@
1{
2 "settings": {
3 "default": {
4 "_comment": "infinite cache",
5 "cache_duration": -1,
6 "timeout": 10
7 },
8 "path": {
9 "cache": "sandbox/"
10 }
11 }
12} \ No newline at end of file
diff --git a/tpl/default/addlink.html b/tpl/default/addlink.html
index 2f956e06..55864a02 100644
--- a/tpl/default/addlink.html
+++ b/tpl/default/addlink.html
@@ -11,7 +11,8 @@
11 <h2 class="window-title">{"Shaare a new link"|t}</h2> 11 <h2 class="window-title">{"Shaare a new link"|t}</h2>
12 <form method="GET" action="#" name="addform" class="addform"> 12 <form method="GET" action="#" name="addform" class="addform">
13 <div> 13 <div>
14 <input type="text" name="post" placeholder="{'URL or leave empty to post a note'|t}" class="autofocus"> 14 <label for="shaare">{'URL or leave empty to post a note'|t}</label>
15 <input type="text" name="post" id="shaare" class="autofocus">
15 </div> 16 </div>
16 <div> 17 <div>
17 <input type="submit" value="{'Add link'|t}"> 18 <input type="submit" value="{'Add link'|t}">
diff --git a/tpl/default/configure.html b/tpl/default/configure.html
index a63c7ad3..42e32230 100644
--- a/tpl/default/configure.html
+++ b/tpl/default/configure.html
@@ -242,6 +242,37 @@
242 </div> 242 </div>
243 </div> 243 </div>
244 </div> 244 </div>
245 <div class="pure-g">
246 <div class="pure-u-lg-{$ratioLabel} pure-u-{$ratioLabelMobile}">
247 <div class="form-label">
248 <label for="enableThumbnails">
249 <span class="label-name">{'Enable thumbnails'|t}</span><br>
250 <span class="label-desc">
251 {if="! $gd_enabled"}
252 {'You need to enable the extension <code>php-gd</code> to use thumbnails.'|t}
253 {elseif="$thumbnails_enabled"}
254 <a href="?do=thumbs_update">{'Synchronize thumbnails'|t}</a>
255 {/if}
256 </span>
257 </label>
258 </div>
259 </div>
260 <div class="pure-u-lg-{$ratioInput} pure-u-{$ratioInputMobile}">
261 <div class="form-input">
262 <select name="enableThumbnails" id="enableThumbnails" class="align">
263 <option value="all" {if="$thumbnails_mode=='all'"}selected{/if}>
264 {'All'|t}
265 </option>
266 <option value="common" {if="$thumbnails_mode=='common'"}selected{/if}>
267 {'Only common media hosts'|t}
268 </option>
269 <option value="none" {if="$thumbnails_mode=='none'"}selected{/if}>
270 {'None'|t}
271 </option>
272 </select>
273 </div>
274 </div>
275 </div>
245 <div class="center"> 276 <div class="center">
246 <input type="submit" value="{'Save'|t}" name="save"> 277 <input type="submit" value="{'Save'|t}" name="save">
247 </div> 278 </div>
diff --git a/tpl/default/daily.html b/tpl/default/daily.html
index 29d845d5..2c409478 100644
--- a/tpl/default/daily.html
+++ b/tpl/default/daily.html
@@ -69,9 +69,12 @@
69 </a> 69 </a>
70 <a href="{$link.real_url}">{$link.title}</a> 70 <a href="{$link.real_url}">{$link.title}</a>
71 </div> 71 </div>
72 {$thumb=thumbnail($value.url)} 72 {if="$thumbnails_enabled && !empty($link.thumbnail)"}
73 {if="$thumb!=false"} 73 <div class="daily-entry-thumbnail">
74 <div class="daily-entry-thumbnail">{$thumb}</div> 74 <img data-src="{$link.thumbnail}#" class="b-lazy"
75 src=""
76 alt="thumbnail" width="{$thumbnails_width}" height="{$thumbnails_height}" />
77 </div>
75 {/if} 78 {/if}
76 <div class="daily-entry-description">{$link.formatedDescription}</div> 79 <div class="daily-entry-description">{$link.formatedDescription}</div>
77 {if="$link.tags"} 80 {if="$link.tags"}
@@ -83,7 +86,7 @@
83 {/loop} 86 {/loop}
84 </div> 87 </div>
85 {/if} 88 {/if}
86 <div class="dailyEntryFooter"> 89 <div class="dailyEntryFooter clear">
87 {loop="$link.link_plugin"} 90 {loop="$link.link_plugin"}
88 {$value} 91 {$value}
89 {/loop} 92 {/loop}
@@ -108,6 +111,7 @@
108 </div> 111 </div>
109</div> 112</div>
110{include="page.footer"} 113{include="page.footer"}
114<script src="js/thumbnails.min.js?v={$version_hash}"></script>
111</body> 115</body>
112</html> 116</html>
113 117
diff --git a/tpl/default/dailyrss.html b/tpl/default/dailyrss.html
index b14a3859..f589b06e 100644
--- a/tpl/default/dailyrss.html
+++ b/tpl/default/dailyrss.html
@@ -8,7 +8,7 @@
8 <h3><a href="{$value.url}">{$value.title}</a></h3> 8 <h3><a href="{$value.url}">{$value.title}</a></h3>
9 <small>{if="!$hide_timestamps"}{function="strftime('%c', $value.timestamp)"} - {/if}{if="$value.tags"}{$value.tags}{/if}<br> 9 <small>{if="!$hide_timestamps"}{function="strftime('%c', $value.timestamp)"} - {/if}{if="$value.tags"}{$value.tags}{/if}<br>
10 {$value.url}</small><br> 10 {$value.url}</small><br>
11 {if="$value.thumbnail"}{$value.thumbnail}{/if}<br> 11 {if="$value.thumbnail"}<img src="{$index_url}{$value.thumbnail}#" alt="thumbnail" />{/if}<br>
12 {if="$value.description"}{$value.formatedDescription}{/if} 12 {if="$value.description"}{$value.formatedDescription}{/if}
13 <br><br><hr> 13 <br><br><hr>
14 {/loop} 14 {/loop}
diff --git a/tpl/default/includes.html b/tpl/default/includes.html
index 5ccacaaf..6c30d1bf 100644
--- a/tpl/default/includes.html
+++ b/tpl/default/includes.html
@@ -15,3 +15,23 @@
15 <link type="text/css" rel="stylesheet" href="data/user.css#" /> 15 <link type="text/css" rel="stylesheet" href="data/user.css#" />
16{/if} 16{/if}
17<link rel="search" type="application/opensearchdescription+xml" href="?do=opensearch#" title="Shaarli search - {$shaarlititle}"/> 17<link rel="search" type="application/opensearchdescription+xml" href="?do=opensearch#" title="Shaarli search - {$shaarlititle}"/>
18{if="! empty($links) && count($links) === 1"}
19 {$link=reset($links)}
20 <meta property="og:title" content="{$link.title}" />
21 <meta property="og:type" content="article" />
22 <meta property="og:url" content="{$index_url}?{$link.shorturl}" />
23 {$ogDescription=isset($link.description_src) ? $link.description_src : $link.description}
24 <meta property="og:description" content="{function="substr(strip_tags($ogDescription), 0, 300)"}" />
25 {if="$link.thumbnail"}
26 <meta property="og:image" content="{$index_url}{$link.thumbnail}" />
27 {/if}
28 {if="!$hide_timestamps || $is_logged_in"}
29 <meta property="article:published_time" content="{$link.created->format(DateTime::ATOM)}" />
30 {if="$link.updated"}
31 <meta property="article:modified_time" content="{$link.updated->format(DateTime::ATOM)}" />
32 {/if}
33 {/if}
34 {loop="link.taglist"}
35 <meta property="article:tag" content="{$value}" />
36 {/loop}
37{/if}
diff --git a/tpl/default/linklist.html b/tpl/default/linklist.html
index 322cddd5..ed78f40a 100644
--- a/tpl/default/linklist.html
+++ b/tpl/default/linklist.html
@@ -125,15 +125,25 @@
125 {$strPermalink=t('Permalink')} 125 {$strPermalink=t('Permalink')}
126 {$strPermalinkLc=t('permalink')} 126 {$strPermalinkLc=t('permalink')}
127 {$strAddTag=t('Add tag')} 127 {$strAddTag=t('Add tag')}
128 {$strToggleSticky=t('Toggle sticky')}
129 {$strSticky=t('Sticky')}
128 {ignore}End of translations{/ignore} 130 {ignore}End of translations{/ignore}
129 {loop="links"} 131 {loop="links"}
130 <div class="anchor" id="{$value.shorturl}"></div> 132 <div class="anchor" id="{$value.shorturl}"></div>
131 133
132 <div class="linklist-item linklist-item{if="$value.class"} {$value.class}{/if}" data-id="{$value.id}"> 134 <div class="linklist-item linklist-item{if="$value.class"} {$value.class}{/if}" data-id="{$value.id}">
133 <div class="linklist-item-title"> 135 <div class="linklist-item-title">
134 {$thumb=thumbnail($value.url)} 136 {if="$thumbnails_enabled && !empty($value.thumbnail)"}
135 {if="$thumb!=false"} 137 <div class="linklist-item-thumbnail" style="width:{$thumbnails_width}px;height:{$thumbnails_height}px;">
136 <div class="linklist-item-thumbnail">{$thumb}</div> 138 <div class="thumbnail">
139 <a href="{$value.real_url}">
140 {ignore}RainTPL hack: put the 2 src on two different line to avoid path replace bug{/ignore}
141 <img data-src="{$value.thumbnail}#" class="b-lazy"
142 src=""
143 alt="thumbnail" width="{$thumbnails_width}" height="{$thumbnails_height}" />
144 </a>
145 </div>
146 </div>
137 {/if} 147 {/if}
138 148
139 {if="$is_logged_in"} 149 {if="$is_logged_in"}
@@ -182,7 +192,7 @@
182 {if="$is_logged_in"} 192 {if="$is_logged_in"}
183 <div class="linklist-item-infos-controls-group pure-u-0 pure-u-lg-visible"> 193 <div class="linklist-item-infos-controls-group pure-u-0 pure-u-lg-visible">
184 <span class="linklist-item-infos-controls-item ctrl-checkbox"> 194 <span class="linklist-item-infos-controls-item ctrl-checkbox">
185 <input type="checkbox" class="delete-checkbox" value="{$value.id}"> 195 <input type="checkbox" class="link-checkbox" value="{$value.id}">
186 </span> 196 </span>
187 <span class="linklist-item-infos-controls-item ctrl-edit"> 197 <span class="linklist-item-infos-controls-item ctrl-edit">
188 <a href="?edit_link={$value.id}" title="{$strEdit}"><i class="fa fa-pencil-square-o edit-link"></i></a> 198 <a href="?edit_link={$value.id}" title="{$strEdit}"><i class="fa fa-pencil-square-o edit-link"></i></a>
@@ -193,7 +203,23 @@
193 <i class="fa fa-trash"></i> 203 <i class="fa fa-trash"></i>
194 </a> 204 </a>
195 </span> 205 </span>
206 <span class="linklist-item-infos-controls-item ctrl-pin">
207 <a href="?do=pin&amp;id={$value.id}&amp;token={$token}"
208 title="{$strToggleSticky}" class="pin-link {if="$value.sticky"}pinned-link{/if} pure-u-0 pure-u-lg-visible">
209 <i class="fa fa-thumb-tack"></i>
210 </a>
211 </span>
196 </div> 212 </div>
213 {else}
214 {if="$value.sticky"}
215 <div class="linklist-item-infos-controls-group pure-u-0 pure-u-lg-visible">
216 <span class="linklist-item-infos-controls-item ctrl-pin">
217 <span title="{$strSticky}" class="pin-link pinned-link pure-u-0 pure-u-lg-visible">
218 <i class="fa fa-thumb-tack"></i>
219 </span>
220 </span>
221 </div>
222 {/if}
197 {/if} 223 {/if}
198 <a href="?{$value.shorturl}" title="{$strPermalink}"> 224 <a href="?{$value.shorturl}" title="{$strPermalink}">
199 {if="!$hide_timestamps || $is_logged_in"} 225 {if="!$hide_timestamps || $is_logged_in"}
@@ -268,5 +294,6 @@
268</div> 294</div>
269 295
270{include="page.footer"} 296{include="page.footer"}
297<script src="js/thumbnails.min.js?v={$version_hash}"></script>
271</body> 298</body>
272</html> 299</html>
diff --git a/tpl/default/linklist.paging.html b/tpl/default/linklist.paging.html
index 5309e348..fe665a84 100644
--- a/tpl/default/linklist.paging.html
+++ b/tpl/default/linklist.paging.html
@@ -16,6 +16,9 @@
16 <a href="?untaggedonly" title="{'Filter untagged links'|t}" 16 <a href="?untaggedonly" title="{'Filter untagged links'|t}"
17 class={if="$untaggedonly"}"filter-on"{else}"filter-off"{/if} 17 class={if="$untaggedonly"}"filter-on"{else}"filter-off"{/if}
18 ><i class="fa fa-tag"></i></a> 18 ><i class="fa fa-tag"></i></a>
19 <a href="#" title="{'Select all'|t}"
20 class="filter-off select-all-button"
21 ><i class="fa fa-check-square-o"></i></a>
19 <a href="#" class="filter-off fold-all pure-u-lg-0" title="{'Fold all'|t}"> 22 <a href="#" class="filter-off fold-all pure-u-lg-0" title="{'Fold all'|t}">
20 <i class="fa fa-chevron-up"></i> 23 <i class="fa fa-chevron-up"></i>
21 </a> 24 </a>
diff --git a/tpl/default/page.header.html b/tpl/default/page.header.html
index 82568d63..4f6dd4d8 100644
--- a/tpl/default/page.header.html
+++ b/tpl/default/page.header.html
@@ -2,7 +2,7 @@
2 <div class="pure-u-lg-0 pure-u-1"> 2 <div class="pure-u-lg-0 pure-u-1">
3 <div class="pure-menu"> 3 <div class="pure-menu">
4 <a href="{$titleLink}" class="pure-menu-link shaarli-title" id="shaarli-title-mobile"> 4 <a href="{$titleLink}" class="pure-menu-link shaarli-title" id="shaarli-title-mobile">
5 <img src="img/icon.png" width="16" height="16" class="head-logo" alt="logo" /> 5 <img src="img/icon.png" width="16" height="16" class="head-logo" alt="" />
6 {$shaarlititle} 6 {$shaarlititle}
7 </a> 7 </a>
8 <a href="#" class="menu-toggle" id="menu-toggle"><s class="bar"></s><s class="bar"></s></a> 8 <a href="#" class="menu-toggle" id="menu-toggle"><s class="bar"></s><s class="bar"></s></a>
@@ -13,7 +13,7 @@
13 <ul class="pure-menu-list pure-u-lg-5-6 pure-u-1"> 13 <ul class="pure-menu-list pure-u-lg-5-6 pure-u-1">
14 <li class="pure-menu-item pure-u-0 pure-u-lg-visible"> 14 <li class="pure-menu-item pure-u-0 pure-u-lg-visible">
15 <a href="{$titleLink}" class="pure-menu-link shaarli-title" id="shaarli-title-desktop"> 15 <a href="{$titleLink}" class="pure-menu-link shaarli-title" id="shaarli-title-desktop">
16 <img src="img/icon.png" width="16" height="16" class="head-logo" alt="logo" /> 16 <img src="img/icon.png" width="16" height="16" class="head-logo" alt="" />
17 {$shaarlititle} 17 {$shaarlititle}
18 </a> 18 </a>
19 </li> 19 </li>
@@ -30,9 +30,11 @@
30 <li class="pure-menu-item" id="shaarli-menu-tags"> 30 <li class="pure-menu-item" id="shaarli-menu-tags">
31 <a href="?do=tagcloud" class="pure-menu-link">{'Tag cloud'|t}</a> 31 <a href="?do=tagcloud" class="pure-menu-link">{'Tag cloud'|t}</a>
32 </li> 32 </li>
33 <li class="pure-menu-item" id="shaarli-menu-picwall"> 33 {if="$thumbnails_enabled"}
34 <a href="?do=picwall{$searchcrits}" class="pure-menu-link">{'Picture wall'|t}</a> 34 <li class="pure-menu-item" id="shaarli-menu-picwall">
35 </li> 35 <a href="?do=picwall{$searchcrits}" class="pure-menu-link">{'Picture wall'|t}</a>
36 </li>
37 {/if}
36 <li class="pure-menu-item" id="shaarli-menu-daily"> 38 <li class="pure-menu-item" id="shaarli-menu-daily">
37 <a href="?do=daily" class="pure-menu-link">{'Daily'|t}</a> 39 <a href="?do=daily" class="pure-menu-link">{'Daily'|t}</a>
38 </li> 40 </li>
@@ -116,7 +118,7 @@
116 <div id="actions" class="subheader-form"> 118 <div id="actions" class="subheader-form">
117 <div class="pure-g"> 119 <div class="pure-g">
118 <div class="pure-u-1"> 120 <div class="pure-u-1">
119 <a href="" id="actions-delete" class="button">Delete</a> 121 <a href="" id="actions-delete" class="button">{'Delete'|t}</a>
120 </div> 122 </div>
121 </div> 123 </div>
122 </div> 124 </div>
@@ -169,4 +171,18 @@
169 </div> 171 </div>
170{/if} 172{/if}
171 173
174{if="!empty($global_warnings) && $is_logged_in"}
175 <div class="pure-g pure-alert pure-alert-warning pure-alert-closable" id="shaarli-warnings-alert">
176 <div class="pure-u-2-24"></div>
177 <div class="pure-u-20-24">
178 {loop="global_warnings"}
179 <p>{$value}</p>
180 {/loop}
181 </div>
182 <div class="pure-u-2-24">
183 <i class="fa fa-times pure-alert-close"></i>
184 </div>
185 </div>
186{/if}
187
172 <div class="clear"></div> 188 <div class="clear"></div>
diff --git a/tpl/default/picwall.html b/tpl/default/picwall.html
index 2f7e03dc..4c325487 100644
--- a/tpl/default/picwall.html
+++ b/tpl/default/picwall.html
@@ -5,41 +5,61 @@
5</head> 5</head>
6<body> 6<body>
7{include="page.header"} 7{include="page.header"}
8{if="!$thumbnails_enabled"}
9<div class="pure-g pure-alert pure-alert-warning page-single-alert">
10 <div class="pure-u-1 center">
11 {'Picture wall unavailable (thumbnails are disabled).'|t}
12 </div>
13</div>
14{else}
15 {if="count($linksToDisplay)===0 && $is_logged_in"}
16 <div class="pure-g pure-alert pure-alert-warning page-single-alert">
17 <div class="pure-u-1 center">
18 {'There is no cached thumbnail. Try to <a href="?do=thumbs_update">synchronize them</a>.'|t}
19 </div>
20 </div>
21 {/if}
8 22
9<div class="pure-g"> 23 <div class="pure-g">
10 <div class="pure-u-lg-1-6 pure-u-1-24"></div> 24 <div class="pure-u-lg-1-6 pure-u-1-24"></div>
11 <div class="pure-u-lg-2-3 pure-u-22-24 page-form page-visitor"> 25 <div class="pure-u-lg-2-3 pure-u-22-24 page-form page-visitor">
12 {$countPics=count($linksToDisplay)} 26 {$countPics=count($linksToDisplay)}
13 <h2 class="window-title">{'Picture Wall'|t} - {$countPics} {'pics'|t}</h2> 27 <h2 class="window-title">{'Picture Wall'|t} - {$countPics} {'pics'|t}</h2>
14 28
15 <div id="plugin_zone_start_picwall" class="plugin_zone"> 29 <div id="plugin_zone_start_picwall" class="plugin_zone">
16 {loop="$plugin_start_zone"} 30 {loop="$plugin_start_zone"}
17 {$value} 31 {$value}
18 {/loop} 32 {/loop}
19 </div> 33 </div>
20 34
21 <div id="picwall_container" class="picwall-container"> 35 <div id="picwall-container" class="picwall-container">
22 {loop="$linksToDisplay"} 36 {loop="$linksToDisplay"}
23 <div class="picwall-pictureframe"> 37 <div class="picwall-pictureframe">
24 {$value.thumbnail}<a href="{$value.real_url}"><span class="info">{$value.title}</span></a> 38 {ignore}RainTPL hack: put the 2 src on two different line to avoid path replace bug{/ignore}
25 {loop="$value.picwall_plugin"} 39 <img data-src="{$value.thumbnail}#" class="b-lazy"
26 {$value} 40 src=""
27 {/loop} 41 alt="thumbnail" width="{$thumbnails_width}" height="{$thumbnails_height}" />
28 </div> 42 <a href="{$value.real_url}"><span class="info">{$value.title}</span></a>
29 {/loop} 43 {loop="$value.picwall_plugin"}
30 <div class="clear"></div> 44 {$value}
31 </div> 45 {/loop}
46 </div>
47 {/loop}
48 <div class="clear"></div>
49 </div>
32 50
33 <div id="plugin_zone_end_picwall" class="plugin_zone"> 51 <div id="plugin_zone_end_picwall" class="plugin_zone">
34 {loop="$plugin_end_zone"} 52 {loop="$plugin_end_zone"}
35 {$value} 53 {$value}
36 {/loop} 54 {/loop}
55 </div>
37 </div> 56 </div>
57 <div class="pure-u-lg-1-6 pure-u-1-24"></div>
38 </div> 58 </div>
39</div> 59{/if}
40 60
41{include="page.footer"} 61{include="page.footer"}
42<script src="js/picwall.min.js?v={$version_hash}"></script> 62<script src="js/thumbnails.min.js?v={$version_hash}"></script>
43</body> 63</body>
44</html> 64</html>
45 65
diff --git a/tpl/default/thumbnails.html b/tpl/default/thumbnails.html
new file mode 100644
index 00000000..a8cf904e
--- /dev/null
+++ b/tpl/default/thumbnails.html
@@ -0,0 +1,48 @@
1<!DOCTYPE html>
2<html>
3<head>
4 {include="includes"}
5</head>
6<body>
7{include="page.header"}
8
9<div class="pure-g thumbnails-page-container">
10 <div class="pure-u-lg-1-3 pure-u-1-24"></div>
11 <div class="pure-u-lg-1-3 pure-u-22-24 page-form page-form-light">
12 <h2 class="window-title">{'Thumbnails update'|t}</h2>
13
14 <div class="pure-g">
15 <div class="pure-u-lg-1-3 pure-u-1-24"></div>
16 <div class="pure-u-lg-1-3 pure-u-22-24">
17 <div class="thumbnail-placeholder" style="width: {$thumbnails_width}px; height: {$thumbnails_height}px;"></div>
18 </div>
19 </div>
20
21 <div class="pure-g">
22 <div class="pure-u-1-12"></div>
23 <div class="pure-u-5-6">
24 <div class="thumbnail-link-title"></div>
25
26 <div class="progressbar">
27 <div></div>
28 </div>
29 </div>
30 </div>
31
32 <div class="pure-g">
33 <div class="pure-u-lg-1-3 pure-u-1-24"></div>
34 <div class="pure-u-lg-1-3 pure-u-22-24">
35 <div class="progress-counter">
36 <span class="progress-current">0</span> / <span class="progress-total">{$ids|count}</span>
37 </div>
38 </div>
39 </div>
40
41 <input type="hidden" name="ids" value="{function="implode($ids, ',')"}" />
42 </div>
43</div>
44
45{include="page.footer"}
46<script src="js/thumbnails_update.min.js?v={$version_hash}"></script>
47</body>
48</html>
diff --git a/tpl/default/tools.html b/tpl/default/tools.html
index ece66884..c9ce1eaf 100644
--- a/tpl/default/tools.html
+++ b/tpl/default/tools.html
@@ -45,6 +45,14 @@
45 </a> 45 </a>
46 </div> 46 </div>
47 47
48 {if="$thumbnails_enabled"}
49 <div class="tools-item">
50 <a href="?do=thumbs_update" title="{'Synchronize all link thumbnails'|t}">
51 <span class="pure-button pure-u-lg-2-3 pure-u-3-4">{'Synchronize thumbnails'|t}</span>
52 </a>
53 </div>
54 {/if}
55
48 {loop="$tools_plugin"} 56 {loop="$tools_plugin"}
49 <div class="tools-item"> 57 <div class="tools-item">
50 {$value} 58 {$value}
@@ -112,47 +120,27 @@
112 </div> 120 </div>
113</div> 121</div>
114 122
115{if="$sslenabled"}
116 <div class="pure-g">
117 <div class="pure-u-lg-1-3 pure-u-1-24"></div>
118 <div class="pure-u-lg-1-3 pure-u-22-24 page-form page-form-light">
119 <h2 class="window-title">Firefox Social API</h2>
120 <p>{'You need to browse your Shaarli over <strong>HTTPS</strong> to use this functionality.'|t}</p>
121
122 <div class="tools-item">
123 <a title="{'Click on this button to add Shaarli to the 'Share this page' button in Firefox"
124 id="ff-social-button">
125 <span class="pure-button pure-u-lg-2-3 pure-u-3-4">✚ {'Add to'|t} Firefox Social</span>
126 </a>
127 </div>
128 </div>
129 </div>
130{/if}
131
132<div class="pure-g"> 123<div class="pure-g">
133 <div class="pure-u-lg-1-3 pure-u-1-24"></div> 124 <div class="pure-u-lg-1-3 pure-u-1-24"></div>
134 <div class="pure-u-lg-1-3 pure-u-22-24 page-form page-form-light"> 125 <div class="pure-u-lg-1-3 pure-u-22-24 page-form page-form-light">
135 <h2 class="window-title">{'3rd party'|t}</h2> 126 <h2 class="window-title">{'3rd party'|t}</h2>
136 <div class="tools-item"> 127 <div class="tools-item">
137 <a href="https://addons.mozilla.org/fr/firefox/addon/shaarli/" title="Firefox {'Plugin'|t}"> 128 <a href="https://addons.mozilla.org/fr/firefox/addon/shaarli/">
138 <span class="pure-button pure-u-lg-2-3 pure-u-3-4">Firefox {'plugin'|t}</span> 129 <span class="pure-button pure-u-lg-2-3 pure-u-3-4">Firefox {'plugin'|t}</span>
139 </a> 130 </a>
140 </div> 131 </div>
141 <div class="tools-item"> 132 <div class="tools-item">
142 <a href="https://chrome.google.com/webstore/detail/shiny-shaarli/hajdfkmbdmadjmmpkkbbcnllepomekin" 133 <a href="https://chrome.google.com/webstore/detail/shiny-shaarli/hajdfkmbdmadjmmpkkbbcnllepomekin">
143 title="Chrome {'Plugin'|t}">
144 <span class="pure-button pure-u-lg-2-3 pure-u-3-4">Chrome {'plugin'|t}</span> 134 <span class="pure-button pure-u-lg-2-3 pure-u-3-4">Chrome {'plugin'|t}</span>
145 </a> 135 </a>
146 </div> 136 </div>
147 <div class="tools-item"> 137 <div class="tools-item">
148 <a href="https://play.google.com/store/apps/details?id=com.dimtion.shaarlier&hl=fr" 138 <a href="https://play.google.com/store/apps/details?id=com.dimtion.shaarlier&hl=fr">
149 title="Android Shaarlier">
150 <span class="pure-button pure-u-lg-2-3 pure-u-3-4">Android Shaarlier</span> 139 <span class="pure-button pure-u-lg-2-3 pure-u-3-4">Android Shaarlier</span>
151 </a> 140 </a>
152 </div> 141 </div>
153 <div class="tools-item"> 142 <div class="tools-item">
154 <a href="https://stakali.toneiv.eu/" 143 <a href="https://stakali.toneiv.eu/">
155 title="Android Stakali">
156 <span class="pure-button pure-u-lg-2-3 pure-u-3-4">Android Stakali</span> 144 <span class="pure-button pure-u-lg-2-3 pure-u-3-4">Android Stakali</span>
157 </a> 145 </a>
158 </div> 146 </div>
diff --git a/tpl/vintage/configure.html b/tpl/vintage/configure.html
index 479284eb..f1892fa1 100644
--- a/tpl/vintage/configure.html
+++ b/tpl/vintage/configure.html
@@ -59,14 +59,6 @@
59 </tr> 59 </tr>
60 60
61 <tr> 61 <tr>
62 <td><b>Redirector</b></td>
63 <td>
64 <input type="text" name="redirector" id="redirector" size="50" value="{$redirector}"><br>
65 (e.g. <i>http://anonym.to/?</i> will mask the HTTP_REFERER)
66 </td>
67 </tr>
68
69 <tr>
70 <td><b>Security:</b></td> 62 <td><b>Security:</b></td>
71 <td> 63 <td>
72 <input type="checkbox" name="disablesessionprotection" id="disablesessionprotection" 64 <input type="checkbox" name="disablesessionprotection" id="disablesessionprotection"
@@ -128,6 +120,29 @@
128 <input type="text" name="apiSecret" id="apiSecret" size="50" value="{$api_secret}" /> 120 <input type="text" name="apiSecret" id="apiSecret" size="50" value="{$api_secret}" />
129 </td> 121 </td>
130 </tr> 122 </tr>
123 <tr>
124 <td valign="top"><b>Enable thumbnails</b></td>
125 <td>
126 <select name="enableThumbnails" id="enableThumbnails" class="align">
127 <option value="all" {if="$thumbnails_mode=='all'"}selected{/if}>
128 {'All'|t}
129 </option>
130 <option value="common" {if="$thumbnails_mode=='common'"}selected{/if}>
131 {'Only common media hosts'|t}
132 </option>
133 <option value="none" {if="$thumbnails_mode=='none'"}selected{/if}>
134 {'None'|t}
135 </option>
136 </select>
137 <label for="enableThumbnails">
138 {if="! $gd_enabled"}
139 {'You need to enable the extension <code>php-gd</code> to use thumbnails.'|t}
140 {elseif="$thumbnails_enabled"}
141 <a href="?do=thumbs_update">{'Synchonize thumbnails'|t}</a>
142 {/if}
143 </label>
144 </td>
145 </tr>
131 146
132 <tr> 147 <tr>
133 <td></td> 148 <td></td>
diff --git a/tpl/vintage/daily.html b/tpl/vintage/daily.html
index ede35910..71d84475 100644
--- a/tpl/vintage/daily.html
+++ b/tpl/vintage/daily.html
@@ -68,8 +68,12 @@
68 <div class="dailyEntryTitle"> 68 <div class="dailyEntryTitle">
69 <a href="{$link.real_url}">{$link.title}</a> 69 <a href="{$link.real_url}">{$link.title}</a>
70 </div> 70 </div>
71 {if="$link.thumbnail"} 71 {if="$thumbnails_enabled && !empty($link.thumbnail)"}
72 <div class="dailyEntryThumbnail">{$link.thumbnail}</div> 72 <div class="dailyEntryThumbnail">
73 <img data-src="{$link.thumbnail}#" class="b-lazy"
74 src=""
75 alt="thumbnail" width="{$thumbnails_width}" height="{$thumbnails_height}" />
76 </div>
73 {/if} 77 {/if}
74 <div class="dailyEntryDescription">{$link.formatedDescription}</div> 78 <div class="dailyEntryDescription">{$link.formatedDescription}</div>
75 79
@@ -97,5 +101,6 @@
97 <div id="closing"><img src="img/squiggle_closing.png" width="66" height="61" alt="-"></div> 101 <div id="closing"><img src="img/squiggle_closing.png" width="66" height="61" alt="-"></div>
98</div> 102</div>
99{include="page.footer"} 103{include="page.footer"}
104<script src="js/thumbnails.min.js?v={$version_hash}"></script>
100</body> 105</body>
101</html> 106</html>
diff --git a/tpl/vintage/dailyrss.html b/tpl/vintage/dailyrss.html
index ddbd6c5e..f589b06e 100644
--- a/tpl/vintage/dailyrss.html
+++ b/tpl/vintage/dailyrss.html
@@ -4,11 +4,11 @@
4 <link>{$absurl}</link> 4 <link>{$absurl}</link>
5 <pubDate>{$rssdate}</pubDate> 5 <pubDate>{$rssdate}</pubDate>
6 <description><![CDATA[ 6 <description><![CDATA[
7 {loop="$links"} 7 {loop="links"}
8 <h3><a href="{$value.url}">{$value.title}</a></h3> 8 <h3><a href="{$value.url}">{$value.title}</a></h3>
9 <small>{if="!$hide_timestamps"}{function="strftime('%c', $value.timestamp)"} - {/if}{if="$value.tags"}{$value.tags}{/if}<br> 9 <small>{if="!$hide_timestamps"}{function="strftime('%c', $value.timestamp)"} - {/if}{if="$value.tags"}{$value.tags}{/if}<br>
10 {$value.url}</small><br> 10 {$value.url}</small><br>
11 {if="$value.thumbnail"}{$value.thumbnail}{/if}<br> 11 {if="$value.thumbnail"}<img src="{$index_url}{$value.thumbnail}#" alt="thumbnail" />{/if}<br>
12 {if="$value.description"}{$value.formatedDescription}{/if} 12 {if="$value.description"}{$value.formatedDescription}{/if}
13 <br><br><hr> 13 <br><br><hr>
14 {/loop} 14 {/loop}
diff --git a/tpl/vintage/includes.html b/tpl/vintage/includes.html
index 410b466b..2efb6b10 100644
--- a/tpl/vintage/includes.html
+++ b/tpl/vintage/includes.html
@@ -12,3 +12,23 @@
12{/loop} 12{/loop}
13{if="is_file('data/user.css')"}<link type="text/css" rel="stylesheet" href="data/user.css#" />{/if} 13{if="is_file('data/user.css')"}<link type="text/css" rel="stylesheet" href="data/user.css#" />{/if}
14<link rel="search" type="application/opensearchdescription+xml" href="?do=opensearch#" title="Shaarli search - {$shaarlititle|htmlspecialchars}"/> 14<link rel="search" type="application/opensearchdescription+xml" href="?do=opensearch#" title="Shaarli search - {$shaarlititle|htmlspecialchars}"/>
15{if="! empty($links) && count($links) === 1"}
16 {$link=reset($links)}
17 <meta property="og:title" content="{$link.title}" />
18 <meta property="og:type" content="article" />
19 <meta property="og:url" content="{$index_url}?{$link.shorturl}" />
20 {$ogDescription=isset($link.description_src) ? $link.description_src : $link.description}
21 <meta property="og:description" content="{function="mb_substr(strip_tags($ogDescription), 0, 300)"}" />
22 {if="$link.thumbnail"}
23 <meta property="og:image" content="{$index_url}{$link.thumbnail}" />
24 {/if}
25 {if="!$hide_timestamps || $is_logged_in"}
26 <meta property="article:published_time" content="{$link.created->format(DateTime::ATOM)}" />
27 {if="$link.updated"}
28 <meta property="article:modified_time" content="{$link.updated->format(DateTime::ATOM)}" />
29 {/if}
30 {/if}
31 {loop="link.taglist"}
32 <meta property="article:tag" content="{$value}" />
33 {/loop}
34{/if}
diff --git a/tpl/vintage/linklist.html b/tpl/vintage/linklist.html
index 1ca51be3..dcb14e90 100644
--- a/tpl/vintage/linklist.html
+++ b/tpl/vintage/linklist.html
@@ -80,7 +80,16 @@
80 {loop="$links"} 80 {loop="$links"}
81 <li{if="$value.class"} class="{$value.class}"{/if}> 81 <li{if="$value.class"} class="{$value.class}"{/if}>
82 <a id="{$value.shorturl}"></a> 82 <a id="{$value.shorturl}"></a>
83 <div class="thumbnail">{$value.url|thumbnail}</div> 83 {if="$thumbnails_enabled && !empty($value.thumbnail)"}
84 <div class="thumbnail">
85 <a href="{$value.real_url}">
86 {ignore}RainTPL hack: put the 2 src on two different line to avoid path replace bug{/ignore}
87 <img data-src="{$value.thumbnail}#" class="b-lazy"
88 src=""
89 alt="thumbnail" width="{$thumbnails_width}" height="{$thumbnails_height}" />
90 </a>
91 </div>
92 {/if}
84 <div class="linkcontainer"> 93 <div class="linkcontainer">
85 {if="$is_logged_in"} 94 {if="$is_logged_in"}
86 <div class="linkeditbuttons"> 95 <div class="linkeditbuttons">
@@ -145,6 +154,7 @@
145</div> 154</div>
146 155
147 {include="page.footer"} 156 {include="page.footer"}
157<script src="js/thumbnails.min.js"></script>
148 158
149</body> 159</body>
150</html> 160</html>
diff --git a/tpl/vintage/picwall.html b/tpl/vintage/picwall.html
index 29688914..b3a16791 100644
--- a/tpl/vintage/picwall.html
+++ b/tpl/vintage/picwall.html
@@ -15,7 +15,11 @@
15 <div id="picwall_container"> 15 <div id="picwall_container">
16 {loop="$linksToDisplay"} 16 {loop="$linksToDisplay"}
17 <div class="picwall_pictureframe"> 17 <div class="picwall_pictureframe">
18 {$value.thumbnail}<a href="{$value.real_url}"><span class="info">{$value.title}</span></a> 18 {ignore}RainTPL hack: put the 2 src on two different line to avoid path replace bug{/ignore}
19 <img data-src="{$value.thumbnail}#" class="b-lazy"
20 src=""
21 alt="thumbnail" width="{$thumbnails_width}" height="{$thumbnails_height}" />
22 <a href="{$value.real_url}"><span class="info">{$value.title}</span></a>
19 {loop="$value.picwall_plugin"} 23 {loop="$value.picwall_plugin"}
20 {$value} 24 {$value}
21 {/loop} 25 {/loop}
@@ -34,6 +38,6 @@
34 38
35{include="page.footer"} 39{include="page.footer"}
36 40
37<script src="js/picwall.min.js"></script> 41<script src="js/thumbnails.min.js"></script>
38</body> 42</body>
39</html> 43</html>
diff --git a/tpl/vintage/thumbnails.html b/tpl/vintage/thumbnails.html
new file mode 100644
index 00000000..79aebf8d
--- /dev/null
+++ b/tpl/vintage/thumbnails.html
@@ -0,0 +1,28 @@
1<!DOCTYPE html>
2<html>
3<head>{include="includes"}</head>
4<body>
5<div id="pageheader">
6{include="page.header"}
7</div>
8
9<div class="center thumbnails-update-container">
10 <div class="thumbnail-placeholder" style="width: {$thumbnails_width}px; height: {$thumbnails_height}px;"></div>
11
12 <div class="thumbnail-link-title"></div>
13
14 <div class="progressbar">
15 <div></div>
16 </div>
17
18 <div class="progress-counter">
19 <span class="progress-current">0</span> / <span class="progress-total">{$ids|count}</span>
20 </div>
21</div>
22
23<input type="hidden" name="ids" value="{function="implode($ids, ',')"}" />
24
25{include="page.footer"}
26<script src="js/thumbnails_update.min.js?v={$version_hash}"></script>
27</body>
28</html>
diff --git a/tpl/vintage/tools.html b/tpl/vintage/tools.html
index 3194844a..1cef726e 100644
--- a/tpl/vintage/tools.html
+++ b/tpl/vintage/tools.html
@@ -55,13 +55,6 @@
55 </span> 55 </span>
56 </a><br><br> 56 </a><br><br>
57 57
58 {if="$sslenabled"}
59 <a class="smallbutton" onclick="activateFirefoxSocial(this)"><b>✚Add to Firefox social</b></a>
60 <a href="#">
61 <span>&#x21D0; Click on this button to add Shaarli to the "Share this page" button in Firefox.</span>
62 </a><br><br>
63 {/if}
64
65 {loop="$tools_plugin"} 58 {loop="$tools_plugin"}
66 {$value} 59 {$value}
67 {/loop} 60 {/loop}
@@ -69,31 +62,6 @@
69 <div class="clear"></div> 62 <div class="clear"></div>
70 63
71 <script> 64 <script>
72 {if="$sslenabled"}
73 function activateFirefoxSocial(node) {
74 var loc = location.href;
75 var baseURL = loc.substring(0, loc.lastIndexOf("/"));
76
77 // Keeping the data separated (ie. not in the DOM) so that it's maintainable and diffable.
78 var data = {
79 name: "{$shaarlititle}",
80 description: "The personal, minimalist, super-fast, no-database delicious clone.",
81 author: "Shaarli",
82 version: "1.0.0",
83
84 iconURL: baseURL + "/img/favicon.ico",
85 icon32URL: baseURL + "/img/favicon.ico",
86 icon64URL: baseURL + "/img/favicon.ico",
87
88 shareURL: baseURL + "{noparse}?post=%{url}&title=%{title}&description=%{text}&source=firefoxsocialapi{/noparse}",
89 homepageURL: baseURL
90 };
91 node.setAttribute("data-service", JSON.stringify(data));
92
93 var activate = new CustomEvent("ActivateSocialFeature");
94 node.dispatchEvent(activate);
95 }
96 {/if}
97 function alertBookmarklet() { 65 function alertBookmarklet() {
98 alert('Drag this link to your bookmarks toolbar, or right-click it and choose Bookmark This Link...'); 66 alert('Drag this link to your bookmarks toolbar, or right-click it and choose Bookmark This Link...');
99 return false; 67 return false;
diff --git a/webpack.config.js b/webpack.config.js
index 94b7aa70..ed548c73 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -23,7 +23,8 @@ const extractCssVintage = new ExtractTextPlugin({
23module.exports = [ 23module.exports = [
24 { 24 {
25 entry: { 25 entry: {
26 picwall: './assets/common/js/picwall.js', 26 thumbnails: './assets/common/js/thumbnails.js',
27 thumbnails_update: './assets/common/js/thumbnails-update.js',
27 pluginsadmin: './assets/default/js/plugins-admin.js', 28 pluginsadmin: './assets/default/js/plugins-admin.js',
28 shaarli: [ 29 shaarli: [
29 './assets/default/js/base.js', 30 './assets/default/js/base.js',
@@ -96,7 +97,8 @@ module.exports = [
96 './assets/vintage/css/reset.css', 97 './assets/vintage/css/reset.css',
97 './assets/vintage/css/shaarli.css', 98 './assets/vintage/css/shaarli.css',
98 ].concat(glob.sync('./assets/vintage/img/*')), 99 ].concat(glob.sync('./assets/vintage/img/*')),
99 picwall: './assets/common/js/picwall.js', 100 thumbnails: './assets/common/js/thumbnails.js',
101 thumbnails_update: './assets/common/js/thumbnails-update.js',
100 }, 102 },
101 output: { 103 output: {
102 filename: '[name].min.js', 104 filename: '[name].min.js',