X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;f=application%2Fbookmark%2FLinkUtils.php;h=77eb2d95dd480e09663e8639174675df3755a210;hb=refs%2Fpull%2F1358%2Fhead;hp=de5b61cbcaf6e8467d57eb9c49aa009d0a832754;hpb=ff3b5dc5542ec150f0d9b447394364a15e9156d0;p=github%2Fshaarli%2FShaarli.git diff --git a/application/bookmark/LinkUtils.php b/application/bookmark/LinkUtils.php index de5b61cb..77eb2d95 100644 --- a/application/bookmark/LinkUtils.php +++ b/application/bookmark/LinkUtils.php @@ -7,13 +7,25 @@ use Shaarli\Bookmark\LinkDB; * * @param string $charset to extract from the downloaded page (reference) * @param string $title to extract from the downloaded page (reference) + * @param string $description to extract from the downloaded page (reference) + * @param string $keywords to extract from the downloaded page (reference) + * @param bool $retrieveDescription Automatically tries to retrieve description and keywords from HTML content * @param string $curlGetInfo Optionally overrides curl_getinfo function * * @return Closure */ -function get_curl_download_callback(&$charset, &$title, $curlGetInfo = 'curl_getinfo') -{ +function get_curl_download_callback( + &$charset, + &$title, + &$description, + &$keywords, + $retrieveDescription, + $curlGetInfo = 'curl_getinfo' +) { $isRedirected = false; + $currentChunk = 0; + $foundChunk = null; + /** * cURL callback function for CURLOPT_WRITEFUNCTION (called during the download). * @@ -25,7 +37,18 @@ function get_curl_download_callback(&$charset, &$title, $curlGetInfo = 'curl_get * * @return int|bool length of $data or false if we need to stop the download */ - return function (&$ch, $data) use ($curlGetInfo, &$charset, &$title, &$isRedirected) { + return function (&$ch, $data) use ( + $retrieveDescription, + $curlGetInfo, + &$charset, + &$title, + &$description, + &$keywords, + &$isRedirected, + &$currentChunk, + &$foundChunk + ) { + $currentChunk++; $responseCode = $curlGetInfo($ch, CURLINFO_RESPONSE_CODE); if (!empty($responseCode) && in_array($responseCode, [301, 302])) { $isRedirected = true; @@ -50,9 +73,34 @@ function get_curl_download_callback(&$charset, &$title, $curlGetInfo = 'curl_get } if (empty($title)) { $title = html_extract_title($data); + $foundChunk = ! empty($title) ? $currentChunk : $foundChunk; + } + if ($retrieveDescription && empty($description)) { + $description = html_extract_tag('description', $data); + $foundChunk = ! empty($description) ? $currentChunk : $foundChunk; + } + if ($retrieveDescription && empty($keywords)) { + $keywords = html_extract_tag('keywords', $data); + if (! empty($keywords)) { + $foundChunk = $currentChunk; + // Keywords use the format tag1, tag2 multiple words, tag + // So we format them to match Shaarli's separator and glue multiple words with '-' + $keywords = implode(' ', array_map(function($keyword) { + return implode('-', preg_split('/\s+/', trim($keyword))); + }, explode(',', $keywords))); + } } + // We got everything we want, stop the download. - if (!empty($responseCode) && !empty($contentType) && !empty($charset) && !empty($title)) { + // If we already found either the title, description or keywords, + // it's highly unlikely that we'll found the other metas further than + // in the same chunk of data or the next one. So we also stop the download after that. + if ((!empty($responseCode) && !empty($contentType) && !empty($charset)) && $foundChunk !== null + && (! $retrieveDescription + || $foundChunk < $currentChunk + || (!empty($title) && !empty($description) && !empty($keywords)) + ) + ) { return false; } @@ -110,6 +158,35 @@ function html_extract_charset($html) return false; } +/** + * Extract meta tag from HTML content in either: + * - OpenGraph: + * - Meta tag: + * + * @param string $tag Name of the tag to retrieve. + * @param string $html HTML content where to look for charset. + * + * @return bool|string Charset string if found, false otherwise. + */ +function html_extract_tag($tag, $html) +{ + $propertiesKey = ['property', 'name', 'itemprop']; + $properties = implode('|', $propertiesKey); + // Try to retrieve OpenGraph image. + $ogRegex = '#]+(?:'. $properties .')=["\']?(?:og:)?'. $tag .'["\'\s][^>]*content=["\']?(.*?)["\'/>]#'; + // If the attributes are not in the order property => content (e.g. Github) + // New regex to keep this readable... more or less. + $ogRegexReverse = '#]+content=["\']([^"\']+)[^>]+(?:'. $properties .')=["\']?(?:og)?:'. $tag .'["\'\s/>]#'; + + if (preg_match($ogRegex, $html, $matches) > 0 + || preg_match($ogRegexReverse, $html, $matches) > 0 + ) { + return $matches[1]; + } + + return false; +} + /** * Count private links in given linklist. * @@ -133,29 +210,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 +260,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 +281,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] === '?'; +}