Release v0.10.2
-----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEWe5LuNiFNDXAgI8BOzJIyqqwgW4FAltu2K0ACgkQOzJIyqqw gW4fpg/+MfXOj0d4sR3QMgafKHAVtiVmrOydVwqFOjVe+BOjpxHliDtOqo++cquF umZ3Ln9D8R3Wocw5cdLOn0/WbS+xMqyLmJWkGb1sn2NS8NWINXwCw6A8QuYF789p NmfmhYnXCW8OoX3TWLT1RR/0UL0V2ZJsMYTWfngxM4EVSPkaZc8C7Sjqs4hL/m4w uPcHgsCziZjxtGmdFUKLEEoFwxWKIvZTnYNTVegD6uHGb7jNZGXz1kizIpsXHC3p LffOpx1bamTbPoNhM0PyTTRAvNF3qBWsWY58Haldv9R60KsxJ7Fxc9PXgt02vUfw dGLMuMEd98iArAlovqQCy4/f+r1JhqJUsfj2IDJM5QSTiYWJL6zShHyHoWWifU07 4eZCOZce3kskRd8kl/0TRqdFKBB1RxIDtEZRBbmIhnkUt8E2fZG+7XPvZiIeTZSc 9/8y0KAxBnOuWtLny/NE6kS6yNUSlYooTU6kkDZ4lvsJFpHlQKwwuoFDcsD6oY0k yZ7lCAJht645pEQAF9b7WaB+qiE55suWFUcXM/uHqRdvl+DhEJE5C/BD7orW2mi9 CVfjmqEz5UFkalG7cZpb/NB1Rtcm1YT1NlY0h1YMRtT6ZILkgUNZLWb6tuZ2e0CS sPvVzSNzyJmw5vRC6MtwAJzRRkqa1cFJ58vnQB1n8N65n/mAFNA= =+fbH -----END PGP SIGNATURE----- Merge tag 'v0.10.2' into myShaarli_commu Release v0.10.2
This commit is contained in:
commit
94716fb2ba
220 changed files with 14665 additions and 12494 deletions
12
.dev/.eslintrc.js
Normal file
12
.dev/.eslintrc.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
module.exports = {
|
||||
"extends": "airbnb-base",
|
||||
"env": {
|
||||
"browser": true,
|
||||
},
|
||||
"rules": {
|
||||
"no-param-reassign": 0, // manipulate DOM style properties
|
||||
"no-restricted-globals": 0, // currently Shaarli uses alert/confirm, could be be improved later
|
||||
"no-alert": 0, // currently Shaarli uses alert/confirm, could be be improved later
|
||||
"no-cond-assign": [2, "except-parens"], // assignment in while loops is readable and avoid assignment duplication
|
||||
}
|
||||
};
|
15
.dev/.sasslintrc
Normal file
15
.dev/.sasslintrc
Normal file
|
@ -0,0 +1,15 @@
|
|||
options:
|
||||
max-warnings: 0
|
||||
rules:
|
||||
property-sort-order:
|
||||
- 1
|
||||
-
|
||||
order: 'concentric'
|
||||
no-important:
|
||||
- 0
|
||||
no-vendor-prefixes:
|
||||
- 0 # this will be fixed with v2: see https://github.com/sasstools/sass-lint/pull/1137
|
||||
nesting-depth:
|
||||
- 1
|
||||
-
|
||||
max-depth: 4
|
|
@ -4,6 +4,9 @@
|
|||
.github
|
||||
tests
|
||||
|
||||
# Docker Compose resources
|
||||
docker-compose.yml
|
||||
|
||||
# Shaarli runtime resources
|
||||
cache/*
|
||||
data/*
|
||||
|
@ -35,10 +38,17 @@ phpmd.html
|
|||
# User plugin configuration
|
||||
plugins/*/config.php
|
||||
|
||||
# HTML documentation
|
||||
doc/html/
|
||||
|
||||
# 3rd party themes
|
||||
tpl/*
|
||||
!tpl/default
|
||||
!tpl/vintage
|
||||
|
||||
# Front end
|
||||
node_modules
|
||||
tpl/default/js
|
||||
tpl/default/css
|
||||
tpl/default/fonts
|
||||
tpl/default/img
|
||||
tpl/vintage/js
|
||||
tpl/vintage/css
|
||||
tpl/vintage/img
|
||||
|
|
|
@ -10,7 +10,7 @@ trim_trailing_whitespace = true
|
|||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.{htaccess,html,xml}]
|
||||
[*.{htaccess,html,scss,js,json,xml,yml}]
|
||||
indent_size = 2
|
||||
|
||||
[*.php]
|
||||
|
|
33
.gitattributes
vendored
33
.gitattributes
vendored
|
@ -25,18 +25,21 @@ Dockerfile text
|
|||
*.mo binary
|
||||
|
||||
# Exclude from Git archives
|
||||
.editorconfig export-ignore
|
||||
.gitattributes export-ignore
|
||||
.github export-ignore
|
||||
.gitignore export-ignore
|
||||
.travis.yml export-ignore
|
||||
doc/**/*.json export-ignore
|
||||
doc/**/*.md export-ignore
|
||||
.docker/ export-ignore
|
||||
.dockerignore export-ignore
|
||||
Dockerfile* export-ignore
|
||||
Doxyfile export-ignore
|
||||
Makefile export-ignore
|
||||
mkdocs.yml export-ignore
|
||||
phpunit.xml export-ignore
|
||||
tests/ export-ignore
|
||||
.editorconfig export-ignore
|
||||
.dev export-ignore
|
||||
.gitattributes export-ignore
|
||||
.github export-ignore
|
||||
.gitignore export-ignore
|
||||
.travis.yml export-ignore
|
||||
doc/**/*.json export-ignore
|
||||
doc/**/*.md export-ignore
|
||||
.docker/ export-ignore
|
||||
.dockerignore export-ignore
|
||||
docker-compose.* export-ignore
|
||||
Dockerfile* export-ignore
|
||||
Doxyfile export-ignore
|
||||
Makefile export-ignore
|
||||
node_modules/ export-ignore
|
||||
mkdocs.yml export-ignore
|
||||
phpunit.xml export-ignore
|
||||
tests/ export-ignore
|
||||
|
|
10
.gitignore
vendored
10
.gitignore
vendored
|
@ -40,3 +40,13 @@ tpl/*
|
|||
|
||||
contact.php
|
||||
formStyle.css
|
||||
|
||||
# Front end
|
||||
node_modules
|
||||
tpl/default/js
|
||||
tpl/default/css
|
||||
tpl/default/fonts
|
||||
tpl/default/img
|
||||
tpl/vintage/js
|
||||
tpl/vintage/css
|
||||
tpl/vintage/img
|
||||
|
|
32
.htaccess
32
.htaccess
|
@ -14,3 +14,35 @@ RewriteRule .* - [e=HTTP_AUTHORIZATION:%1]
|
|||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^ index.php [QSA,L]
|
||||
|
||||
<Limit GET POST PUT DELETE OPTIONS>
|
||||
<IfModule version_module>
|
||||
<IfVersion >= 2.4>
|
||||
Require all granted
|
||||
</IfVersion>
|
||||
<IfVersion < 2.4>
|
||||
Allow from all
|
||||
Deny from none
|
||||
</IfVersion>
|
||||
</IfModule>
|
||||
|
||||
<IfModule !version_module>
|
||||
Require all granted
|
||||
</IfModule>
|
||||
</Limit>
|
||||
|
||||
<LimitExcept GET POST PUT DELETE OPTIONS>
|
||||
<IfModule version_module>
|
||||
<IfVersion >= 2.4>
|
||||
Require all denied
|
||||
</IfVersion>
|
||||
<IfVersion < 2.4>
|
||||
Allow from none
|
||||
Deny from all
|
||||
</IfVersion>
|
||||
</IfModule>
|
||||
|
||||
<IfModule !version_module>
|
||||
Require all denied
|
||||
</IfModule>
|
||||
</LimitExcept>
|
||||
|
|
49
.travis.yml
49
.travis.yml
|
@ -1,20 +1,53 @@
|
|||
sudo: false
|
||||
dist: trusty
|
||||
language: php
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- language: php
|
||||
php: 7.2
|
||||
- language: php
|
||||
php: 7.1
|
||||
- language: php
|
||||
php: 7.0
|
||||
- language: php
|
||||
php: 5.6
|
||||
- language: node_js
|
||||
node_js: 8
|
||||
cache:
|
||||
yarn: true
|
||||
directories:
|
||||
- $HOME/.cache/yarn
|
||||
|
||||
install:
|
||||
- yarn install
|
||||
|
||||
before_script:
|
||||
- PATH=${PATH//:\.\/node_modules\/\.bin/}
|
||||
|
||||
script:
|
||||
- yarn run build # Just to be sure that the build isn't broken
|
||||
- make eslint
|
||||
- make sasslint
|
||||
- language: python
|
||||
python: 3.6
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.cache/pip
|
||||
install:
|
||||
- pip install mkdocs
|
||||
script:
|
||||
- mkdocs build --clean
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.composer/cache
|
||||
php:
|
||||
- 7.1
|
||||
- 7.0
|
||||
- 5.6
|
||||
- 5.5
|
||||
|
||||
install:
|
||||
- composer self-update
|
||||
- composer install --prefer-dist
|
||||
- locale -a
|
||||
|
||||
before_script:
|
||||
- PATH=${PATH//:\.\/node_modules\/\.bin/}
|
||||
|
||||
script:
|
||||
- make clean
|
||||
- make check_permissions
|
||||
|
|
20
AUTHORS
20
AUTHORS
|
@ -1,6 +1,6 @@
|
|||
588 ArthurHoaro <arthur@hoa.ro>
|
||||
283 VirtualTam <virtualtam@flibidi.net>
|
||||
179 nodiscc <nodiscc@gmail.com>
|
||||
687 ArthurHoaro <arthur@hoa.ro>
|
||||
355 VirtualTam <virtualtam@flibidi.net>
|
||||
195 nodiscc <nodiscc@gmail.com>
|
||||
56 Sébastien Sauvage <sebsauvage@sebsauvage.net>
|
||||
15 Florian Eula <eula.florian@gmail.com>
|
||||
13 Emilien Klein <emilien@klein.st>
|
||||
|
@ -9,12 +9,15 @@
|
|||
8 Christophe HENRY <christophe.henry@sbgodin.fr>
|
||||
6 B. van Berkum <dev@dotmpe.com>
|
||||
5 Lucas Cimon <lucas.cimon@gmail.com>
|
||||
5 Mark Schmitz <kramred@gmail.com>
|
||||
5 kalvn <kalvnthereal@gmail.com>
|
||||
4 Alexandre Alapetite <alexandre@alapetite.fr>
|
||||
4 David Sferruzza <david.sferruzza@gmail.com>
|
||||
4 Immánuel Fodor <immanuelfactor+github@gmail.com>
|
||||
4 kalvn <kalvnthereal@gmail.com>
|
||||
3 Teromene <teromene@teromene.fr>
|
||||
3 llune <llune@users.noreply.github.com>
|
||||
2 Chris Kuethe <chris.kuethe@gmail.com>
|
||||
2 Felix Bartels <felix@host-consultants.de>
|
||||
2 Knah Tsaeb <Knah-Tsaeb@knah-tsaeb.org>
|
||||
2 Mathieu Chabanon <git@matchab.fr>
|
||||
2 Miloš Jovanović <mjovanovic@gmail.com>
|
||||
|
@ -23,20 +26,26 @@
|
|||
2 Timo Van Neerden <fire@lehollandaisvolant.net>
|
||||
2 julienCXX <software@chmodplusx.eu>
|
||||
2 philipp-r <philipp-r@users.noreply.github.com>
|
||||
2 pips <pips@e5150.fr>
|
||||
1 Adrien Oliva <adrien.oliva@yapbreak.fr>
|
||||
1 Adrien le Maire <adrien@alemaire.be>
|
||||
1 Alexandre G.-Raymond <alex@ndre.gr>
|
||||
1 Alexis J <alexis@effingo.be>
|
||||
1 Angristan <angristan@users.noreply.github.com>
|
||||
1 BoboTiG <bobotig@gmail.com>
|
||||
1 Bronco <bronco@warriordudimanche.net>
|
||||
1 Buster One <37770318+buster-one@users.noreply.github.com>
|
||||
1 D Low <daniellowtw@gmail.com>
|
||||
1 Daniel Jakots <vigdis@chown.me>
|
||||
1 Dennis Verspuij <dennisverspuij@users.noreply.github.com>
|
||||
1 Dimtion <zizou.xena@gmail.com>
|
||||
1 Fanch <fanch-github@qth.fr>
|
||||
1 Felix Bartels <felix@host-consultants.de>
|
||||
1 Felix Kästner <github.com-fpunktk@fpunktk.de>
|
||||
1 Florian Voigt <flvoigt@me.com>
|
||||
1 Franck Kerbiriou <FranckKe@users.noreply.github.com>
|
||||
1 Gary Marigliano <gmarigliano93@gmail.com>
|
||||
1 Guillaume Virlet <github@virlet.org>
|
||||
1 Jonathan Amiez <jonathan.amiez@gmail.com>
|
||||
1 Jonathan Druart <jonathan.druart@gmail.com>
|
||||
1 Julien Pivotto <roidelapluie@inuits.eu>
|
||||
1 Kevin Canévet <kevin@streamroot.io>
|
||||
|
@ -49,3 +58,4 @@
|
|||
1 TsT <tst2005@gmail.com>
|
||||
1 dimtion <zizou.xena@gmail.com>
|
||||
1 durcheinandr <jochen@durcheinandr.de>
|
||||
1 lapineige <lapineige@users.noreply.github.com>
|
||||
|
|
96
CHANGELOG.md
96
CHANGELOG.md
|
@ -4,6 +4,89 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [v0.10.2](https://github.com/shaarli/Shaarli/releases/tag/v0.10.2) - 2018-08-11
|
||||
|
||||
### Fixed
|
||||
|
||||
- Docker build
|
||||
|
||||
## [v0.10.1](https://github.com/shaarli/Shaarli/releases/tag/v0.10.1) - 2018-08-11
|
||||
|
||||
### Changed
|
||||
|
||||
- Accessibility:
|
||||
- Remove alt text on the logo
|
||||
- Remove redundant title in tools page
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed an error on the daily page and daily RSS
|
||||
- Fixed an issue causing 'You are not authorized to add a link' error while logged out
|
||||
- Fixed thumbnail path when Shaarli's path uses symbolic links
|
||||
- Add a `mod_version` check in Shaarli's root `.htaccess` file for Apache 2.2 syntax
|
||||
- Include assets in the release Makefile target
|
||||
|
||||
### Removed
|
||||
|
||||
- Firefox Social API shaare has been removed
|
||||
|
||||
## [v0.10.0](https://github.com/shaarli/Shaarli/releases/tag/v0.10.0) - 2018-07-28
|
||||
**PHP 5.5 compatibility has been dropped.** Shaarli now requires at least PHP 5.6.
|
||||
|
||||
### Added
|
||||
- Add a filter to display public links only
|
||||
- Add PHP 7.2 support
|
||||
- Add German translation
|
||||
- Resolve front-end dependencies from NPM
|
||||
- Build front-end bundles with Yarn and Webpack
|
||||
- Lint Javascript code with ESLint
|
||||
- Lint SASS code with SASSLint
|
||||
- Support redirection in cURL download callback
|
||||
- Introduce multi-stage builds for Docker images
|
||||
- Use Travis matrix and stages to run Javascript tests in a dedicated environment
|
||||
- Add tag endpoint in the REST API
|
||||
- Build the documentation in Travis builds
|
||||
- Provide a Docker Compose example
|
||||
|
||||
### Changed
|
||||
- Use web-thumbnailer to retrieve thumbnails (see #687)
|
||||
- Use a specific page title in all pages
|
||||
- Daily: run hooks before creating the columns
|
||||
- Load theme translations files automatically
|
||||
- Make max download size and timeout configurable
|
||||
- Make Nginx logs accessible as stdout/stderr for Docker images
|
||||
- Update buttons used to toggle link visibility filters
|
||||
- Rewrite Javascript code for ES6 compliance
|
||||
- Refactor IP ban management
|
||||
- Refactor user login management
|
||||
- Refactor server-side session management
|
||||
- Update Doxygen configuration
|
||||
- Update Parsedown
|
||||
- Improve documentation
|
||||
- Docker: build the images from the local sources
|
||||
- Docker: bump alpine version to 3.7
|
||||
- Docker: expose a volume for the thumbnail cache
|
||||
|
||||
### Removed
|
||||
- Drop support for PHP 5.5
|
||||
- Remove vendored front-end libraries
|
||||
- Remove environment specific .gitignore entries
|
||||
|
||||
### Fixed
|
||||
- Ignore the case while checking DOCTYPE during the file import
|
||||
- Fix removal of on=... attributes from html generated from Markdown
|
||||
- httpd: always forward the 'Authorization' header
|
||||
- Ensure user-specific CSS file is loaded
|
||||
- Fix feed permalink rendering when Markdown escaping is enabled
|
||||
- Fix order of tags with the same number of occurrences
|
||||
- Fixed the referrer meta tag in default template
|
||||
- Disable MkDocs' strict mode for ReadTheDocs builds to pass
|
||||
- fix and simplify Dockerfile for armhf
|
||||
|
||||
### Security
|
||||
- Update `.htaccess` to prevent accessing Git metadata when using a Git-based installation
|
||||
|
||||
|
||||
## [v0.9.7](https://github.com/shaarli/Shaarli/releases/tag/v0.9.7) - 2018-06-20
|
||||
### Changed
|
||||
- Build the Docker images from the local Git sources
|
||||
|
@ -240,6 +323,19 @@ Theming:
|
|||
|
||||
- Editing a link created before the new ID system would change its permalink.
|
||||
|
||||
## [v0.8.7](https://github.com/shaarli/Shaarli/releases/tag/v0.8.7) - 2018-06-20
|
||||
### Changed
|
||||
- Build the Docker image from the local Git sources
|
||||
|
||||
### Removed
|
||||
- Disable PHP 5.3 Travis build (unsupported)
|
||||
|
||||
|
||||
## [v0.8.6](https://github.com/shaarli/Shaarli/releases/tag/v0.8.6) - 2018-02-19
|
||||
### Changed
|
||||
- Run version check tests against the 'stable' branch
|
||||
|
||||
|
||||
## [v0.8.5](https://github.com/shaarli/Shaarli/releases/tag/v0.8.5) - 2018-01-04
|
||||
**XSS vulnerability fixed. Please update.**
|
||||
|
||||
|
|
38
COPYING
38
COPYING
|
@ -1,55 +1,57 @@
|
|||
Files: *
|
||||
License: zlib/libpng
|
||||
Copyright: (c) 2011-2015 Sébastien SAUVAGE <sebsauvage@sebsauvage.net>
|
||||
(c) 2011-2017 The Shaarli Community, see AUTHORS
|
||||
(c) 2011-2018 The Shaarli Community, see AUTHORS
|
||||
|
||||
Files: inc/reset.css
|
||||
Files: assets/vintage/css/reset.css
|
||||
License: BSD (http://opensource.org/licenses/BSD-3-Clause)
|
||||
Copyright: (c) 2010, Yahoo! Inc.
|
||||
|
||||
Files: images/calendar.png, images/edit_icon.png, images/feed-icon-14x14.png, images/private.png, images/private_16x16.png, images/private_16x16_active.png, images/tag_blue.png
|
||||
Files: assets/vintage/img/calendar.png
|
||||
assets/vintage/img/edit_icon.png
|
||||
assets/vintage/img/feed-icon-14x14.png
|
||||
assets/vintage/img/private.png
|
||||
assets/vintage/img/private_16x16.png
|
||||
assets/vintage/img/private_16x16_active.png
|
||||
assets/vintage/img/tag_blue.png
|
||||
License: CC-BY (http://creativecommons.org/licenses/by/3.0/)
|
||||
Copyright: (c) 2014 Yusuke Kamiyamane
|
||||
Source: http://p.yusukekamiyamane.com/
|
||||
|
||||
Files: images/delete_icon.png
|
||||
Files: assets/vintage/img/delete_icon.png
|
||||
License: CC-BY (http://creativecommons.org/licenses/by/3.0/)
|
||||
Copyright: (c) 2014 Designmodo
|
||||
Source: http://designmodo.com/linecons-free/
|
||||
|
||||
Files: images/floral_left.png, images/floral_right.png, images/squiggle.png, images/squiggle_closing.png
|
||||
Files: assets/vintage/img/floral_left.png
|
||||
assets/vintage/img/floral_right.png
|
||||
assets/vintage/img/squiggle.png
|
||||
assets/vintage/img/squiggle_closing.png
|
||||
Licence: Public Domain
|
||||
Source: https://openclipart.org/people/j4p4n/j4p4n_ornimental_bookend_-_left.svg
|
||||
|
||||
Files: images/Paper_texture_v5_by_bashcorpo_w1000.jpg
|
||||
Files: assets/vintage/img/Paper_texture_v5_by_bashcorpo_w1000.jpg
|
||||
Licence: Public Domain
|
||||
Source: http://bashcorpo.deviantart.com/art/Grungy-paper-texture-v-5-22966998
|
||||
|
||||
Files: images/logo.png
|
||||
Files: assets/vintage/img/logo.png
|
||||
assets/vintage/img/logo.png
|
||||
License: zlib/libpng
|
||||
Copyright: (c) 2011-2014 idleman idleman@idleman.fr
|
||||
|
||||
Files: inc/blazy*.js
|
||||
Files: assets/default/img/sad_star.png
|
||||
License: MIT License (http://opensource.org/licenses/MIT)
|
||||
Copyright: (C) Bjoern Klinggaard - @bklinggaard - http://dinbror.dk/blazy
|
||||
Copyright: (C) 2015 kalvn - https://github.com/kalvn/Shaarli-Material
|
||||
|
||||
Files: inc/rain.tpl.class.php
|
||||
License: LGPL-3+ (https://www.gnu.org/licenses/lgpl-3.0.txt)
|
||||
Copyright: 2011-2012, Federico Ulfo <rainelemental@gmail.com>
|
||||
2011-2012, The Rain Team <hello@raintm.com>
|
||||
License: LGPL-3+ (https://www.gnu.org/licenses/lgpl-3.0.txt)
|
||||
|
||||
Files: inc/awesomplete*
|
||||
License: MIT License (http://opensource.org/licenses/MIT)
|
||||
Copyright: (C) 2015 Lea Verou - https://github.com/LeaVerou/awesomplete
|
||||
|
||||
Files: plugins/wallabag/wallabag.png
|
||||
License: MIT License (http://opensource.org/licenses/MIT)
|
||||
Copyright: (C) 2015 Nicolas Lœuillet - https://github.com/wallabag/wallabag
|
||||
|
||||
Files: tpl/default/sad_star.png
|
||||
License: MIT License (http://opensource.org/licenses/MIT)
|
||||
Copyright: (C) 2015 kalvn - https://github.com/kalvn/Shaarli-Material
|
||||
|
||||
----------------------------------------------------
|
||||
ZLIB/LIBPNG LICENSE
|
||||
|
||||
|
|
16
Dockerfile
16
Dockerfile
|
@ -5,7 +5,7 @@ FROM python:3-alpine as docs
|
|||
ADD . /usr/src/app/shaarli
|
||||
RUN cd /usr/src/app/shaarli \
|
||||
&& pip install --no-cache-dir mkdocs \
|
||||
&& mkdocs build
|
||||
&& mkdocs build --clean
|
||||
|
||||
# Stage 2:
|
||||
# - Resolve PHP dependencies with Composer
|
||||
|
@ -15,8 +15,17 @@ RUN cd shaarli \
|
|||
&& composer --prefer-dist --no-dev install
|
||||
|
||||
# Stage 3:
|
||||
# - Frontend dependencies
|
||||
FROM node:9.9-alpine as node
|
||||
COPY --from=composer /app/shaarli shaarli
|
||||
RUN cd shaarli \
|
||||
&& yarn install \
|
||||
&& yarn run build \
|
||||
&& rm -rf node_modules
|
||||
|
||||
# Stage 4:
|
||||
# - Shaarli image
|
||||
FROM alpine:3.6
|
||||
FROM alpine:3.8
|
||||
LABEL maintainer="Shaarli Community"
|
||||
|
||||
RUN apk --update --no-cache add \
|
||||
|
@ -47,12 +56,13 @@ RUN rm -rf /etc/php7/php-fpm.d/www.conf \
|
|||
|
||||
|
||||
WORKDIR /var/www
|
||||
COPY --from=composer /app/shaarli shaarli
|
||||
COPY --from=node /shaarli shaarli
|
||||
|
||||
RUN chown -R nginx:nginx . \
|
||||
&& ln -sf /dev/stdout /var/log/nginx/shaarli.access.log \
|
||||
&& ln -sf /dev/stderr /var/log/nginx/shaarli.error.log
|
||||
|
||||
VOLUME /var/www/shaarli/cache
|
||||
VOLUME /var/www/shaarli/data
|
||||
|
||||
EXPOSE 80
|
||||
|
|
3
Doxyfile
3
Doxyfile
|
@ -51,7 +51,7 @@ PROJECT_BRIEF = "The personal, minimalist, super-fast, no-database deli
|
|||
# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
|
||||
# the logo to the output directory.
|
||||
|
||||
PROJECT_LOGO = images/logo.png
|
||||
PROJECT_LOGO = doc/md/images/logo.png
|
||||
|
||||
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
|
||||
# into which the generated documentation will be written. If a relative path is
|
||||
|
@ -804,6 +804,7 @@ RECURSIVE = YES
|
|||
# run.
|
||||
|
||||
EXCLUDE = vendor \
|
||||
data \
|
||||
tpl \
|
||||
inc \
|
||||
doc \
|
||||
|
|
30
Makefile
30
Makefile
|
@ -157,21 +157,32 @@ composer_dependencies: clean
|
|||
composer install --no-dev --prefer-dist
|
||||
find vendor/ -name ".git" -type d -exec rm -rf {} +
|
||||
|
||||
### download 3rd-party frontend libraries
|
||||
frontend_dependencies:
|
||||
yarn install
|
||||
|
||||
### Build frontend dependencies
|
||||
build_frontend: frontend_dependencies
|
||||
yarn run build
|
||||
|
||||
### generate a release tarball and include 3rd-party dependencies and translations
|
||||
release_tar: composer_dependencies htmldoc translate
|
||||
release_tar: composer_dependencies htmldoc translate build_frontend
|
||||
git archive --prefix=$(ARCHIVE_PREFIX) -o $(ARCHIVE_VERSION).tar HEAD
|
||||
tar rvf $(ARCHIVE_VERSION).tar --transform "s|^vendor|$(ARCHIVE_PREFIX)vendor|" vendor/
|
||||
tar rvf $(ARCHIVE_VERSION).tar --transform "s|^doc/html|$(ARCHIVE_PREFIX)doc/html|" doc/html/
|
||||
tar rvf $(ARCHIVE_VERSION).tar --transform "s|^tpl|$(ARCHIVE_PREFIX)tpl|" tpl/
|
||||
gzip $(ARCHIVE_VERSION).tar
|
||||
|
||||
### generate a release zip and include 3rd-party dependencies and translations
|
||||
release_zip: composer_dependencies htmldoc translate
|
||||
release_zip: composer_dependencies htmldoc translate build_frontend
|
||||
git archive --prefix=$(ARCHIVE_PREFIX) -o $(ARCHIVE_VERSION).zip -9 HEAD
|
||||
mkdir -p $(ARCHIVE_PREFIX)/{doc,vendor}
|
||||
rsync -a doc/html/ $(ARCHIVE_PREFIX)doc/html/
|
||||
zip -r $(ARCHIVE_VERSION).zip $(ARCHIVE_PREFIX)doc/
|
||||
rsync -a vendor/ $(ARCHIVE_PREFIX)vendor/
|
||||
zip -r $(ARCHIVE_VERSION).zip $(ARCHIVE_PREFIX)vendor/
|
||||
rsync -a tpl/ $(ARCHIVE_PREFIX)tpl/
|
||||
zip -r $(ARCHIVE_VERSION).zip $(ARCHIVE_PREFIX)tpl/
|
||||
rm -rf $(ARCHIVE_PREFIX)
|
||||
|
||||
##
|
||||
|
@ -192,18 +203,27 @@ authors:
|
|||
### generate Doxygen documentation
|
||||
doxygen: clean
|
||||
@rm -rf doxygen
|
||||
@( cat Doxyfile ; echo "PROJECT_NUMBER=`git describe`" ) | doxygen -
|
||||
@doxygen Doxyfile
|
||||
|
||||
### generate HTML documentation from Markdown pages with MkDocs
|
||||
htmldoc:
|
||||
python3 -m venv venv/
|
||||
bash -c 'source venv/bin/activate; \
|
||||
pip install mkdocs; \
|
||||
mkdocs build'
|
||||
mkdocs build --clean'
|
||||
find doc/html/ -type f -exec chmod a-x '{}' \;
|
||||
rm -r venv
|
||||
|
||||
|
||||
### Generate Shaarli's translation compiled file (.mo)
|
||||
translate:
|
||||
@find inc/languages/ -name shaarli.po -execdir msgfmt shaarli.po -o shaarli.mo \;
|
||||
@find inc/languages/ -name shaarli.po -execdir msgfmt shaarli.po -o shaarli.mo \;
|
||||
|
||||
### Run ESLint check against Shaarli's JS files
|
||||
eslint:
|
||||
@yarn run eslint -c .dev/.eslintrc.js assets/vintage/js/
|
||||
@yarn run eslint -c .dev/.eslintrc.js assets/default/js/
|
||||
|
||||
### Run CSSLint check against Shaarli's SCSS files
|
||||
sasslint:
|
||||
@yarn run sass-lint -c .dev/.sasslintrc 'assets/default/scss/*.scss' -v -q
|
||||
|
|
|
@ -6,10 +6,10 @@ _Do you want to share the links you discover?_
|
|||
_Shaarli is a minimalist link sharing service that you can install on your own server._
|
||||
_It is designed to be personal (single-user), fast and handy._
|
||||
|
||||
[![](https://img.shields.io/badge/stable-v0.8.5-blue.svg)](https://github.com/shaarli/Shaarli/releases/tag/v0.8.5)
|
||||
[![](https://img.shields.io/badge/stable-v0.9.7-blue.svg)](https://github.com/shaarli/Shaarli/releases/tag/v0.9.7)
|
||||
[![](https://img.shields.io/travis/shaarli/Shaarli/stable.svg?label=stable)](https://travis-ci.org/shaarli/Shaarli)
|
||||
•
|
||||
[![](https://img.shields.io/badge/latest-v0.9.4-blue.svg)](https://github.com/shaarli/Shaarli/releases/tag/v0.9.4)
|
||||
[![](https://img.shields.io/badge/latest-v0.10.1-blue.svg)](https://github.com/shaarli/Shaarli/releases/tag/v0.10.1)
|
||||
[![](https://img.shields.io/travis/shaarli/Shaarli/latest.svg?label=latest)](https://travis-ci.org/shaarli/Shaarli)
|
||||
•
|
||||
[![](https://img.shields.io/badge/master-v0.10.x-blue.svg)](https://github.com/shaarli/Shaarli)
|
||||
|
|
|
@ -37,7 +37,7 @@ class FileUtils
|
|||
if (is_file($file) && !is_writeable($file)) {
|
||||
// The datastore exists but is not writeable
|
||||
throw new IOException($file);
|
||||
} else if (!is_file($file) && !is_writeable(dirname($file))) {
|
||||
} elseif (!is_file($file) && !is_writeable(dirname($file))) {
|
||||
// The datastore does not exist and its parent directory is not writeable
|
||||
throw new IOException(dirname($file));
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
/**
|
||||
* GET an HTTP URL to retrieve its content
|
||||
* Uses the cURL library or a fallback method
|
||||
* Uses the cURL library or a fallback method
|
||||
*
|
||||
* @param string $url URL to get (http://...)
|
||||
* @param int $timeout network timeout (in seconds)
|
||||
|
@ -415,6 +415,37 @@ function getIpAddressFromProxy($server, $trustedIps)
|
|||
return array_pop($ips);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return an identifier based on the advertised client IP address(es)
|
||||
*
|
||||
* This aims at preventing session hijacking from users behind the same proxy
|
||||
* by relying on HTTP headers.
|
||||
*
|
||||
* See:
|
||||
* - https://secure.php.net/manual/en/reserved.variables.server.php
|
||||
* - https://stackoverflow.com/questions/3003145/how-to-get-the-client-ip-address-in-php
|
||||
* - https://stackoverflow.com/questions/12233406/preventing-session-hijacking
|
||||
* - https://stackoverflow.com/questions/21354859/trusting-x-forwarded-for-to-identify-a-visitor
|
||||
*
|
||||
* @param array $server The $_SERVER array
|
||||
*
|
||||
* @return string An identifier based on client IP address information
|
||||
*/
|
||||
function client_ip_id($server)
|
||||
{
|
||||
$ip = $server['REMOTE_ADDR'];
|
||||
|
||||
if (isset($server['HTTP_X_FORWARDED_FOR'])) {
|
||||
$ip = $ip . '_' . $server['HTTP_X_FORWARDED_FOR'];
|
||||
}
|
||||
if (isset($server['HTTP_CLIENT_IP'])) {
|
||||
$ip = $ip . '_' . $server['HTTP_CLIENT_IP'];
|
||||
}
|
||||
return $ip;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if Shaarli's currently browsed in HTTPS.
|
||||
* Supports reverse proxies (if the headers are correctly set).
|
||||
|
|
|
@ -98,6 +98,12 @@ class Languages
|
|||
$this->translator->setLanguage($this->language);
|
||||
$this->translator->loadDomain(self::DEFAULT_DOMAIN, 'inc/languages');
|
||||
|
||||
// Default extension translation from the current theme
|
||||
$themeTransFolder = rtrim($this->conf->get('raintpl_tpl'), '/') .'/'. $this->conf->get('theme') .'/language';
|
||||
if (is_dir($themeTransFolder)) {
|
||||
$this->translator->loadDomain($this->conf->get('theme'), $themeTransFolder, false);
|
||||
}
|
||||
|
||||
foreach ($this->conf->get('translation.extensions', []) as $domain => $translationPath) {
|
||||
if ($domain !== self::DEFAULT_DOMAIN) {
|
||||
$this->translator->loadDomain($domain, $translationPath, false);
|
||||
|
@ -116,12 +122,23 @@ class Languages
|
|||
$translations = new Translations();
|
||||
// Core translations
|
||||
try {
|
||||
/** @var Translations $translations */
|
||||
$translations = $translations->addFromPoFile('inc/languages/'. $this->language .'/LC_MESSAGES/shaarli.po');
|
||||
$translations->setDomain('shaarli');
|
||||
$this->translator->loadTranslations($translations);
|
||||
} catch (\InvalidArgumentException $e) {}
|
||||
|
||||
// Default extension translation from the current theme
|
||||
$theme = $this->conf->get('theme');
|
||||
$themeTransFolder = rtrim($this->conf->get('raintpl_tpl'), '/') .'/'. $theme .'/language';
|
||||
if (is_dir($themeTransFolder)) {
|
||||
try {
|
||||
$translations = Translations::fromPoFile(
|
||||
$themeTransFolder .'/'. $this->language .'/LC_MESSAGES/'. $theme .'.po'
|
||||
);
|
||||
$translations->setDomain($theme);
|
||||
$this->translator->loadTranslations($translations);
|
||||
} catch (\InvalidArgumentException $e) {}
|
||||
}
|
||||
|
||||
// Extension translations (plugins, themes, etc.).
|
||||
foreach ($this->conf->get('translation.extensions', []) as $domain => $translationPath) {
|
||||
|
@ -130,7 +147,6 @@ class Languages
|
|||
}
|
||||
|
||||
try {
|
||||
/** @var Translations $extension */
|
||||
$extension = Translations::fromPoFile($translationPath . $this->language .'/LC_MESSAGES/'. $domain .'.po');
|
||||
$extension->setDomain($domain);
|
||||
$this->translator->loadTranslations($extension);
|
||||
|
@ -161,6 +177,7 @@ class Languages
|
|||
'auto' => t('Automatic'),
|
||||
'en' => t('English'),
|
||||
'fr' => t('French'),
|
||||
'de' => t('German'),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -436,15 +436,17 @@ You use the community supported version of the original Shaarli project, by Seba
|
|||
|
||||
/**
|
||||
* Returns the list tags appearing in the links with the given tags
|
||||
* @param $filteringTags: tags selecting the links to consider
|
||||
* @param $visibility: process only all/private/public links
|
||||
* @return: a tag=>linksCount array
|
||||
*
|
||||
* @param array $filteringTags tags selecting the links to consider
|
||||
* @param string $visibility process only all/private/public links
|
||||
*
|
||||
* @return array tag => linksCount
|
||||
*/
|
||||
public function linksCountPerTag($filteringTags = [], $visibility = 'all')
|
||||
{
|
||||
$links = empty($filteringTags) ? $this->links : $this->filterSearch(['searchtags' => $filteringTags], false, $visibility);
|
||||
$tags = array();
|
||||
$caseMapping = array();
|
||||
$links = $this->filterSearch(['searchtags' => $filteringTags], false, $visibility);
|
||||
$tags = [];
|
||||
$caseMapping = [];
|
||||
foreach ($links as $link) {
|
||||
foreach (preg_split('/\s+/', $link['tags'], 0, PREG_SPLIT_NO_EMPTY) as $tag) {
|
||||
if (empty($tag)) {
|
||||
|
@ -458,8 +460,19 @@ You use the community supported version of the original Shaarli project, by Seba
|
|||
$tags[$caseMapping[strtolower($tag)]]++;
|
||||
}
|
||||
}
|
||||
// Sort tags by usage (most used tag first)
|
||||
arsort($tags);
|
||||
|
||||
/*
|
||||
* Formerly used arsort(), which doesn't define the sort behaviour for equal values.
|
||||
* Also, this function doesn't produce the same result between PHP 5.6 and 7.
|
||||
*
|
||||
* So we now use array_multisort() to sort tags by DESC occurrences,
|
||||
* then ASC alphabetically for equal values.
|
||||
*
|
||||
* @see https://github.com/shaarli/Shaarli/issues/1142
|
||||
*/
|
||||
$keys = array_keys($tags);
|
||||
$tmpTags = array_combine($keys, $keys);
|
||||
array_multisort($tags, SORT_DESC, $tmpTags, SORT_ASC, $tags);
|
||||
return $tags;
|
||||
}
|
||||
|
||||
|
|
|
@ -117,7 +117,7 @@ class LinkFilter
|
|||
foreach ($this->links as $key => $value) {
|
||||
if ($value['private'] && $visibility === 'private') {
|
||||
$out[$key] = $value;
|
||||
} else if (! $value['private'] && $visibility === 'public') {
|
||||
} elseif (! $value['private'] && $visibility === 'public') {
|
||||
$out[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
@ -210,7 +210,7 @@ class LinkFilter
|
|||
if ($visibility !== 'all') {
|
||||
if (! $link['private'] && $visibility === 'private') {
|
||||
continue;
|
||||
} else if ($link['private'] && $visibility === 'public') {
|
||||
} elseif ($link['private'] && $visibility === 'public') {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -337,7 +337,7 @@ class LinkFilter
|
|||
if ($visibility !== 'all') {
|
||||
if (! $link['private'] && $visibility === 'private') {
|
||||
continue;
|
||||
} else if ($link['private'] && $visibility === 'public') {
|
||||
} elseif ($link['private'] && $visibility === 'public') {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -380,7 +380,7 @@ class LinkFilter
|
|||
if ($visibility !== 'all') {
|
||||
if (! $link['private'] && $visibility === 'private') {
|
||||
continue;
|
||||
} else if ($link['private'] && $visibility === 'public') {
|
||||
} elseif ($link['private'] && $visibility === 'public') {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
*/
|
||||
function get_curl_download_callback(&$charset, &$title, $curlGetInfo = 'curl_getinfo')
|
||||
{
|
||||
$isRedirected = false;
|
||||
/**
|
||||
* cURL callback function for CURLOPT_WRITEFUNCTION (called during the download).
|
||||
*
|
||||
|
@ -22,16 +23,24 @@ function get_curl_download_callback(&$charset, &$title, $curlGetInfo = 'curl_get
|
|||
*
|
||||
* @return int|bool length of $data or false if we need to stop the download
|
||||
*/
|
||||
return function(&$ch, $data) use ($curlGetInfo, &$charset, &$title) {
|
||||
return function(&$ch, $data) use ($curlGetInfo, &$charset, &$title, &$isRedirected) {
|
||||
$responseCode = $curlGetInfo($ch, CURLINFO_RESPONSE_CODE);
|
||||
if (!empty($responseCode) && $responseCode != 200) {
|
||||
if (!empty($responseCode) && in_array($responseCode, [301, 302])) {
|
||||
$isRedirected = true;
|
||||
return strlen($data);
|
||||
}
|
||||
if (!empty($responseCode) && $responseCode !== 200) {
|
||||
return false;
|
||||
}
|
||||
$contentType = $curlGetInfo($ch, CURLINFO_CONTENT_TYPE);
|
||||
// After a redirection, the content type will keep the previous request value
|
||||
// until it finds the next content-type header.
|
||||
if (! $isRedirected || strpos(strtolower($data), 'content-type') !== false) {
|
||||
$contentType = $curlGetInfo($ch, CURLINFO_CONTENT_TYPE);
|
||||
}
|
||||
if (!empty($contentType) && strpos($contentType, 'text/html') === false) {
|
||||
return false;
|
||||
}
|
||||
if (empty($charset)) {
|
||||
if (!empty($contentType) && empty($charset)) {
|
||||
$charset = header_extract_charset($contentType);
|
||||
}
|
||||
if (empty($charset)) {
|
||||
|
|
|
@ -108,7 +108,7 @@ class NetscapeBookmarkUtils
|
|||
$filesize = $files['filetoupload']['size'];
|
||||
$data = file_get_contents($files['filetoupload']['tmp_name']);
|
||||
|
||||
if (strpos($data, '<!DOCTYPE NETSCAPE-Bookmark-file-1>') === false) {
|
||||
if (preg_match('/<!DOCTYPE NETSCAPE-Bookmark-file-1>/i', $data) === 0) {
|
||||
return self::importStatus($filename, $filesize);
|
||||
}
|
||||
|
||||
|
@ -154,13 +154,13 @@ class NetscapeBookmarkUtils
|
|||
if (empty($post['privacy']) || $post['privacy'] == 'default') {
|
||||
// use value from the imported file
|
||||
$private = $bkm['pub'] == '1' ? 0 : 1;
|
||||
} else if ($post['privacy'] == 'private') {
|
||||
} elseif ($post['privacy'] == 'private') {
|
||||
// all imported links are private
|
||||
$private = 1;
|
||||
} else if ($post['privacy'] == 'public') {
|
||||
} elseif ($post['privacy'] == 'public') {
|
||||
// all imported links are public
|
||||
$private = 0;
|
||||
}
|
||||
}
|
||||
|
||||
$newLink = array(
|
||||
'title' => $bkm['title'],
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
|
||||
use Shaarli\Config\ConfigManager;
|
||||
use Shaarli\Thumbnailer;
|
||||
|
||||
/**
|
||||
* This class is in charge of building the final page.
|
||||
|
@ -21,25 +22,42 @@ class PageBuilder
|
|||
*/
|
||||
protected $conf;
|
||||
|
||||
/**
|
||||
* @var array $_SESSION
|
||||
*/
|
||||
protected $session;
|
||||
|
||||
/**
|
||||
* @var LinkDB $linkDB instance.
|
||||
*/
|
||||
protected $linkDB;
|
||||
|
||||
/**
|
||||
* @var null|string XSRF token
|
||||
*/
|
||||
protected $token;
|
||||
|
||||
/** @var bool $isLoggedIn Whether the user is logged in **/
|
||||
protected $isLoggedIn = false;
|
||||
|
||||
/**
|
||||
* PageBuilder constructor.
|
||||
* $tpl is initialized at false for lazy loading.
|
||||
*
|
||||
* @param ConfigManager $conf Configuration Manager instance (reference).
|
||||
* @param LinkDB $linkDB instance.
|
||||
* @param string $token Session token
|
||||
* @param ConfigManager $conf Configuration Manager instance (reference).
|
||||
* @param array $session $_SESSION array
|
||||
* @param LinkDB $linkDB instance.
|
||||
* @param string $token Session token
|
||||
* @param bool $isLoggedIn
|
||||
*/
|
||||
public function __construct(&$conf, $linkDB = null, $token = null)
|
||||
public function __construct(&$conf, $session, $linkDB = null, $token = null, $isLoggedIn = false)
|
||||
{
|
||||
$this->tpl = false;
|
||||
$this->conf = $conf;
|
||||
$this->session = $session;
|
||||
$this->linkDB = $linkDB;
|
||||
$this->token = $token;
|
||||
$this->isLoggedIn = $isLoggedIn;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -55,7 +73,7 @@ class PageBuilder
|
|||
$this->conf->get('resource.update_check'),
|
||||
$this->conf->get('updates.check_updates_interval'),
|
||||
$this->conf->get('updates.check_updates'),
|
||||
isLoggedIn(),
|
||||
$this->isLoggedIn,
|
||||
$this->conf->get('updates.check_updates_branch')
|
||||
);
|
||||
$this->tpl->assign('newVersion', escape($version));
|
||||
|
@ -67,6 +85,7 @@ class PageBuilder
|
|||
$this->tpl->assign('versionError', escape($exc->getMessage()));
|
||||
}
|
||||
|
||||
$this->tpl->assign('is_logged_in', $this->isLoggedIn);
|
||||
$this->tpl->assign('feedurl', escape(index_url($_SERVER)));
|
||||
$searchcrits = ''; // Search criteria
|
||||
if (!empty($_GET['searchtags'])) {
|
||||
|
@ -83,7 +102,8 @@ class PageBuilder
|
|||
ApplicationUtils::getVersionHash(SHAARLI_VERSION, $this->conf->get('credentials.salt'))
|
||||
);
|
||||
$this->tpl->assign('scripturl', index_url($_SERVER));
|
||||
$this->tpl->assign('privateonly', !empty($_SESSION['privateonly'])); // Show only private links?
|
||||
$visibility = ! empty($_SESSION['visibility']) ? $_SESSION['visibility'] : '';
|
||||
$this->tpl->assign('visibility', $visibility);
|
||||
$this->tpl->assign('untaggedonly', !empty($_SESSION['untaggedonly']));
|
||||
$this->tpl->assign('pagetitle', $this->conf->get('general.title', 'Shaarli'));
|
||||
if ($this->conf->exists('general.header_link')) {
|
||||
|
@ -99,6 +119,19 @@ class PageBuilder
|
|||
if ($this->linkDB !== null) {
|
||||
$this->tpl->assign('tags', $this->linkDB->linksCountPerTag());
|
||||
}
|
||||
|
||||
$this->tpl->assign(
|
||||
'thumbnails_enabled',
|
||||
$this->conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE
|
||||
);
|
||||
$this->tpl->assign('thumbnails_width', $this->conf->get('thumbnails.width'));
|
||||
$this->tpl->assign('thumbnails_height', $this->conf->get('thumbnails.height'));
|
||||
|
||||
if (! empty($_SESSION['warnings'])) {
|
||||
$this->tpl->assign('global_warnings', $_SESSION['warnings']);
|
||||
unset($_SESSION['warnings']);
|
||||
}
|
||||
|
||||
// To be removed with a proper theme configuration.
|
||||
$this->tpl->assign('conf', $this->conf);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
*/
|
||||
class Router
|
||||
{
|
||||
public static $AJAX_THUMB_UPDATE = 'ajax_thumb_update';
|
||||
|
||||
public static $PAGE_LOGIN = 'login';
|
||||
|
||||
public static $PAGE_PICWALL = 'picwall';
|
||||
|
@ -47,6 +49,8 @@ class Router
|
|||
|
||||
public static $PAGE_SAVE_PLUGINSADMIN = 'save_pluginadmin';
|
||||
|
||||
public static $PAGE_THUMBS_UPDATE = 'thumbs_update';
|
||||
|
||||
public static $GET_TOKEN = 'token';
|
||||
|
||||
/**
|
||||
|
@ -101,6 +105,14 @@ class Router
|
|||
return self::$PAGE_FEED_RSS;
|
||||
}
|
||||
|
||||
if (startsWith($query, 'do='. self::$PAGE_THUMBS_UPDATE)) {
|
||||
return self::$PAGE_THUMBS_UPDATE;
|
||||
}
|
||||
|
||||
if (startsWith($query, 'do='. self::$AJAX_THUMB_UPDATE)) {
|
||||
return self::$AJAX_THUMB_UPDATE;
|
||||
}
|
||||
|
||||
// At this point, only loggedin pages.
|
||||
if (!$loggedIn) {
|
||||
return self::$PAGE_LINKLIST;
|
||||
|
|
|
@ -1,83 +0,0 @@
|
|||
<?php
|
||||
namespace Shaarli;
|
||||
|
||||
/**
|
||||
* Manages the server-side session
|
||||
*/
|
||||
class SessionManager
|
||||
{
|
||||
protected $session = [];
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array $session The $_SESSION array (reference)
|
||||
* @param ConfigManager $conf ConfigManager instance
|
||||
*/
|
||||
public function __construct(& $session, $conf)
|
||||
{
|
||||
$this->session = &$session;
|
||||
$this->conf = $conf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a session token
|
||||
*
|
||||
* @return string token
|
||||
*/
|
||||
public function generateToken()
|
||||
{
|
||||
$token = sha1(uniqid('', true) .'_'. mt_rand() . $this->conf->get('credentials.salt'));
|
||||
$this->session['tokens'][$token] = 1;
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the validity of a session token, and destroys it afterwards
|
||||
*
|
||||
* @param string $token The token to check
|
||||
*
|
||||
* @return bool true if the token is valid, else false
|
||||
*/
|
||||
public function checkToken($token)
|
||||
{
|
||||
if (! isset($this->session['tokens'][$token])) {
|
||||
// the token is wrong, or has already been used
|
||||
return false;
|
||||
}
|
||||
|
||||
// destroy the token to prevent future use
|
||||
unset($this->session['tokens'][$token]);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate session ID to prevent Full Path Disclosure.
|
||||
*
|
||||
* See #298.
|
||||
* The session ID's format depends on the hash algorithm set in PHP settings
|
||||
*
|
||||
* @param string $sessionId Session ID
|
||||
*
|
||||
* @return true if valid, false otherwise.
|
||||
*
|
||||
* @see http://php.net/manual/en/function.hash-algos.php
|
||||
* @see http://php.net/manual/en/session.configuration.php
|
||||
*/
|
||||
public static function checkId($sessionId)
|
||||
{
|
||||
if (empty($sessionId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$sessionId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!preg_match('/^[a-zA-Z0-9,-]{2,128}$/', $sessionId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
127
application/Thumbnailer.php
Normal file
127
application/Thumbnailer.php
Normal file
|
@ -0,0 +1,127 @@
|
|||
<?php
|
||||
|
||||
namespace Shaarli;
|
||||
|
||||
use Shaarli\Config\ConfigManager;
|
||||
use WebThumbnailer\Exception\WebThumbnailerException;
|
||||
use WebThumbnailer\WebThumbnailer;
|
||||
use WebThumbnailer\Application\ConfigManager as WTConfigManager;
|
||||
|
||||
/**
|
||||
* Class Thumbnailer
|
||||
*
|
||||
* Utility class used to retrieve thumbnails using web-thumbnailer dependency.
|
||||
*/
|
||||
class Thumbnailer
|
||||
{
|
||||
const COMMON_MEDIA_DOMAINS = [
|
||||
'imgur.com',
|
||||
'flickr.com',
|
||||
'youtube.com',
|
||||
'wikimedia.org',
|
||||
'redd.it',
|
||||
'gfycat.com',
|
||||
'media.giphy.com',
|
||||
'twitter.com',
|
||||
'twimg.com',
|
||||
'instagram.com',
|
||||
'pinterest.com',
|
||||
'pinterest.fr',
|
||||
'tumblr.com',
|
||||
'deviantart.com',
|
||||
];
|
||||
|
||||
const MODE_ALL = 'all';
|
||||
const MODE_COMMON = 'common';
|
||||
const MODE_NONE = 'none';
|
||||
|
||||
/**
|
||||
* @var WebThumbnailer instance.
|
||||
*/
|
||||
protected $wt;
|
||||
|
||||
/**
|
||||
* @var ConfigManager instance.
|
||||
*/
|
||||
protected $conf;
|
||||
|
||||
/**
|
||||
* Thumbnailer constructor.
|
||||
*
|
||||
* @param ConfigManager $conf instance.
|
||||
*/
|
||||
public function __construct($conf)
|
||||
{
|
||||
$this->conf = $conf;
|
||||
|
||||
if (! $this->checkRequirements()) {
|
||||
$this->conf->set('thumbnails.enabled', false);
|
||||
$this->conf->write(true);
|
||||
// TODO: create a proper error handling system able to catch exceptions...
|
||||
die(t('php-gd extension must be loaded to use thumbnails. Thumbnails are now disabled. Please reload the page.'));
|
||||
}
|
||||
|
||||
$this->wt = new WebThumbnailer();
|
||||
WTConfigManager::addFile('inc/web-thumbnailer.json');
|
||||
$this->wt->maxWidth($this->conf->get('thumbnails.width'))
|
||||
->maxHeight($this->conf->get('thumbnails.height'))
|
||||
->crop(true)
|
||||
->debug($this->conf->get('dev.debug', false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a thumbnail for given URL
|
||||
*
|
||||
* @param string $url where to look for a thumbnail.
|
||||
*
|
||||
* @return bool|string The thumbnail relative cache file path, or false if none has been found.
|
||||
*/
|
||||
public function get($url)
|
||||
{
|
||||
if ($this->conf->get('thumbnails.mode') === self::MODE_COMMON
|
||||
&& ! $this->isCommonMediaOrImage($url)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
return $this->wt->thumbnail($url);
|
||||
} catch (WebThumbnailerException $e) {
|
||||
// Exceptions are only thrown in debug mode.
|
||||
error_log(get_class($e) . ': ' . $e->getMessage());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* We check weather the given URL is from a common media domain,
|
||||
* or if the file extension is an image.
|
||||
*
|
||||
* @param string $url to check
|
||||
*
|
||||
* @return bool true if it's an image or from a common media domain, false otherwise.
|
||||
*/
|
||||
public function isCommonMediaOrImage($url)
|
||||
{
|
||||
foreach (self::COMMON_MEDIA_DOMAINS as $domain) {
|
||||
if (strpos($url, $domain) !== false) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (endsWith($url, '.jpg') || endsWith($url, '.png') || endsWith($url, '.jpeg')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that requirements are match to use thumbnails:
|
||||
* - php-gd is loaded
|
||||
*/
|
||||
protected function checkRequirements()
|
||||
{
|
||||
return extension_loaded('gd');
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
use Shaarli\Config\ConfigJson;
|
||||
use Shaarli\Config\ConfigPhp;
|
||||
use Shaarli\Config\ConfigManager;
|
||||
use Shaarli\Thumbnailer;
|
||||
|
||||
/**
|
||||
* Class Updater.
|
||||
|
@ -30,6 +31,11 @@ class Updater
|
|||
*/
|
||||
protected $isLoggedIn;
|
||||
|
||||
/**
|
||||
* @var array $_SESSION
|
||||
*/
|
||||
protected $session;
|
||||
|
||||
/**
|
||||
* @var ReflectionMethod[] List of current class methods.
|
||||
*/
|
||||
|
@ -42,13 +48,17 @@ class Updater
|
|||
* @param LinkDB $linkDB LinkDB instance.
|
||||
* @param ConfigManager $conf Configuration Manager instance.
|
||||
* @param boolean $isLoggedIn True if the user is logged in.
|
||||
* @param array $session $_SESSION (by reference)
|
||||
*
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
public function __construct($doneUpdates, $linkDB, $conf, $isLoggedIn)
|
||||
public function __construct($doneUpdates, $linkDB, $conf, $isLoggedIn, &$session = [])
|
||||
{
|
||||
$this->doneUpdates = $doneUpdates;
|
||||
$this->linkDB = $linkDB;
|
||||
$this->conf = $conf;
|
||||
$this->isLoggedIn = $isLoggedIn;
|
||||
$this->session = &$session;
|
||||
|
||||
// Retrieve all update methods.
|
||||
$class = new ReflectionClass($this);
|
||||
|
@ -445,6 +455,68 @@ class Updater
|
|||
$this->linkDB->save($this->conf->get('resource.page_cache'));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change privateonly session key to visibility.
|
||||
*/
|
||||
public function updateMethodVisibilitySession()
|
||||
{
|
||||
if (isset($_SESSION['privateonly'])) {
|
||||
unset($_SESSION['privateonly']);
|
||||
$_SESSION['visibility'] = 'private';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add download size and timeout to the configuration file
|
||||
*
|
||||
* @return bool true if the update is successful, false otherwise.
|
||||
*/
|
||||
public function updateMethodDownloadSizeAndTimeoutConf()
|
||||
{
|
||||
if ($this->conf->exists('general.download_max_size')
|
||||
&& $this->conf->exists('general.download_timeout')
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (! $this->conf->exists('general.download_max_size')) {
|
||||
$this->conf->set('general.download_max_size', 1024*1024*4);
|
||||
}
|
||||
|
||||
if (! $this->conf->exists('general.download_timeout')) {
|
||||
$this->conf->set('general.download_timeout', 30);
|
||||
}
|
||||
|
||||
$this->conf->write($this->isLoggedIn);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* * Move thumbnails management to WebThumbnailer, coming with new settings.
|
||||
*/
|
||||
public function updateMethodWebThumbnailer()
|
||||
{
|
||||
if ($this->conf->exists('thumbnails.mode')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$thumbnailsEnabled = extension_loaded('gd') && $this->conf->get('thumbnail.enable_thumbnails', true);
|
||||
$this->conf->set('thumbnails.mode', $thumbnailsEnabled ? Thumbnailer::MODE_ALL : Thumbnailer::MODE_NONE);
|
||||
$this->conf->set('thumbnails.width', 125);
|
||||
$this->conf->set('thumbnails.height', 90);
|
||||
$this->conf->remove('thumbnail');
|
||||
$this->conf->write(true);
|
||||
|
||||
if ($thumbnailsEnabled) {
|
||||
$this->session['warnings'][] = t(
|
||||
'You have enabled or changed thumbnails mode. <a href="?do=thumbs_update">Please synchronize them</a>.'
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -81,7 +81,7 @@ function whitelist_protocols($url, $protocols)
|
|||
// Protocol not allowed: we remove it and replace it with http
|
||||