X-Git-Url: https://git.immae.eu/?a=blobdiff_plain;ds=inline;f=application%2Fbookmark%2FLinkUtils.php;h=77eb2d95dd480e09663e8639174675df3755a210;hb=refs%2Ftags%2Fv0.11.0;hp=988970bdf924854ba06fa352a61a1d3c62da94bd;hpb=5bb384cd2742ff4f982cbe914cee551b74ab4bad;p=github%2Fshaarli%2FShaarli.git
diff --git a/application/bookmark/LinkUtils.php b/application/bookmark/LinkUtils.php
index 988970bd..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.
*
@@ -204,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] === '?';
+}