diff --git a/plugins/myShaarli/favicon/DataAccess.php b/plugins/myShaarli/favicon/DataAccess.php new file mode 100644 index 00000000..2bfdf640 --- /dev/null +++ b/plugins/myShaarli/favicon/DataAccess.php @@ -0,0 +1,40 @@ +set_context(); + return @file_get_contents($url); + } + + public function retrieveHeader($url) { + $this->set_context(); + return @get_headers($url, TRUE); + } + + public function saveCache($file, $data) { + file_put_contents($file, $data); + } + + public function readCache($file) { + return file_get_contents($file); + } + + private function set_context() { + stream_context_set_default( + array( + 'http' => array( + 'method' => 'GET', + 'timeout' => 10, + 'header' => "User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:20.0; Favicon; +https://github.com/ArthurHoaro/favicon) Gecko/20100101 Firefox/32.0\r\n", + ) + ) + ); + } +} \ No newline at end of file diff --git a/plugins/myShaarli/favicon/Favicon.php b/plugins/myShaarli/favicon/Favicon.php new file mode 100644 index 00000000..7ea6ccf1 --- /dev/null +++ b/plugins/myShaarli/favicon/Favicon.php @@ -0,0 +1,293 @@ +url = $args['url']; + } + + $this->cacheDir = __DIR__ . '/../../resources/cache'; + $this->dataAccess = new DataAccess(); + } + + public function cache($args = array()) { + if (isset($args['dir'])) { + $this->cacheDir = $args['dir']; + } + + if (!empty($args['timeout'])) { + $this->cacheTimeout = $args['timeout']; + } else { + $this->cacheTimeout = 0; + } + } + + public static function baseUrl($url, $path = false) + { + $return = ''; + + if (!$url = parse_url($url)) { + return FALSE; + } + + // Scheme + $scheme = isset($url['scheme']) ? strtolower($url['scheme']) : null; + if ($scheme != 'http' && $scheme != 'https') { + + return FALSE; + } + $return .= "{$scheme}://"; + + // Username and password + if (isset($url['user'])) { + $return .= $url['user']; + if (isset($url['pass'])) { + $return .= ":{$url['pass']}"; + } + $return .= '@'; + } + + // Hostname + if( !isset($url['host']) ) { + return FALSE; + } + + $return .= $url['host']; + + // Port + if (isset($url['port'])) { + $return .= ":{$url['port']}"; + } + + // Path + if( $path && isset($url['path']) ) { + $return .= $url['path']; + } + $return .= '/'; + + return $return; + } + + public function info($url) + { + if(empty($url) || $url === false) { + return false; + } + + $max_loop = 5; + + // Discover real status by following redirects. + $loop = TRUE; + while ($loop && $max_loop-- > 0) { + $headers = $this->dataAccess->retrieveHeader($url); + $exploded = explode(' ', $headers[0]); + + if( !isset($exploded[1]) ) { + return false; + } + list(,$status) = $exploded; + + switch ($status) { + case '301': + case '302': + $url = $headers['Location']; + break; + default: + $loop = FALSE; + break; + } + } + + return array('status' => $status, 'url' => $url); + } + + public function endRedirect($url) { + $out = $this->info($url); + return !empty($out['url']) ? $out['url'] : false; + } + + /** + * Find remote (or cached) favicon + * @return favicon URL, false if nothing was found + **/ + public function get($url = '') + { + // URLs passed to this method take precedence. + if (!empty($url)) { + $this->url = $url; + } + + // Get the base URL without the path for clearer concatenations. + $original = rtrim($this->baseUrl($this->url, true), '/'); + $url = rtrim($this->endRedirect($this->baseUrl($this->url, false)), '/'); + + if(($favicon = $this->checkCache($url)) || ($favicon = $this->getFavicon($url))) { + $base = true; + } + elseif(($favicon = $this->checkCache($original)) || ($favicon = $this->getFavicon($original, false))) { + $base = false; + } + else + return false; + + // Save cache if necessary + $cache = $this->cacheDir . '/' . md5($base ? $url : $original); + if ($this->cacheTimeout && !file_exists($cache) || (is_writable($cache) && time() - filemtime($cache) > $this->cacheTimeout)) { + $this->dataAccess->saveCache($cache, $favicon); + } + + return $favicon; + } + + private function getFavicon($url, $checkDefault = true) { + $favicon = false; + + if(empty($url)) { + return false; + } + + // Try /favicon.ico first. + if( $checkDefault ) { + $info = $this->info("{$url}/favicon.ico"); + if ($info['status'] == '200') { + $favicon = $info['url']; + } + } + + // See if it's specified in a link tag in domain url. + if (!$favicon) { + $favicon = $this->getInPage($url); + } + + // Make sure the favicon is an absolute URL. + if( $favicon && filter_var($favicon, FILTER_VALIDATE_URL) === false ) { + $favicon = $url . '/' . $favicon; + } + + // Sometimes people lie, so check the status. + // And sometimes, it's not even an image. Sneaky bastards! + // If cacheDir isn't writable, that's not our problem + if ($favicon && is_writable($this->cacheDir) && !$this->checkImageMType($favicon)) { + $favicon = false; + } + + return $favicon; + } + + private function getInPage($url) { + $html = $this->dataAccess->retrieveUrl("{$url}/"); + preg_match('!.*!ims', $html, $match); + + if(empty($match) || count($match) == 0) { + return false; + } + + $head = $match[0]; + + $dom = new \DOMDocument(); + // Use error supression, because the HTML might be too malformed. + if (@$dom->loadHTML($head)) { + $links = $dom->getElementsByTagName('link'); + foreach ($links as $link) { + if ($link->hasAttribute('rel') && strtolower($link->getAttribute('rel')) == 'shortcut icon') { + return $link->getAttribute('href'); + } elseif ($link->hasAttribute('rel') && strtolower($link->getAttribute('rel')) == 'icon') { + return $link->getAttribute('href'); + } elseif ($link->hasAttribute('href') && strpos($link->getAttribute('href'), 'favicon') !== FALSE) { + return $link->getAttribute('href'); + } + } + } + return false; + } + + private function checkCache($url) { + if ($this->cacheTimeout) { + $cache = $this->cacheDir . '/' . md5($url); + if (file_exists($cache) && is_readable($cache) && (time() - filemtime($cache) < $this->cacheTimeout)) { + return $this->dataAccess->readCache($cache); + } + } + return false; + } + + private function checkImageMType($url) { + $tmpFile = $this->cacheDir . '/tmp.ico'; + + $fileContent = $this->dataAccess->retrieveUrl($url); + $this->dataAccess->saveCache($tmpFile, $fileContent); + + $finfo = finfo_open(FILEINFO_MIME_TYPE); + $isImage = strpos(finfo_file($finfo, $tmpFile), 'image') !== false; + finfo_close($finfo); + + unlink($tmpFile); + + return $isImage; + } + + /** + * @return mixed + */ + public function getCacheDir() + { + return $this->cacheDir; + } + + /** + * @param mixed $cacheDir + */ + public function setCacheDir($cacheDir) + { + $this->cacheDir = $cacheDir; + } + + /** + * @return mixed + */ + public function getCacheTimeout() + { + return $this->cacheTimeout; + } + + /** + * @param mixed $cacheTimeout + */ + public function setCacheTimeout($cacheTimeout) + { + $this->cacheTimeout = $cacheTimeout; + } + + /** + * @return string + */ + public function getUrl() + { + return $this->url; + } + + /** + * @param string $url + */ + public function setUrl($url) + { + $this->url = $url; + } + + /** + * @param DataAccess $dataAccess + */ + public function setDataAccess($dataAccess) + { + $this->dataAccess = $dataAccess; + } +} diff --git a/plugins/myShaarli/myShaarli.meta b/plugins/myShaarli/myShaarli.meta new file mode 100644 index 00000000..1db23d8f --- /dev/null +++ b/plugins/myShaarli/myShaarli.meta @@ -0,0 +1,2 @@ +description="For each link, add thumbshot image and favicon" +parameters=ExternalThumbshot_URL; diff --git a/plugins/myShaarli/myShaarli.php b/plugins/myShaarli/myShaarli.php new file mode 100644 index 00000000..831576a0 --- /dev/null +++ b/plugins/myShaarli/myShaarli.php @@ -0,0 +1,89 @@ +get('plugins.ExternalThumbshot_URL'))) { + $error = 'myShaarli plugin error: '. + 'Please define the "ExternalThumbshot_URL" setting in the plugin administration page.'; + return array($error); + } + if ($conf->get('resource.theme') !== 'myShaarli' AND $conf->get('resource.theme') !== 'myShaarli_Columns') { + $error = 'myShaarli plugin: '. + 'This plugin need modification of template. Use myShaarli theme for test.'; + return array($error); + } + } + + +/** + * Add externalThumbshot icon to link_plugin when rendering linklist. + * + * @param mixed $data - linklist data. + * + * @return mixed - linklist data with readityourself plugin. + */ + +function hook_myShaarli_render_linklist($data,$conf) +{ + $thumUrl = $conf->get('plugins.ExternalThumbshot_URL'); + if (!isset($thumUrl)) { + return $data; + } + + foreach ($data['links'] as &$value) { + $thumb = computeThumbnail($conf, $value['url']); + if(empty($thumb)){ + $value['thumbnail'] = 'Thumbnail'; + } else { + $value['thumbnail'] = ''; + } + } + return $data; +} + +/** + * NOT WORKING + * To poor performance + * Need fix +*/ + +function returnFavicon($url) +{ + $faviconHash = md5($url); + $path = substr($faviconHash, 0, 2).'/'.substr($faviconHash, 2, 2); + $faviconPath = 'cache/'.$path.'/'.$faviconHash.'.ico'; + if (file_exists($faviconPath)) { + $content = file_get_contents($faviconPath); + return 'favicon'; + } + if (file_exists('cache/'.$path.'/'.$faviconHash)) { + return; + } + require_once 'plugins/myShaarli/favicon/DataAccess.php'; + require_once 'plugins/myShaarli/favicon/Favicon.php'; + $favicon = new \Favicon\Favicon(); + $favicon->cache(array('dir' => 'cache')); + $urlOfFavicon = $favicon->get($url); + if (!$urlOfFavicon) { + mkdir('cache/'.$path, 0777, true); + touch('cache/'.$path.'/'.$faviconHash); + return; + } + if (!is_dir('cache/'.$path.'/')) { + mkdir('cache/'.$path, 0777, true); + } + $content = file_get_contents($urlOfFavicon); + file_put_contents($faviconPath, $content); + return 'favicon'; +}