diff options
Diffstat (limited to 'inc/3rdparty/makefulltextfeed.php')
-rwxr-xr-x | inc/3rdparty/makefulltextfeed.php | 714 |
1 files changed, 214 insertions, 500 deletions
diff --git a/inc/3rdparty/makefulltextfeed.php b/inc/3rdparty/makefulltextfeed.php index 2852c4c2..a081f88b 100755 --- a/inc/3rdparty/makefulltextfeed.php +++ b/inc/3rdparty/makefulltextfeed.php | |||
@@ -3,8 +3,8 @@ | |||
3 | // Author: Keyvan Minoukadeh | 3 | // Author: Keyvan Minoukadeh |
4 | // Copyright (c) 2013 Keyvan Minoukadeh | 4 | // Copyright (c) 2013 Keyvan Minoukadeh |
5 | // License: AGPLv3 | 5 | // License: AGPLv3 |
6 | // Version: 3.1 | 6 | // Version: 3.2 |
7 | // Date: 2013-03-05 | 7 | // Date: 2013-05-13 |
8 | // More info: http://fivefilters.org/content-only/ | 8 | // More info: http://fivefilters.org/content-only/ |
9 | // Help: http://help.fivefilters.org | 9 | // Help: http://help.fivefilters.org |
10 | 10 | ||
@@ -25,14 +25,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
25 | 25 | ||
26 | // Usage | 26 | // Usage |
27 | // ----- | 27 | // ----- |
28 | // Request this file passing it your feed in the querystring: makefulltextfeed.php?url=mysite.org | 28 | // Request this file passing it a web page or feed URL in the querystring: makefulltextfeed.php?url=example.org/article |
29 | // The following options can be passed in the querystring: | 29 | // For more request parameters, see http://help.fivefilters.org/customer/portal/articles/226660-usage |
30 | // * URL: url=[feed or website url] (required, should be URL-encoded - in php: urlencode($url)) | ||
31 | // * URL points to HTML (not feed): html=true (optional, by default it's automatically detected) | ||
32 | // * API key: key=[api key] (optional, refer to config.php) | ||
33 | // * Max entries to process: max=[max number of items] (optional) | ||
34 | 30 | ||
35 | error_reporting(E_ALL ^ E_NOTICE); | 31 | //error_reporting(E_ALL ^ E_NOTICE); |
36 | ini_set("display_errors", 1); | 32 | ini_set("display_errors", 1); |
37 | @set_time_limit(120); | 33 | @set_time_limit(120); |
38 | 34 | ||
@@ -55,42 +51,8 @@ if (get_magic_quotes_gpc()) { | |||
55 | 51 | ||
56 | // set include path | 52 | // set include path |
57 | set_include_path(realpath(dirname(__FILE__).'/libraries').PATH_SEPARATOR.get_include_path()); | 53 | set_include_path(realpath(dirname(__FILE__).'/libraries').PATH_SEPARATOR.get_include_path()); |
58 | // Autoloading of classes allows us to include files only when they're | 54 | |
59 | // needed. If we've got a cached copy, for example, only Zend_Cache is loaded. | 55 | require_once dirname(__FILE__).'/makefulltextfeedHelpers.php'; |
60 | function autoload($class_name) { | ||
61 | static $dir = null; | ||
62 | if ($dir === null) $dir = dirname(__FILE__).'/libraries/'; | ||
63 | static $mapping = array( | ||
64 | // Include FeedCreator for RSS/Atom creation | ||
65 | 'FeedWriter' => 'feedwriter/FeedWriter.php', | ||
66 | 'FeedItem' => 'feedwriter/FeedItem.php', | ||
67 | // Include ContentExtractor and Readability for identifying and extracting content from URLs | ||
68 | 'ContentExtractor' => 'content-extractor/ContentExtractor.php', | ||
69 | 'SiteConfig' => 'content-extractor/SiteConfig.php', | ||
70 | 'Readability' => 'readability/Readability.php', | ||
71 | // Include Humble HTTP Agent to allow parallel requests and response caching | ||
72 | 'HumbleHttpAgent' => 'humble-http-agent/HumbleHttpAgent.php', | ||
73 | 'SimplePie_HumbleHttpAgent' => 'humble-http-agent/SimplePie_HumbleHttpAgent.php', | ||
74 | 'CookieJar' => 'humble-http-agent/CookieJar.php', | ||
75 | // Include Zend Cache to improve performance (cache results) | ||
76 | 'Zend_Cache' => 'Zend/Cache.php', | ||
77 | // Language detect | ||
78 | 'Text_LanguageDetect' => 'language-detect/LanguageDetect.php', | ||
79 | // HTML5 Lib | ||
80 | 'HTML5_Parser' => 'html5/Parser.php', | ||
81 | // htmLawed - used if XSS filter is enabled (xss_filter) | ||
82 | 'htmLawed' => 'htmLawed/htmLawed.php' | ||
83 | ); | ||
84 | if (isset($mapping[$class_name])) { | ||
85 | debug("** Loading class $class_name ({$mapping[$class_name]})"); | ||
86 | require $dir.$mapping[$class_name]; | ||
87 | return true; | ||
88 | } else { | ||
89 | return false; | ||
90 | } | ||
91 | } | ||
92 | spl_autoload_register('autoload'); | ||
93 | require dirname(__FILE__).'/libraries/simplepie/autoloader.php'; | ||
94 | 56 | ||
95 | //////////////////////////////// | 57 | //////////////////////////////// |
96 | // Load config file | 58 | // Load config file |
@@ -110,8 +72,8 @@ header('X-Robots-Tag: noindex, nofollow'); | |||
110 | //////////////////////////////// | 72 | //////////////////////////////// |
111 | // Check if service is enabled | 73 | // Check if service is enabled |
112 | //////////////////////////////// | 74 | //////////////////////////////// |
113 | if (!$options->enabled) { | 75 | if (!$options->enabled) { |
114 | die('The full-text RSS service is currently disabled'); | 76 | die('The full-text RSS service is currently disabled'); |
115 | } | 77 | } |
116 | 78 | ||
117 | //////////////////////////////// | 79 | //////////////////////////////// |
@@ -155,8 +117,8 @@ $options->smart_cache = $options->smart_cache && function_exists('apc_inc'); | |||
155 | //////////////////////////////// | 117 | //////////////////////////////// |
156 | // Check for feed URL | 118 | // Check for feed URL |
157 | //////////////////////////////// | 119 | //////////////////////////////// |
158 | if (!isset($_GET['url'])) { | 120 | if (!isset($_GET['url'])) { |
159 | die('No URL supplied'); | 121 | die('No URL supplied'); |
160 | } | 122 | } |
161 | $url = trim($_GET['url']); | 123 | $url = trim($_GET['url']); |
162 | if (strtolower(substr($url, 0, 7)) == 'feed://') { | 124 | if (strtolower(substr($url, 0, 7)) == 'feed://') { |
@@ -195,10 +157,12 @@ if (isset($_GET['key']) && ($key_index = array_search($_GET['key'], $options->ap | |||
195 | if (isset($_GET['links'])) $redirect .= '&links='.urlencode($_GET['links']); | 157 | if (isset($_GET['links'])) $redirect .= '&links='.urlencode($_GET['links']); |
196 | if (isset($_GET['exc'])) $redirect .= '&exc='.urlencode($_GET['exc']); | 158 | if (isset($_GET['exc'])) $redirect .= '&exc='.urlencode($_GET['exc']); |
197 | if (isset($_GET['format'])) $redirect .= '&format='.urlencode($_GET['format']); | 159 | if (isset($_GET['format'])) $redirect .= '&format='.urlencode($_GET['format']); |
198 | if (isset($_GET['callback'])) $redirect .= '&callback='.urlencode($_GET['callback']); | 160 | if (isset($_GET['callback'])) $redirect .= '&callback='.urlencode($_GET['callback']); |
199 | if (isset($_GET['l'])) $redirect .= '&l='.urlencode($_GET['l']); | 161 | if (isset($_GET['l'])) $redirect .= '&l='.urlencode($_GET['l']); |
200 | if (isset($_GET['xss'])) $redirect .= '&xss'; | 162 | if (isset($_GET['xss'])) $redirect .= '&xss'; |
201 | if (isset($_GET['use_extracted_title'])) $redirect .= '&use_extracted_title'; | 163 | if (isset($_GET['use_extracted_title'])) $redirect .= '&use_extracted_title'; |
164 | if (isset($_GET['content'])) $redirect .= '&content='.urlencode($_GET['content']); | ||
165 | if (isset($_GET['summary'])) $redirect .= '&summary='.urlencode($_GET['summary']); | ||
202 | if (isset($_GET['debug'])) $redirect .= '&debug'; | 166 | if (isset($_GET['debug'])) $redirect .= '&debug'; |
203 | if ($debug_mode) { | 167 | if ($debug_mode) { |
204 | debug('Redirecting to hide access key, follow URL below to continue'); | 168 | debug('Redirecting to hide access key, follow URL below to continue'); |
@@ -211,7 +175,7 @@ if (isset($_GET['key']) && ($key_index = array_search($_GET['key'], $options->ap | |||
211 | 175 | ||
212 | /////////////////////////////////////////////// | 176 | /////////////////////////////////////////////// |
213 | // Set timezone. | 177 | // Set timezone. |
214 | // Prevents warnings, but needs more testing - | 178 | // Prevents warnings, but needs more testing - |
215 | // perhaps if timezone is set in php.ini we | 179 | // perhaps if timezone is set in php.ini we |
216 | // don't need to set it at all... | 180 | // don't need to set it at all... |
217 | /////////////////////////////////////////////// | 181 | /////////////////////////////////////////////// |
@@ -233,7 +197,7 @@ if (isset($_GET['key']) && isset($_GET['hash']) && isset($options->api_keys[(int | |||
233 | } | 197 | } |
234 | $key_index = ($valid_key) ? (int)$_GET['key'] : 0; | 198 | $key_index = ($valid_key) ? (int)$_GET['key'] : 0; |
235 | if (!$valid_key && $options->key_required) { | 199 | if (!$valid_key && $options->key_required) { |
236 | die('A valid key must be supplied'); | 200 | die('A valid key must be supplied'); |
237 | } | 201 | } |
238 | if (!$valid_key && isset($_GET['key']) && $_GET['key'] != '') { | 202 | if (!$valid_key && isset($_GET['key']) && $_GET['key'] != '') { |
239 | die('The entered key is invalid'); | 203 | die('The entered key is invalid'); |
@@ -285,6 +249,28 @@ if ($options->favour_feed_titles == 'user') { | |||
285 | } | 249 | } |
286 | 250 | ||
287 | /////////////////////////////////////////////// | 251 | /////////////////////////////////////////////// |
252 | // Include full content in output? | ||
253 | /////////////////////////////////////////////// | ||
254 | if ($options->content === 'user') { | ||
255 | if (isset($_GET['content']) && $_GET['content'] === '0') { | ||
256 | $options->content = false; | ||
257 | } else { | ||
258 | $options->content = true; | ||
259 | } | ||
260 | } | ||
261 | |||
262 | /////////////////////////////////////////////// | ||
263 | // Include summaries in output? | ||
264 | /////////////////////////////////////////////// | ||
265 | if ($options->summary === 'user') { | ||
266 | if (isset($_GET['summary']) && $_GET['summary'] === '1') { | ||
267 | $options->summary = true; | ||
268 | } else { | ||
269 | $options->summary = false; | ||
270 | } | ||
271 | } | ||
272 | |||
273 | /////////////////////////////////////////////// | ||
288 | // Exclude items if extraction fails | 274 | // Exclude items if extraction fails |
289 | /////////////////////////////////////////////// | 275 | /////////////////////////////////////////////// |
290 | if ($options->exclude_items_on_fail === 'user') { | 276 | if ($options->exclude_items_on_fail === 'user') { |
@@ -306,15 +292,6 @@ if ($options->detect_language === 'user') { | |||
306 | $detect_language = $options->detect_language; | 292 | $detect_language = $options->detect_language; |
307 | } | 293 | } |
308 | 294 | ||
309 | if ($detect_language >= 2) { | ||
310 | $language_codes = array('albanian' => 'sq','arabic' => 'ar','azeri' => 'az','bengali' => 'bn','bulgarian' => 'bg', | ||
311 | 'cebuano' => 'ceb', // ISO 639-2 | ||
312 | 'croatian' => 'hr','czech' => 'cs','danish' => 'da','dutch' => 'nl','english' => 'en','estonian' => 'et','farsi' => 'fa','finnish' => 'fi','french' => 'fr','german' => 'de','hausa' => 'ha', | ||
313 | 'hawaiian' => 'haw', // ISO 639-2 | ||
314 | 'hindi' => 'hi','hungarian' => 'hu','icelandic' => 'is','indonesian' => 'id','italian' => 'it','kazakh' => 'kk','kyrgyz' => 'ky','latin' => 'la','latvian' => 'lv','lithuanian' => 'lt','macedonian' => 'mk','mongolian' => 'mn','nepali' => 'ne','norwegian' => 'no','pashto' => 'ps', | ||
315 | 'pidgin' => 'cpe', // ISO 639-2 | ||
316 | 'polish' => 'pl','portuguese' => 'pt','romanian' => 'ro','russian' => 'ru','serbian' => 'sr','slovak' => 'sk','slovene' => 'sl','somali' => 'so','spanish' => 'es','swahili' => 'sw','swedish' => 'sv','tagalog' => 'tl','turkish' => 'tr','ukrainian' => 'uk','urdu' => 'ur','uzbek' => 'uz','vietnamese' => 'vi','welsh' => 'cy'); | ||
317 | } | ||
318 | $use_cld = extension_loaded('cld') && (version_compare(PHP_VERSION, '5.3.0') >= 0); | 295 | $use_cld = extension_loaded('cld') && (version_compare(PHP_VERSION, '5.3.0') >= 0); |
319 | 296 | ||
320 | ///////////////////////////////////// | 297 | ///////////////////////////////////// |
@@ -364,7 +341,7 @@ if ($options->cors) header('Access-Control-Allow-Origin: *'); | |||
364 | ////////////////////////////////// | 341 | ////////////////////////////////// |
365 | if ($options->caching) { | 342 | if ($options->caching) { |
366 | debug('Caching is enabled...'); | 343 | debug('Caching is enabled...'); |
367 | $cache_id = md5($max.$url.$valid_key.$links.$favour_feed_titles.$xss_filter.$exclude_on_fail.$format.$detect_language.(int)isset($_GET['pubsub'])); | 344 | $cache_id = md5($max.$url.(int)$valid_key.$links.(int)$favour_feed_titles.(int)$options->content.(int)$options->summary.(int)$xss_filter.(int)$exclude_on_fail.$format.$detect_language.(int)isset($_GET['pubsub'])); |
368 | $check_cache = true; | 345 | $check_cache = true; |
369 | if ($options->apc && $options->smart_cache) { | 346 | if ($options->apc && $options->smart_cache) { |
370 | apc_add("cache.$cache_id", 0, 10*60); | 347 | apc_add("cache.$cache_id", 0, 10*60); |
@@ -415,6 +392,7 @@ if (!$debug_mode) { | |||
415 | ////////////////////////////////// | 392 | ////////////////////////////////// |
416 | // Set up HTTP agent | 393 | // Set up HTTP agent |
417 | ////////////////////////////////// | 394 | ////////////////////////////////// |
395 | global $http; | ||
418 | $http = new HumbleHttpAgent(); | 396 | $http = new HumbleHttpAgent(); |
419 | $http->debug = $debug_mode; | 397 | $http->debug = $debug_mode; |
420 | $http->userAgentMap = $options->user_agents; | 398 | $http->userAgentMap = $options->user_agents; |
@@ -478,29 +456,6 @@ if ($html_only || !$result) { | |||
478 | $isDummyFeed = true; | 456 | $isDummyFeed = true; |
479 | unset($feed, $result); | 457 | unset($feed, $result); |
480 | // create single item dummy feed object | 458 | // create single item dummy feed object |
481 | class DummySingleItemFeed { | ||
482 | public $item; | ||
483 | function __construct($url) { $this->item = new DummySingleItem($url); } | ||
484 | public function get_title() { return ''; } | ||
485 | public function get_description() { return 'Content extracted from '.$this->item->url; } | ||
486 | public function get_link() { return $this->item->url; } | ||
487 | public function get_language() { return false; } | ||
488 | public function get_image_url() { return false; } | ||
489 | public function get_items($start=0, $max=1) { return array(0=>$this->item); } | ||
490 | } | ||
491 | class DummySingleItem { | ||
492 | public $url; | ||
493 | function __construct($url) { $this->url = $url; } | ||
494 | public function get_permalink() { return $this->url; } | ||
495 | public function get_title() { return null; } | ||
496 | public function get_date($format='') { return false; } | ||
497 | public function get_author($key=0) { return null; } | ||
498 | public function get_authors() { return null; } | ||
499 | public function get_description() { return ''; } | ||
500 | public function get_enclosure($key=0, $prefer=null) { return null; } | ||
501 | public function get_enclosures() { return null; } | ||
502 | public function get_categories() { return null; } | ||
503 | } | ||
504 | $feed = new DummySingleItemFeed($url); | 459 | $feed = new DummySingleItemFeed($url); |
505 | } | 460 | } |
506 | 461 | ||
@@ -524,7 +479,7 @@ if ($img_url = $feed->get_image_url()) { | |||
524 | //////////////////////////////////////////// | 479 | //////////////////////////////////////////// |
525 | // Loop through feed items | 480 | // Loop through feed items |
526 | //////////////////////////////////////////// | 481 | //////////////////////////////////////////// |
527 | $items = $feed->get_items(0, $max); | 482 | $items = $feed->get_items(0, $max); |
528 | // Request all feed items in parallel (if supported) | 483 | // Request all feed items in parallel (if supported) |
529 | $urls_sanitized = array(); | 484 | $urls_sanitized = array(); |
530 | $urls = array(); | 485 | $urls = array(); |
@@ -606,24 +561,43 @@ foreach ($items as $key => $item) { | |||
606 | $is_single_page = false; | 561 | $is_single_page = false; |
607 | if ($single_page_response = getSinglePage($item, $html, $effective_url)) { | 562 | if ($single_page_response = getSinglePage($item, $html, $effective_url)) { |
608 | $is_single_page = true; | 563 | $is_single_page = true; |
609 | $html = $single_page_response['body']; | ||
610 | // remove strange things | ||
611 | $html = str_replace('</[>', '', $html); | ||
612 | $html = convert_to_utf8($html, $single_page_response['headers']); | ||
613 | $effective_url = $single_page_response['effective_url']; | 564 | $effective_url = $single_page_response['effective_url']; |
614 | debug("Retrieved single-page view from $effective_url"); | 565 | // check if action defined for returned Content-Type |
566 | $mime_info = get_mime_action_info($single_page_response['headers']); | ||
567 | if (isset($mime_info['action'])) { | ||
568 | if ($mime_info['action'] == 'exclude') { | ||
569 | continue; // skip this feed item entry | ||
570 | } elseif ($mime_info['action'] == 'link') { | ||
571 | if ($mime_info['type'] == 'image') { | ||
572 | $html = "<a href=\"$effective_url\"><img src=\"$effective_url\" alt=\"{$mime_info['name']}\" /></a>"; | ||
573 | } else { | ||
574 | $html = "<a href=\"$effective_url\">Download {$mime_info['name']}</a>"; | ||
575 | } | ||
576 | $extracted_title = $mime_info['name']; | ||
577 | $do_content_extraction = false; | ||
578 | } | ||
579 | } | ||
580 | if ($do_content_extraction) { | ||
581 | $html = $single_page_response['body']; | ||
582 | // remove strange things | ||
583 | $html = str_replace('</[>', '', $html); | ||
584 | $html = convert_to_utf8($html, $single_page_response['headers']); | ||
585 | debug("Retrieved single-page view from $effective_url"); | ||
586 | } | ||
615 | unset($single_page_response); | 587 | unset($single_page_response); |
616 | } | 588 | } |
589 | } | ||
590 | if ($do_content_extraction) { | ||
617 | debug('--------'); | 591 | debug('--------'); |
618 | debug('Attempting to extract content'); | 592 | debug('Attempting to extract content'); |
619 | $extract_result = $extractor->process($html, $effective_url); | 593 | $extract_result = $extractor->process($html, $effective_url); |
620 | $readability = $extractor->readability; | 594 | $readability = $extractor->readability; |
621 | $content_block = ($extract_result) ? $extractor->getContent() : null; | 595 | $content_block = ($extract_result) ? $extractor->getContent() : null; |
622 | $extracted_title = ($extract_result) ? $extractor->getTitle() : ''; | 596 | $extracted_title = ($extract_result) ? $extractor->getTitle() : ''; |
623 | // Deal with multi-page articles | 597 | // Deal with multi-page articles |
624 | //die('Next: '.$extractor->getNextPageUrl()); | 598 | //die('Next: '.$extractor->getNextPageUrl()); |
625 | $is_multi_page = (!$is_single_page && $extract_result && $extractor->getNextPageUrl()); | 599 | $is_multi_page = (!$is_single_page && $extract_result && $extractor->getNextPageUrl()); |
626 | if ($options->multipage && $is_multi_page) { | 600 | if ($options->multipage && $is_multi_page && $options->content) { |
627 | debug('--------'); | 601 | debug('--------'); |
628 | debug('Attempting to process multi-page article'); | 602 | debug('Attempting to process multi-page article'); |
629 | $multi_page_urls = array(); | 603 | $multi_page_urls = array(); |
@@ -636,7 +610,7 @@ foreach ($items as $key => $item) { | |||
636 | // check it's not what we have already! | 610 | // check it's not what we have already! |
637 | if (!in_array($next_page_url, $multi_page_urls)) { | 611 | if (!in_array($next_page_url, $multi_page_urls)) { |
638 | // it's not, so let's attempt to fetch it | 612 | // it's not, so let's attempt to fetch it |
639 | $multi_page_urls[] = $next_page_url; | 613 | $multi_page_urls[] = $next_page_url; |
640 | $_prev_ref = $http->referer; | 614 | $_prev_ref = $http->referer; |
641 | if (($response = $http->get($next_page_url, true)) && $response['status_code'] < 300) { | 615 | if (($response = $http->get($next_page_url, true)) && $response['status_code'] < 300) { |
642 | // make sure mime type is not something with a different action associated | 616 | // make sure mime type is not something with a different action associated |
@@ -661,13 +635,15 @@ foreach ($items as $key => $item) { | |||
661 | // did we successfully deal with this multi-page article? | 635 | // did we successfully deal with this multi-page article? |
662 | if (empty($multi_page_content)) { | 636 | if (empty($multi_page_content)) { |
663 | debug('Failed to extract all parts of multi-page article, so not going to include them'); | 637 | debug('Failed to extract all parts of multi-page article, so not going to include them'); |
664 | $multi_page_content[] = $readability->dom->createElement('p')->innerHTML = '<em>This article appears to continue on subsequent pages which we could not extract</em>'; | 638 | $_page = $readability->dom->createElement('p'); |
639 | $_page->innerHTML = '<em>This article appears to continue on subsequent pages which we could not extract</em>'; | ||
640 | $multi_page_content[] = $_page; | ||
665 | } | 641 | } |
666 | foreach ($multi_page_content as $_page) { | 642 | foreach ($multi_page_content as $_page) { |
667 | $_page = $content_block->ownerDocument->importNode($_page, true); | 643 | $_page = $content_block->ownerDocument->importNode($_page, true); |
668 | $content_block->appendChild($_page); | 644 | $content_block->appendChild($_page); |
669 | } | 645 | } |
670 | unset($multi_page_urls, $multi_page_content, $page_mime_info, $next_page_url); | 646 | unset($multi_page_urls, $multi_page_content, $page_mime_info, $next_page_url, $_page); |
671 | } | 647 | } |
672 | } | 648 | } |
673 | // use extracted title for both feed and item title if we're using single-item dummy feed | 649 | // use extracted title for both feed and item title if we're using single-item dummy feed |
@@ -695,7 +671,11 @@ foreach ($items as $key => $item) { | |||
695 | $html .= $item->get_description(); | 671 | $html .= $item->get_description(); |
696 | } else { | 672 | } else { |
697 | $readability->clean($content_block, 'select'); | 673 | $readability->clean($content_block, 'select'); |
698 | if ($options->rewrite_relative_urls) makeAbsolute($effective_url, $content_block); | 674 | // get base URL |
675 | $base_url = get_base_url($readability->dom); | ||
676 | if (!$base_url) $base_url = $effective_url; | ||
677 | // rewrite URLs | ||
678 | if ($options->rewrite_relative_urls) makeAbsolute($base_url, $content_block); | ||
699 | // footnotes | 679 | // footnotes |
700 | if (($links == 'footnotes') && (strpos($effective_url, 'wikipedia.org') === false)) { | 680 | if (($links == 'footnotes') && (strpos($effective_url, 'wikipedia.org') === false)) { |
701 | $readability->addFootnotes($content_block); | 681 | $readability->addFootnotes($content_block); |
@@ -714,7 +694,7 @@ foreach ($items as $key => $item) { | |||
714 | } else { | 694 | } else { |
715 | $html = $content_block->ownerDocument->saveXML($content_block); // essentially outerHTML | 695 | $html = $content_block->ownerDocument->saveXML($content_block); // essentially outerHTML |
716 | } | 696 | } |
717 | unset($content_block); | 697 | //unset($content_block); |
718 | // post-processing cleanup | 698 | // post-processing cleanup |
719 | $html = preg_replace('!<p>[\s\h\v]*</p>!u', '', $html); | 699 | $html = preg_replace('!<p>[\s\h\v]*</p>!u', '', $html); |
720 | if ($links == 'remove') { | 700 | if ($links == 'remove') { |
@@ -727,130 +707,155 @@ foreach ($items as $key => $item) { | |||
727 | } | 707 | } |
728 | } | 708 | } |
729 | 709 | ||
730 | if ($valid_key && isset($_GET['pubsub'])) { // used only on fivefilters.org at the moment | 710 | if ($valid_key && isset($_GET['pubsub'])) { // used only on fivefilters.org at the moment |
731 | $newitem->addElement('guid', 'http://fivefilters.org/content-only/redirect.php?url='.urlencode($item->get_permalink()), array('isPermaLink'=>'false')); | 711 | $newitem->addElement('guid', 'http://fivefilters.org/content-only/redirect.php?url='.urlencode($item->get_permalink()), array('isPermaLink'=>'false')); |
712 | } else { | ||
713 | $newitem->addElement('guid', $item->get_permalink(), array('isPermaLink'=>'true')); | ||
714 | } | ||
715 | // filter xss? | ||
716 | if ($xss_filter) { | ||
717 | debug('Filtering HTML to remove XSS'); | ||
718 | $html = htmLawed::hl($html, array('safe'=>1, 'deny_attribute'=>'style', 'comment'=>1, 'cdata'=>1)); | ||
719 | } | ||
720 | |||
721 | // add content | ||
722 | if ($options->summary === true) { | ||
723 | // get summary | ||
724 | $summary = ''; | ||
725 | if (!$do_content_extraction) { | ||
726 | $summary = $html; | ||
732 | } else { | 727 | } else { |
733 | $newitem->addElement('guid', $item->get_permalink(), array('isPermaLink'=>'true')); | 728 | // Try to get first few paragraphs |
734 | } | 729 | if (isset($content_block) && ($content_block instanceof DOMElement)) { |
735 | // filter xss? | 730 | $_paras = $content_block->getElementsByTagName('p'); |
736 | if ($xss_filter) { | 731 | foreach ($_paras as $_para) { |
737 | debug('Filtering HTML to remove XSS'); | 732 | $summary .= preg_replace("/[\n\r\t ]+/", ' ', $_para->textContent).' '; |
738 | $html = htmLawed::hl($html, array('safe'=>1, 'deny_attribute'=>'style', 'comment'=>1, 'cdata'=>1)); | 733 | if (strlen($summary) > 200) break; |
739 | } | ||
740 | $newitem->setDescription($html); | ||
741 | |||
742 | // set date | ||
743 | if ((int)$item->get_date('U') > 0) { | ||
744 | $newitem->setDate((int)$item->get_date('U')); | ||
745 | } elseif ($extractor->getDate()) { | ||
746 | $newitem->setDate($extractor->getDate()); | ||
747 | } | ||
748 | |||
749 | // add authors | ||
750 | if ($authors = $item->get_authors()) { | ||
751 | foreach ($authors as $author) { | ||
752 | // for some feeds, SimplePie stores author's name as email, e.g. http://feeds.feedburner.com/nymag/intel | ||
753 | if ($author->get_name() !== null) { | ||
754 | $newitem->addElement('dc:creator', $author->get_name()); | ||
755 | } elseif ($author->get_email() !== null) { | ||
756 | $newitem->addElement('dc:creator', $author->get_email()); | ||
757 | } | 734 | } |
735 | } else { | ||
736 | $summary = $html; | ||
758 | } | 737 | } |
759 | } elseif ($authors = $extractor->getAuthors()) { | 738 | } |
760 | //TODO: make sure the list size is reasonable | 739 | unset($_paras, $_para); |
761 | foreach ($authors as $author) { | 740 | $summary = get_excerpt($summary); |
762 | // TODO: xpath often selects authors from other articles linked from the page. | 741 | $newitem->setDescription($summary); |
763 | // for now choose first item | 742 | if ($options->content) $newitem->setElement('content:encoded', $html); |
764 | $newitem->addElement('dc:creator', $author); | 743 | } else { |
765 | break; | 744 | if ($options->content) $newitem->setDescription($html); |
745 | } | ||
746 | |||
747 | // set date | ||
748 | if ((int)$item->get_date('U') > 0) { | ||
749 | $newitem->setDate((int)$item->get_date('U')); | ||
750 | } elseif ($extractor->getDate()) { | ||
751 | $newitem->setDate($extractor->getDate()); | ||
752 | } | ||
753 | |||
754 | // add authors | ||
755 | if ($authors = $item->get_authors()) { | ||
756 | foreach ($authors as $author) { | ||
757 | // for some feeds, SimplePie stores author's name as email, e.g. http://feeds.feedburner.com/nymag/intel | ||
758 | if ($author->get_name() !== null) { | ||
759 | $newitem->addElement('dc:creator', $author->get_name()); | ||
760 | } elseif ($author->get_email() !== null) { | ||
761 | $newitem->addElement('dc:creator', $author->get_email()); | ||
766 | } | 762 | } |
767 | } | 763 | } |
768 | 764 | } elseif ($authors = $extractor->getAuthors()) { | |
769 | // add language | 765 | //TODO: make sure the list size is reasonable |
770 | if ($detect_language) { | 766 | foreach ($authors as $author) { |
771 | $language = $extractor->getLanguage(); | 767 | // TODO: xpath often selects authors from other articles linked from the page. |
772 | if (!$language) $language = $feed->get_language(); | 768 | // for now choose first item |
773 | if (($detect_language == 3 || (!$language && $detect_language == 2)) && $text_sample) { | 769 | $newitem->addElement('dc:creator', $author); |
774 | try { | 770 | break; |
775 | if ($use_cld) { | 771 | } |
776 | // Use PHP-CLD extension | 772 | } |
777 | $php_cld = 'CLD\detect'; // in quotes to prevent PHP 5.2 parse error | 773 | |
778 | $res = $php_cld($text_sample); | 774 | // add language |
779 | if (is_array($res) && count($res) > 0) { | 775 | if ($detect_language) { |
780 | $language = $res[0]['code']; | 776 | $language = $extractor->getLanguage(); |
781 | } | 777 | if (!$language) $language = $feed->get_language(); |
782 | } else { | 778 | if (($detect_language == 3 || (!$language && $detect_language == 2)) && $text_sample) { |
783 | //die('what'); | 779 | try { |
784 | // Use PEAR's Text_LanguageDetect | 780 | if ($use_cld) { |
785 | if (!isset($l)) { | 781 | // Use PHP-CLD extension |
786 | $l = new Text_LanguageDetect('libraries/language-detect/lang.dat', 'libraries/language-detect/unicode_blocks.dat'); | 782 | $php_cld = 'CLD\detect'; // in quotes to prevent PHP 5.2 parse error |
787 | } | 783 | $res = $php_cld($text_sample); |
788 | $l_result = $l->detect($text_sample, 1); | 784 | if (is_array($res) && count($res) > 0) { |
789 | if (count($l_result) > 0) { | 785 | $language = $res[0]['code']; |
790 | $language = $language_codes[key($l_result)]; | 786 | } |
791 | } | 787 | } else { |
788 | //die('what'); | ||
789 | // Use PEAR's Text_LanguageDetect | ||
790 | if (!isset($l)) { | ||
791 | $l = new Text_LanguageDetect(); | ||
792 | $l->setNameMode(2); // return ISO 639-1 codes (e.g. "en") | ||
793 | } | ||
794 | $l_result = $l->detect($text_sample, 1); | ||
795 | if (count($l_result) > 0) { | ||
796 | $language = key($l_result); | ||
792 | } | 797 | } |
793 | } catch (Exception $e) { | ||
794 | //die('error: '.$e); | ||
795 | // do nothing | ||
796 | } | 798 | } |
797 | } | 799 | } catch (Exception $e) { |
798 | if ($language && (strlen($language) < 7)) { | 800 | //die('error: '.$e); |
799 | $newitem->addElement('dc:language', $language); | 801 | // do nothing |
800 | } | 802 | } |
801 | } | 803 | } |
802 | 804 | if ($language && (strlen($language) < 7)) { | |
803 | // add MIME type (if it appeared in our exclusions lists) | 805 | $newitem->addElement('dc:language', $language); |
804 | if (isset($mime_info['mime'])) $newitem->addElement('dc:format', $mime_info['mime']); | ||
805 | // add effective URL (URL after redirects) | ||
806 | if (isset($effective_url)) { | ||
807 | //TODO: ensure $effective_url is valid witout - sometimes it causes problems, e.g. | ||
808 | //http://www.siasat.pk/forum/showthread.php?108883-Pakistan-Chowk-by-Rana-Mubashir-–-25th-March-2012-Special-Program-from-Liari-(Karachi) | ||
809 | //temporary measure: use utf8_encode() | ||
810 | $newitem->addElement('dc:identifier', remove_url_cruft(utf8_encode($effective_url))); | ||
811 | } else { | ||
812 | $newitem->addElement('dc:identifier', remove_url_cruft($item->get_permalink())); | ||
813 | } | 806 | } |
814 | 807 | } | |
815 | // add categories | 808 | |
816 | if ($categories = $item->get_categories()) { | 809 | // add MIME type (if it appeared in our exclusions lists) |
817 | foreach ($categories as $category) { | 810 | if (isset($mime_info['mime'])) $newitem->addElement('dc:format', $mime_info['mime']); |
818 | if ($category->get_label() !== null) { | 811 | // add effective URL (URL after redirects) |
819 | $newitem->addElement('category', $category->get_label()); | 812 | if (isset($effective_url)) { |
820 | } | 813 | //TODO: ensure $effective_url is valid witout - sometimes it causes problems, e.g. |
814 | //http://www.siasat.pk/forum/showthread.php?108883-Pakistan-Chowk-by-Rana-Mubashir-�-25th-March-2012-Special-Program-from-Liari-(Karachi) | ||
815 | //temporary measure: use utf8_encode() | ||
816 | $newitem->addElement('dc:identifier', remove_url_cruft(utf8_encode($effective_url))); | ||
817 | } else { | ||
818 | $newitem->addElement('dc:identifier', remove_url_cruft($item->get_permalink())); | ||
819 | } | ||
820 | |||
821 | // add categories | ||
822 | if ($categories = $item->get_categories()) { | ||
823 | foreach ($categories as $category) { | ||
824 | if ($category->get_label() !== null) { | ||
825 | $newitem->addElement('category', $category->get_label()); | ||
821 | } | 826 | } |
822 | } | 827 | } |
823 | 828 | } | |
824 | // check for enclosures | 829 | |
825 | if ($options->keep_enclosures) { | 830 | // check for enclosures |
826 | if ($enclosures = $item->get_enclosures()) { | 831 | if ($options->keep_enclosures) { |
827 | foreach ($enclosures as $enclosure) { | 832 | if ($enclosures = $item->get_enclosures()) { |
828 | // thumbnails | 833 | foreach ($enclosures as $enclosure) { |
829 | foreach ((array)$enclosure->get_thumbnails() as $thumbnail) { | 834 | // thumbnails |
830 | $newitem->addElement('media:thumbnail', '', array('url'=>$thumbnail)); | 835 | foreach ((array)$enclosure->get_thumbnails() as $thumbnail) { |
831 | } | 836 | $newitem->addElement('media:thumbnail', '', array('url'=>$thumbnail)); |
832 | if (!$enclosure->get_link()) continue; | ||
833 | $enc = array(); | ||
834 | // Media RSS spec ($enc): http://search.yahoo.com/mrss | ||
835 | // SimplePie methods ($enclosure): http://simplepie.org/wiki/reference/start#methods4 | ||
836 | $enc['url'] = $enclosure->get_link(); | ||
837 | if ($enclosure->get_length()) $enc['fileSize'] = $enclosure->get_length(); | ||
838 | if ($enclosure->get_type()) $enc['type'] = $enclosure->get_type(); | ||
839 | if ($enclosure->get_medium()) $enc['medium'] = $enclosure->get_medium(); | ||
840 | if ($enclosure->get_expression()) $enc['expression'] = $enclosure->get_expression(); | ||
841 | if ($enclosure->get_bitrate()) $enc['bitrate'] = $enclosure->get_bitrate(); | ||
842 | if ($enclosure->get_framerate()) $enc['framerate'] = $enclosure->get_framerate(); | ||
843 | if ($enclosure->get_sampling_rate()) $enc['samplingrate'] = $enclosure->get_sampling_rate(); | ||
844 | if ($enclosure->get_channels()) $enc['channels'] = $enclosure->get_channels(); | ||
845 | if ($enclosure->get_duration()) $enc['duration'] = $enclosure->get_duration(); | ||
846 | if ($enclosure->get_height()) $enc['height'] = $enclosure->get_height(); | ||
847 | if ($enclosure->get_width()) $enc['width'] = $enclosure->get_width(); | ||
848 | if ($enclosure->get_language()) $enc['lang'] = $enclosure->get_language(); | ||
849 | $newitem->addElement('media:content', '', $enc); | ||
850 | } | 837 | } |
838 | if (!$enclosure->get_link()) continue; | ||
839 | $enc = array(); | ||
840 | // Media RSS spec ($enc): http://search.yahoo.com/mrss | ||
841 | // SimplePie methods ($enclosure): http://simplepie.org/wiki/reference/start#methods4 | ||
842 | $enc['url'] = $enclosure->get_link(); | ||
843 | if ($enclosure->get_length()) $enc['fileSize'] = $enclosure->get_length(); | ||
844 | if ($enclosure->get_type()) $enc['type'] = $enclosure->get_type(); | ||
845 | if ($enclosure->get_medium()) $enc['medium'] = $enclosure->get_medium(); | ||
846 | if ($enclosure->get_expression()) $enc['expression'] = $enclosure->get_expression(); | ||
847 | if ($enclosure->get_bitrate()) $enc['bitrate'] = $enclosure->get_bitrate(); | ||
848 | if ($enclosure->get_framerate()) $enc['framerate'] = $enclosure->get_framerate(); | ||
849 | if ($enclosure->get_sampling_rate()) $enc['samplingrate'] = $enclosure->get_sampling_rate(); | ||
850 | if ($enclosure->get_channels()) $enc['channels'] = $enclosure->get_channels(); | ||
851 | if ($enclosure->get_duration()) $enc['duration'] = $enclosure->get_duration(); | ||
852 | if ($enclosure->get_height()) $enc['height'] = $enclosure->get_height(); | ||
853 | if ($enclosure->get_width()) $enc['width'] = $enclosure->get_width(); | ||
854 | if ($enclosure->get_language()) $enc['lang'] = $enclosure->get_language(); | ||
855 | $newitem->addElement('media:content', '', $enc); | ||
851 | } | 856 | } |
852 | } | 857 | } |
853 | /* } */ | 858 | } |
854 | $output->addItem($newitem); | 859 | $output->addItem($newitem); |
855 | unset($html); | 860 | unset($html); |
856 | $item_count++; | 861 | $item_count++; |
@@ -887,7 +892,7 @@ if (!$debug_mode) { | |||
887 | } | 892 | } |
888 | if ($add_to_cache) { | 893 | if ($add_to_cache) { |
889 | ob_start(); | 894 | ob_start(); |
890 | $output->genarateFeed(); | 895 | $output->genarateFeed(false); |
891 | $output = ob_get_contents(); | 896 | $output = ob_get_contents(); |
892 | ob_end_clean(); | 897 | ob_end_clean(); |
893 | if ($html_only && $item_count == 0) { | 898 | if ($html_only && $item_count == 0) { |
@@ -898,299 +903,8 @@ if (!$debug_mode) { | |||
898 | } | 903 | } |
899 | echo $output; | 904 | echo $output; |
900 | } else { | 905 | } else { |
901 | $output->genarateFeed(); | 906 | $output->genarateFeed(false); |
902 | } | 907 | } |
903 | if ($callback) echo ');'; | 908 | if ($callback) echo ');'; |
904 | } | 909 | } |
905 | 910 | ||
906 | /////////////////////////////// | ||
907 | // HELPER FUNCTIONS | ||
908 | /////////////////////////////// | ||
909 | |||
910 | function url_allowed($url) { | ||
911 | global $options; | ||
912 | if (!empty($options->allowed_urls)) { | ||
913 | $allowed = false; | ||
914 | foreach ($options->allowed_urls as $allowurl) { | ||
915 | if (stristr($url, $allowurl) !== false) { | ||
916 | $allowed = true; | ||
917 | break; | ||
918 | } | ||
919 | } | ||
920 | if (!$allowed) return false; | ||
921 | } else { | ||
922 | foreach ($options->blocked_urls as $blockurl) { | ||
923 | if (stristr($url, $blockurl) !== false) { | ||
924 | return false; | ||
925 | } | ||
926 | } | ||
927 | } | ||
928 | return true; | ||
929 | } | ||
930 | |||
931 | ////////////////////////////////////////////// | ||
932 | // Convert $html to UTF8 | ||
933 | // (uses HTTP headers and HTML to find encoding) | ||
934 | // adapted from http://stackoverflow.com/questions/910793/php-detect-encoding-and-make-everything-utf-8 | ||
935 | ////////////////////////////////////////////// | ||
936 | function convert_to_utf8($html, $header=null) | ||
937 | { | ||
938 | $encoding = null; | ||
939 | if ($html || $header) { | ||
940 | if (is_array($header)) $header = implode("\n", $header); | ||
941 | if (!$header || !preg_match_all('/^Content-Type:\s+([^;]+)(?:;\s*charset=["\']?([^;"\'\n]*))?/im', $header, $match, PREG_SET_ORDER)) { | ||
942 | // error parsing the response | ||
943 | debug('Could not find Content-Type header in HTTP response'); | ||
944 | } else { | ||
945 | $match = end($match); // get last matched element (in case of redirects) | ||
946 | if (isset($match[2])) $encoding = trim($match[2], "\"' \r\n\0\x0B\t"); | ||
947 | } | ||
948 | // TODO: check to see if encoding is supported (can we convert it?) | ||
949 | // If it's not, result will be empty string. | ||
950 | // For now we'll check for invalid encoding types returned by some sites, e.g. 'none' | ||
951 | // Problem URL: http://facta.co.jp/blog/archives/20111026001026.html | ||
952 | if (!$encoding || $encoding == 'none') { | ||
953 | // search for encoding in HTML - only look at the first 50000 characters | ||
954 | // Why 50000? See, for example, http://www.lemonde.fr/festival-de-cannes/article/2012/05/23/deux-cretes-en-goguette-sur-la-croisette_1705732_766360.html | ||
955 | // TODO: improve this so it looks at smaller chunks first | ||
956 | $html_head = substr($html, 0, 50000); | ||
957 | if (preg_match('/^<\?xml\s+version=(?:"[^"]*"|\'[^\']*\')\s+encoding=("[^"]*"|\'[^\']*\')/s', $html_head, $match)) { | ||
958 | $encoding = trim($match[1], '"\''); | ||
959 | } elseif (preg_match('/<meta\s+http-equiv=["\']?Content-Type["\']? content=["\'][^;]+;\s*charset=["\']?([^;"\'>]+)/i', $html_head, $match)) { | ||
960 | $encoding = trim($match[1]); | ||
961 | } elseif (preg_match_all('/<meta\s+([^>]+)>/i', $html_head, $match)) { | ||
962 | foreach ($match[1] as $_test) { | ||
963 | if (preg_match('/charset=["\']?([^"\']+)/i', $_test, $_m)) { | ||
964 | $encoding = trim($_m[1]); | ||
965 | break; | ||
966 | } | ||
967 | } | ||
968 | } | ||
969 | } | ||
970 | if (isset($encoding)) $encoding = trim($encoding); | ||
971 | // trim is important here! | ||
972 | if (!$encoding || (strtolower($encoding) == 'iso-8859-1')) { | ||
973 | // replace MS Word smart qutoes | ||
974 | $trans = array(); | ||
975 | $trans[chr(130)] = '‚'; // Single Low-9 Quotation Mark | ||
976 | $trans[chr(131)] = 'ƒ'; // Latin Small Letter F With Hook | ||
977 | $trans[chr(132)] = '„'; // Double Low-9 Quotation Mark | ||
978 | $trans[chr(133)] = '…'; // Horizontal Ellipsis | ||
979 | $trans[chr(134)] = '†'; // Dagger | ||
980 | $trans[chr(135)] = '‡'; // Double Dagger | ||
981 | $trans[chr(136)] = 'ˆ'; // Modifier Letter Circumflex Accent | ||
982 | $trans[chr(137)] = '‰'; // Per Mille Sign | ||
983 | $trans[chr(138)] = 'Š'; // Latin Capital Letter S With Caron | ||
984 | $trans[chr(139)] = '‹'; // Single Left-Pointing Angle Quotation Mark | ||
985 | $trans[chr(140)] = 'Œ'; // Latin Capital Ligature OE | ||
986 | $trans[chr(145)] = '‘'; // Left Single Quotation Mark | ||
987 | $trans[chr(146)] = '’'; // Right Single Quotation Mark | ||
988 | $trans[chr(147)] = '“'; // Left Double Quotation Mark | ||
989 | $trans[chr(148)] = '”'; // Right Double Quotation Mark | ||
990 | $trans[chr(149)] = '•'; // Bullet | ||
991 | $trans[chr(150)] = '–'; // En Dash | ||
992 | $trans[chr(151)] = '—'; // Em Dash | ||
993 | $trans[chr(152)] = '˜'; // Small Tilde | ||
994 | $trans[chr(153)] = '™'; // Trade Mark Sign | ||
995 | $trans[chr(154)] = 'š'; // Latin Small Letter S With Caron | ||
996 | $trans[chr(155)] = '›'; // Single Right-Pointing Angle Quotation Mark | ||
997 | $trans[chr(156)] = 'œ'; // Latin Small Ligature OE | ||
998 | $trans[chr(159)] = 'Ÿ'; // Latin Capital Letter Y With Diaeresis | ||
999 | $html = strtr($html, $trans); | ||
1000 | } | ||
1001 | if (!$encoding) { | ||
1002 | debug('No character encoding found, so treating as UTF-8'); | ||
1003 | $encoding = 'utf-8'; | ||
1004 | } else { | ||
1005 | debug('Character encoding: '.$encoding); | ||
1006 | if (strtolower($encoding) != 'utf-8') { | ||
1007 | debug('Converting to UTF-8'); | ||
1008 | $html = SimplePie_Misc::change_encoding($html, $encoding, 'utf-8'); | ||
1009 | /* | ||
1010 | if (function_exists('iconv')) { | ||
1011 | // iconv appears to handle certain character encodings better than mb_convert_encoding | ||
1012 | $html = iconv($encoding, 'utf-8', $html); | ||
1013 | } else { | ||
1014 | $html = mb_convert_encoding($html, 'utf-8', $encoding); | ||
1015 | } | ||
1016 | */ | ||
1017 | } | ||
1018 | } | ||
1019 | } | ||
1020 | return $html; | ||
1021 | } | ||
1022 | |||
1023 | function makeAbsolute($base, $elem) { | ||
1024 | $base = new SimplePie_IRI($base); | ||
1025 | // remove '//' in URL path (used to prevent URLs from resolving properly) | ||
1026 | // TODO: check if this is still the case | ||
1027 | if (isset($base->path)) $base->path = preg_replace('!//+!', '/', $base->path); | ||
1028 | foreach(array('a'=>'href', 'img'=>'src') as $tag => $attr) { | ||
1029 | $elems = $elem->getElementsByTagName($tag); | ||
1030 | for ($i = $elems->length-1; $i >= 0; $i--) { | ||
1031 | $e = $elems->item($i); | ||
1032 | //$e->parentNode->replaceChild($articleContent->ownerDocument->createTextNode($e->textContent), $e); | ||
1033 | makeAbsoluteAttr($base, $e, $attr); | ||
1034 | } | ||
1035 | if (strtolower($elem->tagName) == $tag) makeAbsoluteAttr($base, $elem, $attr); | ||
1036 | } | ||
1037 | } | ||
1038 | function makeAbsoluteAttr($base, $e, $attr) { | ||
1039 | if ($e->hasAttribute($attr)) { | ||
1040 | // Trim leading and trailing white space. I don't really like this but | ||
1041 | // unfortunately it does appear on some sites. e.g. <img src=" /path/to/image.jpg" /> | ||
1042 | $url = trim(str_replace('%20', ' ', $e->getAttribute($attr))); | ||
1043 | $url = str_replace(' ', '%20', $url); | ||
1044 | if (!preg_match('!https?://!i', $url)) { | ||
1045 | if ($absolute = SimplePie_IRI::absolutize($base, $url)) { | ||
1046 | $e->setAttribute($attr, $absolute); | ||
1047 | } | ||
1048 | } | ||
1049 | } | ||
1050 | } | ||
1051 | function makeAbsoluteStr($base, $url) { | ||
1052 | $base = new SimplePie_IRI($base); | ||
1053 | // remove '//' in URL path (causes URLs not to resolve properly) | ||
1054 | if (isset($base->path)) $base->path = preg_replace('!//+!', '/', $base->path); | ||
1055 | if (preg_match('!^https?://!i', $url)) { | ||
1056 | // already absolute | ||
1057 | return $url; | ||
1058 | } else { | ||
1059 | if ($absolute = SimplePie_IRI::absolutize($base, $url)) { | ||
1060 | return $absolute; | ||
1061 | } | ||
1062 | return false; | ||
1063 | } | ||
1064 | } | ||
1065 | // returns single page response, or false if not found | ||
1066 | function getSinglePage($item, $html, $url) { | ||
1067 | global $http, $extractor; | ||
1068 | debug('Looking for site config files to see if single page link exists'); | ||
1069 | $site_config = $extractor->buildSiteConfig($url, $html); | ||
1070 | $splink = null; | ||
1071 | if (!empty($site_config->single_page_link)) { | ||
1072 | $splink = $site_config->single_page_link; | ||
1073 | } elseif (!empty($site_config->single_page_link_in_feed)) { | ||
1074 | // single page link xpath is targeted at feed | ||
1075 | $splink = $site_config->single_page_link_in_feed; | ||
1076 | // so let's replace HTML with feed item description | ||
1077 | $html = $item->get_description(); | ||
1078 | } | ||
1079 | if (isset($splink)) { | ||
1080 | // Build DOM tree from HTML | ||
1081 | $readability = new Readability($html, $url); | ||
1082 | $xpath = new DOMXPath($readability->dom); | ||
1083 | // Loop through single_page_link xpath expressions | ||
1084 | $single_page_url = null; | ||
1085 | foreach ($splink as $pattern) { | ||
1086 | $elems = @$xpath->evaluate($pattern, $readability->dom); | ||
1087 | if (is_string($elems)) { | ||
1088 | $single_page_url = trim($elems); | ||
1089 | break; | ||
1090 | } elseif ($elems instanceof DOMNodeList && $elems->length > 0) { | ||
1091 | foreach ($elems as $item) { | ||
1092 | if ($item instanceof DOMElement && $item->hasAttribute('href')) { | ||
1093 | $single_page_url = $item->getAttribute('href'); | ||
1094 | break 2; | ||
1095 | } elseif ($item instanceof DOMAttr && $item->value) { | ||
1096 | $single_page_url = $item->value; | ||
1097 | break 2; | ||
1098 | } | ||
1099 | } | ||
1100 | } | ||
1101 | } | ||
1102 | // If we've got URL, resolve against $url | ||
1103 | if (isset($single_page_url) && ($single_page_url = makeAbsoluteStr($url, $single_page_url))) { | ||
1104 | // check it's not what we have already! | ||
1105 | if ($single_page_url != $url) { | ||
1106 | // it's not, so let's try to fetch it... | ||
1107 | $_prev_ref = $http->referer; | ||
1108 | $http->referer = $single_page_url; | ||
1109 | if (($response = $http->get($single_page_url, true)) && $response['status_code'] < 300) { | ||
1110 | $http->referer = $_prev_ref; | ||
1111 | return $response; | ||
1112 | } | ||
1113 | $http->referer = $_prev_ref; | ||
1114 | } | ||
1115 | } | ||
1116 | } | ||
1117 | return false; | ||
1118 | } | ||
1119 | |||
1120 | // based on content-type http header, decide what to do | ||
1121 | // param: HTTP headers string | ||
1122 | // return: array with keys: 'mime', 'type', 'subtype', 'action', 'name' | ||
1123 | // e.g. array('mime'=>'image/jpeg', 'type'=>'image', 'subtype'=>'jpeg', 'action'=>'link', 'name'=>'Image') | ||
1124 | function get_mime_action_info($headers) { | ||
1125 | global $options; | ||
1126 | // check if action defined for returned Content-Type | ||
1127 | $info = array(); | ||
1128 | if (preg_match('!^Content-Type:\s*(([-\w]+)/([-\w\+]+))!im', $headers, $match)) { | ||
1129 | // look for full mime type (e.g. image/jpeg) or just type (e.g. image) | ||
1130 | // match[1] = full mime type, e.g. image/jpeg | ||
1131 | // match[2] = first part, e.g. image | ||
1132 | // match[3] = last part, e.g. jpeg | ||
1133 | $info['mime'] = strtolower(trim($match[1])); | ||
1134 | $info['type'] = strtolower(trim($match[2])); | ||
1135 | $info['subtype'] = strtolower(trim($match[3])); | ||
1136 | foreach (array($info['mime'], $info['type']) as $_mime) { | ||
1137 | if (isset($options->content_type_exc[$_mime])) { | ||
1138 | $info['action'] = $options->content_type_exc[$_mime]['action']; | ||
1139 | $info['name'] = $options->content_type_exc[$_mime]['name']; | ||
1140 | break; | ||
1141 | } | ||
1142 | } | ||
1143 | } | ||
1144 | return $info; | ||
1145 | } | ||
1146 | |||
1147 | function remove_url_cruft($url) { | ||
1148 | // remove google analytics for the time being | ||
1149 | // regex adapted from http://navitronic.co.uk/2010/12/removing-google-analytics-cruft-from-urls/ | ||
1150 | // https://gist.github.com/758177 | ||
1151 | return preg_replace('/(\?|\&)utm_[a-z]+=[^\&]+/', '', $url); | ||
1152 | } | ||
1153 | |||
1154 | function make_substitutions($string) { | ||
1155 | if ($string == '') return $string; | ||
1156 | global $item, $effective_url; | ||
1157 | $string = str_replace('{url}', htmlspecialchars($item->get_permalink()), $string); | ||
1158 | $string = str_replace('{effective-url}', htmlspecialchars($effective_url), $string); | ||
1159 | return $string; | ||
1160 | } | ||
1161 | |||
1162 | function get_cache() { | ||
1163 | global $options, $valid_key; | ||
1164 | static $cache = null; | ||
1165 | if ($cache === null) { | ||
1166 | $frontendOptions = array( | ||
1167 | 'lifetime' => 10*60, // cache lifetime of 10 minutes | ||
1168 | 'automatic_serialization' => false, | ||
1169 | 'write_control' => false, | ||
1170 | 'automatic_cleaning_factor' => $options->cache_cleanup, | ||
1171 | 'ignore_user_abort' => false | ||
1172 | ); | ||
1173 | $backendOptions = array( | ||
1174 | 'cache_dir' => ($valid_key) ? $options->cache_dir.'/rss-with-key/' : $options->cache_dir.'/rss/', // directory where to put the cache files | ||
1175 | 'file_locking' => false, | ||
1176 | 'read_control' => true, | ||
1177 | 'read_control_type' => 'strlen', | ||
1178 | 'hashed_directory_level' => $options->cache_directory_level, | ||
1179 | 'hashed_directory_perm' => 0777, | ||
1180 | 'cache_file_perm' => 0664, | ||
1181 | 'file_name_prefix' => 'ff' | ||
1182 | ); | ||
1183 | // getting a Zend_Cache_Core object | ||
1184 | $cache = Zend_Cache::factory('Core', 'File', $frontendOptions, $backendOptions); | ||
1185 | } | ||
1186 | return $cache; | ||
1187 | } | ||
1188 | |||
1189 | function debug($msg) { | ||
1190 | global $debug_mode; | ||
1191 | if ($debug_mode) { | ||
1192 | echo '* ',$msg,"\n"; | ||
1193 | ob_flush(); | ||
1194 | flush(); | ||
1195 | } | ||
1196 | } \ No newline at end of file | ||