From 91a21c272960889afd4eaa431a3d29b7785b6efc Mon Sep 17 00:00:00 2001 From: nodiscc Date: Sat, 16 May 2020 12:54:51 +0200 Subject: [PATCH] **General rewording, proof-reading, deduplication, shortening, reordering, simplification, cleanup/formatting/standardization** - standardize page names, rework documentation structure, update TOC - use same example paths everywhere - level 1 titles on all pages - fix broken links - .md suffix on all page links (works both from readthedocs and github repository views) **Server:** A full and concise installation guide with examples is a frequent request. The documentation should provide such a guide for basic installation needs, while explaining alternative/advanced configuration at the end. Links to reference guides and documentation should be used more frequently to avoid recommending an outdated or excessively complex configuration. - server: move most server-related info to server-configuration.md, cleanup/shorten - server: update list of php dependencies/libraries, link to composer.json - server: installation: support 3 install methods (from release zip, from sources, using docker) - server: installation: use rsync instead of mv as mv results will change depending of taget directory already existing or not - server: add example/basic usage of certbot - server, upgrade, installation: update file permissions setup, use sudo for upgrade operations in webserver document root - server: apache: add comments to configuration, fix and factorize file permissions setup, set cache-control header, deny access to dotfiles, add missing apache config steps, add http->https redirect example - server: nginx: refactor nginx configuration, add comments, DO log access to denied/protected files - server: add links to MDN for x-forwarded-* http headers explanation, cleanup/clarify robots.txt and crawlers section - server: bump file upload size limit to 100MB we have reports of bookmark exports weighing +40MB - i have a 13MB one here - server: simplify phpinfo documentation - server: move backup and restore information to dedicated page - docker: move all docker docs to Docker.md, simplify/ docker setup, add docker-compose.yml example, replace docker-101 with docker cheatsheet - troubleshooting: move all troubleshooting documentation to troubleshooting.md **Usage:** - index: add getting started section on index page - features/usage: move all usage-related documentation to usage.md, add links from the main feature list to corresponding usage docs, clarify/reword features list - shaarli configuration: add note about configuring from web interface **Removed:** - remove obsolete/orphan images - remove obsolete shaarchiver example - remove outdated "decode datastore content" snippet **Development:** - development: move development-related docs (static analysis, CI, unit tests, 3rd party libs, link structure/directory, guidelines, security....) to dev/ directory - development: Merge several pages to development.md - **Breaking change?:** remove mentions of 'stable' branch, switch to new branch/release model (master=latest commit, release=latest tag) - **Breaking change?:** refer to base sharing unit as "Shaare" everywhere (TODO: reflect changes in the code?) doc: update featues list/link to usage.md for details - development: directory structure: add note about required file permissions - .travis-ci.yml: add comments - .htaccess: add comment --- .htaccess | 1 + .travis.yml | 13 +- doc/md/3rd-party-libraries.md | 21 - doc/md/Backup-and-restore.md | 11 + doc/md/Browsing-and-searching.md | 37 -- ...e.md => Community-and-related-software.md} | 38 +- doc/md/Continuous-integration-tools.md | 32 - doc/md/Development-guidelines.md | 13 - doc/md/Directory-structure.md | 54 -- doc/md/Docker.md | 207 +++++++ doc/md/Download-and-Installation.md | 124 ---- doc/md/FAQ.md | 46 -- doc/md/Installation.md | 84 +++ doc/md/Link-structure.md | 18 - doc/md/Plugins.md | 51 +- doc/md/REST-API.md | 159 ++--- doc/md/RSS-feeds.md | 28 - doc/md/Release-Shaarli.md | 161 ----- doc/md/Reverse-proxy.md | 116 ++++ doc/md/Security.md | 25 - doc/md/Server-configuration.md | 582 +++++++++--------- doc/md/Server-security.md | 76 --- doc/md/Shaarli-configuration.md | 213 +++---- doc/md/Sharing-content.md | 71 --- doc/md/Static-analysis.md | 13 - doc/md/Troubleshooting.md | 113 +++- doc/md/Unit-tests.md | 119 ---- doc/md/Upgrade-and-migration.md | 154 ++--- doc/md/Usage.md | 109 ++++ doc/md/dev/Development.md | 179 ++++++ doc/md/{ => dev}/GnuPG-signature.md | 20 +- .../Plugin-system.md} | 72 ++- doc/md/dev/Release-Shaarli.md | 145 +++++ doc/md/{ => dev}/Theming.md | 2 + doc/md/{ => dev}/Translations.md | 71 +-- doc/md/dev/Unit-tests.md | 138 +++++ .../Versioning.md} | 28 +- doc/md/{ => dev}/images/poedit-1.jpg | Bin doc/md/docker/docker-101.md | 140 ----- doc/md/docker/resources.md | 19 - doc/md/docker/reverse-proxy-configuration.md | 123 ---- doc/md/docker/shaarli-images.md | 118 ---- doc/md/guides/backup-restore-import-export.md | 64 -- .../images/01-create-droplet-distro.jpg | Bin 20909 -> 0 bytes .../images/02-create-droplet-region.jpg | Bin 21603 -> 0 bytes .../guides/images/03-create-droplet-size.jpg | Bin 20860 -> 0 bytes doc/md/guides/images/04-finalize.jpg | Bin 28233 -> 0 bytes doc/md/guides/images/05-droplet.jpg | Bin 11977 -> 0 bytes doc/md/guides/images/06-domain.jpg | Bin 4499 -> 0 bytes ...install-shaarli-with-debian9-and-docker.md | 257 -------- doc/md/guides/various-hacks.md | 24 - .../{guides => }/images/07-installation.jpg | Bin doc/md/images/bookmarklet.png | Bin 53346 -> 0 bytes doc/md/images/firefoxshare.png | Bin 715 -> 0 bytes doc/md/images/install-shaarli.png | Bin 33827 -> 0 bytes doc/md/index.md | 127 ++-- mkdocs.yml | 46 +- plugins/playvideos/README.md | 9 +- 58 files changed, 1821 insertions(+), 2450 deletions(-) delete mode 100644 doc/md/3rd-party-libraries.md create mode 100644 doc/md/Backup-and-restore.md delete mode 100644 doc/md/Browsing-and-searching.md rename doc/md/{Community-&-Related-software.md => Community-and-related-software.md} (93%) delete mode 100644 doc/md/Continuous-integration-tools.md delete mode 100644 doc/md/Development-guidelines.md delete mode 100644 doc/md/Directory-structure.md create mode 100644 doc/md/Docker.md delete mode 100644 doc/md/Download-and-Installation.md delete mode 100644 doc/md/FAQ.md create mode 100644 doc/md/Installation.md delete mode 100644 doc/md/Link-structure.md delete mode 100644 doc/md/RSS-feeds.md delete mode 100644 doc/md/Release-Shaarli.md create mode 100644 doc/md/Reverse-proxy.md delete mode 100644 doc/md/Security.md delete mode 100644 doc/md/Server-security.md delete mode 100644 doc/md/Sharing-content.md delete mode 100644 doc/md/Static-analysis.md delete mode 100644 doc/md/Unit-tests.md create mode 100644 doc/md/Usage.md create mode 100644 doc/md/dev/Development.md rename doc/md/{ => dev}/GnuPG-signature.md (66%) rename doc/md/{Plugin-System.md => dev/Plugin-system.md} (96%) create mode 100644 doc/md/dev/Release-Shaarli.md rename doc/md/{ => dev}/Theming.md (99%) rename doc/md/{ => dev}/Translations.md (66%) create mode 100644 doc/md/dev/Unit-tests.md rename doc/md/{Versioning-and-Branches.md => dev/Versioning.md} (58%) rename doc/md/{ => dev}/images/poedit-1.jpg (100%) delete mode 100644 doc/md/docker/docker-101.md delete mode 100644 doc/md/docker/resources.md delete mode 100644 doc/md/docker/reverse-proxy-configuration.md delete mode 100644 doc/md/docker/shaarli-images.md delete mode 100644 doc/md/guides/backup-restore-import-export.md delete mode 100644 doc/md/guides/images/01-create-droplet-distro.jpg delete mode 100644 doc/md/guides/images/02-create-droplet-region.jpg delete mode 100644 doc/md/guides/images/03-create-droplet-size.jpg delete mode 100644 doc/md/guides/images/04-finalize.jpg delete mode 100644 doc/md/guides/images/05-droplet.jpg delete mode 100644 doc/md/guides/images/06-domain.jpg delete mode 100644 doc/md/guides/install-shaarli-with-debian9-and-docker.md delete mode 100644 doc/md/guides/various-hacks.md rename doc/md/{guides => }/images/07-installation.jpg (100%) delete mode 100644 doc/md/images/bookmarklet.png delete mode 100644 doc/md/images/firefoxshare.png delete mode 100644 doc/md/images/install-shaarli.png diff --git a/.htaccess b/.htaccess index 4c004271..8876e346 100644 --- a/.htaccess +++ b/.htaccess @@ -7,6 +7,7 @@ RewriteEngine On RewriteRule ^(.git|doxygen|vendor) - [F] # Forward the "Authorization" HTTP header +# fixes JWT token not correctly forwarded on some Apache/FastCGI setups RewriteCond %{HTTP:Authorization} ^(.*) RewriteRule .* - [e=HTTP_AUTHORIZATION:%1] diff --git a/.travis.yml b/.travis.yml index d04a45d1..c414967b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,7 @@ dist: bionic matrix: include: + # jobs for each supported php version - language: php php: 7.4 - language: php @@ -10,23 +11,22 @@ matrix: php: 7.2 - language: php php: 7.1 + # jobs for frontend builds - language: node_js node_js: 8 cache: yarn: true directories: - $HOME/.cache/yarn - install: - yarn install - before_script: - PATH=${PATH//:\.\/node_modules\/\.bin/} - script: - - yarn run build # Just to be sure that the build isn't broken - - make eslint - - make sasslint + - yarn run build # verify successful frontend builds + - make eslint # javascript static analysis + - make sasslint # linter for SASS syntax + # jobs for documentation builds - language: python python: 3.6 cache: @@ -42,6 +42,7 @@ cache: - $HOME/.composer/cache install: + # install/update composer and php dependencies - composer install --prefer-dist before_script: diff --git a/doc/md/3rd-party-libraries.md b/doc/md/3rd-party-libraries.md deleted file mode 100644 index 7e7dd334..00000000 --- a/doc/md/3rd-party-libraries.md +++ /dev/null @@ -1,21 +0,0 @@ -## CSS - -- Yahoo UI [CSS Reset](http://yuilibrary.com/yui/docs/cssreset/) - standardize cross-browser rendering - -## Javascript - -- [Awesomeplete](https://leaverou.github.io/awesomplete/) ([GitHub](https://github.com/LeaVerou/awesomplete)) - autocompletion in input forms -- [bLazy](http://dinbror.dk/blazy/) ([GitHub](https://github.com/dinbror/blazy)) - lazy loading for thumbnails -- [qr.js](http://neocotic.com/qr.js/) ([GitHub](https://github.com/neocotic/qr.js)) - QR code generation - -## PHP - -- [RainTPL](https://github.com/rainphp/raintpl) - HTML templating for PHP - -### Composer - -Library | Usage ----|--- -[`shaarli/netscape-bookmark-parser`](https://packagist.org/packages/shaarli/netscape-bookmark-parser) | Import bookmarks from Netscape files -[`erusev/parsedown`](https://packagist.org/packages/erusev/parsedown) | Parse MarkDown syntax for the MarkDown plugin -[`slim/slim`](https://packagist.org/packages/slim/slim) | Handle routes and middleware for the REST API diff --git a/doc/md/Backup-and-restore.md b/doc/md/Backup-and-restore.md new file mode 100644 index 00000000..e7e2775c --- /dev/null +++ b/doc/md/Backup-and-restore.md @@ -0,0 +1,11 @@ +## Backup and restore + +All data and [configuration](Shaarli-configuration.md) is kept in the `data` directory. Backup this directory: + +```bash +rsync -avzP my.server.com:/var/www/shaarli.mydomain.org/data ~/backups/shaarli-data-$(date +%Y-%m-%d_%H%M) +``` + +It is strongly recommended to do periodic, automatic backups to a seperate machine. You can automate the command above using a cron job or full-featured backup solutions such as [rsnapshot](https://rsnapshot.org/) + +To restore a backup, simply put back the `data/` directory in place, owerwriting any existing files. \ No newline at end of file diff --git a/doc/md/Browsing-and-searching.md b/doc/md/Browsing-and-searching.md deleted file mode 100644 index 16c69855..00000000 --- a/doc/md/Browsing-and-searching.md +++ /dev/null @@ -1,37 +0,0 @@ -## Plain text search - -Use the `Search text` field to search in _any_ of the fields of all links (Title, URL, Description...) - -**Exclude text/tags:** Use the `-` operator before a word or tag (example `-uninteresting`) to prevent entries containing (or tagged) `uninteresting` from showing up in the search results. - -**Exact text search:** Use double-quotes (example `"exact search"`) to search for the exact expression. - -Both exclude patterns and exact searches can be combined with normal searches (example `"exact search" term otherterm -notthis "very exact" stuff -notagain`) - -## Tags search - -Use the `Filter by tags` field to restrict displayed links to entries tagged with one or multiple tags (use space to separate tags). - -**Hidden tags:** Tags starting with a dot `.` (example `.secret`) are private. They can only be seen and searched when logged in. - -### Tag cloud - -The `Tag cloud` page diplays a "cloud" view of all tags in your Shaarli. - - * The most frequently used tags are displayed with a bigger font size. - * When sorting by `Most used` or `Alphabetical`, tags are displayed as a _list_, along with counters and edit/delete buttons for each tag. - * Clicking on any tag will display a list of all Shaares matching this tag. - * Clicking on the counter next to a tag `example`, will filter the tag cloud to only display tags found in Shaares tagged `example`. Repeat this any number of times to further filter the tag cloud. Click `List all links with those tags` to display Shaares matching your current tag filter. - -## 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). - -## Filter buttons - -Filter buttons can be found at the top left of the link list. They allow you to apply different filters to the list: - - * **Private links:** When this toggle button is enabled, only shaares set to `private` will be shown. - * **Untagged links:** When the this toggle button is enabled (top left of the link list), only shaares _without any tags_ will be shown in the link list. - -Filter buttons are only available when logged in. diff --git a/doc/md/Community-&-Related-software.md b/doc/md/Community-and-related-software.md similarity index 93% rename from doc/md/Community-&-Related-software.md rename to doc/md/Community-and-related-software.md index 54f18c8e..eac9d074 100644 --- a/doc/md/Community-&-Related-software.md +++ b/doc/md/Community-and-related-software.md @@ -1,54 +1,68 @@ +# Community & related software + _Unofficial but related work on Shaarli. If you maintain one of these, please get in touch with us to help us find a way to adapt your work to our fork._ -## Related software +## Related software ### REST API clients See [REST API](REST-API) for a list of official and community clients. ### Third party plugins -- [autosave](https://github.com/kalvn/shaarli-plugin-autosave) by [@kalvn](https://github.com/kalvn): Automatically saves data when editing a link to avoid any loss in case of crash or unexpected shutdown. + +- [autosave](https://github.com/kalvn/shaarli-plugin-autosave) by [@kalvn](https://github.com/kalvn): Automatically saves data when editing a Shaare to avoid any loss in case of crash or unexpected shutdown. - [Code Coloration](https://github.com/ArthurHoaro/code-coloration) by [@ArthurHoaro](https://github.com/ArthurHoaro): client side code syntax highlighter. - [Disqus](https://github.com/kalvn/shaarli-plugin-disqus) by [@kalvn](https://github.com/kalvn): Adds Disqus comment system to your Shaarli. - [google analytics](https://github.com/ericjuden/Shaarli-Google-Analytics-Plugin) by [@ericjuden](http://github.com/ericjuden): Adds Google Analytics tracking support - [launch](https://github.com/ArthurHoaro/launch-plugin) - Launch Plugin is a plugin designed to enhance and customize Launch Theme for Shaarli. -- [markdown-toolbar](https://github.com/immanuelfodor/shaarli-markdown-toolbar) by [@immanuelfodor](https://github.com/immanuelfodor) - Easily insert markdown syntax into the Description field when editing a link. -- [related](https://github.com/ilesinge/shaarli-related) by [@ilesinge](https://github.com/ilesinge) - Show related links based on the number of identical tags. +- [markdown-toolbar](https://github.com/immanuelfodor/shaarli-markdown-toolbar) by [@immanuelfodor](https://github.com/immanuelfodor) - Easily insert markdown syntax into the Description field when editing a Shaare. +- [related](https://github.com/ilesinge/shaarli-related) by [@ilesinge](https://github.com/ilesinge) - Show related Shaares based on the number of identical tags. - [social](https://github.com/alexisju/social) by [@alexisju](https://github.com/alexisju): share links to social networks. -- [shaarli2twitter](https://github.com/ArthurHoaro/shaarli2twitter) by [@ArthurHoaro](https://github.com/ArthurHoaro) - Automatically tweet your shared links from Shaarli +- [shaarli2twitter](https://github.com/ArthurHoaro/shaarli2twitter) by [@ArthurHoaro](https://github.com/ArthurHoaro) - Automatically tweet your Shaares from Shaarli - [shaarli2mastodon](https://github.com/kalvn/shaarli2mastodon) by [@kalvn](https://github.com/kalvn) - This Shaarli plugin allows you to automatically publish links you post on your Mastodon timeline. -- [shaarli-descriptor](https://github.com/immanuelfodor/shaarli-descriptor) by [@immanuelfodor](https://github.com/immanuelfodor) - Customize the default height/number of rows of the Description field when editing a link. +- [shaarli-descriptor](https://github.com/immanuelfodor/shaarli-descriptor) by [@immanuelfodor](https://github.com/immanuelfodor) - Customize the default height/number of rows of the Description field when editing a Shaare. - [urlextern](https://github.com/trailjeep/shaarli-urlextern) by [@trailjeep](https://github.com/trailjeep) - Shaarli plugin to open external links in a new tab/window. -- [favicons](https://github.com/trailjeep/shaarli-favicons) by [@trailjeep](https://github.com/trailjeep) - Shaarli plugin to add favicon/filetype icons to links. +- [favicons](https://github.com/trailjeep/shaarli-favicons) by [@trailjeep](https://github.com/trailjeep) - Shaarli plugin to add favicon/filetype icons to Shaares. + ### Third-party themes + See [Theming](Theming) for a list of community-contributed themes, and an installation guide. ### Integration with other platforms + - [tt-rss-shaarli](https://github.com/jcsaaddupuy/tt-rss-shaarli) - [Tiny-Tiny RSS](http://tt-rss.org/) plugin that adds support for sharing articles with Shaarli -- [octopress-shaarli](https://github.com/ahmet2mir/octopress-shaarli) - Octopress plugin to retrieve Shaarli links on the sidebar +- [octopress-shaarli](https://github.com/ahmet2mir/octopress-shaarli) - Octopress plugin to retrieve Shaarli Shaares on the sidebar - [Scuttle to Shaarli](https://github.com/q2apro/scuttle-to-shaarli) - Import bookmarks from Scuttle - [Shaarli app for Cloudron](https://git.cloudron.io/cloudron/shaarli-app) - Effortlessly run Shaarli with the help of [Cloudron](https://cloudron.io/) [![Install](https://cloudron.io/img/button.svg)](https://cloudron.io/button.html?app=com.github.shaarli) - [Shaarli_ynh](https://github.com/YunoHost-Apps/shaarli_ynh) - Shaarli is available as a [Yunohost](https://yunohost.org) app [![Install Shaarli with YunoHost](https://install-app.yunohost.org/install-with-yunohost.png)](https://install-app.yunohost.org/?app=shaarli) - [pelican](https://blog.getpelican.com) static blog generator plugin to auto-post articles on a Shaarli instance: [shaarli_poster](https://github.com/getpelican/pelican-plugins/tree/master/shaarli_poster) + ### Mobile Apps + - [ShaarliOS](https://github.com/mro/ShaarliOS) - Apple iOS share extension. - [Shaarli for Android](http://sebsauvage.net/links/?ZAyDzg) - Android application that adds Shaarli as a sharing provider -- [Shaarlier for Android](https://github.com/dimtion/Shaarlier) - Android application to simply add links directly into your Shaarli +- [Shaarlier for Android](https://github.com/dimtion/Shaarlier) - Android application to simply add Shaares directly into your Shaarli - [Stakali for Android](https://stakali.toneiv.eu) - Stakali is a personal bookmark manager which synchronizes with Shaarli + ### Desktop Apps + - [Ulauncher Extension](https://github.com/sebw/ulauncher-shaarli) - Ulauncher is an an application launcher for Linux, this extension allows research in your Shaarli + ### Browser addons + - [Shaarli Firefox Extension](https://github.com/ikipatang/shaarli-web-extension) - toolbar button to share your current tab with Shaarli. - [Shaarli Chrome Extension](https://github.com/octplane/Shiny-Shaarli) - toolbar button to share your current tab with Shaarli. + ### Server apps + - [shaarchiver](https://github.com/nodiscc/shaarchiver) - Archive your Shaarli bookmarks and their content - [shaarli-river](https://github.com/mknexen/shaarli-river) - An aggregator for shaarlis with many features - [Shaarlo](https://github.com/DMeloni/shaarlo) - An aggregator for shaarlis with many features (a very popular running instance among French shaarliers: [shaarli.fr](http://shaarli.fr/)) @@ -57,10 +71,14 @@ See [Theming](Theming) for a list of community-contributed themes, and an instal - [Self dead link](https://framagit.org/qwertygc/shaarli-dev-code/blob/master/self-dead-link.php) - Detect dead links on shaarli. This version use the database of shaarli. [Another version](https://framagit.org/qwertygc/shaarli-dev-code/blob/master/dead-link.php), can be used for other shaarli instances (but is more resource consuming). - [Bookmark Archiver](https://github.com/pirate/bookmark-archiver) - Save an archived copy of all websites starred using browser bookmarks/Shaarli/Delicious/Instapaper/Unmark.it/Pocket/Pinboard. Outputs browseable html. + ## Alternatives to Shaarli + See [awesome-selfhosted: bookmarks & link sharing](https://github.com/Kickball/awesome-selfhosted/#bookmarks--link-sharing). + ## Community + - [Liens en vrac de sebsauvage](http://sebsauvage.net/links/) - the original Shaarli - [A large list of Shaarlis](http://porneia.free.fr/pub/links/ou-est-shaarli.html) - [A list of working Shaarli aggregators](https://raw.githubusercontent.com/Oros42/find_shaarlis/master/annuaires.json) @@ -71,7 +89,9 @@ See [awesome-selfhosted: bookmarks & link sharing](https://github.com/Kickball/a - [Original revisions history](http://sebsauvage.net/wiki/doku.php?id=php:shaarli:history) - [Shaarli.fr/my](https://www.shaarli.fr/my.php) - Unofficial, unsupported (old fork) hosted Shaarlis provider, courtesy of [DMeloni](https://github.com/DMeloni) + ### 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/) - 2015-06-22 - Hacker News - https://news.ycombinator.com/item?id=9755366 diff --git a/doc/md/Continuous-integration-tools.md b/doc/md/Continuous-integration-tools.md deleted file mode 100644 index f7819d5a..00000000 --- a/doc/md/Continuous-integration-tools.md +++ /dev/null @@ -1,32 +0,0 @@ -## Local development -A [`Makefile`](https://github.com/shaarli/Shaarli/blob/master/Makefile) is available to perform project-related operations: - -- Documentation - generate a local HTML copy of the GitHub wiki -- [Static analysis](Static-analysis) - check that the code is compliant to PHP conventions -- [Unit tests](Unit-tests) - ensure there are no regressions introduced by new commits - -## Automatic builds -[Travis CI](http://docs.travis-ci.com/) is a Continuous Integration build server, that runs a build: - -- each time a commit is merged to the mainline (`master` branch) -- each time a Pull Request is submitted or updated - -A build is composed of several jobs: one for each supported PHP version (see [Server requirements](Server requirements)). - -Each build job: - -- updates Composer -- installs 3rd-party test dependencies with Composer -- runs [Unit tests](Unit-tests) -- runs ESLint check - -After all jobs have finished, Travis returns the results to GitHub: - -- a status icon represents the result for the `master` branch: [![](https://api.travis-ci.org/shaarli/Shaarli.svg)](https://travis-ci.org/shaarli/Shaarli) -- Pull Requests are updated with the Travis result - - Green: all tests have passed - - Red: some tests failed - - Orange: tests are pending - -## Documentation -[mkdocs](https://www.mkdocs.org/) is used to convert markdown documentation to HTML pages. The [public documentation](https://shaarli.readthedocs.io/en/master/) website is rendered and hosted by [readthedocs.org](https://readthedocs.org/). A copy of the documentation is also included in prebuilt [release archives](https://github.com/shaarli/Shaarli/releases) (`doc/html/` path in your Shaarli installation). To generate the HTML documentation locally, install a recent version of Python `setuptools` and run `make doc`. diff --git a/doc/md/Development-guidelines.md b/doc/md/Development-guidelines.md deleted file mode 100644 index 46b7c6f8..00000000 --- a/doc/md/Development-guidelines.md +++ /dev/null @@ -1,13 +0,0 @@ -## Development guidelines - -Please have a look at the following pages: - -- [Contributing to Shaarli](https://github.com/shaarli/Shaarli/tree/master/CONTRIBUTING.md) -- [Static analysis](Static-analysis) - patches should try to stick to the -[PHP Standard Recommendations](http://www.php-fig.org/psr/) (PSR), especially: - - [PSR-1](http://www.php-fig.org/psr/psr-1/) - Basic Coding Standard - - [PSR-2](http://www.php-fig.org/psr/psr-2/) - Coding Style Guide -- [Unit tests](Unit-tests) -- Javascript linting - Shaarli uses [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript). -Run `make eslint` to check JS style. -- [GnuPG signature](GnuPG-signature) for tags/releases diff --git a/doc/md/Directory-structure.md b/doc/md/Directory-structure.md deleted file mode 100644 index c0b49393..00000000 --- a/doc/md/Directory-structure.md +++ /dev/null @@ -1,54 +0,0 @@ -## Directory structure - -Here is the directory structure of Shaarli and the purpose of the different files: - -```bash - index.php # Main program - application/ # Shaarli classes - ├── LinkDB.php - - ... - - └── Utils.php - tests/ # Shaarli unitary & functional tests - ├── LinkDBTest.php - - ... - - ├── utils # utilities to ease testing - │ └── ReferenceLinkDB.php - └── UtilsTest.php - assets/ - ├── common/ # Assets shared by multiple themes - ├── ... - ├── default/ # Assets for the default template, before compilation - ├── fonts/ # Font files - ├── img/ # Images used by the default theme - ├── js/ # JavaScript files in ES6 syntax - ├── scss/ # SASS files - └── vintage/ # Assets for the vintage template, before compilation - └── ... - COPYING # Shaarli license - inc/ # static assets and 3rd party libraries - └── rain.tpl.class.php # RainTPL templating library - images/ # Images and icons used in Shaarli - data/ # data storage: bookmark database, configuration, logs, banlist... - ├── config.json.php # Shaarli configuration (login, password, timezone, title...) - ├── datastore.php # Your link database (compressed). - ├── ipban.php # IP address ban system data - ├── lastupdatecheck.txt # Update check timestamp file - └── log.txt # login/IPban log. - tpl/ # RainTPL templates for Shaarli. They are used to build the pages. - ├── default/ # Default Shaarli theme - ├── fonts/ # Font files - ├── img/ # Images - ├── js/ # JavaScript files compiled by Babel and compatible with all browsers - ├── css/ # CSS files compiled with SASS - └── vintage/ # Legacy Shaarli theme - └── ... - cache/ # thumbnails cache - # This directory is automatically created. You can erase it anytime you want. - tmp/ # Temporary directory for compiled RainTPL templates. - # This directory is automatically created. You can erase it anytime you want. - vendor/ # Third-party dependencies. This directory is created by Composer -``` diff --git a/doc/md/Docker.md b/doc/md/Docker.md new file mode 100644 index 00000000..bcd8cff2 --- /dev/null +++ b/doc/md/Docker.md @@ -0,0 +1,207 @@ +# Docker + +[Docker](https://docs.docker.com/get-started/overview/) is an open platform for developing, shipping, and running applications + +## Install Docker + +Install [Docker](https://www.docker.com/), by following the instructions relevant to your OS / distribution, and start the service. For example on [Debian](https://docs.docker.com/engine/install/debian/): + +```bash +# update your package lists +$ sudo apt update +# remove old versions +$ sudo apt-get remove docker docker-engine docker.io containerd runc +# install requirements +$ sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common +# add docker's GPG signing key +curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add - +# add the repository +$ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable" +# install docker engine +$ sudo apt-get update +$ sudo apt-get install docker-ce docker-ce-cli containerd.io +# verify that Docker is properly configured +root@stretch-shaarli-02:~$ docker run hello-world +``` + + +## Get and run a Shaarli image + +Shaarli images are available on [DockerHub](https://hub.docker.com/r/shaarli/shaarli/): + +- `latest`: latest branch +- `master`: master branch + +These images are built automatically on DockerHub and rely on: + +- [Alpine Linux](https://www.alpinelinux.org/) +- [PHP7-FPM](http://php-fpm.org/) +- [Nginx](http://nginx.org/) + +Additional Dockerfiles are provided for the `arm32v7` platform, relying on [Linuxserver.io Alpine armhf images](https://hub.docker.com/r/lsiobase/alpine.armhf/). These images must be built using [`docker build`](https://docs.docker.com/engine/reference/commandline/build/) on an `arm32v7` machine or using an emulator such as [qemu](https://resin.io/blog/building-arm-containers-on-any-x86-machine-even-dockerhub/). + +```bash +# download the 'latest' image from dockerhub +docker pull shaarli/shaarli + +# create persistent data volumes/directories on the host +docker volume create shaarli-data +docker volume create shaarli-cache + +# create a new container using the Shaarli image +# --detach: run the container in background +# --name: name of the created container/instance +# --publish: map the host's :8000 port to the container's :80 port +# --rm: automatically remove the container when it exits +# --volume: mount persistent volumes in the container ($volume_name:$volume_mountpoint) +docker run --detach \ + --name myshaarli \ + --publish 8000:80 \ + --rm \ + --volume shaarli-data:/var/www/shaarli/data \ + --volume shaarli-cache:/var/www/shaarli/cache \ + shaarli/shaarli + +# verify that the container is running +docker ps | grep myshaarli + +# to completely remove the container +docker stop myshaarli # stop the running container +docker ps | grep myshaarli # verify the container is no longer running +docker ps -a | grep myshaarli # verify the container is stopped +docker rm myshaarli # destroy the container +docker ps -a | grep myshaarli # verify th container has been destroyed + +``` + +## Docker Compose + +A [Compose file](https://docs.docker.com/compose/compose-file/) is a common format for defining and running multi-container Docker applications. + +A `docker-compose.yml` file can be used to run a persistent/autostarted shaarli service using [Docker Compose](https://docs.docker.com/compose/) or in a [Docker stack](https://docs.docker.com/engine/reference/commandline/stack_deploy/). + +Shaarli provides configuration file for Docker Compose, that will setup a Shaarli instance, a [Træfik](https://hub.docker.com/_/traefik/) instance with [Let's Encrypt](https://letsencrypt.org/) certificates, a Docker network, and volumes for Shaarli data and Træfik TLS configuration and certificates. + +```bash +Download docker-compose from the [release page](https://docs.docker.com/compose/install/): + +```shell +$ sudo curl -L "https://github.com/docker/compose/releases/download/1.25.5/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose +$ sudo chmod +x /usr/local/bin/docker-compose +# create a new directory to store the configuration: +$ mkdir shaarli && cd shaarli +# Download the current version of Shaarli's docker-compose.yml +$ curl -L https://raw.githubusercontent.com/shaarli/Shaarli/master/docker-compose.yml -o docker-compose.yml +# Create the .env file and fill in your VPS and domain information +# (replace and with your actual information) +$ echo 'SHAARLI_VIRTUAL_HOST=shaarli.mydomain.org' > .env +$ echo 'SHAARLI_LETSENCRYPT_EMAIL=admin@mydomain.org' >> .env +# Pull the Docker images +$ docker-compose pull +# Run! +$ docker-compose up -d +``` + + + +### Running dockerized Shaarli as a systemd service + +It is possible to start a dockerized Shaarli instance as a systemd service (systemd is the service management tool on several distributions). After installing Docker, use the following steps to run your shaarli container Shaarli to run on system start. + +As root, create `/etc/systemd/system/docker.shaarli.service`: + +```ini +[Unit] +Description=Shaarli Bookmark Manager Container +After=docker.service +Requires=docker.service + + +[Service] +Restart=always + +# Put any environment you want in an included file, like $host- or $domainname in this example +EnvironmentFile=/etc/sysconfig/box-environment + +# It's just an example.. +ExecStart=/usr/bin/docker run \ + -p 28010:80 \ + --name ${hostname}-shaarli \ + --hostname shaarli.${domainname} \ + -v /srv/docker-volumes-local/shaarli-data:/var/www/shaarli/data:rw \ + -v /etc/localtime:/etc/localtime:ro \ + shaarli/shaarli:latest + +ExecStop=/usr/bin/docker rm -f ${hostname}-shaarli + +[Install] +WantedBy=multi-user.target +``` + +```bash +# reload systemd services definitions +systemctl daemon-reload +# start the servie and enable it a boot time +systemctl enable docker.shaarli.service --now +# verify that the service is running +systemctl status docker.* +# inspect system log if needed +journalctl -f +``` + + + +## Docker cheatsheet + +```bash +# pull/update an image +$ docker pull shaarli:release +# run a container from an image +$ docker run shaarli:latest +# list available images +$ docker images ls +# list running containers +$ docker ps +# list running AND stopped containers +$ docker ps -a +# run a command in a running container +$ docker exec -ti bash +# follow logs of a running container +$ docker logs -f +# delete unused images to free up disk space +$ docker system prune --images +# delete unused volumes to free up disk space (CAUTION all data in unused volumes will be lost) +$ docker system prunt --volumes +# delete unused containers +$ docker system prune +``` + + +## References + +- [Docker: using volumes](https://docs.docker.com/storage/volumes/) +- [Dockerfile best practices](https://docs.docker.com/articles/dockerfile_best-practices/) +- [Dockerfile reference](https://docs.docker.com/reference/builder/) +- [DockerHub: GitHub automated build](https://docs.docker.com/docker-hub/github/) +- [DockerHub: Repositories](https://docs.docker.com/userguide/dockerrepos/) +- [DockerHub: Teams and organizations](https://docs.docker.com/docker-hub/orgs/) +- [Get Docker CE for Debian](https://docs.docker.com/install/linux/docker-ce/debian/) +- [Install Docker Compose](https://docs.docker.com/compose/install/) +- [Interactive Docker training portal](https://www.katacoda.com/courses/docker/) on [Katakoda](https://www.katacoda.com/) +- [Service management: Nginx in the foreground](http://nginx.org/en/docs/ngx_core_module.html#daemon) +- [Service management: Using supervisord](https://docs.docker.com/articles/using_supervisord/) +- [Volumes](https://docs.docker.com/storage/volumes/) +- [Volumes](https://docs.docker.com/userguide/dockervolumes/) +- [Where are Docker images stored?](http://blog.thoward37.me/articles/where-are-docker-images-stored/) +- [docker create](https://docs.docker.com/engine/reference/commandline/create/) +- [Docker Documentation](https://docs.docker.com/) +- [docker exec](https://docs.docker.com/engine/reference/commandline/exec/) +- [docker images](https://docs.docker.com/engine/reference/commandline/images/) +- [docker logs](https://docs.docker.com/engine/reference/commandline/logs/) +- [docker logs](https://docs.docker.com/engine/reference/commandline/logs/) +- [Docker Overview](https://docs.docker.com/engine/docker-overview/) +- [docker ps](https://docs.docker.com/engine/reference/commandline/ps/) +- [docker pull](https://docs.docker.com/engine/reference/commandline/pull/) +- [docker run](https://docs.docker.com/engine/reference/commandline/run/) +- [docker-compose logs](https://docs.docker.com/compose/reference/logs/) +- Træfik: [Getting Started](https://docs.traefik.io/), [Docker backend](https://docs.traefik.io/configuration/backends/docker/), [Let's Encrypt](https://docs.traefik.io/user-guide/docker-and-lets-encrypt/), [Docker image](https://hub.docker.com/_/traefik/) \ No newline at end of file diff --git a/doc/md/Download-and-Installation.md b/doc/md/Download-and-Installation.md deleted file mode 100644 index ec68762e..00000000 --- a/doc/md/Download-and-Installation.md +++ /dev/null @@ -1,124 +0,0 @@ -To install Shaarli, simply place the files in a directory under your webserver's -Document Root (or directly at the document root). - -Also, please make sure your server is properly [configured](Server-configuration.md). - -Multiple releases branches are available: - -- latest (last release) -- stable (previous major release) -- master (development) - -Using one of the following methods: - -- by downloading full release archives including all dependencies -- by downloading Github archives -- by cloning the Git repository -- using Docker: [see the documentation](docker/shaarli-images.md) - --------------------------------------------------------------------------------- - -## Latest release (recommended) - -### Download as an archive - -In most cases, you should download the latest Shaarli release from the [releases](https://github.com/shaarli/Shaarli/releases) page. Download our **shaarli-full** archive to include dependencies. - -The current latest released version is `v0.10.4` - -```bash -$ wget https://github.com/shaarli/Shaarli/releases/download/v0.10.4/shaarli-v0.10.4-full.zip -$ unzip shaarli-v0.10.4-full.zip -$ mv Shaarli /path/to/shaarli/ -``` - -### Using git - -Cloning using `git` or downloading Github branches as zip files requires additional steps: - - * Install [Composer](Unit-tests.md#install_composer) to manage third-party [PHP dependencies](3rd-party-libraries.md#composer). - * Install [yarn](https://yarnpkg.com/lang/en/docs/install/) to build the frontend dependencies. - * Install [python3-virtualenv](https://pypi.python.org/pypi/virtualenv) to build the local HTML documentation. - -``` -$ mkdir -p /path/to/shaarli && cd /path/to/shaarli/ -$ git clone -b latest https://github.com/shaarli/Shaarli.git . -$ composer install --no-dev --prefer-dist -$ make build_frontend -$ make translate -$ make htmldoc -``` - --------------------------------------------------------------------------------- - -## Stable version - -The stable version has been experienced by Shaarli users, and will receive security updates. - - -### Download as an archive - -As a .zip archive: - -```bash -$ wget https://github.com/shaarli/Shaarli/archive/stable.zip -$ unzip stable.zip -$ mv Shaarli-stable /path/to/shaarli/ -``` - -As a .tar.gz archive : - -```bash -$ wget https://github.com/shaarli/Shaarli/archive/stable.tar.gz -$ tar xvf stable.tar.gz -$ mv Shaarli-stable /path/to/shaarli/ -``` - -### Using git - -Install [Composer](Unit-tests.md#install_composer) to manage Shaarli dependencies. - -```bash -$ git clone https://github.com/shaarli/Shaarli.git -b stable /path/to/shaarli/ -# install/update third-party dependencies -$ cd /path/to/shaarli/ -$ composer install --no-dev --prefer-dist -``` - - --------------------------------------------------------------------------------- - -## Development version (mainline) - -_Use at your own risk!_ - -Install [Composer](Unit-tests.md#install_composer) to manage Shaarli PHP dependencies, -and [yarn](https://yarnpkg.com/lang/en/docs/install/) -for front-end dependencies. - -To get the latest changes from the `master` branch: - -```bash -# clone the repository -$ git clone https://github.com/shaarli/Shaarli.git -b master /path/to/shaarli/ -# install/update third-party dependencies -$ cd /path/to/shaarli -$ composer install --no-dev --prefer-dist -$ make build_frontend -$ make translate -$ make htmldoc -``` - -------------------------------------------------------------------------------- - -## Finish Installation - -Once Shaarli is downloaded and files have been placed at the correct location, open it this location your favorite browser. - -![install screenshot](images/install-shaarli.png) - -Setup your Shaarli installation, and it's ready to use! - -## Updating Shaarli - -See [Upgrade and Migration](Upgrade-and-migration) diff --git a/doc/md/FAQ.md b/doc/md/FAQ.md deleted file mode 100644 index a2ec7d57..00000000 --- a/doc/md/FAQ.md +++ /dev/null @@ -1,46 +0,0 @@ -### Why did you create Shaarli ? - -I was a StumbleUpon user. Then I got fed up with they big toolbar. I switched to delicious, which was lighter, faster and more beautiful. Until Yahoo bought it. Then the export API broke all the time, delicious became slow and was ditched by Yahoo. I switched to Diigo, which is not bad, but does too much. And Diigo is sslllooooowww and their Firefox extension a bit buggy. And… oh… **their Firefox addon sends to Diigo every single URL you visit** (Don't believe me ? Use [Tamper Data](https://addons.mozilla.org/en-US/firefox/addon/tamper-data/) and open any page). - -Enough is enough. Saving simple links should not be a complicated heavy thing. I ditched them all and wrote my own: Shaarli. It's simple, but it does the job and does it well. And my data is not hosted on a foreign server, but on my server. - -### Why use Shaarli and not Delicious/Diigo ? - -With Shaarli: - -- The data is yours: It's hosted on your server. -- Never fear of having your data locked-in. -- Never fear to have your data sold to third party. -- Your private links are not hosted on a third party server. -- You are not tracked by browser addons (like Diigo does) -- You can change the look and feel of the pages if you want. -- You can change the behaviour of the program. -- It's magnitude faster than most bookmarking services. - -### What does Shaarli mean? - -Shaarli stands for _shaaring_ your _links_. - -### My Shaarli is broken! -First of all, ensure that both the [web server](Server-configuration) and -[Shaarli](Shaarli-configuration) are correctly configured, and that your -installation is [supported](Server-configuration). - -If everything looks right but the issue(s) remain(s), please: - -- take a look at the [troubleshooting](Troubleshooting) section -- come [chat with us](https://gitter.im/shaarli/Shaarli) on Gitter, we'll be happy to help ;-) -- browse active [issues](https://github.com/shaarli/Shaarli/issues) and [Pull Requests](https://github.com/shaarli/Shaarli/pulls) - - if you find one that is related to the issue, feel free to comment and provide additional details (host/Shaarli setup) - - else, [open a new issue](https://github.com/shaarli/Shaarli/issues/new), and provide information about the problem: - - _what happens?_ - display glitches, invalid data, security flaws... - - _what is your configuration?_ - OS, server version, activated extensions, web browser... - - _is it reproducible?_ - -### Why not use a real database? Files are slow! - -Does browsing [this page](http://sebsauvage.net/links/) feel slow? Try browsing older pages, too. - -It's not slow at all, is it? And don't forget the database contains more than 16000 links, and it's on a shared host, with 32000 visitors/day for my website alone. And it's still damn fast. Why? - -The data file is only 3.7 Mb. It's read 99% of the time, and is probably already in the operation system disk cache. So generating a page involves no I/O at all most of the time. diff --git a/doc/md/Installation.md b/doc/md/Installation.md new file mode 100644 index 00000000..1286a6b2 --- /dev/null +++ b/doc/md/Installation.md @@ -0,0 +1,84 @@ +# Installation + +Once your server is [configured](Server-configuration.md), install Shaarli: + +## From release ZIP + +To install Shaarli, simply place the files from the latest [release .zip archive](https://github.com/shaarli/Shaarli/releases) under your webserver's document root (directly at the document root, or in a subdirectory). Download the **shaarli-vX.X.X-full** archive to include dependencies. + +```bash +wget https://github.com/shaarli/Shaarli/releases/download/v0.10.4/shaarli-v0.10.4-full.zip +unzip shaarli-v0.10.4-full.zip +sudo rsync -avP Shaarli/ /var/www/shaarli.mydomain.org/ +``` + +## From sources + +These components are required to build Shaarli: + +- [Composer](dev/Development.md#install-composer) to manage third-party [PHP dependencies](dev/Development#third-party-libraries). +- [yarn](https://yarnpkg.com/lang/en/docs/install/) to build frontend dependencies. +- [python3-virtualenv](https://pypi.python.org/pypi/virtualenv) to build local HTML documentation. + +Clone the repository, either pointing to: + +- any [tagged release](https://github.com/shaarli/Shaarli/releases) +- `latest`: the latest tagged release +- `master`: development branch + +```bash +# clone the branch/tag of your choice +$ git clone -b latest https://github.com/shaarli/Shaarli.git /home/me/Shaarli +# OR download/extract the tar.gz/zip: wget https://github.com/shaarli/Shaarli/archive/latest.tar.gz... + +# enter the directory +$ cd /home/me/Shaarli +# install 3rd-party PHP dependencies +$ composer install --no-dev --prefer-dist +# build frontend static assets +$ make build_frontend +# build translations +$ make translate +# build HTML documentation +$ make htmldoc +# copy the resulting shaarli directory under your webserver's document root +$ rsync -avP /home/me/Shaarli/ /var/www/shaarli.mydomain.org/ +``` + +## Set file permissions + +Regardless of the installation method, appropriate [file permissions](dev/Development.md#directory-structure) must be set: + +```bash +# by default, deny access to everything to the web server +sudo chown -R root:www-data /var/www/shaarli.mydomain.org +sudo chmod -R u=rwX /var/www/shaarli.mydomain.org +# allow read-only access to these files/directories +sudo chmod -R g+rX /var/www/shaarli.mydomain.org/{index.php,application/,plugins/,inc/} +# allow read/write access to these directories +sudo chmod -R g+rwX /var/www/shaarli.mydomain.org/{cache/,data/,pagecache/,tmp/} +``` + + +## Using Docker + +[See the documentation](Docker.md) + + + +## Finish Installation + +Once Shaarli is downloaded and files have been placed at the correct location, open this location your web browser. + +Enter basic settings for your Shaarli installation, and it's ready to use! + +![](images/07-installation.jpg) + +Congratulations! Your Shaarli is now available at `https://shaarli.mydomain.org`. + +You can further [configure Shaarli](Shaarli-configuration.md), setup [Plugins](Plugins.md) or [additional software](Community-and-related-software.md). + + +## Upgrading Shaarli + +See [Upgrade and Migration](Upgrade-and-migration) diff --git a/doc/md/Link-structure.md b/doc/md/Link-structure.md deleted file mode 100644 index 0a2d0f88..00000000 --- a/doc/md/Link-structure.md +++ /dev/null @@ -1,18 +0,0 @@ -## Link structure - -Every link available through the `LinkDB` object is represented as an array -containing the following fields: - - * `id` (integer): Unique identifier. - * `title` (string): Title of the link. - * `url` (string): URL of the link. Used for displayable links (without redirector, url encoding, etc.). - Can be absolute or relative for Notes. - * `real_url` (string): Real destination URL, can be redirected, encoded, etc. - * `shorturl` (string): Permalink small hash. - * `description` (string): Link text description. - * `private` (boolean): whether the link is private or not. - * `tags` (string): all link tags separated by a single space - * `thumbnail` (string|boolean): relative path of the thumbnail cache file, or false if there isn't any. - * `created` (DateTime): link creation date time. - * `updated` (DateTime): last modification date time. - \ No newline at end of file diff --git a/doc/md/Plugins.md b/doc/md/Plugins.md index 3e261815..49a51f51 100644 --- a/doc/md/Plugins.md +++ b/doc/md/Plugins.md @@ -1,14 +1,13 @@ -## Plugin installation +# Plugins -There is a bunch of plugins shipped with Shaarli, where there is nothing to do to install them. +## Installation -If you want to install a third party plugin: +For plugins shipped with Shaarli, no installation is required. -- Download it. -- Put it in the `plugins` directory in Shaarli's installation folder. -- Make sure you put it correctly: +If you want to install a third party plugin, download it to the `plugins` directory in Shaarli's installation folder: -``` +```bash +# example directory structure | index.php | plugins/ |---| custom_plugin/ @@ -17,34 +16,34 @@ If you want to install a third party plugin: ``` - * Make sure your webserver can read and write the files in your plugin folder. +Make sure your webserver can read and write the files in your plugin folder. -## Plugin configuration -In Shaarli's administration page (`Tools` link), go to `Plugin administration`. +## Configuration -Here you can enable and disable all plugins available, and configure them. +From Shaarli's administration page (`Tools` link), go to `Plugin administration`. Here you can enable and disable all plugins available, and configure them. ![administration screenshot](https://camo.githubusercontent.com/5da68e191969007492ca0fbeb25f3b2357b748cc/687474703a2f2f692e696d6775722e636f6d2f766837544643712e706e67) -## Plugin order + +## Order In the plugin administration page, you can move enabled plugins to the top or bottom of the list. The first plugins in the list will be processed first. -This is important in case plugins are depending on each other. Read plugins README details for more information. +This is important in case plugins depend on each other. Read plugins READMEs for more information. **Use case**: The (non existent) plugin `shaares_footer` adds a footer to every shaare in Markdown syntax. It needs to be processed *before* (higher in the list) the Markdown plugin. Otherwise its syntax won't be translated in HTML. -## File mode -Enabled plugin are stored in your `config.json.php` parameters file, under the `array`: +## Configuration file + +Enabled plugins are stored in your [Configuration file](Shaarli-configuration), under the array: ```php $GLOBALS['config']['ENABLED_PLUGINS'] ``` -You can edit them manually here. -Example: +You can edit them manually here. For example: ```php $GLOBALS['config']['ENABLED_PLUGINS'] = array( @@ -55,25 +54,25 @@ $GLOBALS['config']['ENABLED_PLUGINS'] = array( ); ``` -### Plugin usage -#### Official plugins +## Usage + +### Official plugins Usage of each plugin is documented in it's README file: - * `addlink-toolbar`: Adds the addlink input on the linklist page - * `archiveorg`: For each link, add an Archive.org icon + * `addlink-toolbar`: Adds the addlink input on the Shaares list page + * `archiveorg`: For each Shaare, add a link to the archived page on Archive.org * `default_colors`: Override default theme colors. * `isso`: Let visitor comment your shaares on permalinks with Isso. * [`markdown`](https://github.com/shaarli/Shaarli/blob/master/plugins/markdown/README.md): Render shaare description with Markdown syntax. * `piwik`: A plugin that adds Piwik tracking code to Shaarli pages. * [`playvideos`](https://github.com/shaarli/Shaarli/blob/master/plugins/playvideos/README.md): Add a button in the toolbar allowing to watch all videos. * `pubsubhubbub`: Enable PubSubHubbub feed publishing - * `qrcode`: For each link, add a QRCode icon. - * [`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. - + * `qrcode`: For each Shaare, add a QRCode icon. + * [`wallabag`](https://github.com/shaarli/Shaarli/blob/master/plugins/wallabag/README.md): For each Shaare, add a Wallabag icon to save it in your instance. -#### Third party plugins +### Third party plugins -See [Community & related software](https://shaarli.readthedocs.io/en/master/Community-&-Related-software/) +See [Community & related software](https://shaarli.readthedocs.io/en/master/Community-and-Related-software/) diff --git a/doc/md/REST-API.md b/doc/md/REST-API.md index 11bd1cd2..01071d8e 100644 --- a/doc/md/REST-API.md +++ b/doc/md/REST-API.md @@ -1,101 +1,24 @@ -## Usage and Prerequisites +# REST API -See the [REST API documentation](http://shaarli.github.io/api-documentation/) -for a list of available endpoints and parameters. +## Server requirements -Please ensure that your server meets the -[requirements](Server-configuration#prerequisites) and is properly -[configured](Server-configuration): +See the **[REST API documentation](http://shaarli.github.io/api-documentation/)** for a list of available endpoints and parameters. + +Please ensure that your server meets the requirements and is properly [configured](Server-configuration): - URL rewriting is enabled (see specific Apache and Nginx sections) - the server's timezone is properly defined -- the server's clock is synchronized with - [NTP](https://en.wikipedia.org/wiki/Network_Time_Protocol) - -The host where the API client is invoked should also be synchronized with NTP, -see [token expiration](#payload). - -## 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 resources : - -- [jwt.io](https://jwt.io) (including a list of client per language). -- 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] -``` - -#### 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 - -**Token expiration** - -To avoid infinite token validity, JWT tokens must include their creation date -in UNIX timestamp format (timezone independent - UTC) under the key `iat` (issued at). -This token will be valid during **9 minutes**. - -```json -{ - "iat": 1468663519 -} -``` - -See [RFC reference](https://tools.ietf.org/html/rfc7519#section-4.1.6). - +- the server's clock is synchronized with [NTP](https://en.wikipedia.org/wiki/Network_Time_Protocol) -#### 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); -``` +The host where the API client is invoked should also be synchronized with NTP, see _payload/token expiration_ ## Clients and examples -### Android, Java, Kotlin - -- [Android client example with Kotlin](https://gitlab.com/snippets/1665808) - by [Braincoke](https://github.com/Braincoke) - -### Javascript, NodeJS -- [shaarli-client](https://www.npmjs.com/package/shaarli-client) - ([source code](https://github.com/laBecasse/shaarli-client)) - by [laBecasse](https://github.com/laBecasse) +- **[python-shaarli-client](https://github.com/shaarli/python-shaarli-client)** - the reference API client ([Documentation](http://python-shaarli-client.readthedocs.io/en/latest/)) +- [shaarli-client](https://www.npmjs.com/package/shaarli-client) - NodeJs client ([source code](https://github.com/laBecasse/shaarli-client)) by [laBecasse](https://github.com/laBecasse) +- [Android client example with Kotlin](https://gitlab.com/snippets/1665808) by [Braincoke](https://github.com/Braincoke) -### PHP This example uses the [PHP cURL](http://php.net/manual/en/book.curl.php) library. @@ -145,13 +68,57 @@ function getInfo($baseUrl, $secret) { var_dump(getInfo($baseUrl, $secret)); ``` +## Implementation + +### Authentication + +- All requests to Shaarli's API must include a **JWT token** to verify their authenticity. +- This token must be included as an HTTP header called `Authentication: Bearer `. +- JWT tokens are composed by three parts, separated by a dot `.` and encoded in base64: + +``` +[header].[payload].[signature] +``` + +##### 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: -### Python +``` +ewogICAgICAgICJ0eXAiOiAiSldUIiwKICAgICAgICAiYWxnIjogIkhTNTEyIgogICAgfQ== +``` + +##### Payload + +Token expiration: To avoid infinite token validity, JWT tokens must include their creation date in UNIX timestamp format (timezone independent - UTC) under the key `iat` (issued at) field ([1](https://tools.ietf.org/html/rfc7519#section-4.1.6)). This token will be valid during **9 minutes**. + +```json +{ + "iat": 1468663519 +} +``` + +##### Signature + +The signature authenticates 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. + +Example signature with PHP: + +```php +$content = base64_encode($header) . '.' . base64_encode($payload); +$signature = hash_hmac('sha512', $content, $secret); +``` -See the reference API client: -- [Documentation](http://python-shaarli-client.readthedocs.io/en/latest/) on ReadTheDocs -- [python-shaarli-client](https://github.com/shaarli/python-shaarli-client) on Github ## Troubleshooting @@ -171,3 +138,13 @@ to get the actual error message in the HTTP response body with: } } ``` + +## References + +- [jwt.io](https://jwt.io) (including a list of client per language). +- [RFC - JSON Web Token (JWT)](https://tools.ietf.org/html/rfc7519) +- [JSON Web Tokens (JWT) vs Sessions](https://float-middle.com/json-web-tokens-jwt-vs-sessions/), [HackerNews thread](https://news.ycombinator.com/item?id=11929267) + + + + diff --git a/doc/md/RSS-feeds.md b/doc/md/RSS-feeds.md deleted file mode 100644 index ecbff09a..00000000 --- a/doc/md/RSS-feeds.md +++ /dev/null @@ -1,28 +0,0 @@ -### Feeds options - -Feeds are available in ATOM with `/feed/atom` and RSS with `/feed/rss`. - -Options: - -- You can use `permalinks` in the feed URL to get permalink to Shaares instead of direct link to shaared URL. - - E.G. `https://my.shaarli.domain/feed/atom?permalinks`. -- You can use `nb` parameter in the feed URL to specify the number of Shaares you want in a feed (default if not specified: `50`). The keyword `all` is available if you want everything. - - `https://my.shaarli.domain/feed/atom?permalinks&nb=42` - - `https://my.shaarli.domain/feed/atom?permalinks&nb=all` - -### RSS Feeds or Picture Wall for a specific search/tag - -It is possible to filter RSS/ATOM feeds and Picture Wall on a Shaarli to **only display results of a specific search, or for a specific tag**. - -For example, if you want to subscribe only to links tagged `photography`: - -- Go to the desired Shaarli instance. -- Search for the `photography` tag in the _Filter by tag_ box. Links tagged `photography` are displayed. -- Click on the `RSS Feed` button. -- You are presented with an RSS feed showing only these links. Subscribe to it to receive only updates with this tag. -- The same method **also works for a full-text search** (_Search_ box) **and for the Picture Wall** (want to only see pictures about `nature`?) -- You can also build the URLs manually: - - `https://my.shaarli.domain/?do=rss&searchtags=nature` - - `https://my.shaarli.domain/links/picture-wall?searchterm=poney` - -![](images/rss-filter-1.png) ![](images/rss-filter-2.png) diff --git a/doc/md/Release-Shaarli.md b/doc/md/Release-Shaarli.md deleted file mode 100644 index e22eabc9..00000000 --- a/doc/md/Release-Shaarli.md +++ /dev/null @@ -1,161 +0,0 @@ -See [Git - Maintaining a project - Tagging your -releases](http://git-scm.com/book/en/v2/Distributed-Git-Maintaining-a-Project#Tagging-Your-Releases). - -## Prerequisites -This guide assumes that you have: - -- a GPG key matching your GitHub authentication credentials - - i.e., the email address identified by the GPG key is the same as the one in your `~/.gitconfig` -- a GitHub fork of Shaarli -- a local clone of your Shaarli fork, with the following remotes: - - `origin` pointing to your GitHub fork - - `upstream` pointing to the main Shaarli repository -- maintainer permissions on the main Shaarli repository, to: - - push the signed tag - - create a new release -- [Composer](https://getcomposer.org/) needs to be installed -- The [venv](https://docs.python.org/3/library/venv.html) Python 3 module needs to be installed for HTML documentation generation. - -## GitHub release draft and `CHANGELOG.md` -See http://keepachangelog.com/en/0.3.0/ for changelog formatting. - -### GitHub release draft -GitHub allows drafting the release note for the upcoming release, from the [Releases](https://github.com/shaarli/Shaarli/releases) page. This way, the release note can be drafted while contributions are merged to `master`. - -### `CHANGELOG.md` -This file should contain the same information as the release note draft for the upcoming version. - -Update it to: - -- add new entries (additions, fixes, etc.) -- mark the current version as released by setting its date and link -- add a new section for the future unreleased version - -```bash -$ cd /path/to/shaarli - -$ nano CHANGELOG.md - -[...] -## vA.B.C - UNRELEASED -TBA - -## [vX.Y.Z](https://github.com/shaarli/Shaarli/releases/tag/vX.Y.Z) - YYYY-MM-DD -[...] -``` - - -## Increment the version code, update docs, create and push a signed tag -### Update the list of Git contributors -```bash -$ make authors -$ git commit -s -m "Update AUTHORS" -``` - -### Create and merge a Pull Request -This one is pretty straightforward ;-) - -### Bump Shaarli version to v0.x branch - -```bash -$ git checkout master -$ git fetch upstream -$ git pull upstream master - -# IF the branch doesn't exists -$ git checkout -b v0.5 -# OR if the branch already exists -$ git checkout v0.5 -$ git rebase upstream/master - -# Bump shaarli version from dev to 0.5.0, **without the `v`** -$ vim shaarli_version.php -$ git add shaarli_version -$ git commit -s -m "Bump Shaarli version to v0.5.0" -$ git push upstream v0.5 -``` - -### Create and push a signed tag -```bash -# update your local copy -$ git checkout v0.5 -$ git fetch upstream -$ git pull upstream v0.5 - -# create a signed tag -$ git tag -s -m "Release v0.5.0" v0.5.0 - -# push it to "upstream" -$ git push --tags upstream -``` - -### Verify a signed tag -[`v0.5.0`](https://github.com/shaarli/Shaarli/releases/tag/v0.5.0) is the first GPG-signed tag pushed on the Community Shaarli. - -Let's have a look at its signature! - -```bash -$ cd /path/to/shaarli -$ git fetch upstream - -# get the SHA1 reference of the tag -$ git show-ref tags/v0.5.0 -f7762cf803f03f5caf4b8078359a63783d0090c1 refs/tags/v0.5.0 - -# verify the tag signature information -$ git verify-tag f7762cf803f03f5caf4b8078359a63783d0090c1 -gpg: Signature made Thu 30 Jul 2015 11:46:34 CEST using RSA key ID 4100DF6F -gpg: Good signature from "VirtualTam " [ultimate] -``` - -## Publish the GitHub release -### Update release badges -Update `README.md` so version badges display and point to the newly released Shaarli version(s), in the `master` branch. - -### Create a GitHub release from a Git tag -From the previously drafted release: - -- edit the release notes (if needed) -- specify the appropriate Git tag -- publish the release -- profit! - -### Generate and upload all-in-one release archives -Users with a shared hosting may have: - -- no SSH access -- no possibility to install PHP packages or server extensions -- no possibility to run scripts - -To ease Shaarli installations, it is possible to generate and upload additional release archives, -that will contain Shaarli code plus all required third-party libraries. - -**From the `v0.5` branch:** - -```bash -$ make release_archive -``` - -This will create the following archives: - -- `shaarli-vX.Y.Z-full.tar` -- `shaarli-vX.Y.Z-full.zip` - -The archives need to be manually uploaded on the previously created GitHub release. - -### Update `stable` and `latest` branches - -``` -$ git checkout latest -# latest release -$ git merge v0.5.0 -# fix eventual conflicts -$ make test -$ git push upstream latest -$ git checkout stable -# latest previous major -$ git merge v0.4.5 -# fix eventual conflicts -$ make test -$ git push upstream stable -``` diff --git a/doc/md/Reverse-proxy.md b/doc/md/Reverse-proxy.md new file mode 100644 index 00000000..2c1c601e --- /dev/null +++ b/doc/md/Reverse-proxy.md @@ -0,0 +1,116 @@ +# Reverse proxy + +If Shaarli is hosted on a server behind a [reverse proxy](https://en.wikipedia.org/wiki/Reverse_proxy) (i.e. there is a proxy server between clients and the web server hosting Shaarli), configure it accordingly. See [Reverse proxy](Reverse-proxy.md) configuration. In this example: + +- The Shaarli application server exposes port `10080` to the proxy (for example docker container started with `--publish 127.0.0.1:10080:80`). +- The Shaarli application server runs at `127.0.0.1` (container). Replace with the server's IP address if running on a different machine. +- Shaarli's Fully Qualified Domain Name (FQDN) is `shaarli.mydomain.org`. +- No HTTPS is setup on the application server, SSL termination is done at the reverse proxy. + +In your [Shaarli configuration](Shaarli-configuration) `data/config.json.php`, add the public IP of your proxy under `security.trusted_proxies`. + +See also [proxy-related](https://github.com/shaarli/Shaarli/issues?utf8=%E2%9C%93&q=label%3Aproxy+) issues. + + +## Apache + +```apache + + ServerName shaarli.mydomain.org + # Redirect HTTP to HTTPS + Redirect permanent / https://shaarli.mydomain.org + + + + ServerName shaarli.mydomain.org + + SSLEngine on + SSLCertificateFile /path/to/certificate + SSLCertificateKeyFile /path/to/private/key + + LogLevel warn + ErrorLog /var/log/apache2/error.log + CustomLog /var/log/apache2/access.log combined + + # let the proxied shaarli server/container know HTTPS URLs should be served + RequestHeader set X-Forwarded-Proto "https" + + # send the original SERVER_NAME to the proxied host + ProxyPreserveHost On + + # pass requests to the proxied host + # sets X-Forwarded-For, X-Forwarded-Host and X-Forwarded-Server headers + ProxyPass / http://127.0.0.1:10080/ + ProxyPassReverse / http://127.0.0.1:10080/ + +``` + + +## HAProxy + + +```conf +global + [...] + +defaults + [...] + +frontend http-in + bind :80 + redirect scheme https code 301 if !{ ssl_fc } + bind :443 ssl crt /path/to/cert.pem + default_backend shaarli + +backend shaarli + mode http + option http-server-close + option forwardfor + reqadd X-Forwarded-Proto: https + server shaarli1 127.0.0.1:10080 +``` + + +## Nginx + + +```nginx +http { + [...] + + index index.html index.php; + + root /home/john/web; + access_log /var/log/nginx/access.log combined; + error_log /var/log/nginx/error.log; + + server { + listen 80; + server_name shaarli.mydomain.org; + # redirect HTTP to HTTPS + return 301 https://shaarli.mydomain.org$request_uri; + } + + server { + listen 443 ssl http2; + server_name shaarli.mydomain.org; + + ssl_certificate /path/to/certificate + ssl_certificate_key /path/to/private/key + + location / { + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Forwarded-Host $host; + + # pass requests to the proxied host + proxy_pass http://localhost:10080/; + proxy_set_header Host $host; + proxy_connect_timeout 30s; + proxy_read_timeout 120s; + } + } +} +``` + diff --git a/doc/md/Security.md b/doc/md/Security.md deleted file mode 100644 index 65db4225..00000000 --- a/doc/md/Security.md +++ /dev/null @@ -1,25 +0,0 @@ -## Client browser -- Shaarli relies on `HTTP_REFERER` for some functions (like redirects and clicking on tags). If you have disabled or masqueraded `HTTP_REFERER` in your browser, some features of Shaarli may not work - -## Server and sessions -- Directories are protected using `.htaccess` files -- Forms are protected against XSRF (Cross-site requests forgery): - - Forms which act on data (save,delete…) contain a token generated by the server. - - Any posted form which does not contain a valid token is rejected. - - Any token can only be used once. - - Tokens are attached to the session and cannot be reused in another session. -- Sessions automatically expire after 60 minutes. -- Sessions are protected against hijacking: the session ID cannot be used from a different IP address. - -## Shaarli datastore and configuration -- The password is salted, hashed and stored in the data subdirectory, in a PHP file, and protected by htaccess. Even if the webserver does not support htaccess, the hash is not readable by URL. Even if the .php file is stolen, the password cannot deduced from the hash. The salt prevents rainbow-tables attacks. -- Links are stored as an associative array which is serialized, compressed (with deflate), base64-encoded and saved as a comment in a `.php` file. -- Even if the server does not support `.htaccess` files, the data file will still not be readable by URL. -- The database looks like this: - -```php - -``` - -- Small hashes are used to make a link to an entry in Shaarli. They are unique. In fact, the date of the items (eg. `20110923_150523`) is hashed with CRC32, then converted to base64 and some characters are replaced. They are always 6 characters longs and use only `A-Z a-z 0-9 - _` and `@`. diff --git a/doc/md/Server-configuration.md b/doc/md/Server-configuration.md index f9ea2ed2..5c45942c 100644 --- a/doc/md/Server-configuration.md +++ b/doc/md/Server-configuration.md @@ -1,17 +1,29 @@ +# Server configuration -- [Prerequisites](#prerequisistes) -- [Apache](#apache) -- [Nginx](#nginx) -- [Proxies](#proxies) -- [See also](#see-also) -## Prerequisites -### Shaarli -- A web server and PHP interpreter module/service have been installed. -- You have write access to the Shaarli installation directory. -- The correct read/write permissions have been granted to the web server user and group. -- Your PHP interpreter is compatible with supported PHP versions: +## Requirements + +### Operating system and web server + +Shaarli can be hosted on dedicated/virtual servers, or shared hosting. The smallest DigitalOcean VPS (Droplet with 1 CPU, 1 GiB RAM and 25 GiB SSD) costs about $5/month and will run any Shaarli installation without problems. + +You need write access to the Shaarli installation directory - you should have received instructions from your hosting provider on how to connect to the server using SSH (or FTP for shared hosts). + +Examples in this documentation are given for [Debian](https://www.debian.org/), a GNU/Linux distribution widely used in server environments. Please adapt them to your specific Linux distribution. + +### Network and domain name + +Try to host the server in a region that is geographically close to your users. + +A domain name ([DNS record](https://opensource.com/article/17/4/introduction-domain-name-system-dns)) pointing to the server's public IP address is required to obtain a SSL/TLS certificate and setup HTTPS to secure client traffic to your Shaarli instance. + +You can obtain a domain name from a [registrar](https://en.wikipedia.org/wiki/Domain_name_registrar) ([1](https://www.ovh.co.uk/domains), [2](https://www.gandi.net/en/domain)), or from free subdomain providers ([1](https://freedns.afraid.org/)). If you don't have a domain name, please set up a private domain name ([FQDN](ttps://en.wikipedia.org/wiki/Fully_qualified_domain_name) in your clients' [hosts files](https://en.wikipedia.org/wiki/Hosts_(file)) to access the server (direct access by IP address can result in unexpected behavior). + + +### PHP + +Supported PHP versions: Version | Status | Shaarli compatibility :---:|:---:|:---: @@ -23,7 +35,7 @@ Version | Status | Shaarli compatibility 5.4 | EOL: 2015-09-14 | Yes (up to Shaarli 0.8.x) 5.3 | EOL: 2014-08-14 | Yes (up to Shaarli 0.8.x) -- The following PHP extensions are installed on the server: +Required PHP extensions: Extension | Required? | Usage ---|:---:|--- @@ -34,60 +46,108 @@ Extension | Required? | Usage [`php-intl`](http://php.net/manual/en/book.intl.php) | optional | localized text sorting (e.g. `e->è->f`) [`php-curl`](http://php.net/manual/en/book.curl.php) | optional | using cURL for fetching webpages and thumbnails in a more robust way [`php-gettext`](http://php.net/manual/en/book.gettext.php) | optional | Use the translation system in gettext mode (faster) --------------------------------------------------------------------------------- -### SSL/TLS configuration +Some [plugins](Plugins.md) may require additional configuration. + + +## SSL/TLS (HTTPS) -To setup HTTPS / SSL on your webserver (recommended), you must generate a public/private **key pair** and a **certificate**, and install, configure and activate the appropriate **webserver SSL extension**. +We recommend setting up [HTTPS](https://en.wikipedia.org/wiki/HTTPS) on your webserver for secure communication between clients and the server. -#### Let's Encrypt +For public-facing web servers this can be done using free SSL/TLS certificates from [Let's Encrypt](https://en.wikipedia.org/wiki/Let's_Encrypt), a non-profit certificate authority provididing free certificates. -[Let's Encrypt](https://en.wikipedia.org/wiki/Let%27s_Encrypt) is a certificate authority that provides free TLS/X.509 certificates via an automated process. + - [How to secure Apache with Let's Encrypt](https://www.digitalocean.com/community/tutorials/how-to-secure-apache-with-let-s-encrypt-on-debian-10) + - [How to secure Nginx with Let's Encrypt](https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-debian-10) + - [How To Use Certbot Standalone Mode to Retrieve Let's Encrypt SSL Certificates](https://www.digitalocean.com/community/tutorials/how-to-use-certbot-standalone-mode-to-retrieve-let-s-encrypt-ssl-certificates-on-debian-10). - * Install `certbot` using the appropriate method described on https://certbot.eff.org/. - -Location of the `certbot` program and template configuration files may vary depending on which installation method was used. Change the file paths below accordingly. Here is an easy way to create a signed certificate using `certbot`, it assumes `certbot` was installed through APT on a Debian-based distribution: +In short: - * Stop the apache2/nginx service. - * Run `certbot --agree-tos --standalone --preferred-challenges tls-sni --email "youremail@example.com" --domain yourdomain.example.com` - * For the Apache webserver, copy `/usr/lib/python2.7/dist-packages/certbot_apache/options-ssl-apache.conf` to `/etc/letsencrypt/options-ssl-apache.conf` (paths may vary depending on installation method) - * For Nginx: TODO - * Setup your webserver as described below - * Restart the apache2/nginx service. +```bash +# install certbot +sudo apt install certbot -#### Self-signed certificates +# stop your webserver if you already have one running +# certbot in standalone mode needs to bind to port 80 (only needed on initial generation) +sudo systemctl stop apache2 +sudo systemctl stop nginx -If you don't want to request a certificate from Let's Encrypt, or are unable to (for example, webserver on a LAN, or domain name not registered in the public DNS system), you can generate a self-signed certificate. This certificate will trigger security warnings in web browsers, unless you add it to the browser's SSL store manually. +# generate initial certificates - Let's Encrypt ACME servers must be able to access your server! +# (DNS records must be correctly pointing to it, firewall/NAT on port 80/443 must be open) +sudo certbot certonly --standalone --noninteractive --agree-tos --email "admin@shaarli.mydomain.org" -d shaarli.mydomain.org +# this will generate a private key and certificate at /etc/letsencrypt/live/shaarli.mydomain.org/{privkey,fullchain}.pem -* Apache: run `make-ssl-cert generate-default-snakeoil --force-overwrite` -* Nginx: TODO +# restart the web server +sudo systemctl start apache2 +sudo systemctl start nginx +``` + +If you don't want to rely on a certificate authority, or the server can only be accessed from your own network, you can also generate self-signed certificates. Not that this will generate security warnings in web browsers/clients trying to access Shaarli: + +- [How To Create a Self-Signed SSL Certificate for Apache](https://www.digitalocean.com/community/tutorials/how-to-create-a-self-signed-ssl-certificate-for-apache-on-debian-10) +- [How To Create a Self-Signed SSL Certificate for Nginx](https://www.digitalocean.com/community/tutorials/how-to-create-a-self-signed-ssl-certificate-for-nginx-on-debian-10) -------------------------------------------------------------------------------- -## Apache +## Examples + +The following examples assume a Debian-based operating system is installed. On other distributions you may have to adapt details such as package installation procedures, configuration file locations, and webserver username/group (`www-data` or `httpd` are common values). + +In these examples we assume the document root for your web server/virtualhost is at `/var/www/shaarli.mydomain.org/`: + +```bash +sudo mkdir -p /var/www/shaarli.mydomain.org/ +``` + +You can install Shaarli at the root of your virtualhost, or in a subdirectory as well. See [Directory structure](Directory-structure) + -Here is a basic configuration example for the Apache web server with `mod_php`. +### Apache -In `/etc/apache2/sites-available/shaarli.conf`: +```bash +# Install apache + mod_php and PHP modules +sudo apt update +sudo apt install apache2 libapache2-mod-php php-json php-mbstring php-gd php-intl php-curl php-gettext + +# Edit the virtualhost configuration file with your favorite editor +sudo nano /etc/apache2/sites-available/shaarli.mydomain.org.conf +``` ```apache + + ServerName shaarli.mydomain.org + DocumentRoot /var/www/shaarli.mydomain.org/ + + # Log level. Possible values include: debug, info, notice, warn, error, crit, alert, emerg. + LogLevel warn + # Log file locations + ErrorLog /var/log/apache2/error.log + CustomLog /var/log/apache2/access.log combined + + # Redirect HTTP requests to HTTPS + RewriteEngine on + RewriteRule ^.well-known/acme-challenge/ - [L] + # except for Let's Encrypt ACME challenge requests + RewriteCond %{HTTP_HOST} =shaarli.mydomain.org + RewriteRule ^ https://shaarli.mydomain.org%{REQUEST_URI} [END,NE,R=permanent] + + - ServerName shaarli.my-domain.org - DocumentRoot /absolute/path/to/shaarli/ + ServerName shaarli.mydomain.org + DocumentRoot /var/www/shaarli.mydomain.org/ - # Logging - # Possible values include: debug, info, notice, warn, error, crit, alert, emerg. + # Log level. Possible values include: debug, info, notice, warn, error, crit, alert, emerg. LogLevel warn - ErrorLog /var/log/apache2/shaarli-error.log - CustomLog /var/log/apache2/shaarli-access.log combined + # Log file locations + ErrorLog /var/log/apache2/error.log + CustomLog /var/log/apache2/access.log combined - # Let's Encrypt SSL configuration (recommended) + # SSL/TLS configuration (for Let's Encrypt certificates) SSLEngine on - SSLCertificateFile /etc/letsencrypt/live/yourdomain.example.com/fullchain.pem - SSLCertificateKeyFile /etc/letsencrypt/live/yourdomain.example.com/privkey.pem + SSLCertificateFile /etc/letsencrypt/live/shaarli.mydomain.org/fullchain.pem + SSLCertificateKeyFile /etc/letsencrypt/live/shaarli.mydomain.org/privkey.pem Include /etc/letsencrypt/options-ssl-apache.conf - # Self-signed SSL cert configuration + # SSL/TLS configuration (for self-signed certificates) #SSLEngine on #SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem #SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key @@ -98,345 +158,259 @@ In `/etc/apache2/sites-available/shaarli.conf`: #php_value error_reporting 2147483647 #php_value error_log /var/log/apache2/shaarli-php-error.log - - #Required for .htaccess support + + # Required for .htaccess support AllowOverride All Order allow,deny Allow from all - - Options Indexes FollowSymLinks MultiViews #TODO is Indexes/Multiviews required? - - # Optional - required for playvideos plugin - #Header set Content-Security-Policy "script-src 'self' 'unsafe-inline' https://www.youtube.com https://s.ytimg.com 'unsafe-eval'" - -``` - -Enable this configuration with `sudo a2ensite shaarli` - -_Note: 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._ + + # Prevent accessing dotfiles + RedirectMatch 404 ".*" + -_Note: Apache module `mod_rewrite` must be enabled to use the REST API._ + + # allow client-side caching of static files + Header set Cache-Control "max-age=2628000, public, must-revalidate, proxy-revalidate" + + # serve the Shaarli favicon from its custom location + Alias favicon.ico /var/www/shaarli.mydomain.org/images/favicon.ico -## Nginx + +``` -Here is a basic configuration example for the Nginx web server, using the [php-fpm](http://php-fpm.org) PHP FastCGI Process Manager, and Nginx's [FastCGI](https://en.wikipedia.org/wiki/FastCGI) module. +```bash +# Enable the virtualhost +sudo a2ensite shaarli - +# mod_ssl must be enabled to use TLS/SSL certificates +# https://httpd.apache.org/docs/current/mod/mod_ssl.html +sudo a2enmod ssl -### Common setup -Once Nginx and PHP-FPM are installed, we need to ensure: +# mod_rewrite must be enabled to use the REST API +# https://httpd.apache.org/docs/current/mod/mod_rewrite.html +sudo a2enmod rewrite -- Nginx and PHP-FPM are running using the _same user and group_ -- both these user and group have - - `read` permissions for Shaarli resources - - `execute` permissions for Shaarli directories _AND_ their parent directories +# mod_version must only be enabled if you use Apache 2.2 or lower +# https://httpd.apache.org/docs/current/mod/mod_version.html +# sudo a2enmod version -On a production server: +# restart the apache service +systemctl restart apache +``` -- `user:group` will likely be `http:http`, `www:www` or `www-data:www-data` -- files will be located under `/var/www`, `/var/http` or `/usr/share/nginx` +See [How to install the Apache web server](https://www.digitalocean.com/community/tutorials/how-to-install-the-apache-web-server-on-debian-10) for a complete guide. -On a development server: +### Nginx -- files may be located in a user's home directory -- in this case, make sure both Nginx and PHP-FPM are running as the local user/group! +Guide on setting up the Nginx web server: [How to install the Nginx web server](https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-debian-10) -For all following configuration examples, this user/group pair will be used: +You will also need to install the [PHP-FPM](http://php-fpm.org) interpreter as detailed [here](https://www.digitalocean.com/community/tutorials/how-to-install-linux-nginx-mariadb-php-lemp-stack-on-debian-10#step-3-%E2%80%94-installing-php-for-processing). Nginx and PHP-FPM must be running using the same user and group, here we assume the user/group to be `www-data:www-data` but this may vary depending on your Linux distribution. -- `user:group = john:users`, -which corresponds to the following service configuration: +```bash +# install nginx and php-fpm +sudo apt update +sudo apt install nginx php-fpm -```ini -; /etc/php/php-fpm.conf -user = john -group = users - -[...] -listen.owner = john -listen.group = users +# Edit the virtualhost configuration file with your favorite editor +sudo nano /etc/nginx/sites-available/shaarli.mydomain.org ``` ```nginx -# /etc/nginx/nginx.conf -user john users; +server { + listen 80; + server_name shaarli.mydomain.org; -http { - [...] + # redirect all plain HTTP requests to HTTPS + return 301 https://shaarli.mydomain.org$request_uri; } -``` -### (Optional) Increase the maximum file upload size -Some bookmark dumps generated by web browsers can be _huge_ due to the presence of Base64-encoded images and favicons, as well as extra verbosity when nesting links in (sub-)folders. +server { + listen 443 ssl; + server_name shaarli.mydomain.org; + root /var/www/shaarli.mydomain.org; -To increase upload size, you will need to modify both nginx and PHP configuration: - -```nginx -# /etc/nginx/nginx.conf - -http { - [...] - - client_max_body_size 10m; - - [...] -} -``` - -```ini -# /etc/php//fpm/php.ini - -[...] -post_max_size = 10M -[...] -upload_max_filesize = 10M -``` + # log file locations + # combined log format prepends the virtualhost/domain name to log entries + access_log /var/log/nginx/access.log combined; + error_log /var/log/nginx/error.log; -### Minimal -_WARNING: Use for development only!_ + # paths to private key and certificates for SSL/TLS + ssl_certificate /etc/ssl/shaarli.mydomain.org.crt; + ssl_certificate_key /etc/ssl/private/shaarli.mydomain.org.key; -```nginx -user john users; -worker_processes 1; -events { - worker_connections 1024; -} + # increase the maximum file upload size if needed: by default nginx limits file upload to 1MB (413 Entity Too Large error) + client_max_body_size 100m; -http { - include mime.types; - default_type application/octet-stream; - keepalive_timeout 20; - - index index.html index.php; - - server { - listen 80; - server_name localhost; - root /home/john/web; - - access_log /var/log/nginx/access.log; - 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; - } + # relative path to shaarli from the root of the webserver + location / { + # default index file when no file URI is requested + index index.php; + try_files $uri /index.php$is_args$args; } -} -``` -### Modular -The previous setup is sufficient for development purposes, but has several major caveats: + location ~ (index)\.php$ { + try_files $uri =404; + # slim API - split URL path into (script_filename, path_info) + fastcgi_split_path_info ^(.+\.php)(/.+)$; + # pass PHP requests to PHP-FPM + fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock; + fastcgi_index index.php; + include fastcgi.conf; + } -- every content that does not match the PHP rule will be sent to client browsers: - - dotfiles - in our case, `.htaccess` - - temporary files, e.g. Vim or Emacs files: `index.php~` -- asset / static resource caching is not optimized -- if serving several PHP sites, there will be a lot of duplication: `location /shaarli/`, `location /mysite/`, etc. + location ~ \.php$ { + # deny access to all other PHP scripts + # disable this if you host other PHP applications on the same virtualhost + deny all; + } -To solve this, we will split Nginx configuration in several parts, that will be included when needed: + location ~ /\. { + # deny access to dotfiles + deny all; + } -```nginx -# /etc/nginx/deny.conf -location ~ /\. { - # deny access to dotfiles - access_log off; - log_not_found off; - deny all; -} + location ~ ~$ { + # deny access to temp editor files, e.g. "script.php~" + deny all; + } -location ~ ~$ { - # deny access to temp editor files, e.g. "script.php~" - access_log off; - log_not_found off; - deny all; -} -``` + location = /favicon.ico { + # serve the Shaarli favicon from its custom location + alias /var/www/shaarli/images/favicon.ico; + } -```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; - include fastcgi.conf; -} + # allow client-side caching of static files + location ~* \.(?:ico|css|js|gif|jpe?g|png)$ { + expires max; + add_header Cache-Control "public, must-revalidate, proxy-revalidate"; + # HTTP 1.0 compatibility + add_header Pragma public; + } -location ~ \.php$ { - # deny access to all other PHP scripts - deny all; } ``` -```nginx -# /etc/nginx/static_assets.conf -location ~* \.(?:ico|css|js|gif|jpe?g|png)$ { - expires max; - add_header Pragma public; - add_header Cache-Control "public, must-revalidate, proxy-revalidate"; -} +```bash +# enable the configuration/virtualhost +sudo ln -s /etc/nginx/sites-available/shaarli.mydomain.org /etc/nginx/sites-enabled/shaarli.mydomain.org +# reload nginx configuration +sudo systemctl reload nginx ``` -```nginx -# /etc/nginx/nginx.conf -[...] - -http { - [...] - - root /home/john/web; - access_log /var/log/nginx/access.log; - error_log /var/log/nginx/error.log; - server { - # virtual host for a first domain - listen 80; - server_name my.first.domain.org; +## Reverse proxies - location /shaarli/ { - # Slim - rewrite URLs - try_files $uri /shaarli/index.php$is_args$args; +If Shaarli is hosted on a server behind a [reverse proxy](https://en.wikipedia.org/wiki/Reverse_proxy) (i.e. there is a proxy server between clients and the web server hosting Shaarli), configure it accordingly. See [Reverse proxy](Reverse-proxy.md) configuration. - access_log /var/log/nginx/shaarli.access.log; - error_log /var/log/nginx/shaarli.error.log; - } - location = /shaarli/favicon.ico { - # serve the Shaarli favicon from its custom location - alias /var/www/shaarli/images/favicon.ico; - } - include deny.conf; - include static_assets.conf; - include php.conf; - } +## Allow import of large browser bookmarks export - server { - # virtual host for a second domain - listen 80; - server_name second.domain.com; +Web browser bookmark exports can be large due to the presence of base64-encoded images and favicons/long subfolder names. Edit the PHP configuration file. - location /minigal/ { - access_log /var/log/nginx/minigal.access.log; - error_log /var/log/nginx/minigal.error.log; - } +- Apache: `/etc/php//apache2/php.ini` +- Nginx + PHP-FPM: `/etc/php//fpm/php.ini` (in addition to `client_max_body_size` in the [Nginx configuration](#nginx)) - include deny.conf; - include static_assets.conf; - include php.conf; - } -} -``` - -### Redirect HTTP to HTTPS -Assuming you have generated a (self-signed) key and certificate, and they are -located under `/home/john/ssl/localhost.{key,crt}`, it is pretty straightforward -to set an HTTP (:80) to HTTPS (:443) redirection to force SSL/TLS usage. - -```nginx -# /etc/nginx/nginx.conf +```ini [...] +# (optional) increase the maximum file upload size: +post_max_size = 100M +[...] +# (optional) increase the maximum file upload size: +upload_max_filesize = 100M +``` -http { - [...] - - index index.html index.php; - - root /home/john/web; - access_log /var/log/nginx/access.log; - error_log /var/log/nginx/error.log; - - server { - listen 80; - server_name localhost; +To verify PHP settings currently set on the server, create a `phpinfo.php` in your webserver's document root - return 301 https://localhost$request_uri; - } +```bash +# example +echo '' | sudo tee /var/www/shaarli.mydomain.org/phpinfo.php +#give read-only access to this file to the webserver user +sudo chown www-data:root /var/www/shaarli.mydomain.org/phpinfo.php +sudo chmod 0400 /var/www/shaarli.mydomain.org/phpinfo.php +``` - server { - listen 443 ssl; - server_name localhost; +Access the file from a web browser (eg. and look at the _Loaded Configuration File_ and _Scan this dir for additional .ini files_ entries - ssl_certificate /home/john/ssl/localhost.crt; - ssl_certificate_key /home/john/ssl/localhost.key; +It is recommended to remove the `phpinfo.php` when no longer needed as it publicly discloses details about your webserver configuration. - 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; - } +## Robots and crawlers - location = /shaarli/favicon.ico { - # serve the Shaarli favicon from its custom location - alias /var/www/shaarli/images/favicon.ico; - } +To opt-out of indexing your Shaarli instance by search engines, create a `robots.txt` file at the root of your virtualhost: - include deny.conf; - include static_assets.conf; - include php.conf; - } -} +``` +User-agent: * +Disallow: / ``` -## Proxies - -If Shaarli is served behind a proxy (i.e. there is a proxy server between clients and the web server hosting Shaarli), please refer to the proxy server documentation for proper configuration. In particular, you have to ensure that the following server variables are properly set: +By default Shaarli already disallows indexing of your local copy of the documentation by default, using `` HTML tags. Your Shaarli instance may still be indexed by various robots on the public Internet, that do not respect this header or the robots standard. -- `X-Forwarded-Proto` -- `X-Forwarded-Host` -- `X-Forwarded-For` +- [Robots exclusion standard](https://en.wikipedia.org/wiki/Robots_exclusion_standard) +- [Introduction to robots.txt](https://support.google.com/webmasters/answer/6062608?hl=en) +- [Robots meta tag, data-nosnippet, and X-Robots-Tag specifications](https://developers.google.com/search/reference/robots_meta_tag) +- [About robots.txt](http://www.robotstxt.org) +- [About the robots META tag](https://www.robotstxt.org/meta.html) -In you [Shaarli configuration](Shaarli-configuration) `data/config.json.php`, add the public IP of your proxy under `security.trusted_proxies`. -See also [proxy-related](https://github.com/shaarli/Shaarli/issues?utf8=%E2%9C%93&q=label%3Aproxy+) issues. +## Fail2ban -## Robots and crawlers +[fail2ban](http://www.fail2ban.org/wiki/index.php/Main_Page) is an intrusion prevention framework that reads server (Apache, SSH, etc.) and uses `iptables` profiles to block brute-force attempts. You need to create a filter to detect shaarli login failures in logs, and a jail configuation to configure the behavior when failed login attempts are detected: -Shaarli disallows indexing and crawling of your local documentation pages by search engines, using `` HTML tags. -Your Shaarli instance and other pages you host may still be indexed by various robots on the public Internet. -You may want to setup a robots.txt file or other crawler control mechanism on your server. -See [[1]](https://en.wikipedia.org/wiki/Robots_exclusion_standard), [[2]](https://support.google.com/webmasters/answer/6062608?hl=en) and [[3]](https://developers.google.com/search/reference/robots_meta_tag) - -## See also +```ini +# /etc/fail2ban/filter.d/shaarli-auth.conf +[INCLUDES] +before = common.conf +[Definition] +failregex = \s-\s\s-\sLogin failed for user.*$ +ignoreregex = +``` - * [Server security](Server-security.md) +```ini +# /etc/fail2ban/jail.local +[shaarli-auth] +enabled = true +port = https,http +filter = shaarli-auth +logpath = /var/www/shaarli.mydomain.org/data/log.txt +# allow 3 login attempts per IP address +# (over a period specified by findtime = in /etc/fail2ban/jail.conf) +maxretry = 3 +# permanently ban the IP address after reaching the limit +bantime = -1 +``` -#### Webservers +#### References -- [Apache/PHP - error log per VirtualHost](http://stackoverflow.com/q/176) (StackOverflow) +- [Apache/PHP - error log per VirtualHost - StackOverflow](http://stackoverflow.com/q/176) - [Apache - PHP: php_value vs php_admin_value and the use of php_flag explained](https://ma.ttias.be/php-php_value-vs-php_admin_value-and-the-use-of-php_flag-explained/) -- [Server-side TLS (Apache)](https://wiki.mozilla.org/Security/Server_Side_TLS#Apache) (Mozilla) +- [Server-side TLS (Apache) - Mozilla](https://wiki.mozilla.org/Security/Server_Side_TLS#Apache) - [Nginx Beginner's guide](http://nginx.org/en/docs/beginners_guide.html) - [Nginx ngx_http_fastcgi_module](http://nginx.org/en/docs/http/ngx_http_fastcgi_module.html) - [Nginx Pitfalls](http://wiki.nginx.org/Pitfalls) -- [Nginx PHP configuration examples](http://kbeezie.com/nginx-configuration-examples/) (Karl Blessing) -- [Server-side TLS (Nginx)](https://wiki.mozilla.org/Security/Server_Side_TLS#Nginx) (Mozilla) +- [Nginx PHP configuration examples - Karl Blessing](http://kbeezie.com/nginx-configuration-examples/) +- [Apache 2.4 documentation](https://httpd.apache.org/docs/2.4/) +- [Apache mod_proxy](https://httpd.apache.org/docs/2.4/mod/mod_proxy.html) +- [Apache Reverse Proxy Request Headers](https://httpd.apache.org/docs/2.4/mod/mod_proxy.html#x-headers) +- [HAProxy documentation](https://cbonte.github.io/haproxy-dconv/) +- [Nginx documentation](https://nginx.org/en/docs/) +- [`X-Forwarded-Proto`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto) +- [`X-Forwarded-Host`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Host) +- [`X-Forwarded-For`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For) +- [Server-side TLS (Nginx) - Mozilla](https://wiki.mozilla.org/Security/Server_Side_TLS#Nginx) - [How to Create Self-Signed SSL Certificates with OpenSSL](http://www.xenocafe.com/tutorials/linux/centos/openssl/self_signed_certificates/index.php) - [How do I create my own Certificate Authority?](https://workaround.org/certificate-authority) - -#### PHP - - [Travis configuration](https://github.com/shaarli/Shaarli/blob/master/.travis.yml) - [PHP: Supported versions](http://php.net/supported-versions.php) -- [PHP: Unsupported versions](http://php.net/eol.php) _(EOL - End Of Life)_ +- [PHP: Unsupported versions (EOL/End-of-life)](http://php.net/eol.php) - [PHP 7 Changelog](http://php.net/ChangeLog-7.php) - [PHP 5 Changelog](http://php.net/ChangeLog-5.php) - [PHP: Bugs](https://bugs.php.net/) +- [Transport Layer Security](https://en.wikipedia.org/wiki/Transport_Layer_Security) +- Hosting providers: [DigitalOcean](https://www.digitalocean.com/) ([1](https://www.digitalocean.com/docs/droplets/overview/), [2](https://www.digitalocean.com/pricing/), [3](https://www.digitalocean.com/docs/droplets/how-to/create/), [How to Add SSH Keys to Droplets](https://www.digitalocean.com/docs/droplets/how-to/add-ssh-keys/), [4](https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-8), [5](https://www.digitalocean.com/community/tutorials/an-introduction-to-securing-your-linux-vps)), [Gandi](https://www.gandi.net/en), [OVH](https://www.ovh.co.uk/), [RackSpace](https://www.rackspace.com/), etc. + + diff --git a/doc/md/Server-security.md b/doc/md/Server-security.md deleted file mode 100644 index ea1b637d..00000000 --- a/doc/md/Server-security.md +++ /dev/null @@ -1,76 +0,0 @@ -## php.ini -PHP settings are defined in: - -- a main configuration file, usually found under `/etc/php/$php_version/php.ini`; some distributions provide different configuration environments, e.g. - - `/etc/php/$php_version/cli/php.ini` - used when running console scripts - - `/etc/php/$php_version/apache2/php.ini` - used when a client requests PHP resources from Apache - - `/etc/php/$php_version/php-fpm.conf` - used when PHP requests are proxied to PHP-FPM -- additional configuration files/entries, depending on the installed/enabled extensions: - - `/etc/php/conf.d/xdebug.ini` - -### Locate .ini files -#### Console environment -```bash -$ php --ini -Configuration File (php.ini) Path: /etc/php -Loaded Configuration File: /etc/php/php.ini -Scan for additional .ini files in: /etc/php/conf.d -Additional .ini files parsed: /etc/php/conf.d/xdebug.ini -``` - -#### Server environment -- create a `phpinfo.php` script located in a path supported by the web server, e.g. - - Apache (with user dirs enabled): `/home/myself/public_html/phpinfo.php` - - `/var/www/test/phpinfo.php` -- make sure the script is readable by the web server user/group (usually, `www`, `www-data` or `httpd`) -- access the script from a web browser -- look at the _Loaded Configuration File_ and _Scan this dir for additional .ini files_ entries -```php - -``` - -## fail2ban -`fail2ban` is an intrusion prevention framework that reads server (Apache, SSH, etc.) and uses `iptables` profiles to block brute-force attempts: - -- [Official website](http://www.fail2ban.org/wiki/index.php/Main_Page) -- [Source code](https://github.com/fail2ban/fail2ban) - -### Read Shaarli logs to ban IPs -Example configuration: -- allow 3 login attempts per IP address -- after 3 failures, permanently ban the corresponding IP adddress - -`/etc/fail2ban/jail.local` -```ini -[shaarli-auth] -enabled = true -port = https,http -filter = shaarli-auth -logpath = /var/www/path/to/shaarli/data/log.txt -maxretry = 3 -bantime = -1 -``` - -`/etc/fail2ban/filter.d/shaarli-auth.conf` -```ini -[INCLUDES] -before = common.conf -[Definition] -failregex = \s-\s\s-\sLogin failed for user.*$ -ignoreregex = -``` - -## Robots - Restricting search engines and web crawler traffic - -Creating a `robots.txt` with the following contents at the root of your Shaarli installation will prevent _honest_ web crawlers from indexing each and every link and Daily page from a Shaarli instance, thus getting rid of a certain amount of unsollicited network traffic. - -``` -User-agent: * -Disallow: / -``` - -See: - -- http://www.robotstxt.org -- http://www.robotstxt.org/robotstxt.html -- http://www.robotstxt.org/meta.html diff --git a/doc/md/Shaarli-configuration.md b/doc/md/Shaarli-configuration.md index 2462e20e..e93ee245 100644 --- a/doc/md/Shaarli-configuration.md +++ b/doc/md/Shaarli-configuration.md @@ -1,126 +1,19 @@ -## Foreword - -**Do not edit configuration options in index.php! Your changes would be lost.** +# Shaarli configuration Once your Shaarli instance is installed, the file `data/config.json.php` is generated: -* it contains all settings in JSON format, and can be edited to customize values -* it defines which [plugins](Plugin-System) are enabled -* its values override those defined in `index.php` -* it is wrap in a PHP comment to prevent anyone accessing it, regardless of server configuration - -## File and directory permissions - -The server process running Shaarli must have: - -- `read` access to the following resources: - - PHP scripts: `index.php`, `application/*.php`, `plugins/*.php` - - 3rd party PHP and Javascript libraries: `inc/*.php`, `inc/*.js` - - static assets: - - CSS stylesheets: `inc/*.css` - - `images/*` - - RainTPL templates: `tpl/*.html` -- `read`, `write` and `execution` access to the following directories: - - `cache` - thumbnail cache - - `data` - link data store, configuration options - - `pagecache` - Atom/RSS feed cache - - `tmp` - RainTPL page cache - -On a Linux distribution: - -- the web server user will likely be `www` or `http` (for Apache2) -- it will be a member of a group of the same name: `www:www`, `http:http` -- to give it access to Shaarli, either: - - unzip Shaarli in the default web server location (usually `/var/www/`) and set the web server user as the owner - - put users in the same group as the web server, and set the appropriate access rights -- if you have a domain / subdomain to serve Shaarli, [configure the server](Server-configuration) accordingly - -## Configuration - -In `data/config.json.php`. - -See also [Plugin System](Plugin-System). - -### Credentials - -_These settings should not be edited_ - -- **login**: Login username. -- **hash**: Generated password hash. -- **salt**: Password salt. - -### General - -- **title**: Shaarli's instance title. -- **header_link**: Link to the homepage. -- **links_per_page**: Number of shaares displayed per page. -- **timezone**: See [the list of supported timezones](http://php.net/manual/en/timezones.php). -- **enabled_plugins**: List of enabled plugins. -- **default_note_title**: Default title of a new note. -- **retrieve_description** (boolean): If set to true, for every new links Shaarli will try -to retrieve the description and keywords from the HTML meta tags. - -### Security - -- **session_protection_disabled**: Disable session cookie hijacking protection (not recommended). - It might be useful if your IP adress often changes. -- **ban_after**: Failed login attempts before being IP banned. -- **ban_duration**: IP ban duration in seconds. -- **open_shaarli**: Anyone can add a new link while logged out if enabled. -- **trusted_proxies**: List of trusted IP which won't be banned after failed login attemps. Useful if Shaarli is behind a reverse proxy. -- **allowed_protocols**: List of allowed protocols in shaare URLs or markdown-rendered descriptions. Useful if you want to store `javascript:` links (bookmarklets) in Shaarli (default: `["ftp", "ftps", "magnet"]`). - -### Resources - -- **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. -- **raintpl_tpl**: Templates directory. -- **raintpl_tmp**: Template engine cache directory. -- **thumbnails_cache**: Thumbnails cache directory. -- **page_cache**: Shaarli's internal cache directory. -- **ban_file**: Banned IP file path. -### Translation +- it contains all settings in JSON format, and can be edited to customize values +- it defines which [plugins](Plugins.md) are enabled +- its values override those defined in `index.php` +- it is wrapped in a PHP comment so that its contents are never served by the web server, regardless of configuration -- **language**: translation language (also see [Translations](Translations)) - - **auto** (default): The translation language is chosen from the browser locale. - It means that the language can be different for 2 different visitors depending on their locale. - - **en**: Use the English translation. - - **fr**: Use the French translation. -- **mode**: - - **auto** or **php** (default): Use the PHP implementation of gettext (slower) - - **gettext**: Use PHP builtin gettext extension - (faster, but requires `php-gettext` to be installed and to reload the web server on update) -- **extension**: Translation extensions for custom themes or plugins. -Must be an associative array: `translation domain => translation path`. - -### Updates - -- **check_updates**: Enable or disable update check to the git repository. -- **check_updates_branch**: Git branch used to check updates (e.g. `stable` or `master`). -- **check_updates_interval**: Look for new version every N seconds (default: every day). - -### Privacy - -- **default_private_links**: Check the private checkbox by default for every new link. -- **hide_public_links**: All links are hidden while logged out. -- **force_login**: if **hide_public_links** and this are set to `true`, all anonymous users are redirected to the login page. -- **hide_timestamps**: Timestamps are hidden. -- **remember_user_default**: Default state of the login page's *remember me* checkbox - - `true`: checked by default, `false`: unchecked by default - -### Feed +**Do not edit configuration options in index.php! Your changes would be lost.** -- **rss_permalinks**: Enable this to redirect RSS links to Shaarli's permalinks instead of shaared URL. -- **show_atom**: Display ATOM feed button. +## Tools menu -### Thumbnail +Some settings can be configured directly from a web browser by accesing the `Tools` menu. Values are read/written to/from the configuration file. -- **enable_thumbnails**: Enable or disable thumbnail display. -- **enable_localcache**: Enable or disable local cache. +![](https://i.imgur.com/boaaibC.png) ### LDAP @@ -236,9 +129,89 @@ Must be an associative array: `translation domain => translation path`. } ?> ``` -## Additional configuration +## Settings + +### Credentials + +_These settings should not be edited_ + +- **login**: Login username. +- **hash**: Generated password hash. +- **salt**: Password salt. + +### General + +- **title**: Shaarli's instance title. +- **header_link**: Link to the homepage. +- **links_per_page**: Number of Shaares displayed per page. +- **timezone**: See [the list of supported timezones](http://php.net/manual/en/timezones.php). +- **enabled_plugins**: List of enabled plugins. +- **default_note_title**: Default title of a new note. +- **retrieve_description** (boolean): If set to true, for every new Shaare Shaarli will try to retrieve the description and keywords from the HTML meta tags. + +### Security + +- **session_protection_disabled**: Disable session cookie hijacking protection (not recommended). + It might be useful if your IP adress often changes. +- **ban_after**: Failed login attempts before being IP banned. +- **ban_duration**: IP ban duration in seconds. +- **open_shaarli**: Anyone can add a new Shaare while logged out if enabled. +- **trusted_proxies**: List of trusted IP which won't be banned after failed login attemps. Useful if Shaarli is behind a reverse proxy. +- **allowed_protocols**: List of allowed protocols in shaare URLs or markdown-rendered descriptions. Useful if you want to store `javascript:` links (bookmarklets) in Shaarli (default: `["ftp", "ftps", "magnet"]`). + +### Resources + +- **data_dir**: Data directory. +- **datastore**: Shaarli's Shaares 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. +- **raintpl_tpl**: Templates directory. +- **raintpl_tmp**: Template engine cache directory. +- **thumbnails_cache**: Thumbnails cache directory. +- **page_cache**: Shaarli's internal cache directory. +- **ban_file**: Banned IP file path. + +### Translation + +- **language**: translation language (also see [Translations](Translations)) + - **auto** (default): The translation language is chosen from the browser locale. + It means that the language can be different for 2 different visitors depending on their locale. + - **en**: Use the English translation. + - **fr**: Use the French translation. +- **mode**: + - **auto** or **php** (default): Use the PHP implementation of gettext (slower) + - **gettext**: Use PHP builtin gettext extension + (faster, but requires `php-gettext` to be installed and to reload the web server on update) +- **extension**: Translation extensions for custom themes or plugins. +Must be an associative array: `translation domain => translation path`. + +### Updates + +- **check_updates**: Enable or disable update check to the git repository. +- **check_updates_branch**: Git branch used to check updates (e.g. `stable` or `master`). +- **check_updates_interval**: Look for new version every N seconds (default: every day). + +### Privacy + +- **default_private_links**: Check the private checkbox by default for every new Shaare. +- **hide_public_links**: All Shaares are hidden while logged out. +- **force_login**: if **hide_public_links** and this are set to `true`, all anonymous users are redirected to the login page. +- **hide_timestamps**: Timestamps are hidden. +- **remember_user_default**: Default state of the login page's *remember me* checkbox + - `true`: checked by default, `false`: unchecked by default + +### Feed + +- **rss_permalinks**: Enable this to redirect RSS links to Shaarli's permalinks instead of shaared URL. +- **show_atom**: Display ATOM feed button. + +### Thumbnail + +- **enable_thumbnails**: Enable or disable thumbnail display. +- **enable_localcache**: Enable or disable local cache. -The `playvideos` plugin may require that you adapt your server's -[Content Security Policy](https://github.com/shaarli/Shaarli/blob/master/plugins/playvideos/README.md#troubleshooting) -configuration to work properly. +## Plugins configuration +See [Plugins](Plugins.md) \ No newline at end of file diff --git a/doc/md/Sharing-content.md b/doc/md/Sharing-content.md deleted file mode 100644 index 9a16fc62..00000000 --- a/doc/md/Sharing-content.md +++ /dev/null @@ -1,71 +0,0 @@ -Content posted to Shaarli is separated in items called _Shaares_. For each Shaare, -you can customize the following aspects: - - * URL to link to - * Title - * Free-text description - * Tags - * Public/private status - --------------------------------------------------------------------------------- - -## Adding new Shaares - -While logged in to your Shaarli, you can add new Shaares in several ways: - - * [+Shaare button](#shaare-button) - * [Bookmarklet](#bookmarklet) - * Third-party [apps and browser addons](Community-&-Related-software.md#mobile-apps) - * [REST API](https://shaarli.github.io/api-documentation/) - -### +Shaare button - - * While logged in to your Shaarli, click the **`+Shaare`** button located in the toolbar. - * Enter the URL of a link you want to share. - * Click `Add link` - * The `New Shaare` dialog appears, allowing you to fill in the details of your Shaare. - * The Description, Title, and Tags will help you find your Shaare later using tags or full-text search. - * You can also check the “Private” box so that the link is saved but only visible to you (the logged-in user). - * Click `Save`. - - - -### Bookmarklet - -The _Bookmarklet_ \[[1](https://en.wikipedia.org/wiki/Bookmarklet)\] is a special -browser bookmark you can use to add new content to your Shaarli. This bookmarklet is -compatible with Firefox, Opera, Chrome and Safari. To set it up: - - * Access the `Tools` page from the button in the toolbar. - * Drag the **`✚Shaare link` button** to your browser's bookmarks bar. - -Once this is done, you can shaare any URL you are visiting simply by clicking the -bookmarklet in your browser! The same `New Shaare` dialog as above is displayed. - -| Note | Websites which enforce Content Security Policy (CSP), such as github.com, disallow usage of bookmarklets. Unfortunately, there is nothing Shaarli can do about it. \[[1](https://github.com/shaarli/Shaarli/issues/196)]\ \[[2](https://bugzilla.mozilla.org/show_bug.cgi?id=866522)]\ \[[3](https://code.google.com/p/chromium/issues/detail?id=233903)]\ | -|---------|---------| - -| Note | Under Opera, you can't drag'n drop the button: You have to right-click on it and add a bookmark to your personal toolbar. | -|---------|---------| - -![](images/bookmarklet.png) - - --------------------------------------------------------------------------------- - -## Editing Shaares - -Any Shaare can edited by clicking its ![](images/edit_icon.png) `Edit` button. - -Editing a Shaare will not change it's permalink, each permalink always points to the -latest revision of a Shaare. - --------------------------------------------------------------------------------- - -## Using shaarli as a blog, notepad, pastebin... - -While adding or editing a link, leave the URL field blank to create a text-only -("note") post. This allows you to post any kind of text content, such as blog -articles, private or public notes, snippets... There is no character limit! You can -access your Shaare from its permalink. - diff --git a/doc/md/Static-analysis.md b/doc/md/Static-analysis.md deleted file mode 100644 index 29d98362..00000000 --- a/doc/md/Static-analysis.md +++ /dev/null @@ -1,13 +0,0 @@ -## WIP -This topic is currently being discussed here: - -- [Fix coding style (static analysis)](https://github.com/shaarli/Shaarli/issues/95) (#95) -- [Continuous Integration tools & features](https://github.com/shaarli/Shaarli/issues/130) (#130) - -### Usage -Static analysis tools can be installed with Composer, and used through Shaarli's [Makefile](https://github.com/shaarli/Shaarli/blob/master/Makefile). - -For an overview of the available features, see: - -- [Code quality: Makefile to run static code checkers](https://github.com/shaarli/Shaarli/pull/124) (#124) -- [Run PHPCS against different coding standards](https://github.com/shaarli/Shaarli/pull/276) (#276) diff --git a/doc/md/Troubleshooting.md b/doc/md/Troubleshooting.md index 01fd9840..3f75719d 100644 --- a/doc/md/Troubleshooting.md +++ b/doc/md/Troubleshooting.md @@ -1,5 +1,8 @@ # Troubleshooting +First of all, ensure that both the [web server](Server-configuration.md) and [Shaarli](Shaarli-configuration.md) are correctly configured. + + ## Login ### I forgot my password! @@ -8,22 +11,29 @@ Delete the file `data/config.json.php` and display the page again. You will be a ### I'm locked out - Login bruteforce protection -Login form is protected against brute force attacks: 4 failed logins will ban the IP address from login for 30 minutes. Banned IPs can still browse links. +Login form is protected against brute force attacks: 4 failed logins will ban the IP address from login for 30 minutes. Banned IPs can still browse Shaares. - To remove the current IP bans, delete the file `data/ipbans.php` - To list all login attempts, see `data/log.txt` (succesful/failed logins, bans/lifted bans) +-------------------------------------- + ## Browser issues ### Redirection issues (HTTP Referer) -Depending on its configuration and installed plugins, the browser may remove or alter (spoof) [HTTP referers](https://en.wikipedia.org/wiki/HTTP_referer), thus preventing Shaarli from properly redirecting between pages. Referer settings are available by browsing `about:config` and are documented [here](https://wiki.mozilla.org/Security/Referrer). `network.http.referer.spoofSource = true` in particular is known to break some functionality in Shaarli. +Shaarli relies on `HTTP_REFERER` for some functions (like redirects and clicking on tags). If you have disabled or altered/spoofed [HTTP referers](https://en.wikipedia.org/wiki/HTTP_referer) in your browser, some features of Shaarli may not work as expected (depending on configuration and installed plugins), notably redirections between pages. + +Firefox Referer settings are available by browsing `about:config` and are documented [here](https://wiki.mozilla.org/Security/Referrer). `network.http.referer.spoofSource = true` in particular is known to break some functionality in Shaarli. + ### Firefox, localhost and redirections `localhost` is not a proper Fully Qualified Domain Name (FQDN); if Firefox has been set up to spoof referers, or only accept requests from the same base domain/host, Shaarli redirections will not work properly. To solve this, assign a local domain to your host, e.g. `localhost.lan` in your [hosts file](https://en.wikipedia.org/wiki/Hosts_(file)) and browse Shaarli at http://localhost.lan/. +----------------------------------------- + ## Hosting problems ### Old PHP versions @@ -71,11 +81,108 @@ This can be caused by several things: - You may be using OperaTurbo or OperaMini, which use their own proxies which may change from time to time. - If you have another application on the same webserver where Shaarli is installed, these application may forcefully expire php sessions. + ### Old apache versions, Internal Server Error If you hosting provider only provides apache 2.2 and no support for `mod_version`, `.htaccess` files may cause 500 errors (Internal Server Error). See [this workaround](https://github.com/shaarli/Shaarli/issues/1196#issuecomment-412271085). -## Sessions do not seem to work correctly on your server + +### Sessions do not seem to work correctly on your server Follow the instructions in the error message. Make sure you are accessing shaarli via a direct IP address or a proper hostname. If you have **no dots** in the hostname (e.g. `localhost` or `http://my-webserver/shaarli/`), some browsers will not store cookies at all (this respects the [HTTP cookie specification](http://curl.haxx.se/rfc/cookie_spec.html)). +---------------------------------------------------------- + +## Upgrades + +### You must specify an integer as a key + +In `v0.8.1` we changed how Shaare 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 exist 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 + + + +-------------------------------------------------------- + +## Import/export + +### Importing shaarli data to Firefox + +- In Firefox, open the bookmark manager (`Bookmarks menu > Show all bookmarks` or `Ctrl+Shift+B`), select `Import and Backup > Import bookmarks in HTML format` +- Make sure the `Prepend note permalinks with this Shaarli instance's URL` box is checked when exporting, so that text-only/notes Shaares still point to the Shaarli instance you exported them from. +- Depending on the number of bookmarks, the import can take some time. + +You may be interested in these Firefox addons to manage bookmarks imported from Shaarli + +- [Bookmark Deduplicator](https://addons.mozilla.org/en-US/firefox/addon/bookmark-deduplicator/) - provides an easy way to deduplicate your bookmarks +- [TagSieve](https://addons.mozilla.org/en-US/firefox/addon/tagsieve/) - browse your bookmarks by their tags + +### Diigo + +If you export your bookmark from Diigo, make sure you use the Delicious export, not the Netscape export. (Their Netscape export is broken, and they don't seem to be interested in fixing it.) + +### Mister Wong + +See [this issue](https://github.com/sebsauvage/Shaarli/issues/146) for import tweaks. + +### SemanticScuttle + +To correctly import the tags from a [SemanticScuttle](http://semanticscuttle.sourceforge.net/) HTML export, edit the HTML file before importing and replace all occurences of `tags=` (lowercase) to `TAGS=` (uppercase). + +### Scuttle + +Shaarli cannot import data directly from [Scuttle](https://github.com/scronide/scuttle). + +However, you can use the third-party [scuttle-to-shaarli](https://github.com/q2apro/scuttle-to-shaarli) +tool to export the Scuttle database to the Netscape HTML format compatible with the Shaarli importer. + +### Refind.com + +You can use the third-party tool [Derefind](https://github.com/ShawnPConroy/Derefind) to convert refind.com bookmark exports to a format that can be imported into Shaarli. + + +------------------------------------------------------- + +## Other + +### The bookmarklet doesn't work + +Websites which enforce Content Security Policy (CSP), such as github.com, disallow usage of bookmarklets. Unfortunately, there is nothing Shaarli can do about it ([1](https://github.com/shaarli/Shaarli/issues/196), [2](https://bugzilla.mozilla.org/show_bug.cgi?id=866522), [3](https://code.google.com/p/chromium/issues/detail?id=233903). + +Under Opera, you can't drag'n drop the button: You have to right-click on it and add a bookmark to your personal toolbar. + + +### Changing the timestamp for a shaare + +- Look for `` in `tpl/editlink.tpl` (line 14) +- Replace `type="hidden"` with `type="text"` from this line +- A new date/time field becomes available in the edit/new Shaare dialog. +- You can set the timestamp manually by entering it in the format `YYYMMDD_HHMMS`. + + +------------------------------------------------------- + +## Support + +If the solutions above did not help, please: + +- Come and ask question on the [Gitter chat](https://gitter.im/shaarli/Shaarli) (also reachable via [IRC](https://irc.gitter.im/)) +- Search for [issues](https://github.com/shaarli/Shaarli/issues) and [Pull Requests](https://github.com/shaarli/Shaarli/pulls) + - if you find one that is related to the issue, feel free to comment and provide additional details (host/Shaarli setup...) + - check issues labeled [`feature`](https://github.com/shaarli/Shaarli/labels/feature), [`enhancement`](https://github.com/shaarli/Shaarli/labels/enhancement), and [`plugin`](https://github.com/shaarli/Shaarli/labels/plugin) if you would like a feature added to Shaarli. + - else, [open a new issue](https://github.com/shaarli/Shaarli/issues/new), and provide information about the problem: + - _what happens?_ - display glitches, invalid data, security flaws... + - _what is your configuration?_ - OS, server version, activated extensions, web browser... + - _is it reproducible?_ \ No newline at end of file diff --git a/doc/md/Unit-tests.md b/doc/md/Unit-tests.md deleted file mode 100644 index a9544656..00000000 --- a/doc/md/Unit-tests.md +++ /dev/null @@ -1,119 +0,0 @@ -The testing framework used is [PHPUnit](https://phpunit.de/); it can be installed with [Composer](https://getcomposer.org/), which is a dependency management tool. - -## Setup a testing environment - -### Install composer - -You can either use: - -- a system-wide version, e.g. installed through your distro's package manager (eg. `sudo apt install composer`) -- a local version, downloadable [here](https://getcomposer.org/download/). To update a local composer installation, run `php composer.phar self-update` - - -### Install Shaarli development dependencies - -```bash -$ cd /path/to/shaarli -$ composer install -``` - -### Install Xdebug - -Xdebug must be installed and enable for PHPUnit to generate coverage reports. See http://xdebug.org/docs/install. - -```bash -# for Debian-based distributions -$ aptitude install php-xdebug - -# for ArchLinux: -$ pacman -S xdebug -``` - -Then add the following line to `/etc/php//cli/php.ini`: - -```ini -zend_extension=xdebug.so -``` - -## Run unit tests - -Run `make test` and ensure tests return `OK`. If tests return failures, refer to PHPUnit messages and fix your code/tests accordingly. - -By default, PHPUnit will run all suitable tests found under the `tests` directory. Each test has 3 possible outcomes: - -- `.` - success -- `F` - failure: the test was run but its results are invalid - - the code does not behave as expected - - dependencies to external elements: globals, session, cache... -- `E` - error: something went wrong and the tested code has crashed - - typos in the code, or in the test code - - dependencies to missing external elements - -If Xdebug has been installed and activated, two coverage reports will be generated: - -- 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: - -```php -/** - * Netscape bookmark import - * @group WIP - */ -class BookmarkImportTest extends PHPUnit_Framework_TestCase -{ - [...] -} -``` - -To run all tests annotated with `@group WIP`: -```bash -$ vendor/bin/phpunit --group WIP tests/ -``` - -### Running tests inside Docker containers - -Test Dockerfiles are located under `tests/docker//Dockerfile`, -and can be used to build Docker images to run Shaarli test suites under common -Linux environments. - -Dockerfiles are provided for the following environments: - -- `alpine36` - [Alpine 3.6](https://www.alpinelinux.org/downloads/) -- `debian8` - [Debian 8 Jessie](https://www.debian.org/DebianJessie) (oldstable) -- `debian9` - [Debian 9 Stretch](https://wiki.debian.org/DebianStretch) (stable) -- `ubuntu16` - [Ubuntu 16.04 Xenial Xerus](http://releases.ubuntu.com/16.04/) (LTS) - -What's behind the curtains: - -- each image provides: - - a base Linux OS - - Shaarli PHP dependencies (OS packages) - - test PHP dependencies (OS packages) - - Composer -- the local workspace is mapped to the container's `/shaarli/` directory, -- the files are rsync'd so tests are run using a standard Linux user account - (running tests as `root` would bypass permission checks and may hide issues) -- the tests are run inside the container. - -To run tests inside a Docker container: - -```bash -# build the Debian 9 Docker image for unit tests -$ cd /path/to/shaarli -$ cd tests/docker/debian9 -$ docker build -t shaarli-test:debian9 . - -# install/update 3rd-party test dependencies -$ composer install --prefer-dist - -# run tests using the freshly built image -$ docker run -v $PWD:/shaarli shaarli-test:debian9 docker_test - -# run the full test campaign -$ docker run -v $PWD:/shaarli shaarli-test:debian9 docker_all_tests -``` diff --git a/doc/md/Upgrade-and-migration.md b/doc/md/Upgrade-and-migration.md index d5682a34..8b0db1f8 100644 --- a/doc/md/Upgrade-and-migration.md +++ b/doc/md/Upgrade-and-migration.md @@ -1,96 +1,85 @@ -## Preparation +# Upgrade and migration -### Note your current version +## 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 `shaarli_version.php` file. -### Backup your data -Shaarli stores all user data under the `data` directory: +## Backup your data -- `data/config.json.php` (or `data/config.php` for older Shaarli versions) - 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 +Shaarli stores all user data and [configuration](Shaarli-configuration.md) under the `data` directory. [Backup](Backup-and-restore.md) this repository _before_ upgrading Shaarli. You will need to restore it after the following upgrade steps. -See [Shaarli configuration](Shaarli-configuration) for more information about Shaarli resources. - -It is recommended to backup this repository _before_ starting updating/upgrading Shaarli: - -- users with SSH access: copy or archive the directory to a temporary location -- users with FTP access: download a local copy of your Shaarli installation using your favourite client - -### Migrating data from a previous installation - -As all user data is kept under `data`, this is the only directory you need to worry about when migrating to a new installation, which corresponds to the following steps: - -- backup the `data` directory -- install or update Shaarli: - - fresh installation - see [Download and Installation](Download-and-Installation) - - update - see the following sections -- check or restore the `data` directory - -## Recommended : Upgrading from release archives +```bash +sudo cp -r /var/www/shaarli.mydomain.org/data ~/shaarli-data-backup +``` -All tagged revisions can be downloaded as tarballs or ZIP archives from the [releases](https://github.com/shaarli/Shaarli/releases) page. +## Upgrading from ZIP archives -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) for `git` complete instructions. +If you installed Shaarli from a [release ZIP archive](Installation.md#from-release-zip): -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! +```bash +# Download the archive to the server, and extract it +cd ~ +wget https://github.com/shaarli/Shaarli/releases/download/v0.X.Y/shaarli-v0.X.Y-full.zip +unzip shaarli-v0.X.Y-full.zip + +# overwrite your Shaarli installation with the new release **All data will be lost, see _Backup your data_ above.** +sudo rsync -avP --delete Shaarli/ /var/www/shaarli.mydomain.org/ + +# restore file permissions as described on the installation page +sudo chown -R root:www-data /var/www/shaarli.mydomain.org +sudo chmod -R u=rwX /var/www/shaarli.mydomain.org +sudo chmod -R g+rX /var/www/shaarli.mydomain.org/{index.php,application/,plugins/,inc/} +sudo chmod -R g+rwX /var/www/shaarli.mydomain.org/{cache/,data/,pagecache/,tmp/} + +# restore backups of the data directory +sudo cp -r ~/shaarli-data-backup/* /var/www/shaarli.mydomain.org/data/ + +# If you use gettext mode for translations (not the default), reload your web server. +sudo systemctl restart apache2 +sudo systemctl restart nginx +``` -If you use translations in gettext mode - meaning you manually changed the default mode -, -reload your web server. +If you don't have shell access (eg. on shared hosting), backup the shaarli data directory, download the ZIP archive locally, extract it, upload it to the server using file transfer, and restore the data directory backup. -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) for more details). +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.md) for more details). -## Upgrading with Git -### Updating a community Shaarli +## Upgrading from Git -If you have installed Shaarli from the [community Git repository](Download#clone-with-git-recommended), simply [pull new changes](https://www.git-scm.com/docs/git-pull) from your local clone: +If you have installed Shaarli [from sources](Installation.md#from-sources): ```bash -$ cd /path/to/shaarli -$ git pull - -From github.com:shaarli/Shaarli - * branch master -> FETCH_HEAD -Updating ebd67c6..521f0e6 -Fast-forward - application/Url.php | 1 + - shaarli_version.php | 2 +- - tests/Url/UrlTest.php | 1 + - 3 files changed, 3 insertions(+), 1 deletion(-) -``` +# pull new changes from your local clone +cd /var/www/shaarli.mydomain.org/ +sudo git pull -Shaarli >= `v0.8.x`: install/update third-party PHP dependencies using [Composer](https://getcomposer.org/): +# update PHP dependencies (Shaarli >= v0.8) +sudo composer install --no-dev -```bash -$ composer install --no-dev +# update translations (Shaarli >= v0.9.2) +sudo make translate -Loading composer repositories with package information -Updating dependencies - - Installing shaarli/netscape-bookmark-parser (v1.0.1) - Downloading: 100% -``` +# If you use translations in gettext mode (not the default), reload your web server. +sudo systemctl reload apache +sudo systemctl reload nginx -Shaarli >= `v0.9.2` supports translations: +# update front-end dependencies (Shaarli >= v0.10.0) +sudo make build_frontend -```bash -$ make translate -``` +# restore file permissions as described on the installation page +sudo chown -R root:www-data /var/www/shaarli.mydomain.org +sudo chmod -R u=rwX /var/www/shaarli.mydomain.org +sudo chmod -R g+rX /var/www/shaarli.mydomain.org/{index.php,application/,plugins/,inc/} +sudo chmod -R g+rwX /var/www/shaarli.mydomain.org/{cache/,data/,pagecache/,tmp/} +``` -If you use translations in gettext mode, reload your web server. +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.md) for more details). -Shaarli >= `v0.10.0` manages its front-end dependencies with nodejs. You need to install -[yarn](https://yarnpkg.com/lang/en/docs/install/): +--------------------------------------------------------------- -```bash -$ make build_frontend -``` - -### Migrating and upgrading from Sebsauvage's repository +## Migrating and upgrading from Sebsauvage's repository If you have installed Shaarli from [Sebsauvage's original Git repository](https://github.com/sebsauvage/Shaarli), you can use [Git remotes](https://git-scm.com/book/en/v2/Git-Basics-Working-with-Remotes) to update your working copy. @@ -104,7 +93,7 @@ The following guide assumes that: - no versioned file has been locally modified - no untracked files are present -#### Step 0: show repository information +### Step 0: show repository information ```bash $ cd /path/to/shaarli @@ -122,7 +111,7 @@ Your branch is up-to-date with 'origin/master'. nothing to commit, working directory clean ``` -#### Step 1: update Git remotes +### Step 1: update Git remotes ``` $ git remote rename origin sebsauvage @@ -146,7 +135,7 @@ From https://github.com/shaarli/Shaarli * [new tag] v0.7.0 -> v0.7.0 ``` -#### Step 2: use the stable community branch +### Step 2: use the stable community branch ```bash $ git checkout origin/stable -b stable @@ -177,8 +166,7 @@ $ make translate If you use translations in gettext mode, reload your web server. -Shaarli >= `v0.10.0` manages its front-end dependencies with nodejs. You need to install -[yarn](https://yarnpkg.com/lang/en/docs/install/): +Shaarli >= `v0.10.0` manages its front-end dependencies with nodejs. You need to install [yarn](https://yarnpkg.com/lang/en/docs/install/): ```bash $ make build_frontend @@ -204,30 +192,14 @@ Writing objects: 100% (3317/3317), done. Total 3317 (delta 2050), reused 3301 (delta 2034)to ``` -#### Step 3: configuration +### 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.json.php` (see [Shaarli configuration](Shaarli-configuration) for more +`data/config.json.php` (see [Shaarli configuration](Shaarli-configuration.md) for more details). ## Troubleshooting -If the solutions provided here don'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 exist or doesn't contain `updateMethodDatastoreIds` +If the solutions provided here don't work, see [Troubleshooting](Troubleshooting.md) and/or open an issue specifying which version you're upgrading from and to. -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 diff --git a/doc/md/Usage.md b/doc/md/Usage.md new file mode 100644 index 00000000..0a1b9719 --- /dev/null +++ b/doc/md/Usage.md @@ -0,0 +1,109 @@ +## Features + +For any item posted to Shaarli (called a _Shaare_), you can customize the following aspects: + +- URL to link to +- Title +- Free-text description +- Tags +- Public/private status + + +### Adding/editing Shaares + +While logged in to your Shaarli, you can add, edit or delete Shaares: + +- Using the **+Shaare** button: enter the URL you want to share, click `Add link`, fill in the details of your Shaare, and `Save` +- Using the [Bookmarklet](https://en.wikipedia.org/wiki/Bookmarklet): drag the `✚Shaare link` button from the `Tools` page to your browser's bookmarks bar, click it to share the current page. +- Using [apps and browser addons](Community-and-related-software.md#mobile-apps) +- Using the [REST API](https://shaarli.github.io/api-documentation/) +- Any Shaare can edited by clicking its ![](images/edit_icon.png) `Edit` button. + + +### Tags + +Tags can be be used to organize and categorize your Shaares: + +- You can rename, merge and delete tags from the _Tools_ menu or the [tag cloud/list](#tag-cloud) +- Tags are auto-completed (from the list of existing tags) in all dialogs +- Tags can be combined with text in [search](#search) queries + + +### Public/private Shaares + +Additional filter buttons can be found at the top left of the Shaare list **only when logged in**: + +- **Only show private Shaares:** Private shares can be searched by clicking the `only show private links` toggle button top left of the Shaares list (only when logged in) + + +### Permalinks + +Permalinks are fixed, short links attached to each Shaare. Editing a Shaare will not change it's permalink, each permalink always points to the latest revision of a Shaare. + + +### Text-only (note) Shaares + +Shaarli can be used as a minimal blog, notepad, pastebin...: While adding or editing a Shaare, leave the URL field blank to create a text-only ("note") post. This allows you to post any kind of text content, such as blog articles, private or public notes, snippets... There is no character limit! You can access your post from its permalink. + + +### Search + +- **Plain text search:** Use `Search text` to search in all fields of all Shaares (Title, URL, Description...). Use double-quotes (example `"exact search"`) to search for the exact expression. +- **Tags search:** `Filter by tags` allow only displaying Shaares tagged with one or multiple tags (use space to separate tags). +- **Hidden tags:** tags starting with a dot `.` (example `.secret`) are private. They can only be seen and searched when logged in. +- **Exclude text/tags:** Use the `-` operator before a word or tag to exclude Shaares matching this word from search results (`NOT` operator). +- **Untagged links:** Shaares without tags can be searched by clicking the `untagged` toggle button top left of the Shaares list (only when logged in). + + +Both exclude patterns and exact searches can be combined with normal searches (example `"exact search" term otherterm -notthis "very exact" stuff -notagain`). Only AND (and NOT) search is currrently supported. + + +### Tag cloud + +The `Tag cloud` page diplays a "cloud" or list view of all tags in your Shaarli (most frequently used tags are displayed with a bigger font size) + + +- **Tags list:** click on `Most used` or `Alphabetical` to display tags as a list. You can also edit/delete tags for this page. +- Click on any tag to search all Shaares matching this tag. +- **Filtering the tag cloud/list:** Click on the counter next to a tag to show other tags of Shaares with this tag. Repeat this any number of times to further filter the tag cloud. Click `List all links with those tags` to display Shaares matching your current tag filter set. + + + +### RSS feeds + +RSS/ATOM feeds feeds are available (in ATOM with `/feed/atom` and RSS with `/feed/rss`) + +- **Filtering RSS feeds:** RSS feeds and picture wall can also be restricted to only return items matching a text/tag search. For example, search for `photography` (text or tags) in Shaarli, then click the `RSS Feed` button. A feed with only matching results is displayed. +- Add the `&nb` parameter in feed URLs to specify the number of Shaares you want in a feed (default if not specified: `50`). The keyword `all` is available if you want everything. +- Add the `&permalinks` parameter in feed URLs to point permalinks to the corresponding shaarly entry/link instead of the direct, Shaare URL attribute + +![](images/rss-filter-1.png) ![](images/rss-filter-2.png) + +```bash +# examples +https://shaarli.mydomain.org/feed/atom?permalinks +https://shaarli.mydomain.org/feed/atom?permalinks&nb=42 +https://shaarli.mydomain.org/feed/atom?permalinks&nb=all +https://shaarli.mydomain.org/feed/rss?searchtags=nature +https://shaarli.mydomain.org/links/picture-wall?searchterm=poney +``` + + +### Picture wall + +- The picture wall can be filtered by text or tags search in the same way as [RSS feeds](#rss-feeds) + + +### Import/export + +To **export Shaares as a HTML file**, under _Tools > Export_, choose: + +- `Export all` to export both public and private Shaares +- `Export public` to export public Shaares only +- `Export private` to export private Shaares only + +Restore by using the `Import` feature. + +- These exports contain the full data (URL, title, tags, date, description, public/private status of your Shaares) +- They can also be imported to your web browser bookmarks. + diff --git a/doc/md/dev/Development.md b/doc/md/dev/Development.md new file mode 100644 index 00000000..5c085e03 --- /dev/null +++ b/doc/md/dev/Development.md @@ -0,0 +1,179 @@ +# Development + +Please read [Contributing to Shaarli](https://github.com/shaarli/Shaarli/tree/master/CONTRIBUTING.md) + +## Guidelines + + +- [Unit tests](Unit-tests) +- Javascript linting - Shaarli uses [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript). +Run `make eslint` to check JS style. +- [GnuPG signature](GnuPG-signature) for tags/releases + + +## Third-party libraries + +CSS: + +- Yahoo UI [CSS Reset](http://yuilibrary.com/yui/docs/cssreset/) - standardize cross-browser rendering + +Javascript: + +- [Awesomeplete](https://leaverou.github.io/awesomplete/) ([GitHub](https://github.com/LeaVerou/awesomplete)) - autocompletion in input forms +- [bLazy](http://dinbror.dk/blazy/) ([GitHub](https://github.com/dinbror/blazy)) - lazy loading for thumbnails +- [qr.js](http://neocotic.com/qr.js/) ([GitHub](https://github.com/neocotic/qr.js)) - QR code generation + +PHP (managed through [`composer.json`](https://github.com/shaarli/Shaarli/blob/master/composer.json)): + +- [RainTPL](https://github.com/rainphp/raintpl) - HTML templating for PHP +- [`shaarli/netscape-bookmark-parser`](https://packagist.org/packages/shaarli/netscape-bookmark-parser) - Import bookmarks from Netscape files +- [`erusev/parsedown`](https://packagist.org/packages/erusev/parsedown) - Parse MarkDown syntax for the MarkDown plugin +- [`slim/slim`](https://packagist.org/packages/slim/slim) - Handle routes and middleware for the REST API +- [`ArthurHoaro/web-thumbnailer`](https://github.com/ArthurHoaro/web-thumbnailer) - PHP library which will retrieve a thumbnail for any given URL +- [`pubsubhubbub/publisher`](https://github.com/pubsubhubbub/php-publisher) - A PubSubHubbub publisher module for PHP. +- [`gettext/gettext`](https://github.com/php-gettext/Gettext) - PHP library to collect and manipulate gettext (.po, .mo, .php, .json, etc) + + +## Security + +- The password is salted, hashed and stored in the data subdirectory, in a PHP file, and protected by htaccess. Even if the webserver does not support htaccess, the hash is not readable by URL. Even if the .php file is stolen, the password cannot deduced from the hash. The salt prevents rainbow-tables attacks. +- Directories are protected using `.htaccess` files +- Forms are protected against [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery): + - Forms which act on data (save,delete…) contain a token generated by the server. + - Any posted form which does not contain a valid token is rejected. + - Any token can only be used once. + - Tokens are attached to the session and cannot be reused in another session. +- Sessions automatically expire after 60 minutes. +- Sessions are protected against hijacking: the session ID cannot be used from a different IP address. +- Links are stored as an associative array which is serialized, compressed (with deflate), base64-encoded and saved as a comment in a `.php` file - even if the server does not support `.htaccess` files, the data file will still not be readable by URL. +- Bruteforce protection: Successful and failed login attempts are logged - IP bans are enforced after a configurable amount of failures. Logs can also be used consumed by [fail2ban](../Server-configuration.md#fail2ban) +- A pop-up notification is shown when a new release is available. + +## Link structure + +Every link available through the `LinkDB` object is represented as an array +containing the following fields: + + * `id` (integer): Unique identifier. + * `title` (string): Title of the link. + * `url` (string): URL of the link. Used for displayable links (without redirector, url encoding, etc.). + Can be absolute or relative for Notes. + * `real_url` (string): Real destination URL, can be redirected, encoded, etc. + * `shorturl` (string): Permalink small hash. + * `description` (string): Link text description. + * `private` (boolean): whether the link is private or not. + * `tags` (string): all link tags separated by a single space + * `thumbnail` (string|boolean): relative path of the thumbnail cache file, or false if there isn't any. + * `created` (DateTime): link creation date time. + * `updated` (DateTime): last modification date time. + +Small hashes are used to make a link to an entry in Shaarli. They are unique: the date of the item (eg. `20110923_150523`) is hashed with CRC32, then converted to base64 and some characters are replaced. They are always 6 characters longs and use only `A-Z a-z 0-9 - _` and `@`. + + +## Directory structure + +Here is the directory structure of Shaarli and the purpose of the different files: + +```bash + index.php # Main program + application/ # Shaarli classes + ├── LinkDB.php + + ... + + └── Utils.php + tests/ # Shaarli unitary & functional tests + ├── LinkDBTest.php + + ... + + ├── utils # utilities to ease testing + │ └── ReferenceLinkDB.php + └── UtilsTest.php + assets/ + ├── common/ # Assets shared by multiple themes + ├── ... + ├── default/ # Assets for the default template, before compilation + ├── fonts/ # Font files + ├── img/ # Images used by the default theme + ├── js/ # JavaScript files in ES6 syntax + ├── scss/ # SASS files + └── vintage/ # Assets for the vintage template, before compilation + └── ... + COPYING # Shaarli license + inc/ # static assets and 3rd party libraries + └── rain.tpl.class.php # RainTPL templating library + images/ # Images and icons used in Shaarli + data/ # data storage: bookmark database, configuration, logs, banlist... + ├── config.json.php # Shaarli configuration (login, password, timezone, title...) + ├── datastore.php # Your link database (compressed). + ├── ipban.php # IP address ban system data + ├── lastupdatecheck.txt # Update check timestamp file + └── log.txt # login/IPban log. + tpl/ # RainTPL templates for Shaarli. They are used to build the pages. + ├── default/ # Default Shaarli theme + ├── fonts/ # Font files + ├── img/ # Images + ├── js/ # JavaScript files compiled by Babel and compatible with all browsers + ├── css/ # CSS files compiled with SASS + └── vintage/ # Legacy Shaarli theme + └── ... + cache/ # thumbnails cache + # This directory is automatically created. You can erase it anytime you want. + tmp/ # Temporary directory for compiled RainTPL templates. + # This directory is automatically created. You can erase it anytime you want. + vendor/ # Third-party dependencies. This directory is created by Composer +``` + +Shaarli needs read access to: + +- the root index.php file +- the `application/`, `plugins/` and `inc/` directories (recursively) + +Shaarli needs read/write access to the `cache/`, `data/`, `pagecache/`, and `tmp/` directories + + +## Automation + +A [`Makefile`](https://github.com/shaarli/Shaarli/blob/master/Makefile) is available to perform project-related operations: + +- [Static analysis](#Static-analysis) - check that the code is compliant to PHP conventions +- [Unit tests](#Unit-tests) - ensure there are no regressions introduced by new commits +- Documentation - generate a local HTML copy of the markdown documentation + +### Continuous Integration + +[Travis CI](http://docs.travis-ci.com/) is a Continuous Integration build server, that runs a build: + +- each time a commit is merged to the mainline (`master` branch) +- each time a Pull Request is submitted or updated + +After all jobs have finished, Travis returns the results to GitHub: + +- a status icon represents the result for the `master` branch: [![](https://api.travis-ci.org/shaarli/Shaarli.svg)](https://travis-ci.org/shaarli/Shaarli) +- Pull Requests are updated with the Travis build result. + +See [`.travis.yml`](https://github.com/shaarli/Shaarli/blob/master/.travis.yml). + + +### Documentation + +[mkdocs](https://www.mkdocs.org/) is used to convert markdown documentation to HTML pages. The [public documentation](https://shaarli.readthedocs.io/en/master/) website is rendered and hosted by [readthedocs.org](https://readthedocs.org/). A copy of the documentation is also included in prebuilt [release archives](https://github.com/shaarli/Shaarli/releases) (`doc/html/` path in your Shaarli installation). To generate the HTML documentation locally, install a recent version of Python `setuptools` and run `make doc`. + + +## Static analysis + +Patches should try to stick to the [PHP Standard Recommendations](http://www.php-fig.org/psr/) (PSR), especially: + +- [PSR-1](http://www.php-fig.org/psr/psr-1/) - Basic Coding Standard +- [PSR-2](http://www.php-fig.org/psr/psr-2/) - Coding Style Guide + + +**Work in progress:** Static analysis is currently being discussed here: in [#95 - Fix coding style (static analysis)](https://github.com/shaarli/Shaarli/issues/95), [#130 - Continuous Integration tools & features](https://github.com/shaarli/Shaarli/issues/130) + +Static analysis tools can be installed with Composer, and used through Shaarli's [Makefile](https://github.com/shaarli/Shaarli/blob/master/Makefile). + +For an overview of the available features, see: + +- [Code quality: Makefile to run static code checkers](https://github.com/shaarli/Shaarli/pull/124) (#124) +- [Run PHPCS against different coding standards](https://github.com/shaarli/Shaarli/pull/276) (#276) diff --git a/doc/md/GnuPG-signature.md b/doc/md/dev/GnuPG-signature.md similarity index 66% rename from doc/md/GnuPG-signature.md rename to doc/md/dev/GnuPG-signature.md index d1fc10a5..25578001 100644 --- a/doc/md/GnuPG-signature.md +++ b/doc/md/dev/GnuPG-signature.md @@ -1,24 +1,16 @@ ## Introduction ### PGP and GPG -[Gnu Privacy Guard](https://gnupg.org/) (GnuPG) is an Open Source implementation of the -[Pretty Good Privacy](https://en.wikipedia.org/wiki/Pretty_Good_Privacy#OpenPGP) -(OpenPGP) specification. Its main purposes are digital authentication, signature and encryption. +[Gnu Privacy Guard](https://gnupg.org/) (GnuPG) is an Open Source implementation of the [Pretty Good Privacy](https://en.wikipedia.org/wiki/Pretty_Good_Privacy#OpenPGP) (OpenPGP) specification. Its main purposes are digital authentication, signature and encryption. It is often used by the [FLOSS](https://en.wikipedia.org/wiki/Free_and_open-source_software) community to verify: -It is often used by the [FLOSS](https://en.wikipedia.org/wiki/Free_and_open-source_software) community to verify: +- Linux package signatures: Debian [SecureApt](https://wiki.debian.org/SecureApt), ArchLinux [Master Keys](https://www.archlinux.org/master-keys/) +- [Version control](https://en.wikipedia.org/wiki/Revision_control) releases & maintainer identity -- Linux package signatures: Debian [SecureApt](https://wiki.debian.org/SecureApt), ArchLinux [Master -Keys](https://www.archlinux.org/master-keys/) -- [SCM](https://en.wikipedia.org/wiki/Revision_control) releases & maintainer identity +> You MUST understand that presence of data in the keyserver (pools) in no way connotes trust. Anyone can generate a key, with any name or email address, and upload it. All security and trust comes from evaluating security at the “object level”, via PGP [Web of trust](https://en.wikipedia.org/wiki/Web_of_trust) signatures. This keyserver makes it possible to retrieve keys, looking them up via various indices, but the collection of keys in this public pool is KNOWN to contain malicious and fraudulent keys. It is the common expectation of server operators that users understand this and use software which, like all known common OpenPGP implementations, evaluates trust accordingly. This expectation is so common that it is not normally explicitly stated. -### Trust -To quote Phil Pennock (the author of the [SKS](https://bitbucket.org/skskeyserver/sks-keyserver/wiki/Home) key server - http://sks.spodhuis.org/): +-- Phil Pennock (author of the [SKS](https://bitbucket.org/skskeyserver/sks-keyserver/wiki/Home) key server - http://sks.spodhuis.org/) -> You MUST understand that presence of data in the keyserver (pools) in no way connotes trust. Anyone can generate a key, with any name or email address, and upload it. All security and trust comes from evaluating security at the “object level”, via PGP Web-Of-Trust signatures. This keyserver makes it possible to retrieve keys, looking them up via various indices, but the collection of keys in this public pool is KNOWN to contain malicious and fraudulent keys. It is the common expectation of server operators that users understand this and use software which, like all known common OpenPGP implementations, evaluates trust accordingly. This expectation is so common that it is not normally explicitly stated. +Trust can be gained by having your key signed by other people (and signing their key back, too :) ), for instance during [key signing parties](https://en.wikipedia.org/wiki/Key_signing_party): [Keysigning party HOWTO](http://www.cryptnet.net/fdp/crypto/keysigning_party/en/keysigning_party.html), -Trust can be gained by having your key signed by other people (and signing their key back, too :) ), for instance during [key signing parties](https://en.wikipedia.org/wiki/Key_signing_party), see: - -- [The Keysigning party HOWTO](http://www.cryptnet.net/fdp/crypto/keysigning_party/en/keysigning_party.html) -- [Web of trust](https://en.wikipedia.org/wiki/Web_of_trust) ## Generate a GPG key - [Generating a GPG key for Git tagging](http://stackoverflow.com/a/16725717) (StackOverflow) diff --git a/doc/md/Plugin-System.md b/doc/md/dev/Plugin-system.md similarity index 96% rename from doc/md/Plugin-System.md rename to doc/md/dev/Plugin-system.md index 87a2638d..a87bd0cf 100644 --- a/doc/md/Plugin-System.md +++ b/doc/md/dev/Plugin-system.md @@ -1,19 +1,16 @@ -[**I am a developer: ** Developer API](#developer-api) - -[**I am a template designer: ** Guide for template designers](#guide-for-template-designer) - ---- +# Plugin system ## Developer API ### What can I do with plugins? -The plugin system let you: +The plugin system lets you: - insert content into specific places across templates. - alter data before templates rendering. - alter data before saving new links. + ### How can I create a plugin for Shaarli? First, chose a plugin name, such as `demo_plugin`. @@ -30,6 +27,7 @@ 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 in the .php to execute and run it if it exists. This function must be named this way, and takes the `ConfigManager` as parameter. @@ -63,6 +61,7 @@ For example, if my plugin want to add data to the header, this function is neede If this function is declared, and the plugin enabled, it will be called every time Shaarli is rendering the header. + ### Plugin's data #### Parameters @@ -109,6 +108,7 @@ array_push($data['top_placeholder'], 'My', 'content'); return $data; ``` + #### Data manipulation When a page is displayed, every variable send to the template engine is passed to plugins before that in `$data`. @@ -139,12 +139,14 @@ Each file contain two keys: > Note: In PHP, `parse_ini_file()` seems to want strings to be between by quotes `"` in the ini file. + ### It's not working! Use `demo_plugin` as a functional example. It covers most of the plugin system features. If it's still not working, please [open an issue](https://github.com/shaarli/Shaarli/issues/new). + ### Hooks | Hooks | Description | @@ -165,12 +167,10 @@ If it's still not working, please [open an issue](https://github.com/shaarli/Sha | [save_plugin_parameters](#save_plugin_parameters) | Allow to manipulate plugin parameters before they're saved. | - #### render_header -Triggered on every page. +Triggered on every page - allows plugins to add content in page headers. -Allow plugin to add content in page headers. ##### Data @@ -194,13 +194,12 @@ List of placeholders: ![fields_toolbar_example](http://i.imgur.com/3GMifI2.png) -#### render_includes -Triggered on every page. +#### render_includes -Allow plugin to include their own CSS files. +Triggered on every page - allows plugins to include their own CSS files. -##### Data +##### data `$data` is an array containing: @@ -216,13 +215,14 @@ List of placeholders: > Note: only add the path of the CSS file. E.g: `plugins/demo_plugin/custom_demo.css`. + #### render_footer Triggered on every page. Allow plugin to add content in page footer and include their own JS files. -##### Data +##### data `$data` is an array containing: @@ -243,20 +243,21 @@ List of placeholders: > Note: only add the path of the JS file. E.g: `plugins/demo_plugin/custom_demo.js`. + #### render_linklist Triggered when `linklist` is displayed (list of links, permalink, search, tag filtered, etc.). It allows to add content at the begining and end of the page, after every link displayed and to alter link data. -##### Data +##### data `$data` is an array containing: - All templates data, including links. - [Special data](#special-data) -##### Template placeholders +##### template placeholders Items can be displayed in templates by adding an entry in `$data['']` array. @@ -278,20 +279,21 @@ List of placeholders: ![plugin_end_zone_example](http://i.imgur.com/6IoRuop.png) + #### render_editlink Triggered when the link edition form is displayed. Allow to add fields in the form, or display elements. -##### Data +##### data `$data` is an array containing: - All templates data. - [Special data](#special-data) -##### Template placeholders +##### template placeholders Items can be displayed in templates by adding an entry in `$data['']` array. @@ -301,20 +303,21 @@ List of placeholders: ![edit_link_plugin_example](http://i.imgur.com/5u17Ens.png) + #### render_tools Triggered when the "tools" page is displayed. Allow to add content at the end of the page. -##### Data +##### data `$data` is an array containing: - All templates data. - [Special data](#special-data) -##### Template placeholders +##### template placeholders Items can be displayed in templates by adding an entry in `$data['']` array. @@ -324,20 +327,21 @@ List of placeholders: ![tools_plugin_example](http://i.imgur.com/Bqhu9oQ.png) + #### render_picwall Triggered when picwall is displayed. Allow to add content at the top and bottom of the page. -##### Data +##### data `$data` is an array containing: - All templates data. - [Special data](#special-data) -##### Template placeholders +##### template placeholders Items can be displayed in templates by adding an entry in `$data['']` array. @@ -348,13 +352,14 @@ List of placeholders: ![plugin_start_end_zone_example](http://i.imgur.com/tVTQFER.png) + #### render_tagcloud Triggered when tagcloud is displayed. Allow to add content at the top and bottom of the page. -##### Data +##### data `$data` is an array containing: @@ -379,11 +384,9 @@ For each tag, the following placeholder can be used: #### render_taglist -Triggered when taglist is displayed. - -Allow to add content at the top and bottom of the page. +Triggered when taglist is displayed - allows to add content at the top and bottom of the page. -##### Data +##### data `$data` is an array containing: @@ -409,7 +412,8 @@ Triggered when tagcloud is displayed. Allow to add content at the top and bottom of the page, the bottom of each link and to alter data. -##### Data + +##### data `$data` is an array containing: @@ -429,13 +433,14 @@ List of placeholders: - `plugin_start_zone`: before displaying the template content. - `plugin_end_zone`: after displaying the template content. + #### 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 `$data` is an array containing: @@ -454,13 +459,14 @@ For each links: - `feed_plugins`: additional tag for every link entry. + #### save_link Triggered when a link is save (new link or edit). Allow to alter the link being saved in the datastore. -##### Data +##### data `$data` is an array containing the link being saved: @@ -483,7 +489,7 @@ Triggered when a link is deleted. Allow to execute any action before the link is actually removed from the datastore -##### Data +##### data `$data` is an array containing the link being deleted: @@ -506,7 +512,7 @@ Triggered when the plugin parameters are saved from the plugin administration pa Plugins can perform an action every times their settings are updated. For example it is used to update the CSS file of the `default_colors` plugins. -##### Data +##### data `$data` input contains the `$_POST` array. @@ -515,7 +521,7 @@ the array will contain an entry with `MYPLUGIN_PARAMETER` as a key. Also [special data](#special-data). -## Guide for template designer +## Guide for template designers ### Plugin administration diff --git a/doc/md/dev/Release-Shaarli.md b/doc/md/dev/Release-Shaarli.md new file mode 100644 index 00000000..2c772406 --- /dev/null +++ b/doc/md/dev/Release-Shaarli.md @@ -0,0 +1,145 @@ +# Release Shaarli + +## Requirements + +This guide assumes that you have: + +- a GPG key matching your GitHub authentication credentials/email (the email address identified by the GPG key is the same as the one in your `~/.gitconfig`) +- a GitHub fork of Shaarli +- a local clone of your Shaarli fork, with the following remotes: + - `origin` pointing to your GitHub fork + - `upstream` pointing to the main Shaarli repository +- maintainer permissions on the main Shaarli repository, to: + - push the signed tag + - create a new release +- [Composer](https://getcomposer.org/) needs to be installed +- The [venv](https://docs.python.org/3/library/venv.html) Python 3 module needs to be installed for HTML documentation generation. + +## Release notes and `CHANGELOG.md` + +GitHub allows drafting the release notes for the upcoming release, from the [Releases](https://github.com/shaarli/Shaarli/releases) page. This way, the release note can be drafted while contributions are merged to `master`. See http://keepachangelog.com/en/0.3.0/ for changelog formatting. + +`CHANGELOG.md` should contain the same information as the release note draft for the upcoming version. Update it to: + +- add new entries (additions, fixes, etc.) +- mark the current version as released by setting its date and link +- add a new section for the future unreleased version + +```bash +## [v0.x.y](https://github.com/shaarli/Shaarli/releases/tag/v0.x.y) - UNRELEASES + +### Added + +### Changed + +### Fixed + +### Removed + +### Deprecated + +### Security + +``` + + +## Update the list of Git contributors + +```bash +$ make authors +$ git commit -s -m "Update AUTHORS" +``` + +## Create and merge a Pull Request + +Create a Pull Request to marge changes from your remote, into `master` in the community Shaarli repository, and have it merged. + + +## Create the release branch and update shaarli_version.php + +```bash +# fetch latest changes from master to your local copy +git checkout master +git pull upstream master + +# If releasing a new minor version, create a release branch +$ git checkout -b v0.x + +# Bump shaarli_version.php from dev to 0.x.0, **without the v** +$ vim shaarli_version.php +$ git add shaarli_version +$ git commit -s -m "Bump Shaarli version to v0.x.0" +$ git push upstream v0.x +``` + +## Create and push a signed tag + +Git [tags](http://git-scm.com/book/en/v2/Distributed-Git-Maintaining-a-Project#Tagging-Your-Releases) are used to identify specific revisions with a unique version number that follows [semantic versioning](https://semver.org/) + +```bash +# update your local copy +git checkout v0.5 +git pull upstream v0.5 + +# create a signed tag +git tag -s -m "Release v0.5.0" v0.5.0 + +# push the tag to upstream +git push --tags upstream +``` + +Here is how to verify a signed tag. [`v0.5.0`](https://github.com/shaarli/Shaarli/releases/tag/v0.5.0) is the first GPG-signed tag pushed on the Community Shaarli. Let's have a look at its signature! + +```bash +# update the list of available tags +git fetch upstream + +# get the SHA1 reference of the tag +git show-ref tags/v0.5.0 +# gives: f7762cf803f03f5caf4b8078359a63783d0090c1 refs/tags/v0.5.0 + +# verify the tag signature information +git verify-tag f7762cf803f03f5caf4b8078359a63783d0090c1 +# gpg: Signature made Thu 30 Jul 2015 11:46:34 CEST using RSA key ID 4100DF6F +# gpg: Good signature from "VirtualTam " [ultimate] +``` + +## Publish the GitHub release + +- In the `master` banch, update version badges in `README.md` to point to the newly released Shaarli version +- Update the previously drafted [release](https://github.com/shaarli/Shaarli/releases) (notes, tag) and publish it +- Profit! + + +## Generate full release zip archives + +Release archives will contain Shaarli code plus all required third-party libraries. They are useful for users who: + +- have no SSH access, no possibility to install PHP packages/server extensions, no possibility to run scripts (shared hosting) +- do not want to install build/dev dependencies on their server + + `git checkout` the appropriate branch, then: + +```bash +# checkout the appropriate branch +git checkout 0.x.y +# generate zip archives +make release_archive +``` + +This will create `shaarli-v0.x.y-full.tar`, `shaarli-v0.x.y-full.zip`. These archives need to be manually uploaded on the previously created GitHub [release](https://github.com/shaarli/Shaarli/releases). + + +### Update the `latest` branch + +```bash +# checkout the 'latest' branch +git checkout latest +# merge changes from your newly published release branch +git merge v0.x.y +# fix eventual conflicts with git mergetool... +# run tests +make test +# push the latest branch +git push upstream latest +``` diff --git a/doc/md/Theming.md b/doc/md/dev/Theming.md similarity index 99% rename from doc/md/Theming.md rename to doc/md/dev/Theming.md index eb84e11c..5be1a481 100644 --- a/doc/md/Theming.md +++ b/doc/md/dev/Theming.md @@ -1,3 +1,5 @@ +# Theming + ## Foreword There are two ways of customizing how Shaarli looks: diff --git a/doc/md/Translations.md b/doc/md/dev/Translations.md similarity index 66% rename from doc/md/Translations.md rename to doc/md/dev/Translations.md index c23ec962..8f3b8f10 100644 --- a/doc/md/Translations.md +++ b/doc/md/dev/Translations.md @@ -7,87 +7,80 @@ Note that only the `default` theme supports translations. ### Contributing -We encourage the community to contribute to Shaarli's translation either by improving existing -translations or submitting a new language. +We encourage the community to contribute to Shaarli translations, either by improving existing translations or submitting a new language. -Contributing to the translation does not require development skill. +Contributing to the translation does not require software development knowledge. -Please submit a pull request with the `.po` file updated/created. Note that the compiled file (`.mo`) -is not stored on the repository, and is generated during the release process. +Please submit a pull request with the `.po` file updated/created. Note that the compiled file (`.mo`) is not stored on the repository, and is generated during the release process. -### How to - -First, install [Poedit](https://poedit.net/) tool. - -Poedit will extract strings to translate from the PHP source code. -**Important**: due to the usage of a template engine, it's important to generate PHP cache files to extract -every translatable string. +### How to -You can either use [this script](https://gist.github.com/ArthurHoaro/5d0323f758ab2401ef444a53f54e9a07) (recommended) -or visit every template page in your browser to generate cache files, while logged in. +Install [Poedit](https://poedit.net/) (used to extract strings to translate from the PHP source code, and generate `.po` files). -Here is a list : +Due to the usage of a template engine, it's important to generate PHP cache files to extract every translatable string. You can either use [this script](https://gist.github.com/ArthurHoaro/5d0323f758ab2401ef444a53f54e9a07) (recommended) or visit every template page in your browser to generate cache files, while logged in. Here is a list : ``` http:/// +http:///login +http:///daily +http:///tags/cloud +http:///tags/list +http:///picture-wall http:///?nonope http:///admin/add-shaare http:///admin/password http:///admin/tags http:///admin/configure http:///admin/tools -http:///daily http:///admin/shaare http:///admin/export http:///admin/import -http:///login -http:///picture-wall http:///admin/plugins -http:///tags/cloud -http:///tags/list ``` -#### Improve existing translation - -In Poedit, click on "Edit a Translation", and from Shaarli's directory open -`inc/languages//LC_MESSAGES/shaarli.po`. -The existing list of translatable strings should have been loaded, then click on the "Update" button. +#### Improve existing translations -You can start editing the translation. +- In Poedit, click on "Edit a Translation +- Open `inc/languages//LC_MESSAGES/shaarli.po` under Shaarli's directory +- The existing list of translatable strings should load +- Click on the "Update" button. +- Start editing translations. ![poedit-screenshot](images/poedit-1.jpg) Save when you're done, then you can submit a pull request containing the updated `shaarli.po`. -#### Add a new language - -Open Poedit and select "Create New Translation", then from Shaarli's directory open -`inc/languages//LC_MESSAGES/shaarli.po`. - -Then select the language you want to create. -Click on `File > Save as...`, and save your file in `/inc/language//LC_MESSAGES/shaarli.po`. -`` here should be the language code respecting the [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-2) -format in lowercase (e.g. `de` for German). +#### Add a new language -Then click on the "Update" button, and you can start to translate every available string. +- In Poedit select "Create New Translation" +- Open `inc/languages//LC_MESSAGES/shaarli.po` under Shaarli's directory +- Select the language you want to create. +- Click on `File > Save as...`, save your file in `/inc/language//LC_MESSAGES/shaarli.po` (`` here should be the language code respecting the [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-2) format in lowercase - e.g. `de` for German) +- Click on the "Update" button +- Start editing translations. Save when you're done, then you can submit a pull request containing the new `shaarli.po`. + ### Theme translations -Theme translation extensions are loaded automatically if they're present. +[Theme](Theming) translation extensions are loaded automatically if they're present. As a theme developer, all you have to do is to add the `.po` and `.mo` compiled file like this: - tpl//language//LC_MESSAGES/.po - tpl//language//LC_MESSAGES/.mo +``` +tpl//language//LC_MESSAGES/.po +tpl//language//LC_MESSAGES/.mo +``` Where `` is the ISO 3166-1 alpha-2 language code. + Read the following section "Extend Shaarli's translation" to learn how to generate those files. + ### Extend Shaarli's translation If you're writing a custom theme, or a non official plugin, you might want to use the translation system, diff --git a/doc/md/dev/Unit-tests.md b/doc/md/dev/Unit-tests.md new file mode 100644 index 00000000..25af82d7 --- /dev/null +++ b/doc/md/dev/Unit-tests.md @@ -0,0 +1,138 @@ +# Unit tests + +Shaarli uses the [PHPUnit](https://phpunit.de/) test framework; it can be installed with [Composer](https://getcomposer.org/), which is a dependency management tool. + +## Install composer + +You can either use: + +- a system-wide version, e.g. installed through your distro's package manager +- a local version, downloadable [here](https://getcomposer.org/download/). + +```bash +# system-wide version +$ composer install +$ composer update + +# local version +$ php composer.phar self-update +$ php composer.phar install +$ php composer.phar update +``` + +## Install Shaarli dev dependencies + +```bash +$ cd /path/to/shaarli +$ composer update +``` + +## Install and enable Xdebug to generate PHPUnit coverage reports + + +[Xdebug](http://xdebug.org/docs/install) is a PHP extension which provides debugging and profiling capabilities. Install Xdebug: + +```bash +# for Debian-based distros: +sudo aptitude install php5-xdebug + +# for ArchLinux: +pacman -S xdebug + +# then add the following line to /etc/php/php.ini +zend_extension=xdebug.so +``` + +## Run unit tests + +Ensure tests pass successuflly: + +```bash +make test +# ... +# OK (36 tests, 65 assertions) +``` + +In case of failure the test suite will point you to actual errors and output a summary: + +```bash +make test +# ... +# FAILURES! +# Tests: 36, Assertions: 63, Errors: 1, Failures: 2. +``` + +By default, PHPUnit will run all suitable tests found under the `tests` directory. Each test has 3 possible outcomes: + +- `.` - success +- `F` - failure: the test was run but its results are invalid + - the code does not behave as expected + - dependencies to external elements: globals, session, cache... +- `E` - error: something went wrong and the tested code has crashed + - typos in the code, or in the test code + - dependencies to missing external elements + +If Xdebug has been installed and activated, two coverage reports will be generated: + +- 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: + +```php +/** + * Netscape bookmark import + * @group WIP + */ +class BookmarkImportTest extends PHPUnit_Framework_TestCase +{ + [...] +} +``` + +To run all tests annotated with `@group WIP`: +```bash +$ vendor/bin/phpunit --group WIP tests/ +``` + +## Running tests inside Docker containers + +Unit tests can be run inside [Docker](../Docker.md) containers. + +Test Dockerfiles are located under `tests/docker//Dockerfile`, and can be used to build Docker images to run Shaarli test suites under commonLinux environments. Dockerfiles are provided for the following environments: + +- [`alpine36`](https://github.com/shaarli/Shaarli/blob/master/tests/docker/alpine36/Dockerfile) - [Alpine Linux 3.6](https://www.alpinelinux.org/downloads/) +- [`debian8`](https://github.com/shaarli/Shaarli/blob/master/tests/docker/debian8/Dockerfile) - [Debian 8 Jessie](https://www.debian.org/DebianJessie) (oldoldstable) +- [`debian9`](https://github.com/shaarli/Shaarli/blob/master/tests/docker/debian9/Dockerfile) - [Debian 9 Stretch](https://wiki.debian.org/DebianStretch) (oldstable) +- [`ubuntu16`](https://github.com/shaarli/Shaarli/blob/master/tests/docker/ubuntu16/Dockerfile) - [Ubuntu 16.04 Xenial Xerus](http://releases.ubuntu.com/16.04/) (old LTS) + +Each image provides: +- a base Linux OS +- Shaarli PHP dependencies (OS packages) +- test PHP dependencies (OS packages) +- Composer +- Tests that run inside the conatiner using a standard Linux user account (running tests as `root` would bypass permission checks and may hide issues) + +Build a test image: + +```bash +# build the Debian 9 Docker image +cd /path/to/shaarli/tests/docker/debian9 +docker build -t shaarli-test:debian9 . +``` + +Run unit tests in a container: + +```bash +cd /path/to/shaarli +# install/update 3rd-party test dependencies +composer install --prefer-dist +# run tests using the freshly built image +docker run -v $PWD:/shaarli shaarli-test:debian9 docker_test +# run the full test campaign +docker run -v $PWD:/shaarli shaarli-test:debian9 docker_all_tests +``` diff --git a/doc/md/Versioning-and-Branches.md b/doc/md/dev/Versioning.md similarity index 58% rename from doc/md/Versioning-and-Branches.md rename to doc/md/dev/Versioning.md index 7097ca0a..32c80a5c 100644 --- a/doc/md/Versioning-and-Branches.md +++ b/doc/md/dev/Versioning.md @@ -1,6 +1,7 @@ -**WORK IN PROGRESS** +# Versioning + +If you're maintaining a 3rd party tool for Shaarli (theme, plugin, etc.), It's important to understand how Shaarli branches work ensure your tool stays compatible. -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. ## `master` branch @@ -11,39 +12,26 @@ Remarks: - This branch shouldn't be used for production as it isn't necessary stable. - 3rd party aren't required to be compatible with the latest changes. - Official plugins, themes and libraries (contained within Shaarli organization repos) must be compatible with the master branch. -- The version in this branch is always `dev`. -## `v0.x` branch -This `v0.x` branch, points to the latest `v0.x.y` release. +## `v0.x` branch -Explanation: +The `v0.x` branch points to the latest `v0.x.y` release. -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. +If a major bug affects the original `v0.x.0` release, we may [backport](https://en.wikipedia.org/wiki/Backporting) a fix for this bug from master, to the `v0.x` branch, and create a new bugfix release (eg. `v0.x.1`) from this branch. -In this case, the issue is fixed in the `master` branch, and the fix is backported the to the `v0.x` branch. Then a new release is made from the `v0.x` branch. +This allows users of the original release to upgrade to the fixed version, without having to upgrade to a completely new minor/major release. -This workflow allow us to fix any major bug detected, without having to release bleeding edge feature too soon. ## `latest` branch This branch point the latest release. It recommended to use it to get the latest tested changes. -## `stable` branch - -The `stable` branch doesn't contain any major bug, and is one major digit version behind the latest release. - -For example, the current latest release is `v0.8.3`, the stable branch is an alias to the latest `v0.7.x` release. When the `v0.9.0` version will be released, the stable will move to the latest `v0.8.x` release. - -Remarks: - -- Shaarli release pace isn't fast, and the stable branch might be a few months behind the latest release. ## Releases -Releases are always made from the latest `v0.x` branch. +For every release, we manually generate a .zip file which contains all Shaarli dependencies, making Shaarli's installation only one step. -Note that for every release, we manually generate a tarball which contains all Shaarli dependencies, making Shaarli's installation only one step. ## Advices on 3rd party git repos workflow diff --git a/doc/md/images/poedit-1.jpg b/doc/md/dev/images/poedit-1.jpg similarity index 100% rename from doc/md/images/poedit-1.jpg rename to doc/md/dev/images/poedit-1.jpg diff --git a/doc/md/docker/docker-101.md b/doc/md/docker/docker-101.md deleted file mode 100644 index a9c00b85..00000000 --- a/doc/md/docker/docker-101.md +++ /dev/null @@ -1,140 +0,0 @@ -## Basics -Install [Docker](https://www.docker.com/), by following the instructions relevant -to your OS / distribution, and start the service. - -### Search an image on [DockerHub](https://hub.docker.com/) - -```bash -$ docker search debian - -NAME DESCRIPTION STARS OFFICIAL AUTOMATED -ubuntu Ubuntu is a Debian-based Linux operating s... 2065 [OK] -debian Debian is a Linux distribution that's comp... 603 [OK] -google/debian 47 [OK] -``` - -### Show available tags for a repository -```bash -$ curl https://index.docker.io/v1/repositories/debian/tags | python -m json.tool - -% Total % Received % Xferd Average Speed Time Time Time Current -Dload Upload Total Spent Left Speed -100 1283 0 1283 0 0 433 0 --:--:-- 0:00:02 --:--:-- 433 -``` - -Sample output: -```json -[ - { - "layer": "85a02782", - "name": "stretch" - }, - { - "layer": "59abecbc", - "name": "testing" - }, - { - "layer": "bf0fd686", - "name": "unstable" - }, - { - "layer": "60c52dbe", - "name": "wheezy" - }, - { - "layer": "c5b806fe", - "name": "wheezy-backports" - } -] - -``` - -### Pull an image from DockerHub -```bash -$ docker pull repository[:tag] - -$ docker pull debian:wheezy -wheezy: Pulling from debian -4c8cbfd2973e: Pull complete -60c52dbe9d91: Pull complete -Digest: sha256:c584131da2ac1948aa3e66468a4424b6aea2f33acba7cec0b631bdb56254c4fe -Status: Downloaded newer image for debian:wheezy -``` - -Docker re-uses layers already downloaded. In other words if you have images based on Alpine or some Ubuntu version for example, those can share disk space. - -### Start a container -A container is an instance created from an image, that can be run and that keeps running until its main process exits. Or until the user stops the container. - -The simplest way to start a container from image is ``docker run``. It also pulls the image for you if it is not locally available. For more advanced use, refer to ``docker create``. - -Stopped containers are not destroyed, unless you specify ``--rm``. To view all created, running and stopped containers, enter: -```bash -$ docker ps -a -``` - -Some containers may be designed or configured to be restarted, others are not. Also remember both network ports and volumes of a container are created on start, and not editable later. - -### Access a running container -A running container is accessible using ``docker exec``, or ``docker copy``. You can use ``exec`` to start a root shell in the Shaarli container: -```bash -$ docker exec -ti bash -``` -Note the names and ID's of containers are listed in ``docker ps``. You can even type only one or two letters of the ID, given they are unique. - -Access can also be through one or more network ports, or disk volumes. Both are specified on and fixed on ``docker create`` or ``run``. - -You can view the console output of the main container process too: -```bash -$ docker logs -f -``` - -### Docker disk use -Trying out different images can fill some gigabytes of disk quickly. Besides images, the docker volumes usually take up most disk space. - -If you care only about trying out docker and not about what is running or saved, the following commands should help you out quickly if you run low on disk space: - -```bash -$ docker rmi -f $(docker images -aq) # remove or mark all images for disposal -$ docker volume rm $(docker volume ls -q) # remove all volumes -``` - -### Systemd config -Systemd is the process manager of choice on Debian-based distributions. Once you have a ``docker`` service installed, you can use the following steps to set up Shaarli to run on system start. - -```bash -systemctl enable /etc/systemd/system/docker.shaarli.service -systemctl start docker.shaarli -systemctl status docker.* -journalctl -f # inspect system log if needed -``` - -You will need sudo or a root terminal to perform some or all of the steps above. Here are the contents for the service file: -``` -[Unit] -Description=Shaarli Bookmark Manager Container -After=docker.service -Requires=docker.service - - -[Service] -Restart=always - -# Put any environment you want in an included file, like $host- or $domainname in this example -EnvironmentFile=/etc/sysconfig/box-environment - -# It's just an example.. -ExecStart=/usr/bin/docker run \ - -p 28010:80 \ - --name ${hostname}-shaarli \ - --hostname shaarli.${domainname} \ - -v /srv/docker-volumes-local/shaarli-data:/var/www/shaarli/data:rw \ - -v /etc/localtime:/etc/localtime:ro \ - shaarli/shaarli:latest - -ExecStop=/usr/bin/docker rm -f ${hostname}-shaarli - - -[Install] -WantedBy=multi-user.target -``` diff --git a/doc/md/docker/resources.md b/doc/md/docker/resources.md deleted file mode 100644 index 082d4a46..00000000 --- a/doc/md/docker/resources.md +++ /dev/null @@ -1,19 +0,0 @@ -### Docker - -- [Interactive Docker training portal](https://www.katacoda.com/courses/docker/) on [Katakoda](https://www.katacoda.com/) -- [Where are Docker images stored?](http://blog.thoward37.me/articles/where-are-docker-images-stored/) -- [Dockerfile reference](https://docs.docker.com/reference/builder/) -- [Dockerfile best practices](https://docs.docker.com/articles/dockerfile_best-practices/) -- [Volumes](https://docs.docker.com/userguide/dockervolumes/) - -### DockerHub - -- [Repositories](https://docs.docker.com/userguide/dockerrepos/) -- [Teams and organizations](https://docs.docker.com/docker-hub/orgs/) -- [GitHub automated build](https://docs.docker.com/docker-hub/github/) - -### Service management - -- [Using supervisord](https://docs.docker.com/articles/using_supervisord/) -- [Nginx in the foreground](http://nginx.org/en/docs/ngx_core_module.html#daemon) -- [supervisord](http://supervisord.org/) diff --git a/doc/md/docker/reverse-proxy-configuration.md b/doc/md/docker/reverse-proxy-configuration.md deleted file mode 100644 index e53c9422..00000000 --- a/doc/md/docker/reverse-proxy-configuration.md +++ /dev/null @@ -1,123 +0,0 @@ -## Foreword - -This guide assumes that: - -- Shaarli runs in a Docker container -- The host's `10080` port is mapped to the container's `80` port -- Shaarli's Fully Qualified Domain Name (FQDN) is `shaarli.domain.tld` -- HTTP traffic is redirected to HTTPS - -## Apache - -- [Apache 2.4 documentation](https://httpd.apache.org/docs/2.4/) - - [mod_proxy](https://httpd.apache.org/docs/2.4/mod/mod_proxy.html) - - [Reverse Proxy Request Headers](https://httpd.apache.org/docs/2.4/mod/mod_proxy.html#x-headers) - -The following HTTP headers are set when the `ProxyPass` directive is set: - -- `X-Forwarded-For` -- `X-Forwarded-Host` -- `X-Forwarded-Server` - -The original `SERVER_NAME` can be sent to the proxied host by setting the [`ProxyPreserveHost`](https://httpd.apache.org/docs/2.4/mod/mod_proxy.html#ProxyPreserveHost) directive to `On`. - -```apache - - ServerName shaarli.domain.tld - Redirect permanent / https://shaarli.domain.tld - - - - ServerName shaarli.domain.tld - - SSLEngine on - SSLCertificateFile /path/to/cert - SSLCertificateKeyFile /path/to/certkey - - LogLevel warn - ErrorLog /var/log/apache2/shaarli-error.log - CustomLog /var/log/apache2/shaarli-access.log combined - - RequestHeader set X-Forwarded-Proto "https" - ProxyPreserveHost On - - ProxyPass / http://127.0.0.1:10080/ - ProxyPassReverse / http://127.0.0.1:10080/ - -``` - - -## HAProxy - -- [HAProxy documentation](https://cbonte.github.io/haproxy-dconv/) - -```conf -global - [...] - -defaults - [...] - -frontend http-in - bind :80 - redirect scheme https code 301 if !{ ssl_fc } - - bind :443 ssl crt /path/to/cert.pem - - default_backend shaarli - - -backend shaarli - mode http - option http-server-close - option forwardfor - reqadd X-Forwarded-Proto: https - - server shaarli1 127.0.0.1:10080 -``` - - -## Nginx - -- [Nginx documentation](https://nginx.org/en/docs/) - -```nginx -http { - [...] - - index index.html index.php; - - root /home/john/web; - access_log /var/log/nginx/access.log; - error_log /var/log/nginx/error.log; - - server { - listen 80; - server_name shaarli.domain.tld; - return 301 https://shaarli.domain.tld$request_uri; - } - - server { - listen 443 ssl http2; - server_name shaarli.domain.tld; - - ssl_certificate /path/to/cert - ssl_certificate_key /path/to/certkey - - location / { - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_set_header X-Forwarded-Host $host; - - proxy_pass http://localhost:10080/; - proxy_set_header Host $host; - proxy_connect_timeout 30s; - proxy_read_timeout 120s; - - access_log /var/log/nginx/shaarli.access.log; - error_log /var/log/nginx/shaarli.error.log; - } - } -} -``` diff --git a/doc/md/docker/shaarli-images.md b/doc/md/docker/shaarli-images.md deleted file mode 100644 index 14971d54..00000000 --- a/doc/md/docker/shaarli-images.md +++ /dev/null @@ -1,118 +0,0 @@ -A brief guide on getting starting using docker is given in [Docker 101](docker-101.md). -To learn more about user data and how to keep it across versions, please see [Upgrade and Migration](../Upgrade-and-migration.md). - -## Get and run a Shaarli image - -### DockerHub repository -The images can be found in the [`shaarli/shaarli`](https://hub.docker.com/r/shaarli/shaarli/) -repository. - -### Available image tags -- `latest`: latest branch -- `master`: master branch -- `stable`: stable branch - -The `latest`, `master` and `stable` images rely on: - -- [Alpine Linux](https://www.alpinelinux.org/) -- [PHP7-FPM](http://php-fpm.org/) -- [Nginx](http://nginx.org/) - -Additional Dockerfiles are provided for the `arm32v7` platform, relying on -[Linuxserver.io Alpine armhf -images](https://hub.docker.com/r/lsiobase/alpine.armhf/). These images must be -built using [`docker -build`](https://docs.docker.com/engine/reference/commandline/build/) on an -`arm32v7` machine or using an emulator such as -[qemu](https://resin.io/blog/building-arm-containers-on-any-x86-machine-even-dockerhub/). - -### Download from Docker Hub -```shell -$ docker pull shaarli/shaarli - -latest: Pulling from shaarli/shaarli -32716d9fcddb: Pull complete -84899d045435: Pull complete -4b6ad7444763: Pull complete -e0345ef7a3e0: Pull complete -5c1dd344094f: Pull complete -6422305a200b: Pull complete -7d63f861dbef: Pull complete -3eb97210645c: Pull complete -869319d746ff: Already exists -869319d746ff: Pulling fs layer -902b87aaaec9: Already exists -Digest: sha256:f836b4627b958b3f83f59c332f22f02fcd495ace3056f2be2c4912bd8704cc98 -Status: Downloaded newer image for shaarli/shaarli:latest -``` - -### Create and start a new container from the image -```shell -# map the host's :8000 port to the container's :80 port -$ docker create -p 8000:80 shaarli/shaarli -d40b7af693d678958adedfb88f87d6ea0237186c23de5c4102a55a8fcb499101 - -# launch the container in the background -$ docker start d40b7af693d678958adedfb88f87d6ea0237186c23de5c4102a55a8fcb499101 -d40b7af693d678958adedfb88f87d6ea0237186c23de5c4102a55a8fcb499101 - -# list active containers -$ docker ps -CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES -d40b7af693d6 shaarli/shaarli /usr/bin/supervisor 15 seconds ago Up 4 seconds 0.0.0.0:8000->80/tcp backstabbing_galileo -``` - -### Stop and destroy a container -```shell -$ docker stop backstabbing_galileo # those docker guys are really rude to physicists! -backstabbing_galileo - -# check the container is stopped -$ docker ps -CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES - -# list ALL containers -$ docker ps -a -CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES -d40b7af693d6 shaarli/shaarli /usr/bin/supervisor 5 minutes ago Exited (0) 48 seconds ago backstabbing_galileo - -# destroy the container -$ docker rm backstabbing_galileo # let's put an end to these barbarian practices -backstabbing_galileo - -$ docker ps -a -CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES -``` - -### Automatic builds -Docker users can start a personal instance from an -[autobuild image](https://hub.docker.com/r/shaarli/shaarli/). -For example to start a temporary Shaarli at ``localhost:8000``, and keep session -data (config, storage): - -```shell -MY_SHAARLI_VOLUME=$(cd /path/to/shaarli/data/ && pwd -P) -docker run -ti --rm \ - -p 8000:80 \ - -v $MY_SHAARLI_VOLUME:/var/www/shaarli/data \ - shaarli/shaarli -``` - -### Volumes and data persistence -Data can be persisted by [using volumes](https://docs.docker.com/storage/volumes/). -Volumes allow to keep your data when renewing and/or updating container images: - -```shell -# Create data volumes -$ docker volume create shaarli-data -$ docker volume create shaarli-cache - -# Create and start a Shaarli container using these volumes to persist data -$ docker create \ - --name shaarli \ - -v shaarli-cache:/var/www/shaarli/cache \ - -v shaarli-data:/var/www/shaarli/data \ - -p 8000:80 \ - shaarli/shaarli:master -$ docker start shaarli -``` diff --git a/doc/md/guides/backup-restore-import-export.md b/doc/md/guides/backup-restore-import-export.md deleted file mode 100644 index bb790074..00000000 --- a/doc/md/guides/backup-restore-import-export.md +++ /dev/null @@ -1,64 +0,0 @@ -## Backup and restore the datastore file - -Backup the file `data/datastore.php` (by FTP or SSH). Restore by putting the file back in place. - -Example command: -```bash -rsync -avzP my.server.com:/var/www/shaarli/data/datastore.php datastore-$(date +%Y-%m-%d_%H%M).php -``` - -## Export links as... - -To export links as an HTML file, under _Tools > Export_, choose: - -- _Export all_ to export both public and private links -- _Export public_ to export public links only -- _Export private_ to export private links only - -Restore by using the `Import` feature. - -- This can be done using the [shaarchiver](https://github.com/nodiscc/shaarchiver) tool. - -Example command: -```bash -./export-bookmarks.py --url=https://my.server.com/shaarli --username=myusername --password=mysupersecretpassword --download-dir=./ --type=all -``` - -## Import links from... - -### Diigo - -If you export your bookmark from Diigo, make sure you use the Delicious export, not the Netscape export. (Their Netscape export is broken, and they don't seem to be interested in fixing it.) - -### Mister Wong - -See [this issue](https://github.com/sebsauvage/Shaarli/issues/146) for import tweaks. - -### SemanticScuttle - -To correctly import the tags from a [SemanticScuttle](http://semanticscuttle.sourceforge.net/) HTML export, edit the HTML file before importing and replace all occurences of `tags=` (lowercase) to `TAGS=` (uppercase). - -### Scuttle - -Shaarli cannot import data directly from [Scuttle](https://github.com/scronide/scuttle). - -However, you can use the third-party [scuttle-to-shaarli](https://github.com/q2apro/scuttle-to-shaarli) -tool to export the Scuttle database to the Netscape HTML format compatible with the Shaarli importer. - -### Refind - -You can use the third-party tool [Derefind](https://github.com/ShawnPConroy/Derefind) to convert refind.com bookmark exports to a format that can be imported into Shaarli. - -## Import Shaarli links to Firefox - -- Export your Shaarli links as described above. - - For compatibility reasons, check `Prepend note permalinks with this Shaarli instance's URL (useful to import bookmarks in a web browser)` -- In Firefox, open the bookmark manager (not the sidebar! `Bookmarks menu > Show all bookmarks` or `Ctrl+Shift+B`) -- Select `Import and Backup > Import bookmarks in HTML format` - -Your bookmarks will be imported in Firefox, ready to use, with tags and descriptions retained. "Self" (notes) shaares will still point to the Shaarli instance you exported them from, but the note text can be viewed directly in the bookmark properties inside your browser. Depending on the number of bookmarks, the import can take some time. - -You may be interested in these Firefox addons to manage links imported from Shaarli - -- [Bookmark Deduplicator](https://addons.mozilla.org/en-US/firefox/addon/bookmark-deduplicator/) - provides an easy way to deduplicate your bookmarks -- [TagSieve](https://addons.mozilla.org/en-US/firefox/addon/tagsieve/) - browse your bookmarks by their tags diff --git a/doc/md/guides/images/01-create-droplet-distro.jpg b/doc/md/guides/images/01-create-droplet-distro.jpg deleted file mode 100644 index 63682ba89fb041c249a10fd208f647052542562d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20909 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 tYzF1?1}-Qkz!hsvBd!DfmDhIPe*;e#{_Fq% diff --git a/doc/md/guides/images/02-create-droplet-region.jpg b/doc/md/guides/images/02-create-droplet-region.jpg deleted file mode 100644 index 135a78be4a8f3c2d2cb828bb86eb1970f1d7ffb5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21603 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%0AOYbZRBFY&t)t16GDf~Zw|Cbq!j*n@+6BE_r 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 DKH{0l diff --git a/doc/md/guides/images/04-finalize.jpg b/doc/md/guides/images/04-finalize.jpg deleted file mode 100644 index 68ec0dc556b99d355fbef8c39f64219041219be6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28233 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 diff --git a/doc/md/guides/images/05-droplet.jpg b/doc/md/guides/images/05-droplet.jpg deleted file mode 100644 index 44e93a1ef2e6fcd3ac0c48fc3e92b3697d5fc6cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11977 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 Nb_;iF?R);<{{rG33OWD) diff --git a/doc/md/guides/images/06-domain.jpg b/doc/md/guides/images/06-domain.jpg deleted file mode 100644 index 5827dd93c7955f1bb549e4feab15b910aa3c6110..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4499 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 diff --git a/doc/md/guides/install-shaarli-with-debian9-and-docker.md b/doc/md/guides/install-shaarli-with-debian9-and-docker.md deleted file mode 100644 index f1b26d47..00000000 --- a/doc/md/guides/install-shaarli-with-debian9-and-docker.md +++ /dev/null @@ -1,257 +0,0 @@ -_Last updated on 2018-07-01._ - -## Goals -- Getting a Virtual Private Server (VPS) -- Running Shaarli: - - as a Docker container, - - using the Træfik reverse proxy, - - securized with TLS certificates from Let's Encrypt. - - -The following components and tools will be used: - -- [Debian](https://www.debian.org/), a GNU/Linux distribution widely used in - server environments; -- [Docker](https://docs.docker.com/engine/docker-overview/), an open platform - for developing, shipping, and running applications; -- [Docker Compose](https://docs.docker.com/compose/), a tool for defining and - running multi-container Docker applications. - - -More information can be found in the [Resources](#resources) section at the -bottom of the guide. - -## Getting a Virtual Private Server -For this guide, I went for the smallest VPS available from DigitalOcean, -a Droplet with 1 CPU, 1 GiB RAM and 25 GiB SSD storage, which costs -$5/month ($0.007/hour): - -- [Droplets Overview](https://www.digitalocean.com/docs/droplets/overview/) -- [Pricing](https://www.digitalocean.com/pricing/) -- [How to Create a Droplet from the DigitalOcean Control Panel](https://www.digitalocean.com/docs/droplets/how-to/create/) -- [How to Add SSH Keys to Droplets](https://www.digitalocean.com/docs/droplets/how-to/add-ssh-keys/) -- [Initial Server Setup with Debian 8](https://www.digitalocean.com/community/tutorials/initial-server-setup-with-debian-8) (also applies to Debian 9) -- [An Introduction to Securing your Linux VPS](https://www.digitalocean.com/community/tutorials/an-introduction-to-securing-your-linux-vps) - -### Creating a Droplet -Select `Debian 9` as the Droplet distribution: - -Droplet distribution - -Choose a region that is geographically close to you: - -Droplet region - -Choose a Droplet size that corresponds to your usage and budget: - -Droplet size - -Finalize the Droplet creation: - -Droplet finalization - -Droplet information is displayed on the Control Panel: - -Droplet summary - -Once your VPS has been created, you will receive an e-mail with connection -instructions. - -## Obtaining a domain name -After creating your VPS, it will be reachable using its IP address; some hosting -providers also create a DNS record, e.g. `ns4853142.ip-01-47-127.eu`. - -A domain name (DNS record) is required to obtain a certificate and setup HTTPS -(HTTP with TLS encryption). - -Domain names can be obtained from registrars through hosting providers such as -[Gandi](https://www.gandi.net/en/domain). - -Once you have your own domain, you need to create a new DNS record that points -to your VPS' IP address: - -Domain configuration - -## Host setup -Now's the time to connect to your freshly created VPS! - -```shell -$ ssh root@188.166.85.8 - -Linux stretch-shaarli-02 4.9.0-6-amd64 #1 SMP Debian 4.9.88-1+deb9u1 (2018-05-07) x86_64 - -The programs included with the Debian GNU/Linux system are free software; -the exact distribution terms for each program are described in the -individual files in /usr/share/doc/*/copyright. - -Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent -permitted by applicable law. -Last login: Sun Jul 1 11:20:18 2018 from - -root@stretch-shaarli-02:~$ -``` - -### Updating the system -```shell -root@stretch-shaarli-02:~$ apt update && apt upgrade -y -``` - -### Setting up Docker -_The following instructions are from the -[Get Docker CE for Debian](https://docs.docker.com/install/linux/docker-ce/debian/) -guide._ - -Install package dependencies: - -```shell -root@stretch-shaarli-02:~$ apt install -y apt-transport-https ca-certificates curl gnupg2 software-properties-common -``` - -Add Docker's package repository GPG key: - -```shell -root@stretch-shaarli-02:~$ curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add - -``` - -Add Docker's package repository: - -```shell -root@stretch-shaarli-02:~$ add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian stretch stable" -``` - -Update package lists and install Docker: - -```shell -root@stretch-shaarli-02:~$ apt update && apt install -y docker-ce -``` - -Verify Docker is properly configured by running the `hello-world` image: - -```shell -root@stretch-shaarli-02:~$ docker run hello-world -``` - -### Setting up Docker Compose -_The following instructions are from the -[Install Docker Compose](https://docs.docker.com/compose/install/) -guide._ - -Download the current version from the release page: - -```shell -root@stretch-shaarli-02:~$ curl -L https://github.com/docker/compose/releases/download/1.21.2/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose -root@stretch-shaarli-02:~$ chmod +x /usr/local/bin/docker-compose -``` - -## Running Shaarli -Shaarli comes with a configuration file for Docker Compose, that will setup: - -- a local Docker network -- a Docker [volume](https://docs.docker.com/storage/volumes/) to store Shaarli data -- a Docker [volume](https://docs.docker.com/storage/volumes/) to store Træfik TLS configuration and certificates -- a [Shaarli](https://hub.docker.com/r/shaarli/shaarli/) instance -- a [Træfik](https://hub.docker.com/_/traefik/) instance - -[Træfik](https://docs.traefik.io/) is a modern HTTP reverse proxy, with native -support for Docker and [Let's Encrypt](https://letsencrypt.org/). - -### Compose configuration -Create a new directory to store the configuration: - -```shell -root@stretch-shaarli-02:~$ mkdir shaarli && cd shaarli -root@stretch-shaarli-02:~/shaarli$ -``` - -Download the current version of Shaarli's `docker-compose.yml`: - -```shell -root@stretch-shaarli-02:~/shaarli$ curl -L https://raw.githubusercontent.com/shaarli/Shaarli/master/docker-compose.yml -o docker-compose.yml -``` - -Create the `.env` file and fill in your VPS and domain information (replace -`` and `` with your actual information): - -```shell -root@stretch-shaarli-02:~/shaarli$ vim .env -``` - -```shell -SHAARLI_VIRTUAL_HOST= -SHAARLI_LETSENCRYPT_EMAIL= -``` - -### Pull the Docker images -```shell -root@stretch-shaarli-02:~/shaarli$ docker-compose pull -Pulling shaarli ... done -Pulling traefik ... done -``` - -### Run! -```shell -root@stretch-shaarli-02:~/shaarli$ docker-compose up -d -Creating network "shaarli_http-proxy" with the default driver -Creating volume "shaarli_traefik-acme" with default driver -Creating volume "shaarli_shaarli-data" with default driver -Creating shaarli_shaarli_1 ... done -Creating shaarli_traefik_1 ... done -``` - -## Conclusion -Congratulations! Your Shaarli instance should be up and running, and available -at `https://`. - -Shaarli installation page - -## Resources -### Related Shaarli documentation -- [Docker 101](../docker/docker-101.md) -- [Shaarli images](../docker/shaarli-images.md) - -### Hosting providers -- [DigitalOcean](https://www.digitalocean.com/) -- [Gandi](https://www.gandi.net/en) -- [OVH](https://www.ovh.co.uk/) -- [RackSpace](https://www.rackspace.com/) -- etc. - -### Domain Names and Registrars -- [Introduction to the Domain Name System (DNS)](https://opensource.com/article/17/4/introduction-domain-name-system-dns) -- [ICANN](https://www.icann.org/) -- [Domain name registrar](https://en.wikipedia.org/wiki/Domain_name_registrar) -- [OVH Domain Registration](https://www.ovh.co.uk/domains/) -- [Gandi Domain Registration](https://www.gandi.net/en/domain) - -### HTTPS and Security -- [Transport Layer Security](https://en.wikipedia.org/wiki/Transport_Layer_Security) -- [Let's Encrypt](https://letsencrypt.org/) - -### Docker -- [Docker Overview](https://docs.docker.com/engine/docker-overview/) -- [Docker Documentation](https://docs.docker.com/) -- [Get Docker CE for Debian](https://docs.docker.com/install/linux/docker-ce/debian/) -- [docker logs](https://docs.docker.com/engine/reference/commandline/logs/) -- [Volumes](https://docs.docker.com/storage/volumes/) -- [Install Docker Compose](https://docs.docker.com/compose/install/) -- [docker-compose logs](https://docs.docker.com/compose/reference/logs/) - -### Træfik -- [Getting Started](https://docs.traefik.io/) -- [Docker backend](https://docs.traefik.io/configuration/backends/docker/) -- [Let's Encrypt and Docker](https://docs.traefik.io/user-guide/docker-and-lets-encrypt/) -- [traefik](https://hub.docker.com/_/traefik/) Docker image diff --git a/doc/md/guides/various-hacks.md b/doc/md/guides/various-hacks.md deleted file mode 100644 index 0cef99df..00000000 --- a/doc/md/guides/various-hacks.md +++ /dev/null @@ -1,24 +0,0 @@ -### Decode datastore content - -To display the array representing the data saved in `data/datastore.php`, use the following snippet: - -```php -$data = "tZNdb9MwFIb... "; -$out = unserialize(gzinflate(base64_decode($data))); -echo "

