Release v0.9.0
-----BEGIN PGP SIGNATURE----- iQEzBAABCAAdFiEEGqoBQZDmIumKOObJrZlaKwD4KWQFAlkPUlEACgkQrZlaKwD4 KWR5+gf/akFTTxy5uyTfB1U36cGpgdvhf6bjosBKlaXY+Nvpu1NF/LX8xpbrSFDY phI8gECt2XPD8Nk4eMhpXi9KLMMYWMccVdO5NKuZP+NxtjpYnTTueAthosm7sWqy JXtqSJQCDRZJj2GNUhw1WvM/6t2WlmTFUcVRV/2Vq87Hzf0eYnOrTNUXVTsxfc3K 8TY98qu4XgaMenzTjp35O5wza6kihEW27NXwM4KumWjg+VTgRkfePla5EGdK9BcG 16wT94WCy29t/gTIEW9Q9Tf+hTO7Oaq0iyN/8Ha0QFXOOutMuHfrhFMezMDGPzzb esH47/AT2DzaxfCAIqSorgPVHVYzMA== =GN48 -----END PGP SIGNATURE----- Merge tag 'v0.9.0' into latest Release v0.9.0
This commit is contained in:
commit
fcf141926d
265 changed files with 21106 additions and 1154 deletions
9
.gitattributes
vendored
9
.gitattributes
vendored
|
@ -10,15 +10,22 @@
|
|||
*.php text diff=php
|
||||
Dockerfile text
|
||||
|
||||
# Do not alter images nor minified scripts
|
||||
# Do not alter images nor minified scripts nor fonts
|
||||
*.ico binary
|
||||
*.jpg binary
|
||||
*.png binary
|
||||
*.svg binary
|
||||
*.otf binary
|
||||
*.eot binary
|
||||
*.woff binary
|
||||
*.woff2 binary
|
||||
*.ttf binary
|
||||
*.min.css binary
|
||||
*.min.js binary
|
||||
|
||||
# Exclude from Git archives
|
||||
.gitattributes export-ignore
|
||||
.github export-ignore
|
||||
.gitignore export-ignore
|
||||
.travis.yml export-ignore
|
||||
doc/**/*.json export-ignore
|
||||
|
|
13
.github/mailmap
vendored
Normal file
13
.github/mailmap
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
ArthurHoaro <arthur@hoa.ro>
|
||||
Florian Eula <eula.florian@gmail.com> feula
|
||||
Florian Eula <eula.florian@gmail.com> <mr.pikzen@gmail.com>
|
||||
Nicolas Danelon <hi@nicolasmd.com.ar> nicolasm
|
||||
Nicolas Danelon <hi@nicolasmd.com.ar> <nda@3818.com.ar>
|
||||
Nicolas Danelon <hi@nicolasmd.com.ar> <nicolasdanelon@gmail.com>
|
||||
Nicolas Danelon <hi@nicolasmd.com.ar> <nicolasdanelon@users.noreply.github.com>
|
||||
Sébastien Sauvage <sebsauvage@sebsauvage.net>
|
||||
Timo Van Neerden <fire@lehollandaisvolant.net>
|
||||
Timo Van Neerden <fire@lehollandaisvolant.net> lehollandaisvolant <levoltigeurhollandais@gmail.com>
|
||||
VirtualTam <virtualtam@flibidi.net> <tamisier.aurelien@gmail.com>
|
||||
VirtualTam <virtualtam@flibidi.net> <virtualtam+github@flibidi.net>
|
||||
VirtualTam <virtualtam@flibidi.net> <virtualtam@flibidi.org>
|
8
.gitignore
vendored
8
.gitignore
vendored
|
@ -13,11 +13,10 @@ pagecache
|
|||
*.rtpl.php
|
||||
|
||||
# 3rd-party dependencies
|
||||
composer.lock
|
||||
vendor/
|
||||
|
||||
# Release archives
|
||||
*.tar
|
||||
*.tar.gz
|
||||
*.zip
|
||||
|
||||
# Development and test resources
|
||||
|
@ -28,3 +27,8 @@ phpmd.html
|
|||
|
||||
# User plugin configuration
|
||||
plugins/*/config.php
|
||||
|
||||
# 3rd party themes
|
||||
tpl/*
|
||||
!tpl/default
|
||||
!tpl/vintage
|
||||
|
|
4
.htaccess
Normal file
4
.htaccess
Normal file
|
@ -0,0 +1,4 @@
|
|||
RewriteEngine On
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^ index.php [QSA,L]
|
10
.travis.yml
10
.travis.yml
|
@ -1,5 +1,11 @@
|
|||
sudo: false
|
||||
language: php
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- locales
|
||||
- language-pack-de
|
||||
- language-pack-fr
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.composer/cache
|
||||
|
@ -8,12 +14,10 @@ php:
|
|||
- 7.0
|
||||
- 5.6
|
||||
- 5.5
|
||||
- 5.4
|
||||
- 5.3
|
||||
install:
|
||||
- composer self-update
|
||||
- composer install --prefer-dist
|
||||
script:
|
||||
- make clean
|
||||
- make check_permissions
|
||||
- make test
|
||||
- make all_tests
|
||||
|
|
41
AUTHORS
Normal file
41
AUTHORS
Normal file
|
@ -0,0 +1,41 @@
|
|||
472 ArthurHoaro <arthur@hoa.ro>
|
||||
201 VirtualTam <virtualtam@flibidi.net>
|
||||
132 nodiscc <nodiscc@gmail.com>
|
||||
56 Sébastien Sauvage <sebsauvage@sebsauvage.net>
|
||||
15 Florian Eula <eula.florian@gmail.com>
|
||||
13 Emilien Klein <emilien@klein.st>
|
||||
12 Nicolas Danelon <hi@nicolasmd.com.ar>
|
||||
8 Christophe HENRY <christophe.henry@sbgodin.fr>
|
||||
4 Alexandre Alapetite <alexandre@alapetite.fr>
|
||||
4 David Sferruzza <david.sferruzza@gmail.com>
|
||||
3 Teromene <teromene@teromene.fr>
|
||||
2 Chris Kuethe <chris.kuethe@gmail.com>
|
||||
2 Knah Tsaeb <Knah-Tsaeb@knah-tsaeb.org>
|
||||
2 Mathieu Chabanon <git@matchab.fr>
|
||||
2 Miloš Jovanović <mjovanovic@gmail.com>
|
||||
2 Qwerty <champlywood@free.fr>
|
||||
2 Timo Van Neerden <fire@lehollandaisvolant.net>
|
||||
2 julienCXX <software@chmodplusx.eu>
|
||||
2 kalvn <kalvnthereal@gmail.com>
|
||||
1 Adrien Oliva <adrien.oliva@yapbreak.fr>
|
||||
1 Alexis J <alexis@effingo.be>
|
||||
1 BoboTiG <bobotig@gmail.com>
|
||||
1 Bronco <bronco@warriordudimanche.net>
|
||||
1 D Low <daniellowtw@gmail.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 Gary Marigliano <gmarigliano93@gmail.com>
|
||||
1 Guillaume Virlet <github@virlet.org>
|
||||
1 Jonathan Druart <jonathan.druart@gmail.com>
|
||||
1 Julien Pivotto <roidelapluie@inuits.eu>
|
||||
1 Kevin Canévet <kevin@streamroot.io>
|
||||
1 Knah Tsaeb <knah-tsaeb@knah-tsaeb.org>
|
||||
1 Lionel Martin <renarddesmers@gmail.com>
|
||||
1 Marsup <marsup@gmail.com>
|
||||
1 Sbgodin <Sbgodin@users.noreply.github.com>
|
||||
1 TsT <tst2005@gmail.com>
|
||||
1 dimtion <zizou.xena@gmail.com>
|
||||
1 philipp-r <philipp-r@users.noreply.github.com>
|
87
CHANGELOG.md
87
CHANGELOG.md
|
@ -5,13 +5,81 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
|||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
|
||||
## [v0.9.0](https://github.com/shaarli/Shaarli/releases/tag/v0.9.0) - UNPUBLISHED
|
||||
## [v0.9.0](https://github.com/shaarli/Shaarli/releases/tag/v0.9.0) - 2017-05-07
|
||||
|
||||
This release introduces the REST API, and requires updating HTTP server
|
||||
configuration to enable URL rewriting, see:
|
||||
- https://shaarli.github.io/api-documentation/
|
||||
- https://github.com/shaarli/Shaarli/wiki/Server-configuration
|
||||
|
||||
**WARNING**: Shaarli now requires PHP 5.5+.
|
||||
|
||||
### Added
|
||||
- REST API v1
|
||||
- [Slim](https://www.slimframework.com/) framework
|
||||
- [JSON Web Token](https://jwt.io/introduction/) (JWT) authentication
|
||||
- versioned API endpoints:
|
||||
- `/api/v1/info`: get general information on the Shaarli instance
|
||||
- `/api/v1/links`: get a list of shaared links
|
||||
- `/api/v1/history`: get a list of latest actions
|
||||
Theming:
|
||||
- Introduce a new theme
|
||||
- Allow selecting themes/templates from the configuration page
|
||||
- New/Edit link form can be submitted using CTRL+Enter in the textarea
|
||||
- Shaarli version is displayed in the footer when logged in
|
||||
- Add plugin placeholders to Atom/RSS feed templates
|
||||
- Add OpenSearch to feed templates
|
||||
- Add `campaign_` to the URL cleanup pattern list
|
||||
- Add an AUTHORS file and Makefile target to list authors from Git commit data
|
||||
- Link imports are now logged in `data/` folder, and can be debug using `dev.debug=true` setting.
|
||||
- `composer.lock` is now included in git file to allow proper `composer install`
|
||||
- History mechanism which logs link addition/modification/deletion
|
||||
|
||||
### Changed
|
||||
- Docker: enable nginx URL rewriting for the REST API
|
||||
- Theming:
|
||||
- Move `user.css` to the `data` folder
|
||||
- Move default template files to a subfolder (`default`)
|
||||
- Rename the legacy theme to `vintage`
|
||||
- Private only filter is now displayed as a search parameter
|
||||
- Autocomplete: pre-select the first element
|
||||
- Display daily date in the page title (browser title)
|
||||
- Timezone lists are now passed as an array instead of raw HTML
|
||||
- Move PubSubHub to a dedicated plugin
|
||||
- Coding style:
|
||||
- explicit method visibility
|
||||
- safe boolean comparisons
|
||||
- remove unused variables
|
||||
- The updater now keeps custom theme preferences
|
||||
- Simplify the COPYING information
|
||||
- Improved client locale detection
|
||||
- Improved date time display depending on the locale
|
||||
- Partial namespace support for Shaarli classes
|
||||
- Shaarli version is now only present in `shaarli_version.php`
|
||||
- Human readable maximum file size upload
|
||||
|
||||
|
||||
### Removed
|
||||
- PHP < 5.5 compatibility
|
||||
- ReadItYourself plugin
|
||||
|
||||
### Fixed
|
||||
- Ignore generated release tarballs
|
||||
- Hide default port when behind a reverse proxy
|
||||
- Fix a typo in the Markdown plugin description
|
||||
- Fix the presence of empty tags for private tags and in search results
|
||||
- Fix a fatal error during the install
|
||||
- Fix permalink image alignment in daily page
|
||||
- Fix the delete button in `editlink`
|
||||
- Fix redirection after link deletion
|
||||
- Do not access LinkDB links by ID before the Updater applies migrations
|
||||
- Remove extra spaces in the bookmarklet's name
|
||||
- Piwik plugin: Piwik URL protocol can now be set (http or https)
|
||||
- All inline JS has been moved to dedicated JS files
|
||||
- Keep tags after login redirection
|
||||
|
||||
### Security
|
||||
- Markdown plugin: escape HTML entities by default
|
||||
|
||||
## [v0.8.4](https://github.com/shaarli/Shaarli/releases/tag/v0.8.4) - 2017-03-04
|
||||
### Security
|
||||
|
@ -30,6 +98,19 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||
|
||||
- Editing a link created before the new ID system would change its permalink.
|
||||
|
||||
## [v0.8.4](https://github.com/shaarli/Shaarli/releases/tag/v0.8.4) - 2017-03-04
|
||||
### Security
|
||||
- Markdown plugin: escape HTML entities by default
|
||||
|
||||
## [v0.8.3](https://github.com/shaarli/Shaarli/releases/tag/v0.8.3) - 2017-01-20
|
||||
### Fixed
|
||||
- PHP 7.1 compatibility: add ConfigManager parameter to anti-bruteforce function call in login template.
|
||||
|
||||
## [v0.8.2](https://github.com/shaarli/Shaarli/releases/tag/v0.8.2) - 2016-12-15
|
||||
### Fixed
|
||||
|
||||
- Editing a link created before the new ID system would change its permalink.
|
||||
|
||||
## [v0.8.1](https://github.com/shaarli/Shaarli/releases/tag/v0.8.1) - 2016-12-12
|
||||
|
||||
> Note: this version will create an automatic backup of your database if anything goes wrong.
|
||||
|
@ -115,6 +196,10 @@ Please use our release archives, or follow the
|
|||
- XSRF token now generated each time a page is rendered
|
||||
|
||||
|
||||
## [v0.7.1](https://github.com/shaarli/Shaarli/releases/tag/v0.7.1) - 2017-03-08
|
||||
### Security
|
||||
- Markdown plugin: escape HTML entities by default
|
||||
|
||||
## [v0.7.0](https://github.com/shaarli/Shaarli/releases/tag/v0.7.0) - 2016-05-14
|
||||
### Added
|
||||
- Adds an option to encode redirector URL parameter
|
||||
|
|
34
COPYING
34
COPYING
|
@ -1,33 +1,7 @@
|
|||
Files: *
|
||||
License: zlib/libpng
|
||||
Copyright: (c) 2011-2015 Sébastien SAUVAGE <sebsauvage@sebsauvage.net>
|
||||
(c) 2011-2015 Alexandre Alapetite <alexandre@alapetite.fr>
|
||||
(c) 2011-2015 David Sferruzza <david.sferruzza@gmail.com>
|
||||
(c) 2011-2015 Christophe HENRY <christophe.henry@sbgodin.fr>
|
||||
(c) 2011-2015 Mathieu Chabanon <git@matchab.fr>
|
||||
(c) 2011-2015 BoboTiG <bobotig@gmail.com>
|
||||
(c) 2011-2015 Bronco <bronco@warriordudimanche.net>
|
||||
(c) 2011-2015 Emilien Klein <emilien@klein.st>
|
||||
(c) 2011-2015 Knah Tsaeb <knah-tsaeb@knah-tsaeb.org>
|
||||
(c) 2011-2015 Lionel Martin <renarddesmers@gmail.com>
|
||||
(c) 2011-2015 lehollandaisvolant <levoltigeurhollandais@gmail.com>
|
||||
(c) 2011-2015 timo van neerden <fire@lehollandaisvolant.net>
|
||||
(c) 2011-2015 nodiscc <nodiscc@gmail.com>
|
||||
(c) 2011-2015 Florian Eula <mr.pikzen@gmail.com>
|
||||
(c) 2011-2015 Arthur Hoaro <arthur@hoa.ro>
|
||||
(c) 2011-2015 Aurélien "VirtualTam" Tamisier <virtualtam@flibidi.net>
|
||||
(c) 2011-2015 qwertygc <champlywood@free.fr>
|
||||
(c) 2011-2015 idleman <idleman@idleman.fr>
|
||||
(c) 2015 Alexis Ju <alexis@effingo.be>
|
||||
(c) 2015 dimtion <zizou.xena@gmail.com>
|
||||
(c) 2015 Fanch <fanch-github@qth.fr>
|
||||
(c) 2015 Guillaume Virlet <github@virlet.org>
|
||||
(c) 2015 Felix Bartels <felix@host-consultants.de>
|
||||
(c) 2015 Marsup <marsup@gmail.com>
|
||||
(c) 2015 Miloš Jovanović <mjovanovic@gmail.com>
|
||||
(c) 2015 Nicolás Danelón <hola@nicolasdanelon.com.ar>
|
||||
(c) 2015 TsT <tst2005@gmail.com>
|
||||
|
||||
(c) 2011-2017 The Shaarli Community, see AUTHORS
|
||||
|
||||
Files: inc/reset.css
|
||||
License: BSD (http://opensource.org/licenses/BSD-3-Clause)
|
||||
|
@ -43,7 +17,7 @@ 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/squiggle2.png, images/squiggle_closing.png
|
||||
Files: images/floral_left.png, images/floral_right.png, images/squiggle.png, images/squiggle_closing.png
|
||||
Licence: Public Domain
|
||||
Source: https://openclipart.org/people/j4p4n/j4p4n_ornimental_bookend_-_left.svg
|
||||
|
||||
|
@ -72,6 +46,10 @@ 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
|
||||
|
||||
|
|
24
Makefile
24
Makefile
|
@ -124,8 +124,20 @@ test:
|
|||
@echo "-------"
|
||||
@echo "PHPUNIT"
|
||||
@echo "-------"
|
||||
@mkdir -p sandbox
|
||||
@$(BIN)/phpunit tests
|
||||
@mkdir -p sandbox coverage
|
||||
@$(BIN)/phpunit --coverage-php coverage/main.cov --testsuite unit-tests
|
||||
|
||||
locale_test_%:
|
||||
@UT_LOCALE=$*.utf8 \
|
||||
$(BIN)/phpunit \
|
||||
--coverage-php coverage/$(firstword $(subst _, ,$*)).cov \
|
||||
--bootstrap tests/languages/bootstrap.php \
|
||||
--testsuite language-$(firstword $(subst _, ,$*))
|
||||
|
||||
all_tests: test locale_test_de_DE locale_test_en_US locale_test_fr_FR
|
||||
@$(BIN)/phpcov merge --html coverage coverage
|
||||
@# --text doesn't work with phpunit 4.* (v5 requires PHP 5.6)
|
||||
@#$(BIN)/phpcov merge --text coverage/txt coverage
|
||||
|
||||
##
|
||||
# Custom release archive generation
|
||||
|
@ -169,6 +181,12 @@ clean:
|
|||
@git clean -df
|
||||
@rm -rf sandbox
|
||||
|
||||
### generate the AUTHORS file from Git commit information
|
||||
authors:
|
||||
@cp .github/mailmap .mailmap
|
||||
@git shortlog -sne > AUTHORS
|
||||
@rm .mailmap
|
||||
|
||||
### generate Doxygen documentation
|
||||
doxygen: clean
|
||||
@rm -rf doxygen
|
||||
|
@ -214,4 +232,4 @@ htmlpages:
|
|||
-o doc/$$base.html $$file; \
|
||||
done;
|
||||
|
||||
htmldoc: doc htmlsidebar htmlpages
|
||||
htmldoc: authors doc htmlsidebar htmlpages
|
||||
|
|
19
README.md
19
README.md
|
@ -6,13 +6,18 @@ _Do you want to share the links you discover?_
|
|||
_Shaarli is a minimalist delicious clone that you can install on your own server._
|
||||
_It is designed to be personal (single-user), fast and handy._
|
||||
|
||||
[![](https://img.shields.io/travis/shaarli/Shaarli.svg?label=master)](https://travis-ci.org/shaarli/Shaarli)
|
||||
[![](https://img.shields.io/badge/stable-v0.7.1-blue.svg)](https://github.com/shaarli/Shaarli/releases/tag/v0.7.1)
|
||||
[![](https://img.shields.io/travis/shaarli/Shaarli/stable.svg?label=stable)](https://travis-ci.org/shaarli/Shaarli)
|
||||
[![](https://img.shields.io/github/release/shaarli/shaarli.svg)](https://github.com/shaarli/Shaarli/releases/latest/)
|
||||
[![Docker repository](https://img.shields.io/docker/pulls/shaarli/shaarli.svg)](https://hub.docker.com/r/shaarli/shaarli/)
|
||||
•
|
||||
[![](https://img.shields.io/badge/latest-v0.8.4-blue.svg)](https://github.com/shaarli/Shaarli/releases/tag/v0.8.4)
|
||||
[![](https://img.shields.io/travis/shaarli/Shaarli/latest.svg?label=latest)](https://travis-ci.org/shaarli/Shaarli)
|
||||
•
|
||||
[![](https://img.shields.io/badge/master-v0.9.x-blue.svg)](https://github.com/shaarli/Shaarli)
|
||||
[![](https://img.shields.io/travis/shaarli/Shaarli.svg?label=master)](https://travis-ci.org/shaarli/Shaarli)
|
||||
|
||||
[![Join the chat at https://gitter.im/shaarli/Shaarli](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/shaarli/Shaarli)
|
||||
[![Bountysource](https://www.bountysource.com/badge/team?team_id=19583&style=bounties_received)](https://www.bountysource.com/teams/shaarli/issues)
|
||||
[![Docker repository](https://img.shields.io/docker/pulls/shaarli/shaarli.svg)](https://hub.docker.com/r/shaarli/shaarli/)
|
||||
|
||||
## Quickstart
|
||||
- [Wiki/documentation](https://github.com/shaarli/Shaarli/wiki)
|
||||
|
@ -20,7 +25,7 @@ _It is designed to be personal (single-user), fast and handy._
|
|||
- [Bugs/Feature requests/Discussion](https://github.com/shaarli/Shaarli/issues/)
|
||||
|
||||
### Demo
|
||||
You can use this [public demo instance of Shaarli](http://shaarlidemo.tuxfamily.org/Shaarli).
|
||||
You can use this [public demo instance of Shaarli](https://demo.shaarli.org).
|
||||
It runs the latest development version of Shaarli and is updated/reset daily.
|
||||
|
||||
Login: `demo`; Password: `demo`
|
||||
|
@ -80,6 +85,12 @@ dailymotion, flickr, imageshack, imgur, vimeo, xkcd, youtube...
|
|||
- URL cleanup: automatic removal of `?utm_source=...`, `fb=...`
|
||||
- discreet pop-up notification when a new release is available
|
||||
|
||||
### REST API
|
||||
|
||||
Easily extensible by any client using the REST API exposed by Shaarli.
|
||||
|
||||
See the [API documentation](http://shaarli.github.io/api-documentation/).
|
||||
|
||||
### Other usages
|
||||
Though Shaarli is primarily a bookmarking application, it can serve other purposes
|
||||
(see [usage examples](https://github.com/shaarli/Shaarli/wiki#usage-examples)):
|
||||
|
|
|
@ -4,9 +4,13 @@
|
|||
*/
|
||||
class ApplicationUtils
|
||||
{
|
||||
/**
|
||||
* @var string File containing the current version
|
||||
*/
|
||||
public static $VERSION_FILE = 'shaarli_version.php';
|
||||
|
||||
private static $GIT_URL = 'https://raw.githubusercontent.com/shaarli/Shaarli';
|
||||
private static $GIT_BRANCHES = array('master', 'stable');
|
||||
private static $VERSION_FILE = 'shaarli_version.php';
|
||||
private static $GIT_BRANCHES = array('latest', 'stable');
|
||||
private static $VERSION_START_TAG = '<?php /* ';
|
||||
private static $VERSION_END_TAG = ' */ ?>';
|
||||
|
||||
|
@ -29,6 +33,30 @@ class ApplicationUtils
|
|||
return false;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the version from a remote URL or a file.
|
||||
*
|
||||
* @param string $remote URL or file to fetch.
|
||||
* @param int $timeout For URLs fetching.
|
||||
*
|
||||
* @return bool|string The version or false if it couldn't be retrieved.
|
||||
*/
|
||||
public static function getVersion($remote, $timeout = 2)
|
||||
{
|
||||
if (startsWith($remote, 'http')) {
|
||||
if (($data = static::getLatestGitVersionCode($remote, $timeout)) === false) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (! is_file($remote)) {
|
||||
return false;
|
||||
}
|
||||
$data = file_get_contents($remote);
|
||||
}
|
||||
|
||||
return str_replace(
|
||||
array(self::$VERSION_START_TAG, self::$VERSION_END_TAG, PHP_EOL),
|
||||
array('', '', ''),
|
||||
|
@ -65,13 +93,10 @@ class ApplicationUtils
|
|||
$isLoggedIn,
|
||||
$branch='stable')
|
||||
{
|
||||
if (! $isLoggedIn) {
|
||||
// Do not check versions for visitors
|
||||
return false;
|
||||
}
|
||||
|
||||
if (empty($enableCheck)) {
|
||||
// Do not check if the user doesn't want to
|
||||
// Do not check versions for visitors
|
||||
// Do not check if the user doesn't want to
|
||||
// Do not check with dev version
|
||||
if (! $isLoggedIn || empty($enableCheck) || $currentVersion === 'dev') {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -93,7 +118,7 @@ class ApplicationUtils
|
|||
|
||||
// Late Static Binding allows overriding within tests
|
||||
// See http://php.net/manual/en/language.oop5.late-static-bindings.php
|
||||
$latestVersion = static::getLatestGitVersionCode(
|
||||
$latestVersion = static::getVersion(
|
||||
self::$GIT_URL . '/' . $branch . '/' . self::$VERSION_FILE
|
||||
);
|
||||
|
||||
|
@ -150,6 +175,7 @@ class ApplicationUtils
|
|||
'inc',
|
||||
'plugins',
|
||||
$conf->get('resource.raintpl_tpl'),
|
||||
$conf->get('resource.raintpl_tpl').'/'.$conf->get('resource.theme'),
|
||||
) as $path) {
|
||||
if (! is_readable(realpath($path))) {
|
||||
$errors[] = '"'.$path.'" directory is not readable';
|
||||
|
|
34
application/Base64Url.php
Normal file
34
application/Base64Url.php
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace Shaarli;
|
||||
|
||||
|
||||
/**
|
||||
* URL-safe Base64 operations
|
||||
*
|
||||
* @see https://en.wikipedia.org/wiki/Base64#URL_applications
|
||||
*/
|
||||
class Base64Url
|
||||
{
|
||||
/**
|
||||
* Base64Url-encodes data
|
||||
*
|
||||
* @param string $data Data to encode
|
||||
*
|
||||
* @return string Base64Url-encoded data
|
||||
*/
|
||||
public static function encode($data) {
|
||||
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes Base64Url-encoded data
|
||||
*
|
||||
* @param string $data Data to decode
|
||||
*
|
||||
* @return string Decoded data
|
||||
*/
|
||||
public static function decode($data) {
|
||||
return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT));
|
||||
}
|
||||
}
|
|
@ -7,9 +7,6 @@ class CachedPage
|
|||
// Directory containing page caches
|
||||
private $cacheDir;
|
||||
|
||||
// Full URL of the page to cache -typically the value returned by pageUrl()
|
||||
private $url;
|
||||
|
||||
// Should this URL be cached (boolean)?
|
||||
private $shouldBeCached;
|
||||
|
||||
|
@ -27,7 +24,6 @@ class CachedPage
|
|||
{
|
||||
// TODO: check write access to the cache directory
|
||||
$this->cacheDir = $cacheDir;
|
||||
$this->url = $url;
|
||||
$this->filename = $this->cacheDir.'/'.sha1($url).'.cache';
|
||||
$this->shouldBeCached = $shouldBeCached;
|
||||
}
|
||||
|
|
|
@ -62,11 +62,6 @@ class FeedBuilder
|
|||
*/
|
||||
protected $hideDates;
|
||||
|
||||
/**
|
||||
* @var string PubSub hub URL.
|
||||
*/
|
||||
protected $pubsubhubUrl;
|
||||
|
||||
/**
|
||||
* @var string server locale.
|
||||
*/
|
||||
|
@ -120,7 +115,6 @@ class FeedBuilder
|
|||
}
|
||||
|
||||
$data['language'] = $this->getTypeLanguage();
|
||||
$data['pubsubhub_url'] = $this->pubsubhubUrl;
|
||||
$data['last_update'] = $this->getLatestDateFormatted();
|
||||
$data['show_dates'] = !$this->hideDates || $this->isLoggedIn;
|
||||
// Remove leading slash from REQUEST_URI.
|
||||
|
@ -182,16 +176,6 @@ class FeedBuilder
|
|||
return $link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign PubSub hub URL.
|
||||
*
|
||||
* @param string $pubsubhubUrl PubSub hub url.
|
||||
*/
|
||||
public function setPubsubhubUrl($pubsubhubUrl)
|
||||
{
|
||||
$this->pubsubhubUrl = $pubsubhubUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this to true to use permalinks instead of direct links.
|
||||
*
|
||||
|
|
|
@ -1,21 +1,76 @@
|
|||
<?php
|
||||
|
||||
require_once 'exceptions/IOException.php';
|
||||
|
||||
/**
|
||||
* Exception class thrown when a filesystem access failure happens
|
||||
* Class FileUtils
|
||||
*
|
||||
* Utility class for file manipulation.
|
||||
*/
|
||||
class IOException extends Exception
|
||||
class FileUtils
|
||||
{
|
||||
private $path;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected static $phpPrefix = '<?php /* ';
|
||||
|
||||
/**
|
||||
* Construct a new IOException
|
||||
*
|
||||
* @param string $path path to the resource that cannot be accessed
|
||||
* @param string $message Custom exception message.
|
||||
* @var string
|
||||
*/
|
||||
public function __construct($path, $message = '')
|
||||
protected static $phpSuffix = ' */ ?>';
|
||||
|
||||
/**
|
||||
* Write data into a file (Shaarli database format).
|
||||
* The data is stored in a PHP file, as a comment, in compressed base64 format.
|
||||
*
|
||||
* The file will be created if it doesn't exist.
|
||||
*
|
||||
* @param string $file File path.
|
||||
* @param mixed $content Content to write.
|
||||
*
|
||||
* @return int|bool Number of bytes written or false if it fails.
|
||||
*
|
||||
* @throws IOException The destination file can't be written.
|
||||
*/
|
||||
public static function writeFlatDB($file, $content)
|
||||
{
|
||||
$this->path = $path;
|
||||
$this->message = empty($message) ? 'Error accessing' : $message;
|
||||
$this->message .= PHP_EOL . $this->path;
|
||||
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))) {
|
||||
// The datastore does not exist and its parent directory is not writeable
|
||||
throw new IOException(dirname($file));
|
||||
}
|
||||
|
||||
return file_put_contents(
|
||||
$file,
|
||||
self::$phpPrefix.base64_encode(gzdeflate(serialize($content))).self::$phpSuffix
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read data from a file containing Shaarli database format content.
|
||||
* If the file isn't readable or doesn't exists, default data will be returned.
|
||||
*
|
||||
* @param string $file File path.
|
||||
* @param mixed $default The default value to return if the file isn't readable.
|
||||
*
|
||||
* @return mixed The content unserialized, or default if the file isn't readable, or false if it fails.
|
||||
*/
|
||||
public static function readFlatDB($file, $default = null)
|
||||
{
|
||||
// Note that gzinflate is faster than gzuncompress.
|
||||
// See: http://www.php.net/manual/en/function.gzdeflate.php#96439
|
||||
if (is_readable($file)) {
|
||||
return unserialize(
|
||||
gzinflate(
|
||||
base64_decode(
|
||||
substr(file_get_contents($file), strlen(self::$phpPrefix), -strlen(self::$phpSuffix))
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
|
200
application/History.php
Normal file
200
application/History.php
Normal file
|
@ -0,0 +1,200 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Class History
|
||||
*
|
||||
* Handle the history file tracing events in Shaarli.
|
||||
* The history is stored as JSON in a file set by 'resource.history' setting.
|
||||
*
|
||||
* Available data:
|
||||
* - event: event key
|
||||
* - datetime: event date, in ISO8601 format.
|
||||
* - id: event item identifier (currently only link IDs).
|
||||
*
|
||||
* Available event keys:
|
||||
* - CREATED: new link
|
||||
* - UPDATED: link updated
|
||||
* - DELETED: link deleted
|
||||
* - SETTINGS: the settings have been updated through the UI.
|
||||
*
|
||||
* Note: new events are put at the beginning of the file and history array.
|
||||
*/
|
||||
class History
|
||||
{
|
||||
/**
|
||||
* @var string Action key: a new link has been created.
|
||||
*/
|
||||
const CREATED = 'CREATED';
|
||||
|
||||
/**
|
||||
* @var string Action key: a link has been updated.
|
||||
*/
|
||||
const UPDATED = 'UPDATED';
|
||||
|
||||
/**
|
||||
* @var string Action key: a link has been deleted.
|
||||
*/
|
||||
const DELETED = 'DELETED';
|
||||
|
||||
/**
|
||||
* @var string Action key: settings have been updated.
|
||||
*/
|
||||
const SETTINGS = 'SETTINGS';
|
||||
|
||||
/**
|
||||
* @var string History file path.
|
||||
*/
|
||||
protected $historyFilePath;
|
||||
|
||||
/**
|
||||
* @var array History data.
|
||||
*/
|
||||
protected $history;
|
||||
|
||||
/**
|
||||
* @var int History retention time in seconds (1 month).
|
||||
*/
|
||||
protected $retentionTime = 2678400;
|
||||
|
||||
/**
|
||||
* History constructor.
|
||||
*
|
||||
* @param string $historyFilePath History file path.
|
||||
* @param int $retentionTime History content rentention time in seconds.
|
||||
*
|
||||
* @throws Exception if something goes wrong.
|
||||
*/
|
||||
public function __construct($historyFilePath, $retentionTime = null)
|
||||
{
|
||||
$this->historyFilePath = $historyFilePath;
|
||||
if ($retentionTime !== null) {
|
||||
$this->retentionTime = $retentionTime;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize: read history file.
|
||||
*
|
||||
* Allow lazy loading (don't read the file if it isn't necessary).
|
||||
*/
|
||||
protected function initialize()
|
||||
{
|
||||
$this->check();
|
||||
$this->read();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Event: new link.
|
||||
*
|
||||
* @param array $link Link data.
|
||||
*/
|
||||
public function addLink($link)
|
||||
{
|
||||
$this->addEvent(self::CREATED, $link['id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Event: update existing link.
|
||||
*
|
||||
* @param array $link Link data.
|
||||
*/
|
||||
public function updateLink($link)
|
||||
{
|
||||
$this->addEvent(self::UPDATED, $link['id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Event: delete existing link.
|
||||
*
|
||||
* @param array $link Link data.
|
||||
*/
|
||||
public function deleteLink($link)
|
||||
{
|
||||
$this->addEvent(self::DELETED, $link['id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Event: settings updated.
|
||||
*/
|
||||
public function updateSettings()
|
||||
{
|
||||
$this->addEvent(self::SETTINGS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a new event and write it in the history file.
|
||||
*
|
||||
* @param string $status Event key, should be defined as constant.
|
||||
* @param mixed $id Event item identifier (e.g. link ID).
|
||||
*/
|
||||
protected function addEvent($status, $id = null)
|
||||
{
|
||||
if ($this->history === null) {
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
$item = [
|
||||
'event' => $status,
|
||||
'datetime' => new DateTime(),
|
||||
'id' => $id !== null ? $id : '',
|
||||
];
|
||||
$this->history = array_merge([$item], $this->history);
|
||||
$this->write();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the history file is writable.
|
||||
* Create the file if it doesn't exist.
|
||||
*
|
||||
* @throws Exception if it isn't writable.
|
||||
*/
|
||||
protected function check()
|
||||
{
|
||||
if (! is_file($this->historyFilePath)) {
|
||||
FileUtils::writeFlatDB($this->historyFilePath, []);
|
||||
}
|
||||
|
||||
if (! is_writable($this->historyFilePath)) {
|
||||
throw new Exception('History file isn\'t readable or writable');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read JSON history file.
|
||||
*/
|
||||
protected function read()
|
||||
{
|
||||
$this->history = FileUtils::readFlatDB($this->historyFilePath, []);
|
||||
if ($this->history === false) {
|
||||
throw new Exception('Could not parse history file');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write JSON history file and delete old entries.
|
||||
*/
|
||||
protected function write()
|
||||
{
|
||||
$comparaison = new DateTime('-'. $this->retentionTime . ' seconds');
|
||||
foreach ($this->history as $key => $value) {
|
||||
if ($value['datetime'] < $comparaison) {
|
||||
unset($this->history[$key]);
|
||||
}
|
||||
}
|
||||
FileUtils::writeFlatDB($this->historyFilePath, array_values($this->history));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the History.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getHistory()
|
||||
{
|
||||
if ($this->history === null) {
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
return $this->history;
|
||||
}
|
||||
}
|
|
@ -122,7 +122,7 @@ function get_http_response($url, $timeout = 30, $maxBytes = 4194304)
|
|||
$content = substr($response, $headSize);
|
||||
$headers = array();
|
||||
foreach (preg_split('~[\r\n]+~', $rawHeadersLastRedir) as $line) {
|
||||
if (empty($line) or ctype_space($line)) {
|
||||
if (empty($line) || ctype_space($line)) {
|
||||
continue;
|
||||
}
|
||||
$splitLine = explode(': ', $line, 2);
|
||||
|
@ -297,9 +297,17 @@ function server_url($server)
|
|||
// Keep forwarded port
|
||||
if (strpos($server['HTTP_X_FORWARDED_PORT'], ',') !== false) {
|
||||
$ports = explode(',', $server['HTTP_X_FORWARDED_PORT']);
|
||||
$port = ':' . trim($ports[0]);
|
||||
$port = trim($ports[0]);
|
||||
} else {
|
||||
$port = ':' . $server['HTTP_X_FORWARDED_PORT'];
|
||||
$port = $server['HTTP_X_FORWARDED_PORT'];
|
||||
}
|
||||
|
||||
if (($scheme == 'http' && $port != '80')
|
||||
|| ($scheme == 'https' && $port != '443')
|
||||
) {
|
||||
$port = ':' . $port;
|
||||
} else {
|
||||
$port = '';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -50,12 +50,6 @@ class LinkDB implements Iterator, Countable, ArrayAccess
|
|||
// Link date storage format
|
||||
const LINK_DATE_FORMAT = 'Ymd_His';
|
||||
|
||||
// Datastore PHP prefix
|
||||
protected static $phpPrefix = '<?php /* ';
|
||||
|
||||
// Datastore PHP suffix
|
||||
protected static $phpSuffix = ' */ ?>';
|
||||
|
||||
// List of links (associative array)
|
||||
// - key: link date (e.g. "20110823_124546"),
|
||||
// - value: associative array (keys: title, description...)
|
||||
|
@ -144,10 +138,10 @@ class LinkDB implements Iterator, Countable, ArrayAccess
|
|||
if (!isset($value['id']) || empty($value['url'])) {
|
||||
die('Internal Error: A link should always have an id and URL.');
|
||||
}
|
||||
if ((! empty($offset) && ! is_int($offset)) || ! is_int($value['id'])) {
|
||||
if (($offset !== null && ! is_int($offset)) || ! is_int($value['id'])) {
|
||||
die('You must specify an integer as a key.');
|
||||
}
|
||||
if (! empty($offset) && $offset !== $value['id']) {
|
||||
if ($offset !== null && $offset !== $value['id']) {
|
||||
die('Array offset and link ID must be equal.');
|
||||
}
|
||||
|
||||
|
@ -295,16 +289,7 @@ You use the community supported version of the original Shaarli project, by Seba
|
|||
return;
|
||||
}
|
||||
|
||||
// Read data
|
||||
// Note that gzinflate is faster than gzuncompress.
|
||||
// See: http://www.php.net/manual/en/function.gzdeflate.php#96439
|
||||
$this->links = array();
|
||||
|
||||
if (file_exists($this->datastore)) {
|
||||
$this->links = unserialize(gzinflate(base64_decode(
|
||||
substr(file_get_contents($this->datastore),
|
||||
strlen(self::$phpPrefix), -strlen(self::$phpSuffix)))));
|
||||
}
|
||||
$this->links = FileUtils::readFlatDB($this->datastore, []);
|
||||
|
||||
$toremove = array();
|
||||
foreach ($this->links as $key => &$link) {
|
||||
|
@ -361,19 +346,7 @@ You use the community supported version of the original Shaarli project, by Seba
|
|||
*/
|
||||
private function write()
|
||||
{
|
||||
if (is_file($this->datastore) && !is_writeable($this->datastore)) {
|
||||
// The datastore exists but is not writeable
|
||||
throw new IOException($this->datastore);
|
||||
} else if (!is_file($this->datastore) && !is_writeable(dirname($this->datastore))) {
|
||||
// The datastore does not exist and its parent directory is not writeable
|
||||
throw new IOException(dirname($this->datastore));
|
||||
}
|
||||
|
||||
file_put_contents(
|
||||
$this->datastore,
|
||||
self::$phpPrefix.base64_encode(gzdeflate(serialize($this->links))).self::$phpSuffix
|
||||
);
|
||||
|
||||
FileUtils::writeFlatDB($this->datastore, $this->links);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -443,11 +416,11 @@ You use the community supported version of the original Shaarli project, by Seba
|
|||
* - searchtags: list of tags
|
||||
* - searchterm: term search
|
||||
* @param bool $casesensitive Optional: Perform case sensitive filter
|
||||
* @param bool $privateonly Optional: Returns private links only if true.
|
||||
* @param string $visibility return only all/private/public links
|
||||
*
|
||||
* @return array filtered links, all links if no suitable filter was provided.
|
||||
*/
|
||||
public function filterSearch($filterRequest = array(), $casesensitive = false, $privateonly = false)
|
||||
public function filterSearch($filterRequest = array(), $casesensitive = false, $visibility = 'all')
|
||||
{
|
||||
// Filter link database according to parameters.
|
||||
$searchtags = !empty($filterRequest['searchtags']) ? escape($filterRequest['searchtags']) : '';
|
||||
|
@ -475,7 +448,7 @@ You use the community supported version of the original Shaarli project, by Seba
|
|||
}
|
||||
|
||||
$linkFilter = new LinkFilter($this);
|
||||
return $linkFilter->filter($type, $request, $casesensitive, $privateonly);
|
||||
return $linkFilter->filter($type, $request, $casesensitive, $visibility);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -51,12 +51,16 @@ class LinkFilter
|
|||
* @param string $type Type of filter (eg. tags, permalink, etc.).
|
||||
* @param mixed $request Filter content.
|
||||
* @param bool $casesensitive Optional: Perform case sensitive filter if true.
|
||||
* @param bool $privateonly Optional: Only returns private links if true.
|
||||
* @param string $visibility Optional: return only all/private/public links
|
||||
*
|
||||
* @return array filtered link list.
|
||||
*/
|
||||
public function filter($type, $request, $casesensitive = false, $privateonly = false)
|
||||
public function filter($type, $request, $casesensitive = false, $visibility = 'all')
|
||||
{
|
||||
if (! in_array($visibility, ['all', 'public', 'private'])) {
|
||||
$visibility = 'all';
|
||||
}
|
||||
|
||||
switch($type) {
|
||||
case self::$FILTER_HASH:
|
||||
return $this->filterSmallHash($request);
|
||||
|
@ -64,42 +68,44 @@ class LinkFilter
|
|||
if (!empty($request)) {
|
||||
$filtered = $this->links;
|
||||
if (isset($request[0])) {
|
||||
$filtered = $this->filterTags($request[0], $casesensitive, $privateonly);
|
||||
$filtered = $this->filterTags($request[0], $casesensitive, $visibility);
|
||||
}
|
||||
if (isset($request[1])) {
|
||||
$lf = new LinkFilter($filtered);
|
||||
$filtered = $lf->filterFulltext($request[1], $privateonly);
|
||||
$filtered = $lf->filterFulltext($request[1], $visibility);
|
||||
}
|
||||
return $filtered;
|
||||
}
|
||||
return $this->noFilter($privateonly);
|
||||
return $this->noFilter($visibility);
|
||||
case self::$FILTER_TEXT:
|
||||
return $this->filterFulltext($request, $privateonly);
|
||||
return $this->filterFulltext($request, $visibility);
|
||||
case self::$FILTER_TAG:
|
||||
return $this->filterTags($request, $casesensitive, $privateonly);
|
||||
return $this->filterTags($request, $casesensitive, $visibility);
|
||||
case self::$FILTER_DAY:
|
||||
return $this->filterDay($request);
|
||||
default:
|
||||
return $this->noFilter($privateonly);
|
||||
return $this->noFilter($visibility);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unknown filter, but handle private only.
|
||||
*
|
||||
* @param bool $privateonly returns private link only if true.
|
||||
* @param string $visibility Optional: return only all/private/public links
|
||||
*
|
||||
* @return array filtered links.
|
||||
*/
|
||||
private function noFilter($privateonly = false)
|
||||
private function noFilter($visibility = 'all')
|
||||
{
|
||||
if (! $privateonly) {
|
||||
if ($visibility === 'all') {
|
||||
return $this->links;
|
||||
}
|
||||
|
||||
$out = array();
|
||||
foreach ($this->links as $key => $value) {
|
||||
if ($value['private']) {
|
||||
if ($value['private'] && $visibility === 'private') {
|
||||
$out[$key] = $value;
|
||||
} else if (! $value['private'] && $visibility === 'public') {
|
||||
$out[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
@ -151,14 +157,14 @@ class LinkFilter
|
|||
* - see https://github.com/shaarli/Shaarli/issues/75 for examples
|
||||
*
|
||||
* @param string $searchterms search query.
|
||||
* @param bool $privateonly return only private links if true.
|
||||
* @param string $visibility Optional: return only all/private/public links.
|
||||
*
|
||||
* @return array search results.
|
||||
*/
|
||||
private function filterFulltext($searchterms, $privateonly = false)
|
||||
private function filterFulltext($searchterms, $visibility = 'all')
|
||||
{
|
||||
if (empty($searchterms)) {
|
||||
return $this->links;
|
||||
return $this->noFilter($visibility);
|
||||
}
|
||||
|
||||
$filtered = array();
|
||||
|
@ -189,8 +195,12 @@ class LinkFilter
|
|||
foreach ($this->links as $id => $link) {
|
||||
|
||||
// ignore non private links when 'privatonly' is on.
|
||||
if (! $link['private'] && $privateonly === true) {
|
||||
continue;
|
||||
if ($visibility !== 'all') {
|
||||
if (! $link['private'] && $visibility === 'private') {
|
||||
continue;
|
||||
} else if ($link['private'] && $visibility === 'public') {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Concatenate link fields to search across fields.
|
||||
|
@ -235,16 +245,16 @@ class LinkFilter
|
|||
*
|
||||
* @param string $tags list of tags separated by commas or blank spaces.
|
||||
* @param bool $casesensitive ignore case if false.
|
||||
* @param bool $privateonly returns private links only.
|
||||
* @param string $visibility Optional: return only all/private/public links.
|
||||
*
|
||||
* @return array filtered links.
|
||||
*/
|
||||
public function filterTags($tags, $casesensitive = false, $privateonly = false)
|
||||
public function filterTags($tags, $casesensitive = false, $visibility = 'all')
|
||||
{
|
||||
// Implode if array for clean up.
|
||||
$tags = is_array($tags) ? trim(implode(' ', $tags)) : $tags;
|
||||
if (empty($tags)) {
|
||||
return $this->links;
|
||||
return $this->noFilter($visibility);
|
||||
}
|
||||
|
||||
$searchtags = self::tagsStrToArray($tags, $casesensitive);
|
||||
|
@ -255,8 +265,12 @@ class LinkFilter
|
|||
|
||||
foreach ($this->links as $key => $link) {
|
||||
// ignore non private links when 'privatonly' is on.
|
||||
if (! $link['private'] && $privateonly === true) {
|
||||
continue;
|
||||
if ($visibility !== 'all') {
|
||||
if (! $link['private'] && $visibility === 'private') {
|
||||
continue;
|
||||
} else if ($link['private'] && $visibility === 'public') {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$linktags = self::tagsStrToArray($link['tags'], $casesensitive);
|
||||
|
@ -341,14 +355,14 @@ class LinkFilter
|
|||
* @param bool $casesensitive will convert everything to lowercase if false.
|
||||
*
|
||||
* @return array filtered tags string.
|
||||
*/
|
||||
*/
|
||||
public static function tagsStrToArray($tags, $casesensitive)
|
||||
{
|
||||
// We use UTF-8 conversion to handle various graphemes (i.e. cyrillic, or greek)
|
||||
$tagsOut = $casesensitive ? $tags : mb_convert_case($tags, MB_CASE_LOWER, 'UTF-8');
|
||||
$tagsOut = str_replace(',', ' ', $tagsOut);
|
||||
|
||||
return array_values(array_filter(explode(' ', trim($tagsOut)), 'strlen'));
|
||||
return preg_split('/\s+/', $tagsOut, -1, PREG_SPLIT_NO_EMPTY);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -89,7 +89,9 @@ function count_private($links)
|
|||
{
|
||||
$cpt = 0;
|
||||
foreach ($links as $link) {
|
||||
$cpt = $link['private'] == true ? $cpt + 1 : $cpt;
|
||||
if ($link['private']) {
|
||||
$cpt += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return $cpt;
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
<?php
|
||||
|
||||
use Psr\Log\LogLevel;
|
||||
use Shaarli\Config\ConfigManager;
|
||||
use Shaarli\NetscapeBookmarkParser\NetscapeBookmarkParser;
|
||||
use Katzgrau\KLogger\Logger;
|
||||
|
||||
/**
|
||||
* Utilities to import and export bookmarks using the Netscape format
|
||||
* TODO: Not static, use a container.
|
||||
*/
|
||||
class NetscapeBookmarkUtils
|
||||
{
|
||||
|
@ -85,14 +91,15 @@ class NetscapeBookmarkUtils
|
|||
/**
|
||||
* Imports Web bookmarks from an uploaded Netscape bookmark dump
|
||||
*
|
||||
* @param array $post Server $_POST parameters
|
||||
* @param array $files Server $_FILES parameters
|
||||
* @param LinkDB $linkDb Loaded LinkDB instance
|
||||
* @param string $pagecache Page cache
|
||||
* @param array $post Server $_POST parameters
|
||||
* @param array $files Server $_FILES parameters
|
||||
* @param LinkDB $linkDb Loaded LinkDB instance
|
||||
* @param ConfigManager $conf instance
|
||||
* @param History $history History instance
|
||||
*
|
||||
* @return string Summary of the bookmark import status
|
||||
*/
|
||||
public static function import($post, $files, $linkDb, $pagecache)
|
||||
public static function import($post, $files, $linkDb, $conf, $history)
|
||||
{
|
||||
$filename = $files['filetoupload']['name'];
|
||||
$filesize = $files['filetoupload']['size'];
|
||||
|
@ -119,10 +126,20 @@ class NetscapeBookmarkUtils
|
|||
$defaultPrivacy = 0;
|
||||
|
||||
$parser = new NetscapeBookmarkParser(
|
||||
true, // nested tag support
|
||||
$defaultTags, // additional user-specified tags
|
||||
strval(1 - $defaultPrivacy) // defaultPub = 1 - defaultPrivacy
|
||||
true, // nested tag support
|
||||
$defaultTags, // additional user-specified tags
|
||||
strval(1 - $defaultPrivacy), // defaultPub = 1 - defaultPrivacy
|
||||
$conf->get('resource.data_dir') // log path, will be overridden
|
||||
);
|
||||
$logger = new Logger(
|
||||
$conf->get('resource.data_dir'),
|
||||
! $conf->get('dev.debug') ? LogLevel::INFO : LogLevel::DEBUG,
|
||||
[
|
||||
'prefix' => 'import.',
|
||||
'extension' => 'log',
|
||||
]
|
||||
);
|
||||
$parser->setLogger($logger);
|
||||
$bookmarks = $parser->parseString($data);
|
||||
|
||||
$importCount = 0;
|
||||
|
@ -163,9 +180,11 @@ class NetscapeBookmarkUtils
|
|||
$newLink['id'] = $existingLink['id'];
|
||||
$newLink['created'] = $existingLink['created'];
|
||||
$newLink['updated'] = new DateTime();
|
||||
$newLink['shorturl'] = $existingLink['shorturl'];
|
||||
$linkDb[$existingLink['id']] = $newLink;
|
||||
$importCount++;
|
||||
$overwriteCount++;
|
||||
$history->updateLink($newLink);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -177,9 +196,10 @@ class NetscapeBookmarkUtils
|
|||
$newLink['shorturl'] = link_small_hash($newLink['created'], $newLink['id']);
|
||||
$linkDb[$newLink['id']] = $newLink;
|
||||
$importCount++;
|
||||
$history->addLink($newLink);
|
||||
}
|
||||
|
||||
$linkDb->save($pagecache);
|
||||
$linkDb->save($conf->get('resource.page_cache'));
|
||||
return self::importStatus(
|
||||
$filename,
|
||||
$filesize,
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
use Shaarli\Config\ConfigManager;
|
||||
|
||||
/**
|
||||
* This class is in charge of building the final page.
|
||||
* (This is basically a wrapper around RainTPL which pre-fills some fields.)
|
||||
|
@ -19,16 +21,23 @@ class PageBuilder
|
|||
*/
|
||||
protected $conf;
|
||||
|
||||
/**
|
||||
* @var LinkDB $linkDB instance.
|
||||
*/
|
||||
protected $linkDB;
|
||||
|
||||
/**
|
||||
* PageBuilder constructor.
|
||||
* $tpl is initialized at false for lazy loading.
|
||||
*
|
||||
* @param ConfigManager $conf Configuration Manager instance (reference).
|
||||
* @param ConfigManager $conf Configuration Manager instance (reference).
|
||||
* @param LinkDB $linkDB instance.
|
||||
*/
|
||||
function __construct(&$conf)
|
||||
public function __construct(&$conf, $linkDB = null)
|
||||
{
|
||||
$this->tpl = false;
|
||||
$this->conf = $conf;
|
||||
$this->linkDB = $linkDB;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -75,9 +84,13 @@ class PageBuilder
|
|||
}
|
||||
$this->tpl->assign('shaarlititle', $this->conf->get('general.title', 'Shaarli'));
|
||||
$this->tpl->assign('openshaarli', $this->conf->get('security.open_shaarli', false));
|
||||
$this->tpl->assign('showatom', $this->conf->get('feed.show_atom', false));
|
||||
$this->tpl->assign('showatom', $this->conf->get('feed.show_atom', true));
|
||||
$this->tpl->assign('feed_type', $this->conf->get('feed.show_atom', true) !== false ? 'atom' : 'rss');
|
||||
$this->tpl->assign('hide_timestamps', $this->conf->get('privacy.hide_timestamps', false));
|
||||
$this->tpl->assign('token', getToken($this->conf));
|
||||
if ($this->linkDB !== null) {
|
||||
$this->tpl->assign('tags', $this->linkDB->allTags());
|
||||
}
|
||||
// To be removed with a proper theme configuration.
|
||||
$this->tpl->assign('conf', $this->conf);
|
||||
}
|
||||
|
|
|
@ -31,6 +31,8 @@ class Router
|
|||
|
||||
public static $PAGE_EDITLINK = 'edit_link';
|
||||
|
||||
public static $PAGE_DELETELINK = 'delete_link';
|
||||
|
||||
public static $PAGE_EXPORT = 'export';
|
||||
|
||||
public static $PAGE_IMPORT = 'import';
|
||||
|
@ -120,6 +122,10 @@ class Router
|
|||
return self::$PAGE_EDITLINK;
|
||||
}
|
||||
|
||||
if (isset($get['delete_link'])) {
|
||||
return self::$PAGE_DELETELINK;
|
||||
}
|
||||
|
||||
if (startsWith($query, 'do='. self::$PAGE_EXPORT)) {
|
||||
return self::$PAGE_EXPORT;
|
||||
}
|
||||
|
|
33
application/ThemeUtils.php
Normal file
33
application/ThemeUtils.php
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace Shaarli;
|
||||
|
||||
/**
|
||||
* Class ThemeUtils
|
||||
*
|
||||
* Utility functions related to theme management.
|
||||
*
|
||||
* @package Shaarli
|
||||
*/
|
||||
class ThemeUtils
|
||||
{
|
||||
/**
|
||||
* Get a list of available themes.
|
||||
*
|
||||
* It will return the name of any directory present in the template folder.
|
||||
*
|
||||
* @param string $tplDir Templates main directory.
|
||||
*
|
||||
* @return array List of theme names.
|
||||
*/
|
||||
public static function getThemes($tplDir)
|
||||
{
|
||||
$allTheme = glob($tplDir.'/*', GLOB_ONLYDIR);
|
||||
$themes = [];
|
||||
foreach ($allTheme as $value) {
|
||||
$themes[] = str_replace($tplDir.'/', '', $value);
|
||||
}
|
||||
|
||||
return $themes;
|
||||
}
|
||||
}
|
|
@ -1,23 +1,42 @@
|
|||
<?php
|
||||
/**
|
||||
* Generates the timezone selection form and JavaScript.
|
||||
* Generates a list of available timezone continents and cities.
|
||||
*
|
||||
* Note: 'UTC/UTC' is mapped to 'UTC' to form a valid option
|
||||
* Two distinct array based on available timezones
|
||||
* and the one selected in the settings:
|
||||
* - (0) continents:
|
||||
* + list of available continents
|
||||
* + special key 'selected' containing the value of the selected timezone's continent
|
||||
* - (1) cities:
|
||||
* + list of available cities associated with their continent
|
||||
* + special key 'selected' containing the value of the selected timezone's city (without the continent)
|
||||
*
|
||||
* Example: preselect Europe/Paris
|
||||
* list($htmlform, $js) = generateTimeZoneForm('Europe/Paris');
|
||||
* Example:
|
||||
* [
|
||||
* [
|
||||
* 'America',
|
||||
* 'Europe',
|
||||
* 'selected' => 'Europe',
|
||||
* ],
|
||||
* [
|
||||
* ['continent' => 'America', 'city' => 'Toronto'],
|
||||
* ['continent' => 'Europe', 'city' => 'Paris'],
|
||||
* 'selected' => 'Paris',
|
||||
* ],
|
||||
* ];
|
||||
*
|
||||
* Notes:
|
||||
* - 'UTC/UTC' is mapped to 'UTC' to form a valid option
|
||||
* - a few timezone cities includes the country/state, such as Argentina/Buenos_Aires
|
||||
* - these arrays are designed to build timezone selects in template files with any HTML structure
|
||||
*
|
||||
* @param array $installedTimeZones List of installed timezones as string
|
||||
* @param string $preselectedTimezone preselected timezone (optional)
|
||||
*
|
||||
* @return array containing the generated HTML form and Javascript code
|
||||
* @return array[] continents and cities
|
||||
**/
|
||||
function generateTimeZoneForm($preselectedTimezone='')
|
||||
function generateTimeZoneData($installedTimeZones, $preselectedTimezone = '')
|
||||
{
|
||||
// Select the server timezone
|
||||
if ($preselectedTimezone == '') {
|
||||
$preselectedTimezone = date_default_timezone_get();
|
||||
}
|
||||
|
||||
if ($preselectedTimezone == 'UTC') {
|
||||
$pcity = $pcontinent = 'UTC';
|
||||
} else {
|
||||
|
@ -27,62 +46,30 @@ function generateTimeZoneForm($preselectedTimezone='')
|
|||
$pcity = substr($preselectedTimezone, $spos+1);
|
||||
}
|
||||
|
||||
// The list is in the form 'Europe/Paris', 'America/Argentina/Buenos_Aires'
|
||||
// We split the list in continents/cities.
|
||||
$continents = array();
|
||||
$cities = array();
|
||||
|
||||
// TODO: use a template to generate the HTML/Javascript form
|
||||
|
||||
foreach (timezone_identifiers_list() as $tz) {
|
||||
$continents = [];
|
||||
$cities = [];
|
||||
foreach ($installedTimeZones as $tz) {
|
||||
if ($tz == 'UTC') {
|
||||
$tz = 'UTC/UTC';
|
||||
}
|
||||
$spos = strpos($tz, '/');
|
||||
|
||||
if ($spos !== false) {
|
||||
$continent = substr($tz, 0, $spos);
|
||||
$city = substr($tz, $spos+1);
|
||||
$continents[$continent] = 1;
|
||||
|
||||
if (!isset($cities[$continent])) {
|
||||
$cities[$continent] = '';
|
||||
}
|
||||
$cities[$continent] .= '<option value="'.$city.'"';
|
||||
if ($pcity == $city) {
|
||||
$cities[$continent] .= ' selected="selected"';
|
||||
}
|
||||
$cities[$continent] .= '>'.$city.'</option>';
|
||||
// Ignore invalid timezones
|
||||
if ($spos === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$continent = substr($tz, 0, $spos);
|
||||
$city = substr($tz, $spos+1);
|
||||
$cities[] = ['continent' => $continent, 'city' => $city];
|
||||
$continents[$continent] = true;
|
||||
}
|
||||
|
||||
$continentsHtml = '';
|
||||
$continents = array_keys($continents);
|
||||
$continents['selected'] = $pcontinent;
|
||||
$cities['selected'] = $pcity;
|
||||
|
||||
foreach ($continents as $continent) {
|
||||
$continentsHtml .= '<option value="'.$continent.'"';
|
||||
if ($pcontinent == $continent) {
|
||||
$continentsHtml .= ' selected="selected"';
|
||||
}
|
||||
$continentsHtml .= '>'.$continent.'</option>';
|
||||
}
|
||||
|
||||
// Timezone selection form
|
||||
$timezoneForm = 'Continent:';
|
||||
$timezoneForm .= '<select name="continent" id="continent" onChange="onChangecontinent();">';
|
||||
$timezoneForm .= $continentsHtml.'</select>';
|
||||
$timezoneForm .= ' City:';
|
||||
$timezoneForm .= '<select name="city" id="city">'.$cities[$pcontinent].'</select><br />';
|
||||
|
||||
// Javascript handler - updates the city list when the user selects a continent
|
||||
$timezoneJs = '<script>';
|
||||
$timezoneJs .= 'function onChangecontinent() {';
|
||||
$timezoneJs .= 'document.getElementById("city").innerHTML =';
|
||||
$timezoneJs .= ' citiescontinent[document.getElementById("continent").value]; }';
|
||||
$timezoneJs .= 'var citiescontinent = '.json_encode($cities).';';
|
||||
$timezoneJs .= '</script>';
|
||||
|
||||
return array($timezoneForm, $timezoneJs);
|
||||
return [$continents, $cities];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
<?php
|
||||
use Shaarli\Config\ConfigJson;
|
||||
use Shaarli\Config\ConfigPhp;
|
||||
use Shaarli\Config\ConfigManager;
|
||||
|
||||
/**
|
||||
* Class Updater.
|
||||
|
@ -69,7 +72,7 @@ class Updater
|
|||
return $updatesRan;
|
||||
}
|
||||
|
||||
if ($this->methods == null) {
|
||||
if ($this->methods === null) {
|
||||
throw new UpdaterException('Couldn\'t retrieve Updater class methods.');
|
||||
}
|
||||
|
||||
|
@ -132,21 +135,6 @@ class Updater
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename tags starting with a '-' to work with tag exclusion search.
|
||||
*/
|
||||
public function updateMethodRenameDashTags()
|
||||
{
|
||||
$linklist = $this->linkDB->filterSearch();
|
||||
foreach ($linklist as $key => $link) {
|
||||
$link['tags'] = preg_replace('/(^| )\-/', '$1', $link['tags']);
|
||||
$link['tags'] = implode(' ', array_unique(LinkFilter::tagsStrToArray($link['tags'], true)));
|
||||
$this->linkDB[$key] = $link;
|
||||
}
|
||||
$this->linkDB->save($this->conf->get('resource.page_cache'));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move old configuration in PHP to the new config system in JSON format.
|
||||
*
|
||||
|
@ -257,6 +245,104 @@ class Updater
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename tags starting with a '-' to work with tag exclusion search.
|
||||
*/
|
||||
public function updateMethodRenameDashTags()
|
||||
{
|
||||
$linklist = $this->linkDB->filterSearch();
|
||||
foreach ($linklist as $key => $link) {
|
||||
$link['tags'] = preg_replace('/(^| )\-/', '$1', $link['tags']);
|
||||
$link['tags'] = implode(' ', array_unique(LinkFilter::tagsStrToArray($link['tags'], true)));
|
||||
$this->linkDB[$key] = $link;
|
||||
}
|
||||
$this->linkDB->save($this->conf->get('resource.page_cache'));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize API settings:
|
||||
* - api.enabled: true
|
||||
* - api.secret: generated secret
|
||||
*/
|
||||
public function updateMethodApiSettings()
|
||||
{
|
||||
if ($this->conf->exists('api.secret')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->conf->set('api.enabled', true);
|
||||
$this->conf->set(
|
||||
'api.secret',
|
||||
generate_api_secret(
|
||||
$this->conf->get('credentials.login'),
|
||||
$this->conf->get('credentials.salt')
|
||||
)
|
||||
);
|
||||
$this->conf->write($this->isLoggedIn);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* New setting: theme name. If the default theme is used, nothing to do.
|
||||
*
|
||||
* If the user uses a custom theme, raintpl_tpl dir is updated to the parent directory,
|
||||
* and the current theme is set as default in the theme setting.
|
||||
*
|
||||
* @return bool true if the update is successful, false otherwise.
|
||||
*/
|
||||
public function updateMethodDefaultTheme()
|
||||
{
|
||||
// raintpl_tpl isn't the root template directory anymore.
|
||||
// We run the update only if this folder still contains the template files.
|
||||
$tplDir = $this->conf->get('resource.raintpl_tpl');
|
||||
$tplFile = $tplDir . '/linklist.html';
|
||||
if (! file_exists($tplFile)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$parent = dirname($tplDir);
|
||||
$this->conf->set('resource.raintpl_tpl', $parent);
|
||||
$this->conf->set('resource.theme', trim(str_replace($parent, '', $tplDir), '/'));
|
||||
$this->conf->write($this->isLoggedIn);
|
||||
|
||||
// Dependency injection gore
|
||||
RainTPL::$tpl_dir = $tplDir;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the file to inc/user.css to data/user.css.
|
||||
*
|
||||
* Note: Due to hardcoded paths, it's not unit testable. But one line of code should be fine.
|
||||
*
|
||||
* @return bool true if the update is successful, false otherwise.
|
||||
*/
|
||||
public function updateMethodMoveUserCss()
|
||||
{
|
||||
if (! is_file('inc/user.css')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return rename('inc/user.css', 'data/user.css');
|
||||
}
|
||||
|
||||
/**
|
||||
* While the new default theme is in an unstable state
|
||||
* continue to use the vintage theme
|
||||
*/
|
||||
public function updateMethodDefaultThemeVintage()
|
||||
{
|
||||
if ($this->conf->get('resource.theme') !== 'default') {
|
||||
return true;
|
||||
}
|
||||
$this->conf->set('resource.theme', 'vintage');
|
||||
$this->conf->write($this->isLoggedIn);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* * `markdown_escape` is a new setting, set to true as default.
|
||||
*
|
||||
|
@ -278,6 +364,93 @@ class Updater
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add 'http://' to Piwik URL the setting is set.
|
||||
*
|
||||
* @return bool true if the update is successful, false otherwise.
|
||||
*/
|
||||
public function updateMethodPiwikUrl()
|
||||
{
|
||||
if (! $this->conf->exists('plugins.PIWIK_URL') || startsWith($this->conf->get('plugins.PIWIK_URL'), 'http')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->conf->set('plugins.PIWIK_URL', 'http://'. $this->conf->get('plugins.PIWIK_URL'));
|
||||
$this->conf->write($this->isLoggedIn);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use ATOM feed as default.
|
||||
*/
|
||||
public function updateMethodAtomDefault()
|
||||
{
|
||||
if (!$this->conf->exists('feed.show_atom') || $this->conf->get('feed.show_atom') === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->conf->set('feed.show_atom', true);
|
||||
$this->conf->write($this->isLoggedIn);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update updates.check_updates_branch setting.
|
||||
*
|
||||
* If the current major version digit matches the latest branch
|
||||
* major version digit, we set the branch to `latest`,
|
||||
* otherwise we'll check updates on the `stable` branch.
|
||||
*
|
||||
* No update required for the dev version.
|
||||
*
|
||||
* Note: due to hardcoded URL and lack of dependency injection, this is not unit testable.
|
||||
*
|
||||
* FIXME! This needs to be removed when we switch to first digit major version
|
||||
* instead of the second one since the versionning process will change.
|
||||
*/
|
||||
public function updateMethodCheckUpdateRemoteBranch()
|
||||
{
|
||||
if (shaarli_version === 'dev' || $this->conf->get('updates.check_updates_branch') === 'latest') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get latest branch major version digit
|
||||
$latestVersion = ApplicationUtils::getLatestGitVersionCode(
|
||||
'https://raw.githubusercontent.com/shaarli/Shaarli/latest/shaarli_version.php',
|
||||
5
|
||||
);
|
||||
if (preg_match('/(\d+)\.\d+$/', $latestVersion, $matches) === false) {
|
||||
return false;
|
||||
}
|
||||
$latestMajor = $matches[1];
|
||||
|
||||
// Get current major version digit
|
||||
preg_match('/(\d+)\.\d+$/', shaarli_version, $matches);
|
||||
$currentMajor = $matches[1];
|
||||
|
||||
if ($currentMajor === $latestMajor) {
|
||||
$branch = 'latest';
|
||||
} else {
|
||||
$branch = 'stable';
|
||||
}
|
||||
$this->conf->set('updates.check_updates_branch', $branch);
|
||||
$this->conf->write($this->isLoggedIn);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset history store file due to date format change.
|
||||
*/
|
||||
public function updateMethodResetHistoryFile()
|
||||
{
|
||||
if (is_file($this->conf->get('resource.history'))) {
|
||||
unlink($this->conf->get('resource.history'));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -94,7 +94,10 @@ class Url
|
|||
'utm_',
|
||||
|
||||
// ATInternet
|
||||
'xtor='
|
||||
'xtor=',
|
||||
|
||||
// Other
|
||||
'campaign_'
|
||||
);
|
||||
|
||||
private static $annoyingFragments = array(
|
||||
|
|
|
@ -216,18 +216,222 @@ function is_session_id_valid($sessionId)
|
|||
function autoLocale($headerLocale)
|
||||
{
|
||||
// Default if browser does not send HTTP_ACCEPT_LANGUAGE
|
||||
$attempts = array('en_US');
|
||||
if (isset($headerLocale)) {
|
||||
// (It's a bit crude, but it works very well. Preferred language is always presented first.)
|
||||
if (preg_match('/([a-z]{2})-?([a-z]{2})?/i', $headerLocale, $matches)) {
|
||||
$loc = $matches[1] . (!empty($matches[2]) ? '_' . strtoupper($matches[2]) : '');
|
||||
$attempts = array(
|
||||
$loc.'.UTF-8', $loc, str_replace('_', '-', $loc).'.UTF-8', str_replace('_', '-', $loc),
|
||||
$loc . '_' . strtoupper($loc).'.UTF-8', $loc . '_' . strtoupper($loc),
|
||||
$loc . '_' . $loc.'.UTF-8', $loc . '_' . $loc, $loc . '-' . strtoupper($loc).'.UTF-8',
|
||||
$loc . '-' . strtoupper($loc), $loc . '-' . $loc.'.UTF-8', $loc . '-' . $loc
|
||||
);
|
||||
$locales = array('en_US', 'en_US.utf8', 'en_US.UTF-8');
|
||||
if (! empty($headerLocale)) {
|
||||
if (preg_match_all('/([a-z]{2,3})[-_]?([a-z]{2})?,?/i', $headerLocale, $matches, PREG_SET_ORDER)) {
|
||||
$attempts = [];
|
||||
foreach ($matches as $match) {
|
||||
$first = [strtolower($match[1]), strtoupper($match[1])];
|
||||
$separators = ['_', '-'];
|
||||
$encodings = ['utf8', 'UTF-8'];
|
||||
if (!empty($match[2])) {
|
||||
$second = [strtoupper($match[2]), strtolower($match[2])];
|
||||
$items = [$first, $separators, $second, ['.'], $encodings];
|
||||
} else {
|
||||
$items = [$first, $separators, $first, ['.'], $encodings];
|
||||
}
|
||||
$attempts = array_merge($attempts, iterator_to_array(cartesian_product_generator($items)));
|
||||
}
|
||||
|
||||
if (! empty($attempts)) {
|
||||
$locales = array_merge(array_map('implode', $attempts), $locales);
|
||||
}
|
||||
}
|
||||
}
|
||||
setlocale(LC_ALL, $attempts);
|
||||
|
||||
setlocale(LC_ALL, $locales);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a Generator object representing the cartesian product from given $items.
|
||||
*
|
||||
* Example:
|
||||
* [['a'], ['b', 'c']]
|
||||
* will generate:
|
||||
* [
|
||||
* ['a', 'b'],
|
||||
* ['a', 'c'],
|
||||
* ]
|
||||
*
|
||||
* @param array $items array of array of string
|
||||
*
|
||||
* @return Generator representing the cartesian product of given array.
|
||||
*
|
||||
* @see https://en.wikipedia.org/wiki/Cartesian_product
|
||||
*/
|
||||
function cartesian_product_generator($items)
|
||||
{
|
||||
if (empty($items)) {
|
||||
yield [];
|
||||
}
|
||||
$subArray = array_pop($items);
|
||||
if (empty($subArray)) {
|
||||
return;
|
||||
}
|
||||
foreach (cartesian_product_generator($items) as $item) {
|
||||
foreach ($subArray as $value) {
|
||||
yield $item + [count($item) => $value];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a default API secret.
|
||||
*
|
||||
* Note that the random-ish methods used in this function are predictable,
|
||||
* which makes them NOT suitable for crypto.
|
||||
* BUT the random string is salted with the salt and hashed with the username.
|
||||
* It makes the generated API secret secured enough for Shaarli.
|
||||
*
|
||||
* PHP 7 provides random_int(), designed for cryptography.
|
||||
* More info: http://stackoverflow.com/questions/4356289/php-random-string-generator
|
||||
|
||||
* @param string $username Shaarli login username
|
||||
* @param string $salt Shaarli password hash salt
|
||||
*
|
||||
* @return string|bool Generated API secret, 12 char length.
|
||||
* Or false if invalid parameters are provided (which will make the API unusable).
|
||||
*/
|
||||
function generate_api_secret($username, $salt)
|
||||
{
|
||||
if (empty($username) || empty($salt)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return str_shuffle(substr(hash_hmac('sha512', uniqid($salt), $username), 10, 12));
|
||||
}
|
||||
|
||||
/**
|
||||
* Trim string, replace sequences of whitespaces by a single space.
|
||||
* PHP equivalent to `normalize-space` XSLT function.
|
||||
*
|
||||
* @param string $string Input string.
|
||||
*
|
||||
* @return mixed Normalized string.
|
||||
*/
|
||||
function normalize_spaces($string)
|
||||
{
|
||||
return preg_replace('/\s{2,}/', ' ', trim($string));
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the date according to the locale.
|
||||
*
|
||||
* Requires php-intl to display international datetimes,
|
||||
* otherwise default format '%c' will be returned.
|
||||
*
|
||||
* @param DateTime $date to format.
|
||||
* @param bool $time Displays time if true.
|
||||
* @param bool $intl Use international format if true.
|
||||
*
|
||||
* @return bool|string Formatted date, or false if the input is invalid.
|
||||
*/
|
||||
function format_date($date, $time = true, $intl = true)
|
||||
{
|
||||
if (! $date instanceof DateTime) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $intl || ! class_exists('IntlDateFormatter')) {
|
||||
$format = $time ? '%c' : '%x';
|
||||
return strftime($format, $date->getTimestamp());
|
||||
}
|
||||
|
||||
$formatter = new IntlDateFormatter(
|
||||
setlocale(LC_TIME, 0),
|
||||
IntlDateFormatter::LONG,
|
||||
$time ? IntlDateFormatter::LONG : IntlDateFormatter::NONE
|
||||
);
|
||||
|
||||
return $formatter->format($date);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the input is an integer, no matter its real type.
|
||||
*
|
||||
* PHP is a bit messy regarding this:
|
||||
* - is_int returns false if the input is a string
|
||||
* - ctype_digit returns false if the input is an integer or negative
|
||||
*
|
||||
* @param mixed $input value
|
||||
*
|
||||
* @return bool true if the input is an integer, false otherwise
|
||||
*/
|
||||
function is_integer_mixed($input)
|
||||
{
|
||||
if (is_array($input) || is_bool($input) || is_object($input)) {
|
||||
return false;
|
||||
}
|
||||
$input = strval($input);
|
||||
return ctype_digit($input) || (startsWith($input, '-') && ctype_digit(substr($input, 1)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert post_max_size/upload_max_filesize (e.g. '16M') parameters to bytes.
|
||||
*
|
||||
* @param string $val Size expressed in string.
|
||||
*
|
||||
* @return int Size expressed in bytes.
|
||||
*/
|
||||
function return_bytes($val)
|
||||
{
|
||||
if (is_integer_mixed($val) || $val === '0' || empty($val)) {
|
||||
return $val;
|
||||
}
|
||||
$val = trim($val);
|
||||
$last = strtolower($val[strlen($val)-1]);
|
||||
$val = intval(substr($val, 0, -1));
|
||||
switch($last) {
|
||||
case 'g': $val *= 1024;
|
||||
case 'm': $val *= 1024;
|
||||
case 'k': $val *= 1024;
|
||||
}
|
||||
return $val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a human readable size from bytes.
|
||||
*
|
||||
* @param int $bytes value
|
||||
*
|
||||
* @return string Human readable size
|
||||
*/
|
||||
function human_bytes($bytes)
|
||||
{
|
||||
if ($bytes === '') {
|
||||
return t('Setting not set');
|
||||
}
|
||||
if (! is_integer_mixed($bytes)) {
|
||||
return $bytes;
|
||||
}
|
||||
$bytes = intval($bytes);
|
||||
if ($bytes === 0) {
|
||||
return t('Unlimited');
|
||||
}
|
||||
|
||||
$units = [t('B'), t('kiB'), t('MiB'), t('GiB')];
|
||||
for ($i = 0; $i < count($units) && $bytes >= 1024; ++$i) {
|
||||
$bytes /= 1024;
|
||||
}
|
||||
|
||||
return round($bytes) . $units[$i];
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to determine max file size for uploads (POST).
|
||||
* Returns an integer (in bytes) or formatted depending on $format.
|
||||
*
|
||||
* @param mixed $limitPost post_max_size PHP setting
|
||||
* @param mixed $limitUpload upload_max_filesize PHP setting
|
||||
* @param bool $format Format max upload size to human readable size
|
||||
*
|
||||
* @return int|string max upload file size
|
||||
*/
|
||||
function get_max_upload_size($limitPost, $limitUpload, $format = true)
|
||||
{
|
||||
$size1 = return_bytes($limitPost);
|
||||
$size2 = return_bytes($limitUpload);
|
||||
// Return the smaller of two:
|
||||
$maxsize = min($size1, $size2);
|
||||
return $format ? human_bytes($maxsize) : $maxsize;
|
||||
}
|
||||
|
|
138
application/api/ApiMiddleware.php
Normal file
138
application/api/ApiMiddleware.php
Normal file
|
@ -0,0 +1,138 @@
|
|||
<?php
|
||||
namespace Shaarli\Api;
|
||||
|
||||
use Shaarli\Api\Exceptions\ApiException;
|
||||
use Shaarli\Api\Exceptions\ApiAuthorizationException;
|
||||
|
||||
use Shaarli\Config\ConfigManager;
|
||||
use Slim\Container;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
|
||||
/**
|
||||
* Class ApiMiddleware
|
||||
*
|
||||
* This will be called before accessing any API Controller.
|
||||
* Its role is to make sure that the API is enabled, configured, and to validate the JWT token.
|
||||
*
|
||||
* If the request is validated, the controller is called, otherwise a JSON error response is returned.
|
||||
*
|
||||
* @package Api
|
||||
*/
|
||||
class ApiMiddleware
|
||||
{
|
||||
/**
|
||||
* @var int JWT token validity in seconds (9 min).
|
||||
*/
|
||||
public static $TOKEN_DURATION = 540;
|
||||
|
||||
/**
|
||||
* @var Container: contains conf, plugins, etc.
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* @var ConfigManager instance.
|
||||
*/
|
||||
protected $conf;
|
||||
|
||||
/**
|
||||
* ApiMiddleware constructor.
|
||||
*
|
||||
* @param Container $container instance.
|
||||
*/
|
||||
public function __construct($container)
|
||||
{
|
||||
$this->container = $container;
|
||||
$this->conf = $this->container->get('conf');
|
||||
$this->setLinkDb($this->conf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Middleware execution:
|
||||
* - check the API request
|
||||
* - execute the controller
|
||||
* - return the response
|
||||
*
|
||||
* @param Request $request Slim request
|
||||
* @param Response $response Slim response
|
||||
* @param callable $next Next action
|
||||
*
|
||||
* @return Response response.
|
||||
*/
|
||||
public function __invoke($request, $response, $next)
|
||||
{
|
||||
try {
|
||||
$this->checkRequest($request);
|
||||
$response = $next($request, $response);
|
||||
} catch(ApiException $e) {
|
||||
$e->setResponse($response);
|
||||
$e->setDebug($this->conf->get('dev.debug', false));
|
||||
$response = $e->getApiResponse();
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the request validity (HTTP method, request value, etc.),
|
||||
* that the API is enabled, and the JWT token validity.
|
||||
*
|
||||
* @param Request $request Slim request
|
||||
*
|
||||
* @throws ApiAuthorizationException The API is disabled or the token is invalid.
|
||||
*/
|
||||
protected function checkRequest($request)
|
||||
{
|
||||
if (! $this->conf->get('api.enabled', true)) {
|
||||
throw new ApiAuthorizationException('API is disabled');
|
||||
}
|
||||
$this->checkToken($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the JWT token is set and valid.
|
||||
* The API secret setting must be set.
|
||||
*
|
||||
* @param Request $request Slim request
|
||||
*
|
||||
* @throws ApiAuthorizationException The token couldn't be validated.
|
||||
*/
|
||||
protected function checkToken($request) {
|
||||
if (! $request->hasHeader('Authorization')) {
|
||||
throw new ApiAuthorizationException('JWT token not provided');
|
||||
}
|
||||
|
||||
if (empty($this->conf->get('api.secret'))) {
|
||||
throw new ApiAuthorizationException('Token secret must be set in Shaarli\'s administration');
|
||||
}
|
||||
|
||||
$authorization = $request->getHeaderLine('Authorization');
|
||||
|
||||
if (! preg_match('/^Bearer (.*)/i', $authorization, $matches)) {
|
||||
throw new ApiAuthorizationException('Invalid JWT header');
|
||||
}
|
||||
|
||||
ApiUtils::validateJwtToken($matches[1], $this->conf->get('api.secret'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate a new LinkDB including private links,
|
||||
* and load in the Slim container.
|
||||
*
|
||||
* FIXME! LinkDB could use a refactoring to avoid this trick.
|
||||
*
|
||||
* @param ConfigManager $conf instance.
|
||||
*/
|
||||
protected function setLinkDb($conf)
|
||||
{
|
||||
$linkDb = new \LinkDB(
|
||||
$conf->get('resource.datastore'),
|
||||
true,
|
||||
$conf->get('privacy.hide_public_links'),
|
||||
$conf->get('redirector.url'),
|
||||
$conf->get('redirector.encode_url')
|
||||
);
|
||||
$this->container['db'] = $linkDb;
|
||||
}
|
||||
}
|
137
application/api/ApiUtils.php
Normal file
137
application/api/ApiUtils.php
Normal file
|
@ -0,0 +1,137 @@
|
|||
<?php
|
||||
namespace Shaarli\Api;
|
||||
|
||||
use Shaarli\Base64Url;
|
||||
use Shaarli\Api\Exceptions\ApiAuthorizationException;
|
||||
|
||||
/**
|
||||
* REST API utilities
|
||||
*/
|
||||
class ApiUtils
|
||||
{
|
||||
/**
|
||||
* Validates a JWT token authenticity.
|
||||
*
|
||||
* @param string $token JWT token extracted from the headers.
|
||||
* @param string $secret API secret set in the settings.
|
||||
*
|
||||
* @throws ApiAuthorizationException the token is not valid.
|
||||
*/
|
||||
public static function validateJwtToken($token, $secret)
|
||||
{
|
||||
$parts = explode('.', $token);
|
||||
if (count($parts) != 3 || strlen($parts[0]) == 0 || strlen($parts[1]) == 0) {
|
||||
throw new ApiAuthorizationException('Malformed JWT token');
|
||||
}
|
||||
|
||||
$genSign = Base64Url::encode(hash_hmac('sha512', $parts[0] .'.'. $parts[1], $secret, true));
|
||||
if ($parts[2] != $genSign) {
|
||||
throw new ApiAuthorizationException('Invalid JWT signature');
|
||||
}
|
||||
|
||||
$header = json_decode(Base64Url::decode($parts[0]));
|
||||
if ($header === null) {
|
||||
throw new ApiAuthorizationException('Invalid JWT header');
|
||||
}
|
||||
|
||||
$payload = json_decode(Base64Url::decode($parts[1]));
|
||||
if ($payload === null) {
|
||||
throw new ApiAuthorizationException('Invalid JWT payload');
|
||||
}
|
||||
|
||||
if (empty($payload->iat)
|
||||
|| $payload->iat > time()
|
||||
|| time() - $payload->iat > ApiMiddleware::$TOKEN_DURATION
|
||||
) {
|
||||
throw new ApiAuthorizationException('Invalid JWT issued time');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a Link for the REST API.
|
||||
*
|
||||
* @param array $link Link data read from the datastore.
|
||||
* @param string $indexUrl Shaarli's index URL (used for relative URL).
|
||||
*
|
||||
* @return array Link data formatted for the REST API.
|
||||
*/
|
||||
public static function formatLink($link, $indexUrl)
|
||||
{
|
||||
$out['id'] = $link['id'];
|
||||
// Not an internal link
|
||||
if ($link['url'][0] != '?') {
|
||||
$out['url'] = $link['url'];
|
||||
} else {
|
||||
$out['url'] = $indexUrl . $link['url'];
|
||||
}
|
||||
$out['shorturl'] = $link['shorturl'];
|
||||
$out['title'] = $link['title'];
|
||||
$out['description'] = $link['description'];
|
||||
$out['tags'] = preg_split('/\s+/', $link['tags'], -1, PREG_SPLIT_NO_EMPTY);
|
||||
$out['private'] = $link['private'] == true;
|
||||
$out['created'] = $link['created']->format(\DateTime::ATOM);
|
||||
if (! empty($link['updated'])) {
|
||||
$out['updated'] = $link['updated']->format(\DateTime::ATOM);
|
||||
} else {
|
||||
$out['updated'] = '';
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a link given through a request, to a valid link for LinkDB.
|
||||
*
|
||||
* If no URL is provided, it will generate a local note URL.
|
||||
* If no title is provided, it will use the URL as title.
|
||||
*
|
||||
* @param array $input Request Link.
|
||||
* @param bool $defaultPrivate Request Link.
|
||||
*
|
||||
* @return array Formatted link.
|
||||
*/
|
||||
public static function buildLinkFromRequest($input, $defaultPrivate)
|
||||
{
|
||||
$input['url'] = ! empty($input['url']) ? cleanup_url($input['url']) : '';
|
||||
if (isset($input['private'])) {
|
||||
$private = filter_var($input['private'], FILTER_VALIDATE_BOOLEAN);
|
||||
} else {
|
||||
$private = $defaultPrivate;
|
||||
}
|
||||
|
||||
$link = [
|
||||
'title' => ! empty($input['title']) ? $input['title'] : $input['url'],
|
||||
'url' => $input['url'],
|
||||
'description' => ! empty($input['description']) ? $input['description'] : '',
|
||||
'tags' => ! empty($input['tags']) ? implode(' ', $input['tags']) : '',
|
||||
'private' => $private,
|
||||
'created' => new \DateTime(),
|
||||
];
|
||||
return $link;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update link fields using an updated link object.
|
||||
*
|
||||
* @param array $oldLink data
|
||||
* @param array $newLink data
|
||||
*
|
||||
* @return array $oldLink updated with $newLink values
|
||||
*/
|
||||
public static function updateLink($oldLink, $newLink)
|
||||
{
|
||||
foreach (['title', 'url', 'description', 'tags', 'private'] as $field) {
|
||||
$oldLink[$field] = $newLink[$field];
|
||||
}
|
||||
$oldLink['updated'] = new \DateTime();
|
||||
|
||||
if (empty($oldLink['url'])) {
|
||||
$oldLink['url'] = '?' . $oldLink['shorturl'];
|
||||
}
|
||||
|
||||
if (empty($oldLink['title'])) {
|
||||
$oldLink['title'] = $oldLink['url'];
|
||||
}
|
||||
|
||||
return $oldLink;
|
||||
}
|
||||
}
|
71
application/api/controllers/ApiController.php
Normal file
71
application/api/controllers/ApiController.php
Normal file
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
namespace Shaarli\Api\Controllers;
|
||||
|
||||
use Shaarli\Config\ConfigManager;
|
||||
use \Slim\Container;
|
||||
|
||||
/**
|
||||
* Abstract Class ApiController
|
||||
*
|
||||
* Defines REST API Controller dependencies injected from the container.
|
||||
*
|
||||
* @package Api\Controllers
|
||||
*/
|
||||
abstract class ApiController
|
||||
{
|
||||
/**
|
||||
* @var Container
|
||||
*/
|
||||
protected $ci;
|
||||
|
||||
/**
|
||||
* @var ConfigManager
|
||||
*/
|
||||
protected $conf;
|
||||
|
||||
/**
|
||||
* @var \LinkDB
|
||||
*/
|
||||
protected $linkDb;
|
||||
|
||||
/**
|
||||
* @var \History
|
||||
*/
|
||||
protected $history;
|
||||
|
||||
/**
|
||||
* @var int|null JSON style option.
|
||||
*/
|
||||
protected $jsonStyle;
|
||||
|
||||
/**
|
||||
* ApiController constructor.
|
||||
*
|
||||
* Note: enabling debug mode displays JSON with readable formatting.
|
||||
*
|
||||
* @param Container $ci Slim container.
|
||||
*/
|
||||
public function __construct(Container $ci)
|
||||
{
|
||||
$this->ci = $ci;
|
||||
$this->conf = $ci->get('conf');
|
||||
$this->linkDb = $ci->get('db');
|
||||
$this->history = $ci->get('history');
|
||||
if ($this->conf->get('dev.debug', false)) {
|
||||
$this->jsonStyle = JSON_PRETTY_PRINT;
|
||||
} else {
|
||||
$this->jsonStyle = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the container.
|
||||
*
|
||||
* @return Container
|
||||
*/
|
||||
public function getCi()
|
||||
{
|
||||
return $this->ci;
|
||||
}
|
||||
}
|
70
application/api/controllers/History.php
Normal file
70
application/api/controllers/History.php
Normal file
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace Shaarli\Api\Controllers;
|
||||
|
||||
use Shaarli\Api\Exceptions\ApiBadParametersException;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
|
||||
/**
|
||||
* Class History
|
||||
*
|
||||
* REST API Controller: /history
|
||||
*
|
||||
* @package Shaarli\Api\Controllers
|
||||
*/
|
||||
class History extends ApiController
|
||||
{
|
||||
/**
|
||||
* Service providing operation regarding Shaarli datastore and settings.
|
||||
*
|
||||
* @param Request $request Slim request.
|
||||
* @param Response $response Slim response.
|
||||
*
|
||||
* @return Response response.
|
||||
*
|
||||
* @throws ApiBadParametersException Invalid parameters.
|
||||
*/
|
||||
public function getHistory($request, $response)
|
||||
{
|
||||
$history = $this->history->getHistory();
|
||||
|
||||
// Return history operations from the {offset}th, starting from {since}.
|
||||
$since = \DateTime::createFromFormat(\DateTime::ATOM, $request->getParam('since'));
|
||||
$offset = $request->getParam('offset');
|
||||
if (empty($offset)) {
|
||||
$offset = 0;
|
||||
}
|
||||
else if (ctype_digit($offset)) {
|
||||
$offset = (int) $offset;
|
||||
} else {
|
||||
throw new ApiBadParametersException('Invalid offset');
|
||||
}
|
||||
|
||||
// limit parameter is either a number of links or 'all' for everything.
|
||||
$limit = $request->getParam('limit');
|
||||
if (empty($limit)) {
|
||||
$limit = count($history);
|
||||
} else if (ctype_digit($limit)) {
|
||||
$limit = (int) $limit;
|
||||
} else {
|
||||
throw new ApiBadParametersException('Invalid limit');
|
||||
}
|
||||
|
||||
$out = [];
|
||||
$i = 0;
|
||||
foreach ($history as $entry) {
|
||||
if ((! empty($since) && $entry['datetime'] <= $since) || count($out) >= $limit) {
|
||||
break;
|
||||
}
|
||||
if (++$i > $offset) {
|
||||
$out[$i] = $entry;
|
||||
$out[$i]['datetime'] = $out[$i]['datetime']->format(\DateTime::ATOM);
|
||||
}
|
||||
}
|
||||
$out = array_values($out);
|
||||
|
||||
return $response->withJson($out, 200, $this->jsonStyle);
|
||||
}
|
||||
}
|
42
application/api/controllers/Info.php
Normal file
42
application/api/controllers/Info.php
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace Shaarli\Api\Controllers;
|
||||
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
|
||||
/**
|
||||
* Class Info
|
||||
*
|
||||
* REST API Controller: /info
|
||||
*
|
||||
* @package Api\Controllers
|
||||
* @see http://shaarli.github.io/api-documentation/#links-instance-information-get
|
||||
*/
|
||||
class Info extends ApiController
|
||||
{
|
||||
/**
|
||||
* Service providing various information about Shaarli instance.
|
||||
*
|
||||
* @param Request $request Slim request.
|
||||
* @param Response $response Slim response.
|
||||
*
|
||||
* @return Response response.
|
||||
*/
|
||||
public function getInfo($request, $response)
|
||||
{
|
||||
$info = [
|
||||
'global_counter' => count($this->linkDb),
|
||||
'private_counter' => count_private($this->linkDb),
|
||||
'settings' => array(
|
||||
'title' => $this->conf->get('general.title', 'Shaarli'),
|
||||
'header_link' => $this->conf->get('general.header_link', '?'),
|
||||
'timezone' => $this->conf->get('general.timezone', 'UTC'),
|
||||
'enabled_plugins' => $this->conf->get('general.enabled_plugins', []),
|
||||
'default_private_links' => $this->conf->get('privacy.default_private_links', false),
|
||||
),
|
||||
];
|
||||
|
||||
return $response->withJson($info, 200, $this->jsonStyle);
|
||||
}
|
||||
}
|
217
application/api/controllers/Links.php
Normal file
217
application/api/controllers/Links.php
Normal file
|
@ -0,0 +1,217 @@
|
|||
<?php
|
||||
|
||||
namespace Shaarli\Api\Controllers;
|
||||
|
||||
use Shaarli\Api\ApiUtils;
|
||||
use Shaarli\Api\Exceptions\ApiBadParametersException;
|
||||
use Shaarli\Api\Exceptions\ApiLinkNotFoundException;
|
||||
use Slim\Http\Request;
|
||||
use Slim\Http\Response;
|
||||
|
||||
/**
|
||||
* Class Links
|
||||
*
|
||||
* REST API Controller: all services related to links collection.
|
||||
*
|
||||
* @package Api\Controllers
|
||||
* @see http://shaarli.github.io/api-documentation/#links-links-collection
|
||||
*/
|
||||
class Links extends ApiController
|
||||
{
|
||||
/**
|
||||
* @var int Number of links returned if no limit is provided.
|
||||
*/
|
||||
public static $DEFAULT_LIMIT = 20;
|
||||
|
||||
/**
|
||||
* Retrieve a list of links, allowing different filters.
|
||||
*
|
||||
* @param Request $request Slim request.
|
||||
* @param Response $response Slim response.
|
||||
*
|
||||
* @return Response response.
|
||||
*
|
||||
* @throws ApiBadParametersException Invalid parameters.
|
||||
*/
|
||||
public function getLinks($request, $response)
|
||||
{
|
||||
$private = $request->getParam('visibility');
|
||||
$links = $this->linkDb->filterSearch(
|
||||
[
|
||||
'searchtags' => $request->getParam('searchtags', ''),
|
||||
'searchterm' => $request->getParam('searchterm', ''),
|
||||
],
|
||||
false,
|
||||
$private
|
||||
);
|
||||
|
||||
// Return links from the {offset}th link, starting from 0.
|
||||
$offset = $request->getParam('offset');
|
||||
if (! empty($offset) && ! ctype_digit($offset)) {
|
||||
throw new ApiBadParametersException('Invalid offset');
|
||||
}
|
||||
$offset = ! empty($offset) ? intval($offset) : 0;
|
||||
if ($offset > count($links)) {
|
||||
return $response->withJson([], 200, $this->jsonStyle);
|
||||
}
|
||||
|
||||
// limit parameter is either a number of links or 'all' for everything.
|
||||
$limit = $request->getParam('limit');
|
||||
if (empty($limit)) {
|
||||
$limit = self::$DEFAULT_LIMIT;
|
||||
} else if (ctype_digit($limit)) {
|
||||
$limit = intval($limit);
|
||||
} else if ($limit === 'all') {
|
||||
$limit = count($links);
|
||||
} else {
|
||||
throw new ApiBadParametersException('Invalid limit');
|
||||
}
|
||||
|
||||
// 'environment' is set by Slim and encapsulate $_SERVER.
|
||||
$index = index_url($this->ci['environment']);
|
||||
|
||||
$out = [];
|
||||
$cpt = 0;
|
||||
foreach ($links as $link) {
|
||||
if (count($out) >= $limit) {
|
||||
break;
|
||||
}
|
||||
if ($cpt++ >= $offset) {
|
||||
$out[] = ApiUtils::formatLink($link, $index);
|
||||
}
|
||||
}
|
||||
|
||||
return $response->withJson($out, 200, $this->jsonStyle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a single formatted link by its ID.
|
||||
*
|
||||
* @param Request $request Slim request.
|
||||
* @param Response $response Slim response.
|
||||
* @param array $args Path parameters. including the ID.
|
||||
*
|
||||
* @return Response containing the link array.
|
||||
*
|
||||
* @throws ApiLinkNotFoundException generating a 404 error.
|
||||
*/
|
||||
public function getLink($request, $response, $args)
|
||||
{
|
||||
if (!isset($this->linkDb[$args['id']])) {
|
||||
throw new ApiLinkNotFoundException();
|
||||
}
|
||||
$index = index_url($this->ci['environment']);
|
||||
$out = ApiUtils::formatLink($this->linkDb[$args['id']], $index);
|
||||
|
||||
return $response->withJson($out, 200, $this->jsonStyle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new link from posted request body.
|
||||
*
|
||||
* @param Request $request Slim request.
|
||||
* @param Response $response Slim response.
|
||||
*
|
||||
* @return Response response.
|
||||
*/
|
||||
public function postLink($request, $response)
|
||||
{
|
||||
$data = $request->getParsedBody();
|
||||
$link = ApiUtils::buildLinkFromRequest($data, $this->conf->get('privacy.default_private_links'));
|
||||
// duplicate by URL, return 409 Conflict
|
||||
if (! empty($link['url']) && ! empty($dup = $this->linkDb->getLinkFromUrl($link['url']))) {
|
||||
return $response->withJson(
|
||||
ApiUtils::formatLink($dup, index_url($this->ci['environment'])),
|
||||
409,
|
||||
$this->jsonStyle
|
||||
);
|
||||
}
|
||||
|
||||
$link['id'] = $this->linkDb->getNextId();
|
||||
$link['shorturl'] = link_small_hash($link['created'], $link['id']);
|
||||
|
||||
// note: general relative URL
|
||||
if (empty($link['url'])) {
|
||||
$link['url'] = '?' . $link['shorturl'];
|
||||
}
|
||||
|
||||
if (empty($link['title'])) {
|
||||
$link['title'] = $link['url'];
|
||||
}
|
||||
|
||||
$this->linkDb[$link['id']] = $link;
|
||||
$this->linkDb->save($this->conf->get('resource.page_cache'));
|
||||
$this->history->addLink($link);
|
||||
$out = ApiUtils::formatLink($link, index_url($this->ci['environment']));
|
||||
$redirect = $this->ci->router->relativePathFor('getLink', ['id' => $link['id']]);
|
||||
return $response->withAddedHeader('Location', $redirect)
|
||||
->withJson($out, 201, $this->jsonStyle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an existing link from posted request body.
|
||||
*
|
||||
* @param Request $request Slim request.
|
||||
* @param Response $response Slim response.
|
||||
* @param array $args Path parameters. including the ID.
|
||||
*
|
||||
* @return Response response.
|
||||
*
|
||||
* @throws ApiLinkNotFoundException generating a 404 error.
|
||||
*/
|
||||
public function putLink($request, $response, $args)
|
||||
{
|
||||
if (! isset($this->linkDb[$args['id']])) {
|
||||
throw new ApiLinkNotFoundException();
|
||||
}
|
||||
|
||||
$index = index_url($this->ci['environment']);
|
||||
$data = $request->getParsedBody();
|
||||
|
||||
$requestLink = ApiUtils::buildLinkFromRequest($data, $this->conf->get('privacy.default_private_links'));
|
||||
// duplicate URL on a different link, return 409 Conflict
|
||||
if (! empty($requestLink['url'])
|
||||
&& ! empty($dup = $this->linkDb->getLinkFromUrl($requestLink['url']))
|
||||
&& $dup['id'] != $args['id']
|
||||
) {
|
||||
return $response->withJson(
|
||||
ApiUtils::formatLink($dup, $index),
|
||||
409,
|
||||
$this->jsonStyle
|
||||
);
|
||||
}
|
||||
|
||||
$responseLink = $this->linkDb[$args['id']];
|
||||
$responseLink = ApiUtils::updateLink($responseLink, $requestLink);
|
||||
$this->linkDb[$responseLink['id']] = $responseLink;
|
||||
$this->linkDb->save($this->conf->get('resource.page_cache'));
|
||||
$this->history->updateLink($responseLink);
|
||||
|
||||
$out = ApiUtils::formatLink($responseLink, $index);
|
||||
return $response->withJson($out, 200, $this->jsonStyle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an existing link by its ID.
|
||||
*
|
||||
* @param Request $request Slim request.
|
||||
* @param Response $response Slim response.
|
||||
* @param array $args Path parameters. including the ID.
|
||||
*
|
||||
* @return Response response.
|
||||
*
|
||||
* @throws ApiLinkNotFoundException generating a 404 error.
|
||||
*/
|
||||
public function deleteLink($request, $response, $args)
|
||||
{
|
||||
if (! isset($this->linkDb[$args['id']])) {
|
||||
throw new ApiLinkNotFoundException();
|
||||
}
|
||||
$link = $this->linkDb[$args['id']];
|
||||
unset($this->linkDb[(int) $args['id']]);
|
||||
$this->linkDb->save($this->conf->get('resource.page_cache'));
|
||||
$this->history->deleteLink($link);
|
||||
|
||||
return $response->withStatus(204);
|
||||
}
|
||||
}
|
34
application/api/exceptions/ApiAuthorizationException.php
Normal file
34
application/api/exceptions/ApiAuthorizationException.php
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace Shaarli\Api\Exceptions;
|
||||
|
||||
/**
|
||||
* Class ApiAuthorizationException
|
||||
*
|
||||
* Request not authorized, return a 401 HTTP code.
|
||||
*/
|
||||
class ApiAuthorizationException extends ApiException
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getApiResponse()
|
||||
{
|
||||
$this->setMessage('Not authorized');
|
||||
return $this->buildApiResponse(401);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the exception message.
|
||||
*
|
||||
* We only return a generic error message in production mode to avoid giving
|
||||
* to much security information.
|
||||
*
|
||||
* @param $message string the exception message.
|
||||
*/
|
||||
public function setMessage($message)
|
||||
{
|
||||
$original = $this->debug === true ? ': '. $this->getMessage() : '';
|
||||
$this->message = $message . $original;
|
||||
}
|
||||
}
|
19
application/api/exceptions/ApiBadParametersException.php
Normal file
19
application/api/exceptions/ApiBadParametersException.php
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace Shaarli\Api\Exceptions;
|
||||
|
||||
/**
|
||||
* Class ApiBadParametersException
|
||||
*
|
||||
* Invalid request exception, return a 400 HTTP code.
|
||||
*/
|
||||
class ApiBadParametersException extends ApiException
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getApiResponse()
|
||||
{
|
||||
return $this->buildApiResponse(400);
|
||||
}
|
||||
}
|
77
application/api/exceptions/ApiException.php
Normal file
77
application/api/exceptions/ApiException.php
Normal file
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
namespace Shaarli\Api\Exceptions;
|
||||
|
||||
use Slim\Http\Response;
|
||||
|
||||
/**
|
||||
* Abstract class ApiException
|
||||
*
|
||||
* Parent Exception related to the API, able to generate a valid Response (ResponseInterface).
|
||||
* Also can include various information in debug mode.
|
||||
*/
|
||||
abstract class ApiException extends \Exception {
|
||||
|
||||
/**
|
||||
* @var Response instance from Slim.
|
||||
*/
|
||||
protected $response;
|
||||
|
||||
/**
|
||||
* @var bool Debug mode enabled/disabled.
|
||||
*/
|
||||
protected $debug;
|
||||
|
||||
/**
|
||||
* Build the final response.
|
||||
*
|
||||
* @return Response Final response to give.
|
||||
*/
|
||||
public abstract function getApiResponse();
|
||||
|
||||
/**
|
||||
* Creates ApiResponse body.
|
||||
* In production mode, it will only return the exception message,
|
||||
* but in dev mode, it includes additional information in an array.
|
||||
*
|
||||
* @return array|string response body
|
||||
*/
|
||||
protected function getApiResponseBody() {
|
||||
if ($this->debug !== true) {
|
||||
return $this->getMessage();
|
||||
}
|
||||
return [
|
||||
'message' => $this->getMessage(),
|
||||
'stacktrace' => get_class($this) .': '. $this->getTraceAsString()
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the Response object to return.
|
||||
*
|
||||
* @param int $code HTTP status.
|
||||
*
|
||||
* @return Response with status + body.
|
||||
*/
|
||||
protected function buildApiResponse($code)
|
||||
{
|
||||
$style = $this->debug ? JSON_PRETTY_PRINT : null;
|
||||
return $this->response->withJson($this->getApiResponseBody(), $code, $style);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Response $response
|
||||
*/
|
||||
public function setResponse($response)
|
||||
{
|
||||
$this->response = $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $debug
|
||||
*/
|
||||
public function setDebug($debug)
|
||||
{
|
||||
$this->debug = $debug;
|
||||
}
|
||||
}
|
19
application/api/exceptions/ApiInternalException.php
Normal file
19
application/api/exceptions/ApiInternalException.php
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace Shaarli\Api\Exceptions;
|
||||
|
||||
/**
|
||||
* Class ApiInternalException
|
||||
*
|
||||
* Generic exception, return a 500 HTTP code.
|
||||
*/
|
||||
class ApiInternalException extends ApiException
|
||||
{
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getApiResponse()
|
||||
{
|
||||
return $this->buildApiResponse(500);
|
||||
}
|
||||
}
|
32
application/api/exceptions/ApiLinkNotFoundException.php
Normal file
32
application/api/exceptions/ApiLinkNotFoundException.php
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace Shaarli\Api\Exceptions;
|
||||
|
||||
|
||||
use Slim\Http\Response;
|
||||
|
||||
/**
|
||||
* Class ApiLinkNotFoundException
|
||||
*
|
||||
* Link selected by ID couldn't be found, results in a 404 error.
|
||||
*
|
||||
* @package Shaarli\Api\Exceptions
|
||||
*/
|
||||
class ApiLinkNotFoundException extends ApiException
|
||||
{
|
||||
/**
|
||||
* ApiLinkNotFoundException constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->message = 'Link not found';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getApiResponse()
|
||||
{
|
||||
return $this->buildApiResponse(404);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
namespace Shaarli\Config;
|
||||
|
||||
/**
|
||||
* Interface ConfigIO
|
||||
|
@ -14,7 +15,7 @@ interface ConfigIO
|
|||
*
|
||||
* @return array All configuration in an array.
|
||||
*/
|
||||
function read($filepath);
|
||||
public function read($filepath);
|
||||
|
||||
/**
|
||||
* Write configuration.
|
||||
|
@ -22,12 +23,12 @@ interface ConfigIO
|
|||
* @param string $filepath Config file absolute path.
|
||||
* @param array $conf All configuration in an array.
|
||||
*/
|
||||
function write($filepath, $conf);
|
||||
public function write($filepath, $conf);
|
||||
|
||||
/**
|
||||
* Get config file extension according to config type.
|
||||
*
|
||||
* @return string Config file extension.
|
||||
*/
|
||||
function getExtension();
|
||||
public function getExtension();
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
namespace Shaarli\Config;
|
||||
|
||||
/**
|
||||
* Class ConfigJson (ConfigIO implementation)
|
||||
|
@ -10,7 +11,7 @@ class ConfigJson implements ConfigIO
|
|||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
function read($filepath)
|
||||
public function read($filepath)
|
||||
{
|
||||
if (! is_readable($filepath)) {
|
||||
return array();
|
||||
|
@ -20,8 +21,14 @@ class ConfigJson implements ConfigIO
|
|||
$data = str_replace(self::getPhpSuffix(), '', $data);
|
||||
$data = json_decode($data, true);
|
||||
if ($data === null) {
|
||||
$error = json_last_error();
|
||||
throw new Exception('An error occurred while parsing JSON file: error code #'. $error);
|
||||
$errorCode = json_last_error();
|
||||
$error = 'An error occurred while parsing JSON configuration file ('. $filepath .'): error code #';
|
||||
$error .= $errorCode. '<br>➜ <code>' . json_last_error_msg() .'</code>';
|
||||
if ($errorCode === JSON_ERROR_SYNTAX) {
|
||||
$error .= '<br>Please check your JSON syntax (without PHP comment tags) using a JSON lint tool such as ';
|
||||
$error .= '<a href="http://jsonlint.com/">jsonlint.com</a>.';
|
||||
}
|
||||
throw new \Exception($error);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
@ -29,13 +36,13 @@ class ConfigJson implements ConfigIO
|
|||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
function write($filepath, $conf)
|
||||
public function write($filepath, $conf)
|
||||
{
|
||||
// JSON_PRETTY_PRINT is available from PHP 5.4.
|
||||
$print = defined('JSON_PRETTY_PRINT') ? JSON_PRETTY_PRINT : 0;
|
||||
$data = self::getPhpHeaders() . json_encode($conf, $print) . self::getPhpSuffix();
|
||||
if (!file_put_contents($filepath, $data)) {
|
||||
throw new IOException(
|
||||
throw new \IOException(
|
||||
$filepath,
|
||||
'Shaarli could not create the config file.
|
||||
Please make sure Shaarli has the right to write in the folder is it installed in.'
|
||||
|
@ -46,7 +53,7 @@ class ConfigJson implements ConfigIO
|
|||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
function getExtension()
|
||||
public function getExtension()
|
||||
{
|
||||
return '.json.php';
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
<?php
|
||||
namespace Shaarli\Config;
|
||||
|
||||
// FIXME! Namespaces...
|
||||
require_once 'ConfigIO.php';
|
||||
require_once 'ConfigJson.php';
|
||||
require_once 'ConfigPhp.php';
|
||||
use Shaarli\Config\Exception\MissingFieldConfigException;
|
||||
use Shaarli\Config\Exception\UnauthorizedConfigException;
|
||||
|
||||
/**
|
||||
* Class ConfigManager
|
||||
|
@ -20,6 +19,8 @@ class ConfigManager
|
|||
*/
|
||||
protected static $NOT_FOUND = 'NOT_FOUND';
|
||||
|
||||
public static $DEFAULT_PLUGINS = array('qrcode');
|
||||
|
||||
/**
|
||||
* @var string Config folder.
|
||||
*/
|
||||
|
@ -80,7 +81,11 @@ class ConfigManager
|
|||
*/
|
||||
protected function load()
|
||||
{
|
||||
$this->loadedConfig = $this->configIO->read($this->getConfigFileExt());
|
||||
try {
|
||||
$this->loadedConfig = $this->configIO->read($this->getConfigFileExt());
|
||||
} catch (\Exception $e) {
|
||||
die($e->getMessage());
|
||||
}
|
||||
$this->setDefaultValues();
|
||||
}
|
||||
|
||||
|
@ -122,12 +127,12 @@ class ConfigManager
|
|||
* @param bool $write Write the new setting in the config file, default false.
|
||||
* @param bool $isLoggedIn User login state, default false.
|
||||
*
|
||||
* @throws Exception Invalid
|
||||
* @throws \Exception Invalid
|
||||
*/
|
||||
public function set($setting, $value, $write = false, $isLoggedIn = false)
|
||||
{
|
||||
if (empty($setting) || ! is_string($setting)) {
|
||||
throw new Exception('Invalid setting key parameter. String expected, got: '. gettype($setting));
|
||||
throw new \Exception('Invalid setting key parameter. String expected, got: '. gettype($setting));
|
||||
}
|
||||
|
||||
// During the ConfigIO transition, map legacy settings to the new ones.
|
||||
|
@ -175,7 +180,7 @@ class ConfigManager
|
|||
*
|
||||
* @throws MissingFieldConfigException: a mandatory field has not been provided in $conf.
|
||||
* @throws UnauthorizedConfigException: user is not authorize to change configuration.
|
||||
* @throws IOException: an error occurred while writing the new config file.
|
||||
* @throws \IOException: an error occurred while writing the new config file.
|
||||
*/
|
||||
public function write($isLoggedIn)
|
||||
{
|
||||
|
@ -296,7 +301,9 @@ class ConfigManager
|
|||
$this->setEmpty('resource.updates', 'data/updates.txt');
|
||||
$this->setEmpty('resource.log', 'data/log.txt');
|
||||
$this->setEmpty('resource.update_check', 'data/lastupdatecheck.txt');
|
||||
$this->setEmpty('resource.history', 'data/history.php');
|
||||
$this->setEmpty('resource.raintpl_tpl', 'tpl/');
|
||||
$this->setEmpty('resource.theme', 'default');
|
||||
$this->setEmpty('resource.raintpl_tmp', 'tmp/');
|
||||
$this->setEmpty('resource.thumbnails_cache', 'cache');
|
||||
$this->setEmpty('resource.page_cache', 'pagecache');
|
||||
|
@ -308,14 +315,14 @@ class ConfigManager
|
|||
|
||||
$this->setEmpty('general.header_link', '?');
|
||||
$this->setEmpty('general.links_per_page', 20);
|
||||
$this->setEmpty('general.enabled_plugins', array('qrcode'));
|
||||
$this->setEmpty('general.enabled_plugins', self::$DEFAULT_PLUGINS);
|
||||
|
||||
$this->setEmpty('updates.check_updates', false);
|
||||
$this->setEmpty('updates.check_updates_branch', 'stable');
|
||||
$this->setEmpty('updates.check_updates_interval', 86400);
|
||||
|
||||
$this->setEmpty('feed.rss_permalinks', true);
|
||||
$this->setEmpty('feed.show_atom', false);
|
||||
$this->setEmpty('feed.show_atom', true);
|
||||
|
||||
$this->setEmpty('privacy.default_private_links', false);
|
||||
$this->setEmpty('privacy.hide_public_links', false);
|
||||
|
@ -359,36 +366,3 @@ class ConfigManager
|
|||
$this->configIO = $configIO;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception used if a mandatory field is missing in given configuration.
|
||||
*/
|
||||
class MissingFieldConfigException extends Exception
|
||||
{
|
||||
public $field;
|
||||
|
||||
/**
|
||||
* Construct exception.
|
||||
*
|
||||
* @param string $field field name missing.
|
||||
*/
|
||||
public function __construct($field)
|
||||
{
|
||||
$this->field = $field;
|
||||
$this->message = 'Configuration value is required for '. $this->field;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception used if an unauthorized attempt to edit configuration has been made.
|
||||
*/
|
||||
class UnauthorizedConfigException extends Exception
|
||||
{
|
||||
/**
|
||||
* Construct exception.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->message = 'You are not authorized to alter config.';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
namespace Shaarli\Config;
|
||||
|
||||
/**
|
||||
* Class ConfigPhp (ConfigIO implementation)
|
||||
|
@ -41,6 +42,7 @@ class ConfigPhp implements ConfigIO
|
|||
'resource.log' => 'config.LOG_FILE',
|
||||
'resource.update_check' => 'config.UPDATECHECK_FILENAME',
|
||||
'resource.raintpl_tpl' => 'config.RAINTPL_TPL',
|
||||
'resource.theme' => 'config.theme',
|
||||
'resource.raintpl_tmp' => 'config.RAINTPL_TMP',
|
||||
'resource.thumbnails_cache' => 'config.CACHEDIR',
|
||||
'resource.page_cache' => 'config.PAGECACHE',
|
||||
|
@ -71,7 +73,7 @@ class ConfigPhp implements ConfigIO
|
|||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
function read($filepath)
|
||||
public function read($filepath)
|
||||
{
|
||||
if (! file_exists($filepath) || ! is_readable($filepath)) {
|
||||
return array();
|
||||
|
@ -91,7 +93,7 @@ class ConfigPhp implements ConfigIO
|
|||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
function write($filepath, $conf)
|
||||
public function write($filepath, $conf)
|
||||
{
|
||||
$configStr = '<?php '. PHP_EOL;
|
||||
foreach (self::$ROOT_KEYS as $key) {
|
||||
|
@ -99,7 +101,7 @@ class ConfigPhp implements ConfigIO
|
|||
$configStr .= '$GLOBALS[\'' . $key . '\'] = ' . var_export($conf[$key], true) . ';' . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Store all $conf['config']
|
||||
foreach ($conf['config'] as $key => $value) {
|
||||
$configStr .= '$GLOBALS[\'config\'][\''. $key .'\'] = '.var_export($conf['config'][$key], true).';'. PHP_EOL;
|
||||
|
@ -114,7 +116,7 @@ class ConfigPhp implements ConfigIO
|
|||
if (!file_put_contents($filepath, $configStr)
|
||||
|| strcmp(file_get_contents($filepath), $configStr) != 0
|
||||
) {
|
||||
throw new IOException(
|
||||
throw new \IOException(
|
||||
$filepath,
|
||||
'Shaarli could not create the config file.
|
||||
Please make sure Shaarli has the right to write in the folder is it installed in.'
|
||||
|
@ -125,7 +127,7 @@ class ConfigPhp implements ConfigIO
|
|||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
function getExtension()
|
||||
public function getExtension()
|
||||
{
|
||||
return '.php';
|
||||
}
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
<?php
|
||||
|
||||
use Shaarli\Config\Exception\PluginConfigOrderException;
|
||||
|
||||
/**
|
||||
* Plugin configuration helper functions.
|
||||
*
|
||||
|
@ -108,17 +111,3 @@ function load_plugin_parameter_values($plugins, $conf)
|
|||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception used if an error occur while saving plugin configuration.
|
||||
*/
|
||||
class PluginConfigOrderException extends Exception
|
||||
{
|
||||
/**
|
||||
* Construct exception.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->message = 'An error occurred while trying to save plugins loading order.';
|
||||
}
|
||||
}
|
||||
|
|
23
application/config/exception/MissingFieldConfigException.php
Normal file
23
application/config/exception/MissingFieldConfigException.php
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace Shaarli\Config\Exception;
|
||||
|
||||
/**
|
||||
* Exception used if a mandatory field is missing in given configuration.
|
||||
*/
|
||||
class MissingFieldConfigException extends \Exception
|
||||
{
|
||||
public $field;
|
||||
|
||||
/**
|
||||
* Construct exception.
|
||||
*
|
||||
* @param string $field field name missing.
|
||||
*/
|
||||
public function __construct($field)
|
||||
{
|
||||
$this->field = $field;
|
||||
$this->message = 'Configuration value is required for '. $this->field;
|
||||
}
|
||||
}
|
17
application/config/exception/PluginConfigOrderException.php
Normal file
17
application/config/exception/PluginConfigOrderException.php
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace Shaarli\Config\Exception;
|
||||
|
||||
/**
|
||||
* Exception used if an error occur while saving plugin configuration.
|
||||
*/
|
||||
class PluginConfigOrderException extends \Exception
|
||||
{
|
||||
/**
|
||||
* Construct exception.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->message = 'An error occurred while trying to save plugins loading order.';
|
||||
}
|
||||
}
|
18
application/config/exception/UnauthorizedConfigException.php
Normal file
18
application/config/exception/UnauthorizedConfigException.php
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace Shaarli\Config\Exception;
|
||||
|
||||
/**
|
||||
* Exception used if an unauthorized attempt to edit configuration has been made.
|
||||
*/
|
||||
class UnauthorizedConfigException extends \Exception
|
||||
{
|
||||
/**
|
||||
* Construct exception.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->message = 'You are not authorized to alter config.';
|
||||
}
|
||||
}
|
22
application/exceptions/IOException.php
Normal file
22
application/exceptions/IOException.php
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Exception class thrown when a filesystem access failure happens
|
||||
*/
|
||||
class IOException extends Exception
|
||||
{
|
||||
private $path;
|
||||
|
||||
/**
|
||||
* Construct a new IOException
|
||||
*
|
||||
* @param string $path path to the resource that cannot be accessed
|
||||
* @param string $message Custom exception message.
|
||||
*/
|
||||
public function __construct($path, $message = '')
|
||||
{
|
||||
$this->path = $path;
|
||||
$this->message = empty($message) ? 'Error accessing' : $message;
|
||||
$this->message .= ' "' . $this->path .'"';
|
||||
}
|
||||
}
|
|
@ -10,14 +10,27 @@
|
|||
},
|
||||
"keywords": ["bookmark", "link", "share", "web"],
|
||||
"require": {
|
||||
"php": ">=5.3.4",
|
||||
"shaarli/netscape-bookmark-parser": "1.*",
|
||||
"erusev/parsedown": "1.6"
|
||||
"php": ">=5.5",
|
||||
"shaarli/netscape-bookmark-parser": "^2.0",
|
||||
"erusev/parsedown": "1.6",
|
||||
"slim/slim": "^3.0",
|
||||
"pubsubhubbub/publisher": "dev-master"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpmd/phpmd" : "@stable",
|
||||
"phpunit/phpunit": "4.8.*",
|
||||
"sebastian/phpcpd": "*",
|
||||
"squizlabs/php_codesniffer": "2.*"
|
||||
"squizlabs/php_codesniffer": "2.*",
|
||||
"phpunit/phpcov": "*"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Shaarli\\": "application",
|
||||
"Shaarli\\Api\\": "application/api/",
|
||||
"Shaarli\\Api\\Controllers\\": "application/api/controllers",
|
||||
"Shaarli\\Api\\Exceptions\\": "application/api/exceptions",
|
||||
"Shaarli\\Config\\": "application/config/",
|
||||
"Shaarli\\Config\\Exception\\": "application/config/exception"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
2424
composer.lock
generated
Normal file
2424
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -32,6 +32,7 @@
|
|||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
|
@ -50,6 +51,7 @@
|
|||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
|
|
|
@ -69,6 +69,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
|
@ -87,6 +88,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
|
@ -50,6 +51,7 @@
|
|||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
|
@ -64,7 +66,6 @@
|
|||
</div>
|
||||
<h1 id="browsing-and-searching">Browsing and searching</h1>
|
||||
<h1 id="browsing-and-searching-1">Browsing and Searching</h1>
|
||||
<p>Status: DRAFT</p>
|
||||
<p><embed src="(http://pix.toile-libre.org/upload/original/1455571378.png).html" /></p>
|
||||
<h2 id="plain-text-search">Plain text search</h2>
|
||||
<p>Use the <code>Search text</code> field to search in <em>any</em> of the fields of all links (Title, URL, Description...)</p>
|
||||
|
@ -75,6 +76,7 @@
|
|||
<p>Use the <code>Filter by tags</code> field to restrict displayed links to entries tagged with one or multiple tags (use space to separate tags).</p>
|
||||
<p><strong>Hidden tags:</strong> Tags starting with a dot <code>.</code> (example <code>.secret</code>) are private. They can only be seen and searched when logged in.</p>
|
||||
<p>Alternatively you can use the <code>Tag cloud</code> to discover all tags and click on any of them to display related links.</p>
|
||||
<p>To search for links that are not tagged, enter <code>""</code> in the tag search field.</p>
|
||||
<h2 id="filtering-rss-feedspicture-wall">Filtering RSS feeds/Picture wall</h2>
|
||||
<p>RSS feeds can also be restricted to only return items matching a text/tag search: see <a href="RSS-feeds.html">RSS feeds</a>.</p>
|
||||
</body>
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#Browsing and searching
|
||||
# Browsing and Searching
|
||||
|
||||
Status: DRAFT
|
||||
|
||||
![(http://pix.toile-libre.org/upload/original/1455571378.png)]((http://pix.toile-libre.org/upload/original/1455571378.png).html)
|
||||
|
||||
## Plain text search
|
||||
|
@ -23,6 +21,8 @@ Use the `Filter by tags` field to restrict displayed links to entries tagged wit
|
|||
|
||||
Alternatively you can use the `Tag cloud` to discover all tags and click on any of them to display related links.
|
||||
|
||||
To search for links that are not tagged, enter `""` in the tag search field.
|
||||
|
||||
## Filtering RSS feeds/Picture wall
|
||||
|
||||
RSS feeds can also be restricted to only return items matching a text/tag search: see [RSS feeds](RSS-feeds.html).
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
|
@ -50,6 +51,7 @@
|
|||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
|
@ -50,6 +51,7 @@
|
|||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
|
@ -77,12 +79,20 @@
|
|||
<li><a href="http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history">Original revisions history</a><a href=".html"></a></li>
|
||||
<li><a href="https://www.shaarli.fr/my.php">Shaarli.fr/my</a> - Unofficial, unsupported (old fork) hosted Shaarlis provider, courtesy of <a href="https://github.com/DMeloni">DMeloni</a><a href=".html"></a></li>
|
||||
</ul>
|
||||
<h3 id="articles-and-social-media-discussions">Articles and social media discussions</h3>
|
||||
<ul>
|
||||
<li>2016-09-22 - Hacker News - <a href="https://news.ycombinator.com/item?id=12552176" class="uri">https://news.ycombinator.com/item?id=12552176</a></li>
|
||||
<li>2015-08-15 - Reddit - <a href="https://www.reddit.com/r/selfhosted/comments/3h3zwh/question_about_migrating_from_wordpress_to_shaarli/">Question about migrating from WordPress to Shaarli.</a><a href=".html"></a></li>
|
||||
<li>2015-06-22 - Hacker News - <a href="https://news.ycombinator.com/item?id=9755366" class="uri">https://news.ycombinator.com/item?id=9755366</a></li>
|
||||
<li>2015-05-12 - Reddit - <a href="https://www.reddit.com/r/selfhosted/comments/35pkkc/shaarli_self_hosted_bookmarking_delicious_php/">shaarli - Self hosted Bookmarking / Delicious (PHP, MySQL)</a><a href=".html"></a></li>
|
||||
</ul>
|
||||
<h3 id="third-party-plugins">Third party plugins</h3>
|
||||
<ul>
|
||||
<li><a href="https://github.com/kalvn/shaarli-plugin-autosave">autosave</a> by <a href="https://github.com/kalvn">@kalvn</a>: Automatically saves data when editing a link to avoid any loss in case of crash or unexpected shutdown.<a href=".html"></a></li>
|
||||
<li><a href="https://github.com/ArthurHoaro/code-coloration">Code Coloration</a> by <a href="https://github.com/ArthurHoaro">@ArthurHoaro</a>: client side code syntax highlighter.<a href=".html"></a></li>
|
||||
<li><a href="https://github.com/kalvn/shaarli-plugin-disqus">Disqus</a> by <a href="https://github.com/kalvn">@kalvn</a>: Adds Disqus comment system to your Shaarli.<a href=".html"></a></li>
|
||||
<li><a href="https://github.com/NerosTie/emojione">emojione</a> by <a href="https://github.com/NerosTie">@NerosTie</a>: Add colorful emojis to your Shaarli.<a href=".html"></a></li>
|
||||
<li><a href="https://github.com/ericjuden/Shaarli-Google-Analytics-Plugin">google analytics</a> by <a href="http://github.com/ericjuden">@ericjuden</a>: Adds Google Analytics tracking support<a href=".html"></a></li>
|
||||
<li><a href="https://github.com/ArthurHoaro/launch-plugin">launch</a> - Launch Plugin is a plugin designed to enhance and customize Launch Theme for Shaarli.<a href=".html"></a></li>
|
||||
<li><a href="https://github.com/alexisju/social">social</a> by <a href="https://github.com/alexisju">@alexisju</a>: share links to social networks.<a href=".html"></a></li>
|
||||
<li><a href="https://github.com/ArthurHoaro/shaarli2twitter">shaarli2twitter</a> by <a href="https://github.com/ArthurHoaro">@ArthurHoaro</a> - Automatically tweet your shared links from Shaarli<a href=".html"></a></li>
|
||||
|
|
|
@ -14,6 +14,11 @@ _TODO: contact repos owners to see if they'd like to standardize their work with
|
|||
- [Original revisions history](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history)[](.html)
|
||||
- [Shaarli.fr/my](https://www.shaarli.fr/my.php) - Unofficial, unsupported (old fork) hosted Shaarlis provider, courtesy of [DMeloni](https://github.com/DMeloni)[](.html)
|
||||
|
||||
### Articles and social media discussions
|
||||
- 2016-09-22 - Hacker News - https://news.ycombinator.com/item?id=12552176
|
||||
- 2015-08-15 - Reddit - [Question about migrating from WordPress to Shaarli.](https://www.reddit.com/r/selfhosted/comments/3h3zwh/question_about_migrating_from_wordpress_to_shaarli/)[](.html)
|
||||
- 2015-06-22 - Hacker News - https://news.ycombinator.com/item?id=9755366
|
||||
- 2015-05-12 - Reddit - [shaarli - Self hosted Bookmarking / Delicious (PHP, MySQL)](https://www.reddit.com/r/selfhosted/comments/35pkkc/shaarli_self_hosted_bookmarking_delicious_php/)[](.html)
|
||||
|
||||
### Third party plugins
|
||||
|
||||
|
@ -22,6 +27,7 @@ _TODO: contact repos owners to see if they'd like to standardize their work with
|
|||
* [Code Coloration](https://github.com/ArthurHoaro/code-coloration) by [@ArthurHoaro](https://github.com/ArthurHoaro): client side code syntax highlighter.[](.html)
|
||||
* [Disqus](https://github.com/kalvn/shaarli-plugin-disqus) by [@kalvn](https://github.com/kalvn): Adds Disqus comment system to your Shaarli.[](.html)
|
||||
* [emojione](https://github.com/NerosTie/emojione) by [@NerosTie](https://github.com/NerosTie): Add colorful emojis to your Shaarli.[](.html)
|
||||
* [google analytics](https://github.com/ericjuden/Shaarli-Google-Analytics-Plugin) by [@ericjuden](http://github.com/ericjuden): Adds Google Analytics tracking support[](.html)
|
||||
* [launch](https://github.com/ArthurHoaro/launch-plugin) - Launch Plugin is a plugin designed to enhance and customize Launch Theme for Shaarli.[](.html)
|
||||
* [social](https://github.com/alexisju/social) by [@alexisju](https://github.com/alexisju): share links to social networks.[](.html)
|
||||
* [shaarli2twitter](https://github.com/ArthurHoaro/shaarli2twitter) by [@ArthurHoaro](https://github.com/ArthurHoaro) - Automatically tweet your shared links from Shaarli[](.html)
|
||||
|
|
|
@ -69,6 +69,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
|
@ -87,6 +88,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
|
|
|
@ -69,6 +69,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
|
@ -87,6 +88,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
|
|
|
@ -69,6 +69,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
|
@ -87,6 +88,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
|
@ -50,6 +51,7 @@
|
|||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
|
|
|
@ -69,6 +69,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
|
@ -87,6 +88,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
|
@ -101,33 +103,33 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
</div>
|
||||
<h1 id="directory-structure">Directory structure</h1>
|
||||
<p>Here is the directory structure of Shaarli and the purpose of the different files:</p>
|
||||
<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"> <span class="ex">index.php</span> <span class="co"># Main program</span>
|
||||
<span class="ex">application/</span> <span class="co"># Shaarli classes</span>
|
||||
<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"> <span class="ex">index.php</span> # Main program
|
||||
<span class="ex">application/</span> # Shaarli classes
|
||||
├── <span class="ex">LinkDB.php</span>
|
||||
└── <span class="ex">Utils.php</span>
|
||||
<span class="ex">tests/</span> <span class="co"># Shaarli unitary & functional tests</span>
|
||||
<span class="ex">tests/</span> # Shaarli unitary <span class="kw">&</span> <span class="ex">functional</span> tests
|
||||
├── <span class="ex">LinkDBTest.php</span>
|
||||
├── <span class="ex">utils</span> <span class="co"># utilities to ease testing</span>
|
||||
├── <span class="ex">utils</span> # utilities to ease testing
|
||||
│ └── <span class="ex">ReferenceLinkDB.php</span>
|
||||
└── <span class="ex">UtilsTest.php</span>
|
||||
<span class="ex">COPYING</span> <span class="co"># Shaarli license</span>
|
||||
<span class="ex">inc/</span> <span class="co"># static assets and 3rd party libraries</span>
|
||||
├── <span class="ex">awesomplete.*</span> <span class="co"># tags autocompletion library</span>
|
||||
├── <span class="ex">blazy.*</span> <span class="co"># picture wall lazy image loading library</span>
|
||||
<span class="ex">COPYING</span> # Shaarli license
|
||||
<span class="ex">inc/</span> # static assets and 3rd party libraries
|
||||
├── <span class="ex">awesomplete.*</span> # tags autocompletion library
|
||||
├── <span class="ex">blazy.*</span> # picture wall lazy image loading library
|
||||
├── <span class="ex">shaarli.css</span>, reset.css <span class="co"># Shaarli stylesheet.</span>
|
||||
├── <span class="ex">qr.*</span> <span class="co"># qr code generation library</span>
|
||||
└──<span class="ex">rain.tpl.class.php</span> <span class="co"># RainTPL templating library</span>
|
||||
<span class="ex">tpl/</span> <span class="co"># RainTPL templates for Shaarli. They are used to build the pages.</span>
|
||||
<span class="ex">images/</span> <span class="co"># Images and icons used in Shaarli</span>
|
||||
<span class="ex">data/</span> <span class="co"># data storage: bookmark database, configuration, logs, banlist…</span>
|
||||
├── <span class="ex">config.php</span> <span class="co"># Shaarli configuration (login, password, timezone, title…)</span>
|
||||
├── <span class="ex">datastore.php</span> <span class="co"># Your link database (compressed).</span>
|
||||
├── <span class="ex">ipban.php</span> <span class="co"># IP address ban system data</span>
|
||||
├── <span class="ex">lastupdatecheck.txt</span> <span class="co"># Update check timestamp file</span>
|
||||
└──<span class="ex">log.txt</span> <span class="co"># login/IPban log.</span>
|
||||
<span class="ex">cache/</span> <span class="co"># thumbnails cache</span>
|
||||
├── <span class="ex">qr.*</span> # qr code generation library
|
||||
└──<span class="ex">rain.tpl.class.php</span> # RainTPL templating library
|
||||
<span class="ex">tpl/</span> # RainTPL templates for Shaarli. They are used to build the pages.
|
||||
<span class="ex">images/</span> # Images and icons used in Shaarli
|
||||
<span class="ex">data/</span> # data storage: bookmark database, configuration, logs, banlist…
|
||||
├── <span class="ex">config.php</span> # Shaarli configuration (login, password, timezone, title…)
|
||||
├── <span class="ex">datastore.php</span> # Your link database (compressed)<span class="ex">.</span>
|
||||
├── <span class="ex">ipban.php</span> # IP address ban system data
|
||||
├── <span class="ex">lastupdatecheck.txt</span> # Update check timestamp file
|
||||
└──<span class="ex">log.txt</span> # login/IPban log.
|
||||
<span class="ex">cache/</span> # thumbnails cache
|
||||
<span class="co"># This directory is automatically created. You can erase it anytime you want.</span>
|
||||
<span class="ex">tmp/</span> <span class="co"># Temporary directory for compiled RainTPL templates.</span>
|
||||
<span class="ex">tmp/</span> # Temporary directory for compiled RainTPL templates.
|
||||
<span class="co"># This directory is automatically created. You can erase it anytime you want.</span></code></pre></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -69,6 +69,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
|
@ -87,6 +88,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
|
@ -200,7 +202,7 @@ $ <span class="ex">docker</span> ps
|
|||
<span class="ex">CONTAINER</span> ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
<span class="ex">d40b7af693d6</span> shaarli/shaarli /usr/bin/supervisor 15 seconds ago Up 4 seconds 0.0.0.0:8000-<span class="op">></span>80/tcp backstabbing_galileo</code></pre></div>
|
||||
<h3 id="stop-and-destroy-a-container">Stop and destroy a container</h3>
|
||||
<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="ex">docker</span> stop backstabbing_galileo <span class="co"># those docker guys are really rude to physicists!</span>
|
||||
<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="ex">docker</span> stop backstabbing_galileo # those docker guys are really rude to physicists!
|
||||
<span class="ex">backstabbing_galileo</span>
|
||||
|
||||
<span class="co"># check the container is stopped</span>
|
||||
|
@ -213,14 +215,15 @@ $ <span class="ex">docker</span> ps -a
|
|||
<span class="ex">d40b7af693d6</span> shaarli/shaarli /usr/bin/supervisor 5 minutes ago Exited (0) <span class="ex">48</span> seconds ago backstabbing_galileo
|
||||
|
||||
<span class="co"># destroy the container</span>
|
||||
$ <span class="ex">docker</span> rm backstabbing_galileo <span class="co"># let's put an end to these barbarian practices</span>
|
||||
<span class="ex">backstabbing_galileo</span>
|
||||
$ <span class="ex">docker</span> rm backstabbing_galileo # let<span class="st">'s put an end to these barbarian practices</span>
|
||||
<span class="st">backstabbing_galileo</span>
|
||||
|
||||
$ <span class="ex">docker</span> ps -a
|
||||
<span class="ex">CONTAINER</span> ID IMAGE COMMAND CREATED STATUS PORTS NAMES</code></pre></div>
|
||||
<span class="st">$ docker ps -a</span>
|
||||
<span class="st">CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES</span></code></pre></div>
|
||||
<h2 id="resources">Resources</h2>
|
||||
<h3 id="docker-1">Docker</h3>
|
||||
<ul>
|
||||
<li><a href="https://www.katacoda.com/courses/docker/">Interactive Docker training portal</a> on <a href="https://www.katacoda.com/">Katakoda</a><a href=".html"></a></li>
|
||||
<li><a href="http://blog.thoward37.me/articles/where-are-docker-images-stored/">Where are Docker images stored?</a><a href=".html"></a></li>
|
||||
<li><a href="https://docs.docker.com/reference/builder/">Dockerfile reference</a><a href=".html"></a></li>
|
||||
<li><a href="https://docs.docker.com/articles/dockerfile_best-practices/">Dockerfile best practices</a><a href=".html"></a></li>
|
||||
|
|
|
@ -141,6 +141,7 @@ CONTAINER ID IMAGE COMMAND CREATED STATUS
|
|||
|
||||
## Resources
|
||||
### Docker
|
||||
- [Interactive Docker training portal](https://www.katacoda.com/courses/docker/) on [Katakoda](https://www.katacoda.com/)[](.html)
|
||||
- [Where are Docker images stored?](http://blog.thoward37.me/articles/where-are-docker-images-stored/)[](.html)
|
||||
- [Dockerfile reference](https://docs.docker.com/reference/builder/)[](.html)
|
||||
- [Dockerfile best practices](https://docs.docker.com/articles/dockerfile_best-practices/)[](.html)
|
||||
|
|
|
@ -69,6 +69,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
|
@ -87,6 +88,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
|
@ -155,7 +157,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<span class="kw">function</span> copyUserStyleFrom<span class="ot">(</span><span class="kw">$url</span><span class="ot">,</span> <span class="kw">$name</span><span class="ot">,</span> <span class="kw">$knownStyles</span><span class="ot">)</span> {
|
||||
<span class="kw">$userStyle</span> = <span class="kw">$url</span>.<span class="st">"inc/user.css"</span><span class="ot">;</span>
|
||||
<span class="kw">if</span><span class="ot">(</span><span class="fu">in_array</span><span class="ot">(</span><span class="kw">$url</span><span class="ot">,</span> <span class="kw">$knownStyles</span><span class="ot">))</span> {
|
||||
<span class="co">// TODO add log message</span>
|
||||
<span class="co">// </span><span class="al">TODO</span><span class="co"> add log message</span>
|
||||
} <span class="kw">else</span> {
|
||||
<span class="kw">$statusCode</span> = get_http_response_code<span class="ot">(</span><span class="kw">$userStyle</span><span class="ot">);</span>
|
||||
<span class="kw">if</span><span class="ot">(</span><span class="fu">intval</span><span class="ot">(</span><span class="kw">$statusCode</span><span class="ot">)</span><<span class="dv">300</span><span class="ot">)</span> {
|
||||
|
|
|
@ -69,6 +69,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
|
@ -87,6 +88,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
|
@ -108,10 +110,10 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<h3 id="download-as-an-archive">Download as an archive</h3>
|
||||
<p>Get the latest released version from the <a href="https://github.com/shaarli/Shaarli/releases">releases</a> page.<a href=".html"></a></p>
|
||||
<p><strong>Download our <em>shaarli-full</em> archive</strong> to include dependencies.</p>
|
||||
<p>The current latest released version is <code>v0.8.0</code></p>
|
||||
<p>The current latest released version is <code>v0.8.4</code></p>
|
||||
<p>Or in command lines:</p>
|
||||
<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="fu">wget</span> https://github.com/shaarli/Shaarli/releases/download/v0.8.0/shaarli-v0.8.0-full.zip
|
||||
$ <span class="fu">unzip</span> shaarli-v0.8.0-full.zip
|
||||
<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="fu">wget</span> https://github.com/shaarli/Shaarli/releases/download/v0.8.4/shaarli-v0.8.4-full.zip
|
||||
$ <span class="fu">unzip</span> shaarli-v0.8.4-full.zip
|
||||
$ <span class="fu">mv</span> Shaarli /path/to/shaarli/</code></pre></div>
|
||||
<table style="width:46%;">
|
||||
<colgroup>
|
||||
|
@ -129,8 +131,8 @@ $ <span class="fu">mv</span> Shaarli /path/to/shaarli/</code></pre></div>
|
|||
</table>
|
||||
<h3 id="using-git">Using git</h3>
|
||||
<pre><code>mkdir -p /path/to/shaarli && cd /path/to/shaarli/
|
||||
git clone -b v0.8.0 https://github.com/shaarli/Shaarli.git .
|
||||
composer update --no-dev</code></pre>
|
||||
git clone -b v0.8 https://github.com/shaarli/Shaarli.git .
|
||||
composer install --no-dev</code></pre>
|
||||
<hr />
|
||||
<h2 id="stable-version">Stable version</h2>
|
||||
<p>The stable version has been experienced by Shaarli users, and will receive security updates.</p>
|
||||
|
@ -148,16 +150,16 @@ $ <span class="fu">mv</span> Shaarli-stable /path/to/shaarli/</code></pre></div>
|
|||
<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="fu">git</span> clone https://github.com/shaarli/Shaarli.git -b stable /path/to/shaarli/
|
||||
<span class="co"># install/update third-party dependencies</span>
|
||||
$ <span class="bu">cd</span> /path/to/shaarli/
|
||||
$ <span class="ex">composer</span> update --no-dev</code></pre></div>
|
||||
$ <span class="ex">composer</span> install --no-dev</code></pre></div>
|
||||
<hr />
|
||||
<h2 id="development-version-mainline">Development version (mainline)</h2>
|
||||
<p><em>Use at your own risk!</em></p>
|
||||
<p>To get the latest changes from the <code>master</code> branch:</p>
|
||||
<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="co"># clone the repository </span>
|
||||
$ <span class="fu">git</span> clone https://github.com/shaarli/Shaarli.git master /path/to/shaarli/
|
||||
$ <span class="fu">git</span> clone https://github.com/shaarli/Shaarli.git -b master /path/to/shaarli/
|
||||
<span class="co"># install/update third-party dependencies</span>
|
||||
$ <span class="bu">cd</span> /path/to/shaarli
|
||||
$ <span class="ex">composer</span> update --no-dev</code></pre></div>
|
||||
$ <span class="ex">composer</span> install --no-dev</code></pre></div>
|
||||
<hr />
|
||||
<h2 id="finish-installation">Finish Installation</h2>
|
||||
<p>Once Shaarli is downloaded and files have been placed at the correct location, open it this location your favorite browser.</p>
|
||||
|
|
|
@ -13,13 +13,13 @@ Get the latest released version from the [releases](https://github.com/shaarli/S
|
|||
|
||||
**Download our *shaarli-full* archive** to include dependencies.
|
||||
|
||||
The current latest released version is `v0.8.0`
|
||||
The current latest released version is `v0.8.4`
|
||||
|
||||
Or in command lines:
|
||||
|
||||
```bash
|
||||
$ wget https://github.com/shaarli/Shaarli/releases/download/v0.8.0/shaarli-v0.8.0-full.zip
|
||||
$ unzip shaarli-v0.8.0-full.zip
|
||||
$ wget https://github.com/shaarli/Shaarli/releases/download/v0.8.4/shaarli-v0.8.4-full.zip
|
||||
$ unzip shaarli-v0.8.4-full.zip
|
||||
$ mv Shaarli /path/to/shaarli/
|
||||
```
|
||||
|
||||
|
@ -30,8 +30,8 @@ $ mv Shaarli /path/to/shaarli/
|
|||
|
||||
```
|
||||
mkdir -p /path/to/shaarli && cd /path/to/shaarli/
|
||||
git clone -b v0.8.0 https://github.com/shaarli/Shaarli.git .
|
||||
composer update --no-dev
|
||||
git clone -b v0.8 https://github.com/shaarli/Shaarli.git .
|
||||
composer install --no-dev
|
||||
```
|
||||
|
||||
--------------------------------------------------------
|
||||
|
@ -66,7 +66,7 @@ $ mv Shaarli-stable /path/to/shaarli/
|
|||
$ git clone https://github.com/shaarli/Shaarli.git -b stable /path/to/shaarli/
|
||||
# install/update third-party dependencies
|
||||
$ cd /path/to/shaarli/
|
||||
$ composer update --no-dev
|
||||
$ composer install --no-dev
|
||||
```
|
||||
|
||||
--------------------------------------------------------
|
||||
|
@ -79,10 +79,10 @@ To get the latest changes from the `master` branch:
|
|||
|
||||
```bash
|
||||
# clone the repository
|
||||
$ git clone https://github.com/shaarli/Shaarli.git master /path/to/shaarli/
|
||||
$ git clone https://github.com/shaarli/Shaarli.git -b master /path/to/shaarli/
|
||||
# install/update third-party dependencies
|
||||
$ cd /path/to/shaarli
|
||||
$ composer update --no-dev
|
||||
$ composer install --no-dev
|
||||
```
|
||||
|
||||
--------------------------------------------------------
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
|
@ -50,6 +51,7 @@
|
|||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
|
@ -50,6 +51,7 @@
|
|||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
|
@ -50,6 +51,7 @@
|
|||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
|
|
|
@ -69,6 +69,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
|
@ -87,6 +88,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
|
@ -50,6 +51,7 @@
|
|||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
|
|
|
@ -69,6 +69,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
|
@ -87,6 +88,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
|
@ -100,9 +102,6 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
</ul>
|
||||
</div>
|
||||
<h1 id="plugin-system">Plugin System</h1>
|
||||
<blockquote>
|
||||
<p>Note: Plugin current status - in development (not merged into master).</p>
|
||||
</blockquote>
|
||||
<p><a href="#developer-api"><strong>I am a developer.</strong> Developer API.</a><a href=".html"></a></p>
|
||||
<p><a href="#guide-for-template-designer"><strong>I am a template designer.</strong> Guide for template designer.</a><a href=".html"></a></p>
|
||||
<h2 id="developer-api">Developer API</h2>
|
||||
|
@ -121,12 +120,21 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
| plugins/
|
||||
|---| demo_plugin/
|
||||
| |---| demo_plugin.php</code></pre>
|
||||
<h3 id="plugin-initialization">Plugin initialization</h3>
|
||||
<p>At the beginning of Shaarli execution, all enabled plugins are loaded. At this point, the plugin system looks for an <code>init()</code> function to execute and run it if it exists. This function must be named this way, and takes the <code>ConfigManager</code> as parameter.</p>
|
||||
<pre><code><plugin_name>_init($conf)</code></pre>
|
||||
<p>This function can be used to create initial data, load default settings, etc. But also to set <em>plugin errors</em>. If the initialization function returns an array of strings, they will be understand as errors, and displayed in the header to logged in users.</p>
|
||||
<h3 id="understanding-hooks">Understanding hooks</h3>
|
||||
<p>A plugin is a set of functions. Each function will be triggered by the plugin system at certain point in Shaarli execution.</p>
|
||||
<p>These functions need to be named with this pattern:</p>
|
||||
<pre><code>hook_<plugin_name>_<hook_name></code></pre>
|
||||
<pre><code>hook_<plugin_name>_<hook_name>($data, $conf)</code></pre>
|
||||
<p>Parameters:</p>
|
||||
<ul>
|
||||
<li>data: see <a href="https://github.com/shaarli/Shaarli/wiki/Plugin-System#plugins-data">$data section</a><a href=".html"></a></li>
|
||||
<li>conf: the <code>ConfigManager</code> instance.</li>
|
||||
</ul>
|
||||
<p>For exemple, if my plugin want to add data to the header, this function is needed:</p>
|
||||
<pre><code>hook_demo_plugin_render_header()</code></pre>
|
||||
<pre><code>hook_demo_plugin_render_header</code></pre>
|
||||
<p>If this function is declared, and the plugin enabled, it will be called every time Shaarli is rendering the header.</p>
|
||||
<h3 id="plugins-data">Plugin's data</h3>
|
||||
<h4 id="parameters">Parameters</h4>
|
||||
|
@ -159,6 +167,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<ul>
|
||||
<li><code>description</code>: plugin description</li>
|
||||
<li><code>parameters</code>: user parameter names, separated by a <code>;</code>.</li>
|
||||
<li><code>parameter.<PARAMETER_NAME></code>: add a text description the specified parameter.</li>
|
||||
</ul>
|
||||
<blockquote>
|
||||
<p>Note: In PHP, <code>parse_ini_file()</code> seems to want strings to be between by quotes <code>"</code> in the ini file.</p>
|
||||
|
@ -209,16 +218,28 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
</tr>
|
||||
<tr class="even">
|
||||
<td><a href="#render_tagcloud">render_tagcloud</a></td>
|
||||
<td style="text-align: center;">Allow to add content at the top and bottom of the page.</td>
|
||||
<td style="text-align: center;">Allow to add content at the top and bottom of the page, and after all tags.</td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td><a href="#render_taglist">render_taglist</a></td>
|
||||
<td style="text-align: center;">Allow to add content at the top and bottom of the page, and after all tags.</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td><a href="#render_daily">render_daily</a></td>
|
||||
<td style="text-align: center;">Allow to add content at the top and bottom of the page, the bottom of each link and to alter data.</td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td><a href="#render_feed">render_feed</a></td>
|
||||
<td style="text-align: center;">Allow to do add tags in RSS and ATOM feeds.</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td><a href="#savelink">savelink</a></td>
|
||||
<td><a href="#save_link">save_link</a></td>
|
||||
<td style="text-align: center;">Allow to alter the link being saved in the datastore.</td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td><a href="#delete_link">delete_link</a></td>
|
||||
<td style="text-align: center;">Allow to do an action before a link is deleted from the datastore.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h4 id="render_header">render_header</h4>
|
||||
|
@ -376,17 +397,41 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><p><code>plugin_start_zone</code>: before displaying the template content.</p></li>
|
||||
<li><p><code>plugin_end_zone</code>: after displaying the template content.</p></li>
|
||||
</ul>
|
||||
<p>For each tag, the following placeholder can be used:</p>
|
||||
<ul>
|
||||
<li><code>tag_plugin</code>: after each tag</li>
|
||||
</ul>
|
||||
<p><img src="http://i.imgur.com/vHmyT3a.png" alt="plugin_start_end_zone_example" /><a href=".html"></a></p>
|
||||
<h4 id="render_taglist">render_taglist</h4>
|
||||
<p>Triggered when taglist is displayed.</p>
|
||||
<p>Allow to add content at the top and bottom of the page.</p>
|
||||
<h5 id="data-8">Data</h5>
|
||||
<p><code>$data</code> is an array containing:</p>
|
||||
<ul>
|
||||
<li><code>_LOGGEDIN_</code>: true if user is logged in, false otherwise.</li>
|
||||
<li>All templates data.</li>
|
||||
</ul>
|
||||
<h5 id="template-placeholders-8">Template placeholders</h5>
|
||||
<p>Items can be displayed in templates by adding an entry in <code>$data['<placeholder>']</code> array.<a href=".html"></a></p>
|
||||
<p>List of placeholders:</p>
|
||||
<ul>
|
||||
<li><p><code>plugin_start_zone</code>: before displaying the template content.</p></li>
|
||||
<li><p><code>plugin_end_zone</code>: after displaying the template content.</p></li>
|
||||
</ul>
|
||||
<p>For each tag, the following placeholder can be used:</p>
|
||||
<ul>
|
||||
<li><code>tag_plugin</code>: after each tag</li>
|
||||
</ul>
|
||||
<h4 id="render_daily">render_daily</h4>
|
||||
<p>Triggered when tagcloud is displayed.</p>
|
||||
<p>Allow to add content at the top and bottom of the page, the bottom of each link and to alter data.</p>
|
||||
<h5 id="data-8">Data</h5>
|
||||
<h5 id="data-9">Data</h5>
|
||||
<p><code>$data</code> is an array containing:</p>
|
||||
<ul>
|
||||
<li><code>_LOGGEDIN_</code>: true if user is logged in, false otherwise.</li>
|
||||
<li>All templates data, including links.</li>
|
||||
</ul>
|
||||
<h5 id="template-placeholders-8">Template placeholders</h5>
|
||||
<h5 id="template-placeholders-9">Template placeholders</h5>
|
||||
<p>Items can be displayed in templates by adding an entry in <code>$data['<placeholder>']</code> array.<a href=".html"></a></p>
|
||||
<p>List of placeholders:</p>
|
||||
<ul>
|
||||
|
@ -397,18 +442,57 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><p><code>plugin_start_zone</code>: before displaying the template content.</p></li>
|
||||
<li><p><code>plugin_end_zone</code>: after displaying the template content.</p></li>
|
||||
</ul>
|
||||
<h4 id="savelink">savelink</h4>
|
||||
<h4 id="render_feed">render_feed</h4>
|
||||
<p>Triggered when the ATOM or RSS feed is displayed.</p>
|
||||
<p>Allow to add tags in the feed, either in the header or for each items. Items (links) can also be altered before being rendered.</p>
|
||||
<h5 id="data-10">Data</h5>
|
||||
<p><code>$data</code> is an array containing:</p>
|
||||
<ul>
|
||||
<li><code>_LOGGEDIN_</code>: true if user is logged in, false otherwise.</li>
|
||||
<li><code>_PAGE_</code>: containing either <code>rss</code> or <code>atom</code>.</li>
|
||||
<li>All templates data, including links.</li>
|
||||
</ul>
|
||||
<h5 id="template-placeholders-10">Template placeholders</h5>
|
||||
<p>Tags can be added in feeds by adding an entry in <code>$data['<placeholder>']</code> array.<a href=".html"></a></p>
|
||||
<p>List of placeholders:</p>
|
||||
<ul>
|
||||
<li><code>feed_plugins_header</code>: used as a header tag in the feed.</li>
|
||||
</ul>
|
||||
<p>For each links:</p>
|
||||
<ul>
|
||||
<li><code>feed_plugins</code>: additional tag for every link entry.</li>
|
||||
</ul>
|
||||
<h4 id="save_link">save_link</h4>
|
||||
<p>Triggered when a link is save (new link or edit).</p>
|
||||
<p>Allow to alter the link being saved in the datastore.</p>
|
||||
<h5 id="data-9">Data</h5>
|
||||
<h5 id="data-11">Data</h5>
|
||||
<p><code>$data</code> is an array containing the link being saved:</p>
|
||||
<ul>
|
||||
<li>id</li>
|
||||
<li>title</li>
|
||||
<li>url</li>
|
||||
<li>shorturl</li>
|
||||
<li>description</li>
|
||||
<li>linkdate</li>
|
||||
<li>private</li>
|
||||
<li>tags</li>
|
||||
<li>created</li>
|
||||
<li>updated</li>
|
||||
</ul>
|
||||
<h4 id="delete_link">delete_link</h4>
|
||||
<p>Triggered when a link is deleted.</p>
|
||||
<p>Allow to execute any action before the link is actually removed from the datastore</p>
|
||||
<h5 id="data-12">Data</h5>
|
||||
<p><code>$data</code> is an array containing the link being saved:</p>
|
||||
<ul>
|
||||
<li>id</li>
|
||||
<li>title</li>
|
||||
<li>url</li>
|
||||
<li>shorturl</li>
|
||||
<li>description</li>
|
||||
<li>private</li>
|
||||
<li>tags</li>
|
||||
<li>created</li>
|
||||
<li>updated</li>
|
||||
</ul>
|
||||
<h2 id="guide-for-template-designer">Guide for template designer</h2>
|
||||
<h3 id="plugin-administration">Plugin administration</h3>
|
||||
|
@ -537,5 +621,14 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
{$value}
|
||||
{/loop}
|
||||
<span class="kw"></div></span></code></pre></div>
|
||||
<p><strong>feed.atom.xml</strong> and <strong>feed.rss.xml</strong>:</p>
|
||||
<p>In headers tags section:</p>
|
||||
<div class="sourceCode"><pre class="sourceCode xml"><code class="sourceCode xml">{loop="$feed_plugins_header"}
|
||||
{$value}
|
||||
{/loop}</code></pre></div>
|
||||
<p>After each entry:</p>
|
||||
<div class="sourceCode"><pre class="sourceCode xml"><code class="sourceCode xml">{loop="$value.feed_plugins"}
|
||||
{$value}
|
||||
{/loop}</code></pre></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
#Plugin System
|
||||
> Note: Plugin current status - in development (not merged into master).
|
||||
|
||||
[**I am a developer.** Developer API.](#developer-api)[](.html)
|
||||
|
||||
[**I am a template designer.** Guide for template designer.](#guide-for-template-designer)[](.html)
|
||||
|
@ -30,6 +28,14 @@ You should have the following tree view:
|
|||
| |---| demo_plugin.php
|
||||
```
|
||||
|
||||
### Plugin initialization
|
||||
|
||||
At the beginning of Shaarli execution, all enabled plugins are loaded. At this point, the plugin system looks for an `init()` function to execute and run it if it exists. This function must be named this way, and takes the `ConfigManager` as parameter.
|
||||
|
||||
<plugin_name>_init($conf)
|
||||
|
||||
This function can be used to create initial data, load default settings, etc. But also to set *plugin errors*. If the initialization function returns an array of strings, they will be understand as errors, and displayed in the header to logged in users.
|
||||
|
||||
### Understanding hooks
|
||||
|
||||
A plugin is a set of functions. Each function will be triggered by the plugin system at certain point in Shaarli execution.
|
||||
|
@ -37,12 +43,17 @@ A plugin is a set of functions. Each function will be triggered by the plugin sy
|
|||
These functions need to be named with this pattern:
|
||||
|
||||
```
|
||||
hook_<plugin_name>_<hook_name>
|
||||
hook_<plugin_name>_<hook_name>($data, $conf)
|
||||
```
|
||||
|
||||
Parameters:
|
||||
|
||||
- data: see [$data section](https://github.com/shaarli/Shaarli/wiki/Plugin-System#plugins-data)[](.html)
|
||||
- conf: the `ConfigManager` instance.
|
||||
|
||||
For exemple, if my plugin want to add data to the header, this function is needed:
|
||||
|
||||
hook_demo_plugin_render_header()
|
||||
hook_demo_plugin_render_header
|
||||
|
||||
If this function is declared, and the plugin enabled, it will be called every time Shaarli is rendering the header.
|
||||
|
||||
|
@ -98,6 +109,7 @@ Each file contain two keys:
|
|||
|
||||
* `description`: plugin description
|
||||
* `parameters`: user parameter names, separated by a `;`.
|
||||
* `parameter.<PARAMETER_NAME>`: add a text description the specified parameter.
|
||||
|
||||
> Note: In PHP, `parse_ini_file()` seems to want strings to be between by quotes `"` in the ini file.
|
||||
|
||||
|
@ -118,9 +130,13 @@ If it's still not working, please [open an issue](https://github.com/shaarli/Sha
|
|||
| [render_editlink](#render_editlink) | Allow to add fields in the form, or display elements. |[](.html)
|
||||
| [render_tools](#render_tools) | Allow to add content at the end of the page. |[](.html)
|
||||
| [render_picwall](#render_picwall) | Allow to add content at the top and bottom of the page. |[](.html)
|
||||
| [render_tagcloud](#render_tagcloud) | Allow to add content at the top and bottom of the page. |[](.html)
|
||||
| [render_tagcloud](#render_tagcloud) | Allow to add content at the top and bottom of the page, and after all tags. |[](.html)
|
||||
| [render_taglist](#render_taglist) | Allow to add content at the top and bottom of the page, and after all tags. |[](.html)
|
||||
| [render_daily](#render_daily) | Allow to add content at the top and bottom of the page, the bottom of each link and to alter data. |[](.html)
|
||||
| [savelink](#savelink) | Allow to alter the link being saved in the datastore. |[](.html)
|
||||
| [render_feed](#render_feed) | Allow to do add tags in RSS and ATOM feeds. |[](.html)
|
||||
| [save_link](#save_link) | Allow to alter the link being saved in the datastore. |[](.html)
|
||||
| [delete_link](#delete_link) | Allow to do an action before a link is deleted from the datastore. |[](.html)
|
||||
|
||||
|
||||
|
||||
#### render_header
|
||||
|
@ -330,8 +346,40 @@ List of placeholders:
|
|||
|
||||
* `plugin_end_zone`: after displaying the template content.
|
||||
|
||||
For each tag, the following placeholder can be used:
|
||||
|
||||
* `tag_plugin`: after each tag
|
||||
|
||||
![plugin_start_end_zone_example](http://i.imgur.com/vHmyT3a.png)[](.html)
|
||||
|
||||
|
||||
#### render_taglist
|
||||
|
||||
Triggered when taglist is displayed.
|
||||
|
||||
Allow to add content at the top and bottom of the page.
|
||||
|
||||
##### Data
|
||||
|
||||
`$data` is an array containing:
|
||||
|
||||
* `_LOGGEDIN_`: true if user is logged in, false otherwise.
|
||||
* All templates data.
|
||||
|
||||
##### Template placeholders
|
||||
|
||||
Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.[](.html)
|
||||
|
||||
List of placeholders:
|
||||
|
||||
* `plugin_start_zone`: before displaying the template content.
|
||||
|
||||
* `plugin_end_zone`: after displaying the template content.
|
||||
|
||||
For each tag, the following placeholder can be used:
|
||||
|
||||
* `tag_plugin`: after each tag
|
||||
|
||||
#### render_daily
|
||||
|
||||
Triggered when tagcloud is displayed.
|
||||
|
@ -359,7 +407,33 @@ List of placeholders:
|
|||
|
||||
* `plugin_end_zone`: after displaying the template content.
|
||||
|
||||
#### savelink
|
||||
#### render_feed
|
||||
|
||||
Triggered when the ATOM or RSS feed is displayed.
|
||||
|
||||
Allow to add tags in the feed, either in the header or for each items. Items (links) can also be altered before being rendered.
|
||||
|
||||
##### Data
|
||||
|
||||
`$data` is an array containing:
|
||||
|
||||
* `_LOGGEDIN_`: true if user is logged in, false otherwise.
|
||||
* `_PAGE_`: containing either `rss` or `atom`.
|
||||
* All templates data, including links.
|
||||
|
||||
##### Template placeholders
|
||||
|
||||
Tags can be added in feeds by adding an entry in `$data['<placeholder>']` array.[](.html)
|
||||
|
||||
List of placeholders:
|
||||
|
||||
* `feed_plugins_header`: used as a header tag in the feed.
|
||||
|
||||
For each links:
|
||||
|
||||
* `feed_plugins`: additional tag for every link entry.
|
||||
|
||||
#### save_link
|
||||
|
||||
Triggered when a link is save (new link or edit).
|
||||
|
||||
|
@ -369,12 +443,36 @@ Allow to alter the link being saved in the datastore.
|
|||
|
||||
`$data` is an array containing the link being saved:
|
||||
|
||||
* id
|
||||
* title
|
||||
* url
|
||||
* shorturl
|
||||
* description
|
||||
* linkdate
|
||||
* private
|
||||
* tags
|
||||
* created
|
||||
* updated
|
||||
|
||||
|
||||
#### delete_link
|
||||
|
||||
Triggered when a link is deleted.
|
||||
|
||||
Allow to execute any action before the link is actually removed from the datastore
|
||||
|
||||
##### Data
|
||||
|
||||
`$data` is an array containing the link being saved:
|
||||
|
||||
* id
|
||||
* title
|
||||
* url
|
||||
* shorturl
|
||||
* description
|
||||
* private
|
||||
* tags
|
||||
* created
|
||||
* updated
|
||||
|
||||
## Guide for template designer
|
||||
|
||||
|
@ -595,3 +693,19 @@ Bottom:
|
|||
{/loop}
|
||||
</div>
|
||||
```
|
||||
|
||||
**feed.atom.xml** and **feed.rss.xml**:
|
||||
|
||||
In headers tags section:
|
||||
```xml
|
||||
{loop="$feed_plugins_header"}
|
||||
{$value}
|
||||
{/loop}
|
||||
```
|
||||
|
||||
After each entry:
|
||||
```xml
|
||||
{loop="$value.feed_plugins"}
|
||||
{$value}
|
||||
{/loop}
|
||||
```
|
||||
|
|
|
@ -69,6 +69,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
|
@ -87,6 +88,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
|
@ -145,7 +147,6 @@ Example:</p>
|
|||
<li><a href="https://github.com/shaarli/Shaarli/blob/master/plugins/markdown/README.md"><code>markdown</code></a>: Render shaare description with Markdown syntax.<a href=".html"></a></li>
|
||||
<li><a href="https://github.com/shaarli/Shaarli/blob/master/plugins/playvideos/README.md"><code>playvideos</code></a>: Add a button in the toolbar allowing to watch all videos.<a href=".html"></a></li>
|
||||
<li><code>qrcode</code>: For each link, add a QRCode icon.</li>
|
||||
<li><code>readityourself</code>: For each link, add a ReadItYourself icon to save the shaared URL</li>
|
||||
<li><a href="https://github.com/shaarli/Shaarli/blob/master/plugins/wallabag/README.md"><code>wallabag</code></a>: For each link, add a Wallabag icon to save it in your instance.<a href=".html"></a></li>
|
||||
</ul>
|
||||
<h4 id="third-party-plugins">Third party plugins</h4>
|
||||
|
|
|
@ -67,7 +67,6 @@ Usage of each plugin is documented in it's README file:
|
|||
* [`markdown`](https://github.com/shaarli/Shaarli/blob/master/plugins/markdown/README.md): Render shaare description with Markdown syntax.[](.html)
|
||||
* [`playvideos`](https://github.com/shaarli/Shaarli/blob/master/plugins/playvideos/README.md): Add a button in the toolbar allowing to watch all videos.[](.html)
|
||||
* `qrcode`: For each link, add a QRCode icon.
|
||||
* `readityourself`: For each link, add a ReadItYourself icon to save the shaared URL
|
||||
* [`wallabag`](https://github.com/shaarli/Shaarli/blob/master/plugins/wallabag/README.md): For each link, add a Wallabag icon to save it in your instance.[](.html)
|
||||
|
||||
|
||||
|
|
169
doc/REST-API.html
Normal file
169
doc/REST-API.html
Normal file
|
@ -0,0 +1,169 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="generator" content="pandoc">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
|
||||
<title>Shaarli – REST API</title>
|
||||
<style type="text/css">code{white-space: pre;}</style>
|
||||
<style type="text/css">
|
||||
div.sourceCode { overflow-x: auto; }
|
||||
table.sourceCode, tr.sourceCode, td.lineNumbers, td.sourceCode {
|
||||
margin: 0; padding: 0; vertical-align: baseline; border: none; }
|
||||
table.sourceCode { width: 100%; line-height: 100%; }
|
||||
td.lineNumbers { text-align: right; padding-right: 4px; padding-left: 4px; color: #aaaaaa; border-right: 1px solid #aaaaaa; }
|
||||
td.sourceCode { padding-left: 5px; }
|
||||
code > span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code > span.dt { color: #902000; } /* DataType */
|
||||
code > span.dv { color: #40a070; } /* DecVal */
|
||||
code > span.bn { color: #40a070; } /* BaseN */
|
||||
code > span.fl { color: #40a070; } /* Float */
|
||||
code > span.ch { color: #4070a0; } /* Char */
|
||||
code > span.st { color: #4070a0; } /* String */
|
||||
code > span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code > span.ot { color: #007020; } /* Other */
|
||||
code > span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code > span.fu { color: #06287e; } /* Function */
|
||||
code > span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code > span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
code > span.cn { color: #880000; } /* Constant */
|
||||
code > span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code > span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code > span.ss { color: #bb6688; } /* SpecialString */
|
||||
code > span.im { } /* Import */
|
||||
code > span.va { color: #19177c; } /* Variable */
|
||||
code > span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code > span.op { color: #666666; } /* Operator */
|
||||
code > span.bu { } /* BuiltIn */
|
||||
code > span.ex { } /* Extension */
|
||||
code > span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code > span.at { color: #7d9029; } /* Attribute */
|
||||
code > span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code > span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code > span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
</style>
|
||||
<link rel="stylesheet" href="github-markdown.css">
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
<div id="local-sidebar">
|
||||
<ul>
|
||||
<li><a href="Home.html">Home</a></li>
|
||||
<li>Setup
|
||||
<ul>
|
||||
<li><a href="Download-and-Installation.html">Download and Installation</a></li>
|
||||
<li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li>
|
||||
<li><a href="Server-requirements.html">Server requirements</a></li>
|
||||
<li><a href="Server-configuration.html">Server configuration</a></li>
|
||||
<li><a href="Server-security.html">Server security</a></li>
|
||||
<li><a href="Shaarli-configuration.html">Shaarli configuration</a></li>
|
||||
<li><a href="Plugins.html">Plugins</a></li>
|
||||
</ul></li>
|
||||
<li><a href="Docker.html">Docker</a></li>
|
||||
<li><a href="Usage.html">Usage</a>
|
||||
<ul>
|
||||
<li><a href="Sharing-button.html">Sharing button</a> (bookmarklet)</li>
|
||||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
<li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li>
|
||||
<li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li>
|
||||
<li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li>
|
||||
<li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li>
|
||||
<li><a href="Datastore-hacks.html">Datastore hacks</a></li>
|
||||
</ul></li>
|
||||
<li><a href="Troubleshooting.html">Troubleshooting</a></li>
|
||||
<li><a href="Development.html">Development</a>
|
||||
<ul>
|
||||
<li><a href="GnuPG-signature.html">GnuPG signature</a></li>
|
||||
<li><a href="Coding-guidelines.html">Coding guidelines</a></li>
|
||||
<li><a href="Directory-structure.html">Directory structure</a></li>
|
||||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
<li><a href="Unit-tests.html">Unit tests</a></li>
|
||||
</ul></li>
|
||||
<li>About
|
||||
<ul>
|
||||
<li><a href="FAQ.html">FAQ</a></li>
|
||||
<li><a href="Community-&-Related-software.html">Community & Related software</a></li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
</div>
|
||||
<h1 id="rest-api">REST API</h1>
|
||||
<h2 id="usage">Usage</h2>
|
||||
<p>See the <a href="http://shaarli.github.io/api-documentation/">REST API documentation</a>.<a href=".html"></a></p>
|
||||
<h2 id="authentication">Authentication</h2>
|
||||
<p>All requests to Shaarli's API must include a JWT token to verify their authenticity.</p>
|
||||
<p>This token has to be included as an HTTP header called <code>Authentication: Bearer <jwt token></code>.</p>
|
||||
<p>JWT resources :</p>
|
||||
<ul>
|
||||
<li><a href="https://jwt.io">jwt.io</a> (including a list of client per language).<a href=".html"></a></li>
|
||||
<li>RFC : <a href="https://tools.ietf.org/html/rfc7519" class="uri">https://tools.ietf.org/html/rfc7519</a></li>
|
||||
<li><a href="https://float-middle.com/json-web-tokens-jwt-vs-sessions/" class="uri">https://float-middle.com/json-web-tokens-jwt-vs-sessions/</a></li>
|
||||
<li>HackerNews thread: <a href="https://news.ycombinator.com/item?id=11929267" class="uri">https://news.ycombinator.com/item?id=11929267</a></li>
|
||||
</ul>
|
||||
<h3 id="shaarli-jwt-token">Shaarli JWT Token</h3>
|
||||
<p>JWT tokens are composed by three parts, separated by a dot <code>.</code> and encoded in base64:</p>
|
||||
<pre><code>[header].[payload].[signature][](.html)</code></pre>
|
||||
<h4 id="header">Header</h4>
|
||||
<p>Shaarli only allow one hash algorithm, so the header will always be the same:</p>
|
||||
<div class="sourceCode"><pre class="sourceCode json"><code class="sourceCode json"><span class="fu">{</span>
|
||||
<span class="dt">"typ"</span><span class="fu">:</span> <span class="st">"JWT"</span><span class="fu">,</span>
|
||||
<span class="dt">"alg"</span><span class="fu">:</span> <span class="st">"HS512"</span>
|
||||
<span class="fu">}</span></code></pre></div>
|
||||
<p>Encoded in base64, it gives:</p>
|
||||
<pre><code>ewogICAgICAgICJ0eXAiOiAiSldUIiwKICAgICAgICAiYWxnIjogIkhTNTEyIgogICAgfQ==</code></pre>
|
||||
<h4 id="payload">Payload</h4>
|
||||
<p><strong>Validity duration</strong></p>
|
||||
<p>To avoid infinite token validity, JWT tokens must include their creation date in UNIX timestamp format (timezone independant - UTC) under the key <code>iat</code> (issued at). This token will be accepted during 9 minutes.</p>
|
||||
<div class="sourceCode"><pre class="sourceCode json"><code class="sourceCode json"><span class="fu">{</span>
|
||||
<span class="dt">"iat"</span><span class="fu">:</span> <span class="dv">1468663519</span>
|
||||
<span class="fu">}</span></code></pre></div>
|
||||
<p>See <a href="https://tools.ietf.org/html/rfc7519#section-4.1.6">RFC reference</a>.<a href=".html"></a></p>
|
||||
<h4 id="signature">Signature</h4>
|
||||
<p>The signature authenticate the token validity. It contains the base64 of the header and the body, separated by a dot <code>.</code>, hashed in SHA512 with the API secret available in Shaarli administration page.</p>
|
||||
<p>Signature example with PHP:</p>
|
||||
<div class="sourceCode"><pre class="sourceCode php"><code class="sourceCode php"><span class="kw">$content</span> = <span class="fu">base64_encode</span><span class="ot">(</span><span class="kw">$header</span><span class="ot">)</span> . <span class="st">'.'</span> . <span class="fu">base64_encode</span><span class="ot">(</span><span class="kw">$payload</span><span class="ot">);</span>
|
||||
<span class="kw">$signature</span> = <span class="fu">hash_hmac</span><span class="ot">(</span><span class="st">'sha512'</span><span class="ot">,</span> <span class="kw">$content</span><span class="ot">,</span> <span class="kw">$secret</span><span class="ot">);</span></code></pre></div>
|
||||
<h3 id="complete-example">Complete example</h3>
|
||||
<h4 id="php">PHP</h4>
|
||||
<div class="sourceCode"><pre class="sourceCode php"><code class="sourceCode php"><span class="kw">function</span> generateToken<span class="ot">(</span><span class="kw">$secret</span><span class="ot">)</span> {
|
||||
<span class="kw">$header</span> = <span class="fu">base64_encode</span><span class="ot">(</span><span class="st">'{</span>
|
||||
<span class="st"> "typ": "JWT",</span>
|
||||
<span class="st"> "alg": "HS512"</span>
|
||||
<span class="st"> }'</span><span class="ot">);</span>
|
||||
<span class="kw">$payload</span> = <span class="fu">base64_encode</span><span class="ot">(</span><span class="st">'{</span>
|
||||
<span class="st"> "iat": '</span>. <span class="fu">time</span><span class="ot">()</span> .<span class="st">'</span>
|
||||
<span class="st"> }'</span><span class="ot">);</span>
|
||||
<span class="kw">$signature</span> = <span class="fu">hash_hmac</span><span class="ot">(</span><span class="st">'sha512'</span><span class="ot">,</span> <span class="kw">$header</span> .<span class="st">'.'</span>. <span class="kw">$payload</span> <span class="ot">,</span> <span class="kw">$secret</span><span class="ot">);</span>
|
||||
<span class="kw">return</span> <span class="kw">$header</span> .<span class="st">'.'</span>. <span class="kw">$payload</span> .<span class="st">'.'</span>. <span class="kw">$signature</span><span class="ot">;</span>
|
||||
}
|
||||
|
||||
<span class="kw">$secret</span> = <span class="st">'mysecret'</span><span class="ot">;</span>
|
||||
<span class="kw">$token</span> = generateToken<span class="ot">(</span><span class="kw">$secret</span><span class="ot">);</span>
|
||||
<span class="fu">echo</span> <span class="kw">$token</span><span class="ot">;</span></code></pre></div>
|
||||
<blockquote>
|
||||
<p><code>ewogICAgICAgICJ0eXAiOiAiSldUIiwKICAgICAgICAiYWxnIjogIkhTNTEyIgogICAgfQ==.ewogICAgICAgICJpYXQiOiAxNDY4NjY3MDQ3CiAgICB9.1d2c54fa947daf594fdbf7591796195652c8bc63bffad7f6a6db2a41c313f495a542cbfb595acade79e83f3810d709b4251d7b940bbc10b531a6e6134af63a68</code></p>
|
||||
</blockquote>
|
||||
<div class="sourceCode"><pre class="sourceCode php"><code class="sourceCode php"><span class="kw">$options</span> = <span class="ot">[[](</span>.html<span class="ot">)</span>
|
||||
<span class="st">'http'</span> => <span class="ot">[[](</span>.html<span class="ot">)</span>
|
||||
<span class="st">'method'</span> => <span class="st">'GET'</span><span class="ot">,</span>
|
||||
<span class="st">'jwt'</span> => <span class="kw">$token</span><span class="ot">,</span>
|
||||
<span class="ot">],</span>
|
||||
<span class="ot">];</span>
|
||||
<span class="kw">$context</span> = <span class="fu">stream_context_create</span><span class="ot">(</span><span class="kw">$options</span><span class="ot">);</span>
|
||||
<span class="fu">file_get_contents</span><span class="ot">(</span><span class="kw">$apiEndpoint</span><span class="ot">,</span> <span class="kw">false</span><span class="ot">,</span> <span class="kw">$context</span><span class="ot">);</span></code></pre></div>
|
||||
</body>
|
||||
</html>
|
105
doc/REST-API.md
Normal file
105
doc/REST-API.md
Normal file
|
@ -0,0 +1,105 @@
|
|||
#REST API
|
||||
## Usage
|
||||
|
||||
See the [REST API documentation](http://shaarli.github.io/api-documentation/).[](.html)
|
||||
|
||||
## Authentication
|
||||
|
||||
All requests to Shaarli's API must include a JWT token to verify their authenticity.
|
||||
|
||||
This token has to be included as an HTTP header called `Authentication: Bearer <jwt token>`.
|
||||
|
||||
JWT resources :
|
||||
|
||||
* [jwt.io](https://jwt.io) (including a list of client per language).[](.html)
|
||||
* RFC : https://tools.ietf.org/html/rfc7519
|
||||
* https://float-middle.com/json-web-tokens-jwt-vs-sessions/
|
||||
* HackerNews thread: https://news.ycombinator.com/item?id=11929267
|
||||
|
||||
|
||||
### Shaarli JWT Token
|
||||
|
||||
JWT tokens are composed by three parts, separated by a dot `.` and encoded in base64:
|
||||
|
||||
```
|
||||
[header].[payload].[signature][](.html)
|
||||
```
|
||||
|
||||
#### Header
|
||||
|
||||
Shaarli only allow one hash algorithm, so the header will always be the same:
|
||||
|
||||
```json
|
||||
{
|
||||
"typ": "JWT",
|
||||
"alg": "HS512"
|
||||
}
|
||||
```
|
||||
|
||||
Encoded in base64, it gives:
|
||||
|
||||
```
|
||||
ewogICAgICAgICJ0eXAiOiAiSldUIiwKICAgICAgICAiYWxnIjogIkhTNTEyIgogICAgfQ==
|
||||
```
|
||||
|
||||
#### Payload
|
||||
|
||||
**Validity duration**
|
||||
|
||||
To avoid infinite token validity, JWT tokens must include their creation date in UNIX timestamp format (timezone independant - UTC) under the key `iat` (issued at). This token will be accepted during 9 minutes.
|
||||
|
||||
```json
|
||||
{
|
||||
"iat": 1468663519
|
||||
}
|
||||
```
|
||||
|
||||
See [RFC reference](https://tools.ietf.org/html/rfc7519#section-4.1.6).[](.html)
|
||||
|
||||
|
||||
#### Signature
|
||||
|
||||
The signature authenticate the token validity. It contains the base64 of the header and the body, separated by a dot `.`, hashed in SHA512 with the API secret available in Shaarli administration page.
|
||||
|
||||
Signature example with PHP:
|
||||
|
||||
```php
|
||||
$content = base64_encode($header) . '.' . base64_encode($payload);
|
||||
$signature = hash_hmac('sha512', $content, $secret);
|
||||
```
|
||||
|
||||
|
||||
### Complete example
|
||||
|
||||
#### PHP
|
||||
|
||||
```php
|
||||
function generateToken($secret) {
|
||||
$header = base64_encode('{
|
||||
"typ": "JWT",
|
||||
"alg": "HS512"
|
||||
}');
|
||||
$payload = base64_encode('{
|
||||
"iat": '. time() .'
|
||||
}');
|
||||
$signature = hash_hmac('sha512', $header .'.'. $payload , $secret);
|
||||
return $header .'.'. $payload .'.'. $signature;
|
||||
}
|
||||
|
||||
$secret = 'mysecret';
|
||||
$token = generateToken($secret);
|
||||
echo $token;
|
||||
```
|
||||
|
||||
> `ewogICAgICAgICJ0eXAiOiAiSldUIiwKICAgICAgICAiYWxnIjogIkhTNTEyIgogICAgfQ==.ewogICAgICAgICJpYXQiOiAxNDY4NjY3MDQ3CiAgICB9.1d2c54fa947daf594fdbf7591796195652c8bc63bffad7f6a6db2a41c313f495a542cbfb595acade79e83f3810d709b4251d7b940bbc10b531a6e6134af63a68`
|
||||
|
||||
```php
|
||||
$options = [[](.html)
|
||||
'http' => [[](.html)
|
||||
'method' => 'GET',
|
||||
'jwt' => $token,
|
||||
],
|
||||
];
|
||||
$context = stream_context_create($options);
|
||||
file_get_contents($apiEndpoint, false, $context);
|
||||
```
|
|
@ -32,6 +32,7 @@
|
|||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
|
@ -50,6 +51,7 @@
|
|||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
|
|
|
@ -69,6 +69,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
|
@ -87,6 +88,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
|
@ -192,6 +194,8 @@ $ <span class="fu">git</span> verify-tag f7762cf803f03f5caf4b8078359a63783d0090c
|
|||
<span class="ex">gpg</span>: Signature made Thu 30 Jul 2015 11:46:34 CEST using RSA key ID 4100DF6F
|
||||
<span class="ex">gpg</span>: Good signature from <span class="st">"VirtualTam <virtualtam@flibidi.net>"</span> [ultimate][](.html)</code></pre></div>
|
||||
<h2 id="publish-the-github-release">Publish the GitHub release</h2>
|
||||
<h3 id="update-release-badges">Update release badges</h3>
|
||||
<p>Update <code>README.md</code> so version badges display and point to the newly released Shaarli version(s).</p>
|
||||
<h3 id="create-a-github-release-from-a-git-tag">Create a GitHub release from a Git tag</h3>
|
||||
<p>From the previously drafted release:</p>
|
||||
<ul>
|
||||
|
|
|
@ -103,6 +103,9 @@ gpg: Good signature from "VirtualTam <virtualtam@flibidi.net>" [ultimate][](.htm
|
|||
```
|
||||
|
||||
## Publish the GitHub release
|
||||
### Update release badges
|
||||
Update `README.md` so version badges display and point to the newly released Shaarli version(s).
|
||||
|
||||
### Create a GitHub release from a Git tag
|
||||
From the previously drafted release:
|
||||
- edit the release notes (if needed)
|
||||
|
|
|
@ -69,6 +69,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
|
@ -87,6 +88,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
|
|
|
@ -69,6 +69,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
|
@ -87,6 +88,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
|
@ -196,6 +198,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<h3 id="htaccess">.htaccess</h3>
|
||||
<p>Shaarli use <code>.htaccess</code> Apache files to deny access to files that shouldn't be directly accessed (datastore, config, etc.). You need the directive <code>AllowOverride All</code> in your virtual host configuration for them to work.</p>
|
||||
<p><strong>Warning</strong>: If you use Apache 2.2 or lower, you need <a href="https://httpd.apache.org/docs/current/mod/mod_version.html">mod_version</a> to be installed and enabled.<a href=".html"></a></p>
|
||||
<p>Apache module <code>mod_rewrite</code> <strong>must</strong> be enabled to use the REST API. URL rewriting rules for the Slim microframework are stated in the root <code>.htaccess</code> file.</p>
|
||||
<h2 id="lighthttpd">LightHttpd</h2>
|
||||
<h2 id="nginx">Nginx</h2>
|
||||
<h3 id="foreword">Foreword</h3>
|
||||
|
@ -296,11 +299,14 @@ http {
|
|||
error_log /var/log/nginx/error.log;
|
||||
|
||||
location /shaarli/ {
|
||||
try_files $uri /shaarli/index.php$is_args$args;
|
||||
access_log /var/log/nginx/shaarli.access.log;
|
||||
error_log /var/log/nginx/shaarli.error.log;
|
||||
}
|
||||
|
||||
location ~ (index)\.php$ {
|
||||
try_files $uri =404;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
|
||||
fastcgi_index index.php;
|
||||
include fastcgi.conf;
|
||||
|
@ -335,6 +341,10 @@ location ~ ~$ {
|
|||
}</code></pre>
|
||||
<pre class="nginx"><code># /etc/nginx/php.conf
|
||||
location ~ (index)\.php$ {
|
||||
# Slim - split URL path into (script_filename, path_info)
|
||||
try_files $uri =404;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
|
||||
# filter and proxy PHP requests to PHP-FPM
|
||||
fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
|
||||
fastcgi_index index.php;
|
||||
|
@ -367,6 +377,9 @@ http {
|
|||
server_name my.first.domain.org;
|
||||
|
||||
location /shaarli/ {
|
||||
# Slim - rewrite URLs
|
||||
try_files $uri /shaarli/index.php$is_args$args;
|
||||
|
||||
access_log /var/log/nginx/shaarli.access.log;
|
||||
error_log /var/log/nginx/shaarli.error.log;
|
||||
}
|
||||
|
@ -425,6 +438,9 @@ http {
|
|||
ssl_certificate_key /home/john/ssl/localhost.key;
|
||||
|
||||
location /shaarli/ {
|
||||
# Slim - rewrite URLs
|
||||
try_files $uri /index.php$is_args$args;
|
||||
|
||||
access_log /var/log/nginx/shaarli.access.log;
|
||||
error_log /var/log/nginx/shaarli.error.log;
|
||||
}
|
||||
|
|
|
@ -107,6 +107,8 @@ See [Server-side TLS](https://wiki.mozilla.org/Security/Server_Side_TLS#Apache)
|
|||
Shaarli use `.htaccess` Apache files to deny access to files that shouldn't be directly accessed (datastore, config, etc.). You need the directive `AllowOverride All` in your virtual host configuration for them to work.
|
||||
|
||||
**Warning**: If you use Apache 2.2 or lower, you need [mod_version](https://httpd.apache.org/docs/current/mod/mod_version.html) to be installed and enabled.[](.html)
|
||||
|
||||
Apache module `mod_rewrite` **must** be enabled to use the REST API. URL rewriting rules for the Slim microframework are stated in the root `.htaccess` file.
|
||||
|
||||
## LightHttpd
|
||||
|
||||
|
@ -218,11 +220,14 @@ http {
|
|||
error_log /var/log/nginx/error.log;
|
||||
|
||||
location /shaarli/ {
|
||||
try_files $uri /shaarli/index.php$is_args$args;
|
||||
access_log /var/log/nginx/shaarli.access.log;
|
||||
error_log /var/log/nginx/shaarli.error.log;
|
||||
}
|
||||
|
||||
location ~ (index)\.php$ {
|
||||
try_files $uri =404;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
|
||||
fastcgi_index index.php;
|
||||
include fastcgi.conf;
|
||||
|
@ -261,6 +266,10 @@ location ~ ~$ {
|
|||
```nginx
|
||||
# /etc/nginx/php.conf
|
||||
location ~ (index)\.php$ {
|
||||
# Slim - split URL path into (script_filename, path_info)
|
||||
try_files $uri =404;
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
|
||||
# filter and proxy PHP requests to PHP-FPM
|
||||
fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
|
||||
fastcgi_index index.php;
|
||||
|
@ -299,6 +308,9 @@ http {
|
|||
server_name my.first.domain.org;
|
||||
|
||||
location /shaarli/ {
|
||||
# Slim - rewrite URLs
|
||||
try_files $uri /shaarli/index.php$is_args$args;
|
||||
|
||||
access_log /var/log/nginx/shaarli.access.log;
|
||||
error_log /var/log/nginx/shaarli.error.log;
|
||||
}
|
||||
|
@ -361,6 +373,9 @@ http {
|
|||
ssl_certificate_key /home/john/ssl/localhost.key;
|
||||
|
||||
location /shaarli/ {
|
||||
# Slim - rewrite URLs
|
||||
try_files $uri /index.php$is_args$args;
|
||||
|
||||
access_log /var/log/nginx/shaarli.access.log;
|
||||
error_log /var/log/nginx/shaarli.error.log;
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
|
@ -50,6 +51,7 @@
|
|||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
|
@ -83,26 +85,31 @@
|
|||
</thead>
|
||||
<tbody>
|
||||
<tr class="odd">
|
||||
<td style="text-align: center;">7.1</td>
|
||||
<td style="text-align: center;">Supported (v0.9.x)</td>
|
||||
<td style="text-align: center;">✅</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td style="text-align: center;">7.0</td>
|
||||
<td style="text-align: center;">Supported</td>
|
||||
<td style="text-align: center;">✅</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<tr class="odd">
|
||||
<td style="text-align: center;">5.6</td>
|
||||
<td style="text-align: center;">Supported</td>
|
||||
<td style="text-align: center;">✅</td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<tr class="even">
|
||||
<td style="text-align: center;">5.5</td>
|
||||
<td style="text-align: center;">EOL: 2016-07-10</td>
|
||||
<td style="text-align: center;">✅</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<tr class="odd">
|
||||
<td style="text-align: center;">5.4</td>
|
||||
<td style="text-align: center;">EOL: 2015-09-14</td>
|
||||
<td style="text-align: center;">✅ (up to Shaarli 0.8.x)</td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<tr class="even">
|
||||
<td style="text-align: center;">5.3</td>
|
||||
<td style="text-align: center;">EOL: 2014-08-14</td>
|
||||
<td style="text-align: center;">✅ (up to Shaarli 0.8.x)</td>
|
||||
|
@ -130,6 +137,16 @@ download and install third-party PHP dependencies.</p>
|
|||
<td style="text-align: center;">All</td>
|
||||
<td>Import bookmarks from Netscape files<a href=".html"></a></td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td><a href="https://packagist.org/packages/erusev/parsedown"><code>erusev/parsedown</code></a></td>
|
||||
<td style="text-align: center;">All</td>
|
||||
<td>Parse MarkDown syntax for the MarkDown plugin<a href=".html"></a></td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td><a href="https://packagist.org/packages/slim/slim"><code>slim/slim</code></a></td>
|
||||
<td style="text-align: center;">All</td>
|
||||
<td>Handle routes and middleware for the REST API<a href=".html"></a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3 id="extensions">Extensions</h3>
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
### Supported versions
|
||||
Version | Status | Shaarli compatibility
|
||||
:---:|:---:|:---:
|
||||
7.1 | Supported (v0.9.x) | :white_check_mark:
|
||||
7.0 | Supported | :white_check_mark:
|
||||
5.6 | Supported | :white_check_mark:
|
||||
5.5 | EOL: 2016-07-10 | :white_check_mark:
|
||||
|
@ -26,6 +27,8 @@ download and install third-party PHP dependencies.
|
|||
Library | Required? | Usage
|
||||
---|:---:|---
|
||||
[`shaarli/netscape-bookmark-parser`](https://packagist.org/packages/shaarli/netscape-bookmark-parser) | All | Import bookmarks from Netscape files[](.html)
|
||||
[`erusev/parsedown`](https://packagist.org/packages/erusev/parsedown) | All | Parse MarkDown syntax for the MarkDown plugin[](.html)
|
||||
[`slim/slim`](https://packagist.org/packages/slim/slim) | All | Handle routes and middleware for the REST API[](.html)
|
||||
|
||||
### Extensions
|
||||
Extension | Required? | Usage
|
||||
|
|
|
@ -69,6 +69,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
|
@ -87,6 +88,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
|
|
|
@ -69,6 +69,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
|
@ -87,6 +88,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
|
@ -169,6 +171,7 @@ It might be useful if your IP adress often changes.<br />
|
|||
<h3 id="resources">Resources</h3>
|
||||
<p><strong>data_dir</strong>: Data directory.<br />
|
||||
<strong>datastore</strong>: Shaarli's links database file path.<br />
|
||||
<strong>history</strong>: Shaarli's operation history file path.<br />
|
||||
<strong>updates</strong>: File path for the ran updates file.<br />
|
||||
<strong>log</strong>: Log file path.<br />
|
||||
<strong>update_check</strong>: Last update check file path.<br />
|
||||
|
|
|
@ -70,6 +70,7 @@ It might be useful if your IP adress often changes.
|
|||
|
||||
**data_dir**: Data directory.
|
||||
**datastore**: Shaarli's links database file path.
|
||||
**history**: Shaarli's operation history file path.
|
||||
**updates**: File path for the ran updates file.
|
||||
**log**: Log file path.
|
||||
**update_check**: Last update check file path.
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
|
@ -50,6 +51,7 @@
|
|||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
|
@ -50,6 +51,7 @@
|
|||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
|
|
|
@ -69,6 +69,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
|
@ -87,6 +88,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
|
@ -100,42 +102,56 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
</ul>
|
||||
</div>
|
||||
<h1 id="theming">Theming</h1>
|
||||
<h2 id="user-css">User CSS</h2>
|
||||
<h2 id="foreword">Foreword</h2>
|
||||
<p>There are two ways of customizing how Shaarli looks:</p>
|
||||
<ol>
|
||||
<li>by using a custom CSS to override Shaarli's CSS</li>
|
||||
<li>by using a full theme that provides its own RainTPL templates, CSS and Javascript resources</li>
|
||||
</ol>
|
||||
<h2 id="custom-css">Custom CSS</h2>
|
||||
<p>Shaarli's appearance can be modified by adding CSS rules to:</p>
|
||||
<ul>
|
||||
<li>Shaarli's apparence can be modified by editing CSS rules in <code>inc/user.css</code>. This file allows to override rules defined in the main <code>inc/shaarli.css</code> (only add changed rules), or define a whole new theme.</li>
|
||||
<li>Do not edit <code>inc/shaarli.css</code>! Your changes would be overriden when updating Shaarli.</li>
|
||||
<li>Some themes are available at <a href="https://github.com/shaarli/shaarli-themes" class="uri">https://github.com/shaarli/shaarli-themes</a>.</li>
|
||||
<li>Shaarli < <code>v0.9.0</code>: <code>inc/user.css</code></li>
|
||||
<li>Shaarli >= <code>v0.9.0</code>: <code>data/user.css</code></li>
|
||||
</ul>
|
||||
<p>See also:</p>
|
||||
<ul>
|
||||
<li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li>
|
||||
</ul>
|
||||
<h2 id="raintpl-template">RainTPL template</h2>
|
||||
<p>This file allows overriding rules defined in the template CSS files (only add changed rules), or define a whole new theme.</p>
|
||||
<p><strong>Note</strong>: Do not edit <code>tpl/default/css/shaarli.css</code>! Your changes would be overridden when updating Shaarli.</p>
|
||||
<p>See also <a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></p>
|
||||
<h2 id="themes">Themes</h2>
|
||||
<p><em>WARNING - This feature is currently being worked on and will be improved in the next releases. Experimental.</em></p>
|
||||
<p>Installation:</p>
|
||||
<ul>
|
||||
<li>Find the template you'd like to install (see the list of <a href="available-templates%7CTheming#community-themes--templates.html">available templates|Theming#community-themes--templates</a>)</li>
|
||||
<li>Find it's git clone URL or download the zip archive for the template.</li>
|
||||
<li>In your Shaarli <code>tpl/</code> directory, run <code>git clone https://url/of/my-template/</code> or unpack the zip archive.
|
||||
<li>find a theme you'd like to install</li>
|
||||
<li>copy or clone the theme folder under <code>tpl/<a_sweet_theme></code></li>
|
||||
<li>enable the theme:
|
||||
<ul>
|
||||
<li>There should now be a <code>my-template/</code> directory under the <code>tpl/</code> dir, containing directly all the template files.</li>
|
||||
<li>Shaarli < <code>v0.9.0</code>: edit <code>data/config.json.php</code> and set the value of <code>raintpl_tpl</code> to the new theme name:<br />
|
||||
<code>"raintpl_tpl": "tpl\/my-template\/"</code></li>
|
||||
<li>Shaarli >= <code>v0.9.0</code>: select the theme through the <em>Tools</em> page</li>
|
||||
</ul></li>
|
||||
<li><p>Edit <code>data/config.json.php</code> to have Shaarli use this template, in <code>"resource"</code> e.g.</p>
|
||||
<div class="sourceCode"><pre class="sourceCode json"><code class="sourceCode json"><span class="er">"raintpl_tpl":</span> <span class="er">"tpl\/my-template\/",</span></code></pre></div></li>
|
||||
</ul>
|
||||
<h2 id="community-themes-templates">Community themes & templates</h2>
|
||||
<h2 id="community-css-themes">Community CSS & themes</h2>
|
||||
<h3 id="custom-css-1">Custom CSS</h3>
|
||||
<ul>
|
||||
<li><a href="https://github.com/mrjovanovic/serious-theme-shaarli">mrjovanovic/serious-theme-shaarli</a> - A serious theme for Shaarli<a href=".html"></a></li>
|
||||
<li><a href="https://github.com/shaarli/shaarli-themes">shaarli/shaarli-themes</a><a href=".html"></a></li>
|
||||
</ul>
|
||||
<h3 id="themes-1">Themes</h3>
|
||||
<ul>
|
||||
<li><a href="https://github.com/AkibaTech/Shaarli---SuperHero-Theme">AkibaTech/Shaarli Superhero Theme</a> - A template/theme for Shaarli<a href=".html"></a></li>
|
||||
<li><a href="https://github.com/alexisju/albinomouse-template">alexisju/albinomouse-template</a> - A full template for Shaarli<a href=".html"></a></li>
|
||||
<li><a href="https://github.com/ArthurHoaro/shaarli-launch">ArthurHoaro/shaarli-launch</a> - Customizable Shaarli theme.<a href=".html"></a></li>
|
||||
<li><a href="https://github.com/ArthurHoaro/shaarli-launch">ArthurHoaro/shaarli-launch</a> - Customizable Shaarli theme<a href=".html"></a></li>
|
||||
<li><a href="https://github.com/dhoko/ShaarliTemplate">dhoko/ShaarliTemplate</a> - A template/theme for Shaarli<a href=".html"></a></li>
|
||||
<li><a href="https://github.com/kalvn/shaarli-blocks">kalvn/shaarli-blocks</a> - A template/theme for Shaarli<a href=".html"></a></li>
|
||||
<li><a href="https://github.com/kalvn/Shaarli-Material">kalvn/Shaarli-Material</a> - A theme (template) based on Google's Material Design for Shaarli, the superfast delicious clone.<a href=".html"></a></li>
|
||||
<li><a href="https://github.com/ManufacturaInd/shaarli-2004licious-theme">ManufacturaInd/shaarli-2004licious-theme</a> - A template/theme as a humble homage to the early looks of the del.icio.us site.<a href=".html"></a></li>
|
||||
<li><a href="https://github.com/kalvn/Shaarli-Material">kalvn/Shaarli-Material</a> - A theme (template) based on Google's Material Design for Shaarli, the superfast delicious clone<a href=".html"></a></li>
|
||||
<li><a href="https://github.com/ManufacturaInd/shaarli-2004licious-theme">ManufacturaInd/shaarli-2004licious-theme</a> - A template/theme as a humble homage to the early looks of the del.icio.us site<a href=".html"></a></li>
|
||||
</ul>
|
||||
<h3 id="shaarli-forks">Shaarli forks</h3>
|
||||
<ul>
|
||||
<li><a href="https://github.com/misterair/limonade">misterair/Limonade</a> - A fork of (legacy) Shaarli with a new template<a href=".html"></a></li>
|
||||
<li><a href="https://github.com/mrjovanovic/serious-theme-shaarli">mrjovanovic/serious-theme-shaarli</a> - A serious theme for SHaarli.<a href=".html"></a></li>
|
||||
<li><a href="https://github.com/vivienhaese/shaarlitheme">vivienhaese/shaarlitheme</a> - A Shaarli fork meant to be run in an openshift instance<a href=".html"></a></li>
|
||||
</ul>
|
||||
<h3 id="example-installation-albinomouse-template">Example installation: AlbinoMouse template</h3>
|
||||
<h2 id="example-installation-albinomouse-theme">Example installation: AlbinoMouse theme</h2>
|
||||
<p>With the following configuration:</p>
|
||||
<ul>
|
||||
<li>Apache 2 / PHP 5.6</li>
|
||||
|
|
|
@ -1,39 +1,51 @@
|
|||
#Theming
|
||||
## User CSS
|
||||
## Foreword
|
||||
There are two ways of customizing how Shaarli looks:
|
||||
|
||||
- Shaarli's apparence can be modified by editing CSS rules in `inc/user.css`. This file allows to override rules defined in the main `inc/shaarli.css` (only add changed rules), or define a whole new theme.
|
||||
- Do not edit `inc/shaarli.css`! Your changes would be overriden when updating Shaarli.
|
||||
- Some themes are available at https://github.com/shaarli/shaarli-themes.
|
||||
1. by using a custom CSS to override Shaarli's CSS
|
||||
2. by using a full theme that provides its own RainTPL templates, CSS and Javascript resources
|
||||
|
||||
See also:
|
||||
- [Download CSS styles from an OPML list](Download-CSS-styles-from-an-OPML-list.html)
|
||||
## Custom CSS
|
||||
Shaarli's appearance can be modified by adding CSS rules to:
|
||||
- Shaarli < `v0.9.0`: `inc/user.css`
|
||||
- Shaarli >= `v0.9.0`: `data/user.css`
|
||||
|
||||
## RainTPL template
|
||||
This file allows overriding rules defined in the template CSS files (only add changed rules), or define a whole new theme.
|
||||
|
||||
**Note**: Do not edit `tpl/default/css/shaarli.css`! Your changes would be overridden when updating Shaarli.
|
||||
|
||||
See also [Download CSS styles from an OPML list](Download-CSS-styles-from-an-OPML-list.html)
|
||||
|
||||
## Themes
|
||||
_WARNING - This feature is currently being worked on and will be improved in the next releases. Experimental._
|
||||
|
||||
- Find the template you'd like to install (see the list of [available templates|Theming#community-themes--templates](available-templates|Theming#community-themes--templates.html))
|
||||
- Find it's git clone URL or download the zip archive for the template.
|
||||
- In your Shaarli `tpl/` directory, run `git clone https://url/of/my-template/` or unpack the zip archive.
|
||||
- There should now be a `my-template/` directory under the `tpl/` dir, containing directly all the template files.
|
||||
- Edit `data/config.json.php` to have Shaarli use this template, in `"resource"` e.g.
|
||||
```json
|
||||
"raintpl_tpl": "tpl\/my-template\/",
|
||||
```
|
||||
Installation:
|
||||
- find a theme you'd like to install
|
||||
- copy or clone the theme folder under `tpl/<a_sweet_theme>`
|
||||
- enable the theme:
|
||||
- Shaarli < `v0.9.0`: edit `data/config.json.php` and set the value of `raintpl_tpl` to the new theme name:
|
||||
`"raintpl_tpl": "tpl\/my-template\/"`
|
||||
- Shaarli >= `v0.9.0`: select the theme through the _Tools_ page
|
||||
|
||||
## Community themes & templates
|
||||
## Community CSS & themes
|
||||
### Custom CSS
|
||||
- [mrjovanovic/serious-theme-shaarli](https://github.com/mrjovanovic/serious-theme-shaarli) - A serious theme for Shaarli[](.html)
|
||||
- [shaarli/shaarli-themes](https://github.com/shaarli/shaarli-themes)[](.html)
|
||||
|
||||
### Themes
|
||||
- [AkibaTech/Shaarli Superhero Theme](https://github.com/AkibaTech/Shaarli---SuperHero-Theme) - A template/theme for Shaarli[](.html)
|
||||
- [alexisju/albinomouse-template](https://github.com/alexisju/albinomouse-template) - A full template for Shaarli[](.html)
|
||||
- [ArthurHoaro/shaarli-launch](https://github.com/ArthurHoaro/shaarli-launch) - Customizable Shaarli theme.[](.html)
|
||||
- [ArthurHoaro/shaarli-launch](https://github.com/ArthurHoaro/shaarli-launch) - Customizable Shaarli theme[](.html)
|
||||
- [dhoko/ShaarliTemplate](https://github.com/dhoko/ShaarliTemplate) - A template/theme for Shaarli[](.html)
|
||||
- [kalvn/shaarli-blocks](https://github.com/kalvn/shaarli-blocks) - A template/theme for Shaarli[](.html)
|
||||
- [kalvn/Shaarli-Material](https://github.com/kalvn/Shaarli-Material) - A theme (template) based on Google's Material Design for Shaarli, the superfast delicious clone.[](.html)
|
||||
- [ManufacturaInd/shaarli-2004licious-theme](https://github.com/ManufacturaInd/shaarli-2004licious-theme) - A template/theme as a humble homage to the early looks of the del.icio.us site.[](.html)
|
||||
- [kalvn/Shaarli-Material](https://github.com/kalvn/Shaarli-Material) - A theme (template) based on Google's Material Design for Shaarli, the superfast delicious clone[](.html)
|
||||
- [ManufacturaInd/shaarli-2004licious-theme](https://github.com/ManufacturaInd/shaarli-2004licious-theme) - A template/theme as a humble homage to the early looks of the del.icio.us site[](.html)
|
||||
|
||||
### Shaarli forks
|
||||
- [misterair/Limonade](https://github.com/misterair/limonade) - A fork of (legacy) Shaarli with a new template[](.html)
|
||||
- [mrjovanovic/serious-theme-shaarli](https://github.com/mrjovanovic/serious-theme-shaarli) - A serious theme for SHaarli.[](.html)
|
||||
- [vivienhaese/shaarlitheme](https://github.com/vivienhaese/shaarlitheme) - A Shaarli fork meant to be run in an openshift instance[](.html)
|
||||
|
||||
### Example installation: AlbinoMouse template
|
||||
## Example installation: AlbinoMouse theme
|
||||
With the following configuration:
|
||||
- Apache 2 / PHP 5.6
|
||||
- user sites are enabled, e.g. `/home/user/public_html/somedir` is served as `http://localhost/~user/somedir`
|
||||
|
|
|
@ -69,6 +69,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
|
@ -87,6 +88,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
|
@ -172,7 +174,7 @@ Search for <code>failed</code> in this file to look for unauthorized login attem
|
|||
<li>If you have the error <code>Warning: file_get_contents() [function.file-get-contents]: URL file-access is disabled in the server configuration in /…/index.php on line xxx</code>, it means that your host has disabled the ability to fetch a file by HTTP in the php config (Typically in 1and1 hosting). Bad host. Change host. Or comment the following lines:<a href=".html"></a></li>
|
||||
</ul>
|
||||
<div class="sourceCode"><pre class="sourceCode php"><code class="sourceCode php"><span class="co">//list($status,$headers,$data) = getHTTP($url,4); // Short timeout to keep the application responsive.</span>
|
||||
<span class="co">// FIXME: Decode charset according to charset specified in either 1) HTTP response headers or 2) <head> in html </span>
|
||||
<span class="co">// </span><span class="al">FIXME</span><span class="co">: Decode charset according to charset specified in either 1) HTTP response headers or 2) <head> in html </span>
|
||||
<span class="co">//if (strpos($status,'200 OK')) $title=html_extract_title($data);</span></code></pre></div>
|
||||
<ul>
|
||||
<li>On hosts which forbid outgoing HTTP requests (such as free.fr), some thumbnails will not work.</li>
|
||||
|
|
|
@ -69,6 +69,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
|
@ -87,6 +88,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
|
@ -208,5 +210,17 @@ DBTest.php on line 79 and defined
|
|||
<li>a detailed HTML report with metrics for tested code</li>
|
||||
<li>to open it in a web browser: <code>firefox coverage/index.html &</code></li>
|
||||
</ul>
|
||||
<h3 id="executing-specific-tests">Executing specific tests</h3>
|
||||
<p>Add a <a href="https://phpunit.de/manual/current/en/appendixes.annotations.html#appendixes.annotations.group"><code>@group</code></a> annotation in a test class or method comment:<a href=".html"></a></p>
|
||||
<div class="sourceCode"><pre class="sourceCode php"><code class="sourceCode php"><span class="co">/**</span>
|
||||
<span class="co"> * Netscape bookmark import</span>
|
||||
<span class="co"> * </span><span class="an">@group</span><span class="co"> WIP</span>
|
||||
<span class="co"> */</span>
|
||||
<span class="kw">class</span> BookmarkImportTest <span class="kw">extends</span> PHPUnit_Framework_TestCase
|
||||
{
|
||||
<span class="ot">[</span><span class="st">...</span><span class="ot">][](</span>.html<span class="ot">)</span>
|
||||
}</code></pre></div>
|
||||
<p>To run all tests annotated with <code>@group WIP</code>:</p>
|
||||
<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="ex">vendor/bin/phpunit</span> --group WIP tests/</code></pre></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -126,3 +126,22 @@ If Xdebug has been installed and activated, two coverage reports will be generat
|
|||
* a summary in the console
|
||||
* a detailed HTML report with metrics for tested code
|
||||
* to open it in a web browser: `firefox coverage/index.html &`
|
||||
|
||||
### Executing specific tests
|
||||
Add a [`@group`](https://phpunit.de/manual/current/en/appendixes.annotations.html#appendixes.annotations.group) annotation in a test class or method comment:[](.html)
|
||||
|
||||
```php
|
||||
/**
|
||||
* Netscape bookmark import
|
||||
* @group WIP
|
||||
*/
|
||||
class BookmarkImportTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
[...][](.html)
|
||||
}
|
||||
```
|
||||
|
||||
To run all tests annotated with `@group WIP`:
|
||||
```bash
|
||||
$ vendor/bin/phpunit --group WIP tests/
|
||||
```
|
||||
|
|
|
@ -69,6 +69,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
|
@ -87,6 +88,7 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
|
@ -101,12 +103,16 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
</div>
|
||||
<h1 id="upgrade-and-migration">Upgrade and migration</h1>
|
||||
<h2 id="preparation">Preparation</h2>
|
||||
<h3 id="note-your-current-version">Note your current version</h3>
|
||||
<p>If anything goes wrong, it's important for us to know which version you're upgrading from.<br />
|
||||
The current version is present in the <code>version.php</code> file.</p>
|
||||
<h3 id="backup-your-data">Backup your data</h3>
|
||||
<p>Shaarli stores all user data under the <code>data</code> directory:</p>
|
||||
<ul>
|
||||
<li><code>data/config.php</code> - main configuration file</li>
|
||||
<li><code>data/datastore.php</code> - bookmarked links</li>
|
||||
<li><code>data/ipbans.php</code> - banned IP addresses</li>
|
||||
<li><code>data/updates.txt</code> - contains all automatic update to the configuration and datastore files already run</li>
|
||||
</ul>
|
||||
<p>See <a href="Shaarli-configuration.html">Shaarli configuration</a> for more information about Shaarli resources.</p>
|
||||
<p>It is recommended to backup this repository <em>before</em> starting updating/upgrading Shaarli:</p>
|
||||
|
@ -125,15 +131,11 @@ code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Inf
|
|||
</ul></li>
|
||||
<li>check or restore the <code>data</code> directory</li>
|
||||
</ul>
|
||||
<h2 id="upgrading-from-release-archives">Upgrading from release archives</h2>
|
||||
<h2 id="recommended-upgrading-from-release-archives">Recommended : Upgrading from release archives</h2>
|
||||
<p>All tagged revisions can be downloaded as tarballs or ZIP archives from the <a href="https://github.com/shaarli/Shaarli/releases">releases</a> page.<a href=".html"></a></p>
|
||||
<p>We <em>recommend</em> using the releases from the <code>stable</code> branch, which are available as:</p>
|
||||
<ul>
|
||||
<li>gzipped tarball - <a href="https://github.com/shaarli/Shaarli/archive/stable.tar.gz" class="uri">https://github.com/shaarli/Shaarli/archive/stable.tar.gz</a></li>
|
||||
<li>ZIP archive - <a href="https://github.com/shaarli/Shaarli/archive/stable.zip" class="uri">https://github.com/shaarli/Shaarli/archive/stable.zip</a></li>
|
||||
</ul>
|
||||
<p>Once downloaded, extract the archive locally and update your remote installation (e.g. via FTP) -be sure you keep the contents of the <code>data</code> directory!</p>
|
||||
<p>After upgrading, access your fresh Shaarli installation from a web browser; the configuration will then be automatically updated, and new settings added to <code>data/config.php</code> (see <a href="Shaarli-configuration.html">Shaarli configuration</a> for more details).</p>
|
||||
<p>We recommend that you use the latest release tarball with the <code>-full</code> suffix. It contains the dependencies, please read <a href="Download-and-installation.html">Download and installation</a> for <code>git</code> complete instructions.</p>
|
||||
<p>Once downloaded, extract the archive locally and update your remote installation (e.g. via FTP) -be sure you keep the content of the <code>data</code> directory!</p>
|
||||
<p>After upgrading, access your fresh Shaarli installation from a web browser; the configuration and data store will then be automatically updated, and new settings added to <code>data/config.json.php</code> (see <a href="Shaarli-configuration.html">Shaarli configuration</a> for more details).</p>
|
||||
<h2 id="upgrading-with-git">Upgrading with Git</h2>
|
||||
<h3 id="updating-a-community-shaarli">Updating a community Shaarli</h3>
|
||||
<p>If you have installed Shaarli from the <a href="Download#clone-with-git-recommended">community Git repository</a>, simply <a href="https://www.git-scm.com/docs/git-pull">pull new changes</a> from your local clone:<a href=".html"></a></p>
|
||||
|
@ -149,7 +151,7 @@ $ <span class="fu">git</span> pull
|
|||
<span class="ex">tests/Url/UrlTest.php</span> <span class="kw">|</span> <span class="ex">1</span> +
|
||||
<span class="ex">3</span> files changed, 3 insertions(+), <span class="ex">1</span> deletion(-)</code></pre></div>
|
||||
<p>Shaarli >= <code>v0.8.x</code>: install/update third-party PHP dependencies using <a href="https://getcomposer.org/">Composer</a>:<a href=".html"></a></p>
|
||||
<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="ex">composer</span> update --no-dev
|
||||
<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="ex">composer</span> install --no-dev
|
||||
|
||||
<span class="ex">Loading</span> composer repositories with package information
|
||||
<span class="ex">Updating</span> dependencies
|
||||
|
@ -214,7 +216,7 @@ $ <span class="fu">git</span> branch -vv
|
|||
<span class="ex">master</span> 029f75f [sebsauvage/master] Update README.md[](.html)
|
||||
<span class="ex">*</span> stable 890afc3 [origin/stable] Merge pull request <span class="co">#509 from ArthurHoaro/v0.6.5[](.html)</span></code></pre></div>
|
||||
<p>Shaarli >= <code>v0.8.x</code>: install/update third-party PHP dependencies using <a href="https://getcomposer.org/">Composer</a>:<a href=".html"></a></p>
|
||||
<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="ex">composer</span> update --no-dev
|
||||
<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash">$ <span class="ex">composer</span> install --no-dev
|
||||
|
||||
<span class="ex">Loading</span> composer repositories with package information
|
||||
<span class="ex">Updating</span> dependencies
|
||||
|
@ -238,5 +240,20 @@ $ <span class="fu">git</span> gc
|
|||
<span class="ex">Total</span> 3317 (delta 2050), <span class="ex">reused</span> 3301 (delta 2034)<span class="ex">to</span></code></pre></div>
|
||||
<h4 id="step-3-configuration">Step 3: configuration</h4>
|
||||
<p>After migrating, access your fresh Shaarli installation from a web browser; the configuration will then be automatically updated, and new settings added to <code>data/config.php</code> (see <a href="Shaarli-configuration.html">Shaarli configuration</a> for more details).</p>
|
||||
<h2 id="troubleshooting">Troubleshooting</h2>
|
||||
<p>If the solutions provided here doesn't work, please open an issue specifying which version you're upgrading from and to.</p>
|
||||
<h3 id="you-must-specify-an-integer-as-a-key">You must specify an integer as a key</h3>
|
||||
<p>In <code>v0.8.1</code> we changed how link keys are handled (from timestamps to incremental integers).<br />
|
||||
Take a look at <code>data/updates.txt</code> content.</p>
|
||||
<h4 id="updates.txt-contains-updatemethoddatastoreids"><code>updates.txt</code> contains <code>updateMethodDatastoreIds</code></h4>
|
||||
<p>Try to delete it and refresh your page while being logged in.</p>
|
||||
<h4 id="updates.txt-doesnt-exists-or-doesnt-contain-updatemethoddatastoreids"><code>updates.txt</code> doesn't exists or doesn't contain <code>updateMethodDatastoreIds</code></h4>
|
||||
<ol>
|
||||
<li>Create <code>data/updates.txt</code> if it doesn't exist.</li>
|
||||
<li>Paste this string in the update file <code>;updateMethodRenameDashTags;</code></li>
|
||||
<li>Login to Shaarli.</li>
|
||||
<li>Delete the update file.</li>
|
||||
<li>Refresh.</li>
|
||||
</ol>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
#Upgrade and migration
|
||||
## Preparation
|
||||
### Note your current version
|
||||
|
||||
If anything goes wrong, it's important for us to know which version you're upgrading from.
|
||||
The current version is present in the `version.php` file.
|
||||
|
||||
### Backup your data
|
||||
|
||||
Shaarli stores all user data under the `data` directory:
|
||||
- `data/config.php` - main configuration file
|
||||
- `data/datastore.php` - bookmarked links
|
||||
- `data/ipbans.php` - banned IP addresses
|
||||
- `data/updates.txt` - contains all automatic update to the configuration and datastore files already run
|
||||
|
||||
See [Shaarli configuration](Shaarli-configuration.html) for more information about Shaarli resources.
|
||||
|
||||
|
@ -22,16 +28,14 @@ As all user data is kept under `data`, this is the only directory you need to wo
|
|||
- update - see the following sections
|
||||
- check or restore the `data` directory
|
||||
|
||||
## Upgrading from release archives
|
||||
## Recommended : Upgrading from release archives
|
||||
All tagged revisions can be downloaded as tarballs or ZIP archives from the [releases](https://github.com/shaarli/Shaarli/releases) page.[](.html)
|
||||
|
||||
We _recommend_ using the releases from the `stable` branch, which are available as:
|
||||
- gzipped tarball - https://github.com/shaarli/Shaarli/archive/stable.tar.gz
|
||||
- ZIP archive - https://github.com/shaarli/Shaarli/archive/stable.zip
|
||||
We recommend that you use the latest release tarball with the `-full` suffix. It contains the dependencies, please read [Download and installation](Download-and-installation.html) for `git` complete instructions.
|
||||
|
||||
Once downloaded, extract the archive locally and update your remote installation (e.g. via FTP) -be sure you keep the contents of the `data` directory!
|
||||
Once downloaded, extract the archive locally and update your remote installation (e.g. via FTP) -be sure you keep the content of the `data` directory!
|
||||
|
||||
After upgrading, access your fresh Shaarli installation from a web browser; the configuration will then be automatically updated, and new settings added to `data/config.php` (see [Shaarli configuration](Shaarli-configuration.html) for more details).
|
||||
After upgrading, access your fresh Shaarli installation from a web browser; the configuration and data store will then be automatically updated, and new settings added to `data/config.json.php` (see [Shaarli configuration](Shaarli-configuration.html) for more details).
|
||||
|
||||
## Upgrading with Git
|
||||
### Updating a community Shaarli
|
||||
|
@ -54,7 +58,7 @@ Fast-forward
|
|||
Shaarli >= `v0.8.x`: install/update third-party PHP dependencies using [Composer](https://getcomposer.org/):[](.html)
|
||||
|
||||
```bash
|
||||
$ composer update --no-dev
|
||||
$ composer install --no-dev
|
||||
|
||||
Loading composer repositories with package information
|
||||
Updating dependencies
|
||||
|
@ -129,7 +133,7 @@ $ git branch -vv
|
|||
Shaarli >= `v0.8.x`: install/update third-party PHP dependencies using [Composer](https://getcomposer.org/):[](.html)
|
||||
|
||||
```bash
|
||||
$ composer update --no-dev
|
||||
$ composer install --no-dev
|
||||
|
||||
Loading composer repositories with package information
|
||||
Updating dependencies
|
||||
|
@ -159,3 +163,24 @@ Total 3317 (delta 2050), reused 3301 (delta 2034)to
|
|||
|
||||
#### Step 3: configuration
|
||||
After migrating, access your fresh Shaarli installation from a web browser; the configuration will then be automatically updated, and new settings added to `data/config.php` (see [Shaarli configuration](Shaarli-configuration.html) for more details).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If the solutions provided here doesn't work, please open an issue specifying which version you're upgrading from and to.
|
||||
|
||||
### You must specify an integer as a key
|
||||
|
||||
In `v0.8.1` we changed how link keys are handled (from timestamps to incremental integers).
|
||||
Take a look at `data/updates.txt` content.
|
||||
|
||||
#### `updates.txt` contains `updateMethodDatastoreIds`
|
||||
|
||||
Try to delete it and refresh your page while being logged in.
|
||||
|
||||
#### `updates.txt` doesn't exists or doesn't contain `updateMethodDatastoreIds`
|
||||
|
||||
1. Create `data/updates.txt` if it doesn't exist.
|
||||
2. Paste this string in the update file `;updateMethodRenameDashTags;`
|
||||
3. Login to Shaarli.
|
||||
4. Delete the update file.
|
||||
5. Refresh.
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
|
@ -50,6 +51,7 @@
|
|||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
|
|
156
doc/Versioning-and-Branches.html
Normal file
156
doc/Versioning-and-Branches.html
Normal file
|
@ -0,0 +1,156 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="generator" content="pandoc">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
|
||||
<title>Shaarli – Versioning and Branches</title>
|
||||
<style type="text/css">code{white-space: pre;}</style>
|
||||
<style type="text/css">
|
||||
div.sourceCode { overflow-x: auto; }
|
||||
table.sourceCode, tr.sourceCode, td.lineNumbers, td.sourceCode {
|
||||
margin: 0; padding: 0; vertical-align: baseline; border: none; }
|
||||
table.sourceCode { width: 100%; line-height: 100%; }
|
||||
td.lineNumbers { text-align: right; padding-right: 4px; padding-left: 4px; color: #aaaaaa; border-right: 1px solid #aaaaaa; }
|
||||
td.sourceCode { padding-left: 5px; }
|
||||
code > span.kw { color: #007020; font-weight: bold; } /* Keyword */
|
||||
code > span.dt { color: #902000; } /* DataType */
|
||||
code > span.dv { color: #40a070; } /* DecVal */
|
||||
code > span.bn { color: #40a070; } /* BaseN */
|
||||
code > span.fl { color: #40a070; } /* Float */
|
||||
code > span.ch { color: #4070a0; } /* Char */
|
||||
code > span.st { color: #4070a0; } /* String */
|
||||
code > span.co { color: #60a0b0; font-style: italic; } /* Comment */
|
||||
code > span.ot { color: #007020; } /* Other */
|
||||
code > span.al { color: #ff0000; font-weight: bold; } /* Alert */
|
||||
code > span.fu { color: #06287e; } /* Function */
|
||||
code > span.er { color: #ff0000; font-weight: bold; } /* Error */
|
||||
code > span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
|
||||
code > span.cn { color: #880000; } /* Constant */
|
||||
code > span.sc { color: #4070a0; } /* SpecialChar */
|
||||
code > span.vs { color: #4070a0; } /* VerbatimString */
|
||||
code > span.ss { color: #bb6688; } /* SpecialString */
|
||||
code > span.im { } /* Import */
|
||||
code > span.va { color: #19177c; } /* Variable */
|
||||
code > span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
|
||||
code > span.op { color: #666666; } /* Operator */
|
||||
code > span.bu { } /* BuiltIn */
|
||||
code > span.ex { } /* Extension */
|
||||
code > span.pp { color: #bc7a00; } /* Preprocessor */
|
||||
code > span.at { color: #7d9029; } /* Attribute */
|
||||
code > span.do { color: #ba2121; font-style: italic; } /* Documentation */
|
||||
code > span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
|
||||
code > span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
|
||||
code > span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
|
||||
</style>
|
||||
<link rel="stylesheet" href="github-markdown.css">
|
||||
<!--[if lt IE 9]>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
|
||||
<![endif]-->
|
||||
</head>
|
||||
<body>
|
||||
<div id="local-sidebar">
|
||||
<ul>
|
||||
<li><a href="Home.html">Home</a></li>
|
||||
<li>Setup
|
||||
<ul>
|
||||
<li><a href="Download-and-Installation.html">Download and Installation</a></li>
|
||||
<li><a href="Upgrade-and-migration.html">Upgrade and migration</a></li>
|
||||
<li><a href="Server-requirements.html">Server requirements</a></li>
|
||||
<li><a href="Server-configuration.html">Server configuration</a></li>
|
||||
<li><a href="Server-security.html">Server security</a></li>
|
||||
<li><a href="Shaarli-configuration.html">Shaarli configuration</a></li>
|
||||
<li><a href="Plugins.html">Plugins</a></li>
|
||||
</ul></li>
|
||||
<li><a href="Docker.html">Docker</a></li>
|
||||
<li><a href="Usage.html">Usage</a>
|
||||
<ul>
|
||||
<li><a href="Sharing-button.html">Sharing button</a> (bookmarklet)</li>
|
||||
<li><a href="Browsing-and-Searching.html">Browsing and Searching</a></li>
|
||||
<li><a href="Firefox-share.html">Firefox share</a></li>
|
||||
<li><a href="RSS-feeds.html">RSS feeds</a></li>
|
||||
<li><a href="REST-API.html">REST API</a></li>
|
||||
</ul></li>
|
||||
<li>How To
|
||||
<ul>
|
||||
<li><a href="Backup,-restore,-import-and-export.html">Backup, restore, import and export</a></li>
|
||||
<li><a href="Copy-an-existing-installation-over-SSH-and-serve-it-locally.html">Copy an existing installation over SSH and serve it locally</a></li>
|
||||
<li><a href="Create-and-serve-multiple-Shaarlis-(farm).html">Create and serve multiple Shaarlis (farm)</a></li>
|
||||
<li><a href="Download-CSS-styles-from-an-OPML-list.html">Download CSS styles from an OPML list</a></li>
|
||||
<li><a href="Datastore-hacks.html">Datastore hacks</a></li>
|
||||
</ul></li>
|
||||
<li><a href="Troubleshooting.html">Troubleshooting</a></li>
|
||||
<li><a href="Development.html">Development</a>
|
||||
<ul>
|
||||
<li><a href="GnuPG-signature.html">GnuPG signature</a></li>
|
||||
<li><a href="Coding-guidelines.html">Coding guidelines</a></li>
|
||||
<li><a href="Directory-structure.html">Directory structure</a></li>
|
||||
<li><a href="3rd-party-libraries.html">3rd party libraries</a></li>
|
||||
<li><a href="Plugin-System.html">Plugin System</a></li>
|
||||
<li><a href="Release-Shaarli.html">Release Shaarli</a></li>
|
||||
<li><a href="Versioning-and-Branches.html">Versioning and Branches</a></li>
|
||||
<li><a href="Security.html">Security</a></li>
|
||||
<li><a href="Static-analysis.html">Static analysis</a></li>
|
||||
<li><a href="Theming.html">Theming</a></li>
|
||||
<li><a href="Unit-tests.html">Unit tests</a></li>
|
||||
</ul></li>
|
||||
<li>About
|
||||
<ul>
|
||||
<li><a href="FAQ.html">FAQ</a></li>
|
||||
<li><a href="Community-&-Related-software.html">Community & Related software</a></li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
</div>
|
||||
<h1 id="versioning-and-branches">Versioning and Branches</h1>
|
||||
<p>[<strong>WORK IN PROGRESS</strong>][](.html)</p>
|
||||
<p>It's important to understand how Shaarli branches work, especially if you're maintaining a 3rd party tools for Shaarli (theme, plugin, etc.), to be sure stay compatible.</p>
|
||||
<h2 id="master-branch"><code>master</code> branch</h2>
|
||||
<p>The <code>master</code> branch is the development branch. Any new change MUST go through this branch using Pull Requests.</p>
|
||||
<p>Remarks:</p>
|
||||
<ul>
|
||||
<li>This branch shouldn't be used for production as it isn't necessary stable.</li>
|
||||
<li>3rd party aren't required to be compatible with the latest changes.</li>
|
||||
<li>Official plugins, themes and libraries (contained within Shaarli organization repos) must be compatible with the master branch.</li>
|
||||
<li>The version in this branch is always <code>dev</code>.</li>
|
||||
</ul>
|
||||
<h2 id="v0.x-branch"><code>v0.x</code> branch</h2>
|
||||
<p>This <code>v0.x</code> branch, points to the latest <code>v0.x.y</code> release.</p>
|
||||
<p>Explanation:</p>
|
||||
<p>When a new version is released, it might contains a major bug which isn't detected right away. For example, a new PHP version is released, containing backward compatibility issue which doesn't work with Shaarli.</p>
|
||||
<p>In this case, the issue is fixed in the <code>master</code> branch, and the fix is backported the to the <code>v0.x</code> branch. Then a new release is made from the <code>v0.x</code> branch.</p>
|
||||
<p>This workflow allow us to fix any major bug detected, without having to release bleeding edge feature too soon.</p>
|
||||
<h2 id="latest-branch"><code>latest</code> branch</h2>
|
||||
<p>This branch point the latest release. It recommended to use it to get the latest tested changes.</p>
|
||||
<h2 id="stable-branch"><code>stable</code> branch</h2>
|
||||
<p>The <code>stable</code> branch doesn't contain any major bug, and is one major digit version behind the latest release.</p>
|
||||
<p>For example, the current latest release is <code>v0.8.3</code>, the stable branch is an alias to the latest <code>v0.7.x</code> release. When the <code>v0.9.0</code> version will be released, the stable will move to the latest <code>v0.8.x</code> release.</p>
|
||||
<p>Remarks:</p>
|
||||
<ul>
|
||||
<li>Shaarli release pace isn't fast, and the stable branch might be a few months behind the latest release.</li>
|
||||
</ul>
|
||||
<h2 id="releases">Releases</h2>
|
||||
<p>Releases are always made from the latest <code>v0.x</code> branch.</p>
|
||||
<p>Note that for every release, we manually generate a tarball which contains all Shaarli dependencies, making Shaarli's installation only one step.</p>
|
||||
<h2 id="advices-on-3rd-party-git-repos-workflow">Advices on 3rd party git repos workflow</h2>
|
||||
<h3 id="versioning">Versioning</h3>
|
||||
<p>Any time a new Shaarli release is published, you should publish a new release of your repo if the changes affected you since the latest release (take a look at the <a href="https://github.com/shaarli/Shaarli/releases">changelog</a> (<em>Draft</em> means not released yet) and the commit log (like <a href="https://github.com/shaarli/Shaarli/commits/master/tpl/default"><code>tpl</code> folder</a> for themes)). You can either:<a href=".html"></a></p>
|
||||
<ul>
|
||||
<li>use the Shaarli version number, with your repo version. For example, if Shaarli <code>v0.8.3</code> is released, publish a <code>v0.8.3-1</code> release, where <code>v0.8.3</code> states Shaarli compatibility and <code>-1</code> is your own version digit for the current Shaarli version.</li>
|
||||
<li>use your own versioning scheme, and state Shaarli compatibility in the release description.</li>
|
||||
</ul>
|
||||
<p>Using this, any user will be able to pick the release matching his own Shaarli version.</p>
|
||||
<h3 id="major-bugfix-backport-releases">Major bugfix backport releases</h3>
|
||||
<p>To be able to support backported fixes, it recommended to use our workflow:</p>
|
||||
<div class="sourceCode"><pre class="sourceCode bash"><code class="sourceCode bash"><span class="co"># In master, fix the major bug</span>
|
||||
<span class="fu">git</span> commit -m <span class="st">"Katastrophe"</span>
|
||||
<span class="fu">git</span> push origin master
|
||||
<span class="co"># Get your commit hash</span>
|
||||
<span class="fu">git</span> log --format=<span class="st">"%H"</span> -n 1
|
||||
<span class="co"># Create a new branch from your latest release, let's say v0.8.2-1 (the tag name)</span>
|
||||
<span class="fu">git</span> checkout -b katastrophe v0.8.2-1
|
||||
<span class="co"># Backport the fix commit to your brand new branch</span>
|
||||
<span class="fu">git</span> cherry-pick <span class="op"><</span>fix commit hash<span class="op">></span>
|
||||
<span class="fu">git</span> push origin katastrophe
|
||||
<span class="co"># Then you just have to make a new release from the `katastrophe` branch tagged `v0.8.3-1`</span></code></pre></div>
|
||||
</body>
|
||||
</html>
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue