From cc8f572bc063aa1e9d0368c8a8361f15efe04c9b Mon Sep 17 00:00:00 2001 From: Willi Eggeling Date: Sat, 26 Aug 2017 09:40:57 +0200 Subject: migrated Github wiki links to readthedocs --- application/LinkDB.php | 2 +- application/config/ConfigManager.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'application') diff --git a/application/LinkDB.php b/application/LinkDB.php index 9308164a..22c1f0ab 100644 --- a/application/LinkDB.php +++ b/application/LinkDB.php @@ -249,7 +249,7 @@ class LinkDB implements Iterator, Countable, ArrayAccess $link = array( 'id' => 1, 'title'=>' Shaarli: the personal, minimalist, super-fast, no-database delicious clone', - 'url'=>'https://github.com/shaarli/Shaarli/wiki', + 'url'=>'https://shaarli.readthedocs.io', 'description'=>'Welcome to Shaarli! This is your first public bookmark. To edit or delete me, you must first login. To learn how to use Shaarli, consult the link "Help/documentation" at the bottom of this page. diff --git a/application/config/ConfigManager.php b/application/config/ConfigManager.php index 8eab26f1..0fc5a5c7 100644 --- a/application/config/ConfigManager.php +++ b/application/config/ConfigManager.php @@ -9,8 +9,8 @@ use Shaarli\Config\Exception\UnauthorizedConfigException; * * Manages all Shaarli's settings. * See the documentation for more information on settings: - * - doc/Shaarli-configuration.html - * - https://github.com/shaarli/Shaarli/wiki/Shaarli-configuration + * - doc/md/Shaarli-configuration.md + * - https://shaarli.readthedocs.io/en/master/Shaarli-configuration/#configuration */ class ConfigManager { -- cgit v1.2.3 From 2e07e77573a379f9af006b2c523117eef9fdf9b2 Mon Sep 17 00:00:00 2001 From: Willi Eggeling Date: Sat, 26 Aug 2017 09:27:10 +0200 Subject: new setting: default value for 'remember me' checkbox - the default state for the login page's 'remember me' checkbox can now be configured - adapted the default and vintage theme to consider the new setting - added documentation for the new setting --- application/config/ConfigManager.php | 2 ++ 1 file changed, 2 insertions(+) (limited to 'application') diff --git a/application/config/ConfigManager.php b/application/config/ConfigManager.php index 8eab26f1..c94d92a8 100644 --- a/application/config/ConfigManager.php +++ b/application/config/ConfigManager.php @@ -328,6 +328,8 @@ class ConfigManager $this->setEmpty('privacy.default_private_links', false); $this->setEmpty('privacy.hide_public_links', false); $this->setEmpty('privacy.hide_timestamps', false); + // default state of the 'remember me' checkbox of the login form + $this->setEmpty('privacy.remember_user_default', true); $this->setEmpty('thumbnail.enable_thumbnails', true); $this->setEmpty('thumbnail.enable_localcache', true); -- cgit v1.2.3 From 341527bae96e0d1c6a0cb5dfc790eacb3ef58bf8 Mon Sep 17 00:00:00 2001 From: Willi Eggeling Date: Sat, 26 Aug 2017 23:05:02 +0200 Subject: wildcard tag search support - when searching for tags you can now include '*' as wildcard placeholder - new search reduces overall overhead when filtering for tags - fixed combination with description tag search ('#' prefix) - tests added --- application/LinkFilter.php | 131 +++++++++++++++++++++++++++++---------------- 1 file changed, 84 insertions(+), 47 deletions(-) (limited to 'application') diff --git a/application/LinkFilter.php b/application/LinkFilter.php index 95519528..99ecd1e2 100644 --- a/application/LinkFilter.php +++ b/application/LinkFilter.php @@ -249,6 +249,51 @@ class LinkFilter return $filtered; } + /** + * generate a regex fragment out of a tag + * @param string $tag to to generate regexs from. may start with '-' to negate, contain '*' as wildcard + * @return string generated regex fragment + */ + private static function tag2regex($tag) + { + $len = strlen($tag); + if(!$len || $tag === "-" || $tag === "*"){ + // nothing to search, return empty regex + return ''; + } + if($tag[0] === "-") { + // query is negated + $i = 1; // use offset to start after '-' character + $regex = '(?!'; // create negative lookahead + } else { + $i = 0; // start at first character + $regex = '(?='; // use positive lookahead + } + $regex .= '.*(?:^| )'; // before tag may only be a space or the beginning + // iterate over string, separating it into placeholder and content + for(; $i < $len; $i++){ + if($tag[$i] === '*'){ + // placeholder found + $regex .= '[^ ]*?'; + } else { + // regular characters + $offset = strpos($tag, '*', $i); + if($offset === false){ + // no placeholder found, set offset to end of string + $offset = $len; + } + // subtract one, as we want to get before the placeholder or end of string + $offset -= 1; + // we got a tag name that we want to search for. escape any regex characters to prevent conflicts. + $regex .= preg_quote(substr($tag, $i, $offset - $i + 1), '/'); + // move $i on + $i = $offset; + } + } + $regex .= '(?:$| ))'; // after the tag may only be a space or the end + return $regex; + } + /** * Returns the list of links associated with a given list of tags * @@ -263,20 +308,32 @@ class LinkFilter */ public function filterTags($tags, $casesensitive = false, $visibility = 'all') { - // Implode if array for clean up. - $tags = is_array($tags) ? trim(implode(' ', $tags)) : $tags; - if (empty($tags)) { + // get single tags (we may get passed an array, even though the docs say different) + $inputTags = $tags; + if(!is_array($tags)) { + // we got an input string, split tags + $inputTags = preg_split('/(?:\s+)|,/', $inputTags, -1, PREG_SPLIT_NO_EMPTY); + } + + if(!count($inputTags)){ + // no input tags return $this->noFilter($visibility); } - $searchtags = self::tagsStrToArray($tags, $casesensitive); - $filtered = array(); - if (empty($searchtags)) { - return $filtered; + // build regex from all tags + $re = '/^' . implode(array_map("self::tag2regex", $inputTags)) . '.*$/'; + if(!$casesensitive) { + // make regex case insensitive + $re .= 'i'; } + // create resulting array + $filtered = array(); + + // iterate over each link foreach ($this->links as $key => $link) { - // ignore non private links when 'privatonly' is on. + // check level of visibility + // ignore non private links when 'privateonly' is on. if ($visibility !== 'all') { if (! $link['private'] && $visibility === 'private') { continue; @@ -284,25 +341,27 @@ class LinkFilter continue; } } - - $linktags = self::tagsStrToArray($link['tags'], $casesensitive); - - $found = true; - for ($i = 0 ; $i < count($searchtags) && $found; $i++) { - // Exclusive search, quit if tag found. - // Or, tag not found in the link, quit. - if (($searchtags[$i][0] == '-' - && $this->searchTagAndHashTag(substr($searchtags[$i], 1), $linktags, $link['description'])) - || ($searchtags[$i][0] != '-') - && ! $this->searchTagAndHashTag($searchtags[$i], $linktags, $link['description']) - ) { - $found = false; + $search = $link['tags']; // build search string, start with tags of current link + if(strlen(trim($link['description'])) && strpos($link['description'], '#') !== false){ + // description given and at least one possible tag found + $descTags = array(); + // find all tags in the form of #tag in the description + preg_match_all( + '/(? 0) { - return true; - } - - return false; - } - /** * Convert a list of tags (str) to an array. Also * - handle case sensitivity. -- cgit v1.2.3 From a3130d2c2f27052710d4dbd51d0001190b19b383 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Fri, 25 Aug 2017 19:47:57 +0200 Subject: Make work behind a reverse proxy Without HTTP_X_FORWARDED_PORT check, might be set to false even though the user is using HTTPS, thus disabling Firefox Social block display --- application/HttpUtils.php | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'application') diff --git a/application/HttpUtils.php b/application/HttpUtils.php index 88a1efdb..00835966 100644 --- a/application/HttpUtils.php +++ b/application/HttpUtils.php @@ -401,3 +401,31 @@ function getIpAddressFromProxy($server, $trustedIps) return array_pop($ips); } + +/** + * Returns true if Shaarli's currently browsed in HTTPS. + * Supports reverse proxies (if the headers are correctly set). + * + * @param array $server $_SERVER. + * + * @return bool true if HTTPS, false otherwise. + */ +function is_https($server) +{ + + if (isset($server['HTTP_X_FORWARDED_PORT'])) { + // Keep forwarded port + if (strpos($server['HTTP_X_FORWARDED_PORT'], ',') !== false) { + $ports = explode(',', $server['HTTP_X_FORWARDED_PORT']); + $port = trim($ports[0]); + } else { + $port = $server['HTTP_X_FORWARDED_PORT']; + } + + if ($port == '443') { + return true; + } + } + + return ! empty($server['HTTPS']); +} -- cgit v1.2.3 From 27e21231e168e5a2a89563b2538a4f86df24e582 Mon Sep 17 00:00:00 2001 From: Willi Eggeling Date: Thu, 31 Aug 2017 00:39:15 +0200 Subject: added option to redirect all anonymous users to login page - new setting *force_login* added and documented - if both, *force_login* and *hide_public_links* are set to true, all requests (except for the feeds) are redirected to the login page --- application/config/ConfigManager.php | 1 + 1 file changed, 1 insertion(+) (limited to 'application') diff --git a/application/config/ConfigManager.php b/application/config/ConfigManager.php index fdd5b3d7..32f6ef6d 100644 --- a/application/config/ConfigManager.php +++ b/application/config/ConfigManager.php @@ -327,6 +327,7 @@ class ConfigManager $this->setEmpty('privacy.default_private_links', false); $this->setEmpty('privacy.hide_public_links', false); + $this->setEmpty('privacy.force_login', false); $this->setEmpty('privacy.hide_timestamps', false); // default state of the 'remember me' checkbox of the login form $this->setEmpty('privacy.remember_user_default', true); -- cgit v1.2.3 From e4325b1517c3d9769c8e0141e37b2845bf8e4d09 Mon Sep 17 00:00:00 2001 From: VirtualTam Date: Tue, 19 Sep 2017 20:21:28 +0200 Subject: Robustness: safer RainTPL directory handling Relates to https://github.com/shaarli/Shaarli/issues/845 Relates to https://github.com/shaarli/Shaarli/issues/846 Relates to https://github.com/shaarli/Shaarli/pull/909 Signed-off-by: VirtualTam --- application/ApplicationUtils.php | 5 +++-- application/ThemeUtils.php | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'application') diff --git a/application/ApplicationUtils.php b/application/ApplicationUtils.php index 85dcbeeb..123cc0b3 100644 --- a/application/ApplicationUtils.php +++ b/application/ApplicationUtils.php @@ -168,14 +168,15 @@ class ApplicationUtils public static function checkResourcePermissions($conf) { $errors = array(); + $rainTplDir = rtrim($conf->get('resource.raintpl_tpl'), '/'); // Check script and template directories are readable foreach (array( 'application', 'inc', 'plugins', - $conf->get('resource.raintpl_tpl'), - $conf->get('resource.raintpl_tpl').'/'.$conf->get('resource.theme'), + $rainTplDir, + $rainTplDir.'/'.$conf->get('resource.theme'), ) as $path) { if (! is_readable(realpath($path))) { $errors[] = '"'.$path.'" directory is not readable'; diff --git a/application/ThemeUtils.php b/application/ThemeUtils.php index 2718ed13..16f2f6a2 100644 --- a/application/ThemeUtils.php +++ b/application/ThemeUtils.php @@ -22,6 +22,7 @@ class ThemeUtils */ public static function getThemes($tplDir) { + $tplDir = rtrim($tplDir, '/'); $allTheme = glob($tplDir.'/*', GLOB_ONLYDIR); $themes = []; foreach ($allTheme as $value) { -- cgit v1.2.3 From 8c322aaba197bab1a9992b731db80d9faa133bc4 Mon Sep 17 00:00:00 2001 From: VirtualTam Date: Tue, 19 Sep 2017 22:08:29 +0200 Subject: Robustness: safer gzinflate/zlib usage Relates to https://github.com/shaarli/Shaarli/pull/846 PHP's `gzinflate()` fails with an error when being passed an empty string See: - https://bugs.php.net/bug.php?id=71395 Signed-off-by: VirtualTam --- application/FileUtils.php | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) (limited to 'application') diff --git a/application/FileUtils.php b/application/FileUtils.php index a167f642..918cb83b 100644 --- a/application/FileUtils.php +++ b/application/FileUtils.php @@ -50,7 +50,8 @@ class FileUtils /** * Read data from a file containing Shaarli database format content. - * If the file isn't readable or doesn't exists, default data will be returned. + * + * If the file isn't readable or doesn't exist, default data will be returned. * * @param string $file File path. * @param mixed $default The default value to return if the file isn't readable. @@ -61,16 +62,21 @@ class FileUtils { // Note that gzinflate is faster than gzuncompress. // See: http://www.php.net/manual/en/function.gzdeflate.php#96439 - if (is_readable($file)) { - return unserialize( - gzinflate( - base64_decode( - substr(file_get_contents($file), strlen(self::$phpPrefix), -strlen(self::$phpSuffix)) - ) - ) - ); + if (! is_readable($file)) { + return $default; + } + + $data = file_get_contents($file); + if ($data == '') { + return $default; } - return $default; + return unserialize( + gzinflate( + base64_decode( + substr($data, strlen(self::$phpPrefix), -strlen(self::$phpSuffix)) + ) + ) + ); } } -- cgit v1.2.3 From 601faf97516a836e4ae57dc4cecb9225c0a04338 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Fri, 29 Sep 2017 18:52:38 +0200 Subject: Fix parsing for description links with parentheses With markdown plugin disabled relates to #966 --- application/LinkUtils.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'application') diff --git a/application/LinkUtils.php b/application/LinkUtils.php index 976474de..267e62cd 100644 --- a/application/LinkUtils.php +++ b/application/LinkUtils.php @@ -109,7 +109,7 @@ function count_private($links) */ function text2clickable($text, $redirector = '') { - $regex = '!(((?:https?|ftp|file)://|apt:|magnet:)\S+[[:alnum:]]/?)!si'; + $regex = '!(((?:https?|ftp|file)://|apt:|magnet:)\S+[a-z0-9\(\)]/?)!si'; if (empty($redirector)) { return preg_replace($regex, '$1', $text); -- cgit v1.2.3 From bfe4f536bbfe03f38e0c801bfbd26587a2b64a7f Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Sun, 1 Oct 2017 11:02:48 +0200 Subject: Add a version hash for asset loading to prevent browser's cache issue The hash is generated using the same salt as the one used for credentials (1 salt per instance) in order to avoid exposing the instance version. Fixes #965 --- application/ApplicationUtils.php | 15 +++++++++++++++ application/PageBuilder.php | 5 +++++ 2 files changed, 20 insertions(+) (limited to 'application') diff --git a/application/ApplicationUtils.php b/application/ApplicationUtils.php index 85dcbeeb..20fec376 100644 --- a/application/ApplicationUtils.php +++ b/application/ApplicationUtils.php @@ -220,4 +220,19 @@ class ApplicationUtils return $errors; } + + /** + * Returns a salted hash representing the current Shaarli version. + * + * Useful for assets browser cache. + * + * @param string $currentVersion of Shaarli + * @param string $salt User personal salt, also used for the authentication + * + * @return string version hash + */ + public static function getVersionHash($currentVersion, $salt) + { + return hash_hmac('sha256', $currentVersion, $salt); + } } diff --git a/application/PageBuilder.php b/application/PageBuilder.php index 7a42400d..c91b662c 100644 --- a/application/PageBuilder.php +++ b/application/PageBuilder.php @@ -76,6 +76,10 @@ class PageBuilder $this->tpl->assign('searchcrits', $searchcrits); $this->tpl->assign('source', index_url($_SERVER)); $this->tpl->assign('version', shaarli_version); + $this->tpl->assign( + 'version_hash', + ApplicationUtils::getVersionHash(SHAARLI_VERSION, $this->conf->get('credentials.salt')) + ); $this->tpl->assign('scripturl', index_url($_SERVER)); $this->tpl->assign('privateonly', !empty($_SESSION['privateonly'])); // Show only private links? $this->tpl->assign('untaggedonly', !empty($_SESSION['untaggedonly'])); @@ -89,6 +93,7 @@ class PageBuilder $this->tpl->assign('feed_type', $this->conf->get('feed.show_atom', true) !== false ? 'atom' : 'rss'); $this->tpl->assign('hide_timestamps', $this->conf->get('privacy.hide_timestamps', false)); $this->tpl->assign('token', getToken($this->conf)); + if ($this->linkDB !== null) { $this->tpl->assign('tags', $this->linkDB->linksCountPerTag()); } -- cgit v1.2.3 From b3e1f92e9cd0cae35bc726ca3a2356b4e631ccfa Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Sun, 1 Oct 2017 11:09:12 +0200 Subject: Rename shaarli_version constant to uppercase --- application/PageBuilder.php | 4 ++-- application/Updater.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'application') diff --git a/application/PageBuilder.php b/application/PageBuilder.php index c91b662c..291860ad 100644 --- a/application/PageBuilder.php +++ b/application/PageBuilder.php @@ -49,7 +49,7 @@ class PageBuilder try { $version = ApplicationUtils::checkUpdate( - shaarli_version, + SHAARLI_VERSION, $this->conf->get('resource.update_check'), $this->conf->get('updates.check_updates_interval'), $this->conf->get('updates.check_updates'), @@ -75,7 +75,7 @@ class PageBuilder } $this->tpl->assign('searchcrits', $searchcrits); $this->tpl->assign('source', index_url($_SERVER)); - $this->tpl->assign('version', shaarli_version); + $this->tpl->assign('version', SHAARLI_VERSION); $this->tpl->assign( 'version_hash', ApplicationUtils::getVersionHash(SHAARLI_VERSION, $this->conf->get('credentials.salt')) diff --git a/application/Updater.php b/application/Updater.php index 40a15906..72b2def0 100644 --- a/application/Updater.php +++ b/application/Updater.php @@ -398,7 +398,7 @@ class Updater */ public function updateMethodCheckUpdateRemoteBranch() { - if (shaarli_version === 'dev' || $this->conf->get('updates.check_updates_branch') === 'latest') { + if (SHAARLI_VERSION === 'dev' || $this->conf->get('updates.check_updates_branch') === 'latest') { return true; } @@ -413,7 +413,7 @@ class Updater $latestMajor = $matches[1]; // Get current major version digit - preg_match('/(\d+)\.\d+$/', shaarli_version, $matches); + preg_match('/(\d+)\.\d+$/', SHAARLI_VERSION, $matches); $currentMajor = $matches[1]; if ($currentMajor === $latestMajor) { -- cgit v1.2.3 From 722caa209005a23b0cfba31aa10cd34a45880cec Mon Sep 17 00:00:00 2001 From: Mark Gerarts Date: Sun, 1 Oct 2017 14:19:57 +0200 Subject: Allow setting of a default note title, see #963 --- application/config/ConfigManager.php | 1 + 1 file changed, 1 insertion(+) (limited to 'application') diff --git a/application/config/ConfigManager.php b/application/config/ConfigManager.php index 32f6ef6d..7ff2fe67 100644 --- a/application/config/ConfigManager.php +++ b/application/config/ConfigManager.php @@ -317,6 +317,7 @@ class ConfigManager $this->setEmpty('general.header_link', '?'); $this->setEmpty('general.links_per_page', 20); $this->setEmpty('general.enabled_plugins', self::$DEFAULT_PLUGINS); + $this->setEmpty('general.default_note_title', 'Note: '); $this->setEmpty('updates.check_updates', false); $this->setEmpty('updates.check_updates_branch', 'stable'); -- cgit v1.2.3