diff options
author | ArthurHoaro <arthur@hoa.ro> | 2018-07-28 09:41:29 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-07-28 09:41:29 +0200 |
commit | ad5f47adbaee1eef85e90950ab8a45fe82959924 (patch) | |
tree | d23a186661db00d36cb2b2287a7bf890fbc62cfb /index.php | |
parent | 8fdd65b88412a0db28c723a486650c434fe5668c (diff) | |
parent | 7b4fea0e39be9e74e9aef13e73af9bbd2b1a6397 (diff) | |
download | Shaarli-ad5f47adbaee1eef85e90950ab8a45fe82959924.tar.gz Shaarli-ad5f47adbaee1eef85e90950ab8a45fe82959924.tar.zst Shaarli-ad5f47adbaee1eef85e90950ab8a45fe82959924.zip |
Merge pull request #687 from ArthurHoaro/web-thumb
Use web-thumbnailer to retrieve thumbnails
Diffstat (limited to 'index.php')
-rw-r--r-- | index.php | 527 |
1 files changed, 98 insertions, 429 deletions
@@ -75,11 +75,12 @@ require_once 'application/Utils.php'; | |||
75 | require_once 'application/PluginManager.php'; | 75 | require_once 'application/PluginManager.php'; |
76 | require_once 'application/Router.php'; | 76 | require_once 'application/Router.php'; |
77 | require_once 'application/Updater.php'; | 77 | require_once 'application/Updater.php'; |
78 | use \Shaarli\Languages; | ||
79 | use \Shaarli\ThemeUtils; | ||
80 | use \Shaarli\Config\ConfigManager; | 78 | use \Shaarli\Config\ConfigManager; |
79 | use \Shaarli\Languages; | ||
81 | use \Shaarli\Security\LoginManager; | 80 | use \Shaarli\Security\LoginManager; |
82 | use \Shaarli\Security\SessionManager; | 81 | use \Shaarli\Security\SessionManager; |
82 | use \Shaarli\ThemeUtils; | ||
83 | use \Shaarli\Thumbnailer; | ||
83 | 84 | ||
84 | // Ensure the PHP version is supported | 85 | // Ensure the PHP version is supported |
85 | try { | 86 | try { |
@@ -513,7 +514,8 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager, | |||
513 | read_updates_file($conf->get('resource.updates')), | 514 | read_updates_file($conf->get('resource.updates')), |
514 | $LINKSDB, | 515 | $LINKSDB, |
515 | $conf, | 516 | $conf, |
516 | $loginManager->isLoggedIn() | 517 | $loginManager->isLoggedIn(), |
518 | $_SESSION | ||
517 | ); | 519 | ); |
518 | try { | 520 | try { |
519 | $newUpdates = $updater->update(); | 521 | $newUpdates = $updater->update(); |
@@ -528,7 +530,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager, | |||
528 | die($e->getMessage()); | 530 | die($e->getMessage()); |
529 | } | 531 | } |
530 | 532 | ||
531 | $PAGE = new PageBuilder($conf, $LINKSDB, $sessionManager->generateToken(), $loginManager->isLoggedIn()); | 533 | $PAGE = new PageBuilder($conf, $_SESSION, $LINKSDB, $sessionManager->generateToken(), $loginManager->isLoggedIn()); |
532 | $PAGE->assign('linkcount', count($LINKSDB)); | 534 | $PAGE->assign('linkcount', count($LINKSDB)); |
533 | $PAGE->assign('privateLinkcount', count_private($LINKSDB)); | 535 | $PAGE->assign('privateLinkcount', count_private($LINKSDB)); |
534 | $PAGE->assign('plugin_errors', $pluginManager->getErrors()); | 536 | $PAGE->assign('plugin_errors', $pluginManager->getErrors()); |
@@ -601,19 +603,23 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager, | |||
601 | // -------- Picture wall | 603 | // -------- Picture wall |
602 | if ($targetPage == Router::$PAGE_PICWALL) | 604 | if ($targetPage == Router::$PAGE_PICWALL) |
603 | { | 605 | { |
606 | $PAGE->assign('pagetitle', t('Picture wall') .' - '. $conf->get('general.title', 'Shaarli')); | ||
607 | if (! $conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) === Thumbnailer::MODE_NONE) { | ||
608 | $PAGE->assign('linksToDisplay', []); | ||
609 | $PAGE->renderPage('picwall'); | ||
610 | exit; | ||
611 | } | ||
612 | |||
604 | // Optionally filter the results: | 613 | // Optionally filter the results: |
605 | $links = $LINKSDB->filterSearch($_GET); | 614 | $links = $LINKSDB->filterSearch($_GET); |
606 | $linksToDisplay = array(); | 615 | $linksToDisplay = array(); |
607 | 616 | ||
608 | // Get only links which have a thumbnail. | 617 | // Get only links which have a thumbnail. |
609 | foreach($links as $link) | 618 | // Note: we do not retrieve thumbnails here, the request is too heavy. |
619 | foreach($links as $key => $link) | ||
610 | { | 620 | { |
611 | $permalink='?'.$link['shorturl']; | 621 | if (isset($link['thumbnail']) && $link['thumbnail'] !== false) { |
612 | $thumb=lazyThumbnail($conf, $link['url'],$permalink); | 622 | $linksToDisplay[] = $link; // Add to array. |
613 | if ($thumb!='') // Only output links which have a thumbnail. | ||
614 | { | ||
615 | $link['thumbnail']=$thumb; // Thumbnail HTML code. | ||
616 | $linksToDisplay[]=$link; // Add to array. | ||
617 | } | 623 | } |
618 | } | 624 | } |
619 | 625 | ||
@@ -626,7 +632,7 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager, | |||
626 | $PAGE->assign($key, $value); | 632 | $PAGE->assign($key, $value); |
627 | } | 633 | } |
628 | 634 | ||
629 | $PAGE->assign('pagetitle', t('Picture wall') .' - '. $conf->get('general.title', 'Shaarli')); | 635 | |
630 | $PAGE->renderPage('picwall'); | 636 | $PAGE->renderPage('picwall'); |
631 | exit; | 637 | exit; |
632 | } | 638 | } |
@@ -1009,6 +1015,16 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager, | |||
1009 | $conf->set('api.secret', escape($_POST['apiSecret'])); | 1015 | $conf->set('api.secret', escape($_POST['apiSecret'])); |
1010 | $conf->set('translation.language', escape($_POST['language'])); | 1016 | $conf->set('translation.language', escape($_POST['language'])); |
1011 | 1017 | ||
1018 | $thumbnailsMode = extension_loaded('gd') ? $_POST['enableThumbnails'] : Thumbnailer::MODE_NONE; | ||
1019 | if ($thumbnailsMode !== Thumbnailer::MODE_NONE | ||
1020 | && $thumbnailsMode !== $conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) | ||
1021 | ) { | ||
1022 | $_SESSION['warnings'][] = t( | ||
1023 | 'You have enabled or changed thumbnails mode. <a href="?do=thumbs_update">Please synchronize them</a>.' | ||
1024 | ); | ||
1025 | } | ||
1026 | $conf->set('thumbnails.mode', $thumbnailsMode); | ||
1027 | |||
1012 | try { | 1028 | try { |
1013 | $conf->write($loginManager->isLoggedIn()); | 1029 | $conf->write($loginManager->isLoggedIn()); |
1014 | $history->updateSettings(); | 1030 | $history->updateSettings(); |
@@ -1047,6 +1063,8 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager, | |||
1047 | $PAGE->assign('api_secret', $conf->get('api.secret')); | 1063 | $PAGE->assign('api_secret', $conf->get('api.secret')); |
1048 | $PAGE->assign('languages', Languages::getAvailableLanguages()); | 1064 | $PAGE->assign('languages', Languages::getAvailableLanguages()); |
1049 | $PAGE->assign('language', $conf->get('translation.language')); | 1065 | $PAGE->assign('language', $conf->get('translation.language')); |
1066 | $PAGE->assign('gd_enabled', extension_loaded('gd')); | ||
1067 | $PAGE->assign('thumbnails_mode', $conf->get('thumbnails.mode', Thumbnailer::MODE_NONE)); | ||
1050 | $PAGE->assign('pagetitle', t('Configure') .' - '. $conf->get('general.title', 'Shaarli')); | 1068 | $PAGE->assign('pagetitle', t('Configure') .' - '. $conf->get('general.title', 'Shaarli')); |
1051 | $PAGE->renderPage('configure'); | 1069 | $PAGE->renderPage('configure'); |
1052 | exit; | 1070 | exit; |
@@ -1148,6 +1166,11 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager, | |||
1148 | $link['title'] = $link['url']; | 1166 | $link['title'] = $link['url']; |
1149 | } | 1167 | } |
1150 | 1168 | ||
1169 | if ($conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE) { | ||
1170 | $thumbnailer = new Thumbnailer($conf); | ||
1171 | $link['thumbnail'] = $thumbnailer->get($url); | ||
1172 | } | ||
1173 | |||
1151 | $pluginManager->executeHooks('save_link', $link); | 1174 | $pluginManager->executeHooks('save_link', $link); |
1152 | 1175 | ||
1153 | $LINKSDB[$id] = $link; | 1176 | $LINKSDB[$id] = $link; |
@@ -1486,6 +1509,43 @@ function renderPage($conf, $pluginManager, $LINKSDB, $history, $sessionManager, | |||
1486 | exit; | 1509 | exit; |
1487 | } | 1510 | } |
1488 | 1511 | ||
1512 | // -------- Thumbnails Update | ||
1513 | if ($targetPage == Router::$PAGE_THUMBS_UPDATE) { | ||
1514 | $ids = []; | ||
1515 | foreach ($LINKSDB as $link) { | ||
1516 | // A note or not HTTP(S) | ||
1517 | if ($link['url'][0] === '?' || ! startsWith(strtolower($link['url']), 'http')) { | ||
1518 | continue; | ||
1519 | } | ||
1520 | $ids[] = $link['id']; | ||
1521 | } | ||
1522 | $PAGE->assign('ids', $ids); | ||
1523 | $PAGE->assign('pagetitle', t('Thumbnails update') .' - '. $conf->get('general.title', 'Shaarli')); | ||
1524 | $PAGE->renderPage('thumbnails'); | ||
1525 | exit; | ||
1526 | } | ||
1527 | |||
1528 | // -------- Single Thumbnail Update | ||
1529 | if ($targetPage == Router::$AJAX_THUMB_UPDATE) { | ||
1530 | if (! isset($_POST['id']) || ! ctype_digit($_POST['id'])) { | ||
1531 | http_response_code(400); | ||
1532 | exit; | ||
1533 | } | ||
1534 | $id = (int) $_POST['id']; | ||
1535 | if (empty($LINKSDB[$id])) { | ||
1536 | http_response_code(404); | ||
1537 | exit; | ||
1538 | } | ||
1539 | $thumbnailer = new Thumbnailer($conf); | ||
1540 | $link = $LINKSDB[$id]; | ||
1541 | $link['thumbnail'] = $thumbnailer->get($link['url']); | ||
1542 | $LINKSDB[$id] = $link; | ||
1543 | $LINKSDB->save($conf->get('resource.page_cache')); | ||
1544 | |||
1545 | echo json_encode($link); | ||
1546 | exit; | ||
1547 | } | ||
1548 | |||
1489 | // -------- Otherwise, simply display search form and links: | 1549 | // -------- Otherwise, simply display search form and links: |
1490 | showLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager); | 1550 | showLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager); |
1491 | exit; | 1551 | exit; |
@@ -1549,6 +1609,12 @@ function buildLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager) | |||
1549 | // Start index. | 1609 | // Start index. |
1550 | $i = ($page-1) * $_SESSION['LINKS_PER_PAGE']; | 1610 | $i = ($page-1) * $_SESSION['LINKS_PER_PAGE']; |
1551 | $end = $i + $_SESSION['LINKS_PER_PAGE']; | 1611 | $end = $i + $_SESSION['LINKS_PER_PAGE']; |
1612 | |||
1613 | $thumbnailsEnabled = $conf->get('thumbnails.mode', Thumbnailer::MODE_NONE) !== Thumbnailer::MODE_NONE; | ||
1614 | if ($thumbnailsEnabled) { | ||
1615 | $thumbnailer = new Thumbnailer($conf); | ||
1616 | } | ||
1617 | |||
1552 | $linkDisp = array(); | 1618 | $linkDisp = array(); |
1553 | while ($i<$end && $i<count($keys)) | 1619 | while ($i<$end && $i<count($keys)) |
1554 | { | 1620 | { |
@@ -1569,9 +1635,21 @@ function buildLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager) | |||
1569 | $taglist = preg_split('/\s+/', $link['tags'], -1, PREG_SPLIT_NO_EMPTY); | 1635 | $taglist = preg_split('/\s+/', $link['tags'], -1, PREG_SPLIT_NO_EMPTY); |
1570 | uasort($taglist, 'strcasecmp'); | 1636 | uasort($taglist, 'strcasecmp'); |
1571 | $link['taglist'] = $taglist; | 1637 | $link['taglist'] = $taglist; |
1638 | |||
1639 | // Thumbnails enabled, not a note, | ||
1640 | // and (never retrieved yet or no valid cache file) | ||
1641 | if ($thumbnailsEnabled && $link['url'][0] != '?' | ||
1642 | && (! isset($link['thumbnail']) || ($link['thumbnail'] !== false && ! is_file($link['thumbnail']))) | ||
1643 | ) { | ||
1644 | $elem = $LINKSDB[$keys[$i]]; | ||
1645 | $elem['thumbnail'] = $thumbnailer->get($link['url']); | ||
1646 | $LINKSDB[$keys[$i]] = $elem; | ||
1647 | $updateDB = true; | ||
1648 | $link['thumbnail'] = $elem['thumbnail']; | ||
1649 | } | ||
1650 | |||
1572 | // Check for both signs of a note: starting with ? and 7 chars long. | 1651 | // Check for both signs of a note: starting with ? and 7 chars long. |
1573 | if ($link['url'][0] === '?' && | 1652 | if ($link['url'][0] === '?' && strlen($link['url']) === 7) { |
1574 | strlen($link['url']) === 7) { | ||
1575 | $link['url'] = index_url($_SERVER) . $link['url']; | 1653 | $link['url'] = index_url($_SERVER) . $link['url']; |
1576 | } | 1654 | } |
1577 | 1655 | ||
@@ -1579,6 +1657,11 @@ function buildLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager) | |||
1579 | $i++; | 1657 | $i++; |
1580 | } | 1658 | } |
1581 | 1659 | ||
1660 | // If we retrieved new thumbnails, we update the database. | ||
1661 | if (!empty($updateDB)) { | ||
1662 | $LINKSDB->save($conf->get('resource.page_cache')); | ||
1663 | } | ||
1664 | |||
1582 | // Compute paging navigation | 1665 | // Compute paging navigation |
1583 | $searchtagsUrl = $searchtags === '' ? '' : '&searchtags=' . urlencode($searchtags); | 1666 | $searchtagsUrl = $searchtags === '' ? '' : '&searchtags=' . urlencode($searchtags); |
1584 | $searchtermUrl = empty($searchterm) ? '' : '&searchterm=' . urlencode($searchterm); | 1667 | $searchtermUrl = empty($searchterm) ? '' : '&searchterm=' . urlencode($searchterm); |
@@ -1630,194 +1713,6 @@ function buildLinkList($PAGE, $LINKSDB, $conf, $pluginManager, $loginManager) | |||
1630 | } | 1713 | } |
1631 | 1714 | ||
1632 | /** | 1715 | /** |
1633 | * Compute the thumbnail for a link. | ||
1634 | * | ||
1635 | * With a link to the original URL. | ||
1636 | * Understands various services (youtube.com...) | ||
1637 | * Input: $url = URL for which the thumbnail must be found. | ||
1638 | * $href = if provided, this URL will be followed instead of $url | ||
1639 | * Returns an associative array with thumbnail attributes (src,href,width,height,style,alt) | ||
1640 | * Some of them may be missing. | ||
1641 | * Return an empty array if no thumbnail available. | ||
1642 | * | ||
1643 | * @param ConfigManager $conf Configuration Manager instance. | ||
1644 | * @param string $url | ||
1645 | * @param string|bool $href | ||
1646 | * | ||
1647 | * @return array | ||
1648 | */ | ||
1649 | function computeThumbnail($conf, $url, $href = false) | ||
1650 | { | ||
1651 | if (!$conf->get('thumbnail.enable_thumbnails')) return array(); | ||
1652 | if ($href==false) $href=$url; | ||
1653 | |||
1654 | // For most hosts, the URL of the thumbnail can be easily deduced from the URL of the link. | ||
1655 | // (e.g. http://www.youtube.com/watch?v=spVypYk4kto ---> http://img.youtube.com/vi/spVypYk4kto/default.jpg ) | ||
1656 | // ^^^^^^^^^^^ ^^^^^^^^^^^ | ||
1657 | $domain = parse_url($url,PHP_URL_HOST); | ||
1658 | if ($domain=='youtube.com' || $domain=='www.youtube.com') | ||
1659 | { | ||
1660 | parse_str(parse_url($url,PHP_URL_QUERY), $params); // Extract video ID and get thumbnail | ||
1661 | if (!empty($params['v'])) return array('src'=>'https://img.youtube.com/vi/'.$params['v'].'/default.jpg', | ||
1662 | 'href'=>$href,'width'=>'120','height'=>'90','alt'=>'YouTube thumbnail'); | ||
1663 | } | ||
1664 | if ($domain=='youtu.be') // Youtube short links | ||
1665 | { | ||
1666 | $path = parse_url($url,PHP_URL_PATH); | ||
1667 | return array('src'=>'https://img.youtube.com/vi'.$path.'/default.jpg', | ||
1668 | 'href'=>$href,'width'=>'120','height'=>'90','alt'=>'YouTube thumbnail'); | ||
1669 | } | ||
1670 | if ($domain=='pix.toile-libre.org') // pix.toile-libre.org image hosting | ||
1671 | { | ||
1672 | parse_str(parse_url($url,PHP_URL_QUERY), $params); // Extract image filename. | ||
1673 | if (!empty($params) && !empty($params['img'])) return array('src'=>'http://pix.toile-libre.org/upload/thumb/'.urlencode($params['img']), | ||
1674 | 'href'=>$href,'style'=>'max-width:120px; max-height:150px','alt'=>'pix.toile-libre.org thumbnail'); | ||
1675 | } | ||
1676 | |||
1677 | if ($domain=='imgur.com') | ||
1678 | { | ||
1679 | $path = parse_url($url,PHP_URL_PATH); | ||
1680 | if (startsWith($path,'/a/')) return array(); // Thumbnails for albums are not available. | ||
1681 | if (startsWith($path,'/r/')) return array('src'=>'https://i.imgur.com/'.basename($path).'s.jpg', | ||
1682 | 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail'); | ||
1683 | if (startsWith($path,'/gallery/')) return array('src'=>'https://i.imgur.com'.substr($path,8).'s.jpg', | ||
1684 | 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail'); | ||
1685 | |||
1686 | if (substr_count($path,'/')==1) return array('src'=>'https://i.imgur.com/'.substr($path,1).'s.jpg', | ||
1687 | 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail'); | ||
1688 | } | ||
1689 | if ($domain=='i.imgur.com') | ||
1690 | { | ||
1691 | $pi = pathinfo(parse_url($url,PHP_URL_PATH)); | ||
1692 | if (!empty($pi['filename'])) return array('src'=>'https://i.imgur.com/'.$pi['filename'].'s.jpg', | ||
1693 | 'href'=>$href,'width'=>'90','height'=>'90','alt'=>'imgur.com thumbnail'); | ||
1694 | } | ||
1695 | if ($domain=='dailymotion.com' || $domain=='www.dailymotion.com') | ||
1696 | { | ||
1697 | if (strpos($url,'dailymotion.com/video/')!==false) | ||
1698 | { | ||
1699 | $thumburl=str_replace('dailymotion.com/video/','dailymotion.com/thumbnail/video/',$url); | ||
1700 | return array('src'=>$thumburl, | ||
1701 | 'href'=>$href,'width'=>'120','style'=>'height:auto;','alt'=>'DailyMotion thumbnail'); | ||
1702 | } | ||
1703 | } | ||
1704 | if (endsWith($domain,'.imageshack.us')) | ||
1705 | { | ||
1706 | $ext=strtolower(pathinfo($url,PATHINFO_EXTENSION)); | ||
1707 | if ($ext=='jpg' || $ext=='jpeg' || $ext=='png' || $ext=='gif') | ||
1708 | { | ||
1709 | $thumburl = substr($url,0,strlen($url)-strlen($ext)).'th.'.$ext; | ||
1710 | return array('src'=>$thumburl, | ||
1711 | 'href'=>$href,'width'=>'120','style'=>'height:auto;','alt'=>'imageshack.us thumbnail'); | ||
1712 | } | ||
1713 | } | ||
1714 | |||
1715 | // Some other hosts are SLOW AS HELL and usually require an extra HTTP request to get the thumbnail URL. | ||
1716 | // So we deport the thumbnail generation in order not to slow down page generation | ||
1717 | // (and we also cache the thumbnail) | ||
1718 | |||
1719 | if (! $conf->get('thumbnail.enable_localcache')) return array(); // If local cache is disabled, no thumbnails for services which require the use a local cache. | ||
1720 | |||
1721 | if ($domain=='flickr.com' || endsWith($domain,'.flickr.com') | ||
1722 | || $domain=='vimeo.com' | ||
1723 | || $domain=='ted.com' || endsWith($domain,'.ted.com') | ||
1724 | || $domain=='xkcd.com' || endsWith($domain,'.xkcd.com') | ||
1725 | ) | ||
1726 | { | ||
1727 | if ($domain=='vimeo.com') | ||
1728 | { // Make sure this vimeo URL points to a video (/xxx... where xxx is numeric) | ||
1729 | $path = parse_url($url,PHP_URL_PATH); | ||
1730 | if (!preg_match('!/\d+.+?!',$path)) return array(); // This is not a single video URL. | ||
1731 | } | ||
1732 | if ($domain=='xkcd.com' || endsWith($domain,'.xkcd.com')) | ||
1733 | { // Make sure this URL points to a single comic (/xxx... where xxx is numeric) | ||
1734 | $path = parse_url($url,PHP_URL_PATH); | ||
1735 | if (!preg_match('!/\d+.+?!',$path)) return array(); | ||
1736 | } | ||
1737 | if ($domain=='ted.com' || endsWith($domain,'.ted.com')) | ||
1738 | { // Make sure this TED URL points to a video (/talks/...) | ||
1739 | $path = parse_url($url,PHP_URL_PATH); | ||
1740 | if ("/talks/" !== substr($path,0,7)) return array(); // This is not a single video URL. | ||
1741 | } | ||
1742 | $sign = hash_hmac('sha256', $url, $conf->get('credentials.salt')); // We use the salt to sign data (it's random, secret, and specific to each installation) | ||
1743 | return array('src'=>index_url($_SERVER).'?do=genthumbnail&hmac='.$sign.'&url='.urlencode($url), | ||
1744 | 'href'=>$href,'width'=>'120','style'=>'height:auto;','alt'=>'thumbnail'); | ||
1745 | } | ||
1746 | |||
1747 | // For all other, we try to make a thumbnail of links ending with .jpg/jpeg/png/gif | ||
1748 | // Technically speaking, we should download ALL links and check their Content-Type to see if they are images. | ||
1749 | // But using the extension will do. | ||
1750 | $ext=strtolower(pathinfo($url,PATHINFO_EXTENSION)); | ||
1751 | if ($ext=='jpg' || $ext=='jpeg' || $ext=='png' || $ext=='gif') | ||
1752 | { | ||
1753 | $sign = hash_hmac('sha256', $url, $conf->get('credentials.salt')); // We use the salt to sign data (it's random, secret, and specific to each installation) | ||
1754 | return array('src'=>index_url($_SERVER).'?do=genthumbnail&hmac='.$sign.'&url='.urlencode($url), | ||
1755 | 'href'=>$href,'width'=>'120','style'=>'height:auto;','alt'=>'thumbnail'); | ||
1756 | } | ||
1757 | return array(); // No thumbnail. | ||
1758 | |||
1759 | } | ||
1760 | |||
1761 | |||
1762 | // Returns the HTML code to display a thumbnail for a link | ||
1763 | // with a link to the original URL. | ||
1764 | // Understands various services (youtube.com...) | ||
1765 | // Input: $url = URL for which the thumbnail must be found. | ||
1766 | // $href = if provided, this URL will be followed instead of $url | ||
1767 | // Returns '' if no thumbnail available. | ||
1768 | function thumbnail($url,$href=false) | ||
1769 | { | ||
1770 | // FIXME! | ||
1771 | global $conf; | ||
1772 | $t = computeThumbnail($conf, $url,$href); | ||
1773 | if (count($t)==0) return ''; // Empty array = no thumbnail for this URL. | ||
1774 | |||
1775 | $html='<a href="'.escape($t['href']).'"><img src="'.escape($t['src']).'"'; | ||
1776 | if (!empty($t['width'])) $html.=' width="'.escape($t['width']).'"'; | ||
1777 | if (!empty($t['height'])) $html.=' height="'.escape($t['height']).'"'; | ||
1778 | if (!empty($t['style'])) $html.=' style="'.escape($t['style']).'"'; | ||
1779 | if (!empty($t['alt'])) $html.=' alt="'.escape($t['alt']).'"'; | ||
1780 | $html.='></a>'; | ||
1781 | return $html; | ||
1782 | } | ||
1783 | |||
1784 | // Returns the HTML code to display a thumbnail for a link | ||
1785 | // for the picture wall (using lazy image loading) | ||
1786 | // Understands various services (youtube.com...) | ||
1787 | // Input: $url = URL for which the thumbnail must be found. | ||
1788 | // $href = if provided, this URL will be followed instead of $url | ||
1789 | // Returns '' if no thumbnail available. | ||
1790 | function lazyThumbnail($conf, $url,$href=false) | ||
1791 | { | ||
1792 | // FIXME! | ||
1793 | global $conf; | ||
1794 | $t = computeThumbnail($conf, $url,$href); | ||
1795 | if (count($t)==0) return ''; // Empty array = no thumbnail for this URL. | ||
1796 | |||
1797 | $html='<a href="'.escape($t['href']).'">'; | ||
1798 | |||
1799 | // Lazy image | ||
1800 | $html.='<img class="b-lazy" src="#" data-src="'.escape($t['src']).'"'; | ||
1801 | |||
1802 | if (!empty($t['width'])) $html.=' width="'.escape($t['width']).'"'; | ||
1803 | if (!empty($t['height'])) $html.=' height="'.escape($t['height']).'"'; | ||
1804 | if (!empty($t['style'])) $html.=' style="'.escape($t['style']).'"'; | ||
1805 | if (!empty($t['alt'])) $html.=' alt="'.escape($t['alt']).'"'; | ||
1806 | $html.='>'; | ||
1807 | |||
1808 | // No-JavaScript fallback. | ||
1809 | $html.='<noscript><img src="'.escape($t['src']).'"'; | ||
1810 | if (!empty($t['width'])) $html.=' width="'.escape($t['width']).'"'; | ||
1811 | if (!empty($t['height'])) $html.=' height="'.escape($t['height']).'"'; | ||
1812 | if (!empty($t['style'])) $html.=' style="'.escape($t['style']).'"'; | ||
1813 | if (!empty($t['alt'])) $html.=' alt="'.escape($t['alt']).'"'; | ||
1814 | $html.='></noscript></a>'; | ||
1815 | |||
1816 | return $html; | ||
1817 | } | ||
1818 | |||
1819 | |||
1820 | /** | ||
1821 | * Installation | 1716 | * Installation |
1822 | * This function should NEVER be called if the file data/config.php exists. | 1717 | * This function should NEVER be called if the file data/config.php exists. |
1823 | * | 1718 | * |
@@ -1908,7 +1803,7 @@ function install($conf, $sessionManager, $loginManager) { | |||
1908 | exit; | 1803 | exit; |
1909 | } | 1804 | } |
1910 | 1805 | ||
1911 | $PAGE = new PageBuilder($conf, null, $sessionManager->generateToken()); | 1806 | $PAGE = new PageBuilder($conf, $_SESSION, null, $sessionManager->generateToken()); |
1912 | list($continents, $cities) = generateTimeZoneData(timezone_identifiers_list(), date_default_timezone_get()); | 1807 | list($continents, $cities) = generateTimeZoneData(timezone_identifiers_list(), date_default_timezone_get()); |
1913 | $PAGE->assign('continents', $continents); | 1808 | $PAGE->assign('continents', $continents); |
1914 | $PAGE->assign('cities', $cities); | 1809 | $PAGE->assign('cities', $cities); |
@@ -1917,232 +1812,6 @@ function install($conf, $sessionManager, $loginManager) { | |||
1917 | exit; | 1812 | exit; |
1918 | } | 1813 | } |
1919 | 1814 | ||
1920 | /** | ||
1921 | * Because some f*cking services like flickr require an extra HTTP request to get the thumbnail URL, | ||
1922 | * I have deported the thumbnail URL code generation here, otherwise this would slow down page generation. | ||
1923 | * The following function takes the URL a link (e.g. a flickr page) and return the proper thumbnail. | ||
1924 | * This function is called by passing the URL: | ||
1925 | * http://mywebsite.com/shaarli/?do=genthumbnail&hmac=[HMAC]&url=[URL] | ||
1926 | * [URL] is the URL of the link (e.g. a flickr page) | ||
1927 | * [HMAC] is the signature for the [URL] (so that these URL cannot be forged). | ||
1928 | * The function below will fetch the image from the webservice and store it in the cache. | ||
1929 | * | ||
1930 | * @param ConfigManager $conf Configuration Manager instance, | ||
1931 | */ | ||
1932 | function genThumbnail($conf) | ||
1933 | { | ||
1934 | // Make sure the parameters in the URL were generated by us. | ||
1935 | $sign = hash_hmac('sha256', $_GET['url'], $conf->get('credentials.salt')); | ||
1936 | if ($sign!=$_GET['hmac']) die('Naughty boy!'); | ||
1937 | |||
1938 | $cacheDir = $conf->get('resource.thumbnails_cache', 'cache'); | ||
1939 | // Let's see if we don't already have the image for this URL in the cache. | ||
1940 | $thumbname=hash('sha1',$_GET['url']).'.jpg'; | ||
1941 | if (is_file($cacheDir .'/'. $thumbname)) | ||
1942 | { // We have the thumbnail, just serve it: | ||
1943 | header('Content-Type: image/jpeg'); | ||
1944 | echo file_get_contents($cacheDir .'/'. $thumbname); | ||
1945 | return; | ||
1946 | } | ||
1947 | // We may also serve a blank image (if service did not respond) | ||
1948 | $blankname=hash('sha1',$_GET['url']).'.gif'; | ||
1949 | if (is_file($cacheDir .'/'. $blankname)) | ||
1950 | { | ||
1951 | header('Content-Type: image/gif'); | ||
1952 | echo file_get_contents($cacheDir .'/'. $blankname); | ||
1953 | return; | ||
1954 | } | ||
1955 | |||
1956 | // Otherwise, generate the thumbnail. | ||
1957 | $url = $_GET['url']; | ||
1958 | $domain = parse_url($url,PHP_URL_HOST); | ||
1959 | |||
1960 | if ($domain=='flickr.com' || endsWith($domain,'.flickr.com')) | ||
1961 | { | ||
1962 | // Crude replacement to handle new flickr domain policy (They prefer www. now) | ||
1963 | $url = str_replace('http://flickr.com/','http://www.flickr.com/',$url); | ||
1964 | |||
1965 | // Is this a link to an image, or to a flickr page ? | ||
1966 | $imageurl=''; | ||
1967 | if (endsWith(parse_url($url, PHP_URL_PATH), '.jpg')) | ||
1968 | { // This is a direct link to an image. e.g. http://farm1.staticflickr.com/5/5921913_ac83ed27bd_o.jpg | ||
1969 | preg_match('!(http://farm\d+\.staticflickr\.com/\d+/\d+_\w+_)\w.jpg!',$url,$matches); | ||
1970 | if (!empty($matches[1])) $imageurl=$matches[1].'m.jpg'; | ||
1971 | } | ||
1972 | else // This is a flickr page (html) | ||
1973 | { | ||
1974 | // Get the flickr html page. | ||
1975 | list($headers, $content) = get_http_response($url, 20); | ||
1976 | if (strpos($headers[0], '200 OK') !== false) | ||
1977 | { | ||
1978 | // flickr now nicely provides the URL of the thumbnail in each flickr page. | ||
1979 | preg_match('!<link rel=\"image_src\" href=\"(.+?)\"!', $content, $matches); | ||
1980 | if (!empty($matches[1])) $imageurl=$matches[1]; | ||
1981 | |||
1982 | // In albums (and some other pages), the link rel="image_src" is not provided, | ||
1983 | // but flickr provides: | ||
1984 | // <meta property="og:image" content="http://farm4.staticflickr.com/3398/3239339068_25d13535ff_z.jpg" /> | ||
1985 | if ($imageurl=='') | ||
1986 | { | ||
1987 | preg_match('!<meta property=\"og:image\" content=\"(.+?)\"!', $content, $matches); | ||
1988 | if (!empty($matches[1])) $imageurl=$matches[1]; | ||
1989 | } | ||
1990 | } | ||
1991 | } | ||
1992 | |||
1993 | if ($imageurl!='') | ||
1994 | { // Let's download the image. | ||
1995 | // Image is 240x120, so 10 seconds to download should be enough. | ||
1996 | list($headers, $content) = get_http_response($imageurl, 10); | ||
1997 | if (strpos($headers[0], '200 OK') !== false) { | ||
1998 | // Save image to cache. | ||
1999 | file_put_contents($cacheDir .'/'. $thumbname, $content); | ||
2000 | header('Content-Type: image/jpeg'); | ||
2001 | echo $content; | ||
2002 | return; | ||
2003 | } | ||
2004 | } | ||
2005 | } | ||
2006 | |||
2007 | elseif ($domain=='vimeo.com' ) | ||
2008 | { | ||
2009 | // This is more complex: we have to perform a HTTP request, then parse the result. | ||
2010 | // Maybe we should deport this to JavaScript ? Example: http://stackoverflow.com/questions/1361149/get-img-thumbnails-from-vimeo/4285098#4285098 | ||
2011 | $vid = substr(parse_url($url,PHP_URL_PATH),1); | ||
2012 | list($headers, $content) = get_http_response('https://vimeo.com/api/v2/video/'.escape($vid).'.php', 5); | ||
2013 | if (strpos($headers[0], '200 OK') !== false) { | ||
2014 | $t = unserialize($content); | ||
2015 | $imageurl = $t[0]['thumbnail_medium']; | ||
2016 | // Then we download the image and serve it to our client. | ||
2017 | list($headers, $content) = get_http_response($imageurl, 10); | ||
2018 | if (strpos($headers[0], '200 OK') !== false) { | ||
2019 | // Save image to cache. | ||
2020 | file_put_contents($cacheDir .'/'. $thumbname, $content); | ||
2021 | header('Content-Type: image/jpeg'); | ||
2022 | echo $content; | ||
2023 | return; | ||
2024 | } | ||
2025 | } | ||
2026 | } | ||
2027 | |||
2028 | elseif ($domain=='ted.com' || endsWith($domain,'.ted.com')) | ||
2029 | { | ||
2030 | // The thumbnail for TED talks is located in the <link rel="image_src" [...]> tag on that page | ||
2031 | // http://www.ted.com/talks/mikko_hypponen_fighting_viruses_defending_the_net.html | ||
2032 | // <link rel="image_src" href="http://images.ted.com/images/ted/28bced335898ba54d4441809c5b1112ffaf36781_389x292.jpg" /> | ||
2033 | list($headers, $content) = get_http_response($url, 5); | ||
2034 | if (strpos($headers[0], '200 OK') !== false) { | ||
2035 | // Extract the link to the thumbnail | ||
2036 | preg_match('!link rel="image_src" href="(http://images.ted.com/images/ted/.+_\d+x\d+\.jpg)"!', $content, $matches); | ||
2037 | if (!empty($matches[1])) | ||
2038 | { // Let's download the image. | ||
2039 | $imageurl=$matches[1]; | ||
2040 | // No control on image size, so wait long enough | ||
2041 | list($headers, $content) = get_http_response($imageurl, 20); | ||
2042 | if (strpos($headers[0], '200 OK') !== false) { | ||
2043 | $filepath = $cacheDir .'/'. $thumbname; | ||
2044 | file_put_contents($filepath, $content); // Save image to cache. | ||
2045 | if (resizeImage($filepath)) | ||
2046 | { | ||
2047 | header('Content-Type: image/jpeg'); | ||
2048 | echo file_get_contents($filepath); | ||
2049 | return; | ||
2050 | } | ||
2051 | } | ||
2052 | } | ||
2053 | } | ||
2054 | } | ||
2055 | |||
2056 | elseif ($domain=='xkcd.com' || endsWith($domain,'.xkcd.com')) | ||
2057 | { | ||
2058 | // There is no thumbnail available for xkcd comics, so download the whole image and resize it. | ||
2059 | // http://xkcd.com/327/ | ||
2060 | // <img src="http://imgs.xkcd.com/comics/exploits_of_a_mom.png" title="<BLABLA>" alt="<BLABLA>" /> | ||
2061 | list($headers, $content) = get_http_response($url, 5); | ||
2062 | if (strpos($headers[0], '200 OK') !== false) { | ||
2063 | // Extract the link to the thumbnail | ||
2064 | preg_match('!<img src="(http://imgs.xkcd.com/comics/.*)" title="[^s]!', $content, $matches); | ||
2065 | if (!empty($matches[1])) | ||
2066 | { // Let's download the image. | ||
2067 | $imageurl=$matches[1]; | ||
2068 | // No control on image size, so wait long enough | ||
2069 | list($headers, $content) = get_http_response($imageurl, 20); | ||
2070 | if (strpos($headers[0], '200 OK') !== false) { | ||
2071 | $filepath = $cacheDir.'/'.$thumbname; | ||
2072 | // Save image to cache. | ||
2073 | file_put_contents($filepath, $content); | ||
2074 | if (resizeImage($filepath)) | ||
2075 | { | ||
2076 | header('Content-Type: image/jpeg'); | ||
2077 | echo file_get_contents($filepath); | ||
2078 | return; | ||
2079 | } | ||
2080 | } | ||
2081 | } | ||
2082 | } | ||
2083 | } | ||
2084 | |||
2085 | else | ||
2086 | { | ||
2087 | // For all other domains, we try to download the image and make a thumbnail. | ||
2088 | // We allow 30 seconds max to download (and downloads are limited to 4 Mb) | ||
2089 | list($headers, $content) = get_http_response($url, 30); | ||
2090 | if (strpos($headers[0], '200 OK') !== false) { | ||
2091 | $filepath = $cacheDir .'/'.$thumbname; | ||
2092 | // Save image to cache. | ||
2093 | file_put_contents($filepath, $content); | ||
2094 | if (resizeImage($filepath)) | ||
2095 | { | ||
2096 | header('Content-Type: image/jpeg'); | ||
2097 | echo file_get_contents($filepath); | ||
2098 | return; | ||
2099 | } | ||
2100 | } | ||
2101 | } | ||
2102 | |||
2103 | |||
2104 | // Otherwise, return an empty image (8x8 transparent gif) | ||
2105 | $blankgif = base64_decode('R0lGODlhCAAIAIAAAP///////yH5BAEKAAEALAAAAAAIAAgAAAIHjI+py+1dAAA7'); | ||
2106 | // Also put something in cache so that this URL is not requested twice. | ||
2107 | file_put_contents($cacheDir .'/'. $blankname, $blankgif); | ||
2108 | header('Content-Type: image/gif'); | ||
2109 | echo $blankgif; | ||
2110 | } | ||
2111 | |||
2112 | // Make a thumbnail of the image (to width: 120 pixels) | ||
2113 | // Returns true if success, false otherwise. | ||
2114 | function resizeImage($filepath) | ||
2115 | { | ||
2116 | if (!function_exists('imagecreatefromjpeg')) return false; // GD not present: no thumbnail possible. | ||
2117 | |||
2118 | // Trick: some stupid people rename GIF as JPEG... or else. | ||
2119 | // So we really try to open each image type whatever the extension is. | ||
2120 | $header=file_get_contents($filepath,false,NULL,0,256); // Read first 256 bytes and try to sniff file type. | ||
2121 | $im=false; | ||
2122 | $i=strpos($header,'GIF8'); if (($i!==false) && ($i==0)) $im = imagecreatefromgif($filepath); // Well this is crude, but it should be enough. | ||
2123 | $i=strpos($header,'PNG'); if (($i!==false) && ($i==1)) $im = imagecreatefrompng($filepath); | ||
2124 | $i=strpos($header,'JFIF'); if ($i!==false) $im = imagecreatefromjpeg($filepath); | ||
2125 | if (!$im) return false; // Unable to open image (corrupted or not an image) | ||
2126 | $w = imagesx($im); | ||
2127 | $h = imagesy($im); | ||
2128 | $ystart = 0; $yheight=$h; | ||
2129 | if ($h>$w) { $ystart= ($h/2)-($w/2); $yheight=$w/2; } | ||
2130 | $nw = 120; // Desired width | ||
2131 | $nh = min(floor(($h*$nw)/$w),120); // Compute new width/height, but maximum 120 pixels height. | ||
2132 | // Resize image: | ||
2133 | $im2 = imagecreatetruecolor($nw,$nh); | ||
2134 | imagecopyresampled($im2, $im, 0, 0, 0, $ystart, $nw, $nh, $w, $yheight); | ||
2135 | imageinterlace($im2,true); // For progressive JPEG. | ||
2136 | $tempname=$filepath.'_TEMP.jpg'; | ||
2137 | imagejpeg($im2, $tempname, 90); | ||
2138 | imagedestroy($im); | ||
2139 | imagedestroy($im2); | ||
2140 | unlink($filepath); | ||
2141 | rename($tempname,$filepath); // Overwrite original picture with thumbnail. | ||
2142 | return true; | ||
2143 | } | ||
2144 | |||
2145 | if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=genthumbnail')) { genThumbnail($conf); exit; } // Thumbnail generation/cache does not need the link database. | ||
2146 | if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=dailyrss')) { showDailyRSS($conf); exit; } | 1815 | if (isset($_SERVER['QUERY_STRING']) && startsWith($_SERVER['QUERY_STRING'], 'do=dailyrss')) { showDailyRSS($conf); exit; } |
2147 | if (!isset($_SESSION['LINKS_PER_PAGE'])) { | 1816 | if (!isset($_SESSION['LINKS_PER_PAGE'])) { |
2148 | $_SESSION['LINKS_PER_PAGE'] = $conf->get('general.links_per_page', 20); | 1817 | $_SESSION['LINKS_PER_PAGE'] = $conf->get('general.links_per_page', 20); |