Use hashes to identify favicons
This commit is contained in:
parent
89433f8374
commit
a2d2e95555
@ -41,7 +41,7 @@ Router\get_action('bookmarks', function() {
|
|||||||
$items = Model\Item\get_bookmarks($offset, Model\Config\get('items_per_page'));
|
$items = Model\Item\get_bookmarks($offset, Model\Config\get('items_per_page'));
|
||||||
|
|
||||||
Response\html(Template\layout('bookmarks', array(
|
Response\html(Template\layout('bookmarks', array(
|
||||||
'favicons' => Model\Feed\get_item_favicons($items),
|
'favicons' => Model\Favicon\get_item_favicons($items),
|
||||||
'original_marks_read' => Model\Config\get('original_marks_read'),
|
'original_marks_read' => Model\Config\get('original_marks_read'),
|
||||||
'order' => '',
|
'order' => '',
|
||||||
'direction' => '',
|
'direction' => '',
|
||||||
|
@ -124,7 +124,7 @@ Router\get_action('feeds', function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Response\html(Template\layout('feeds', array(
|
Response\html(Template\layout('feeds', array(
|
||||||
'favicons' => Model\Feed\get_all_favicons(),
|
'favicons' => Model\Favicon\get_all_favicons(),
|
||||||
'feeds' => Model\Feed\get_all_item_counts(),
|
'feeds' => Model\Feed\get_all_item_counts(),
|
||||||
'nothing_to_read' => $nothing_to_read,
|
'nothing_to_read' => $nothing_to_read,
|
||||||
'nb_unread_items' => $nb_unread_items,
|
'nb_unread_items' => $nb_unread_items,
|
||||||
|
@ -15,7 +15,7 @@ Router\get_action('history', function() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Response\html(Template\layout('history', array(
|
Response\html(Template\layout('history', array(
|
||||||
'favicons' => Model\Feed\get_item_favicons($items),
|
'favicons' => Model\Favicon\get_item_favicons($items),
|
||||||
'original_marks_read' => Model\Config\get('original_marks_read'),
|
'original_marks_read' => Model\Config\get('original_marks_read'),
|
||||||
'items' => $items,
|
'items' => $items,
|
||||||
'order' => '',
|
'order' => '',
|
||||||
|
@ -34,7 +34,7 @@ Router\get_action('unread', function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Response\html(Template\layout('unread_items', array(
|
Response\html(Template\layout('unread_items', array(
|
||||||
'favicons' => Model\Feed\get_item_favicons($items),
|
'favicons' => Model\Favicon\get_item_favicons($items),
|
||||||
'original_marks_read' => Model\Config\get('original_marks_read'),
|
'original_marks_read' => Model\Config\get('original_marks_read'),
|
||||||
'order' => $order,
|
'order' => $order,
|
||||||
'direction' => $direction,
|
'direction' => $direction,
|
||||||
|
@ -42,7 +42,7 @@ function favicon_extension($type)
|
|||||||
'image/jpg' => '.jpg'
|
'image/jpg' => '.jpg'
|
||||||
);
|
);
|
||||||
|
|
||||||
if (in_array($type, $types)) {
|
if (array_key_exists($type, $types)) {
|
||||||
return $types[$type];
|
return $types[$type];
|
||||||
} else {
|
} else {
|
||||||
return '.ico';
|
return '.ico';
|
||||||
@ -52,7 +52,7 @@ function favicon_extension($type)
|
|||||||
function favicon(array $favicons, $feed_id)
|
function favicon(array $favicons, $feed_id)
|
||||||
{
|
{
|
||||||
if (! empty($favicons[$feed_id])) {
|
if (! empty($favicons[$feed_id])) {
|
||||||
return '<img src="'.FAVICON_URL_PATH.'/'.$favicons[$feed_id].'" class="favicon"/>';
|
return '<img src="'.FAVICON_URL_PATH.'/'.$favicons[$feed_id]['hash'].favicon_extension($favicons[$feed_id]['type']).'" class="favicon"/>';
|
||||||
}
|
}
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
|
202
models/favicon.php
Normal file
202
models/favicon.php
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Model\Favicon;
|
||||||
|
|
||||||
|
use UnexpectedValueException;
|
||||||
|
use Model\Config;
|
||||||
|
use Model\Item;
|
||||||
|
use Model\Group;
|
||||||
|
use Helper;
|
||||||
|
use SimpleValidator\Validator;
|
||||||
|
use SimpleValidator\Validators;
|
||||||
|
use PicoDb\Database;
|
||||||
|
use PicoFeed\Serialization\Export;
|
||||||
|
use PicoFeed\Serialization\Import;
|
||||||
|
use PicoFeed\Reader\Reader;
|
||||||
|
use PicoFeed\Reader\Favicon;
|
||||||
|
use PicoFeed\PicoFeedException;
|
||||||
|
use PicoFeed\Client\InvalidUrlException;
|
||||||
|
|
||||||
|
const LIMIT_ALL = -1;
|
||||||
|
|
||||||
|
// Create a favicons
|
||||||
|
function create_feed_favicon($feed_id, $site_url, $icon_link) {
|
||||||
|
if (has_favicon($feed_id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$favicon = fetch($feed_id, $site_url, $icon_link);
|
||||||
|
|
||||||
|
if ($favicon === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$favicon_id = store($favicon->getType(), $favicon->getContent());
|
||||||
|
|
||||||
|
if ($favicon_id === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Database::getInstance('db')
|
||||||
|
->table('favicons_feeds')
|
||||||
|
->save(array(
|
||||||
|
'feed_id' => $feed_id,
|
||||||
|
'favicon_id' => $favicon_id
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download a favicon
|
||||||
|
function fetch($feed_id, $site_url, $icon_link)
|
||||||
|
{
|
||||||
|
if (Config\get('favicons') == 1 && ! has_favicon($feed_id)) {
|
||||||
|
$favicon = new Favicon;
|
||||||
|
$favicon->find($site_url, $icon_link);
|
||||||
|
return $favicon;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the favicon (only if it does not exist yet)
|
||||||
|
function store($type, $icon)
|
||||||
|
{
|
||||||
|
if ($icon == "") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$hash = sha1($icon);
|
||||||
|
|
||||||
|
$favicon_id = get_favicon_id($hash);
|
||||||
|
|
||||||
|
if ($favicon_id) {
|
||||||
|
return $favicon_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = $hash.Helper\favicon_extension($type);
|
||||||
|
|
||||||
|
if (file_put_contents(FAVICON_DIRECTORY.DIRECTORY_SEPARATOR.$file, $icon) === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$saved = Database::getInstance('db')
|
||||||
|
->table('favicons')
|
||||||
|
->save(array(
|
||||||
|
'hash' => $hash,
|
||||||
|
'type' => $type
|
||||||
|
));
|
||||||
|
|
||||||
|
if ($saved === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return get_favicon_id($hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
function get_favicon_id($hash) {
|
||||||
|
return Database::getInstance('db')
|
||||||
|
->table('favicons')
|
||||||
|
->eq('hash', $hash)
|
||||||
|
->findOneColumn('id');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the favicon
|
||||||
|
function delete_favicon($favicon)
|
||||||
|
{
|
||||||
|
unlink(FAVICON_DIRECTORY.DIRECTORY_SEPARATOR.$favicon['hash'].Helper\favicon_extension($favicon['type']));
|
||||||
|
Database::getInstance('db')
|
||||||
|
->table('favicons')
|
||||||
|
->eq('hash', $favicon['hash'])
|
||||||
|
->remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Purge orphaned favicons from database
|
||||||
|
function purge_favicons()
|
||||||
|
{
|
||||||
|
$favicons = Database::getInstance('db')
|
||||||
|
->table('favicons')
|
||||||
|
->columns(
|
||||||
|
'favicons.type',
|
||||||
|
'favicons.hash',
|
||||||
|
'favicons_feeds.feed_id'
|
||||||
|
)
|
||||||
|
->join('favicons_feeds', 'favicon_id', 'id')
|
||||||
|
->isnull('favicons_feeds.feed_id')
|
||||||
|
->findAll();
|
||||||
|
|
||||||
|
foreach ($favicons as $favicon) {
|
||||||
|
delete_favicon($favicon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return true if the feed has a favicon
|
||||||
|
function has_favicon($feed_id)
|
||||||
|
{
|
||||||
|
return Database::getInstance('db')->table('favicons_feeds')->eq('feed_id', $feed_id)->count() === 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get favicons for those feeds
|
||||||
|
function get_favicons(array $feed_ids)
|
||||||
|
{
|
||||||
|
if (Config\get('favicons') == 0) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = array();
|
||||||
|
|
||||||
|
foreach ($feed_ids as $feed_id) {
|
||||||
|
$result[$feed_id] = Database::getInstance('db')
|
||||||
|
->table('favicons')
|
||||||
|
->columns(
|
||||||
|
'favicons.type',
|
||||||
|
'favicons.hash'
|
||||||
|
)
|
||||||
|
->join('favicons_feeds', 'favicon_id', 'id')
|
||||||
|
->eq('favicons_feeds.feed_id', $feed_id)
|
||||||
|
->findOne();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all favicons for a list of items
|
||||||
|
function get_item_favicons(array $items)
|
||||||
|
{
|
||||||
|
$feed_ids = array();
|
||||||
|
|
||||||
|
foreach ($items as $item) {
|
||||||
|
$feed_ids[$item['feed_id']] = $item['feed_id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return get_favicons($feed_ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all favicons
|
||||||
|
function get_all_favicons()
|
||||||
|
{
|
||||||
|
if (Config\get('favicons') == 0) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = Database::getInstance('db')
|
||||||
|
->table('favicons')
|
||||||
|
->columns(
|
||||||
|
'favicons_feeds.feed_id',
|
||||||
|
'favicons.type',
|
||||||
|
'favicons.hash'
|
||||||
|
)
|
||||||
|
->join('favicons_feeds', 'favicon_id', 'id')
|
||||||
|
->findAll();
|
||||||
|
|
||||||
|
$map = array();
|
||||||
|
|
||||||
|
foreach ($result as $row) {
|
||||||
|
$map[$row['feed_id']] = array (
|
||||||
|
"type" => $row['type'],
|
||||||
|
"hash" => $row['hash']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $map;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
113
models/feed.php
113
models/feed.php
@ -6,6 +6,7 @@ use UnexpectedValueException;
|
|||||||
use Model\Config;
|
use Model\Config;
|
||||||
use Model\Item;
|
use Model\Item;
|
||||||
use Model\Group;
|
use Model\Group;
|
||||||
|
use Model\Favicon;
|
||||||
use Helper;
|
use Helper;
|
||||||
use SimpleValidator\Validator;
|
use SimpleValidator\Validator;
|
||||||
use SimpleValidator\Validators;
|
use SimpleValidator\Validators;
|
||||||
@ -13,109 +14,11 @@ use PicoDb\Database;
|
|||||||
use PicoFeed\Serialization\Export;
|
use PicoFeed\Serialization\Export;
|
||||||
use PicoFeed\Serialization\Import;
|
use PicoFeed\Serialization\Import;
|
||||||
use PicoFeed\Reader\Reader;
|
use PicoFeed\Reader\Reader;
|
||||||
use PicoFeed\Reader\Favicon;
|
|
||||||
use PicoFeed\PicoFeedException;
|
use PicoFeed\PicoFeedException;
|
||||||
use PicoFeed\Client\InvalidUrlException;
|
use PicoFeed\Client\InvalidUrlException;
|
||||||
|
|
||||||
const LIMIT_ALL = -1;
|
const LIMIT_ALL = -1;
|
||||||
|
|
||||||
// Store the favicon
|
|
||||||
function store_favicon($feed_id, $link, $type, $icon)
|
|
||||||
{
|
|
||||||
$file = $feed_id.Helper\favicon_extension($type);
|
|
||||||
|
|
||||||
if (file_put_contents(FAVICON_DIRECTORY.DIRECTORY_SEPARATOR.$file, $icon) === false) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Database::getInstance('db')
|
|
||||||
->table('favicons')
|
|
||||||
->save(array(
|
|
||||||
'feed_id' => $feed_id,
|
|
||||||
'link' => $link,
|
|
||||||
'file' => $file,
|
|
||||||
'type' => $type
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete the favicon
|
|
||||||
function delete_favicon($feed_id)
|
|
||||||
{
|
|
||||||
foreach (get_favicons(array ($feed_id)) as $favicon) {
|
|
||||||
unlink(FAVICON_DIRECTORY.DIRECTORY_SEPARATOR.$favicon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete all the favicons
|
|
||||||
function delete_all_favicons()
|
|
||||||
{
|
|
||||||
foreach (get_all_favicons() as $favicon) {
|
|
||||||
unlink(FAVICON_DIRECTORY.DIRECTORY_SEPARATOR.$favicon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Download favicon
|
|
||||||
function fetch_favicon($feed_id, $site_url, $icon_link)
|
|
||||||
{
|
|
||||||
if (Config\get('favicons') == 1 && ! has_favicon($feed_id)) {
|
|
||||||
$favicon = new Favicon;
|
|
||||||
|
|
||||||
$link = $favicon->find($site_url, $icon_link);
|
|
||||||
$icon = $favicon->getContent();
|
|
||||||
$type = $favicon->getType();
|
|
||||||
|
|
||||||
if ($icon !== '') {
|
|
||||||
store_favicon($feed_id, $link, $type, $icon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return true if the feed have a favicon
|
|
||||||
function has_favicon($feed_id)
|
|
||||||
{
|
|
||||||
return Database::getInstance('db')->table('favicons')->eq('feed_id', $feed_id)->count() === 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get favicons for those feeds
|
|
||||||
function get_favicons(array $feed_ids)
|
|
||||||
{
|
|
||||||
if (Config\get('favicons') == 0) {
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
|
|
||||||
$db = Database::getInstance('db')
|
|
||||||
->hashtable('favicons')
|
|
||||||
->columnKey('feed_id')
|
|
||||||
->columnValue('file');
|
|
||||||
|
|
||||||
// pass $feeds_ids as argument list to hashtable::get(), use ... operator with php 5.6+
|
|
||||||
return call_user_func_array(array($db, 'get'), $feed_ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get all favicons for a list of items
|
|
||||||
function get_item_favicons(array $items)
|
|
||||||
{
|
|
||||||
$feed_ids = array();
|
|
||||||
|
|
||||||
foreach ($items as $item) {
|
|
||||||
$feed_ids[$item['feed_id']] = $item['feed_id'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return get_favicons($feed_ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get all favicons
|
|
||||||
function get_all_favicons()
|
|
||||||
{
|
|
||||||
if (Config\get('favicons') == 0) {
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Database::getInstance('db')
|
|
||||||
->hashtable('favicons')
|
|
||||||
->getAll('feed_id', 'file');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update feed information
|
// Update feed information
|
||||||
function update(array $values)
|
function update(array $values)
|
||||||
{
|
{
|
||||||
@ -236,7 +139,7 @@ function create($url, $enable_grabber = false, $force_rtl = false, $cloak_referr
|
|||||||
|
|
||||||
Group\update_feed_groups($feed_id, $group_ids, $create_group);
|
Group\update_feed_groups($feed_id, $group_ids, $create_group);
|
||||||
Item\update_all($feed_id, $feed->getItems());
|
Item\update_all($feed_id, $feed->getItems());
|
||||||
fetch_favicon($feed_id, $feed->getSiteUrl(), $feed->getIcon());
|
Favicon\create_feed_favicon($feed_id, $feed->getSiteUrl(), $feed->getIcon());
|
||||||
}
|
}
|
||||||
|
|
||||||
return $feed_id;
|
return $feed_id;
|
||||||
@ -301,7 +204,7 @@ function refresh($feed_id)
|
|||||||
update_cache($feed_id, $resource->getLastModified(), $resource->getEtag());
|
update_cache($feed_id, $resource->getLastModified(), $resource->getEtag());
|
||||||
|
|
||||||
Item\update_all($feed_id, $feed->getItems());
|
Item\update_all($feed_id, $feed->getItems());
|
||||||
fetch_favicon($feed_id, $feed->getSiteUrl(), $feed->getIcon());
|
Favicon\create_feed_favicon($feed_id, $feed->getSiteUrl(), $feed->getIcon());
|
||||||
}
|
}
|
||||||
|
|
||||||
update_parsing_error($feed_id, 0);
|
update_parsing_error($feed_id, 0);
|
||||||
@ -435,18 +338,20 @@ function update_cache($feed_id, $last_modified, $etag)
|
|||||||
// Remove one feed
|
// Remove one feed
|
||||||
function remove($feed_id)
|
function remove($feed_id)
|
||||||
{
|
{
|
||||||
delete_favicon($feed_id);
|
|
||||||
Group\remove_all($feed_id);
|
Group\remove_all($feed_id);
|
||||||
|
|
||||||
// Items are removed by a sql constraint
|
// Items are removed by a sql constraint
|
||||||
return Database::getInstance('db')->table('feeds')->eq('id', $feed_id)->remove();
|
$result = Database::getInstance('db')->table('feeds')->eq('id', $feed_id)->remove();
|
||||||
|
Favicon\purge_favicons();
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove all feeds
|
// Remove all feeds
|
||||||
function remove_all()
|
function remove_all()
|
||||||
{
|
{
|
||||||
delete_all_favicons();
|
$result = Database::getInstance('db')->table('feeds')->remove();
|
||||||
return Database::getInstance('db')->table('feeds')->remove();
|
Favicon\purge_favicons();
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable a feed (activate refresh)
|
// Enable a feed (activate refresh)
|
||||||
|
@ -5,7 +5,30 @@ namespace Schema;
|
|||||||
use PDO;
|
use PDO;
|
||||||
use Model\Config;
|
use Model\Config;
|
||||||
|
|
||||||
const VERSION = 42;
|
const VERSION = 43;
|
||||||
|
|
||||||
|
function version_43(PDO $pdo)
|
||||||
|
{
|
||||||
|
$pdo->exec('DROP TABLE favicons');
|
||||||
|
|
||||||
|
$pdo->exec(
|
||||||
|
'CREATE TABLE favicons (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
hash TEXT UNIQUE,
|
||||||
|
type TEXT
|
||||||
|
)'
|
||||||
|
);
|
||||||
|
|
||||||
|
$pdo->exec('
|
||||||
|
CREATE TABLE "favicons_feeds" (
|
||||||
|
feed_id INTEGER NOT NULL,
|
||||||
|
favicon_id INTEGER NOT NULL,
|
||||||
|
PRIMARY KEY(feed_id, favicon_id)
|
||||||
|
FOREIGN KEY(favicon_id) REFERENCES favicons(id) ON DELETE CASCADE
|
||||||
|
FOREIGN KEY(feed_id) REFERENCES feeds(id) ON DELETE CASCADE
|
||||||
|
)
|
||||||
|
');
|
||||||
|
}
|
||||||
|
|
||||||
function version_42(PDO $pdo)
|
function version_42(PDO $pdo)
|
||||||
{
|
{
|
||||||
|
1
vendor/composer/autoload_files.php
vendored
1
vendor/composer/autoload_files.php
vendored
@ -24,4 +24,5 @@ return array(
|
|||||||
$baseDir . '/models/database.php',
|
$baseDir . '/models/database.php',
|
||||||
$baseDir . '/models/remember_me.php',
|
$baseDir . '/models/remember_me.php',
|
||||||
$baseDir . '/models/group.php',
|
$baseDir . '/models/group.php',
|
||||||
|
$baseDir . '/models/favicon.php',
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user