From: ArthurHoaro Date: Sat, 25 May 2019 14:13:56 +0000 (+0200) Subject: Merge pull request #1273 from ArthurHoaro/feature/ban-manager X-Git-Tag: v0.11.0~14 X-Git-Url: https://git.immae.eu/?p=github%2Fshaarli%2FShaarli.git;a=commitdiff_plain;h=c3a04e328f2c8d40890b0b26b118a193110634ce;hp=b49a04f796b9ad8533c53fee541132a4c2214909 Merge pull request #1273 from ArthurHoaro/feature/ban-manager Rewrite IP ban management --- diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 00000000..09e5a6a5 --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,15 @@ +# .readthedocs.yml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Build documentation with MkDocs +mkdocs: + configuration: mkdocs.yml + +# Optionally set the version of Python and requirements required to build your docs +# https://github.com/rtfd/readthedocs.org/issues/5250 +python: + version: 3.5 \ No newline at end of file diff --git a/AUTHORS b/AUTHORS index db23ad32..7fa2734a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,13 +1,15 @@ - 687 ArthurHoaro - 355 VirtualTam - 195 nodiscc + 734 ArthurHoaro + 400 VirtualTam + 215 nodiscc 56 Sébastien Sauvage 15 Florian Eula 13 Emilien Klein 12 Nicolas Danelon 9 Willi Eggeling 8 Christophe HENRY + 7 Luce Carević 6 B. van Berkum + 6 llune 5 Lucas Cimon 5 Mark Schmitz 5 kalvn @@ -15,7 +17,7 @@ 4 David Sferruzza 4 Immánuel Fodor 3 Teromene - 3 llune + 2 Alexandre G.-Raymond 2 Chris Kuethe 2 Felix Bartels 2 Knah Tsaeb @@ -27,11 +29,12 @@ 2 julienCXX 2 philipp-r 2 pips + 2 trailjeep 1 Adrien Oliva 1 Adrien le Maire - 1 Alexandre G.-Raymond 1 Alexis J 1 Angristan + 1 Bish Erbas <42714627+bisherbas@users.noreply.github.com> 1 BoboTiG 1 Bronco 1 Buster One <37770318+buster-one@users.noreply.github.com> diff --git a/CHANGELOG.md b/CHANGELOG.md index aa1f0d8a..865e0370 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,41 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). + +## [v0.10.4](https://github.com/shaarli/Shaarli/releases/tag/v0.10.4) - 2019-04-16 +### Fixed +- Fix thumbnails disabling if PHP GD is not installed +- Fix a warning if links sticky status isn't set + +## [v0.10.3](https://github.com/shaarli/Shaarli/releases/tag/v0.10.3) - 2019-02-23 +### Added +- Add OpenGraph metadata tags on permalink page +- Add CORS headers to REST API reponses +- Add a button to toggle checkboxes of displayed links +- Add an icon to the link list when the Isso plugin is enabled +- Add noindex, nofollow to documentation pages +- Document usage of robots.txt +- Add a button to set links as sticky + +### Changed +- Update French translation +- Refactor the documentation homepage +- Bump netscape-bookmark-parser +- Update session_start condition +- Improve accessibility +- Cleanup and refactor lint tooling + +### Fixed +- Fix input size for dropdown search form +- Fix history for bulk link deletion +- Fix thumbnail requests +- Fix hashtag rendering when markdown escaping is enabled +- Fix AJAX tag deletion +- Fix lint errors and improve PSR-1 and PSR-2 compliance + +### Removed +- Remove Firefox Share documentation + ## [v0.10.2](https://github.com/shaarli/Shaarli/releases/tag/v0.10.2) - 2018-08-11 ### Fixed @@ -12,7 +47,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [v0.10.1](https://github.com/shaarli/Shaarli/releases/tag/v0.10.1) - 2018-08-11 -### Changed +### Changed - Accessibility: - Remove alt text on the logo @@ -46,7 +81,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Use Travis matrix and stages to run Javascript tests in a dedicated environment - Add tag endpoint in the REST API - Build the documentation in Travis builds -- Provide a Docker Compose example +- Provide a Docker Compose example ### Changed - Use web-thumbnailer to retrieve thumbnails (see #687) diff --git a/README.md b/README.md index 0e23e33d..21f2eae7 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,10 @@ _It is designed to be personal (single-user), fast and handy._ [![](https://img.shields.io/badge/stable-v0.9.7-blue.svg)](https://github.com/shaarli/Shaarli/releases/tag/v0.9.7) [![](https://img.shields.io/travis/shaarli/Shaarli/stable.svg?label=stable)](https://travis-ci.org/shaarli/Shaarli) • -[![](https://img.shields.io/badge/latest-v0.10.2-blue.svg)](https://github.com/shaarli/Shaarli/releases/tag/v0.10.2) +[![](https://img.shields.io/badge/latest-v0.10.4-blue.svg)](https://github.com/shaarli/Shaarli/releases/tag/v0.10.4) [![](https://img.shields.io/travis/shaarli/Shaarli/latest.svg?label=latest)](https://travis-ci.org/shaarli/Shaarli) • -[![](https://img.shields.io/badge/master-v0.10.x-blue.svg)](https://github.com/shaarli/Shaarli) +[![](https://img.shields.io/badge/master-v0.11.x-blue.svg)](https://github.com/shaarli/Shaarli) [![](https://img.shields.io/travis/shaarli/Shaarli.svg?label=master)](https://travis-ci.org/shaarli/Shaarli) [![Join the chat at https://gitter.im/shaarli/Shaarli](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/shaarli/Shaarli) diff --git a/application/Router.php b/application/Router.php index 05877acd..d7187487 100644 --- a/application/Router.php +++ b/application/Router.php @@ -38,6 +38,8 @@ class Router public static $PAGE_DELETELINK = 'delete_link'; + public static $PAGE_CHANGE_VISIBILITY = 'change_visibility'; + public static $PAGE_PINLINK = 'pin'; public static $PAGE_EXPORT = 'export'; @@ -149,6 +151,10 @@ class Router return self::$PAGE_DELETELINK; } + if (isset($get[self::$PAGE_CHANGE_VISIBILITY])) { + return self::$PAGE_CHANGE_VISIBILITY; + } + if (startsWith($query, 'do=' . self::$PAGE_PINLINK)) { return self::$PAGE_PINLINK; } diff --git a/application/Thumbnailer.php b/application/Thumbnailer.php index a23f98e9..d5f5ac28 100644 --- a/application/Thumbnailer.php +++ b/application/Thumbnailer.php @@ -55,7 +55,7 @@ class Thumbnailer $this->conf = $conf; if (! $this->checkRequirements()) { - $this->conf->set('thumbnails.enabled', false); + $this->conf->set('thumbnails.mode', Thumbnailer::MODE_NONE); $this->conf->write(true); // TODO: create a proper error handling system able to catch exceptions... die(t( diff --git a/application/api/ApiMiddleware.php b/application/api/ApiMiddleware.php index 5ffb8c6d..2d55bda6 100644 --- a/application/api/ApiMiddleware.php +++ b/application/api/ApiMiddleware.php @@ -129,9 +129,7 @@ class ApiMiddleware $linkDb = new \Shaarli\Bookmark\LinkDB( $conf->get('resource.datastore'), true, - $conf->get('privacy.hide_public_links'), - $conf->get('redirector.url'), - $conf->get('redirector.encode_url') + $conf->get('privacy.hide_public_links') ); $this->container['db'] = $linkDb; } diff --git a/application/api/ApiUtils.php b/application/api/ApiUtils.php index 1824b5d0..1e3ac02e 100644 --- a/application/api/ApiUtils.php +++ b/application/api/ApiUtils.php @@ -59,7 +59,7 @@ class ApiUtils { $out['id'] = $link['id']; // Not an internal link - if ($link['url'][0] != '?') { + if (! is_note($link['url'])) { $out['url'] = $link['url']; } else { $out['url'] = $indexUrl . $link['url']; diff --git a/application/bookmark/LinkDB.php b/application/bookmark/LinkDB.php index c13a1141..efde8468 100644 --- a/application/bookmark/LinkDB.php +++ b/application/bookmark/LinkDB.php @@ -29,10 +29,10 @@ use Shaarli\FileUtils; * - private: Is this link private? 0=no, other value=yes * - tags: tags attached to this entry (separated by spaces) * - title Title of the link - * - url URL of the link. Used for displayable links (no redirector, relative, etc.). - * Can be absolute or relative. - * Relative URLs are permalinks (e.g.'?m-ukcw') - * - real_url Absolute processed URL. + * - url URL of the link. Used for displayable links. + * Can be absolute or relative in the database but the relative links + * will be converted to absolute ones in templates. + * - real_url Raw URL in stored in the DB (absolute or relative). * - shorturl Permalink smallhash * * Implements 3 interfaces: @@ -88,19 +88,6 @@ class LinkDB implements Iterator, Countable, ArrayAccess // Hide public links private $hidePublicLinks; - // link redirector set in user settings. - private $redirector; - - /** - * Set this to `true` to urlencode link behind redirector link, `false` to leave it untouched. - * - * Example: - * anonym.to needs clean URL while dereferer.org needs urlencoded URL. - * - * @var boolean $redirectorEncode parameter: true or false - */ - private $redirectorEncode; - /** * Creates a new LinkDB * @@ -109,22 +96,16 @@ class LinkDB implements Iterator, Countable, ArrayAccess * @param string $datastore datastore file path. * @param boolean $isLoggedIn is the user logged in? * @param boolean $hidePublicLinks if true all links are private. - * @param string $redirector link redirector set in user settings. - * @param boolean $redirectorEncode Enable urlencode on redirected urls (default: true). */ public function __construct( $datastore, $isLoggedIn, - $hidePublicLinks, - $redirector = '', - $redirectorEncode = true + $hidePublicLinks ) { $this->datastore = $datastore; $this->loggedIn = $isLoggedIn; $this->hidePublicLinks = $hidePublicLinks; - $this->redirector = $redirector; - $this->redirectorEncode = $redirectorEncode === true; $this->check(); $this->read(); } @@ -271,7 +252,8 @@ You use the community supported version of the original Shaarli project, by Seba ), 'private' => 0, 'created' => new DateTime(), - 'tags' => 'opensource software' + 'tags' => 'opensource software', + 'sticky' => false, ); $link['shorturl'] = link_small_hash($link['created'], $link['id']); $this->links[1] = $link; @@ -284,6 +266,7 @@ You use the community supported version of the original Shaarli project, by Seba 'private' => 1, 'created' => new DateTime('1 minute ago'), 'tags' => 'secretstuff', + 'sticky' => false, ); $link['shorturl'] = link_small_hash($link['created'], $link['id']); $this->links[0] = $link; @@ -323,17 +306,9 @@ You use the community supported version of the original Shaarli project, by Seba $link['tags'] = preg_replace('/(^|\s+)\.[^($|\s)]+\s*/', ' ', $link['tags']); } - // Do not use the redirector for internal links (Shaarli note URL starting with a '?'). - if (!empty($this->redirector) && !startsWith($link['url'], '?')) { - $link['real_url'] = $this->redirector; - if ($this->redirectorEncode) { - $link['real_url'] .= urlencode(unescape($link['url'])); - } else { - $link['real_url'] .= $link['url']; - } - } else { - $link['real_url'] = $link['url']; - } + $link['real_url'] = $link['url']; + + $link['sticky'] = isset($link['sticky']) ? $link['sticky'] : false; // To be able to load links before running the update, and prepare the update if (!isset($link['created'])) { diff --git a/application/bookmark/LinkUtils.php b/application/bookmark/LinkUtils.php index de5b61cb..35a5b290 100644 --- a/application/bookmark/LinkUtils.php +++ b/application/bookmark/LinkUtils.php @@ -133,29 +133,15 @@ function count_private($links) * In a string, converts URLs to clickable links. * * @param string $text input string. - * @param string $redirector if a redirector is set, use it to gerenate links. - * @param bool $urlEncode Use `urlencode()` on the URL after the redirector or not. * * @return string returns $text with all links converted to HTML links. * * @see Function inspired from http://www.php.net/manual/en/function.preg-replace.php#85722 */ -function text2clickable($text, $redirector = '', $urlEncode = true) +function text2clickable($text) { $regex = '!(((?:https?|ftp|file)://|apt:|magnet:)\S+[a-z0-9\(\)]/?)!si'; - - if (empty($redirector)) { - return preg_replace($regex, '$1', $text); - } - // Redirector is set, urlencode the final URL. - return preg_replace_callback( - $regex, - function ($matches) use ($redirector, $urlEncode) { - $url = $urlEncode ? urlencode($matches[1]) : $matches[1]; - return ''. $matches[1] .''; - }, - $text - ); + return preg_replace($regex, '$1', $text); } /** @@ -197,15 +183,13 @@ function space2nbsp($text) * Format Shaarli's description * * @param string $description shaare's description. - * @param string $redirector if a redirector is set, use it to gerenate links. - * @param bool $urlEncode Use `urlencode()` on the URL after the redirector or not. * @param string $indexUrl URL to Shaarli's index. * @return string formatted description. */ -function format_description($description, $redirector = '', $urlEncode = true, $indexUrl = '') +function format_description($description, $indexUrl = '') { - return nl2br(space2nbsp(hashtag_autolink(text2clickable($description, $redirector, $urlEncode), $indexUrl))); + return nl2br(space2nbsp(hashtag_autolink(text2clickable($description), $indexUrl))); } /** @@ -220,3 +204,16 @@ function link_small_hash($date, $id) { return smallHash($date->format(LinkDB::LINK_DATE_FORMAT) . $id); } + +/** + * Returns whether or not the link is an internal note. + * Its URL starts by `?` because it's actually a permalink. + * + * @param string $linkUrl + * + * @return bool true if internal note, false otherwise. + */ +function is_note($linkUrl) +{ + return isset($linkUrl[0]) && $linkUrl[0] === '?'; +} diff --git a/application/config/ConfigManager.php b/application/config/ConfigManager.php index e6c35073..30993928 100644 --- a/application/config/ConfigManager.php +++ b/application/config/ConfigManager.php @@ -221,7 +221,6 @@ class ConfigManager 'general.title', 'general.header_link', 'privacy.default_private_links', - 'redirector.url', ); // Only logged in user can alter config. @@ -381,9 +380,6 @@ class ConfigManager // default state of the 'remember me' checkbox of the login form $this->setEmpty('privacy.remember_user_default', true); - $this->setEmpty('redirector.url', ''); - $this->setEmpty('redirector.encode_url', true); - $this->setEmpty('thumbnails.width', '125'); $this->setEmpty('thumbnails.height', '90'); diff --git a/application/feed/FeedBuilder.php b/application/feed/FeedBuilder.php index b66f2f91..7c859474 100644 --- a/application/feed/FeedBuilder.php +++ b/application/feed/FeedBuilder.php @@ -147,8 +147,8 @@ class FeedBuilder protected function buildItem($link, $pageaddr) { $link['guid'] = $pageaddr . '?' . $link['shorturl']; - // Check for both signs of a note: starting with ? and 7 chars long. - if ($link['url'][0] === '?' && strlen($link['url']) === 7) { + // Prepend the root URL for notes + if (is_note($link['url'])) { $link['url'] = $pageaddr . $link['url']; } if ($this->usePermalinks === true) { @@ -156,7 +156,7 @@ class FeedBuilder } else { $permalink = '' . t('Permalink') . ''; } - $link['description'] = format_description($link['description'], '', false, $pageaddr); + $link['description'] = format_description($link['description'], $pageaddr); $link['description'] .= PHP_EOL . '
— ' . $permalink; $pubDate = $link['created']; diff --git a/application/netscape/NetscapeBookmarkUtils.php b/application/netscape/NetscapeBookmarkUtils.php index 2fb1a4a6..28665941 100644 --- a/application/netscape/NetscapeBookmarkUtils.php +++ b/application/netscape/NetscapeBookmarkUtils.php @@ -54,7 +54,7 @@ class NetscapeBookmarkUtils $link['timestamp'] = $date->getTimestamp(); $link['taglist'] = str_replace(' ', ',', $link['tags']); - if (startsWith($link['url'], '?') && $prependNoteUrl) { + if (is_note($link['url']) && $prependNoteUrl) { $link['url'] = $indexUrl . $link['url']; } diff --git a/application/render/PageBuilder.php b/application/render/PageBuilder.php index 0569b67f..3f86fc26 100644 --- a/application/render/PageBuilder.php +++ b/application/render/PageBuilder.php @@ -123,6 +123,8 @@ class PageBuilder $this->tpl->assign('hide_timestamps', $this->conf->get('privacy.hide_timestamps', false)); $this->tpl->assign('token', $this->token); + $this->tpl->assign('language', $this->conf->get('translation.language')); + if ($this->linkDB !== null) { $this->tpl->assign('tags', $this->linkDB->linksCountPerTag()); } diff --git a/application/updater/Updater.php b/application/updater/Updater.php index f12e3516..beb9ea9b 100644 --- a/application/updater/Updater.php +++ b/application/updater/Updater.php @@ -218,7 +218,6 @@ class Updater try { $this->conf->set('general.title', escape($this->conf->get('general.title'))); $this->conf->set('general.header_link', escape($this->conf->get('general.header_link'))); - $this->conf->set('redirector.url', escape($this->conf->get('redirector.url'))); $this->conf->write($this->isLoggedIn); } catch (Exception $e) { error_log($e->getMessage()); @@ -550,4 +549,14 @@ class Updater return true; } + + /** + * Remove redirector settings. + */ + public function updateMethodRemoveRedirector() + { + $this->conf->remove('redirector'); + $this->conf->write(true); + return true; + } } diff --git a/assets/default/img/icon.png b/assets/default/img/icon.png deleted file mode 100644 index 474edec3..00000000 Binary files a/assets/default/img/icon.png and /dev/null differ diff --git a/assets/default/js/base.js b/assets/default/js/base.js index 99e03370..d5c29c69 100644 --- a/assets/default/js/base.js +++ b/assets/default/js/base.js @@ -466,6 +466,28 @@ function init(description) { }); } + const changeVisibilityButtons = document.querySelectorAll('.actions-change-visibility'); + if (changeVisibilityButtons != null && token != null) { + [...changeVisibilityButtons].forEach((button) => { + button.addEventListener('click', (event) => { + event.preventDefault(); + const visibility = event.target.getAttribute('data-visibility'); + + const links = []; + const linkCheckedCheckboxes = document.querySelectorAll('.link-checkbox:checked'); + [...linkCheckedCheckboxes].forEach((checkbox) => { + links.push({ + id: checkbox.value, + title: document.querySelector(`.linklist-item[data-id="${checkbox.value}"] .linklist-link`).innerHTML, + }); + }); + + const ids = links.map(item => item.id); + window.location = `?change_visibility&token=${token.value}&newVisibility=${visibility}&ids=${ids.join('+')}`; + }); + }); + } + /** * Select all button */ diff --git a/assets/default/scss/shaarli.scss b/assets/default/scss/shaarli.scss index 760d8d6a..9e5464a0 100644 --- a/assets/default/scss/shaarli.scss +++ b/assets/default/scss/shaarli.scss @@ -1,6 +1,6 @@ -$fa-font-path: '~font-awesome/fonts'; +$fa-font-path: '~fork-awesome/fonts'; -@import '~font-awesome/scss/font-awesome'; +@import '~fork-awesome/scss/fork-awesome'; @import '~purecss/build/pure.css'; @import '~purecss/build/grids-responsive.css'; @import '~pure-extras/css/pure-extras.css'; @@ -13,7 +13,7 @@ $dark-grey: #252525; $light-grey: #797979; $main-green: #1b926c; $light-green: #b0ddce; -$dark-green: #2a4c41; +$dark-green: #186446; $red: #ac2925; $orange: #f89406; $blue: #0b5ea6; @@ -544,7 +544,10 @@ body, color: $dark-grey; font-size: .9em; + a { + display: inline-block; + margin: 3px 0; padding: 5px 8px; text-decoration: none; } @@ -713,11 +716,19 @@ body, } } + .label { + font-family: Arial, sans-serif; + font-size: .65em; + } + .label-private { border: solid 1px $orange; color: $orange; - font-family: Arial, sans-serif; - font-size: .65em; + } + + .label-sticky { + border: solid 1px $blue; + color: $blue; } } @@ -1600,3 +1611,17 @@ form { white-space: nowrap; } } + +// Print rules +@media print { + .shaarli-menu { + position: absolute; + } + + .search-linklist, + .link-count-block, + .linklist-item-infos-controls-group, + .mobile-buttons { + display: none; + } +} diff --git a/doc/md/Download-and-Installation.md b/doc/md/Download-and-Installation.md index 14649e06..8c9e8a32 100644 --- a/doc/md/Download-and-Installation.md +++ b/doc/md/Download-and-Installation.md @@ -24,11 +24,11 @@ Using one of the following methods: 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.9.7` +The current latest released version is `v0.10.4` ```bash -$ wget https://github.com/shaarli/Shaarli/releases/download/v0.9.7/shaarli-v0.9.7-full.zip -$ unzip shaarli-v0.9.7-full.zip +$ 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/ ``` diff --git a/doc/md/Server-configuration.md b/doc/md/Server-configuration.md index 0930a489..88eed8e6 100644 --- a/doc/md/Server-configuration.md +++ b/doc/md/Server-configuration.md @@ -17,7 +17,7 @@ Version | Status | Shaarli compatibility :---:|:---:|:---: 7.2 | Supported | Yes 7.1 | Supported | Yes -7.0 | Supported | Yes +7.0 | EOL: 2018-12-03 | Yes (up to Shaarli 0.10.x) 5.6 | EOL: 2018-12-31 | Yes (up to Shaarli 0.10.x) 5.5 | EOL: 2016-07-10 | Yes 5.4 | EOL: 2015-09-14 | Yes (up to Shaarli 0.8.x) diff --git a/doc/md/Shaarli-configuration.md b/doc/md/Shaarli-configuration.md index 920c7e27..a931ab1e 100644 --- a/doc/md/Shaarli-configuration.md +++ b/doc/md/Shaarli-configuration.md @@ -4,7 +4,7 @@ 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[](.html) +* 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 @@ -32,13 +32,13 @@ On a Linux distribution: - 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[](.html) +- 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.html). +See also [Plugin System](Plugin-System). ### Credentials @@ -120,11 +120,6 @@ Must be an associative array: `translation domain => translation path`. - **enable_thumbnails**: Enable or disable thumbnail display. - **enable_localcache**: Enable or disable local cache. -### Redirector - -- **url**: Redirector URL, such as `anonym.to`. -- **encode_url**: Enable this if the redirector needs encoded URL to work properly. - ## Configuration file example ```json @@ -185,8 +180,6 @@ Must be an associative array: `translation domain => translation path`. "hide_public_links": false, "hide_timestamps": false, "open_shaarli": false, - "redirector": "http://anonym.to/?", - "redirector_encode_url": false }, "general": { "header_link": "?", @@ -218,10 +211,6 @@ Must be an associative array: `translation domain => translation path`. "enable_thumbnails": true, "enable_localcache": true }, - "redirector": { - "url": "http://anonym.to/?", - "encode_url": false - }, "plugins": { "WALLABAG_URL": "http://demo.wallabag.org", "WALLABAG_VERSION": "1" diff --git a/inc/languages/fr/LC_MESSAGES/shaarli.po b/inc/languages/fr/LC_MESSAGES/shaarli.po index 102c80da..649f6dd5 100644 --- a/inc/languages/fr/LC_MESSAGES/shaarli.po +++ b/inc/languages/fr/LC_MESSAGES/shaarli.po @@ -1255,6 +1255,9 @@ msgstr "" #~ msgid "Selection" #~ msgstr "Choisir" +#~ msgid "Select all" +#~ msgstr "Tout sélectionner" + #~ msgid "Public" #~ msgstr "Publics" diff --git a/index.php b/index.php index a27681dc..ff42114c 100644 --- a/index.php +++ b/index.php @@ -312,9 +312,7 @@ function showDailyRSS($conf, $loginManager) $LINKSDB = new LinkDB( $conf->get('resource.datastore'), $loginManager->isLoggedIn(), - $conf->get('privacy.hide_public_links'), - $conf->get('redirector.url'), - $conf->get('redirector.encode_url') + $conf->get('privacy.hide_public_links') ); /* Some Shaarlies may have very few links, so we need to look @@ -356,13 +354,9 @@ function showDailyRSS($conf, $loginManager) // We pre-format some fields for proper output. foreach ($links as &$link) { - $link['formatedDescription'] = format_description( - $link['description'], - $conf->get('redirector.url'), - $conf->get('redirector.encode_url') - ); + $link['formatedDescription'] = format_description($link['description']); $link['timestamp'] = $link['created']->getTimestamp(); - if (startsWith($link['url'], '?')) { + if (is_note($link['url'])) { $link['url'] = index_url($_SERVER) . $link['url']; // make permalink URL absolute } } @@ -433,11 +427,7 @@ function showDaily($pageBuilder, $LINKSDB, $conf, $pluginManager, $loginManager) $taglist = explode(' ', $link['tags']); uasort($taglist, 'strcasecmp'); $linksToDisplay[$key]['taglist']=$taglist; - $linksToDisplay[$key]['formatedDescription'] = format_description( - $link['description'], - $conf->get('redirector.url'), - $conf->get('redirector.encode_url') - ); + $linksToDisplay[$key]['formatedDescription'] = format_description($link['description']); $linksToDisplay[$key]['timestamp'] = $link['created']->getTimestamp(); } @@ -1074,7 +1064,6 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager, $PAGE->assign('api_enabled', $conf->get('api.enabled', true)); $PAGE->assign('api_secret', $conf->get('api.secret')); $PAGE->assign('languages', Languages::getAvailableLanguages()); - $PAGE->assign('language', $conf->get('translation.language')); $PAGE->assign('gd_enabled', extension_loaded('gd')); $PAGE->assign('thumbnails_mode', $conf->get('thumbnails.mode', Thumbnailer::MODE_NONE)); $PAGE->assign('pagetitle', t('Configure') .' - '. $conf->get('general.title', 'Shaarli')); @@ -1176,11 +1165,15 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager, $link['title'] = $link['url']; } - if ($conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE) { + if ($conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE + && ! is_note($link['url']) + ) { $thumbnailer = new Thumbnailer($conf); $link['thumbnail'] = $thumbnailer->get($url); } + $link['sticky'] = isset($link['sticky']) ? $link['sticky'] : false; + $pluginManager->executeHooks('save_link', $link); $LINKSDB[$id] = $link; @@ -1273,6 +1266,51 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager, exit; } + // -------- User clicked either "Set public" or "Set private" bulk operation + if ($targetPage == Router::$PAGE_CHANGE_VISIBILITY) { + if (! $sessionManager->checkToken($_GET['token'])) { + die(t('Wrong token.')); + } + + $ids = trim($_GET['ids']); + if (strpos($ids, ' ') !== false) { + // multiple, space-separated ids provided + $ids = array_values(array_filter(preg_split('/\s+/', escape($ids)))); + } else { + // only a single id provided + $ids = [$ids]; + } + + // assert at least one id is given + if (!count($ids)) { + die('no id provided'); + } + // assert that the visibility is valid + if (!isset($_GET['newVisibility']) || !in_array($_GET['newVisibility'], ['public', 'private'])) { + die('invalid visibility'); + } else { + $private = $_GET['newVisibility'] === 'private'; + } + foreach ($ids as $id) { + $id = (int) escape($id); + $link = $LINKSDB[$id]; + $link['private'] = $private; + $pluginManager->executeHooks('save_link', $link); + $LINKSDB[$id] = $link; + } + $LINKSDB->save($conf->get('resource.page_cache')); // save to disk + + $location = '?'; + if (isset($_SERVER['HTTP_REFERER'])) { + $location = generateLocation( + $_SERVER['HTTP_REFERER'], + $_SERVER['HTTP_HOST'] + ); + } + header('Location: ' . $location); // After deleting the link, redirect to appropriate location + exit; + } + // -------- User clicked the "EDIT" button on a link: Display link edit form. if (isset($_GET['edit_link'])) { $id = (int) escape($_GET['edit_link']); @@ -1558,7 +1596,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager, $ids = []; foreach ($LINKSDB as $link) { // A note or not HTTP(S) - if ($link['url'][0] === '?' || ! startsWith(strtolower($link['url']), 'http')) { + if (is_note($link['url']) || ! startsWith(strtolower($link['url']), 'http')) { continue; } $ids[] = $link['id']; @@ -1662,11 +1700,7 @@ function buildLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager) $linkDisp = array(); while ($i<$end && $iget('redirector.url'), - $conf->get('redirector.encode_url') - ); + $link['description'] = format_description($link['description']); $classLi = ($i % 2) != 0 ? '' : 'publicLinkHightLight'; $link['class'] = $link['private'] == 0 ? $classLi : 'private'; $link['timestamp'] = $link['created']->getTimestamp(); @@ -1727,7 +1761,6 @@ function buildLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager) 'search_term' => $searchterm, 'search_tags' => $searchtags, 'visibility' => ! empty($_SESSION['visibility']) ? $_SESSION['visibility'] : '', - 'redirector' => $conf->get('redirector.url'), // Optional redirector URL. 'links' => $linkDisp, ); @@ -1877,9 +1910,7 @@ try { $linkDb = new LinkDB( $conf->get('resource.datastore'), $loginManager->isLoggedIn(), - $conf->get('privacy.hide_public_links'), - $conf->get('redirector.url'), - $conf->get('redirector.encode_url') + $conf->get('privacy.hide_public_links') ); $container = new \Slim\Container(); @@ -1902,7 +1933,7 @@ $app->group('/api/v1', function () { $this->put('/tags/{tagName:[\w]+}', '\Shaarli\Api\Controllers\Tags:putTag')->setName('putTag'); $this->delete('/tags/{tagName:[\w]+}', '\Shaarli\Api\Controllers\Tags:deleteTag')->setName('deleteTag'); - $this->get('/history', '\Shaarli\Api\Controllers\History:getHistory')->setName('getHistory'); + $this->get('/history', '\Shaarli\Api\Controllers\HistoryController:getHistory')->setName('getHistory'); })->add('\Shaarli\Api\ApiMiddleware'); $response = $app->run(true); diff --git a/package.json b/package.json index 3dd1e0fc..f3d9b51e 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "dependencies": { "awesomplete": "^1.1.2", "blazy": "^1.8.2", - "font-awesome": "^4.7.0", + "fork-awesome": "^1.1.7", "pure-extras": "^1.0.0", "purecss": "^1.0.0" }, @@ -21,7 +21,7 @@ "eslint-plugin-import": "^2.8.0", "extract-text-webpack-plugin": "^3.0.2", "file-loader": "^1.1.6", - "node-sass": "^4.7.2", + "node-sass": "^4.12.0", "sass-lint": "^1.12.1", "sass-loader": "^6.0.6", "style-loader": "^0.19.1", diff --git a/tests/bookmark/LinkDBTest.php b/tests/bookmark/LinkDBTest.php index ff5c0b97..2990a6b5 100644 --- a/tests/bookmark/LinkDBTest.php +++ b/tests/bookmark/LinkDBTest.php @@ -361,36 +361,6 @@ class LinkDBTest extends \PHPUnit\Framework\TestCase ); } - /** - * Test real_url without redirector. - */ - public function testLinkRealUrlWithoutRedirector() - { - $db = new LinkDB(self::$testDatastore, false, false); - foreach ($db as $link) { - $this->assertEquals($link['url'], $link['real_url']); - } - } - - /** - * Test real_url with redirector. - */ - public function testLinkRealUrlWithRedirector() - { - $redirector = 'http://redirector.to?'; - $db = new LinkDB(self::$testDatastore, false, false, $redirector); - foreach ($db as $link) { - $this->assertStringStartsWith($redirector, $link['real_url']); - $this->assertNotFalse(strpos($link['real_url'], urlencode('://'))); - } - - $db = new LinkDB(self::$testDatastore, false, false, $redirector, false); - foreach ($db as $link) { - $this->assertStringStartsWith($redirector, $link['real_url']); - $this->assertFalse(strpos($link['real_url'], urlencode('://'))); - } - } - /** * Test filter with string. */ @@ -516,7 +486,7 @@ class LinkDBTest extends \PHPUnit\Framework\TestCase public function testRenameTagCaseSensitive() { self::$refDB->write(self::$testDatastore); - $linkDB = new LinkDB(self::$testDatastore, true, false, ''); + $linkDB = new LinkDB(self::$testDatastore, true, false); $res = $linkDB->renameTag('sTuff', 'Taz'); $this->assertEquals(1, count($res)); diff --git a/tests/bookmark/LinkUtilsTest.php b/tests/bookmark/LinkUtilsTest.php index 1b8688e6..25fb3043 100644 --- a/tests/bookmark/LinkUtilsTest.php +++ b/tests/bookmark/LinkUtilsTest.php @@ -216,56 +216,27 @@ class LinkUtilsTest extends \PHPUnit\Framework\TestCase } /** - * Test text2clickable without a redirector being set. + * Test text2clickable. */ - public function testText2clickableWithoutRedirector() + public function testText2clickable() { $text = 'stuff http://hello.there/is=someone#here otherstuff'; $expectedText = 'stuff ' . 'http://hello.there/is=someone#here otherstuff'; - $processedText = text2clickable($text, ''); + $processedText = text2clickable($text); $this->assertEquals($expectedText, $processedText); $text = 'stuff http://hello.there/is=someone#here(please) otherstuff'; $expectedText = 'stuff ' . 'http://hello.there/is=someone#here(please) otherstuff'; - $processedText = text2clickable($text, ''); + $processedText = text2clickable($text); $this->assertEquals($expectedText, $processedText); + $text = 'stuff http://hello.there/is=someone#here(please)&no otherstuff'; $text = 'stuff http://hello.there/is=someone#here(please)&no otherstuff'; $expectedText = 'stuff ' . 'http://hello.there/is=someone#here(please)&no otherstuff'; - $processedText = text2clickable($text, ''); - $this->assertEquals($expectedText, $processedText); - } - - /** - * Test text2clickable with a redirector set. - */ - public function testText2clickableWithRedirector() - { - $text = 'stuff http://hello.there/is=someone#here otherstuff'; - $redirector = 'http://redirector.to'; - $expectedText = 'stuff http://hello.there/is=someone#here otherstuff'; - $processedText = text2clickable($text, $redirector); - $this->assertEquals($expectedText, $processedText); - } - - /** - * Test text2clickable a redirector set and without URL encode. - */ - public function testText2clickableWithRedirectorDontEncode() - { - $text = 'stuff http://hello.there/?is=someone&or=something#here otherstuff'; - $redirector = 'http://redirector.to'; - $expectedText = 'stuff http://hello.there/?is=someone&or=something#here otherstuff'; - $processedText = text2clickable($text, $redirector, false); + $processedText = text2clickable($text); $this->assertEquals($expectedText, $processedText); } @@ -317,6 +288,26 @@ class LinkUtilsTest extends \PHPUnit\Framework\TestCase $this->assertNotContains('>#nothashtag', $autolinkedDescription); } + /** + * Test is_note with note URLs. + */ + public function testIsNote() + { + $this->assertTrue(is_note('?')); + $this->assertTrue(is_note('?abcDEf')); + $this->assertTrue(is_note('?_abcDEf#123')); + } + + /** + * Test is_note with non note URLs. + */ + public function testIsNotNote() + { + $this->assertFalse(is_note('')); + $this->assertFalse(is_note('nope')); + $this->assertFalse(is_note('https://github.com/shaarli/Shaarli/?hi')); + } + /** * Util function to build an hashtag link. * diff --git a/tests/plugins/PluginMarkdownTest.php b/tests/plugins/PluginMarkdownTest.php index 5e7c02b0..9ddbc558 100644 --- a/tests/plugins/PluginMarkdownTest.php +++ b/tests/plugins/PluginMarkdownTest.php @@ -107,7 +107,7 @@ class PluginMarkdownTest extends \PHPUnit\Framework\TestCase public function testReverseText2clickable() { $text = 'stuff http://hello.there/is=someone#here otherstuff'; - $clickableText = text2clickable($text, ''); + $clickableText = text2clickable($text); $reversedText = reverse_text2clickable($clickableText); $this->assertEquals($text, $reversedText); } diff --git a/tests/updater/UpdaterTest.php b/tests/updater/UpdaterTest.php index d7df5963..93bc86c1 100644 --- a/tests/updater/UpdaterTest.php +++ b/tests/updater/UpdaterTest.php @@ -287,17 +287,14 @@ $GLOBALS[\'privateLinkByDefault\'] = true;'; $this->conf = new ConfigManager($sandbox); $title = ''; $headerLink = ''; - $redirectorUrl = ''; $this->conf->set('general.title', $title); $this->conf->set('general.header_link', $headerLink); - $this->conf->set('redirector.url', $redirectorUrl); $updater = new Updater(array(), array(), $this->conf, true); $done = $updater->updateMethodEscapeUnescapedConfig(); $this->assertTrue($done); $this->conf->reload(); $this->assertEquals(escape($title), $this->conf->get('general.title')); $this->assertEquals(escape($headerLink), $this->conf->get('general.header_link')); - $this->assertEquals(escape($redirectorUrl), $this->conf->get('redirector.url')); unlink($sandbox . '.json.php'); } @@ -707,7 +704,6 @@ $GLOBALS[\'privateLinkByDefault\'] = true;'; } /** -<<<<<<< HEAD * Test updateMethodWebThumbnailer with thumbnails enabled. */ public function testUpdateMethodWebThumbnailerEnabled() @@ -812,4 +808,19 @@ $GLOBALS[\'privateLinkByDefault\'] = true;'; $linkDB = new LinkDB(self::$testDatastore, true, false); $this->assertTrue($linkDB[1]['sticky']); } + + /** + * Test updateMethodRemoveRedirector(). + */ + public function testUpdateRemoveRedirector() + { + $sandboxConf = 'sandbox/config'; + copy(self::$configFile . '.json.php', $sandboxConf . '.json.php'); + $this->conf = new ConfigManager($sandboxConf); + $updater = new Updater([], null, $this->conf, true); + $this->assertTrue($updater->updateMethodRemoveRedirector()); + $this->assertFalse($this->conf->exists('redirector')); + $this->conf = new ConfigManager($sandboxConf); + $this->assertFalse($this->conf->exists('redirector')); + } } diff --git a/tpl/default/404.html b/tpl/default/404.html index fd337cad..10a9458a 100644 --- a/tpl/default/404.html +++ b/tpl/default/404.html @@ -1,5 +1,5 @@ - + {include="includes"} diff --git a/tpl/default/addlink.html b/tpl/default/addlink.html index 55864a02..b4b4a0ec 100644 --- a/tpl/default/addlink.html +++ b/tpl/default/addlink.html @@ -1,5 +1,5 @@ - + {include="includes"} diff --git a/tpl/default/changepassword.html b/tpl/default/changepassword.html index 2d15c92a..3867e3ca 100644 --- a/tpl/default/changepassword.html +++ b/tpl/default/changepassword.html @@ -1,5 +1,5 @@ - + {include="includes"} diff --git a/tpl/default/changetag.html b/tpl/default/changetag.html index 6606c4fa..0da6a5eb 100644 --- a/tpl/default/changetag.html +++ b/tpl/default/changetag.html @@ -1,5 +1,5 @@ - + {include="includes"} diff --git a/tpl/default/configure.html b/tpl/default/configure.html index 42e32230..7a87c05d 100644 --- a/tpl/default/configure.html +++ b/tpl/default/configure.html @@ -1,5 +1,5 @@ - + {include="includes"} diff --git a/tpl/default/daily.html b/tpl/default/daily.html index 2c409478..359ecbd0 100644 --- a/tpl/default/daily.html +++ b/tpl/default/daily.html @@ -1,5 +1,5 @@ - + {include="includes"} diff --git a/tpl/default/editlink.html b/tpl/default/editlink.html index d8c57155..df14535d 100644 --- a/tpl/default/editlink.html +++ b/tpl/default/editlink.html @@ -1,5 +1,5 @@ - + {include="includes"} diff --git a/tpl/default/export.html b/tpl/default/export.html index af1d6e33..99c01b11 100644 --- a/tpl/default/export.html +++ b/tpl/default/export.html @@ -1,5 +1,5 @@ - + {include="includes"} diff --git a/tpl/default/import.html b/tpl/default/import.html index bdc9086e..20f854d1 100644 --- a/tpl/default/import.html +++ b/tpl/default/import.html @@ -1,5 +1,5 @@ - + {include="includes"} diff --git a/tpl/default/install.html b/tpl/default/install.html index 6199b33d..f0e7040e 100644 --- a/tpl/default/install.html +++ b/tpl/default/install.html @@ -1,5 +1,5 @@ - + {include="includes"} diff --git a/tpl/default/linklist.html b/tpl/default/linklist.html index ed78f40a..a025d53c 100644 --- a/tpl/default/linklist.html +++ b/tpl/default/linklist.html @@ -1,5 +1,5 @@ - + {include="includes"} @@ -52,7 +52,7 @@ {/loop} -