"; // Pretty printing is love, pretty printing is life
-print_r($out);
-echo "
"; -exit; -``` -This will output the internal representation of the datastore, "unobfuscated" (if this can really be considered obfuscation). - -Alternatively, you can transform to JSON format (and pretty-print if you have `jq` installed): -``` -php -r 'print(json_encode(unserialize(gzinflate(base64_decode(preg_replace("!.*/\* (.+) \*/.*!", "$1", file_get_contents("data/datastore.php")))))));' | jq . -``` - -### See also - -- [Add a new custom field to shaares (example patch)](https://gist.github.com/nodiscc/8b0194921f059d7b9ad89a581ecd482c) -- [Copy an existing Shaarli installation over SSH, and serve it locally](https://gist.github.com/nodiscc/ed161c66e5b028b5299b0a3733d01c77) -- [Create multiple Shaarli instances, generate an HTML index of them](https://gist.github.com/nodiscc/52e711cda3bc47717c16065231cf6b20) diff --git a/doc/md/guides/images/07-installation.jpg b/doc/md/images/07-installation.jpg similarity index 100% rename from doc/md/guides/images/07-installation.jpg rename to doc/md/images/07-installation.jpg diff --git a/doc/md/images/bookmarklet.png b/doc/md/images/bookmarklet.png deleted file mode 100644 index 0262578e467d9e356d49cccc1b4917d276844712..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53346 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 z zW)^D_3fpqZ>1QP;8JuUGAj#6lFN}rGIBEsh96RUwYLtNi=3@avib}t??in*^LhE~H zD>o!i25mLB!Ffgu%*QhudV8L98+V)5_x01HsvNPSbm-G6hE2>BRS7u`m3YM_#SJG| z<-R{_PC=H*WY)X}=E?w#M}Uh5LM2kAs`3GJJkL@3yvtH3m>v_Rnd@T)efl~OTZlm z*}wQ;1uBO0sroCX0WkZQW>O=ZD-8B<&>t4l+pthdfW%+~=tH@NU8Wqtm_*!!C1$kD zqQKT%rLnd|XONsr5^}3}PD4=JlZw=tAx0rB{l$vKt9?Yd!2rfFnX*ob^d%CSR#cp* z)*ynx21`+J6bKNA5lzVBAC4z&@($L0ST1`)VU!x>3&0O__yO*GMsm4JjrpRCnknSD(pmdUR*fDB<7W!vOLGY=)UW?N;( zT}O=7o@auA=oG`8csm#yW;d$M(ZXJ(6KF-KVz5gUqF6&HIav>?(v=FnUslt37Igj$ zQR`ecWDRMJsTo@>EL20rn&8CPkFuf|g8tBA+=$x5YMEJi21KX>EkY4hR^R{m%0AGd z1sOrbGyJ#iBFmL0^2z|^u6x_DE-C*wJuIsgS|~(R`7;VY69KwKcIdrGl`y= z-t+qzqa?s2 zpXsK7XDw}QX-0;TZdkkA>fn)*QB=KrMHFIan{B9~T}iP`TYc(hiUZrSAN)8FN=yi` z)9-J=v;tLWS0@yzS{3tl+X(h+2?#M`I9g?kvKv&|V@iKl2Uu!Z=E>%Krz+x>5~e-q zc<9lliZ*E-4@9Wp&sVg@+lVMbRP|c9ByHSEfr0uy7I~#8GWHSf==y_qXBnU;A(Wp6^Vdvg((s35e zloZ@7!NG^jTrHD^*_mp;8ST@_E(jEus;*25ax009(YUJ*K*-TZdTI%Q6v;|Vj)HzL zt654;9S=dWwpIe&MJyszOkV=BoW4>XfzAe3$=&>Hy;t=BU*8FNBRATNl1A3@{iFu+jTtL%u?YqlvFF?aq zHjvtU4114jDGX!ELg!>=-Slz@uCk0}2WvaLOxCpW?jqFpXw19l98TWkyy)P|V(xhAZ-; zakXJAj4pkwpROl9b2F?bFB)mZie&Jsv+(b$aKQ)I?TH z@z5$mrNxc`H&wO6zrXtJ=c-PWcA=GA+OHy;G_f5!U>%cEHJScxjoCwD3a2pS zRfr7ANkyurJtZg6{FN2K@w}0!h*#fkFW<a@w5M3C5C?c_SlPDmYN=M>_qe zjBePOP6R_iRZH8;fr3*!mnq;zKi%GocAN)h!-~&+POiX=;>@I-nQNyTL%^Wojhr*< zIOk1qZrR$e2MT1x(yi|^Bd;ZogkfMV_bI?Qnbr1HM+>v4!67i&na$iYXRgUCj?Qu* zJIl_>b`lc=D{#N0__2f|=J_D3`?kKHZB0s@+ih*o<(bZ&Y#wYkHSEpi4msz+Tt z-jf1rf{N3BDe%7L?dQc!u|qMvs0>3OrjWl8MJ%}Tr9^K}0ryRGv%rU2AahB9k)^ir zN)_B8lAL`Ni8!qXoPVE7eXaUt0Ko*cU_j^{<<5jENHekbN|k6BuMT8|3Q?FTODPjG zyBQ&NF$huwEu%f3XubllOBpYS0-HhmIRsMs&7)Vnk zT`B~!`msRYP%*6az*+)dazC3D9GqN|Ry;Wq4auf(h&Fni?klPxeQ_%q+gC-09_5XU z+OWVY2vIr{#qU__4{9A@gAU@1s)%mCq#(N*CI#Yb{5r~+2Gc`C(S%aGB^0)v#mx2~;R~d8V3Z!P=t>aUc z^VI~XgpPH*eS5i#b(MiySFj>2wqes5JZV$F8~*;6TIRsDuc#-bGQ$-tD&vaSMz%V zlBHmAPH493s}rC#Y|1;$d<9oqkPWPQqA8(dCs45yD1i_|WF`XkG47)K%}e^G(6_VR ztwoT$?sr-jlS(rz!JOg*wBQh*xU{N-Kn9l;r4sw{-sohVy;{{JkBEm&5A7_bLY1j_ z^vl7nr5Ms+6Q(x$3vsk>NE@8jjf}?pRj#<46Qep93YwYK_v%V3t&Sj)ubpIRSW# zu(6aruK=Q)iW%=(nQAvjsg8((gBNof4d0?8t)v_k*^+esIVWhMqb1MH{ziMouq=VI zJQ}TqZv1G|cu%Ta6Vn)($iw$vapAeyOugK|G$5bYUbUe+Q`Pp1nTMg6Q!zdl#b`|S za#Hn*?Bxo$37W7#)$+$Yk zv4>nesYSPn)OxN+UYG+4ifxefi}eknQ~*uNL8&hb)?-z#EzjKs=rwX?zK;ELE`|r` zUe-he1HHhaWysDmlhr;nQN)Clm_KXMl-Vj2tCc2M$`iOK6b(Ul-PM(_WN~taw8rUu z$F&r!NdrLh-c~IDcUyA}BBNK8TQ{ldr`To*x!0dyVlutf5R&(FoTJZ-V58oYWf0jB z)9~ptmNm@QU;(v*nP$cRH<9QFiCe-Befj$VamdOe+McTC>>(B=CM{-B=_{MD3RS-8JMPb*YPL34L9zt{%f^7KT9J^m z7Kb@aa}>~9XKLs!RNw?xntz)0j`1X2cNpcfrUH5{RH`1wE8li5R15?*aAk835WtEX z3XZ1~!h}=r4~MDx8wFYV z{>G8@S|uW6+6jq;odQRa*JDuc(sF~GlT!gjF=0)(vxWyc_$GuVs7`m%vbGR%ZR;5$={bO9lg|}Y2H{ELTyy5O1(riwvZutf95d@$Rl$z zkpa|rk|;6r4s?*aN{SOq&!I;vafUammStEQru9-l0cZ>@0EuduH-Yv*fPT|B788cv z=4$NY4oSk1^*|p6eSa%eR~x4@SR4k`j=&y061PI-#FAS$3{sP*f;AIximDT5 zqx9(!hyj8pTAkS_-Ek>_J9x8%VYa`qk9QEH>jdX}h&RRya5wN$@qB2S~MKs4_ z6aL96Kz54cCW`5+i8}?|VMic^b$f}N*>30xFe)hONK1diYpb8>eFTj*!Xc$nH?-_GHoALvOEIue~+Bv@)iko zMzxcS=_NUCN;GH6@2ed>stIuT!v|NH3Jo1XMP379<ipTJfVLcF5f#k2HZ|DfTc!65?mXTw!myWUAZYcX;Ur+=uXYJ7Sgz}wxil~A_vT% zWfY;eH$3mFkfskh%`j%FH~Ok?HQ#bABKqvWg{abG1A!>NUMr$eJ{#R&G$Aq1e*&sw zX_(52#e}H%nh8s41;R)ry36o_WQ1*EU|?@Kda0MYX-J7 zPHL0VVx{y{xE+-P7L2TkSj`7=+;N|S!vjk?_lV*I@AVOH{Y3yb-ZF*n&&NK7!NbbQmx}$q0}HBAVAjyz*IK!i4LpRi%m9h<`&VY z6=X$f2-&-kwsQ7V48>A_w-e-0(u(S1wMdMPmCE)&r$_L7GWrU$xZPN3v?}x{Y2AWE zjqeIk$BiFbXQw{!|C<8s6<^K)H3?LtU^{LV(VzAg-gu5_vR4R;ru1jhOVIOQaZwBF z&=r(=K4oZyUCX0XMb#`qM>4*m@Mojv#>1_J^y(pcW!~NPEAmtMdA0AsRSHys;M#u!% zqiRhCxJHy&`XMoUx#(7F#cI)NUsWiZ)*HU#W(e;C!j)>?;pQJ>Ht zMsQ8*!5ROz>jx6L}zbv@c^+A#A*h#qi|=}*9KGyPy2a&R&JRgah(e9y^zfYf+Gi`p8w!2s3U5ML6NTUKCHtM8!M zdDZ*1&{EF0SS5ABB~0ZpDibM0Az++E9waYhfm}ZN#t!c(9g!Kgki4*`hp|fxWXq^g zu1ePOOJL~-cLNdYUNpx#MXy)VI-N;|jpNX{R0>T+VY}i8bIFhcEG8wC8OeC#m5UN? zL*;k4hguJf?p6K#@_LVs_WxnOdFbRg)!9>tZYu=Bl<5w6E^mh^a8oPLe*T$_b? zWtJPu*aYKh>a=3zdVnX+BzVt79^q5{azvT1)y9S+tP!@tEVkvISHa3m-<)dHATpJ` zqS$i)l+CSN{ewU$p0l|Vx9D%oWmi;WqqUk&Br@6q7o|QP+N`&I_@jF=)FpKDKG53U ztkpdp9;_F?>glFG1A+>v3K&KNV5=R)D8j0+7169&ktO0X;xwl_jfp6c2q4##hoj3d zozhapUIJ*NezM!1FXzr^(SM{RsPOZb;3ZpDZN;$&{UEA z1SdU8W)c7%)$dV)$E4mR04Ym`UlRgV_z6M5W@#j7oQv-Es@Z0HhIlTTh($z}K-aF} zjCe`i6ZO0AVxBB7L_0-6)tlXiRxxYW$-AQciQT0Smfl^VJLH$;x zO4W)sP~57Kr%moik=p~8R}-2`fg|FR;{4H`&DV%p;6&phS7Tld&RoXC+G#;f*}mnG zKPX2py_9Ztd4Y}*lSVnlq0)WfE1ouG)@-bql4DN4;U4bR#{4@%o2&lu4~McwuXX?_ zRf2`-1UFLg`qQkW`gBK}$rnUn=Yge~6yX>j>oCsEOe_mNaCA*g6#dBZl&&kOK(-B2 zRDkJsLWGk-sKP0`A;OSFRL|N=KFcR++d*TUr5Y)PMJrsxRcw`~7)jkB2fXsRQYpFG z2?rN5FvH?3c9W%?#p4YU({DyWcCN@*y8o=ZcTzh{!RJqX+(YBn>ll=}#OTH`8(@v1 zSqAE6+c07MVo?H=ogjk2Qm>Vut|d2&>7`0Vr(m0sbCIqISJy$c^>cG%9y3A#bCImG zI%;|Bw72ZmM{|wK8z%!C2Sb=vYX=z1wrW!jZpn-jpeQ&Z?sH(q0Ig_;JEAjp8F-_< z|B|SMm1}Yha+5?qL&!d~t&b8Q8H&eY!_S%ttWxoN$~91i3(bVGeX;n=NB!^!9pCJV zuU7pP8)S#C&w1%nawN~$u&T$qX+|*`t)`2>>k0B%_kmVchn3c8&Nvk&&uxo95KLFH ziCi=sJ|ZeWTNNJ{k_WjkAuD+%x<>&H zL)wFqRed;;bD9R$VuT9J_Ul~t0N_}F3WULTSG@i^%4xPb=Ui7QL9}9!&dRz%%0u-d z=>M^^nKC;lM<#Y@Ms{|dN(oL}Q^OqYpfP-CKdaX3SL`)_b@% zXb6rNe65%y{>YDTW>K=3q|`}Bk+dbsOo706*oZ-3X49BEXJVhoq5gm1j**FSq{vbUf2;8%K{Occ; zy~{77Q!o9;NtO+IvZ)7?dbPvfpL^bu^X3}zred<)VFQ8w4S~Tx?h_1pxC6At0mRco zfri+EpU$vzUf4CECK$^-&0Q+hL4nE(8bdM(OH zmJw>d&VAD)xd;|lf$D13tx&*KiP3qg*>kF!3QX1|NW;H@%hg#ypL;bDrw&be(0#xi zs@ruOoniKv_TG3CJOglpLMCT~JkC~IyEzi3meHv1jP7}|_H$L;#-wDXp1ivMD2=M8 zh=6z_Z)OBV>O#|J2y?>-hc{r4bCn7;DLAexDL0PGRzF@lWhQnA@rJcdZ9AHcJ!j8> zhqs7m#s~xMue7OU?lg(iu9z(}cSF}4rxVzDvkW0pMUrunLN^4r)U59J!;kMP+RNys zmw!b;#woZa!HV?oKl6D%K0=vk22Wo`i8HkkgzTcQJ(s`}#vr_&+(Y`GTpv0-Ldl}w zvQ!J9$h^)N*jaLfH0<73X= zb6Pzqp%`kSkBf^@sYv6es;W@T_`6v>V3vT2N`)0Hx|gap|ITPavny_r?`}$-h6=Y& z*e0#vp@(}EvS_MwWi5pz@BGo9 z=quXG>83q&oEB6X&u^(%CU^r%IAzQ0-tmsFTY2G>dini<#X~2Qz)Aud~zgLx8?gheAs-#=-wa z;1jNTR)uaKa{q*>vSTN59FFRqq!mvw1Ub{=W4ksn8Hk@XjQxa&1a$HF632boIxBSu zWt;V)N9MSJq_dt`TtIYLve9#^@*0wf?nr-bd)Z8*he zIoO1#@}P+P`y!9t*e*-mr(F_c6_3CZ?2s*zn5yqz_OJkn;rOo)`^l$HJ=!B*%Yimk zvIW{Hzwo4||2xw2h&+;EwMrVWkdrkWp)8swki|7LlQ);i!!=lv95Rv7+#qHCLvkRx zBh-ap0&_EWZwpXZu*pbxCPxZ4*qGp4{3D8CyxDt=(0G8x)XTJ&LbFVJEYGE?kB7Lcd2B|H8U;EqaI0W(f7hIp8TTPVNUu%EK=GV%s85kTVf&Wm*U71J#RdOf{ zue@c4*+vj+Xa&+9?HenxSIliYFa<+s4_J2;sv1W~;Z661#Wkg`5yHS+g?>*uI&Jo1 zXmw&}?->phd#py+Iyf5(Z2lhAMelP#88i?WCs^p$c1e-66wH_hSL$gk(w9(sRD&15 z$<#Ct=Tz0E-y7^JAsLa>_l}l|6F(tjD?)Kn3yFdorC-YKW?logHN{LegxLA;>&-UU zT8?$Kk6zfKQ44}+d5#3VbvR^OOxT{aQ5Nu=(1{j*>U=s^beoxyfkj=E*h7#7XTMc( zS}9#E82|E;BC*=?@q0h;v(Fg=?M;qels8ZB!AX_wPdxeG)EkNz@jI6jYg3p|>F)=!rGkLbINZ!!46u^1z)g&lhIIMNRbcStEuOGJz^plWlUCUuXCSsdQ^E6m))&!d9l;iiRpT1 z+(Dgz>X9o@LzCV7Q6glEn+~?8E{0w1O85h{Yh~*lyKQ`C$040$gVAt-BZRy+1zdYC zLp;NgEE#I%y-ZF+<*u)4Q*#e7F~TTfcpy^wg9)@Oee|o{(zT)8PitBP8=_j>x2~f$ z2R+*Y!uDbrO%k=_O9HDI-(}8p-aNf-IkcGvY9n=*Cs9k8!a)-lC3-@u%L~cRg_|G^E%AVD5N#obFY3z+=$r#!GYE8#m;^A z;CP}s(ePQ~H4^t!%XYk7Ar1z>j&C7ZQfcNj@q&t3mTG+((OziR(y2V%?{X^ z;MiP3dW>!&hq3GtobsTE9wwDWZ+lK(tSTIgm18}Iy}UHeF$!qWW5V#G&wY`sj~tQ)AImr7}xq))EPB?Ah_)=zw^x7o;CR!MRIs6N`ZsveYcJ z4jh#tm1x&r0)C|t>gS#XZYr?UeNJ)f2+jdU^0-A_h>;;428sql@J<8qX_P9007=() ztaRh|_J$3upY_a@UkJr=FNvr}Ho25ex6||*P`}#rk zcmBW?Rcp43gcy_9>{lJ9wNArUOWi|FBT->N)?BBg))bIEra`kEhZ^70$#l>h3mUo> zFDPv_VOj5HBJlhQp^uf2Q;kb;l|j0NKD03N+;JviiPN)W@#2ZN=iLsz;v`JetaZiV zzAwOjmq*%}Hy34mL2YX9MkDrytEZS>ubH9o`i9~i%S@J?EsecZXv0{rJ#3f-SI>YQ z*p*{scO&BagcqFGSF~5qH}n-PR<8&dCkLP5z#jW!kF1CUsd~cMIAYsFVla^QND@@( z_oJHy8_{nz^P~FeCtjk|_&)26DG70Ns;cT#VE=2`HN9qUP_b-_cHF{=5A2C(peb}; zmJTCS0fj}3Emne616FoiJT4_x1{2|+C!=H%8GN$Lr_@5)LJ`SSdNx`&V7L;N_Xo1r z=%vo}Uwi_OV>-SRT5=NzY>1FU|A%woddvprsd=u?<=jhJyh(dH<)t#=EX0CpWl05l zf@Y;Em84F#i558d*ti>V;MKw>Uh_@B{JhmaDk31xt|{80jjk)RfXxHw2$HD23O8o|yszf!s{l;J@(G*#)^r~^gFuMS*yHae^MMUv<|veGUoKe z%@}Ljr30@w&-?=UfqFAJMw)P)H9d0CA=0y@>{TM9D>1vix5~@=e(89RMF*h7rlzpU zp1=N9w*!H(O#0^_zywLP*`AH-V$2?i>Md>?9Jk%jmlCvQ zb}Kso;XuJ&WvcSXial+*s7?-}5vjKKZ#`*DjqdgG;HpkuXhUMN!}hf& zlUzY9svwR^kDd7}Q+Q;>`U!D&$FXa&U;qKNdLaic*SeS9_a>K3dUv!@dw=C#^v8kj2Bvq^Un}gfTUM7i7iN-44prd2{vFZz) z!!XO67F!1^Q^w!nh>{G<@u}_C`?U^%9HleKnr_z+IZhl0CaRT?d;##fQ#qZ~73_Mux?t<*z50=vW}AqYXmH{eiEB?USk3=T%le@nLqFmYZ^8_oG6uxaEq#IyyR_jZ zgds|Crpd=4(5_a8GMQyM-md5|$BH`5?@kplXZZ1mMCP};-#k1;;r7 zV}rC*W*i=~O}xIT)a$}4KUxHb8Q8BvBG~3+;HX1&r7SIFBPEQnaA?FOUo`}8<_cSj zSXi>Dh%aE_wIH%3rgtcC4MD$}W{f(dnJ#K~F<-o9X-AfP6s2WsYm)>4?`o0Sda!vU zCK-Rx8!nLb(Ia%dqnCoOq!z2diuQZI|AA^9EBQf(tu=Q(dLt6kc=9vYs$LPPHZ^Ru zrCHH3Q(D`8h*GkF&mDtJr#_##h`6WGp}iD%@=Rqhxt2&U5^g9gvu39z;+O4IR)v7E zk(=W?Yn83Ccr3(?&@+SZ2T8HC8Ue@+H}Z`lV9Op_zN^t=c=9GKP-bjH4D-)a$X3M_ zD2iZ=0wTd!bQa!OGiDnLROia@1@5?4CEcyD-Y`xz>LA@tfBhn(NWFCTtf} zKc1JWeN)$+!jVq3DFH9Q)D}!MZr(bVPDRe9J+oCDTUV(4t3e!0qFmla+*5+dDlO+B@E7fdO4Fx?CXBiY-X-`e(rv{ygxkt&P(5PVIOFZ(kVwTtyqGd zW!J+V@g4OoVO_(bAb`8o*mSXxsc`ebarkp0jcsd!GIQW%0iV}UXHJ;xbstV(nK=F%dZdk9`%iTHvZ4=S9sAe`D4^wTZ(1;0* zCf1c0c&Q<6wx4LrTi=U0WS;gB0+$`yaWGJzxfV40Ws3U88M0psYJT~J@93pRkJ5Gf zKxJWfxRgGP#t|P0Tufvuo z^ZGKo$SAy9sPG&XJzN_7VrPwUo-=OhSeve8@e(g?Wp#D-I&N{ATQO@+V9!aL`+YBgy+a|tw8&lUwgN7dwrnYySyn< zfza`TA9~+rQ1>jdr2#6)MgXH-1+;ALigZ`$;d%O|=8yW_q%#k>7M>OeO2`a$J3gsO zF{(QO^p%rE9fy_E$DnF3f4@*xOL&1dW_#?mglxcL^4XQ#>Q{}7?ofHfdX+QH#HsRF zvYzH>q5aYSS(gbl^_hcJA z0?*%97M;T~goO8fx0lrZO3hVS87SAA22zs2uIU=OHI7z?`HuJ8AL`) z{q6tN(&SlCWfWI%YR>>}+moI9S>z0JlZd)x`7An?<3<_YCfMxvnzfr)K5#H^Zzuab ztc~^#$5w|kwSkc3_|}a2A9f<&{9C`>2iiS4xev5had;5&@cOpzJZoQ*U7KoGO(OKS zfjpBq23r|>s905&B2tuGUAb>T!GBp@B5n+tUC`PdkQeO%ywU!>{wUouo0qcN2w5%iT!o}^>)dyS zaxj0^^Lq^QaoM1o+ak}v2TLQ(GvOn~fj!(SQ{TO^y~5xLor z+iK+~hPf%3VUNfbyZ^HCahs0te!%Vx5b%b(CtIE+|LXhRKL*-s$BMRHNKOIxKKDCw z!v-aqNV`p+!4m8mcXSf8?{c7>DF348XQkH2Weg5kV3)NTja`J;0(1l_G;yc{LXP8Z z-rP1=+0UuUkl7E`TL`&Tt*uHUj<OO8i|+fgglE59^;m@c);O+Akqz4i8Jcx8=H{FKcPA3;BTJk|N4Rd)d$)&ozw?fJ3s4HtarQnH*Z*rx;A8xw^DPD z!bOBZ$tGI;GV?Lw5lm|Tvif$48t7-*0MJ+Pxl{eM3N@h_pv!w`OQ6R=Z*eGAWL;Y3 zQmK7yDBOH2UsJX%9M2m|!&bm%cP|y1Cu4YQN||ZhE(n7(myd7K7OKY78fA66Q?@x8 zVykm|vHDhuHN$i~h>d4yBdn46RhrHX;*7s}OJk+;0%-79g5~~`U>8PCwt$=~{iDZIPjnC3=@LZ?B($r97SH zPEc0@%jGU;LB%W!j?0*hHI#)KRw50Sb)#_=t8OuKaBQ?#bG}jaEjDkT8NA&`;9VwA zzMwGTdFWGSg2sFZ`gLIcLAeFtn(8rEos1i<8;xpSHh08`3eN<%Fm0D&J*r?bYF~6V(zp9)E`a=SMy! zgW>Xt>!Yqfg@Qx-zu)nWr>hM~RWzGrKxgd{YZ%Ty)p^ZPY1S-tR_G@b%_Fz&E&^4% z;@`E|Ar{_7SeTtEf)~Sk%Yun)JlBKFzGp z>_bgUyJoV_Eu|9T+3&Q;d=q@@r{lo3BVNkZox7q3+n?l(_wQ;_?atjc*PPV`P6G{Y zT$VA=cDO`U|DbTj@$F|_Bmt1bItaVkT4+rvq*`~nT-o^uuDErY%;;Z0FHJG@bY}(Ztn&1t_%e3Al}KV^!by2^*6&nDjizyRn;p7+4Q=_El%Cn zLT{P+v4UY2W2MI}pYhXyB@1#!2W`{6ZRmy*9lNAJb+PpvO!++VE9GOq8^=I)VE@>} zm4yV<;9#%XF$AH5L#!$$scR&1RVM?-qfXjVBe{_up$W>BA552E%3Jh)N1fN+V6r#! zSo$tkR%qI{5Xio+*CuC$t0rr5%gVp}Kc5)`?J5S^Ih(xUb=qk+UWAg(9)xv1+msG0 zKOJ~uKJYDI`4+ZGB96D)cQ)+B?Wx?Wa__X>tQ+qd9sC{JUr^gRrpnBIFr%TnA6m`v z52Ks_{KNfdh?%U>~HfNZ$dlN#^#VVxb@^7`*zP8 z_K4$J32tefwYkf-z^rlh#vF$*&?F#k@*h3^S?jjB_wtHvIh1!?1HcmLBtGby%*SoX zT>ZD`7Th(3s%~iW_5X!F&`NVIRS(e|2M+qi(d_vSBNl-yE)VL}ydByy$t~B>8YK_1f{fHv3?tE=hv;s@#;Y zmnp`N<7#r)S807Qv!br~g~rTP+SGTx8eN=`4Apyntq$*NGWvMqAF?q2-&W>Cwp`^0 zz84Q~1=8%+OH96#CN-%dH@-K=I1=F%Q`)g$6KKR+ps{+QO1)KNP_YMpN&w3<8g>ZQ5@t%VY zy35`VA>+1rpX@uMzxt=ke9Lqmy^rn-!_nyNE)HB20qPNN=c@^f{Eq4|m2Cl diff --git a/doc/md/images/firefoxshare.png b/doc/md/images/firefoxshare.png deleted file mode 100644 index 8f8fdba422c491bac285e3a100c416f7d66d6c13..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 715 zcmV;+0yO=JP)rawV9LM>an(rDpMq=DlGy);Hc^N38SUIC*Tftgxy~(uJ zYF%9UBF!|H(Xy?L4d-MwQWE6unint-T5rFdbFj6QaxwS3_@3uHdw%Eld49LU%k9R% zm3SP(qTou$2m2l?4cjBj|0By`6QL`qGyf-qfX=D&oJt>sFO?4XSYErIa3ZSj7mfuE_mF;c`YR3 zTHGJK1Cvn)nN++FJN#_fu-tATqOm9UAP}5l&L>~KKwGN;9qlI6*U63tM(dN_48k$c z2cb{^MME9No<4%hIS9Q@3u~7d4fXPJVCyI*IzOSn0yND^$Ym0!&NX4^mJ9FRPC|KB z0gJgEJ+?0P1Jt&dFJ3maj>AMFns>i~SS&(QV*_+88aOUrLL#~fwMq$-Nsn%;lUqw2<%TCg7fXjTQeA(mP4S zA^|o}v(w}F^zjYu4&TPoq6g0FgD@Ie7_<;sg)pi-x#RMgD7_WN xw`7pDa;0YpYz=!-Bo>+VmloS8}002ovPDHLkV1oFwLa6`% diff --git a/doc/md/images/install-shaarli.png b/doc/md/images/install-shaarli.png deleted file mode 100644 index d5d5baa7069b6d506a7db380ff3c7e41587981a3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33827 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 diff --git a/doc/md/index.md b/doc/md/index.md index 1431f9e1..2c4995f8 100644 --- a/doc/md/index.md +++ b/doc/md/index.md @@ -2,21 +2,19 @@ The personal, minimalist, super-fast, database free, bookmarking service. -Do you want to share the links you discover? -Shaarli is a minimalist bookmark manager and link sharing service that you can install on your own server. -It is designed to be personal (single-user), fast and handy. - - +Do you want to share the links you discover? Shaarli is a minimalist bookmark manager and link sharing service that you can install on your own server. It is designed to be personal (single-user), fast and handy. Visit the pages in the sidebar to find information on how to setup, use, configure, tweak and troubleshoot Shaarli. - * [GitHub project page](https://github.com/shaarli/Shaarli) -* [Online documentation](https://shaarli.readthedocs.io/) -* [Latest releases](https://github.com/shaarli/Shaarli/releases) +* [Documentation](https://shaarli.readthedocs.io/) * [Changelog](https://github.com/shaarli/Shaarli/blob/master/CHANGELOG.md) +[![](https://i.imgur.com/8wEBRSG.png)](https://i.imgur.com/WWPfSj0.png) [![](https://i.imgur.com/93PpLLs.png)](https://i.imgur.com/V09kAQt.png) [![](https://i.imgur.com/rrsjWYy.png)](https://i.imgur.com/TZzGHMs.png) [![](https://i.imgur.com/8iRzHfe.png)](https://i.imgur.com/sfJJ6NT.png) [![](https://i.imgur.com/GjZGvIh.png)](https://i.imgur.com/QsedIuJ.png) [![](https://i.imgur.com/TFZ9PEq.png)](https://i.imgur.com/KdtF8Ll.png) [![](https://i.imgur.com/uICDOle.png)](https://i.imgur.com/27wYsbC.png) [![](https://i.imgur.com/tVvD3gH.png)](https://i.imgur.com/zGF4d6L.jpg) + + + ## Demo You can use this [public demo instance of Shaarli](https://demo.shaarli.org). @@ -25,101 +23,80 @@ It runs the latest development version of Shaarli and is updated/reset daily. Login: `demo`; Password: `demo` +## Getting started + +- [Configure your server](Server-configuration.md) +- [Install Shaarli](Installation.md) +- Or install Shaarli using [Docker](Docker.md) + + ## Features Shaarli can be used: -- to share, comment and save interesting links and news +- to share, comment and save interesting links - to bookmark useful/frequent links and share them between computers - as a minimal blog/microblog/writing platform -- as a read-it-later list -- to draft and save articles/posts/ideas -- to keep notes, documentation and code snippets -- as a shared clipboard/notepad/pastebin between machines -- as a todo list -- to store media playlists -- to keep extracts/comments from webpages that may disappear. -- to keep track of ongoing discussions -- to feed other blogs, aggregators, social networks... using RSS feeds +- as a read-it-later/todo list +- as a notepad to draft and save articles/posts/ideas +- as a knowledge base to keep notes, documentation and code snippets +- as a shared clipboard/notepad/pastebin between computers +- as playlist manager for online media +- to feed other blogs, aggregators, social networks... ### Edit, view and search your links -- Minimalist design -- FAST -- Customizable link titles and descriptions -- Tags to organize your links (features tag autocompletion, renaming, merging and deletion) -- Search by tag or using the full-text search -- Public and private links (visible only to logged-in users) -- Unique permalinks for easy reference -- Paginated link list (with image and video thumbnails) -- Tag cloud and list views -- Picture wall: image and video thumbnails view (with lazy loading) -- ATOM and RSS feeds (can also be filtered using tags or text search) -- Daily: newspaper-like daily digest (and daily RSS feed) -- URL cleanup: automatic removal of `?utm_source=...`, `fb=...` -- Extensible through [plugins](https://shaarli.readthedocs.io/en/master/Plugins/#plugin-usage) - -### Easy setup - -- Dead-simple installation: drop the files, open the page -- Links are stored in a file (no database required, easy backup: simply copy the datastore file) -- Import and export links as Netscape bookmarks compatible with most Web browsers - -### Accessibility - -- Bookmarklet and other tools to share links in one click -- Support for mobile browsers -- Degrades gracefully with Javascript disabled -- Easy page customization through HTML/CSS/RainTPL - -### Security - -- Discreet pop-up notification when a new release is available -- Bruteforce protection on the login form -- Protected against [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) and session cookie hijacking +- Editable URL, title, description, tags, private/public status for all your [Shaares](Usage.md) +- [Tags](Usage.md#tags) to organize your Shaares +- [Search](Usage.md#search) in all fields +- Unique [permalinks](Usage.md#permalinks) for easy reference +- Paginated Shaares list view (with image and video thumbnails) +- [Tag cloud/list](Usage#tag-cloud) views +- [Picture wall](Usage#picture-wall)/thumbnails view (with lazy loading) +- [ATOM and RSS feeds](Usage.md#rss-feeds) (can also be filtered using tags or text search) +- [Daily](Usage.md#daily): newspaper-like daily digest (and daily RSS feed) +- URL cleanup: automatic removal of `?utm_source=...`, `fb=...` tracking parameters +- Extensible through [plugins](Plugins.md) +- Easily extensible by any client using the [REST API](REST-API.md) exposed by Shaarli +- Bookmarklet and [other tools](Community-and-related-software.md) to share links in one click +- Responsive/support for mobile browsers, degrades gracefully with Javascript disabled - -### REST API - -- Easily extensible by any client using the REST API exposed by Shaarli ([API documentation](http://shaarli.github.io/api-documentation/)). +### Easy setup +- Dead-simple [installation](Installation.md): drop the files on your server, open the page +- Shaares are stored in a file (no database required, easy [backup](Backup-and-restore.md)) +- [Configurable](Shaarli-configuration.md) from dialog and configuration file +- Extensible through third-party [plugins and themes](Community-and-related-software.md) -## Screenshots +### Fast -[![](https://i.imgur.com/8wEBRSG.png)](https://i.imgur.com/WWPfSj0.png) [![](https://i.imgur.com/rrsjWYy.png)](https://i.imgur.com/TZzGHMs.png) [![](https://i.imgur.com/uICDOle.png)](https://i.imgur.com/27wYsbC.png) [![](https://i.imgur.com/KNvFGVB.png)](https://i.imgur.com/0f5faqw.png) [![](https://i.imgur.com/tVvD3gH.png)](https://i.imgur.com/zGF4d6L.jpg) [![](https://i.imgur.com/8iRzHfe.png)](https://i.imgur.com/sfJJ6NT.png) [![](https://i.imgur.com/GjZGvIh.png)](https://i.imgur.com/QsedIuJ.png) [![](https://i.imgur.com/TFZ9PEq.png)](https://i.imgur.com/KdtF8Ll.png) [![](https://i.imgur.com/IvlqXXK.png)](https://i.imgur.com/boaaibC.png) [![](https://i.imgur.com/nlETouG.png)](https://i.imgur.com/Ib9O7n3.png) +- Fast! Small datastore file, write-once/read-many, served most of the time from OS disk caches (no disk I/O) +- Stays fast with even tens of thousands shaares! +### Self-hosted +- Shaarli is an alternative to commercial services such as StumbleUpon, Delicio.us, Diigo... +- The data is yours, [import and export](Usage#import-export) it to HTML bookmarksformat compatible with most web browser, and from a variety of formats +- Shaarli does not send any telemetry/metrics/private information to developers +- Shaarli is Free and Open-Source software, inspect and change how the program works in the [source code](https://github.com/shaarli/Shaarli) +- Built-in [Security](dev/Development.md#security) features to help you protect your Shaarli instance ## About -### Shaarli community fork - -This friendly fork is maintained by the Shaarli community at - -This is a community fork of the original [Shaarli](https://github.com/sebsauvage/Shaarli/) project by [Sébastien Sauvage](http://sebsauvage.net/). - -The original project is currently unmaintained, and the developer [has informed us](https://github.com/sebsauvage/Shaarli/issues/191) that he would have no time to work on Shaarli in the near future. +This [community fork](https://github.com/shaarli/Shaarli) of the original [Shaarli](https://github.com/sebsauvage/Shaarli/) project by [Sébastien Sauvage](http://sebsauvage.net/) (now [unmaintained](https://github.com/sebsauvage/Shaarli/issues/191)) has carried on the work to provide [many patches](https://github.com/shaarli/Shaarli/compare/sebsauvage:master...master) for [bug fixes and enhancements](https://github.com/shaarli/Shaarli/issues?q=is%3Aclosed+) in this repository, and will keep maintaining the project for the foreseeable future, while keeping Shaarli simple and efficient. -The Shaarli community has carried on the work to provide [many -patches](https://github.com/shaarli/Shaarli/compare/sebsauvage:master...master) for -[bug fixes and enhancements](https://github.com/shaarli/Shaarli/issues?q=is%3Aclosed+) -in this repository, and will keep maintaining the project for the foreseeable -future, while keeping Shaarli simple and efficient. +The original Shaarli instance is still available [here](https://sebsauvage.net/links/) (+25000 shaares!) ### Contributing and getting help -Feedback is very appreciated! +Feedback is very appreciated! Feel free to propose solutions to existing problems, help us improve the documentation and translations, and submit pull requests :-) -- If you have any questions or ideas, please join the [chat](https://gitter.im/shaarli/Shaarli) (also reachable via [IRC](https://irc.gitter.im/)), post them in our [general discussion](https://github.com/shaarli/Shaarli/issues/308) or read the current [issues](https://github.com/shaarli/Shaarli/issues). -- Have a look at the open [issues](https://github.com/shaarli/Shaarli/issues) and [pull requests](https://github.com/shaarli/Shaarli/pulls) -- If you would like a feature added to Shaarli, check the issues labeled [`feature`](https://github.com/shaarli/Shaarli/labels/feature), [`enhancement`](https://github.com/shaarli/Shaarli/labels/enhancement), and [`plugin`](https://github.com/shaarli/Shaarli/labels/plugin). -- If you've found a bug, please create a [new issue](https://github.com/shaarli/Shaarli/issues/new). -- Feel free to propose solutions to existing problems, help us improve the documentation and translations, and submit pull requests :-) +See [Support](Troubleshooting.md#support) to get in touch with the Shaarli community. ### License diff --git a/mkdocs.yml b/mkdocs.yml index cee2c5fb..2e201d03 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -15,41 +15,25 @@ site_dir: doc/html pages: - Home: index.md - Setup: - - Download and Installation: Download-and-Installation.md - - Upgrade and migration: Upgrade-and-migration.md - Server configuration: Server-configuration.md - - Server security: Server-security.md + - Installation: Installation.md + - Docker: Docker.md + - Reverse Proxy: Reverse-proxy.md + - Backup and restore: Backup-and-restore.md - Shaarli configuration: Shaarli-configuration.md - Plugins: Plugins.md -- Docker: - - Docker 101: docker/docker-101.md - - Shaarli images: docker/shaarli-images.md - - Reverse proxy configuration: docker/reverse-proxy-configuration.md - - Docker resources: docker/resources.md + - Upgrade and migration: Upgrade-and-migration.md - Usage: - - Browsing and searching: Browsing-and-searching.md - - Sharing content: Sharing-content.md - - RSS feeds: RSS-feeds.md + - Usage: Usage.md - REST API: REST-API.md - - Community & Related software: Community-&-Related-software.md -- Guides: - - Install Shaarli on Debian 9 with Docker: guides/install-shaarli-with-debian9-and-docker.md - - Backup, restore, import and export: guides/backup-restore-import-export.md - - Various hacks: guides/various-hacks.md + - Community and Related software: Community-and-related-software.md - Development: - - Development guidelines: Development-guidelines.md - - Continuous integration tools: Continuous-integration-tools.md - - GnuPG signature: GnuPG-signature.md - - Directory structure: Directory-structure.md - - Link Structure: Link-structure.md - - 3rd party libraries: 3rd-party-libraries.md - - Plugin System: Plugin-System.md - - Release Shaarli: Release-Shaarli.md - - Versioning and Branches: Versioning-and-Branches.md - - Security: Security.md - - Static analysis: Static-analysis.md - - Translations: Translations.md - - Theming: Theming.md - - Unit tests: Unit-tests.md -- FAQ: FAQ.md + - Development: dev/Development.md + - Versioning: dev/Versioning.md + - GnuPG signature: dev/GnuPG-signature.md + - Plugin System: dev/Plugin-system.md + - Translations: dev/Translations.md + - Release Shaarli: dev/Release-Shaarli.md + - Theming: dev/Theming.md + - Unit tests: dev/Unit-tests.md - Troubleshooting: Troubleshooting.md diff --git a/plugins/playvideos/README.md b/plugins/playvideos/README.md index ab4be22a..32a94e88 100644 --- a/plugins/playvideos/README.md +++ b/plugins/playvideos/README.md @@ -8,22 +8,21 @@ This uses code from https://zaius.github.io/youtube_playlist/ and is currently o #### Installation and setup -This is a default Shaarli plugin, you just have to enable it. See https://shaarli.readthedocs.io/en/master/Shaarli-configuration/ +This is a default Shaarli plugin, you just have to enable it. See [Shaarli configuration](../../doc/md/Shaarli-configuration.md). #### Troubleshooting -If your server has [Content Security Policy](http://content-security-policy.com/) headers enabled, this may prevent the script from loading fully. You should relax the CSP in your server settings. Example CSP rule for apache2: - -In `/etc/apache2/conf-available/shaarli-csp.conf`: +If your server has [Content Security Policy](http://content-security-policy.com/) headers enabled, this may prevent the script from loading fully. You should relax the CSP in your server settings. Example CSP rule for apache2: ```apache + # Required for playvideos plugin Header set Content-Security-Policy "script-src 'self' 'unsafe-inline' https://www.youtube.com https://s.ytimg.com 'unsafe-eval'" ``` -Then run `a2enconf shaarli-csp; service apache2 reload` +You may place the `Header` directive in the `