diff --git a/check_setup.php b/check_setup.php index 1b3db7b..4fb89e1 100644 --- a/check_setup.php +++ b/check_setup.php @@ -62,6 +62,11 @@ if (! is_writable(DATA_DIRECTORY)) { die('The data directory must be writeable by your web server user'); } +// Check if favicon directory is writeable +if (! is_writable(FAVICON_DIRECTORY)) { + die('The favicon directory must be writeable by your web server user'); +} + // Include password_compat for PHP < 5.5 if (version_compare(PHP_VERSION, '5.5.0', '<')) { require __DIR__.'/lib/password.php'; diff --git a/common.php b/common.php index 632a69c..6adca5c 100644 --- a/common.php +++ b/common.php @@ -15,6 +15,9 @@ defined('BASE_URL_DIRECTORY') or define('BASE_URL_DIRECTORY', dirname($_SERVER[' defined('ROOT_DIRECTORY') or define('ROOT_DIRECTORY', __DIR__); defined('DATA_DIRECTORY') or define('DATA_DIRECTORY', ROOT_DIRECTORY.DIRECTORY_SEPARATOR.'data'); +defined('FAVICON_DIRECTORY') or define('FAVICON_DIRECTORY', DATA_DIRECTORY.DIRECTORY_SEPARATOR.'favicons'); +defined('FAVICON_PUBLIC_DIRECTORY') or define('FAVICON_PUBLIC_DIRECTORY', 'data'.DIRECTORY_SEPARATOR.'favicons'); + defined('ENABLE_MULTIPLE_DB') or define('ENABLE_MULTIPLE_DB', true); defined('DB_FILENAME') or define('DB_FILENAME', 'db.sqlite'); diff --git a/config.default.php b/config.default.php index af2183e..fb5cf8b 100644 --- a/config.default.php +++ b/config.default.php @@ -9,6 +9,12 @@ define('HTTP_MAX_RESPONSE_SIZE', 2097152); // DATA_DIRECTORY => default is data (writable directory) define('DATA_DIRECTORY', __DIR__.'/data'); +// FAVICON_DIRECTORY => default is favicons (writable directory) +define('FAVICON_DIRECTORY', DATA_DIRECTORY.DIRECTORY_SEPARATOR.'favicons'); + +// FAVICON_PUBLIC_DIRECTORY => default is data/favicons/ +define('FAVICON_PUBLIC_DIRECTORY', 'data'.DIRECTORY_SEPARATOR.'favicons'); + // DB_FILENAME => default value is db.sqlite (default database filename) define('DB_FILENAME', 'db.sqlite'); diff --git a/data/favicons/.htaccess b/data/favicons/.htaccess new file mode 100644 index 0000000..605d2f4 --- /dev/null +++ b/data/favicons/.htaccess @@ -0,0 +1 @@ +Allow from all diff --git a/fever/index.php b/fever/index.php index 3453a33..c68ca08 100644 --- a/fever/index.php +++ b/fever/index.php @@ -122,7 +122,8 @@ route('favicons', function() { ->table('favicons') ->columns( 'feed_id', - 'icon' + 'file', + 'type' ) ->findAll(); @@ -130,7 +131,7 @@ route('favicons', function() { foreach ($favicons as $favicon) { $response['favicons'][] = array( 'id' => (int) $favicon['feed_id'], - 'data' => $favicon['icon'] + 'data' => 'data:'.$favicon['type'].';base64,'.base64_encode(file_get_contents(FAVICON_DIRECTORY.DIRECTORY_SEPARATOR.$favicon['file'])) ); } } diff --git a/lib/helpers.php b/lib/helpers.php index 1b0e864..b6c3786 100644 --- a/lib/helpers.php +++ b/lib/helpers.php @@ -29,10 +29,30 @@ function parse_app_version($refnames, $commithash) return $version; } +/* + * get Image extension from mime type + */ +function favicon_extension($type) +{ + $types = array( + 'image/png' => '.png', + 'image/gif' => '.gif', + 'image/x-icon' => '.ico', + 'image/jpeg' => '.jpg', + 'image/jpg' => '.jpg' + ); + + if (in_array($type, $types)) { + return $types[$type]; + } else { + return '.ico'; + } +} + function favicon(array $favicons, $feed_id) { if (! empty($favicons[$feed_id])) { - return ''; + return ''; } return ''; diff --git a/models/feed.php b/models/feed.php index bdf5a4e..0e2dc6e 100644 --- a/models/feed.php +++ b/models/feed.php @@ -6,6 +6,7 @@ use UnexpectedValueException; use Model\Config; use Model\Item; use Model\Group; +use Helper; use SimpleValidator\Validator; use SimpleValidator\Validators; use PicoDb\Database; @@ -19,17 +20,40 @@ use PicoFeed\Client\InvalidUrlException; const LIMIT_ALL = -1; // Store the favicon -function store_favicon($feed_id, $link, $icon) +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, - 'icon' => $icon, + '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) { @@ -37,10 +61,11 @@ function fetch_favicon($feed_id, $site_url, $icon_link) $favicon = new Favicon; $link = $favicon->find($site_url, $icon_link); - $icon = $favicon->getDataUri(); + $icon = $favicon->getContent(); + $type = $favicon->getType(); if ($icon !== '') { - store_favicon($feed_id, $link, $icon); + store_favicon($feed_id, $link, $type, $icon); } } } @@ -61,7 +86,7 @@ function get_favicons(array $feed_ids) $db = Database::getInstance('db') ->hashtable('favicons') ->columnKey('feed_id') - ->columnValue('icon'); + ->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); @@ -88,7 +113,7 @@ function get_all_favicons() return Database::getInstance('db') ->hashtable('favicons') - ->getAll('feed_id', 'icon'); + ->getAll('feed_id', 'file'); } // Update feed information @@ -409,6 +434,7 @@ function update_cache($feed_id, $last_modified, $etag) // Remove one feed function remove($feed_id) { + delete_favicon($feed_id); // Items are removed by a sql constraint return Database::getInstance('db')->table('feeds')->eq('id', $feed_id)->remove(); } @@ -416,6 +442,7 @@ function remove($feed_id) // Remove all feeds function remove_all() { + delete_all_favicons(); return Database::getInstance('db')->table('feeds')->remove(); } diff --git a/models/schema.php b/models/schema.php index 605649c..42019c0 100644 --- a/models/schema.php +++ b/models/schema.php @@ -5,7 +5,22 @@ namespace Schema; use PDO; use Model\Config; -const VERSION = 41; +const VERSION = 42; + +function version_42(PDO $pdo) +{ + $pdo->exec('DROP TABLE favicons'); + + $pdo->exec( + 'CREATE TABLE favicons ( + feed_id INTEGER UNIQUE, + link TEXT, + file TEXT, + type TEXT, + FOREIGN KEY(feed_id) REFERENCES feeds(id) ON DELETE CASCADE + )' + ); +} function version_41(PDO $pdo) {