/** * Refresh the specified feeds and returns an array with URLs in error * * @param $feeds should be an array of associative arrays ('id', 'url', 'post'} as values. post is a JSON array of post parameters to send with the URL. * @param $update_feeds_infos should be true to update the feed infos from values in the RSS / ATOM * @todo assert(false) */ function refresh_feeds($feeds, $check_favicons = false, $verbose = true) { global $dbh, $config; // TODO: Import tag from feeds // Download the feeds $download = curl_downloader($feeds, true, $verbose); $errors = array(); $favicons_to_check = array(); foreach ($download['status_codes'] as $url => $status_code) { // Keep the errors to return them and display them to the user if ($status_code != 200) { $errors[] = array('url' => $url, 'msg' => 'Feed page not found (http status: ' . $status_code . ')'); unset($download['results'][$url]); } elseif (!startswith($download['content_types'][$url], 'application/xml') && !startswith($download['content_types'][$url], 'text/xml') && !startswith($download['content_types'][$url], 'application/rss+xml') && !startswith($download['content_types'][$url], 'application/atom+xml')) { $errors[] = array('url' => $url, 'msg' => 'Unable to find a feed at the address ' . $url); unset($download['results'][$url]); } } $updated_feeds = $download['results']; // Put everything in a transaction to make it faster $dbh->beginTransaction(); // Delete old tags which were not user added delete_auto_added_tags(); // Query to update feeds table with latest infos in the RSS / ATOM $query_feeds = $dbh->prepare('UPDATE feeds SET title=(CASE WHEN has_user_title=1 THEN title ELSE :title END), links=:links, description=:description, ttl=(CASE WHEN has_user_ttl=1 THEN ttl ELSE :ttl END), image=:image WHERE url=:old_url'); $query_feeds->bindParam(':title', $feed_title); $query_feeds->bindParam(':links', $feed_links); $query_feeds->bindParam(':description', $feed_description); $query_feeds->bindParam(':ttl', $feed_ttl, PDO::PARAM_INT); $query_feeds->bindParam(':image', $image); $query_feeds->bindParam(':old_url', $url); // Two queries, to upsert (update OR insert) entries : update the existing entry and insert a new one if the update errorred $query_entries = $dbh->prepare('UPDATE entries SET authors=:authors, title=:title, links=:links, description=:description, content=:content, enclosures=:enclosures, comments=:comments, pubDate=:pubDate, lastUpdate=:lastUpdate WHERE guid=:guid'); $query_entries->bindParam(':authors', $authors); $query_entries->bindParam(':title', $title); $query_entries->bindParam(':links', $links); $query_entries->bindParam(':description', $description); $query_entries->bindParam(':content', $content); $query_entries->bindParam(':enclosures', $enclosures); $query_entries->bindParam(':comments', $comments); $query_entries->bindParam(':guid', $guid); $query_entries->bindParam(':pubDate', $pubDate, PDO::PARAM_INT); $query_entries->bindParam(':lastUpdate', $last_update, PDO::PARAM_INT); $query_entries_fail = $dbh->prepare('INSERT INTO entries(feed_id, authors, title, links, description, content, enclosures, comments, pubDate, lastUpdate, guid) VALUES(:feed_id, :authors, :title, :links, :description, :content, :enclosures, :comments, :pubDate, :lastUpdate, :guid)'); $query_entries_fail->bindParam(':feed_id', $feed_id); $query_entries_fail->bindParam(':authors', $authors); $query_entries_fail->bindParam(':title', $title); $query_entries_fail->bindParam(':links', $links); $query_entries_fail->bindParam(':description', $description); $query_entries_fail->bindParam(':content', $content); $query_entries_fail->bindParam(':enclosures', $enclosures); $query_entries_fail->bindParam(':comments', $comments); $query_entries_fail->bindParam(':guid', $guid); $query_entries_fail->bindParam(':pubDate', $pubDate, PDO::PARAM_INT); $query_entries_fail->bindParam(':lastUpdate', $last_update, PDO::PARAM_INT); // Query to insert tags if not already existing $query_insert_tag = $dbh->prepare('INSERT OR IGNORE INTO tags(name) VALUES(:name)'); $query_insert_tag->bindParam(':name', $tag_name); // Register the tags of the feed $query_feeds_tags = $dbh->prepare('INSERT OR IGNORE INTO tags_feeds(tag_id, feed_id, auto_added_tag) VALUES((SELECT id FROM tags WHERE name=:name), :feed_id, 1)'); $query_feeds_tags->bindParam(':name', $tag_name); $query_feeds_tags->bindParam(':feed_id', $feed_id); // Finally, query to register the tags of the entry $query_tags = $dbh->prepare('INSERT OR IGNORE INTO tags_entries(tag_id, entry_id, auto_added_tag) VALUES((SELECT id FROM tags WHERE name=:name), (SELECT id FROM entries WHERE guid=:entry_guid), 1)'); $query_tags->bindParam(':name', $tag_name); $query_tags->bindParam(':entry_guid', $guid); foreach ($updated_feeds as $url => $feed) { $array_feed_id = multiarray_search_key('url', $url, $feeds); if ($feed_id === -1) { assert(false); // TODO exit; } $feed_id = $feeds[$array_feed_id]['id']; // Parse feed $parsed = @feed2array($feed); // If an error has occurred, keep a trace of it if ($parsed === false || empty($parsed['infos']) || empty($parsed['items'])) { $errors[] = array('url' => $url, 'msg' => 'Unable to parse feed file'); continue; } // Define feed params $feed_title = isset($parsed['infos']['title']) ? $parsed['infos']['title'] : ''; $feed_links = isset($parsed['infos']['links']) ? json_encode(multiarray_filter('rel', 'self', $parsed['infos']['links'])) : ''; $feed_description = isset($parsed['infos']['description']) ? $parsed['infos']['description'] : ''; $feed_ttl = isset($parsed['infos']['ttl']) ? $parsed['infos']['ttl'] : 0; $feed_image = isset($parsed['infos']['image']) ? json_encode($parsed['infos']['image']) : ''; if ($check_favicons && empty($feed_image)) { $favicons_to_check[] = array('url' => $url); } $query_feeds->execute(); // Feeds tags if ($feeds[$array_feed_id]['import_tags_from_feed']) { if (!empty($parsed['infos']['categories'])) { foreach ($parsed['infos']['categories'] as $tag_name) { // Create tags if needed, get their id and add bind the articles to these tags $query_insert_tag->execute(); $query_feeds_tags->execute(); } } } // Insert / Update entries $items = $parsed['items']; foreach ($items as $event) { $authors = isset($event['authors']) ? json_encode($event['authors']) : ''; $title = isset($event['title']) ? $event['title'] : ''; $links = isset($event['links']) ? json_encode(multiarray_filter('rel', 'self', $event['links'])) : ''; $description = isset($event['description']) ? $event['description'] : ''; $content = isset($event['content']) ? $event['content'] : ''; $enclosures = isset($event['enclosures']) ? json_encode($event['enclosures']) : ''; if (isset($event['comments'])) { $comments = $event['comments']; } else { if (isset($event['links'])) { $tmp = multiarray_search('rel', 'replies', $event['links'], array('href' => '')); $comments = $tmp['href']; } else { $comments = ''; } } $guid = isset($event['guid']) ? $event['guid'] : ''; $pubDate = isset($event['pubDate']) ? $event['pubDate'] : ''; $last_update = isset($event['updated']) ? $event['updated'] : ''; $query_entries->execute(); if ($query_entries->rowCount() == 0) { $query_entries_fail->execute(); } if ($feeds[$array_feed_id]['import_tags_from_feed']) { if (!empty($event['categories'])) { foreach ($event['categories'] as $tag_name) { // Create tags if needed, get their id and add bind the articles to these tags $query_insert_tag->execute(); $query_tags->execute(); } } } if (!empty($event['enclosures'])) { foreach ($event['enclosures'] as $enclosure) { $tag_name = '_type_' . get_category_mime_type($enclosure['type']); if ($tag_name !== false) { $query_tags->execute(); } } } } } // Check favicons if ($check_favicons && !empty($favicons_to_check)) { $favicons = get_favicon($favicons_to_check); $favicons = $favicons['favicons']; $query_favicon = $dbh->prepare('UPDATE feeds SET image=:image WHERE url=:url'); $query_favicon->bindParam(':url', $url); $query_favicon->bindParam(':image', $image); foreach ($favicons as $url => $favicon) { if (!empty($favicon[0]['favicon_url'])) { $image = $favicon[0]['favicon_url']; $query_favicon->execute(); } } } $dbh->commit(); delete_old_entries(); return $errors; }
/** * Parse an OPML file. * * @return An array of associative array for each feed with URL, title and associated tags. * @copyright Heavily based on a function from FreshRSS. */ function opml_import($xml) { $opml = simplexml_load_string($xml); if (!$opml) { return false; } $categories = array(); $feeds = array(); foreach ($opml->body->outline as $outline) { if (!isset($outline['xmlUrl'])) { // Folder $tag = ''; if (isset($outline['text'])) { $tag = (string) $outline['text']; } elseif (isset($outline['title'])) { $tag = (string) $outline['title']; } if ($tag) { foreach ($outline->outline as $feed) { if (!isset($feed['xmlUrl'])) { continue; } $search = multiarray_search_key('url', (string) $feed['xmlUrl'], $feeds); if ($search === -1) { // Feed was not yet encountered, so add it first if (isset($feed['title'])) { $feed_title = (string) $feed['title']; } elseif (isset($feed['text'])) { $feed_title = (string) $feed['text']; } else { $feed_title = ''; } $feeds[] = array('url' => (string) $feed['xmlUrl'], 'title' => $feed_title, 'tags' => array(), 'post' => ''); $search = count($feeds) - 1; } $feeds[$search]['tags'][] = $tag; } } } else { // This is a RSS feed without any folder if (isset($outline['title'])) { $title = (string) $outline['title']; } elseif (isset($outline['text'])) { $title = (string) $outline['text']; } else { $title = ''; } if (multiarray_search_key('url', (string) $outline['xmlUrl'], $feeds) !== -1) { $feeds[] = array('url' => (string) $outline['xmlUrl'], 'title' => $title, 'tags' => array(), 'post' => ''); } } } return $feeds; }