diff options
author | Frederic Guillot <fred@kanboard.net> | 2017-10-25 16:22:10 -0700 |
---|---|---|
committer | Frederic Guillot <fred@kanboard.net> | 2017-10-25 16:22:10 -0700 |
commit | 9e2b2a32fd0e967ad3184e9a5d091a29953acb91 (patch) | |
tree | 00822e24aa1110c73ca455a8d096ef296c008cbc /vendor/miniflux/picofeed | |
parent | c507c5416251c505cb3e088a03c6664bed73c812 (diff) |
Include composer dependencies in repo
Diffstat (limited to 'vendor/miniflux/picofeed')
314 files changed, 13476 insertions, 0 deletions
diff --git a/vendor/miniflux/picofeed/LICENSE b/vendor/miniflux/picofeed/LICENSE new file mode 100644 index 00000000..6a362bc1 --- /dev/null +++ b/vendor/miniflux/picofeed/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Frederic Guillot + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Base.php b/vendor/miniflux/picofeed/lib/PicoFeed/Base.php new file mode 100644 index 00000000..41a6f8f0 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Base.php @@ -0,0 +1,38 @@ +<?php + +namespace PicoFeed; + +use PicoFeed\Config\Config; +use PicoFeed\Logging\Logger; + +/** + * Base class + * + * @package PicoFeed + * @author Frederic Guillot + */ +abstract class Base +{ + /** + * Config class instance + * + * @access protected + * @var \PicoFeed\Config\Config + */ + protected $config; + + /** + * Constructor. + * + * @param \PicoFeed\Config\Config $config Config class instance + */ + public function __construct(Config $config = null) + { + $this->config = $config ?: new Config(); + Logger::setTimezone($this->config->getTimezone()); + } + + public function setConfig(Config $config) { + $this->config = $config; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/Client.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/Client.php new file mode 100644 index 00000000..0548d5c6 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/Client.php @@ -0,0 +1,719 @@ +<?php + +namespace PicoFeed\Client; + +use DateTime; +use Exception; +use LogicException; +use PicoFeed\Logging\Logger; +use PicoFeed\Config\Config; + +/** + * Client class. + * + * @author Frederic Guillot + */ +abstract class Client +{ + /** + * Flag that say if the resource have been modified. + * + * @var bool + */ + private $is_modified = true; + + /** + * HTTP Content-Type. + * + * @var string + */ + private $content_type = ''; + + /** + * HTTP encoding. + * + * @var string + */ + private $encoding = ''; + + /** + * HTTP request headers. + * + * @var array + */ + protected $request_headers = array(); + + /** + * HTTP Etag header. + * + * @var string + */ + protected $etag = ''; + + /** + * HTTP Last-Modified header. + * + * @var string + */ + protected $last_modified = ''; + + /** + * Expiration DateTime + * + * @var DateTime + */ + protected $expiration = null; + + /** + * Proxy hostname. + * + * @var string + */ + protected $proxy_hostname = ''; + + /** + * Proxy port. + * + * @var int + */ + protected $proxy_port = 3128; + + /** + * Proxy username. + * + * @var string + */ + protected $proxy_username = ''; + + /** + * Proxy password. + * + * @var string + */ + protected $proxy_password = ''; + + /** + * Basic auth username. + * + * @var string + */ + protected $username = ''; + + /** + * Basic auth password. + * + * @var string + */ + protected $password = ''; + + /** + * Client connection timeout. + * + * @var int + */ + protected $timeout = 10; + + /** + * User-agent. + * + * @var string + */ + protected $user_agent = 'PicoFeed (https://github.com/miniflux/picoFeed)'; + + /** + * Real URL used (can be changed after a HTTP redirect). + * + * @var string + */ + protected $url = ''; + + /** + * Page/Feed content. + * + * @var string + */ + protected $content = ''; + + /** + * Number maximum of HTTP redirections to avoid infinite loops. + * + * @var int + */ + protected $max_redirects = 5; + + /** + * Maximum size of the HTTP body response. + * + * @var int + */ + protected $max_body_size = 2097152; // 2MB + + /** + * HTTP response status code. + * + * @var int + */ + protected $status_code = 0; + + /** + * Enables direct passthrough to requesting client. + * + * @var bool + */ + protected $passthrough = false; + + /** + * Do the HTTP request. + * + * @abstract + * + * @return array + */ + abstract public function doRequest(); + + /** + * Get client instance: curl or stream driver. + * + * @static + * + * @return \PicoFeed\Client\Client + */ + public static function getInstance() + { + if (function_exists('curl_init')) { + return new Curl(); + } elseif (ini_get('allow_url_fopen')) { + return new Stream(); + } + + throw new LogicException('You must have "allow_url_fopen=1" or curl extension installed'); + } + + /** + * Add HTTP Header to the request. + * + * @param array $headers + */ + public function setHeaders($headers) + { + $this->request_headers = $headers; + } + + /** + * Perform the HTTP request. + * + * @param string $url URL + * + * @return Client + */ + public function execute($url = '') + { + if ($url !== '') { + $this->url = $url; + } + + Logger::setMessage(get_called_class().' Fetch URL: '.$this->url); + Logger::setMessage(get_called_class().' Etag provided: '.$this->etag); + Logger::setMessage(get_called_class().' Last-Modified provided: '.$this->last_modified); + + $response = $this->doRequest(); + + $this->status_code = $response['status']; + $this->handleNotModifiedResponse($response); + $this->handleErrorResponse($response); + $this->handleNormalResponse($response); + + $this->expiration = $this->parseExpiration($response['headers']); + Logger::setMessage(get_called_class().' Expiration: '.$this->expiration->format(DATE_ISO8601)); + + return $this; + } + + /** + * Handle not modified response. + * + * @param array $response Client response + */ + protected function handleNotModifiedResponse(array $response) + { + if ($response['status'] == 304) { + $this->is_modified = false; + } elseif ($response['status'] == 200) { + $this->is_modified = $this->hasBeenModified($response, $this->etag, $this->last_modified); + $this->etag = $this->getHeader($response, 'ETag'); + $this->last_modified = $this->getHeader($response, 'Last-Modified'); + } + + if ($this->is_modified === false) { + Logger::setMessage(get_called_class().' Resource not modified'); + } + } + + /** + * Handle Http Error codes + * + * @param array $response Client response + * @throws ForbiddenException + * @throws InvalidUrlException + * @throws UnauthorizedException + */ + protected function handleErrorResponse(array $response) + { + $status = $response['status']; + if ($status == 401) { + throw new UnauthorizedException('Wrong or missing credentials'); + } else if ($status == 403) { + throw new ForbiddenException('Not allowed to access resource'); + } else if ($status == 404) { + throw new InvalidUrlException('Resource not found'); + } + } + + /** + * Handle normal response. + * + * @param array $response Client response + */ + protected function handleNormalResponse(array $response) + { + if ($response['status'] == 200) { + $this->content = $response['body']; + $this->content_type = $this->findContentType($response); + $this->encoding = $this->findCharset(); + } + } + + /** + * Check if a request has been modified according to the parameters. + * + * @param array $response + * @param string $etag + * @param string $lastModified + * + * @return bool + */ + private function hasBeenModified($response, $etag, $lastModified) + { + $headers = array( + 'Etag' => $etag, + 'Last-Modified' => $lastModified, + ); + + // Compare the values for each header that is present + $presentCacheHeaderCount = 0; + foreach ($headers as $key => $value) { + if (isset($response['headers'][$key])) { + if ($response['headers'][$key] !== $value) { + return true; + } + ++$presentCacheHeaderCount; + } + } + + // If at least one header is present and the values match, the response + // was not modified + if ($presentCacheHeaderCount > 0) { + return false; + } + + return true; + } + + /** + * Find content type from response headers. + * + * @param array $response Client response + * + * @return string + */ + public function findContentType(array $response) + { + return strtolower($this->getHeader($response, 'Content-Type')); + } + + /** + * Find charset from response headers. + * + * @return string + */ + public function findCharset() + { + $result = explode('charset=', $this->content_type); + + return isset($result[1]) ? $result[1] : ''; + } + + /** + * Get header value from a client response. + * + * @param array $response Client response + * @param string $header Header name + * + * @return string + */ + public function getHeader(array $response, $header) + { + return isset($response['headers'][$header]) ? $response['headers'][$header] : ''; + } + + /** + * Set the Last-Modified HTTP header. + * + * @param string $last_modified Header value + * + * @return \PicoFeed\Client\Client + */ + public function setLastModified($last_modified) + { + $this->last_modified = $last_modified; + + return $this; + } + + /** + * Get the value of the Last-Modified HTTP header. + * + * @return string + */ + public function getLastModified() + { + return $this->last_modified; + } + + /** + * Set the value of the Etag HTTP header. + * + * @param string $etag Etag HTTP header value + * + * @return \PicoFeed\Client\Client + */ + public function setEtag($etag) + { + $this->etag = $etag; + + return $this; + } + + /** + * Get the Etag HTTP header value. + * + * @return string + */ + public function getEtag() + { + return $this->etag; + } + + /** + * Get the final url value. + * + * @return string + */ + public function getUrl() + { + return $this->url; + } + + /** + * Set the url. + * + * @param $url + * @return string + */ + public function setUrl($url) + { + $this->url = $url; + return $this; + } + + /** + * Get the HTTP response status code. + * + * @return int + */ + public function getStatusCode() + { + return $this->status_code; + } + + /** + * Get the body of the HTTP response. + * + * @return string + */ + public function getContent() + { + return $this->content; + } + + /** + * Get the content type value from HTTP headers. + * + * @return string + */ + public function getContentType() + { + return $this->content_type; + } + + /** + * Get the encoding value from HTTP headers. + * + * @return string + */ + public function getEncoding() + { + return $this->encoding; + } + + /** + * Return true if the remote resource has changed. + * + * @return bool + */ + public function isModified() + { + return $this->is_modified; + } + + /** + * return true if passthrough mode is enabled. + * + * @return bool + */ + public function isPassthroughEnabled() + { + return $this->passthrough; + } + + /** + * Set connection timeout. + * + * @param int $timeout Connection timeout + * + * @return \PicoFeed\Client\Client + */ + public function setTimeout($timeout) + { + $this->timeout = $timeout ?: $this->timeout; + + return $this; + } + + /** + * Set a custom user agent. + * + * @param string $user_agent User Agent + * + * @return \PicoFeed\Client\Client + */ + public function setUserAgent($user_agent) + { + $this->user_agent = $user_agent ?: $this->user_agent; + + return $this; + } + + /** + * Set the maximum number of HTTP redirections. + * + * @param int $max Maximum + * + * @return \PicoFeed\Client\Client + */ + public function setMaxRedirections($max) + { + $this->max_redirects = $max ?: $this->max_redirects; + + return $this; + } + + /** + * Set the maximum size of the HTTP body. + * + * @param int $max Maximum + * + * @return \PicoFeed\Client\Client + */ + public function setMaxBodySize($max) + { + $this->max_body_size = $max ?: $this->max_body_size; + + return $this; + } + + /** + * Set the proxy hostname. + * + * @param string $hostname Proxy hostname + * + * @return \PicoFeed\Client\Client + */ + public function setProxyHostname($hostname) + { + $this->proxy_hostname = $hostname ?: $this->proxy_hostname; + + return $this; + } + + /** + * Set the proxy port. + * + * @param int $port Proxy port + * + * @return \PicoFeed\Client\Client + */ + public function setProxyPort($port) + { + $this->proxy_port = $port ?: $this->proxy_port; + + return $this; + } + + /** + * Set the proxy username. + * + * @param string $username Proxy username + * + * @return \PicoFeed\Client\Client + */ + public function setProxyUsername($username) + { + $this->proxy_username = $username ?: $this->proxy_username; + + return $this; + } + + /** + * Set the proxy password. + * + * @param string $password Password + * + * @return \PicoFeed\Client\Client + */ + public function setProxyPassword($password) + { + $this->proxy_password = $password ?: $this->proxy_password; + + return $this; + } + + /** + * Set the username. + * + * @param string $username Basic Auth username + * + * @return \PicoFeed\Client\Client + */ + public function setUsername($username) + { + $this->username = $username ?: $this->username; + + return $this; + } + + /** + * Set the password. + * + * @param string $password Basic Auth Password + * + * @return \PicoFeed\Client\Client + */ + public function setPassword($password) + { + $this->password = $password ?: $this->password; + + return $this; + } + + /** + * Enable the passthrough mode. + * + * @return \PicoFeed\Client\Client + */ + public function enablePassthroughMode() + { + $this->passthrough = true; + + return $this; + } + + /** + * Disable the passthrough mode. + * + * @return \PicoFeed\Client\Client + */ + public function disablePassthroughMode() + { + $this->passthrough = false; + + return $this; + } + + /** + * Set config object. + * + * @param \PicoFeed\Config\Config $config Config instance + * + * @return \PicoFeed\Client\Client + */ + public function setConfig(Config $config) + { + if ($config !== null) { + $this->setTimeout($config->getClientTimeout()); + $this->setUserAgent($config->getClientUserAgent()); + $this->setMaxRedirections($config->getMaxRedirections()); + $this->setMaxBodySize($config->getMaxBodySize()); + $this->setProxyHostname($config->getProxyHostname()); + $this->setProxyPort($config->getProxyPort()); + $this->setProxyUsername($config->getProxyUsername()); + $this->setProxyPassword($config->getProxyPassword()); + } + + return $this; + } + + /** + * Return true if the HTTP status code is a redirection + * + * @access protected + * @param integer $code + * @return boolean + */ + public function isRedirection($code) + { + return $code == 301 || $code == 302 || $code == 303 || $code == 307; + } + + public function parseExpiration(HttpHeaders $headers) + { + try { + + if (isset($headers['Cache-Control'])) { + if (preg_match('/s-maxage=(\d+)/', $headers['Cache-Control'], $matches)) { + return new DateTime('+' . $matches[1] . ' seconds'); + } else if (preg_match('/max-age=(\d+)/', $headers['Cache-Control'], $matches)) { + return new DateTime('+' . $matches[1] . ' seconds'); + } + } + + if (! empty($headers['Expires'])) { + return new DateTime($headers['Expires']); + } + } catch (Exception $e) { + Logger::setMessage('Unable to parse expiration date: '.$e->getMessage()); + } + + return new DateTime(); + } + + /** + * Get expiration date time from "Expires" or "Cache-Control" headers + * + * @return DateTime + */ + public function getExpiration() + { + return $this->expiration ?: new DateTime(); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/ClientException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/ClientException.php new file mode 100644 index 00000000..b3a95c9f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/ClientException.php @@ -0,0 +1,14 @@ +<?php + +namespace PicoFeed\Client; + +use PicoFeed\PicoFeedException; + +/** + * ClientException Exception. + * + * @author Frederic Guillot + */ +abstract class ClientException extends PicoFeedException +{ +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/Curl.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/Curl.php new file mode 100644 index 00000000..f4a65782 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/Curl.php @@ -0,0 +1,402 @@ +<?php + +namespace PicoFeed\Client; + +use PicoFeed\Logging\Logger; + +/** + * cURL HTTP client. + * + * @author Frederic Guillot + */ +class Curl extends Client +{ + protected $nbRedirects = 0; + + /** + * HTTP response body. + * + * @var string + */ + private $body = ''; + + /** + * Body size. + * + * @var int + */ + private $body_length = 0; + + /** + * HTTP response headers. + * + * @var array + */ + private $response_headers = array(); + + /** + * Counter on the number of header received. + * + * @var int + */ + private $response_headers_count = 0; + + /** + * cURL callback to read the HTTP body. + * + * If the function return -1, curl stop to read the HTTP response + * + * @param resource $ch cURL handler + * @param string $buffer Chunk of data + * + * @return int Length of the buffer + */ + public function readBody($ch, $buffer) + { + $length = strlen($buffer); + $this->body_length += $length; + + if ($this->body_length > $this->max_body_size) { + return -1; + } + + $this->body .= $buffer; + + return $length; + } + + /** + * cURL callback to read HTTP headers. + * + * @param resource $ch cURL handler + * @param string $buffer Header line + * + * @return int Length of the buffer + */ + public function readHeaders($ch, $buffer) + { + $length = strlen($buffer); + + if ($buffer === "\r\n" || $buffer === "\n") { + ++$this->response_headers_count; + } else { + if (!isset($this->response_headers[$this->response_headers_count])) { + $this->response_headers[$this->response_headers_count] = ''; + } + + $this->response_headers[$this->response_headers_count] .= $buffer; + } + + return $length; + } + + /** + * cURL callback to passthrough the HTTP body to the client. + * + * If the function return -1, curl stop to read the HTTP response + * + * @param resource $ch cURL handler + * @param string $buffer Chunk of data + * + * @return int Length of the buffer + */ + public function passthroughBody($ch, $buffer) + { + // do it only at the beginning of a transmission + if ($this->body_length === 0) { + list($status, $headers) = HttpHeaders::parse(explode("\n", $this->response_headers[$this->response_headers_count - 1])); + + if ($this->isRedirection($status)) { + return $this->handleRedirection($headers['Location']); + } + + // Do not work with PHP-FPM + if (strpos(PHP_SAPI, 'cgi') !== false) { + header(':', true, $status); + } + + if (isset($headers['Content-Type'])) { + header('Content-Type:' .$headers['Content-Type']); + } + } + + $length = strlen($buffer); + $this->body_length += $length; + + echo $buffer; + + return $length; + } + + /** + * Prepare HTTP headers. + * + * @return string[] + */ + private function prepareHeaders() + { + $headers = array( + 'Connection: close', + ); + + if ($this->etag) { + $headers[] = 'If-None-Match: '.$this->etag; + $headers[] = 'A-IM: feed'; + } + + if ($this->last_modified) { + $headers[] = 'If-Modified-Since: '.$this->last_modified; + } + + $headers = array_merge($headers, $this->request_headers); + + return $headers; + } + + /** + * Prepare curl proxy context. + * + * @param resource $ch + * + * @return resource $ch + */ + private function prepareProxyContext($ch) + { + if ($this->proxy_hostname) { + Logger::setMessage(get_called_class().' Proxy: '.$this->proxy_hostname.':'.$this->proxy_port); + + curl_setopt($ch, CURLOPT_PROXYPORT, $this->proxy_port); + curl_setopt($ch, CURLOPT_PROXYTYPE, 'HTTP'); + curl_setopt($ch, CURLOPT_PROXY, $this->proxy_hostname); + + if ($this->proxy_username) { + Logger::setMessage(get_called_class().' Proxy credentials: Yes'); + curl_setopt($ch, CURLOPT_PROXYUSERPWD, $this->proxy_username.':'.$this->proxy_password); + } else { + Logger::setMessage(get_called_class().' Proxy credentials: No'); + } + } + + return $ch; + } + + /** + * Prepare curl auth context. + * + * @param resource $ch + * + * @return resource $ch + */ + private function prepareAuthContext($ch) + { + if ($this->username && $this->password) { + curl_setopt($ch, CURLOPT_USERPWD, $this->username.':'.$this->password); + } + + return $ch; + } + + /** + * Set write/header functions. + * + * @param resource $ch + * + * @return resource $ch + */ + private function prepareDownloadMode($ch) + { + $this->body = ''; + $this->response_headers = array(); + $this->response_headers_count = 0; + $write_function = 'readBody'; + $header_function = 'readHeaders'; + + if ($this->isPassthroughEnabled()) { + $write_function = 'passthroughBody'; + } + + curl_setopt($ch, CURLOPT_WRITEFUNCTION, array($this, $write_function)); + curl_setopt($ch, CURLOPT_HEADERFUNCTION, array($this, $header_function)); + + return $ch; + } + + /** + * Prepare curl context. + * + * @return resource + */ + private function prepareContext() + { + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_URL, $this->url); + curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->timeout); + curl_setopt($ch, CURLOPT_USERAGENT, $this->user_agent); + curl_setopt($ch, CURLOPT_HTTPHEADER, $this->prepareHeaders()); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false); + curl_setopt($ch, CURLOPT_ENCODING, ''); + curl_setopt($ch, CURLOPT_COOKIEJAR, 'php://memory'); + curl_setopt($ch, CURLOPT_COOKIEFILE, 'php://memory'); + + // Disable SSLv3 by enforcing TLSv1.x for curl >= 7.34.0 and < 7.39.0. + // Versions prior to 7.34 and at least when compiled against openssl + // interpret this parameter as "limit to TLSv1.0" which fails for sites + // which enforce TLS 1.1+. + // Starting with curl 7.39.0 SSLv3 is disabled by default. + $version = curl_version(); + if ($version['version_number'] >= 467456 && $version['version_number'] < 468736) { + curl_setopt($ch, CURLOPT_SSLVERSION, 1); + } + + $ch = $this->prepareDownloadMode($ch); + $ch = $this->prepareProxyContext($ch); + $ch = $this->prepareAuthContext($ch); + + return $ch; + } + + /** + * Execute curl context. + */ + private function executeContext() + { + $ch = $this->prepareContext(); + curl_exec($ch); + + Logger::setMessage(get_called_class().' cURL total time: '.curl_getinfo($ch, CURLINFO_TOTAL_TIME)); + Logger::setMessage(get_called_class().' cURL dns lookup time: '.curl_getinfo($ch, CURLINFO_NAMELOOKUP_TIME)); + Logger::setMessage(get_called_class().' cURL connect time: '.curl_getinfo($ch, CURLINFO_CONNECT_TIME)); + Logger::setMessage(get_called_class().' cURL speed download: '.curl_getinfo($ch, CURLINFO_SPEED_DOWNLOAD)); + Logger::setMessage(get_called_class().' cURL effective url: '.curl_getinfo($ch, CURLINFO_EFFECTIVE_URL)); + + $curl_errno = curl_errno($ch); + + if ($curl_errno) { + Logger::setMessage(get_called_class().' cURL error: '.curl_error($ch)); + curl_close($ch); + + $this->handleError($curl_errno); + } + + // Update the url if there where redirects + $this->url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL); + + curl_close($ch); + } + + /** + * Do the HTTP request. + * + * @return array HTTP response ['body' => ..., 'status' => ..., 'headers' => ...] + */ + public function doRequest() + { + $this->executeContext(); + + list($status, $headers) = HttpHeaders::parse(explode("\n", $this->response_headers[$this->response_headers_count - 1])); + + if ($this->isRedirection($status)) { + if (empty($headers['Location'])) { + $status = 200; + } else { + return $this->handleRedirection($headers['Location']); + } + } + + return array( + 'status' => $status, + 'body' => $this->body, + 'headers' => $headers, + ); + } + + /** + * Handle HTTP redirects + * + * @param string $location Redirected URL + * @return array + * @throws MaxRedirectException + */ + private function handleRedirection($location) + { + $result = array(); + $this->url = Url::resolve($location, $this->url); + $this->body = ''; + $this->body_length = 0; + $this->response_headers = array(); + $this->response_headers_count = 0; + + while (true) { + $this->nbRedirects++; + + if ($this->nbRedirects >= $this->max_redirects) { + throw new MaxRedirectException('Maximum number of redirections reached'); + } + + $result = $this->doRequest(); + + if ($this->isRedirection($result['status'])) { + $this->url = Url::resolve($result['headers']['Location'], $this->url); + $this->body = ''; + $this->body_length = 0; + $this->response_headers = array(); + $this->response_headers_count = 0; + } else { + break; + } + } + + return $result; + } + + /** + * Handle cURL errors (throw individual exceptions). + * + * We don't use constants because they are not necessary always available + * (depends of the version of libcurl linked to php) + * + * @see http://curl.haxx.se/libcurl/c/libcurl-errors.html + * + * @param int $errno cURL error code + * @throws InvalidCertificateException + * @throws InvalidUrlException + * @throws MaxRedirectException + * @throws MaxSizeException + * @throws TimeoutException + */ + private function handleError($errno) + { + switch ($errno) { + case 78: // CURLE_REMOTE_FILE_NOT_FOUND + throw new InvalidUrlException('Resource not found', $errno); + case 6: // CURLE_COULDNT_RESOLVE_HOST + throw new InvalidUrlException('Unable to resolve hostname', $errno); + case 7: // CURLE_COULDNT_CONNECT + throw new InvalidUrlException('Unable to connect to the remote host', $errno); + case 23: // CURLE_WRITE_ERROR + throw new MaxSizeException('Maximum response size exceeded', $errno); + case 28: // CURLE_OPERATION_TIMEDOUT + throw new TimeoutException('Operation timeout', $errno); + case 35: // CURLE_SSL_CONNECT_ERROR + case 51: // CURLE_PEER_FAILED_VERIFICATION + case 58: // CURLE_SSL_CERTPROBLEM + case 60: // CURLE_SSL_CACERT + case 59: // CURLE_SSL_CIPHER + case 64: // CURLE_USE_SSL_FAILED + case 66: // CURLE_SSL_ENGINE_INITFAILED + case 77: // CURLE_SSL_CACERT_BADFILE + case 83: // CURLE_SSL_ISSUER_ERROR + $msg = 'Invalid SSL certificate caused by CURL error number ' . $errno; + throw new InvalidCertificateException($msg, $errno); + case 47: // CURLE_TOO_MANY_REDIRECTS + throw new MaxRedirectException('Maximum number of redirections reached', $errno); + case 63: // CURLE_FILESIZE_EXCEEDED + throw new MaxSizeException('Maximum response size exceeded', $errno); + default: + throw new InvalidUrlException('Unable to fetch the URL', $errno); + } + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/ForbiddenException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/ForbiddenException.php new file mode 100644 index 00000000..c226e95a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/ForbiddenException.php @@ -0,0 +1,10 @@ +<?php + +namespace PicoFeed\Client; + +/** + * @author Bernhard Posselt + */ +class ForbiddenException extends ClientException +{ +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/HttpHeaders.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/HttpHeaders.php new file mode 100644 index 00000000..34b81399 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/HttpHeaders.php @@ -0,0 +1,79 @@ +<?php + +namespace PicoFeed\Client; + +use ArrayAccess; +use PicoFeed\Logging\Logger; + +/** + * Class to handle HTTP headers case insensitivity. + * + * @author Bernhard Posselt + * @author Frederic Guillot + */ +class HttpHeaders implements ArrayAccess +{ + private $headers = array(); + + public function __construct(array $headers) + { + foreach ($headers as $key => $value) { + $this->headers[strtolower($key)] = $value; + } + } + + public function offsetGet($offset) + { + return $this->offsetExists($offset) ? $this->headers[strtolower($offset)] : ''; + } + + public function offsetSet($offset, $value) + { + $this->headers[strtolower($offset)] = $value; + } + + public function offsetExists($offset) + { + return isset($this->headers[strtolower($offset)]); + } + + public function offsetUnset($offset) + { + unset($this->headers[strtolower($offset)]); + } + + /** + * Parse HTTP headers. + * + * @static + * + * @param array $lines List of headers + * + * @return array + */ + public static function parse(array $lines) + { + $status = 0; + $headers = array(); + + foreach ($lines as $line) { + if (strpos($line, 'HTTP/1') === 0) { + $headers = array(); + $status = (int) substr($line, 9, 3); + } elseif (strpos($line, ': ') !== false) { + list($name, $value) = explode(': ', $line); + if ($value) { + $headers[trim($name)] = trim($value); + } + } + } + + Logger::setMessage(get_called_class().' HTTP status code: '.$status); + + foreach ($headers as $name => $value) { + Logger::setMessage(get_called_class().' HTTP header: '.$name.' => '.$value); + } + + return array($status, new self($headers)); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/InvalidCertificateException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/InvalidCertificateException.php new file mode 100644 index 00000000..8d25d7e4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/InvalidCertificateException.php @@ -0,0 +1,12 @@ +<?php + +namespace PicoFeed\Client; + +/** + * InvalidCertificateException Exception. + * + * @author Frederic Guillot + */ +class InvalidCertificateException extends ClientException +{ +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/InvalidUrlException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/InvalidUrlException.php new file mode 100644 index 00000000..15534d98 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/InvalidUrlException.php @@ -0,0 +1,12 @@ +<?php + +namespace PicoFeed\Client; + +/** + * InvalidUrlException Exception. + * + * @author Frederic Guillot + */ +class InvalidUrlException extends ClientException +{ +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/MaxRedirectException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/MaxRedirectException.php new file mode 100644 index 00000000..0a221af6 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/MaxRedirectException.php @@ -0,0 +1,12 @@ +<?php + +namespace PicoFeed\Client; + +/** + * MaxRedirectException Exception. + * + * @author Frederic Guillot + */ +class MaxRedirectException extends ClientException +{ +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/MaxSizeException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/MaxSizeException.php new file mode 100644 index 00000000..201b22a6 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/MaxSizeException.php @@ -0,0 +1,12 @@ +<?php + +namespace PicoFeed\Client; + +/** + * MaxSizeException Exception. + * + * @author Frederic Guillot + */ +class MaxSizeException extends ClientException +{ +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/Stream.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/Stream.php new file mode 100644 index 00000000..2e91d472 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/Stream.php @@ -0,0 +1,205 @@ +<?php + +namespace PicoFeed\Client; + +use PicoFeed\Logging\Logger; + +/** + * Stream context HTTP client. + * + * @author Frederic Guillot + */ +class Stream extends Client +{ + /** + * Prepare HTTP headers. + * + * @return string[] + */ + private function prepareHeaders() + { + $headers = array( + 'Connection: close', + 'User-Agent: '.$this->user_agent, + ); + + // disable compression in passthrough mode. It could result in double + // compressed content which isn't decodeable by browsers + if (function_exists('gzdecode') && !$this->isPassthroughEnabled()) { + $headers[] = 'Accept-Encoding: gzip'; + } + + if ($this->etag) { + $headers[] = 'If-None-Match: '.$this->etag; + $headers[] = 'A-IM: feed'; + } + + if ($this->last_modified) { + $headers[] = 'If-Modified-Since: '.$this->last_modified; + } + + if ($this->proxy_username) { + $headers[] = 'Proxy-Authorization: Basic '.base64_encode($this->proxy_username.':'.$this->proxy_password); + } + + if ($this->username && $this->password) { + $headers[] = 'Authorization: Basic '.base64_encode($this->username.':'.$this->password); + } + + $headers = array_merge($headers, $this->request_headers); + + return $headers; + } + + /** + * Construct the final URL from location headers. + * + * @param array $headers List of HTTP response header + */ + private function setEffectiveUrl($headers) + { + foreach ($headers as $header) { + if (stripos($header, 'Location') === 0) { + list(, $value) = explode(': ', $header); + + $this->url = Url::resolve($value, $this->url); + } + } + } + + /** + * Prepare stream context. + * + * @return array + */ + private function prepareContext() + { + $context = array( + 'http' => array( + 'method' => 'GET', + 'protocol_version' => 1.1, + 'timeout' => $this->timeout, + 'max_redirects' => $this->max_redirects, + ), + ); + + if ($this->proxy_hostname) { + Logger::setMessage(get_called_class().' Proxy: '.$this->proxy_hostname.':'.$this->proxy_port); + + $context['http']['proxy'] = 'tcp://'.$this->proxy_hostname.':'.$this->proxy_port; + $context['http']['request_fulluri'] = true; + + if ($this->proxy_username) { + Logger::setMessage(get_called_class().' Proxy credentials: Yes'); + } else { + Logger::setMessage(get_called_class().' Proxy credentials: No'); + } + } + + $context['http']['header'] = implode("\r\n", $this->prepareHeaders()); + + return $context; + } + + /** + * Do the HTTP request. + * + * @return array HTTP response ['body' => ..., 'status' => ..., 'headers' => ...] + * @throws InvalidUrlException + * @throws MaxSizeException + * @throws TimeoutException + */ + public function doRequest() + { + $body = ''; + + // Create context + $context = stream_context_create($this->prepareContext()); + + // Make HTTP request + $stream = @fopen($this->url, 'r', false, $context); + if (!is_resource($stream)) { + throw new InvalidUrlException('Unable to establish a connection'); + } + + // Get HTTP headers response + $metadata = stream_get_meta_data($stream); + list($status, $headers) = HttpHeaders::parse($metadata['wrapper_data']); + + if ($this->isPassthroughEnabled()) { + header(':', true, $status); + + if (isset($headers['Content-Type'])) { + header('Content-Type: '.$headers['Content-Type']); + } + + fpassthru($stream); + } else { + // Get the entire body until the max size + $body = stream_get_contents($stream, $this->max_body_size + 1); + + // If the body size is too large abort everything + if (strlen($body) > $this->max_body_size) { + throw new MaxSizeException('Content size too large'); + } + + if ($metadata['timed_out']) { + throw new TimeoutException('Operation timeout'); + } + } + + fclose($stream); + + $this->setEffectiveUrl($metadata['wrapper_data']); + + return array( + 'status' => $status, + 'body' => $this->decodeBody($body, $headers), + 'headers' => $headers, + ); + } + + /** + * Decode body response according to the HTTP headers. + * + * @param string $body Raw body + * @param HttpHeaders $headers HTTP headers + * + * @return string + */ + public function decodeBody($body, HttpHeaders $headers) + { + if (isset($headers['Transfer-Encoding']) && $headers['Transfer-Encoding'] === 'chunked') { + $body = $this->decodeChunked($body); + } + + if (isset($headers['Content-Encoding']) && $headers['Content-Encoding'] === 'gzip') { + $body = gzdecode($body); + } + + return $body; + } + + /** + * Decode a chunked body. + * + * @param string $str Raw body + * + * @return string Decoded body + */ + public function decodeChunked($str) + { + for ($result = ''; !empty($str); $str = trim($str)) { + + // Get the chunk length + $pos = strpos($str, "\r\n"); + $len = hexdec(substr($str, 0, $pos)); + + // Append the chunk to the result + $result .= substr($str, $pos + 2, $len); + $str = substr($str, $pos + 2 + $len); + } + + return $result; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/TimeoutException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/TimeoutException.php new file mode 100644 index 00000000..da98da12 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/TimeoutException.php @@ -0,0 +1,12 @@ +<?php + +namespace PicoFeed\Client; + +/** + * TimeoutException Exception. + * + * @author Frederic Guillot + */ +class TimeoutException extends ClientException +{ +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/UnauthorizedException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/UnauthorizedException.php new file mode 100644 index 00000000..81898b99 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/UnauthorizedException.php @@ -0,0 +1,10 @@ +<?php + +namespace PicoFeed\Client; + +/** + * @author Bernhard Posselt + */ +class UnauthorizedException extends ClientException +{ +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Client/Url.php b/vendor/miniflux/picofeed/lib/PicoFeed/Client/Url.php new file mode 100644 index 00000000..a9337988 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Client/Url.php @@ -0,0 +1,290 @@ +<?php + +namespace PicoFeed\Client; + +/** + * URL class. + * + * @author Frederic Guillot + */ +class Url +{ + /** + * URL. + * + * @var string + */ + private $url = ''; + + /** + * URL components. + * + * @var array + */ + private $components = array(); + + /** + * Constructor. + * + * @param string $url URL + */ + public function __construct($url) + { + $this->url = $url; + $this->components = parse_url($url) ?: array(); + + // Issue with PHP < 5.4.7 and protocol relative url + if (version_compare(PHP_VERSION, '5.4.7', '<') && $this->isProtocolRelative()) { + $pos = strpos($this->components['path'], '/', 2); + + if ($pos === false) { + $pos = strlen($this->components['path']); + } + + $this->components['host'] = substr($this->components['path'], 2, $pos - 2); + $this->components['path'] = substr($this->components['path'], $pos); + } + } + + /** + * Shortcut method to get an absolute url from relative url. + * + * @static + * + * @param mixed $item_url Unknown url (can be relative or not) + * @param mixed $website_url Website url + * + * @return string + */ + public static function resolve($item_url, $website_url) + { + $link = is_string($item_url) ? new self($item_url) : $item_url; + $website = is_string($website_url) ? new self($website_url) : $website_url; + + if ($link->isRelativeUrl()) { + if ($link->isRelativePath()) { + return $link->getAbsoluteUrl($website->getBaseUrl($website->getBasePath())); + } + + return $link->getAbsoluteUrl($website->getBaseUrl()); + } elseif ($link->isProtocolRelative()) { + $link->setScheme($website->getScheme()); + } + + return $link->getAbsoluteUrl(); + } + + /** + * Shortcut method to get a base url. + * + * @static + * + * @param string $url + * + * @return string + */ + public static function base($url) + { + $link = new self($url); + + return $link->getBaseUrl(); + } + + /** + * Get the base URL. + * + * @param string $suffix Add a suffix to the url + * + * @return string + */ + public function getBaseUrl($suffix = '') + { + return $this->hasHost() ? $this->getScheme('://').$this->getHost().$this->getPort(':').$suffix : ''; + } + + /** + * Get the absolute URL. + * + * @param string $base_url Use this url as base url + * + * @return string + */ + public function getAbsoluteUrl($base_url = '') + { + if ($base_url) { + $base = new self($base_url); + $url = $base->getAbsoluteUrl().substr($this->getFullPath(), 1); + } else { + $url = $this->hasHost() ? $this->getBaseUrl().$this->getFullPath() : ''; + } + + return $url; + } + + /** + * Return true if the url is relative. + * + * @return bool + */ + public function isRelativeUrl() + { + return !$this->hasScheme() && !$this->isProtocolRelative(); + } + + /** + * Return true if the path is relative. + * + * @return bool + */ + public function isRelativePath() + { + $path = $this->getPath(); + + return empty($path) || $path{0} + !== '/'; + } + + /** + * Filters the path of a URI. + * + * Imported from Guzzle library: https://github.com/guzzle/psr7/blob/master/src/Uri.php#L568-L582 + * + * @param $path + * + * @return string + */ + public function filterPath($path, $charUnreserved = 'a-zA-Z0-9_\-\.~', $charSubDelims = '!\$&\'\(\)\*\+,;=') + { + return preg_replace_callback( + '/(?:[^'.$charUnreserved.$charSubDelims.':@\/%]+|%(?![A-Fa-f0-9]{2}))/', + function (array $matches) { return rawurlencode($matches[0]); }, + $path + ); + } + + /** + * Get the path. + * + * @return string + */ + public function getPath() + { + return $this->filterPath(empty($this->components['path']) ? '' : $this->components['path']); + } + + /** + * Get the base path. + * + * @return string + */ + public function getBasePath() + { + $current_path = $this->getPath(); + + $path = $this->isRelativePath() ? '/' : ''; + $path .= substr($current_path, -1) === '/' ? $current_path : dirname($current_path); + + return preg_replace('/\\\\\/|\/\//', '/', $path.'/'); + } + + /** + * Get the full path (path + querystring + fragment). + * + * @return string + */ + public function getFullPath() + { + $path = $this->isRelativePath() ? '/' : ''; + $path .= $this->getPath(); + $path .= empty($this->components['query']) ? '' : '?'.$this->components['query']; + $path .= empty($this->components['fragment']) ? '' : '#'.$this->components['fragment']; + + return $path; + } + + /** + * Get the hostname. + * + * @return string + */ + public function getHost() + { + return empty($this->components['host']) ? '' : $this->components['host']; + } + + /** + * Return true if the url has a hostname. + * + * @return bool + */ + public function hasHost() + { + return !empty($this->components['host']); + } + + /** + * Get the scheme. + * + * @param string $suffix Suffix to add when there is a scheme + * + * @return string + */ + public function getScheme($suffix = '') + { + return ($this->hasScheme() ? $this->components['scheme'] : 'http').$suffix; + } + + /** + * Set the scheme. + * + * @param string $scheme Set a scheme + * + * @return string + */ + public function setScheme($scheme) + { + $this->components['scheme'] = $scheme; + } + + /** + * Return true if the url has a scheme. + * + * @return bool + */ + public function hasScheme() + { + return !empty($this->components['scheme']); + } + + /** + * Get the port. + * + * @param string $prefix Prefix to add when there is a port + * + * @return string + */ + public function getPort($prefix = '') + { + return $this->hasPort() ? $prefix.$this->components['port'] : ''; + } + + /** + * Return true if the url has a port. + * + * @return bool + */ + public function hasPort() + { + return !empty($this->components['port']); + } + + /** + * Return true if the url is protocol relative (start with //). + * + * @return bool + */ + public function isProtocolRelative() + { + return strpos($this->url, '//') === 0; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Encoding/Encoding.php b/vendor/miniflux/picofeed/lib/PicoFeed/Encoding/Encoding.php new file mode 100644 index 00000000..fa0917e8 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Encoding/Encoding.php @@ -0,0 +1,33 @@ +<?php + +namespace PicoFeed\Encoding; + +/** + * Encoding class. + */ +class Encoding +{ + public static function convert($input, $encoding) + { + if ($encoding === 'utf-8' || $encoding === '') { + return $input; + } + + // suppress all notices since it isn't possible to silence only the + // notice "Wrong charset, conversion from $in_encoding to $out_encoding is not allowed" + set_error_handler(function () {}, E_NOTICE); + + // convert input to utf-8 and strip invalid characters + $value = iconv($encoding, 'UTF-8//IGNORE', $input); + + // stop silencing of notices + restore_error_handler(); + + // return input if something went wrong, maybe it's usable anyway + if ($value === false) { + return $input; + } + + return $value; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Attribute.php b/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Attribute.php new file mode 100644 index 00000000..f0021532 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Attribute.php @@ -0,0 +1,700 @@ +<?php + +namespace PicoFeed\Filter; + +use PicoFeed\Client\Url; + +/** + * Attribute Filter class. + * + * @author Frederic Guillot + */ +class Attribute +{ + /** + * Image proxy url. + * + * @var string + */ + private $image_proxy_url = ''; + + /** + * Image proxy callback. + * + * @var \Closure|null + */ + private $image_proxy_callback = null; + + /** + * limits the image proxy usage to this protocol. + * + * @var string + */ + private $image_proxy_limit_protocol = ''; + + /** + * Tags and attribute whitelist. + * + * @var array + */ + private $attribute_whitelist = array( + 'audio' => array('controls', 'src'), + 'video' => array('poster', 'controls', 'height', 'width', 'src'), + 'source' => array('src', 'type'), + 'dt' => array(), + 'dd' => array(), + 'dl' => array(), + 'table' => array(), + 'caption' => array(), + 'tr' => array(), + 'th' => array(), + 'td' => array(), + 'tbody' => array(), + 'thead' => array(), + 'h1' => array(), + 'h2' => array(), + 'h3' => array(), + 'h4' => array(), + 'h5' => array(), + 'h6' => array(), + 'strong' => array(), + 'em' => array(), + 'code' => array(), + 'pre' => array(), + 'blockquote' => array(), + 'p' => array(), + 'ul' => array(), + 'li' => array(), + 'ol' => array(), + 'br' => array(), + 'del' => array(), + 'a' => array('href'), + 'img' => array('src', 'title', 'alt'), + 'figure' => array(), + 'figcaption' => array(), + 'cite' => array(), + 'time' => array('datetime'), + 'abbr' => array('title'), + 'iframe' => array('width', 'height', 'frameborder', 'src', 'allowfullscreen'), + 'q' => array('cite'), + ); + + /** + * Scheme whitelist. + * + * For a complete list go to http://en.wikipedia.org/wiki/URI_scheme + * + * @var array + */ + private $scheme_whitelist = array( + 'bitcoin:', + 'callto:', + 'ed2k://', + 'facetime://', + 'feed:', + 'ftp://', + 'geo:', + 'git://', + 'http://', + 'https://', + 'irc://', + 'irc6://', + 'ircs://', + 'jabber:', + 'magnet:', + 'mailto:', + 'nntp://', + 'rtmp://', + 'sftp://', + 'sip:', + 'sips:', + 'skype:', + 'smb://', + 'sms:', + 'spotify:', + 'ssh:', + 'steam:', + 'svn://', + 'tel:', + ); + + /** + * Iframe source whitelist, everything else is ignored. + * + * @var array + */ + private $iframe_whitelist = array( + 'http://www.youtube.com', + 'https://www.youtube.com', + 'http://player.vimeo.com', + 'https://player.vimeo.com', + 'http://www.dailymotion.com', + 'https://www.dailymotion.com', + 'http://vk.com', + 'https://vk.com', + ); + + /** + * Blacklisted resources. + * + * @var array + */ + private $media_blacklist = array( + 'api.flattr.com', + 'feeds.feedburner.com', + 'share.feedsportal.com', + 'da.feedsportal.com', + 'rc.feedsportal.com', + 'rss.feedsportal.com', + 'res.feedsportal.com', + 'res1.feedsportal.com', + 'res2.feedsportal.com', + 'res3.feedsportal.com', + 'pi.feedsportal.com', + 'rss.nytimes.com', + 'feeds.wordpress.com', + 'stats.wordpress.com', + 'rss.cnn.com', + 'twitter.com/home?status=', + 'twitter.com/share', + 'twitter_icon_large.png', + 'www.facebook.com/sharer.php', + 'facebook_icon_large.png', + 'plus.google.com/share', + 'www.gstatic.com/images/icons/gplus-16.png', + 'www.gstatic.com/images/icons/gplus-32.png', + 'www.gstatic.com/images/icons/gplus-64.png', + ); + + /** + * Attributes used for external resources. + * + * @var array + */ + private $media_attributes = array( + 'src', + 'href', + 'poster', + ); + + /** + * Attributes that must be integer. + * + * @var array + */ + private $integer_attributes = array( + 'width', + 'height', + 'frameborder', + ); + + /** + * Mandatory attributes for specified tags. + * + * @var array + */ + private $required_attributes = array( + 'a' => array('href'), + 'img' => array('src'), + 'iframe' => array('src'), + 'audio' => array('src'), + 'source' => array('src'), + ); + + /** + * Add attributes to specified tags. + * + * @var array + */ + private $add_attributes = array( + 'a' => array('rel' => 'noreferrer', 'target' => '_blank'), + 'video' => array('controls' => 'true'), + ); + + /** + * List of filters to apply. + * + * @var array + */ + private $filters = array( + 'filterAllowedAttribute', + 'filterIntegerAttribute', + 'rewriteAbsoluteUrl', + 'filterIframeAttribute', + 'filterBlacklistResourceAttribute', + 'filterProtocolUrlAttribute', + 'rewriteImageProxyUrl', + 'secureIframeSrc', + 'removeYouTubeAutoplay', + ); + + /** + * Add attributes to specified tags. + * + * @var \PicoFeed\Client\Url + */ + private $website; + + /** + * Constructor. + * + * @param \PicoFeed\Client\Url $website Website url instance + */ + public function __construct(Url $website) + { + $this->website = $website; + } + + /** + * Apply filters to the attributes list. + * + * @param string $tag Tag name + * @param array $attributes Attributes dictionary + * + * @return array Filtered attributes + */ + public function filter($tag, array $attributes) + { + foreach ($attributes as $attribute => &$value) { + foreach ($this->filters as $filter) { + if (!$this->$filter($tag, $attribute, $value)) { + unset($attributes[$attribute]); + break; + } + } + } + + return $attributes; + } + + /** + * Return true if the value is allowed (remove not allowed attributes). + * + * @param string $tag Tag name + * @param string $attribute Attribute name + * @param string $value Attribute value + * + * @return bool + */ + public function filterAllowedAttribute($tag, $attribute, $value) + { + return isset($this->attribute_whitelist[$tag]) && in_array($attribute, $this->attribute_whitelist[$tag]); + } + + /** + * Return true if the value is not integer (remove attributes that should have an integer value). + * + * @param string $tag Tag name + * @param string $attribute Attribute name + * @param string $value Attribute value + * + * @return bool + */ + public function filterIntegerAttribute($tag, $attribute, $value) + { + if (in_array($attribute, $this->integer_attributes)) { + return ctype_digit($value); + } + + return true; + } + + /** + * Return true if the iframe source is allowed (remove not allowed iframe). + * + * @param string $tag Tag name + * @param string $attribute Attribute name + * @param string $value Attribute value + * + * @return bool + */ + public function filterIframeAttribute($tag, $attribute, $value) + { + if ($tag === 'iframe' && $attribute === 'src') { + foreach ($this->iframe_whitelist as $url) { + if (strpos($value, $url) === 0) { + return true; + } + } + + return false; + } + + return true; + } + + /** + * Return true if the resource is not blacklisted (remove blacklisted resource attributes). + * + * @param string $tag Tag name + * @param string $attribute Attribute name + * @param string $value Attribute value + * + * @return bool + */ + public function filterBlacklistResourceAttribute($tag, $attribute, $value) + { + if ($this->isResource($attribute) && $this->isBlacklistedMedia($value)) { + return false; + } + + return true; + } + + /** + * Convert all relative links to absolute url. + * + * @param string $tag Tag name + * @param string $attribute Attribute name + * @param string $value Attribute value + * + * @return bool + */ + public function rewriteAbsoluteUrl($tag, $attribute, &$value) + { + if ($this->isResource($attribute)) { + $value = Url::resolve($value, $this->website); + } + + return true; + } + + /** + * Turns iframes' src attribute from http to https to prevent + * mixed active content. + * + * @param string $tag Tag name + * @param array $attribute Atttributes name + * @param string $value Attribute value + * + * @return bool + */ + public function secureIframeSrc($tag, $attribute, &$value) + { + if ($tag === 'iframe' && $attribute === 'src' && strpos($value, 'http://') === 0) { + $value = substr_replace($value, 's', 4, 0); + } + + return true; + } + + /** + * Removes YouTube autoplay from iframes. + * + * @param string $tag Tag name + * @param array $attribute Atttributes name + * @param string $value Attribute value + * + * @return bool + */ + public function removeYouTubeAutoplay($tag, $attribute, &$value) + { + $regex = '%^(https://(?:www\.)?youtube.com/.*\?.*autoplay=)(1)(.*)%i'; + if ($tag === 'iframe' && $attribute === 'src' && preg_match($regex, $value)) { + $value = preg_replace($regex, '${1}0$3', $value); + } + + return true; + } + + /** + * Rewrite image url to use with a proxy. + * + * @param string $tag Tag name + * @param string $attribute Attribute name + * @param string $value Attribute value + * + * @return bool + */ + public function rewriteImageProxyUrl($tag, $attribute, &$value) + { + if ($tag === 'img' && $attribute === 'src' + && !($this->image_proxy_limit_protocol !== '' && stripos($value, $this->image_proxy_limit_protocol.':') !== 0)) { + if ($this->image_proxy_url) { + $value = sprintf($this->image_proxy_url, rawurlencode($value)); + } elseif (is_callable($this->image_proxy_callback)) { + $value = call_user_func($this->image_proxy_callback, $value); + } + } + + return true; + } + + /** + * Return true if the scheme is authorized. + * + * @param string $tag Tag name + * @param string $attribute Attribute name + * @param string $value Attribute value + * + * @return bool + */ + public function filterProtocolUrlAttribute($tag, $attribute, $value) + { + if ($this->isResource($attribute) && !$this->isAllowedProtocol($value)) { + return false; + } + + return true; + } + + /** + * Automatically add/override some attributes for specific tags. + * + * @param string $tag Tag name + * @param array $attributes Attributes list + * + * @return array + */ + public function addAttributes($tag, array $attributes) + { + if (isset($this->add_attributes[$tag])) { + $attributes += $this->add_attributes[$tag]; + } + + return $attributes; + } + + /** + * Return true if all required attributes are present. + * + * @param string $tag Tag name + * @param array $attributes Attributes list + * + * @return bool + */ + public function hasRequiredAttributes($tag, array $attributes) + { + if (isset($this->required_attributes[$tag])) { + foreach ($this->required_attributes[$tag] as $attribute) { + if (!isset($attributes[$attribute])) { + return false; + } + } + } + + return true; + } + + /** + * Check if an attribute name is an external resource. + * + * @param string $attribute Attribute name + * + * @return bool + */ + public function isResource($attribute) + { + return in_array($attribute, $this->media_attributes); + } + + /** + * Detect if the protocol is allowed or not. + * + * @param string $value Attribute value + * + * @return bool + */ + public function isAllowedProtocol($value) + { + foreach ($this->scheme_whitelist as $protocol) { + if (strpos($value, $protocol) === 0) { + return true; + } + } + + return false; + } + + /** + * Detect if an url is blacklisted. + * + * @param string $resource Attribute value (URL) + * + * @return bool + */ + public function isBlacklistedMedia($resource) + { + foreach ($this->media_blacklist as $name) { + if (strpos($resource, $name) !== false) { + return true; + } + } + + return false; + } + + /** + * Convert the attribute list to html. + * + * @param array $attributes Attributes + * + * @return string + */ + public function toHtml(array $attributes) + { + $html = array(); + + foreach ($attributes as $attribute => $value) { + $html[] = sprintf('%s="%s"', $attribute, Filter::escape($value)); + } + + return implode(' ', $html); + } + + /** + * Set whitelisted tags and attributes for each tag. + * + * @param array $values List of tags: ['video' => ['src', 'cover'], 'img' => ['src']] + * + * @return Attribute + */ + public function setWhitelistedAttributes(array $values) + { + $this->attribute_whitelist = $values ?: $this->attribute_whitelist; + + return $this; + } + + /** + * Set scheme whitelist. + * + * @param array $values List of scheme: ['http://', 'ftp://'] + * + * @return Attribute + */ + public function setSchemeWhitelist(array $values) + { + $this->scheme_whitelist = $values ?: $this->scheme_whitelist; + + return $this; + } + + /** + * Set media attributes (used to load external resources). + * + * @param array $values List of values: ['src', 'href'] + * + * @return Attribute + */ + public function setMediaAttributes(array $values) + { + $this->media_attributes = $values ?: $this->media_attributes; + + return $this; + } + + /** + * Set blacklisted external resources. + * + * @param array $values List of tags: ['http://google.com/', '...'] + * + * @return Attribute + */ + public function setMediaBlacklist(array $values) + { + $this->media_blacklist = $values ?: $this->media_blacklist; + + return $this; + } + + /** + * Set mandatory attributes for whitelisted tags. + * + * @param array $values List of tags: ['img' => 'src'] + * + * @return Attribute + */ + public function setRequiredAttributes(array $values) + { + $this->required_attributes = $values ?: $this->required_attributes; + + return $this; + } + + /** + * Set attributes to automatically to specific tags. + * + * @param array $values List of tags: ['a' => 'target="_blank"'] + * + * @return Attribute + */ + public function setAttributeOverrides(array $values) + { + $this->add_attributes = $values ?: $this->add_attributes; + + return $this; + } + + /** + * Set attributes that must be an integer. + * + * @param array $values List of tags: ['width', 'height'] + * + * @return Attribute + */ + public function setIntegerAttributes(array $values) + { + $this->integer_attributes = $values ?: $this->integer_attributes; + + return $this; + } + + /** + * Set allowed iframe resources. + * + * @param array $values List of tags: ['http://www.youtube.com'] + * + * @return Attribute + */ + public function setIframeWhitelist(array $values) + { + $this->iframe_whitelist = $values ?: $this->iframe_whitelist; + + return $this; + } + + /** + * Set image proxy URL. + * + * The original image url will be urlencoded + * + * @param string $url Proxy URL + * + * @return Attribute + */ + public function setImageProxyUrl($url) + { + $this->image_proxy_url = $url ?: $this->image_proxy_url; + + return $this; + } + + /** + * Set image proxy callback. + * + * @param \Closure $callback + * + * @return Attribute + */ + public function setImageProxyCallback($callback) + { + $this->image_proxy_callback = $callback ?: $this->image_proxy_callback; + + return $this; + } + + /** + * Set image proxy protocol restriction. + * + * @param string $value + * + * @return Attribute + */ + public function setImageProxyProtocol($value) + { + $this->image_proxy_limit_protocol = $value ?: $this->image_proxy_limit_protocol; + + return $this; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Filter.php b/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Filter.php new file mode 100644 index 00000000..bae2aff0 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Filter.php @@ -0,0 +1,155 @@ +<?php + +namespace PicoFeed\Filter; + +/** + * Filter class. + * + * @author Frederic Guillot + */ +class Filter +{ + /** + * Get the Html filter instance. + * + * @static + * + * @param string $html HTML content + * @param string $website Site URL (used to build absolute URL) + * + * @return Html + */ + public static function html($html, $website) + { + $filter = new Html($html, $website); + + return $filter; + } + + /** + * Escape HTML content. + * + * @static + * + * @return string + */ + public static function escape($content) + { + return htmlspecialchars($content, ENT_QUOTES, 'UTF-8', false); + } + + /** + * Remove HTML tags. + * + * @param string $data Input data + * + * @return string + */ + public function removeHTMLTags($data) + { + return preg_replace('~<(?:!DOCTYPE|/?(?:html|head|body))[^>]*>\s*~i', '', $data); + } + + /** + * Remove the XML tag from a document. + * + * @static + * + * @param string $data Input data + * + * @return string + */ + public static function stripXmlTag($data) + { + if (strpos($data, '<?xml') !== false) { + $data = ltrim(substr($data, strpos($data, '?>') + 2)); + } + + do { + $pos = strpos($data, '<?xml-stylesheet '); + + if ($pos !== false) { + $data = ltrim(substr($data, strpos($data, '?>') + 2)); + } + } while ($pos !== false && $pos < 200); + + return $data; + } + + /** + * Strip head tag from the HTML content. + * + * @static + * + * @param string $data Input data + * + * @return string + */ + public static function stripHeadTags($data) + { + return preg_replace('@<head[^>]*?>.*?</head>@siu', '', $data); + } + + /** + * Trim whitespace from the begining, the end and inside a string and don't break utf-8 string. + * + * @static + * + * @param string $value Raw data + * + * @return string Normalized data + */ + public static function stripWhiteSpace($value) + { + $value = str_replace("\r", ' ', $value); + $value = str_replace("\t", ' ', $value); + $value = str_replace("\n", ' ', $value); + // $value = preg_replace('/\s+/', ' ', $value); <= break utf-8 + return trim($value); + } + + /** + * Fixes before XML parsing. + * + * @static + * + * @param string $data Raw data + * + * @return string Normalized data + */ + public static function normalizeData($data) + { + $entities = array( + '/(&#)(\d+);/m', // decimal encoded + '/(&#x)([a-f0-9]+);/mi', // hex encoded + ); + + // strip invalid XML 1.0 characters which are encoded as entities + $data = preg_replace_callback($entities, function ($matches) { + $code_point = $matches[2]; + + // convert hex entity to decimal + if (strtolower($matches[1]) === '&#x') { + $code_point = hexdec($code_point); + } + + $code_point = (int) $code_point; + + // replace invalid characters + if ($code_point < 9 + || ($code_point > 10 && $code_point < 13) + || ($code_point > 13 && $code_point < 32) + || ($code_point > 55295 && $code_point < 57344) + || ($code_point > 65533 && $code_point < 65536) + || $code_point > 1114111 + ) { + return ''; + }; + + return $matches[0]; + }, $data); + + // strip every utf-8 character than isn't in the range of valid XML 1.0 characters + return (string) preg_replace('/[^\x{0009}\x{000A}\x{000D}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]/u', '', $data); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Html.php b/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Html.php new file mode 100644 index 00000000..0ccc192f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Html.php @@ -0,0 +1,243 @@ +<?php + +namespace PicoFeed\Filter; + +use PicoFeed\Config\Config; +use PicoFeed\Client\Url; +use PicoFeed\Scraper\RuleLoader; +use PicoFeed\Parser\XmlParser; + +/** + * HTML Filter class. + * + * @author Frederic Guillot + */ +class Html +{ + /** + * Config object. + * + * @var \PicoFeed\Config\Config + */ + private $config; + + /** + * Unfiltered XML data. + * + * @var string + */ + private $input = ''; + + /** + * Filtered XML data. + * + * @var string + */ + private $output = ''; + + /** + * List of empty tags. + * + * @var array + */ + private $empty_tags = array(); + + /** + * Empty flag. + * + * @var bool + */ + private $empty = true; + + /** + * Tag instance. + * + * @var \PicoFeed\Filter\Tag + */ + public $tag = ''; + + /** + * Attribute instance. + * + * @var \PicoFeed\Filter\Attribute + */ + public $attribute = ''; + + /** + * The website to filter. + * + * @var string + */ + private $website; + + /** + * Initialize the filter, all inputs data must be encoded in UTF-8 before. + * + * @param string $html HTML content + * @param string $website Site URL (used to build absolute URL) + */ + public function __construct($html, $website) + { + $this->config = new Config(); + $this->input = XmlParser::htmlToXml($html); + $this->output = ''; + $this->tag = new Tag($this->config); + $this->website = $website; + $this->attribute = new Attribute(new Url($website)); + } + + /** + * Set config object. + * + * @param \PicoFeed\Config\Config $config Config instance + * + * @return \PicoFeed\Filter\Html + */ + public function setConfig($config) + { + $this->config = $config; + + if ($this->config !== null) { + $this->attribute->setImageProxyCallback($this->config->getFilterImageProxyCallback()); + $this->attribute->setImageProxyUrl($this->config->getFilterImageProxyUrl()); + $this->attribute->setImageProxyProtocol($this->config->getFilterImageProxyProtocol()); + $this->attribute->setIframeWhitelist($this->config->getFilterIframeWhitelist(array())); + $this->attribute->setIntegerAttributes($this->config->getFilterIntegerAttributes(array())); + $this->attribute->setAttributeOverrides($this->config->getFilterAttributeOverrides(array())); + $this->attribute->setRequiredAttributes($this->config->getFilterRequiredAttributes(array())); + $this->attribute->setMediaBlacklist($this->config->getFilterMediaBlacklist(array())); + $this->attribute->setMediaAttributes($this->config->getFilterMediaAttributes(array())); + $this->attribute->setSchemeWhitelist($this->config->getFilterSchemeWhitelist(array())); + $this->attribute->setWhitelistedAttributes($this->config->getFilterWhitelistedTags(array())); + $this->tag->setWhitelistedTags(array_keys($this->config->getFilterWhitelistedTags(array()))); + } + + return $this; + } + + /** + * Run tags/attributes filtering. + * + * @return string + */ + public function execute() + { + $this->preFilter(); + + $parser = xml_parser_create(); + + xml_set_object($parser, $this); + xml_set_element_handler($parser, 'startTag', 'endTag'); + xml_set_character_data_handler($parser, 'dataTag'); + xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false); + xml_parse($parser, $this->input, true); + xml_parser_free($parser); + + $this->postFilter(); + + return $this->output; + } + + /** + * Called before XML parsing. + */ + public function preFilter() + { + $this->input = $this->tag->removeBlacklistedTags($this->input); + } + + /** + * Called after XML parsing. + */ + public function postFilter() + { + $this->output = $this->tag->removeEmptyTags($this->output); + $this->output = $this->filterRules($this->output); + $this->output = $this->tag->removeMultipleBreakTags($this->output); + $this->output = trim($this->output); + } + + /** + * Called after XML parsing. + * + * @param string $content the content that should be filtered + */ + public function filterRules($content) + { + // the constructor should require a config, then this if can be removed + if ($this->config === null) { + $config = new Config(); + } else { + $config = $this->config; + } + + $loader = new RuleLoader($config); + $rules = $loader->getRules($this->website); + + $url = new Url($this->website); + $sub_url = $url->getFullPath(); + + if (isset($rules['filter'])) { + foreach ($rules['filter'] as $pattern => $rule) { + if (preg_match($pattern, $sub_url)) { + foreach ($rule as $search => $replace) { + $content = preg_replace($search, $replace, $content); + } + } + } + } + + return $content; + } + + /** + * Parse opening tag. + * + * @param resource $parser XML parser + * @param string $tag Tag name + * @param array $attributes Tag attributes + */ + public function startTag($parser, $tag, array $attributes) + { + $this->empty = true; + + if ($this->tag->isAllowed($tag, $attributes)) { + $attributes = $this->attribute->filter($tag, $attributes); + + if ($this->attribute->hasRequiredAttributes($tag, $attributes)) { + $attributes = $this->attribute->addAttributes($tag, $attributes); + + $this->output .= $this->tag->openHtmlTag($tag, $this->attribute->toHtml($attributes)); + $this->empty = false; + } + } + + $this->empty_tags[] = $this->empty; + } + + /** + * Parse closing tag. + * + * @param resource $parser XML parser + * @param string $tag Tag name + */ + public function endTag($parser, $tag) + { + if (!array_pop($this->empty_tags) && $this->tag->isAllowedTag($tag)) { + $this->output .= $this->tag->closeHtmlTag($tag); + } + } + + /** + * Parse tag content. + * + * @param resource $parser XML parser + * @param string $content Tag content + */ + public function dataTag($parser, $content) + { + // Replace with normal space + $content = str_replace("\xc2\xa0", ' ', $content); + $this->output .= Filter::escape($content); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Tag.php b/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Tag.php new file mode 100644 index 00000000..84a298a7 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Filter/Tag.php @@ -0,0 +1,218 @@ +<?php + +namespace PicoFeed\Filter; + +use DOMXPath; +use PicoFeed\Base; +use PicoFeed\Parser\XmlParser; + +/** + * Tag Filter class. + * + * @author Frederic Guillot + */ +class Tag extends Base +{ + /** + * Tags blacklist (Xpath expressions). + * + * @var array + */ + private $tag_blacklist = array( + '//script', + '//style', + ); + + /** + * Tags whitelist. + * + * @var array + */ + private $tag_whitelist = array( + 'audio', + 'video', + 'source', + 'dt', + 'dd', + 'dl', + 'table', + 'caption', + 'tr', + 'th', + 'td', + 'tbody', + 'thead', + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'strong', + 'em', + 'code', + 'pre', + 'blockquote', + 'p', + 'ul', + 'li', + 'ol', + 'br', + 'del', + 'a', + 'img', + 'figure', + 'figcaption', + 'cite', + 'time', + 'abbr', + 'iframe', + 'q', + 'sup', + 'sub', + ); + + /** + * Check if the tag is allowed and is not a pixel tracker. + * + * @param string $tag Tag name + * @param array $attributes Attributes dictionary + * + * @return bool + */ + public function isAllowed($tag, array $attributes) + { + return $this->isAllowedTag($tag) && !$this->isPixelTracker($tag, $attributes); + } + + /** + * Return the HTML opening tag. + * + * @param string $tag Tag name + * @param string $attributes Attributes converted in html + * + * @return string + */ + public function openHtmlTag($tag, $attributes = '') + { + return '<'.$tag.(empty($attributes) ? '' : ' '.$attributes).($this->isSelfClosingTag($tag) ? '/>' : '>'); + } + + /** + * Return the HTML closing tag. + * + * @param string $tag Tag name + * + * @return string + */ + public function closeHtmlTag($tag) + { + return $this->isSelfClosingTag($tag) ? '' : '</'.$tag.'>'; + } + + /** + * Return true is the tag is self-closing. + * + * @param string $tag Tag name + * + * @return bool + */ + public function isSelfClosingTag($tag) + { + return $tag === 'br' || $tag === 'img'; + } + + /** + * Check if a tag is on the whitelist. + * + * @param string $tag Tag name + * + * @return bool + */ + public function isAllowedTag($tag) + { + return in_array($tag, array_merge( + $this->tag_whitelist, + array_keys($this->config->getFilterWhitelistedTags(array())) + )); + } + + /** + * Detect if an image tag is a pixel tracker. + * + * @param string $tag Tag name + * @param array $attributes Tag attributes + * + * @return bool + */ + public function isPixelTracker($tag, array $attributes) + { + return $tag === 'img' && + isset($attributes['height']) && isset($attributes['width']) && + $attributes['height'] == 1 && $attributes['width'] == 1; + } + + /** + * Remove script tags. + * + * @param string $data Input data + * + * @return string + */ + public function removeBlacklistedTags($data) + { + $dom = XmlParser::getDomDocument($data); + + if ($dom === false) { + return ''; + } + + $xpath = new DOMXpath($dom); + + $nodes = $xpath->query(implode(' | ', $this->tag_blacklist)); + + foreach ($nodes as $node) { + $node->parentNode->removeChild($node); + } + + return $dom->saveXML(); + } + + /** + * Remove empty tags. + * + * @param string $data Input data + * + * @return string + */ + public function removeEmptyTags($data) + { + return preg_replace('/<([^<\/>]*)>([\s]*?|(?R))<\/\1>/imsU', '', $data); + } + + /** + * Replace <br/><br/> by only one. + * + * @param string $data Input data + * + * @return string + */ + public function removeMultipleBreakTags($data) + { + return preg_replace("/(<br\s*\/?>\s*)+/", '<br/>', $data); + } + + /** + * Set whitelisted tags adn attributes for each tag. + * + * @param array $values List of tags: ['video' => ['src', 'cover'], 'img' => ['src']] + * + * @return Tag + */ + public function setWhitelistedTags(array $values) + { + $this->tag_whitelist = $values ?: $this->tag_whitelist; + + return $this; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Generator/ContentGeneratorInterface.php b/vendor/miniflux/picofeed/lib/PicoFeed/Generator/ContentGeneratorInterface.php new file mode 100644 index 00000000..5c2f205c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Generator/ContentGeneratorInterface.php @@ -0,0 +1,23 @@ +<?php + +namespace PicoFeed\Generator; + +use PicoFeed\Parser\Item; + +/** + * Content Generator Interface + * + * @package PicoFeed\Generator + * @author Frederic Guillot + */ +interface ContentGeneratorInterface +{ + /** + * Execute Content Generator + * + * @access public + * @param Item $item + * @return boolean + */ + public function execute(Item $item); +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Generator/FileContentGenerator.php b/vendor/miniflux/picofeed/lib/PicoFeed/Generator/FileContentGenerator.php new file mode 100644 index 00000000..03f37e16 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Generator/FileContentGenerator.php @@ -0,0 +1,36 @@ +<?php + +namespace PicoFeed\Generator; + +use PicoFeed\Base; +use PicoFeed\Parser\Item; + +/** + * File Content Generator + * + * @package PicoFeed\Generator + * @author Frederic Guillot + */ +class FileContentGenerator extends Base implements ContentGeneratorInterface +{ + private $extensions = array('pdf'); + + /** + * Execute Content Generator + * + * @access public + * @param Item $item + * @return boolean + */ + public function execute(Item $item) + { + foreach ($this->extensions as $extension) { + if (substr($item->getUrl(), - strlen($extension)) === $extension) { + $item->setContent('<a href="'.$item->getUrl().'" target="_blank">'.$item->getUrl().'</a>'); + return true; + } + } + + return false; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Generator/YoutubeContentGenerator.php b/vendor/miniflux/picofeed/lib/PicoFeed/Generator/YoutubeContentGenerator.php new file mode 100644 index 00000000..198090d4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Generator/YoutubeContentGenerator.php @@ -0,0 +1,67 @@ +<?php + +namespace PicoFeed\Generator; + +use PicoFeed\Base; +use PicoFeed\Parser\Item; + +/** + * Youtube Content Generator + * + * @package PicoFeed\Generator + * @author Frederic Guillot + */ +class YoutubeContentGenerator extends Base implements ContentGeneratorInterface +{ + /** + * Execute Content Generator + * + * @access public + * @param Item $item + * @return boolean + */ + public function execute(Item $item) + { + if ($item->hasNamespace('yt')) { + return $this->generateHtmlFromXml($item); + } + + return $this->generateHtmlFromUrl($item); + } + + /** + * Generate HTML + * + * @access public + * @param Item $item + * @return boolean + */ + private function generateHtmlFromXml(Item $item) + { + $videoId = $item->getTag('yt:videoId'); + + if (! empty($videoId)) { + $item->setContent('<iframe width="560" height="315" src="//www.youtube.com/embed/'.$videoId[0].'" frameborder="0"></iframe>'); + return true; + } + + return false; + } + + /** + * Generate HTML from item URL + * + * @access public + * @param Item $item + * @return bool + */ + public function generateHtmlFromUrl(Item $item) + { + if (preg_match('/youtube\.com\/watch\?v=(.*)/', $item->getUrl(), $matches)) { + $item->setContent('<iframe width="560" height="315" src="//www.youtube.com/embed/'.$matches[1].'" frameborder="0"></iframe>'); + return true; + } + + return false; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Logging/Logger.php b/vendor/miniflux/picofeed/lib/PicoFeed/Logging/Logger.php new file mode 100644 index 00000000..caec463f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Logging/Logger.php @@ -0,0 +1,114 @@ +<?php + +namespace PicoFeed\Logging; + +use DateTime; +use DateTimeZone; + +/** + * Logging class. + * + * @author Frederic Guillot + */ +class Logger +{ + /** + * List of messages. + * + * @static + * + * @var array + */ + private static $messages = array(); + + /** + * Default timezone. + * + * @static + * + * @var string + */ + private static $timezone = 'UTC'; + + /** + * Enable or disable logging. + * + * @static + * + * @var bool + */ + public static $enable = false; + + /** + * Enable logging. + * + * @static + */ + public static function enable() + { + self::$enable = true; + } + + /** + * Add a new message. + * + * @static + * + * @param string $message Message + */ + public static function setMessage($message) + { + if (self::$enable) { + $date = new DateTime('now', new DateTimeZone(self::$timezone)); + self::$messages[] = '['.$date->format('Y-m-d H:i:s').'] '.$message; + } + } + + /** + * Get all logged messages. + * + * @static + * + * @return array + */ + public static function getMessages() + { + return self::$messages; + } + + /** + * Remove all logged messages. + * + * @static + */ + public static function deleteMessages() + { + self::$messages = array(); + } + + /** + * Set a different timezone. + * + * @static + * + * @see http://php.net/manual/en/timezones.php + * + * @param string $timezone Timezone + */ + public static function setTimeZone($timezone) + { + self::$timezone = $timezone ?: self::$timezone; + } + + /** + * Get all messages serialized into a string. + * + * @static + * + * @return string + */ + public static function toString() + { + return implode(PHP_EOL, self::$messages).PHP_EOL; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Atom.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Atom.php new file mode 100644 index 00000000..1c570a08 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Atom.php @@ -0,0 +1,382 @@ +<?php + +namespace PicoFeed\Parser; + +use SimpleXMLElement; +use PicoFeed\Filter\Filter; +use PicoFeed\Client\Url; + +/** + * Atom parser. + * + * @package PicoFeed\Parser + * @author Frederic Guillot + */ +class Atom extends Parser +{ + /** + * Supported namespaces. + */ + protected $namespaces = array( + 'atom' => 'http://www.w3.org/2005/Atom', + ); + + /** + * Get the path to the items XML tree. + * + * @param SimpleXMLElement $xml Feed xml + * + * @return SimpleXMLElement + */ + public function getItemsTree(SimpleXMLElement $xml) + { + return XmlParser::getXPathResult($xml, 'atom:entry', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'entry'); + } + + /** + * Find the feed url. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedUrl(SimpleXMLElement $xml, Feed $feed) + { + $feed->setFeedUrl($this->getUrl($xml, 'self')); + } + + /** + * Find the site url. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findSiteUrl(SimpleXMLElement $xml, Feed $feed) + { + $feed->setSiteUrl($this->getUrl($xml, 'alternate', true)); + } + + /** + * Find the feed description. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedDescription(SimpleXMLElement $xml, Feed $feed) + { + $description = XmlParser::getXPathResult($xml, 'atom:subtitle', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'subtitle'); + + $feed->setDescription(XmlParser::getValue($description)); + } + + /** + * Find the feed logo url. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedLogo(SimpleXMLElement $xml, Feed $feed) + { + $logo = XmlParser::getXPathResult($xml, 'atom:logo', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'logo'); + + $feed->setLogo(XmlParser::getValue($logo)); + } + + /** + * Find the feed icon. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedIcon(SimpleXMLElement $xml, Feed $feed) + { + $icon = XmlParser::getXPathResult($xml, 'atom:icon', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'icon'); + + $feed->setIcon(XmlParser::getValue($icon)); + } + + /** + * Find the feed title. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedTitle(SimpleXMLElement $xml, Feed $feed) + { + $title = XmlParser::getXPathResult($xml, 'atom:title', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'title'); + + $feed->setTitle(Filter::stripWhiteSpace(XmlParser::getValue($title)) ?: $feed->getSiteUrl()); + } + + /** + * Find the feed language. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedLanguage(SimpleXMLElement $xml, Feed $feed) + { + $language = XmlParser::getXPathResult($xml, '*[not(self::atom:entry)]/@xml:lang', $this->namespaces) + ?: XmlParser::getXPathResult($xml, '@xml:lang'); + + $feed->setLanguage(XmlParser::getValue($language)); + } + + /** + * Find the feed id. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedId(SimpleXMLElement $xml, Feed $feed) + { + $id = XmlParser::getXPathResult($xml, 'atom:id', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'id'); + + $feed->setId(XmlParser::getValue($id)); + } + + /** + * Find the feed date. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedDate(SimpleXMLElement $xml, Feed $feed) + { + $updated = XmlParser::getXPathResult($xml, 'atom:updated', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'updated'); + + $feed->setDate($this->getDateParser()->getDateTime(XmlParser::getValue($updated))); + } + + /** + * Find the item published date. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemPublishedDate(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $date = XmlParser::getXPathResult($entry, 'atom:published', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'published'); + + $item->setPublishedDate(!empty($date) ? $this->getDateParser()->getDateTime((string) current($date)) : null); + } + + /** + * Find the item updated date. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemUpdatedDate(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $date = XmlParser::getXPathResult($entry, 'atom:updated', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'updated'); + + $item->setUpdatedDate(!empty($date) ? $this->getDateParser()->getDateTime((string) current($date)) : null); + } + + /** + * Find the item title. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + */ + public function findItemTitle(SimpleXMLElement $entry, Item $item) + { + $title = XmlParser::getXPathResult($entry, 'atom:title', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'title'); + + $item->setTitle(Filter::stripWhiteSpace(XmlParser::getValue($title)) ?: $item->getUrl()); + } + + /** + * Find the item author. + * + * @param SimpleXMLElement $xml Feed + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + */ + public function findItemAuthor(SimpleXMLElement $xml, SimpleXMLElement $entry, Item $item) + { + $author = XmlParser::getXPathResult($entry, 'atom:author/atom:name', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'author/name') + ?: XmlParser::getXPathResult($xml, 'atom:author/atom:name', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'author/name'); + + $item->setAuthor(XmlParser::getValue($author)); + } + + /** + * Find the item content. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + */ + public function findItemContent(SimpleXMLElement $entry, Item $item) + { + $item->setContent($this->getContent($entry)); + } + + /** + * Find the item URL. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + */ + public function findItemUrl(SimpleXMLElement $entry, Item $item) + { + $item->setUrl($this->getUrl($entry, 'alternate', true)); + } + + /** + * Genereate the item id. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemId(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $id = XmlParser::getXPathResult($entry, 'atom:id', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'id'); + + if (!empty($id)) { + $item->setId($this->generateId(XmlParser::getValue($id))); + } else { + $item->setId($this->generateId( + $item->getTitle(), $item->getUrl(), $item->getContent() + )); + } + } + + /** + * Find the item enclosure. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemEnclosure(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $enclosure = $this->findLink($entry, 'enclosure'); + + if ($enclosure) { + $item->setEnclosureUrl(Url::resolve((string) $enclosure['href'], $feed->getSiteUrl())); + $item->setEnclosureType((string) $enclosure['type']); + } + } + + /** + * Find the item language. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemLanguage(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $language = XmlParser::getXPathResult($entry, './/@xml:lang'); + $item->setLanguage(XmlParser::getValue($language) ?: $feed->getLanguage()); + } + + /** + * Find the item categories. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param Feed $feed Feed object + */ + public function findItemCategories(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $categories = XmlParser::getXPathResult($entry, 'atom:category/@term', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'category/@term'); + $item->setCategoriesFromXml($categories); + } + + /** + * Get the URL from a link tag. + * + * @param SimpleXMLElement $xml XML tag + * @param string $rel Link relationship: alternate, enclosure, related, self, via + * + * @return string + */ + private function getUrl(SimpleXMLElement $xml, $rel, $fallback = false) + { + $link = $this->findLink($xml, $rel); + + if ($link) { + return (string) $link['href']; + } + + if ($fallback) { + $link = $this->findLink($xml, ''); + return $link ? (string) $link['href'] : ''; + } + + return ''; + } + + /** + * Get a link tag that match a relationship. + * + * @param SimpleXMLElement $xml XML tag + * @param string $rel Link relationship: alternate, enclosure, related, self, via + * + * @return SimpleXMLElement|null + */ + private function findLink(SimpleXMLElement $xml, $rel) + { + $links = XmlParser::getXPathResult($xml, 'atom:link', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'link'); + + foreach ($links as $link) { + if ($rel === (string) $link['rel']) { + return $link; + } + } + + return null; + } + + /** + * Get the entry content. + * + * @param SimpleXMLElement $entry XML Entry + * + * @return string + */ + private function getContent(SimpleXMLElement $entry) + { + $content = current( + XmlParser::getXPathResult($entry, 'atom:content', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'content') + ); + + if (!empty($content) && count($content->children())) { + $xml_string = ''; + + foreach ($content->children() as $child) { + $xml_string .= $child->asXML(); + } + + return $xml_string; + } elseif (trim((string) $content) !== '') { + return (string) $content; + } + + $summary = XmlParser::getXPathResult($entry, 'atom:summary', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'summary'); + + return (string) current($summary); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/DateParser.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/DateParser.php new file mode 100644 index 00000000..0e5b80e3 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/DateParser.php @@ -0,0 +1,128 @@ +<?php + +namespace PicoFeed\Parser; + +use DateTime; +use DateTimeZone; +use PicoFeed\Base; + +/** + * Date Parser. + * + * @package PicoFeed\Parser + * @author Frederic Guillot + */ +class DateParser extends Base +{ + /** + * Timezone used to parse feed dates. + * + * @access private + * @var string + */ + private $timezone = 'UTC'; + + /** + * Supported formats [ 'format' => length ]. + * + * @var array + */ + public $formats = array( + DATE_ATOM => null, + DATE_RSS => null, + DATE_COOKIE => null, + DATE_ISO8601 => null, + DATE_RFC822 => null, + DATE_RFC850 => null, + DATE_RFC1036 => null, + DATE_RFC1123 => null, + DATE_RFC2822 => null, + DATE_RFC3339 => null, + 'l, d M Y H:i:s' => null, + 'D, d M Y H:i:s' => 25, + 'D, d M Y h:i:s' => 25, + 'D M d Y H:i:s' => 24, + 'j M Y H:i:s' => 20, + 'Y-m-d H:i:s' => 19, + 'Y-m-d\TH:i:s' => 19, + 'd/m/Y H:i:s' => 19, + 'D, d M Y' => 16, + 'Y-m-d' => 10, + 'd-m-Y' => 10, + 'm-d-Y' => 10, + 'd.m.Y' => 10, + 'm.d.Y' => 10, + 'd/m/Y' => 10, + 'm/d/Y' => 10, + ); + + /** + * Try to parse all date format for broken feeds. + * + * @param string $value Original date format + * + * @return DateTime + */ + public function getDateTime($value) + { + $value = trim($value); + + foreach ($this->formats as $format => $length) { + $truncated_value = $value; + if ($length !== null) { + $truncated_value = substr($truncated_value, 0, $length); + } + + $date = $this->getValidDate($format, $truncated_value); + if ($date !== false) { + return $date; + } + } + + return $this->getCurrentDateTime(); + } + + /** + * Get a valid date from a given format. + * + * @param string $format Date format + * @param string $value Original date value + * + * @return DateTime|bool + */ + public function getValidDate($format, $value) + { + $date = DateTime::createFromFormat($format, $value, $this->getTimeZone()); + + if ($date !== false) { + $errors = DateTime::getLastErrors(); + + if ($errors['error_count'] === 0 && $errors['warning_count'] === 0) { + return $date; + } + } + + return false; + } + + /** + * Get the current datetime. + * + * @return DateTime + */ + public function getCurrentDateTime() + { + return new DateTime('now', $this->getTimeZone()); + } + + /** + * Get DateTimeZone instance + * + * @access public + * @return DateTimeZone + */ + public function getTimeZone() + { + return new DateTimeZone($this->config->getTimezone() ?: $this->timezone); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Feed.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Feed.php new file mode 100644 index 00000000..a56e71c3 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Feed.php @@ -0,0 +1,315 @@ +<?php + +namespace PicoFeed\Parser; + +/** + * Feed. + * + * @package PicoFeed\Parser + * @author Frederic Guillot + */ +class Feed +{ + /** + * Feed items. + * + * @var Item[] + */ + public $items = array(); + + /** + * Feed id. + * + * @var string + */ + public $id = ''; + + /** + * Feed title. + * + * @var string + */ + public $title = ''; + + /** + * Feed description. + * + * @var string + */ + public $description = ''; + + /** + * Feed url. + * + * @var string + */ + public $feedUrl = ''; + + /** + * Site url. + * + * @var string + */ + public $siteUrl = ''; + + /** + * Feed date. + * + * @var \DateTime + */ + public $date = null; + + /** + * Feed language. + * + * @var string + */ + public $language = ''; + + /** + * Feed logo URL. + * + * @var string + */ + public $logo = ''; + + /** + * Feed icon URL. + * + * @var string + */ + public $icon = ''; + + /** + * Return feed information. + */ + public function __toString() + { + $output = ''; + + foreach (array('id', 'title', 'feedUrl', 'siteUrl', 'language', 'description', 'logo') as $property) { + $output .= 'Feed::'.$property.' = '.$this->$property.PHP_EOL; + } + + $output .= 'Feed::date = '.$this->date->format(DATE_RFC822).PHP_EOL; + $output .= 'Feed::isRTL() = '.($this->isRTL() ? 'true' : 'false').PHP_EOL; + $output .= 'Feed::items = '.count($this->items).' items'.PHP_EOL; + + foreach ($this->items as $item) { + $output .= '----'.PHP_EOL; + $output .= $item; + } + + return $output; + } + + /** + * Get title. + */ + public function getTitle() + { + return $this->title; + } + + /** + * Get description. + */ + public function getDescription() + { + return $this->description; + } + + /** + * Get the logo url. + */ + public function getLogo() + { + return $this->logo; + } + + /** + * Get the icon url. + */ + public function getIcon() + { + return $this->icon; + } + + /** + * Get feed url. + */ + public function getFeedUrl() + { + return $this->feedUrl; + } + + /** + * Get site url. + */ + public function getSiteUrl() + { + return $this->siteUrl; + } + + /** + * Get date. + */ + public function getDate() + { + return $this->date; + } + + /** + * Get language. + */ + public function getLanguage() + { + return $this->language; + } + + /** + * Get id. + */ + public function getId() + { + return $this->id; + } + + /** + * Get feed items. + */ + public function getItems() + { + return $this->items; + } + + /** + * Return true if the feed is "Right to Left". + * + * @return bool + */ + public function isRTL() + { + return Parser::isLanguageRTL($this->language); + } + + /** + * Set feed items. + * + * @param Item[] $items + * @return Feed + */ + public function setItems(array $items) + { + $this->items = $items; + return $this; + } + + /** + * Set feed id. + * + * @param string $id + * @return Feed + */ + public function setId($id) + { + $this->id = $id; + return $this; + } + + /** + * Set feed title. + * + * @param string $title + * @return Feed + */ + public function setTitle($title) + { + $this->title = $title; + return $this; + } + + /** + * Set feed description. + * + * @param string $description + * @return Feed + */ + public function setDescription($description) + { + $this->description = $description; + return $this; + } + + /** + * Set feed url. + * + * @param string $feedUrl + * @return Feed + */ + public function setFeedUrl($feedUrl) + { + $this->feedUrl = $feedUrl; + return $this; + } + + /** + * Set feed website url. + * + * @param string $siteUrl + * @return Feed + */ + public function setSiteUrl($siteUrl) + { + $this->siteUrl = $siteUrl; + return $this; + } + + /** + * Set feed date. + * + * @param \DateTime $date + * @return Feed + */ + public function setDate($date) + { + $this->date = $date; + return $this; + } + + /** + * Set feed language. + * + * @param string $language + * @return Feed + */ + public function setLanguage($language) + { + $this->language = $language; + return $this; + } + + /** + * Set feed logo. + * + * @param string $logo + * @return Feed + */ + public function setLogo($logo) + { + $this->logo = $logo; + return $this; + } + + /** + * Set feed icon. + * + * @param string $icon + * @return Feed + */ + public function setIcon($icon) + { + $this->icon = $icon; + return $this; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Item.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Item.php new file mode 100644 index 00000000..f9581941 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Item.php @@ -0,0 +1,534 @@ +<?php + +namespace PicoFeed\Parser; + +/** + * Feed Item. + * + * @package PicoFeed\Parser + * @author Frederic Guillot + */ +class Item +{ + /** + * List of known RTL languages. + * + * @var string[] + */ + public $rtl = array( + 'ar', // Arabic (ar-**) + 'fa', // Farsi (fa-**) + 'ur', // Urdu (ur-**) + 'ps', // Pashtu (ps-**) + 'syr', // Syriac (syr-**) + 'dv', // Divehi (dv-**) + 'he', // Hebrew (he-**) + 'yi', // Yiddish (yi-**) + ); + + /** + * Item id. + * + * @var string + */ + public $id = ''; + + /** + * Item title. + * + * @var string + */ + public $title = ''; + + /** + * Item url. + * + * @var string + */ + public $url = ''; + + /** + * Item author. + * + * @var string + */ + public $author = ''; + + /** + * Item date. + * + * @var \DateTime + */ + public $date = null; + + /** + * Item published date. + * + * @var \DateTime + */ + public $publishedDate = null; + + /** + * Item updated date. + * + * @var \DateTime + */ + public $updatedDate = null; + + /** + * Item content. + * + * @var string + */ + public $content = ''; + + /** + * Item enclosure url. + * + * @var string + */ + public $enclosureUrl = ''; + + /** + * Item enclusure type. + * + * @var string + */ + public $enclosureType = ''; + + /** + * Item language. + * + * @var string + */ + public $language = ''; + + /** + * Item categories. + * + * @var array + */ + public $categories = array(); + + /** + * Raw XML. + * + * @var \SimpleXMLElement + */ + public $xml; + + /** + * List of namespaces. + * + * @var array + */ + public $namespaces = array(); + + /** + * Check if a XML namespace exists + * + * @access public + * @param string $namespace + * @return bool + */ + public function hasNamespace($namespace) + { + return array_key_exists($namespace, $this->namespaces); + } + + /** + * Get specific XML tag or attribute value. + * + * @param string $tag Tag name (examples: guid, media:content) + * @param string $attribute Tag attribute + * + * @return array|false Tag values or error + */ + public function getTag($tag, $attribute = '') + { + if ($attribute !== '') { + $attribute = '/@'.$attribute; + } + + $query = './/'.$tag.$attribute; + $elements = XmlParser::getXPathResult($this->xml, $query, $this->namespaces); + + if ($elements === false) { // xPath error + return false; + } + + return array_map(function ($element) { return (string) $element;}, $elements); + } + + /** + * Return item information. + * + * @return string + */ + public function __toString() + { + $output = ''; + + foreach (array('id', 'title', 'url', 'language', 'author', 'enclosureUrl', 'enclosureType') as $property) { + $output .= 'Item::'.$property.' = '.$this->$property.PHP_EOL; + } + + $publishedDate = $this->publishedDate != null ? $this->publishedDate->format(DATE_RFC822) : null; + $updatedDate = $this->updatedDate != null ? $this->updatedDate->format(DATE_RFC822) : null; + + $categoryString = $this->categories != null ? implode(',', $this->categories) : null; + + $output .= 'Item::date = '.$this->date->format(DATE_RFC822).PHP_EOL; + $output .= 'Item::publishedDate = '.$publishedDate.PHP_EOL; + $output .= 'Item::updatedDate = '.$updatedDate.PHP_EOL; + $output .= 'Item::isRTL() = '.($this->isRTL() ? 'true' : 'false').PHP_EOL; + $output .= 'Item::categories = ['.$categoryString.']'.PHP_EOL; + $output .= 'Item::content = '.strlen($this->content).' bytes'.PHP_EOL; + + return $output; + } + + /** + * Get title. + * + * @return string + */ + public function getTitle() + { + return $this->title; + } + + /** + * Get URL + * + * @access public + * @return string + */ + public function getUrl() + { + return $this->url; + } + + /** + * Set URL + * + * @access public + * @param string $url + * @return Item + */ + public function setUrl($url) + { + $this->url = $url; + return $this; + } + + /** + * Get id. + * + * @return string + */ + public function getId() + { + return $this->id; + } + + /** + * Get date. + * + * @return \DateTime + */ + public function getDate() + { + return $this->date; + } + + /** + * Get published date. + * + * @return \DateTime + */ + public function getPublishedDate() + { + return $this->publishedDate; + } + + /** + * Get updated date. + * + * @return \DateTime + */ + public function getUpdatedDate() + { + return $this->updatedDate; + } + + /** + * Get content. + * + * @return string + */ + public function getContent() + { + return $this->content; + } + + /** + * Set content + * + * @access public + * @param string $value + * @return Item + */ + public function setContent($value) + { + $this->content = $value; + return $this; + } + + /** + * Get enclosure url. + * + * @return string + */ + public function getEnclosureUrl() + { + return $this->enclosureUrl; + } + + /** + * Get enclosure type. + * + * @return string + */ + public function getEnclosureType() + { + return $this->enclosureType; + } + + /** + * Get language. + * + * @return string + */ + public function getLanguage() + { + return $this->language; + } + + /** + * Get categories. + * + * @return string + */ + public function getCategories() + { + return $this->categories; + } + + /** + * Get author. + * + * @return string + */ + public function getAuthor() + { + return $this->author; + } + + /** + * Return true if the item is "Right to Left". + * + * @return bool + */ + public function isRTL() + { + return Parser::isLanguageRTL($this->language); + } + + /** + * Set item id. + * + * @param string $id + * @return Item + */ + public function setId($id) + { + $this->id = $id; + return $this; + } + + /** + * Set item title. + * + * @param string $title + * @return Item + */ + public function setTitle($title) + { + $this->title = $title; + return $this; + } + + /** + * Set author. + * + * @param string $author + * @return Item + */ + public function setAuthor($author) + { + $this->author = $author; + return $this; + } + + /** + * Set item date. + * + * @param \DateTime $date + * @return Item + */ + public function setDate($date) + { + $this->date = $date; + return $this; + } + + /** + * Set item published date. + * + * @param \DateTime $publishedDate + * @return Item + */ + public function setPublishedDate($publishedDate) + { + $this->publishedDate = $publishedDate; + return $this; + } + + /** + * Set item updated date. + * + * @param \DateTime $updatedDate + * @return Item + */ + public function setUpdatedDate($updatedDate) + { + $this->updatedDate = $updatedDate; + return $this; + } + + /** + * Set enclosure url. + * + * @param string $enclosureUrl + * @return Item + */ + public function setEnclosureUrl($enclosureUrl) + { + $this->enclosureUrl = $enclosureUrl; + return $this; + } + + /** + * Set enclosure type. + * + * @param string $enclosureType + * @return Item + */ + public function setEnclosureType($enclosureType) + { + $this->enclosureType = $enclosureType; + return $this; + } + + /** + * Set item language. + * + * @param string $language + * @return Item + */ + public function setLanguage($language) + { + $this->language = $language; + return $this; + } + + /** + * Set item categories. + * + * @param array $categories + * @return Item + */ + public function setCategories($categories) + { + $this->categories = $categories; + return $this; + } + + /** + * Set item categories from xml. + * + * @param SimpleXMLElement[] $categories + * @return Item + */ + public function setCategoriesFromXml($categories) + { + if ($categories !== false) { + $this->setCategories( + array_map( + function ($element) { + return trim((string) $element); + }, + $categories + ) + ); + } else { + $categories = array(); + } + return $this; + } + + /** + * Set raw XML. + * + * @param \SimpleXMLElement $xml + * @return Item + */ + public function setXml($xml) + { + $this->xml = $xml; + return $this; + } + + /** + * Get raw XML. + * + * @return \SimpleXMLElement + */ + public function getXml() + { + return $this->xml; + } + + /** + * Set XML namespaces. + * + * @param array $namespaces + * @return Item + */ + public function setNamespaces($namespaces) + { + $this->namespaces = $namespaces; + return $this; + } + + /** + * Get XML namespaces. + * + * @return array + */ + public function getNamespaces() + { + return $this->namespaces; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/MalformedXmlException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/MalformedXmlException.php new file mode 100644 index 00000000..efaf0ff1 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/MalformedXmlException.php @@ -0,0 +1,13 @@ +<?php + +namespace PicoFeed\Parser; + +/** + * MalformedXmlException Exception. + * + * @package PicoFeed\Parser + * @author Frederic Guillot + */ +class MalformedXmlException extends ParserException +{ +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Parser.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Parser.php new file mode 100644 index 00000000..3ee99f59 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Parser.php @@ -0,0 +1,404 @@ +<?php + +namespace PicoFeed\Parser; + +use PicoFeed\Processor\ContentFilterProcessor; +use PicoFeed\Processor\ContentGeneratorProcessor; +use PicoFeed\Processor\ItemPostProcessor; +use PicoFeed\Processor\ScraperProcessor; +use SimpleXMLElement; +use PicoFeed\Client\Url; +use PicoFeed\Encoding\Encoding; +use PicoFeed\Filter\Filter; +use PicoFeed\Logging\Logger; + +/** + * Base parser class. + * + * @package PicoFeed\Parser + * @author Frederic Guillot + */ +abstract class Parser implements ParserInterface +{ + /** + * Config object. + * + * @var \PicoFeed\Config\Config + */ + private $config; + + /** + * DateParser object. + * + * @var \PicoFeed\Parser\DateParser + */ + private $dateParser; + + /** + * Hash algorithm used to generate item id, any value supported by PHP, see hash_algos(). + * + * @var string + */ + private $hash_algo = 'sha256'; + + /** + * Feed content (XML data). + * + * @var string + */ + protected $content = ''; + + /** + * Fallback url. + * + * @var string + */ + protected $fallback_url = ''; + + /** + * XML namespaces supported by parser. + * + * @var array + */ + protected $namespaces = array(); + + /** + * XML namespaces used in document. + * + * @var array + */ + protected $used_namespaces = array(); + + /** + * Item Post Processor instance + * + * @access private + * @var ItemPostProcessor + */ + private $itemPostProcessor; + + /** + * Constructor. + * + * @param string $content Feed content + * @param string $http_encoding HTTP encoding (headers) + * @param string $fallback_url Fallback url when the feed provide relative or broken url + */ + public function __construct($content, $http_encoding = '', $fallback_url = '') + { + $this->fallback_url = $fallback_url; + $xml_encoding = XmlParser::getEncodingFromXmlTag($content); + + // Strip XML tag to avoid multiple encoding/decoding in the next XML processing + $this->content = Filter::stripXmlTag($content); + + // Encode everything in UTF-8 + Logger::setMessage(get_called_class().': HTTP Encoding "'.$http_encoding.'" ; XML Encoding "'.$xml_encoding.'"'); + $this->content = Encoding::convert($this->content, $xml_encoding ?: $http_encoding); + + $this->itemPostProcessor = new ItemPostProcessor($this->config); + $this->itemPostProcessor->register(new ContentGeneratorProcessor($this->config)); + $this->itemPostProcessor->register(new ContentFilterProcessor($this->config)); + } + + /** + * Parse the document. + * + * @return \PicoFeed\Parser\Feed + */ + public function execute() + { + Logger::setMessage(get_called_class().': begin parsing'); + + $xml = XmlParser::getSimpleXml($this->content); + + if ($xml === false) { + Logger::setMessage(get_called_class().': Applying XML workarounds'); + $this->content = Filter::normalizeData($this->content); + $xml = XmlParser::getSimpleXml($this->content); + + if ($xml === false) { + Logger::setMessage(get_called_class().': XML parsing error'); + Logger::setMessage(XmlParser::getErrors()); + throw new MalformedXmlException('XML parsing error'); + } + } + + $this->used_namespaces = $xml->getNamespaces(true); + $xml = $this->registerSupportedNamespaces($xml); + + $feed = new Feed(); + + $this->findFeedUrl($xml, $feed); + $this->checkFeedUrl($feed); + + $this->findSiteUrl($xml, $feed); + $this->checkSiteUrl($feed); + + $this->findFeedTitle($xml, $feed); + $this->findFeedDescription($xml, $feed); + $this->findFeedLanguage($xml, $feed); + $this->findFeedId($xml, $feed); + $this->findFeedDate($xml, $feed); + $this->findFeedLogo($xml, $feed); + $this->findFeedIcon($xml, $feed); + + foreach ($this->getItemsTree($xml) as $entry) { + $entry = $this->registerSupportedNamespaces($entry); + + $item = new Item(); + $item->xml = $entry; + $item->namespaces = $this->used_namespaces; + + $this->findItemAuthor($xml, $entry, $item); + + $this->findItemUrl($entry, $item); + $this->checkItemUrl($feed, $item); + + $this->findItemTitle($entry, $item); + $this->findItemContent($entry, $item); + + // Id generation can use the item url/title/content (order is important) + $this->findItemId($entry, $item, $feed); + $this->findItemDate($entry, $item, $feed); + $this->findItemEnclosure($entry, $item, $feed); + $this->findItemLanguage($entry, $item, $feed); + $this->findItemCategories($entry, $item, $feed); + + $this->itemPostProcessor->execute($feed, $item); + $feed->items[] = $item; + } + + Logger::setMessage(get_called_class().PHP_EOL.$feed); + + return $feed; + } + + /** + * Check if the feed url is correct. + * + * @param Feed $feed Feed object + */ + public function checkFeedUrl(Feed $feed) + { + if ($feed->getFeedUrl() === '') { + $feed->feedUrl = $this->fallback_url; + } else { + $feed->feedUrl = Url::resolve($feed->getFeedUrl(), $this->fallback_url); + } + } + + /** + * Check if the site url is correct. + * + * @param Feed $feed Feed object + */ + public function checkSiteUrl(Feed $feed) + { + if ($feed->getSiteUrl() === '') { + $feed->siteUrl = Url::base($feed->getFeedUrl()); + } else { + $feed->siteUrl = Url::resolve($feed->getSiteUrl(), $this->fallback_url); + } + } + + /** + * Check if the item url is correct. + * + * @param Feed $feed Feed object + * @param Item $item Item object + */ + public function checkItemUrl(Feed $feed, Item $item) + { + $item->url = Url::resolve($item->getUrl(), $feed->getSiteUrl()); + } + + /** + * Find the item date. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemDate(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $this->findItemPublishedDate($entry, $item, $feed); + $this->findItemUpdatedDate($entry, $item, $feed); + + if ($item->getPublishedDate() === null) { + // Use the updated date if available, otherwise use the feed date + $item->setPublishedDate($item->getUpdatedDate() ?: $feed->getDate()); + } + + if ($item->getUpdatedDate() === null) { + // Use the published date as fallback + $item->setUpdatedDate($item->getPublishedDate()); + } + + // Use the most recent of published and updated dates + $item->setDate(max($item->getPublishedDate(), $item->getUpdatedDate())); + } + + /** + * Get Item Post Processor instance + * + * @access public + * @return ItemPostProcessor + */ + public function getItemPostProcessor() + { + return $this->itemPostProcessor; + } + + /** + * Get DateParser instance + * + * @access public + * @return DateParser + */ + public function getDateParser() + { + if ($this->dateParser === null) { + $this->dateParser = new DateParser($this->config); + } + + return $this->dateParser; + } + + /** + * Generate a unique id for an entry (hash all arguments). + * + * @return string + */ + public function generateId() + { + return hash($this->hash_algo, implode(func_get_args())); + } + + /** + * Return true if the given language is "Right to Left". + * + * @static + * + * @param string $language Language: fr-FR, en-US + * + * @return bool + */ + public static function isLanguageRTL($language) + { + $language = strtolower($language); + + $rtl_languages = array( + 'ar', // Arabic (ar-**) + 'fa', // Farsi (fa-**) + 'ur', // Urdu (ur-**) + 'ps', // Pashtu (ps-**) + 'syr', // Syriac (syr-**) + 'dv', // Divehi (dv-**) + 'he', // Hebrew (he-**) + 'yi', // Yiddish (yi-**) + ); + + foreach ($rtl_languages as $prefix) { + if (strpos($language, $prefix) === 0) { + return true; + } + } + + return false; + } + + /** + * Set Hash algorithm used for id generation. + * + * @param string $algo Algorithm name + * @return \PicoFeed\Parser\Parser + */ + public function setHashAlgo($algo) + { + $this->hash_algo = $algo ?: $this->hash_algo; + return $this; + } + + /** + * Set config object. + * + * @param \PicoFeed\Config\Config $config Config instance + * + * @return \PicoFeed\Parser\Parser + */ + public function setConfig($config) + { + $this->config = $config; + $this->itemPostProcessor->setConfig($config); + return $this; + } + + /** + * Enable the content grabber. + * + * @return \PicoFeed\Parser\Parser + */ + public function disableContentFiltering() + { + $this->itemPostProcessor->unregister('PicoFeed\Processor\ContentFilterProcessor'); + return $this; + } + + /** + * Enable the content grabber. + * + * @param bool $needsRuleFile true if only pages with rule files should be + * scraped + * @param null|\Closure $scraperCallback Callback function that gets called for each + * scraper execution + * + * @return \PicoFeed\Parser\Parser + */ + public function enableContentGrabber($needsRuleFile = false, $scraperCallback = null) + { + $processor = new ScraperProcessor($this->config); + + if ($needsRuleFile) { + $processor->getScraper()->disableCandidateParser(); + } + + if ($scraperCallback !== null) { + $processor->setExecutionCallback($scraperCallback); + } + + $this->itemPostProcessor->register($processor); + return $this; + } + + /** + * Set ignored URLs for the content grabber. + * + * @param array $urls URLs + * + * @return \PicoFeed\Parser\Parser + */ + public function setGrabberIgnoreUrls(array $urls) + { + $this->itemPostProcessor->getProcessor('PicoFeed\Processor\ScraperProcessor')->ignoreUrls($urls); + return $this; + } + + /** + * Register all supported namespaces to be used within an xpath query. + * + * @param SimpleXMLElement $xml Feed xml + * + * @return SimpleXMLElement + */ + public function registerSupportedNamespaces(SimpleXMLElement $xml) + { + foreach ($this->namespaces as $prefix => $ns) { + $xml->registerXPathNamespace($prefix, $ns); + } + + return $xml; + } + + +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/ParserException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/ParserException.php new file mode 100644 index 00000000..b5fbb699 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/ParserException.php @@ -0,0 +1,15 @@ +<?php + +namespace PicoFeed\Parser; + +use PicoFeed\PicoFeedException; + +/** + * ParserException Exception. + * + * @package PicoFeed\Parser + * @author Frederic Guillot + */ +abstract class ParserException extends PicoFeedException +{ +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/ParserInterface.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/ParserInterface.php new file mode 100644 index 00000000..8d6be085 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/ParserInterface.php @@ -0,0 +1,182 @@ +<?php + +namespace PicoFeed\Parser; + +use SimpleXMLElement; + +/** + * Interface ParserInterface + * + * @package PicoFeed\Parser + * @author Frederic Guillot + */ +interface ParserInterface +{ + /** + * Find the feed url. + * + * @param SimpleXMLElement $xml Feed xml + * @param Feed $feed Feed object + */ + public function findFeedUrl(SimpleXMLElement $xml, Feed $feed); + + /** + * Find the site url. + * + * @param SimpleXMLElement $xml Feed xml + * @param Feed $feed Feed object + */ + public function findSiteUrl(SimpleXMLElement $xml, Feed $feed); + + /** + * Find the feed title. + * + * @param SimpleXMLElement $xml Feed xml + * @param Feed $feed Feed object + */ + public function findFeedTitle(SimpleXMLElement $xml, Feed $feed); + + /** + * Find the feed description. + * + * @param SimpleXMLElement $xml Feed xml + * @param Feed $feed Feed object + */ + public function findFeedDescription(SimpleXMLElement $xml, Feed $feed); + + /** + * Find the feed language. + * + * @param SimpleXMLElement $xml Feed xml + * @param Feed $feed Feed object + */ + public function findFeedLanguage(SimpleXMLElement $xml, Feed $feed); + + /** + * Find the feed id. + * + * @param SimpleXMLElement $xml Feed xml + * @param Feed $feed Feed object + */ + public function findFeedId(SimpleXMLElement $xml, Feed $feed); + + /** + * Find the feed date. + * + * @param SimpleXMLElement $xml Feed xml + * @param Feed $feed Feed object + */ + public function findFeedDate(SimpleXMLElement $xml, Feed $feed); + + /** + * Find the feed logo url. + * + * @param SimpleXMLElement $xml Feed xml + * @param Feed $feed Feed object + */ + public function findFeedLogo(SimpleXMLElement $xml, Feed $feed); + + /** + * Find the feed icon. + * + * @param SimpleXMLElement $xml Feed xml + * @param Feed $feed Feed object + */ + public function findFeedIcon(SimpleXMLElement $xml, Feed $feed); + + /** + * Get the path to the items XML tree. + * + * @param SimpleXMLElement $xml Feed xml + * + * @return SimpleXMLElement + */ + public function getItemsTree(SimpleXMLElement $xml); + + /** + * Find the item author. + * + * @param SimpleXMLElement $xml Feed + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + */ + public function findItemAuthor(SimpleXMLElement $xml, SimpleXMLElement $entry, Item $item); + + /** + * Find the item URL. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + */ + public function findItemUrl(SimpleXMLElement $entry, Item $item); + + /** + * Find the item title. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + */ + public function findItemTitle(SimpleXMLElement $entry, Item $item); + + /** + * Genereate the item id. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param Feed $feed Feed object + */ + public function findItemId(SimpleXMLElement $entry, Item $item, Feed $feed); + + /** + * Find the item published date. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param Feed $feed Feed object + */ + public function findItemPublishedDate(SimpleXMLElement $entry, Item $item, Feed $feed); + + /** + * Find the item updated date. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param Feed $feed Feed object + */ + public function findItemUpdatedDate(SimpleXMLElement $entry, Item $item, Feed $feed); + + /** + * Find the item content. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + */ + public function findItemContent(SimpleXMLElement $entry, Item $item); + + /** + * Find the item enclosure. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param Feed $feed Feed object + */ + public function findItemEnclosure(SimpleXMLElement $entry, Item $item, Feed $feed); + + /** + * Find the item language. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param Feed $feed Feed object + */ + public function findItemLanguage(SimpleXMLElement $entry, Item $item, Feed $feed); + + /** + * Find the item categories. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param Feed $feed Feed object + */ + public function findItemCategories(SimpleXMLElement $entry, Item $item, Feed $feed); +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss10.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss10.php new file mode 100644 index 00000000..bb9bf424 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss10.php @@ -0,0 +1,306 @@ +<?php + +namespace PicoFeed\Parser; + +use SimpleXMLElement; +use PicoFeed\Filter\Filter; + +/** + * RSS 1.0 parser. + * + * @package PicoFeed\Parser + * @author Frederic Guillot + */ +class Rss10 extends Parser +{ + /** + * Supported namespaces. + */ + protected $namespaces = array( + 'rss' => 'http://purl.org/rss/1.0/', + 'dc' => 'http://purl.org/dc/elements/1.1/', + 'content' => 'http://purl.org/rss/1.0/modules/content/', + 'feedburner' => 'http://rssnamespace.org/feedburner/ext/1.0', + ); + + /** + * Get the path to the items XML tree. + * + * @param SimpleXMLElement $xml Feed xml + * + * @return SimpleXMLElement + */ + public function getItemsTree(SimpleXMLElement $xml) + { + return XmlParser::getXPathResult($xml, 'rss:item', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'item') + ?: $xml->item; + } + + /** + * Find the feed url. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedUrl(SimpleXMLElement $xml, Feed $feed) + { + $feed->setFeedUrl(''); + } + + /** + * Find the site url. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findSiteUrl(SimpleXMLElement $xml, Feed $feed) + { + $value = XmlParser::getXPathResult($xml, 'rss:channel/rss:link', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'channel/link') + ?: $xml->channel->link; + + $feed->setSiteUrl(XmlParser::getValue($value)); + } + + /** + * Find the feed description. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedDescription(SimpleXMLElement $xml, Feed $feed) + { + $description = XmlParser::getXPathResult($xml, 'rss:channel/rss:description', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'channel/description') + ?: $xml->channel->description; + + $feed->setDescription(XmlParser::getValue($description)); + } + + /** + * Find the feed logo url. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedLogo(SimpleXMLElement $xml, Feed $feed) + { + $logo = XmlParser::getXPathResult($xml, 'rss:image/rss:url', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'image/url'); + + $feed->setLogo(XmlParser::getValue($logo)); + } + + /** + * Find the feed icon. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedIcon(SimpleXMLElement $xml, Feed $feed) + { + $feed->setIcon(''); + } + + /** + * Find the feed title. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedTitle(SimpleXMLElement $xml, Feed $feed) + { + $title = XmlParser::getXPathResult($xml, 'rss:channel/rss:title', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'channel/title') + ?: $xml->channel->title; + + $feed->setTitle(Filter::stripWhiteSpace(XmlParser::getValue($title)) ?: $feed->getSiteUrl()); + } + + /** + * Find the feed language. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedLanguage(SimpleXMLElement $xml, Feed $feed) + { + $language = XmlParser::getXPathResult($xml, 'rss:channel/dc:language', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'channel/dc:language', $this->namespaces); + + $feed->setLanguage(XmlParser::getValue($language)); + } + + /** + * Find the feed id. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedId(SimpleXMLElement $xml, Feed $feed) + { + $feed->setId($feed->getFeedUrl() ?: $feed->getSiteUrl()); + } + + /** + * Find the feed date. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedDate(SimpleXMLElement $xml, Feed $feed) + { + $date = XmlParser::getXPathResult($xml, 'rss:channel/dc:date', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'channel/dc:date', $this->namespaces); + + $feed->setDate($this->getDateParser()->getDateTime(XmlParser::getValue($date))); + } + + /** + * Find the item published date. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemPublishedDate(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $date = XmlParser::getXPathResult($entry, 'dc:date', $this->namespaces); + + $item->setPublishedDate(!empty($date) ? $this->getDateParser()->getDateTime(XmlParser::getValue($date)) : null); + } + + /** + * Find the item updated date. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemUpdatedDate(SimpleXMLElement $entry, Item $item, Feed $feed) + { + if ($item->publishedDate === null) { + $this->findItemPublishedDate($entry, $item, $feed); + } + $item->setUpdatedDate($item->getPublishedDate()); // No updated date in RSS 1.0 specifications + } + + /** + * Find the item title. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + */ + public function findItemTitle(SimpleXMLElement $entry, Item $item) + { + $title = XmlParser::getXPathResult($entry, 'rss:title', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'title') + ?: $entry->title; + + $item->setTitle(Filter::stripWhiteSpace(XmlParser::getValue($title)) ?: $item->getUrl()); + } + + /** + * Find the item author. + * + * @param SimpleXMLElement $xml Feed + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + */ + public function findItemAuthor(SimpleXMLElement $xml, SimpleXMLElement $entry, Item $item) + { + $author = XmlParser::getXPathResult($entry, 'dc:creator', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'rss:channel/dc:creator', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'channel/dc:creator', $this->namespaces); + + $item->setAuthor(XmlParser::getValue($author)); + } + + /** + * Find the item content. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + */ + public function findItemContent(SimpleXMLElement $entry, Item $item) + { + $content = XmlParser::getXPathResult($entry, 'content:encoded', $this->namespaces); + + if (XmlParser::getValue($content) === '') { + $content = XmlParser::getXPathResult($entry, 'rss:description', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'description') + ?: $entry->description; + } + + $item->setContent(XmlParser::getValue($content)); + } + + /** + * Find the item URL. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + */ + public function findItemUrl(SimpleXMLElement $entry, Item $item) + { + $link = XmlParser::getXPathResult($entry, 'feedburner:origLink', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'rss:link', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'link') + ?: $entry->link; + + $item->setUrl(XmlParser::getValue($link)); + } + + /** + * Genereate the item id. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemId(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $item->setId($this->generateId( + $item->getTitle(), $item->getUrl(), $item->getContent() + )); + } + + /** + * Find the item enclosure. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemEnclosure(SimpleXMLElement $entry, Item $item, Feed $feed) + { + } + + /** + * Find the item language. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemLanguage(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $language = XmlParser::getXPathResult($entry, 'dc:language', $this->namespaces); + + $item->setLanguage(XmlParser::getValue($language) ?: $feed->getLanguage()); + } + + /** + * Find the item categories. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param Feed $feed Feed object + */ + public function findItemCategories(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $categories = XmlParser::getXPathResult($entry, 'dc:subject', $this->namespaces); + $item->setCategoriesFromXml($categories); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss20.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss20.php new file mode 100644 index 00000000..1dd3bf8c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss20.php @@ -0,0 +1,319 @@ +<?php + +namespace PicoFeed\Parser; + +use SimpleXMLElement; +use PicoFeed\Filter\Filter; +use PicoFeed\Client\Url; + +/** + * RSS 2.0 Parser. + * + * @package PicoFeed\Parser + * @author Frederic Guillot + */ +class Rss20 extends Parser +{ + /** + * Supported namespaces. + */ + protected $namespaces = array( + 'dc' => 'http://purl.org/dc/elements/1.1/', + 'content' => 'http://purl.org/rss/1.0/modules/content/', + 'feedburner' => 'http://rssnamespace.org/feedburner/ext/1.0', + 'atom' => 'http://www.w3.org/2005/Atom', + ); + + /** + * Get the path to the items XML tree. + * + * @param SimpleXMLElement $xml Feed xml + * + * @return SimpleXMLElement + */ + public function getItemsTree(SimpleXMLElement $xml) + { + return XmlParser::getXPathResult($xml, 'channel/item'); + } + + /** + * Find the feed url. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedUrl(SimpleXMLElement $xml, Feed $feed) + { + $feed->setFeedUrl(''); + } + + /** + * Find the site url. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findSiteUrl(SimpleXMLElement $xml, Feed $feed) + { + $value = XmlParser::getXPathResult($xml, 'channel/link'); + $feed->setSiteUrl(XmlParser::getValue($value)); + } + + /** + * Find the feed description. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedDescription(SimpleXMLElement $xml, Feed $feed) + { + $value = XmlParser::getXPathResult($xml, 'channel/description'); + $feed->setDescription(XmlParser::getValue($value)); + } + + /** + * Find the feed logo url. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedLogo(SimpleXMLElement $xml, Feed $feed) + { + $value = XmlParser::getXPathResult($xml, 'channel/image/url'); + $feed->setLogo(XmlParser::getValue($value)); + } + + /** + * Find the feed icon. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedIcon(SimpleXMLElement $xml, Feed $feed) + { + $feed->setIcon(''); + } + + /** + * Find the feed title. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedTitle(SimpleXMLElement $xml, Feed $feed) + { + $title = XmlParser::getXPathResult($xml, 'channel/title'); + $feed->setTitle(Filter::stripWhiteSpace(XmlParser::getValue($title)) ?: $feed->getSiteUrl()); + } + + /** + * Find the feed language. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedLanguage(SimpleXMLElement $xml, Feed $feed) + { + $value = XmlParser::getXPathResult($xml, 'channel/language'); + $feed->setLanguage(XmlParser::getValue($value)); + } + + /** + * Find the feed id. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedId(SimpleXMLElement $xml, Feed $feed) + { + $feed->setId($feed->getFeedUrl() ?: $feed->getSiteUrl()); + } + + /** + * Find the feed date. + * + * @param SimpleXMLElement $xml Feed xml + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findFeedDate(SimpleXMLElement $xml, Feed $feed) + { + $publish_date = XmlParser::getXPathResult($xml, 'channel/pubDate'); + $update_date = XmlParser::getXPathResult($xml, 'channel/lastBuildDate'); + + $published = !empty($publish_date) ? $this->getDateParser()->getDateTime(XmlParser::getValue($publish_date)) : null; + $updated = !empty($update_date) ? $this->getDateParser()->getDateTime(XmlParser::getValue($update_date)) : null; + + if ($published === null && $updated === null) { + $feed->setDate($this->getDateParser()->getCurrentDateTime()); // We use the current date if there is no date for the feed + } elseif ($published !== null && $updated !== null) { + $feed->setDate(max($published, $updated)); // We use the most recent date between published and updated + } else { + $feed->setDate($updated ?: $published); + } + } + + /** + * Find the item published date. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemPublishedDate(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $date = XmlParser::getXPathResult($entry, 'pubDate'); + + $item->setPublishedDate(!empty($date) ? $this->getDateParser()->getDateTime(XmlParser::getValue($date)) : null); + } + + /** + * Find the item updated date. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemUpdatedDate(SimpleXMLElement $entry, Item $item, Feed $feed) + { + if ($item->publishedDate === null) { + $this->findItemPublishedDate($entry, $item, $feed); + } + $item->setUpdatedDate($item->getPublishedDate()); // No updated date in RSS 2.0 specifications + } + + /** + * Find the item title. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + */ + public function findItemTitle(SimpleXMLElement $entry, Item $item) + { + $value = XmlParser::getXPathResult($entry, 'title'); + $item->setTitle(Filter::stripWhiteSpace(XmlParser::getValue($value)) ?: $item->getUrl()); + } + + /** + * Find the item author. + * + * @param SimpleXMLElement $xml Feed + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + */ + public function findItemAuthor(SimpleXMLElement $xml, SimpleXMLElement $entry, Item $item) + { + $value = XmlParser::getXPathResult($entry, 'dc:creator', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'author') + ?: XmlParser::getXPathResult($xml, 'channel/dc:creator', $this->namespaces) + ?: XmlParser::getXPathResult($xml, 'channel/managingEditor'); + + $item->setAuthor(XmlParser::getValue($value)); + } + + /** + * Find the item content. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + */ + public function findItemContent(SimpleXMLElement $entry, Item $item) + { + $content = XmlParser::getXPathResult($entry, 'content:encoded', $this->namespaces); + + if (XmlParser::getValue($content) === '') { + $content = XmlParser::getXPathResult($entry, 'description'); + } + + $item->setContent(XmlParser::getValue($content)); + } + + /** + * Find the item URL. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + */ + public function findItemUrl(SimpleXMLElement $entry, Item $item) + { + $link = XmlParser::getXPathResult($entry, 'feedburner:origLink', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'link') + ?: XmlParser::getXPathResult($entry, 'atom:link/@href', $this->namespaces); + + if (!empty($link)) { + $item->setUrl(XmlParser::getValue($link)); + } else { + $link = XmlParser::getXPathResult($entry, 'guid'); + $link = XmlParser::getValue($link); + + if (filter_var($link, FILTER_VALIDATE_URL) !== false) { + $item->setUrl($link); + } + } + } + + /** + * Genereate the item id. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemId(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $id = XmlParser::getValue(XmlParser::getXPathResult($entry, 'guid')); + + if ($id) { + $item->setId($this->generateId($id)); + } else { + $item->setId($this->generateId( + $item->getTitle(), $item->getUrl(), $item->getContent() + )); + } + } + + /** + * Find the item enclosure. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemEnclosure(SimpleXMLElement $entry, Item $item, Feed $feed) + { + if (isset($entry->enclosure)) { + $type = XmlParser::getXPathResult($entry, 'enclosure/@type'); + $url = XmlParser::getXPathResult($entry, 'feedburner:origEnclosureLink', $this->namespaces) + ?: XmlParser::getXPathResult($entry, 'enclosure/@url'); + + $item->setEnclosureUrl(Url::resolve(XmlParser::getValue($url), $feed->getSiteUrl())); + $item->setEnclosureType(XmlParser::getValue($type)); + } + } + + /** + * Find the item language. + * + * @param SimpleXMLElement $entry Feed item + * @param \PicoFeed\Parser\Item $item Item object + * @param \PicoFeed\Parser\Feed $feed Feed object + */ + public function findItemLanguage(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $language = XmlParser::getXPathResult($entry, 'dc:language', $this->namespaces); + $item->setLanguage(XmlParser::getValue($language) ?: $feed->getLanguage()); + } + + /** + * Find the item categories. + * + * @param SimpleXMLElement $entry Feed item + * @param Item $item Item object + * @param Feed $feed Feed object + */ + public function findItemCategories(SimpleXMLElement $entry, Item $item, Feed $feed) + { + $categories = XmlParser::getXPathResult($entry, 'category'); + $item->setCategoriesFromXml($categories); + } + +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss91.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss91.php new file mode 100644 index 00000000..058fca1b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss91.php @@ -0,0 +1,13 @@ +<?php + +namespace PicoFeed\Parser; + +/** + * RSS 0.91 Parser. + * + * @package PicoFeed\Parser + * @author Frederic Guillot + */ +class Rss91 extends Rss20 +{ +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss92.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss92.php new file mode 100644 index 00000000..e3df3c8b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/Rss92.php @@ -0,0 +1,13 @@ +<?php + +namespace PicoFeed\Parser; + +/** + * RSS 0.92 Parser. + * + * @package PicoFeed\Parser + * @author Frederic Guillot + */ +class Rss92 extends Rss20 +{ +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/XmlEntityException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/XmlEntityException.php new file mode 100644 index 00000000..1188217d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/XmlEntityException.php @@ -0,0 +1,13 @@ +<?php + +namespace PicoFeed\Parser; + +/** + * XmlEntityException Exception. + * + * @package PicoFeed\Parser + * @author Bernhard Posselt + */ +class XmlEntityException extends MalformedXmlException +{ +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Parser/XmlParser.php b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/XmlParser.php new file mode 100644 index 00000000..ea42949f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Parser/XmlParser.php @@ -0,0 +1,246 @@ +<?php + +namespace PicoFeed\Parser; + +use DOMDocument; +use SimpleXMLElement; +use ZendXml\Exception\RuntimeException; +use ZendXml\Security; + +/** + * XML parser class. + * + * Checks for XML eXternal Entity (XXE) and XML Entity Expansion (XEE) attacks on XML documents + * + * @package PicoFeed\Parser + * @author Frederic Guillot + */ +class XmlParser +{ + /** + * Get a SimpleXmlElement instance or return false. + * + * @static + * @param string $input XML content + * @return mixed + */ + public static function getSimpleXml($input) + { + return self::scan($input); + } + + /** + * Get a DomDocument instance or return false. + * + * @static + * @param string $input XML content + * @return DOMDocument + */ + public static function getDomDocument($input) + { + if (empty($input)) { + return false; + } + + $dom = self::scan($input, new DOMDocument()); + + // The document is empty, there is probably some parsing errors + if ($dom && $dom->childNodes->length === 0) { + return false; + } + + return $dom; + } + + /** + * Small wrapper around ZendXml to turn their exceptions into PicoFeed exceptions + * + * @static + * @access private + * @param string $input + * @param DOMDocument $dom + * @throws XmlEntityException + * @return SimpleXMLElement|DomDocument|boolean + */ + private static function scan($input, $dom = null) + { + try { + return Security::scan($input, $dom); + } catch(RuntimeException $e) { + throw new XmlEntityException($e->getMessage()); + } + } + + /** + * Load HTML document by using a DomDocument instance or return false on failure. + * + * @static + * @access public + * @param string $input XML content + * @return DOMDocument + */ + public static function getHtmlDocument($input) + { + $dom = new DomDocument(); + + if (empty($input)) { + return $dom; + } + + libxml_use_internal_errors(true); + + if (version_compare(PHP_VERSION, '5.4.0', '>=')) { + $dom->loadHTML($input, LIBXML_NONET); + } else { + $dom->loadHTML($input); + } + + return $dom; + } + + /** + * Convert a HTML document to XML. + * + * @static + * @access public + * @param string $html HTML document + * @return string + */ + public static function htmlToXml($html) + { + $dom = self::getHtmlDocument('<?xml version="1.0" encoding="UTF-8">'.$html); + return $dom->saveXML($dom->getElementsByTagName('body')->item(0)); + } + + /** + * Get XML parser errors. + * + * @static + * @access public + * @return string + */ + public static function getErrors() + { + $errors = array(); + + foreach (libxml_get_errors() as $error) { + $errors[] = sprintf('XML error: %s (Line: %d - Column: %d - Code: %d)', + $error->message, + $error->line, + $error->column, + $error->code + ); + } + + return implode(', ', $errors); + } + + /** + * Get the encoding from a xml tag. + * + * @static + * @access public + * @param string $data Input data + * @return string + */ + public static function getEncodingFromXmlTag($data) + { + $encoding = ''; + + if (strpos($data, '<?xml') !== false) { + $data = substr($data, 0, strrpos($data, '?>')); + $data = str_replace("'", '"', $data); + + $p1 = strpos($data, 'encoding='); + $p2 = strpos($data, '"', $p1 + 10); + + if ($p1 !== false && $p2 !== false) { + $encoding = substr($data, $p1 + 10, $p2 - $p1 - 10); + $encoding = strtolower($encoding); + } + } + + return $encoding; + } + + /** + * Get the charset from a meta tag. + * + * @static + * @access public + * @param string $data Input data + * @return string + */ + public static function getEncodingFromMetaTag($data) + { + $encoding = ''; + + if (preg_match('/<meta.*?charset\s*=\s*["\']?\s*([^"\'\s\/>;]+)/i', $data, $match) === 1) { + $encoding = strtolower($match[1]); + } + + return $encoding; + } + + /** + * Rewrite XPath query to use namespace-uri and local-name derived from prefix. + * + * @static + * @access public + * @param string $query XPath query + * @param array $ns Prefix to namespace URI mapping + * @return string + */ + public static function replaceXPathPrefixWithNamespaceURI($query, array $ns) + { + return preg_replace_callback('/([A-Z0-9]+):([A-Z0-9]+)/iu', function ($matches) use ($ns) { + // don't try to map the special prefix XML + if (strtolower($matches[1]) === 'xml') { + return $matches[0]; + } + + return '*[namespace-uri()="'.$ns[$matches[1]].'" and local-name()="'.$matches[2].'"]'; + }, + $query); + } + + /** + * Get the result elements of a XPath query. + * + * @static + * @access public + * @param SimpleXMLElement $xml XML element + * @param string $query XPath query + * @param array $ns Prefix to namespace URI mapping + * @return SimpleXMLElement[] + */ + public static function getXPathResult(SimpleXMLElement $xml, $query, array $ns = array()) + { + if (!empty($ns)) { + $query = static::replaceXPathPrefixWithNamespaceURI($query, $ns); + } + + return $xml->xpath($query); + } + + /** + * Get the first Xpath result or SimpleXMLElement value + * + * @static + * @access public + * @param mixed $value + * @return string + */ + public static function getValue($value) + { + $result = ''; + + if (is_array($value) && count($value) > 0) { + $result = (string) $value[0]; + } elseif (is_a($value, 'SimpleXMLElement')) { + return $result = (string) $value; + } + + return trim($result); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/PicoFeedException.php b/vendor/miniflux/picofeed/lib/PicoFeed/PicoFeedException.php new file mode 100644 index 00000000..2de9e4b7 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/PicoFeedException.php @@ -0,0 +1,14 @@ +<?php + +namespace PicoFeed; + +use Exception; + +/** + * PicoFeedException Exception. + * + * @author Frederic Guillot + */ +abstract class PicoFeedException extends Exception +{ +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ContentFilterProcessor.php b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ContentFilterProcessor.php new file mode 100644 index 00000000..9b7ddcce --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ContentFilterProcessor.php @@ -0,0 +1,37 @@ +<?php + +namespace PicoFeed\Processor; + +use PicoFeed\Base; +use PicoFeed\Filter\Filter; +use PicoFeed\Logging\Logger; +use PicoFeed\Parser\Feed; +use PicoFeed\Parser\Item; + +/** + * Item Content Filter + * + * @package PicoFeed\Processor + * @author Frederic Guillot + */ +class ContentFilterProcessor extends Base implements ItemProcessorInterface +{ + /** + * Execute Item Processor + * + * @access public + * @param Feed $feed + * @param Item $item + * @return bool + */ + public function execute(Feed $feed, Item $item) + { + if ($this->config->getContentFiltering(true)) { + $filter = Filter::html($item->getContent(), $feed->getSiteUrl()); + $filter->setConfig($this->config); + $item->setContent($filter->execute()); + } else { + Logger::setMessage(get_called_class().': Content filtering disabled'); + } + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ContentGeneratorProcessor.php b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ContentGeneratorProcessor.php new file mode 100644 index 00000000..49adf9cc --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ContentGeneratorProcessor.php @@ -0,0 +1,49 @@ +<?php + +namespace PicoFeed\Processor; + +use PicoFeed\Base; +use PicoFeed\Parser\Feed; +use PicoFeed\Parser\Item; + +/** + * Item Content Generator + * + * @package PicoFeed\Processor + * @author Frederic Guillot + */ +class ContentGeneratorProcessor extends Base implements ItemProcessorInterface +{ + /** + * List of generators + * + * @access protected + * @var array + */ + protected $generators = array( + 'youtube', + 'file', + ); + + /** + * Execute Item Processor + * + * @access public + * @param Feed $feed + * @param Item $item + * @return bool + */ + public function execute(Feed $feed, Item $item) + { + foreach ($this->generators as $generator) { + $className = '\PicoFeed\Generator\\'.ucfirst($generator).'ContentGenerator'; + $object = new $className($this->config); + + if ($object->execute($item)) { + return true; + } + } + + return false; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ItemPostProcessor.php b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ItemPostProcessor.php new file mode 100644 index 00000000..d2787677 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ItemPostProcessor.php @@ -0,0 +1,106 @@ +<?php + +namespace PicoFeed\Processor; + +use PicoFeed\Base; +use PicoFeed\Parser\Feed; +use PicoFeed\Parser\Item; +use PicoFeed\Config\Config; + +/** + * Item Post Processor + * + * @package PicoFeed\Processor + * @author Frederic Guillot + */ +class ItemPostProcessor extends Base +{ + /** + * List of processors + * + * @access private + * @var array + */ + private $processors = array(); + + /** + * Execute all processors + * + * @access public + * @param Feed $feed + * @param Item $item + * @return bool + */ + public function execute(Feed $feed, Item $item) + { + foreach ($this->processors as $processor) { + if ($processor->execute($feed, $item)) { + return true; + } + } + + return false; + } + + /** + * Register a new Item post-processor + * + * @access public + * @param ItemProcessorInterface $processor + * @return ItemPostProcessor + */ + public function register(ItemProcessorInterface $processor) + { + $this->processors[get_class($processor)] = $processor; + return $this; + } + + /** + * Remove Processor instance + * + * @access public + * @param string $class + * @return ItemPostProcessor + */ + public function unregister($class) + { + if (isset($this->processors[$class])) { + unset($this->processors[$class]); + } + + return $this; + } + + /** + * Checks wheather a specific processor is registered or not + * + * @access public + * @param string $class + * @return bool + */ + public function hasProcessor($class) + { + return isset($this->processors[$class]); + } + + /** + * Get Processor instance + * + * @access public + * @param string $class + * @return ItemProcessorInterface|null + */ + public function getProcessor($class) + { + return isset($this->processors[$class]) ? $this->processors[$class] : null; + } + + public function setConfig(Config $config) + { + foreach ($this->processors as $processor) { + $processor->setConfig($config); + } + + return false; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ItemProcessorInterface.php b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ItemProcessorInterface.php new file mode 100644 index 00000000..5d532262 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ItemProcessorInterface.php @@ -0,0 +1,25 @@ +<?php + +namespace PicoFeed\Processor; + +use PicoFeed\Parser\Feed; +use PicoFeed\Parser\Item; + +/** + * Item Processor Interface + * + * @package PicoFeed\Processor + * @author Frederic Guillot + */ +interface ItemProcessorInterface +{ + /** + * Execute Item Processor + * + * @access public + * @param Feed $feed + * @param Item $item + * @return bool + */ + public function execute(Feed $feed, Item $item); +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ScraperProcessor.php b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ScraperProcessor.php new file mode 100644 index 00000000..0c467af0 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Processor/ScraperProcessor.php @@ -0,0 +1,96 @@ +<?php + +namespace PicoFeed\Processor; + +use Closure; +use PicoFeed\Base; +use PicoFeed\Parser\Feed; +use PicoFeed\Parser\Item; +use PicoFeed\Scraper\Scraper; + +/** + * Scraper Processor + * + * @package PicoFeed\Processor + * @author Frederic Guillot + */ +class ScraperProcessor extends Base implements ItemProcessorInterface +{ + private $ignoredUrls = array(); + private $scraper; + + /** + * Callback function for each scraper execution + * + * @var Closure + */ + private $executionCallback; + + /** + * Add a new execution callback + * + * @access public + * @param Closure $executionCallback + * @return $this + */ + public function setExecutionCallback(Closure $executionCallback) + { + $this->executionCallback = $executionCallback; + return $this; + } + + /** + * Execute Item Processor + * + * @access public + * @param Feed $feed + * @param Item $item + * @return bool + */ + public function execute(Feed $feed, Item $item) + { + if (!in_array($item->getUrl(), $this->ignoredUrls)) { + $scraper = $this->getScraper(); + $scraper->setUrl($item->getUrl()); + $scraper->execute(); + + if ($this->executionCallback && is_callable($this->executionCallback)) { + call_user_func($this->executionCallback, $feed, $item, $scraper); + } + + if ($scraper->hasRelevantContent()) { + $item->setContent($scraper->getFilteredContent()); + } + } + + return false; + } + + /** + * Ignore list of URLs + * + * @access public + * @param array $urls + * @return $this + */ + public function ignoreUrls(array $urls) + { + $this->ignoredUrls = $urls; + return $this; + } + + /** + * Returns Scraper instance + * + * @access public + * @return Scraper + */ + public function getScraper() + { + if ($this->scraper === null) { + $this->scraper = new Scraper($this->config); + } + + return $this->scraper; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Reader/Favicon.php b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/Favicon.php new file mode 100644 index 00000000..d4ca07db --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/Favicon.php @@ -0,0 +1,186 @@ +<?php + +namespace PicoFeed\Reader; + +use DOMXPath; +use PicoFeed\Base; +use PicoFeed\Client\Client; +use PicoFeed\Client\ClientException; +use PicoFeed\Client\Url; +use PicoFeed\Logging\Logger; +use PicoFeed\Parser\XmlParser; + +/** + * Favicon class. + * + * https://en.wikipedia.org/wiki/Favicon + * + * @author Frederic Guillot + */ +class Favicon extends Base +{ + /** + * Valid types for favicon (supported by browsers). + * + * @var array + */ + private $types = array( + 'image/png', + 'image/gif', + 'image/x-icon', + 'image/jpeg', + 'image/jpg', + 'image/svg+xml', + ); + + /** + * Icon binary content. + * + * @var string + */ + private $content = ''; + + /** + * Icon content type. + * + * @var string + */ + private $content_type = ''; + + /** + * Get the icon file content (available only after the download). + * + * @return string + */ + public function getContent() + { + return $this->content; + } + + /** + * Get the icon file type (available only after the download). + * + * @return string + */ + public function getType() + { + foreach ($this->types as $type) { + if (strpos($this->content_type, $type) === 0) { + return $type; + } + } + + return 'image/x-icon'; + } + + /** + * Get data URI (http://en.wikipedia.org/wiki/Data_URI_scheme). + * + * @return string + */ + public function getDataUri() + { + if (empty($this->content)) { + return ''; + } + + return sprintf( + 'data:%s;base64,%s', + $this->getType(), + base64_encode($this->content) + ); + } + + /** + * Download and check if a resource exists. + * + * @param string $url URL + * @return \PicoFeed\Client\Client Client instance + */ + public function download($url) + { + $client = Client::getInstance(); + $client->setConfig($this->config); + + Logger::setMessage(get_called_class().' Download => '.$url); + + try { + $client->execute($url); + } catch (ClientException $e) { + Logger::setMessage(get_called_class().' Download Failed => '.$e->getMessage()); + } + + return $client; + } + + /** + * Check if a remote file exists. + * + * @param string $url URL + * @return bool + */ + public function exists($url) + { + return $this->download($url)->getContent() !== ''; + } + + /** + * Get the icon link for a website. + * + * @param string $website_link URL + * @param string $favicon_link optional URL + * @return string + */ + public function find($website_link, $favicon_link = '') + { + $website = new Url($website_link); + + if ($favicon_link !== '') { + $icons = array($favicon_link); + } else { + $icons = $this->extract($this->download($website->getBaseUrl('/'))->getContent()); + $icons[] = $website->getBaseUrl('/favicon.ico'); + } + + foreach ($icons as $icon_link) { + $icon_link = Url::resolve($icon_link, $website); + $resource = $this->download($icon_link); + $this->content = $resource->getContent(); + $this->content_type = $resource->getContentType(); + + if ($this->content !== '') { + return $icon_link; + } elseif ($favicon_link !== '') { + return $this->find($website_link); + } + } + + return ''; + } + + /** + * Extract the icon links from the HTML. + * + * @param string $html HTML + * @return array + */ + public function extract($html) + { + $icons = array(); + + if (empty($html)) { + return $icons; + } + + $dom = XmlParser::getHtmlDocument($html); + + $xpath = new DOMXpath($dom); + $elements = $xpath->query('//link[@rel="icon" or @rel="shortcut icon" or @rel="Shortcut Icon" or @rel="icon shortcut"]'); + + for ($i = 0; $i < $elements->length; ++$i) { + $icons[] = $elements->item($i)->getAttribute('href'); + } + + return $icons; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Reader/Reader.php b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/Reader.php new file mode 100644 index 00000000..769ffe93 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/Reader.php @@ -0,0 +1,190 @@ +<?php + +namespace PicoFeed\Reader; + +use DOMXPath; +use PicoFeed\Base; +use PicoFeed\Client\Client; +use PicoFeed\Client\Url; +use PicoFeed\Logging\Logger; +use PicoFeed\Parser\XmlParser; + +/** + * Reader class. + * + * @author Frederic Guillot + */ +class Reader extends Base +{ + /** + * Feed formats for detection. + * + * @var array + */ + private $formats = array( + 'Atom' => '//feed', + 'Rss20' => '//rss[@version="2.0"]', + 'Rss92' => '//rss[@version="0.92"]', + 'Rss91' => '//rss[@version="0.91"]', + 'Rss10' => '//rdf', + ); + + /** + * Download a feed (no discovery). + * + * @param string $url Feed url + * @param string $last_modified Last modified HTTP header + * @param string $etag Etag HTTP header + * @param string $username HTTP basic auth username + * @param string $password HTTP basic auth password + * + * @return \PicoFeed\Client\Client + */ + public function download($url, $last_modified = '', $etag = '', $username = '', $password = '') + { + $url = $this->prependScheme($url); + + return Client::getInstance() + ->setConfig($this->config) + ->setLastModified($last_modified) + ->setEtag($etag) + ->setUsername($username) + ->setPassword($password) + ->execute($url); + } + + /** + * Discover and download a feed. + * + * @param string $url Feed or website url + * @param string $last_modified Last modified HTTP header + * @param string $etag Etag HTTP header + * @param string $username HTTP basic auth username + * @param string $password HTTP basic auth password + * @return Client + * @throws SubscriptionNotFoundException + */ + public function discover($url, $last_modified = '', $etag = '', $username = '', $password = '') + { + $client = $this->download($url, $last_modified, $etag, $username, $password); + + // It's already a feed or the feed was not modified + if (!$client->isModified() || $this->detectFormat($client->getContent())) { + return $client; + } + + // Try to find a subscription + $links = $this->find($client->getUrl(), $client->getContent()); + + if (empty($links)) { + throw new SubscriptionNotFoundException('Unable to find a subscription'); + } + + return $this->download($links[0], $last_modified, $etag, $username, $password); + } + + /** + * Find feed urls inside a HTML document. + * + * @param string $url Website url + * @param string $html HTML content + * + * @return array List of feed links + */ + public function find($url, $html) + { + Logger::setMessage(get_called_class().': Try to discover subscriptions'); + + $dom = XmlParser::getHtmlDocument($html); + $xpath = new DOMXPath($dom); + $links = array(); + + $queries = array( + '//link[@type="application/rss+xml"]', + '//link[@type="application/atom+xml"]', + ); + + foreach ($queries as $query) { + $nodes = $xpath->query($query); + + foreach ($nodes as $node) { + $link = $node->getAttribute('href'); + + if (!empty($link)) { + $feedUrl = new Url($link); + $siteUrl = new Url($url); + + $links[] = $feedUrl->getAbsoluteUrl($feedUrl->isRelativeUrl() ? $siteUrl->getBaseUrl() : ''); + } + } + } + + Logger::setMessage(get_called_class().': '.implode(', ', $links)); + + return $links; + } + + /** + * Get a parser instance. + * + * @param string $url Site url + * @param string $content Feed content + * @param string $encoding HTTP encoding + * + * @return \PicoFeed\Parser\Parser + */ + public function getParser($url, $content, $encoding) + { + $format = $this->detectFormat($content); + + if (empty($format)) { + throw new UnsupportedFeedFormatException('Unable to detect feed format'); + } + + $className = '\PicoFeed\Parser\\'.$format; + + $parser = new $className($content, $encoding, $url); + $parser->setHashAlgo($this->config->getParserHashAlgo()); + $parser->setConfig($this->config); + + return $parser; + } + + /** + * Detect the feed format. + * + * @param string $content Feed content + * + * @return string + */ + public function detectFormat($content) + { + $dom = XmlParser::getHtmlDocument($content); + $xpath = new DOMXPath($dom); + + foreach ($this->formats as $parser_name => $query) { + $nodes = $xpath->query($query); + + if ($nodes->length === 1) { + return $parser_name; + } + } + + return ''; + } + + /** + * Add the prefix "http://" if the end-user just enter a domain name. + * + * @param string $url Url + * @retunr string + */ + public function prependScheme($url) + { + if (!preg_match('%^https?://%', $url)) { + $url = 'http://'.$url; + } + + return $url; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Reader/ReaderException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/ReaderException.php new file mode 100644 index 00000000..4f03dbe0 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/ReaderException.php @@ -0,0 +1,14 @@ +<?php + +namespace PicoFeed\Reader; + +use PicoFeed\PicoFeedException; + +/** + * ReaderException Exception. + * + * @author Frederic Guillot + */ +abstract class ReaderException extends PicoFeedException +{ +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Reader/SubscriptionNotFoundException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/SubscriptionNotFoundException.php new file mode 100644 index 00000000..dd77847e --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/SubscriptionNotFoundException.php @@ -0,0 +1,12 @@ +<?php + +namespace PicoFeed\Reader; + +/** + * SubscriptionNotFoundException Exception. + * + * @author Frederic Guillot + */ +class SubscriptionNotFoundException extends ReaderException +{ +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Reader/UnsupportedFeedFormatException.php b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/UnsupportedFeedFormatException.php new file mode 100644 index 00000000..4aa8d87f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Reader/UnsupportedFeedFormatException.php @@ -0,0 +1,12 @@ +<?php + +namespace PicoFeed\Reader; + +/** + * UnsupportedFeedFormatException Exception. + * + * @author Frederic Guillot + */ +class UnsupportedFeedFormatException extends ReaderException +{ +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.blog.lemonde.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.blog.lemonde.fr.php new file mode 100644 index 00000000..773616c4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.blog.lemonde.fr.php @@ -0,0 +1,14 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://combat.blog.lemonde.fr/2013/08/31/teddy-riner-le-rookie-devenu-rambo/#xtor=RSS-3208', + 'body' => array( + '//div[@class="entry-content"]', + ), + 'strip' => array( + '//*[contains(@class, "fb-like") or contains(@class, "social")]' + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.blogs.nytimes.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.blogs.nytimes.com.php new file mode 100644 index 00000000..ee641b09 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.blogs.nytimes.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'title' => '//header/h1', + 'test_url' => 'http://bits.blogs.nytimes.com/2012/01/16/wikipedia-plans-to-go-dark-on-wednesday-to-protest-sopa/', + 'body' => array( + '//div[@class="postContent"]', + ), + 'strip' => array( + '//*[@class="shareToolsBox"]', + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.igen.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.igen.fr.php new file mode 100644 index 00000000..f2028f4e --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.igen.fr.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.igen.fr/ailleurs/2014/05/nvidia-va-delaisser-les-smartphones-grand-public-86031', + 'body' => array( + '//div[contains(@class, "field-name-body")]' + ), + 'strip' => array( + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.nytimes.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.nytimes.com.php new file mode 100644 index 00000000..ed27bb5c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.nytimes.com.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.nytimes.com/2011/05/15/world/middleeast/15prince.html', + 'body' => array( + '//div[@class="articleBody"]', + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.over-blog.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.over-blog.com.php new file mode 100644 index 00000000..cc5d83c7 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.over-blog.com.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://eliascarpe.over-blog.com/2015/12/re-upload-projets-d-avenir.html', + 'body' => array( + '//div[contains(concat(" ", normalize-space(@class), " "), " ob-section ")]', + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.phoronix.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.phoronix.com.php new file mode 100644 index 00000000..66713f71 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.phoronix.com.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.phoronix.com/scan.php?page=article&item=amazon_ec2_bare&num=1', + 'body' => array( + '//div[@class="content"]', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.slate.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.slate.com.php new file mode 100644 index 00000000..a795bca3 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.slate.com.php @@ -0,0 +1,20 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.slate.com/articles/business/moneybox/2013/08/microsoft_ceo_steve_ballmer_retires_a_firsthand_account_of_the_company_s.html', + 'body' => array( + '//div[@class="sl-art-body"]', + ), + 'strip' => array( + '//*[contains(@class, "social") or contains(@class, "comments") or contains(@class, "sl-article-floatin-tools") or contains(@class, "sl-art-pag")]', + '//*[@id="mys_slate_logged_in"]', + '//*[@id="sl_article_tools_myslate_bottom"]', + '//*[@id="mys_myslate"]', + '//*[@class="sl-viral-container"]', + '//*[@class="sl-art-creds-cntr"]', + '//*[@class="sl-art-ad-midflex"]', + ) + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.theguardian.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.theguardian.com.php new file mode 100644 index 00000000..e0d6f3fd --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.theguardian.com.php @@ -0,0 +1,14 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.theguardian.com/sustainable-business/2015/feb/02/2015-hyper-transparency-global-business', + 'body' => array( + '//div[contains(@class, "content__main-column--article")]', + ), + 'strip' => array( + '//div[contains(@class, "meta-container")]', + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wikipedia.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wikipedia.org.php new file mode 100644 index 00000000..7b8f76e5 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wikipedia.org.php @@ -0,0 +1,29 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://en.wikipedia.org/wiki/Grace_Hopper', + 'body' => array( + '//div[@id="bodyContent"]', + ), + 'strip' => array( + "//div[@id='toc']", + "//div[@id='catlinks']", + "//div[@id='jump-to-nav']", + "//div[@class='thumbcaption']//div[@class='magnify']", + "//table[@class='navbox']", + "//table[contains(@class, 'infobox')]", + "//div[@class='dablink']", + "//div[@id='contentSub']", + "//div[@id='siteSub']", + "//table[@id='persondata']", + "//table[contains(@class, 'metadata')]", + "//*[contains(@class, 'noprint')]", + "//*[contains(@class, 'printfooter')]", + "//*[contains(@class, 'editsection')]", + "//*[contains(@class, 'error')]", + "//span[@title='pronunciation:']", + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wired.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wired.com.php new file mode 100644 index 00000000..952b09ac --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wired.com.php @@ -0,0 +1,44 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.wired.com/gamelife/2013/09/ouya-free-the-games/', + 'body' => array( + '//div[@data-js="gallerySlides"]', + '//div[starts-with(@class,"post")]', + ), + 'strip' => array( + '//h1', + '//nav', + '//button', + '//figure[starts-with(@class,"rad-slide")]', + '//figure[starts-with(@class,"end-slate")]', + '//div[contains(@class,"mobile-")]', + '//div[starts-with(@class,"mob-gallery-launcher")]', + '//div[contains(@id,"mobile-")]', + '//span[contains(@class,"slide-count")]', + '//div[contains(@class,"show-ipad")]', + '//img[contains(@id,"-hero-bg")]', + '//div[@data-js="overlayWrap"]', + '//ul[contains(@class,"metadata")]', + '//div[@class="opening center"]', + '//p[contains(@class="byline-mob"]', + '//div[@id="o-gallery"]', + '//div[starts-with(@class,"sm-col")]', + '//div[contains(@class,"pad-b-huge")]', + '//a[contains(@class,"visually-hidden")]', + '//*[@class="social"]', + '//i', + '//div[@data-js="mobGalleryAd"]', + '//div[contains(@class,"footer")]', + '//div[contains(@data-js,"fader")]', + '//div[@id="sharing"]', + '//div[contains(@id,"related")]', + '//div[@id="most-pop"]', + '//ul[@id="article-tags"]', + '//style', + '//section[contains(@class,"footer")]' + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wsj.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wsj.com.php new file mode 100644 index 00000000..f6e6cc12 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/.wsj.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://online.wsj.com/article/SB10001424127887324108204579023143974408428.html', + 'body' => array( + '//div[@class="articlePage"]', + ), + 'strip' => array( + '//*[@id="articleThumbnail_2"]', + '//*[@class="socialByline"]', + ) + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/01net.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/01net.com.php new file mode 100644 index 00000000..6d144f05 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/01net.com.php @@ -0,0 +1,18 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.01net.com/editorial/624550/twitter-rachete-madbits-un-specialiste-francais-de-lanalyse-dimages/', + 'body' => array( + '//div[@class="article_ventre_box"]', + ), + 'strip' => array( + '//link', + '//*[contains(@class, "article_navigation")]', + '//h1', + '//*[contains(@class, "article_toolbarMain")]', + '//*[contains(@class, "article_imagehaute_box")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/abstrusegoose.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/abstrusegoose.com.php new file mode 100644 index 00000000..752d0413 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/abstrusegoose.com.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%alt="(.+)" title="(.+)" */>%' => '/><br/>$1<br/>$2', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/adventuregamers.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/adventuregamers.com.php new file mode 100644 index 00000000..98d384e6 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/adventuregamers.com.php @@ -0,0 +1,23 @@ +<?php +return array( + 'grabber' => array( + '%^/news.*%' => array( + 'test_url' => 'http://www.adventuregamers.com/news/view/31079', + 'body' => array( + '//div[@class="bodytext"]', + ) + ), + '%^/videos.*%' => array( + 'test_url' => 'http://www.adventuregamers.com/videos/view/31056', + 'body' => array( + '//iframe', + ) + ), + '%^/articles.*%' => array( + 'test_url' => 'http://www.adventuregamers.com/articles/view/31049', + 'body' => array( + '//div[@class="cleft"]', + ) + ) + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/alainonline.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/alainonline.net.php new file mode 100644 index 00000000..f440b234 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/alainonline.net.php @@ -0,0 +1,14 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.alainonline.net/news_details.php?lang=arabic&sid=18907', + 'body' => array( + '//div[@class="news_details"]', + ), + 'strip' => array( + '//div[@class="news_details"]/div/div[last()]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/aljazeera.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/aljazeera.com.php new file mode 100644 index 00000000..c02eb219 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/aljazeera.com.php @@ -0,0 +1,25 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.aljazeera.com/news/2015/09/xi-jinping-seattle-china-150922230118373.html', + 'body' => array( + '//article[@id="main-story"]', + ), + 'strip' => array( + '//script', + '//header', + '//ul', + '//section[contains(@class,"profile")]', + '//a[@target="_self"]', + '//div[contains(@id,"_2")]', + '//div[contains(@id,"_3")]', + '//img[@class="viewMode"]', + '//table[contains(@class,"in-article-item")]', + '//div[@data-embed-type="Brightcove"]', + '//div[@class="QuoteContainer"]', + '//div[@class="BottomByLine"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/allafrica.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/allafrica.com.php new file mode 100644 index 00000000..e8a506d4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/allafrica.com.php @@ -0,0 +1,20 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.aljazeera.com/news/2015/09/xi-jinping-seattle-china-150922230118373.html', + 'body' => array( + '//div[@class="story-body"]', + ), + 'strip' => array( + '//p[@class="kindofstory"]', + '//cite[@class="byline"]', + '//div[@class="useful-top"]', + '//div[contains(@class,"related-topics")]', + '//links', + '//sharebar', + '//related-topics', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/allgemeine-zeitung.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/allgemeine-zeitung.de.php new file mode 100644 index 00000000..8ede99b1 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/allgemeine-zeitung.de.php @@ -0,0 +1,23 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.allgemeine-zeitung.de/lokales/polizei/mainz-gonsenheim-unbekannte-rauben-esso-tankstelle-in-kurt-schumacher-strasse-aus_14913147.htm', + 'body' => array( + '//div[contains(@class, "article")][1]', + ), + 'strip' => array( + '//read/h1', + '//*[@id="t-map"]', + '//*[contains(@class, "modules")]', + '//*[contains(@class, "adsense")]', + '//*[contains(@class, "linkbox")]', + '//*[contains(@class, "info")]', + '//*[@class="skip"]', + '//*[@class="funcs"]', + '//span[@class="nd address"]', + '//a[contains(@href, "abo-und-services")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/amazingsuperpowers.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/amazingsuperpowers.com.php new file mode 100644 index 00000000..3214c62a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/amazingsuperpowers.com.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%title="(.+)" */>%' => '/><br/>$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/anythingcomic.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/anythingcomic.com.php new file mode 100644 index 00000000..51247f76 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/anythingcomic.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'body' => array( + '//img[@id="comic_image"]', + '//div[@class="comment-wrapper"][position()=1]', + ), + 'strip' => array(), + 'test_url' => 'http://www.anythingcomic.com/comics/2108929/stress-free/', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ap.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ap.org.php new file mode 100644 index 00000000..5bb2bb6c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ap.org.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://hosted.ap.org/dynamic/stories/A/AS_CHINA_GAO_ZHISHENG?SITE=AP&SECTION=HOME&TEMPLATE=DEFAULT', + 'body' => array( + '//img[@class="ap-smallphoto-img"]', + '//span[@class="entry-content"]', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/areadvd.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/areadvd.de.php new file mode 100644 index 00000000..fc569220 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/areadvd.de.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.areadvd.de/news/daily-deals-angebote-bei-lautsprecher-teufel-3/', + 'body' => array('//div[contains(@class,"entry")]'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/arstechnica.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/arstechnica.com.php new file mode 100644 index 00000000..55e01ce3 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/arstechnica.com.php @@ -0,0 +1,25 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://arstechnica.com/tech-policy/2015/09/judge-warners-2m-happy-birthday-copyright-is-bogus/', + 'body' => array( + '//article', + ), + 'strip' => array( + '//h4[@class="post-upperdek"]', + '//h1', + '//ul[@class="lSPager lSGallery"]', + '//div[@class="lSAction"]', + '//section[@class="post-meta"]', + '//figcaption', + '//aside', + '//div[@class="gallery-image-credit"]', + '//section[@class="article-author"]', + '//*[contains(@id,"social-")]', + '//div[contains(@id,"footer")]', + ), + ), + ), +); + diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/awkwardzombie.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/awkwardzombie.com.php new file mode 100644 index 00000000..5ab70514 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/awkwardzombie.com.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%/index.php.*comic=.*%' => array( + 'test_url' => 'http://www.awkwardzombie.com/index.php?comic=041315', + 'body' => array('//*[@id="comic"]/img'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/backchannel.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/backchannel.com.php new file mode 100644 index 00000000..bc5932a2 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/backchannel.com.php @@ -0,0 +1,18 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://medium.com/lessons-learned/917b8b63ae3e', + 'body' => array( + '//div[contains(@class,"section-inner")]', + ), + 'strip' => array( + '//div[contains(@class,"metabar")]', + '//img[contains(@class,"thumbnail")]', + '//h1', + '//blockquote', + '//p[contains(@class,"graf-after--h4")]' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bangkokpost.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bangkokpost.com.php new file mode 100644 index 00000000..165515bb --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bangkokpost.com.php @@ -0,0 +1,19 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.bangkokpost.com/news/politics/704204/new-us-ambassador-arrives-in-bangkok', + 'body' => array( + '//article/div[@class="articleContents"]', + ), + 'strip' => array( + '//h2', + '//h4', + '//div[@class="text-size"]', + '//div[@class="relate-story"]', + '//div[@class="text-ads"]', + '//ul', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bgr.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bgr.com.php new file mode 100644 index 00000000..7507a2f1 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bgr.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://bgr.com/2015/09/27/iphone-6s-waterproof-testing/', + 'body' => array( + '//img[contains(@class,"img")]', + '//div[@class="text-column"]', + ), + 'strip' => array( + '//strong', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bigfootjustice.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bigfootjustice.com.php new file mode 100644 index 00000000..d06ed124 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bigfootjustice.com.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%-150x150%' => '', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bigpicture.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bigpicture.ru.php new file mode 100644 index 00000000..55c40894 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bigpicture.ru.php @@ -0,0 +1,31 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://bigpicture.ru/?p=556658', + 'body' => array( + '//div[@class="article container"]', + ), + 'strip' => array( + '//script', + '//form', + '//style', + '//h1', + '//*[@class="wp-smiley"]', + '//div[@class="ipmd"]', + '//div[@class="tags"]', + '//div[@class="social-button"]', + '//div[@class="bottom-share"]', + '//div[@class="raccoonbox"]', + '//div[@class="yndadvert"]', + '//div[@class="we-recommend"]', + '//div[@class="relap-bigpicture_ru-wrapper"]', + '//div[@id="mmail"]', + '//div[@id="mobile-ads-cut"]', + '//div[@id="liquidstorm-alt-html"]', + '//div[contains(@class, "post-tags")]', + '//*[contains(text(),"Смотрите также")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bizjournals.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bizjournals.com.php new file mode 100644 index 00000000..d1cc3da9 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bizjournals.com.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.bizjournals.com/milwaukee/news/2015/09/30/bucks-will-hike-prices-on-best-seats-at-new-arena.html', + 'body' => array( + '//figure/div/a/img', + '//p[@class="content__segment"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/biztimes.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/biztimes.com.php new file mode 100644 index 00000000..d21aa98c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/biztimes.com.php @@ -0,0 +1,22 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://www.biztimes.com/2017/02/10/settlement-would-revive-fowler-lake-condo-project-in-oconomowoc/', + 'body' => array( + '//h2/span[@class="subhead"]', + '//div[contains(@class,"article-content")]', + ), + 'strip' => array( + '//script', + '//div[contains(@class,"mobile-article-content")]', + '//div[contains(@class,"sharedaddy")]', + '//div[contains(@class,"author-details")]', + '//div[@class="row ad"]', + '//div[contains(@class,"relatedposts")]', + '//div[@class="col-lg-12"]', + '//div[contains(@class,"widget")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bleepingcomputer.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bleepingcomputer.com.php new file mode 100644 index 00000000..7b740600 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bleepingcomputer.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://www.bleepingcomputer.com/news/google/chromes-sandbox-feature-infringes-on-three-patents-so-google-must-now-pay-20m/', + 'body' => array( + '//div[@class="article_section"]', + ), + 'strip' => array( + '//*[@itemprop="headline"]', + '//div[@class="cz-news-story-title-section"]' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/blog.fefe.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/blog.fefe.de.php new file mode 100644 index 00000000..39c88ae4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/blog.fefe.de.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://blog.fefe.de/?ts=ad706a73', + 'body' => array( + '/html/body/ul', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/blog.mapillary.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/blog.mapillary.com.php new file mode 100644 index 00000000..ce016510 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/blog.mapillary.com.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://blog.mapillary.com/update/2015/08/26/traffic-sign-updates.html', + 'body' => array( + '//div[contains(@class, "blog-post__content")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/brewers.mlb.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/brewers.mlb.com.php new file mode 100644 index 00000000..be406faf --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/brewers.mlb.com.php @@ -0,0 +1,22 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://m.brewers.mlb.com/news/article/161364798', + 'body' => array( + '//article[contains(@class,"article")]', + ), + 'strip' => array( + '//div[contains(@class,"ad-slot")]', + '//h1', + '//span[@class="timestamp"]', + '//div[contains(@class,"contributor-bottom")]', + '//div[contains(@class,"video")]', + '//ul[contains(@class,"social")]', + '//p[@class="tagline"]', + '//div[contains(@class,"social")]', + '//div[@class="button-wrap"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/buenosairesherald.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/buenosairesherald.com.php new file mode 100644 index 00000000..4e73e79f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/buenosairesherald.com.php @@ -0,0 +1,17 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.buenosairesherald.com/article/199344/manzur-named-next-governor-of-tucum%C3%A1n', + 'body' => array( + '//div[@style="float:none"]', + ), + 'strip' => array( + '//div[contains(@class, "bz_alias_short_desc_container"]', + '//td[@id="bz_show_bug_column_1"]', + '//table[@id="attachment_table"]', + '//table[@class="bz_comment_table"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bunicomic.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bunicomic.com.php new file mode 100644 index 00000000..ad83e436 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/bunicomic.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.bunicomic.com/comic/buni-623/', + 'body' => array( + '//div[@class="comic-table"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/buttersafe.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/buttersafe.com.php new file mode 100644 index 00000000..1f313cd0 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/buttersafe.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://buttersafe.com/2015/04/21/the-incredible-flexible-man/', + 'body' => array( + '//div[@id="comic"]', + '//div[@class="post-comic"]', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cad-comic.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cad-comic.com.php new file mode 100644 index 00000000..a631c97f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cad-comic.com.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%/cad/.+%' => array( + 'test_url' => 'http://www.cad-comic.com/cad/20150417', + 'body' => array( + '//*[@id="content"]/img', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/chaoslife.findchaos.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/chaoslife.findchaos.com.php new file mode 100644 index 00000000..ea6191e8 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/chaoslife.findchaos.com.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://chaoslife.findchaos.com/pets-in-the-wild', + 'body' => array('//div[@id="comic"]'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/chinafile.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/chinafile.com.php new file mode 100644 index 00000000..450117b3 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/chinafile.com.php @@ -0,0 +1,18 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.chinafile.com/books/shanghai-faithful?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+chinafile%2FAll+%28ChinaFile%29', + 'body' => array( + '//div[contains(@class,"pane-featured-photo-panel-pane-1")]', + '//div[contains(@class,"video-above-fold")]', + '//div[@class="sc-media"]', + '//div[contains(@class,"field-name-body")]', + ), + 'strip' => array( + '//div[contains(@class,"cboxes")]', + '//div[contains(@class,"l-middle")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cliquerefresh.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cliquerefresh.com.php new file mode 100644 index 00000000..9dcc7e54 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cliquerefresh.com.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%/comic.*%' => array( + 'test_url' => 'http://cliquerefresh.com/comic/078-stating-the-obvious/', + 'body' => array('//div[@class="comicImg"]/img | //div[@class="comicImg"]/a/img'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cnet.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cnet.com.php new file mode 100644 index 00000000..60767a53 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cnet.com.php @@ -0,0 +1,37 @@ +<?php +return array( + 'grabber' => array( + '%^/products.*%' => array( + 'test_url' => 'http://www.cnet.com/products/fibaro-flood-sensor/#ftag=CADf328eec', + 'body' => array( + '//li[contains(@class,"slide first"] || //figure[contains(@class,(promoFigure))]', + '//div[@class="quickInfo"]', + '//div[@class="col-6 ratings"]', + '//div[@id="editorReview"]', + ), + 'strip' => array( + '//script', + '//a[@class="clickToEnlarge"]', + '//div[@section="topSharebar"]', + '//div[contains(@class,"related")]', + '//div[contains(@class,"ad-")]', + '//div[@section="shortcodeGallery"]', + ), + ), + '%.*%' => array( + 'test_url' => 'http://cnet.com.feedsportal.com/c/34938/f/645093/s/4a340866/sc/28/l/0L0Scnet0N0Cnews0Cman0Eclaims0Eonline0Epsychic0Emade0Ehim0Ebuy0E10Emillion0Epowerball0Ewinning0Eticket0C0Tftag0FCAD590Aa51e/story01.htm', + 'body' => array( + '//p[@itemprop="description"]', + '//div[@itemprop="articleBody"]', + ), + 'strip' => array( + '//script', + '//a[@class="clickToEnlarge"]', + '//div[@section="topSharebar"]', + '//div[contains(@class,"related")]', + '//div[contains(@class,"ad-")]', + '//div[@section="shortcodeGallery"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/consomac.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/consomac.fr.php new file mode 100644 index 00000000..9209f9cb --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/consomac.fr.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://consomac.fr/news-2430-l-iphone-6-toujours-un-secret-bien-garde.html', + 'body' => array( + '//div[contains(@id, "newscontent")]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cowbirdsinlove.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cowbirdsinlove.com.php new file mode 100644 index 00000000..3214c62a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/cowbirdsinlove.com.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%title="(.+)" */>%' => '/><br/>$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/crash.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/crash.net.php new file mode 100644 index 00000000..9d9b35a6 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/crash.net.php @@ -0,0 +1,28 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.crash.net/motogp/interview/247550/1/exclusive-andrea-dovizioso-interview.html', + 'body' => array( + '//div[@id="content"]', + ), + 'strip' => array( + '//script', + '//style', + '//*[@title="Social Networking"]', + '//*[@class="crash-ad2"]', + '//*[@class="clearfix"]', + '//*[@class="crash-ad2"]', + '//*[contains(@id, "divCB"]', + '//*[@class="pnlComment"]', + '//*[@class="comments-tabs"]', + '//*[contains(@class, "ad-twocol"]', + '//*[@class="stories-list"]', + '//*[contains(@class, "btn")]', + '//*[@class="content"]', + '//h3', + ), + ), + ), +); + diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/csmonitor.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/csmonitor.com.php new file mode 100644 index 00000000..481e4b09 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/csmonitor.com.php @@ -0,0 +1,19 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.csmonitor.com/USA/Politics/2015/0925/John-Boehner-steps-down-Self-sacrificing-but-will-it-lead-to-better-government', + 'body' => array( + '//h2[@id="summary"]', + '//div[@class="flex-video youtube"]', + '//div[contains(@class,"eza-body")]', + ), + 'strip' => array( + '//span[@id="breadcrumb"]', + '//div[@id="byline-wrapper"]', + '//div[@class="injection"]', + '//*[contains(@class,"promo_link")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailyjs.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailyjs.com.php new file mode 100644 index 00000000..20eb1d75 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailyjs.com.php @@ -0,0 +1,19 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://dailyjs.com/2014/08/07/p5js/', + 'body' => array( + '//div[@id="post"]', + ), + 'strip' => array( + '//h2[@class="post"]', + '//div[@class="meta"]', + '//*[contains(@class, "addthis_toolbox")]', + '//*[contains(@class, "addthis_default_style")]', + '//*[@class="navigation small"]', + '//*[@id="related"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailyreporter.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailyreporter.com.php new file mode 100644 index 00000000..db3fc0e1 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailyreporter.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://dailyreporter.com/2016/01/09/us-supreme-court-case-could-weaken-government-workers-unions/', + 'body' => array( + '//div[contains(@class, "entry-content")]', + ), + 'strip' => array( + '//div[@class="dmcss_login_form"]', + '//*[contains(@class, "sharedaddy")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailytech.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailytech.com.php new file mode 100644 index 00000000..5d1df4a9 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dailytech.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.dailytech.com/Apples+First+Fixes+to+iOS+9+Land+w+iOS++901+Release/article37495.htm', + 'body' => array( + '//div[@class="NewsBodyImage"]', + '//span[@id="lblSummary"]', + '//span[@id="lblBody"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/degroupnews.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/degroupnews.com.php new file mode 100644 index 00000000..91f5c56e --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/degroupnews.com.php @@ -0,0 +1,14 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.degroupnews.com/medias/vodsvod/amazon-concurrence-la-chromecast-de-google-avec-fire-tv-stick', + 'body' => array( + '//div[@class="contenu"]', + ), + 'strip' => array( + '//div[contains(@class, "a2a")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/derstandard.at.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/derstandard.at.php new file mode 100644 index 00000000..7e95a51f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/derstandard.at.php @@ -0,0 +1,14 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://derstandard.at/2000010267354/The-Witcher-3-Hohe-Hardware-Anforderungen-fuer-PC-Spieler?ref=rss', + 'body' => array( + '//div[@class="copytext"]', + '//ul[@id="media-list"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dilbert.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dilbert.com.php new file mode 100644 index 00000000..b8e9b3d6 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dilbert.com.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'body' => array( + '//img[@class="img-responsive img-comic"]', + ), + 'test_url' => 'http://dilbert.com/strip/2016-01-28', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/discovermagazine.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/discovermagazine.com.php new file mode 100644 index 00000000..ae0dfe71 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/discovermagazine.com.php @@ -0,0 +1,26 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://blogs.discovermagazine.com/neuroskeptic/2017/01/25/publishers-jeffrey-beall/', + 'body' => array( + '//div[@class="contentWell"]', + ), + 'strip' => array( + '//h1', + '//div[@class="breadcrumbs"]', + '//div[@class="mobile"]', + '//div[@class="fromIssue"]', + '//div[contains(@class,"belowDeck")]', + '//div[@class="meta"]', + '//div[@class="shareIcons"]', + '//div[@class="categories"]', + '//div[@class="navigation"]', + '//div[@class="heading"]', + '//div[contains(@id,"-ad")]', + '//div[@class="relatedArticles"]', + '//div[@id="disqus_thread"]' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/distrowatch.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/distrowatch.com.php new file mode 100644 index 00000000..aefc8f81 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/distrowatch.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://distrowatch.com/?newsid=08355', + 'body' => array( + '//td[@class="NewsText"][1]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dozodomo.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dozodomo.com.php new file mode 100644 index 00000000..e1166957 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/dozodomo.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://dozodomo.com/bento/2014/03/04/lart-des-maki-de-takayo-kiyota/', + 'body' => array( + '//div[@class="joke"]', + '//div[@class="story-cover"]', + '//div[@class="story-content"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/drawingboardcomic.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/drawingboardcomic.com.php new file mode 100644 index 00000000..cd30f2e0 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/drawingboardcomic.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'body' => array('//img[@id="comicimage"]'), + 'strip' => array(), + 'test_url' => 'http://drawingboardcomic.com/index.php?comic=208', + ), + ), + 'filter' => array( + '%.*%' => array( + '%title="(.+)" */>%' => '/><br/>$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/e-w-e.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/e-w-e.ru.php new file mode 100644 index 00000000..8139cc9a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/e-w-e.ru.php @@ -0,0 +1,22 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://e-w-e.ru/16-prekrasnyx-izobretenij-zhenshhin/', + 'body' => array( + '//div[contains(@class, "post_text")]', + ), + 'strip' => array( + '//script', + '//form', + '//style', + '//*[@class="views_post"]', + '//*[@class="adman_mobile"]', + '//*[@class="adman_desctop"]', + '//*[contains(@rel, "nofollow")]', + '//*[contains(@class, "wp-smiley")]', + '//*[contains(text(),"Источник:")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/economist.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/economist.com.php new file mode 100644 index 00000000..522032fd --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/economist.com.php @@ -0,0 +1,25 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.economist.com/blogs/buttonwood/2017/02/mixed-signals?fsrc=rss', + 'body' => array( + '//article', + ), + 'strip' => array( + '//span[@class="blog-post__siblings-list-header "]', + '//h1', + '//aside', + '//div[@class="blog-post__asideable-wrapper"]', + '//div[@class="share_inline_header"]', + '//div[@id="column-right"]', + '//div[contains(@class,"blog-post__siblings-list-aside")]', + '//div[@class="video-player__wrapper"]', + '//div[@class="blog-post__bottom-panel"]', + '//div[contains(@class,"latest-updates-panel__container")]', + '//div[contains(@class,"blog-post__asideable-content")]', + '//div[@aria-label="Advertisement"]' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/encyclopedie.naheulbeuk.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/encyclopedie.naheulbeuk.com.php new file mode 100644 index 00000000..19bcbdef --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/encyclopedie.naheulbeuk.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://encyclopedie.naheulbeuk.com/article.php3?id_article=352', + 'body' => array( + '//td//h1[@class="titre-texte"]', + '//td//div[@class="surtitre"]', + '//td//div[@class="texte"]', + ), + ) + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/endlessorigami.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/endlessorigami.com.php new file mode 100644 index 00000000..d06ed124 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/endlessorigami.com.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%-150x150%' => '', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/engadget.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/engadget.com.php new file mode 100644 index 00000000..cf9e4485 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/engadget.com.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.engadget.com/2015/04/20/dark-matter-discovery/?ncid=rss_truncated', + 'body' => array('//div[@id="page_body"]/div[@class="container@m-"]'), + 'strip' => array('//aside[@role="banner"]'), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/escapistmagazine.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/escapistmagazine.com.php new file mode 100644 index 00000000..e86b59cb --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/escapistmagazine.com.php @@ -0,0 +1,45 @@ +<?php +return array( + 'grabber' => array( + '%/articles/view/comicsandcosplay/comics/critical-miss.*%' => array( + 'body' => array('//*[@class="body"]/span/img | //div[@class="folder_nav_links"]/following::p'), + 'test_url' => 'http://www.escapistmagazine.com/articles/view/comicsandcosplay/comics/critical-miss/13776-Critical-Miss-on-Framerates?utm_source=rss&utm_medium=rss&utm_campaign=articles', + 'strip' => array(), + ), + '%/articles/view/comicsandcosplay/comics/namegame.*%' => array( + 'body' => array('//*[@class="body"]/span/p/img[@height != "120"]'), + 'test_url' => 'http://www.escapistmagazine.com/articles/view/comicsandcosplay/comics/namegame/9759-Leaving-the-Nest?utm_source=rss&utm_medium=rss&utm_campaign=articles', + 'strip' => array(), + ), + '%/articles/view/comicsandcosplay/comics/stolen-pixels.*%' => array( + 'body' => array('//*[@class="body"]/span/p[2]/img'), + 'test_url' => 'http://www.escapistmagazine.com/articles/view/comicsandcosplay/comics/stolen-pixels/8866-Stolen-Pixels-258-Where-the-Boys-Are?utm_source=rss&utm_medium=rss&utm_campaign=articles', + 'strip' => array(), + ), + '%/articles/view/comicsandcosplay/comics/bumhugparade.*%' => array( + 'body' => array('//*[@class="body"]/span/p[2]/img'), + 'test_url' => 'http://www.escapistmagazine.com/articles/view/comicsandcosplay/comics/bumhugparade/8262-Bumhug-Parade-13?utm_source=rss&utm_medium=rss&utm_campaign=articles', + 'strip' => array(), + ), + '%/articles/view/comicsandcosplay.*/comics/escapistradiotheater%' => array( + 'body' => array('//*[@class="body"]/span/p[2]/img'), + 'test_url' => 'http://www.escapistmagazine.com/articles/view/comicsandcosplay/comics/escapistradiotheater/8265-The-Escapist-Radio-Theater-13?utm_source=rss&utm_medium=rss&utm_campaign=articles', + 'strip' => array(), + ), + '%/articles/view/comicsandcosplay/comics/paused.*%' => array( + 'body' => array('//*[@class="body"]/span/p[2]/img | //*[@class="body"]/span/div/img'), + 'test_url' => 'http://www.escapistmagazine.com/articles/view/comicsandcosplay/comics/paused/8263-Paused-16?utm_source=rss&utm_medium=rss&utm_campaign=articles', + 'strip' => array(), + ), + '%/articles/view/comicsandcosplay/comics/fraughtwithperil.*%' => array( + 'body' => array('//*[@class="body"]'), + 'test_url' => 'http://www.escapistmagazine.com/articles/view/comicsandcosplay/comics/fraughtwithperil/12166-The-Escapist-Presents-Escapist-Comics-Critical-Miss-B-lyeh-Fhlop?utm_source=rss&utm_medium=rss&utm_campaign=articles', + 'strip' => array(), + ), + '%/articles/view/video-games/columns/.*%' => array( + 'body' => array('//*[@id="article_content"]'), + 'test_url' => 'http://www.escapistmagazine.com/articles/view/video-games/columns/experienced-points/13971-What-50-Shades-and-Batman-Have-in-Common.2', + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/espn.go.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/espn.go.com.php new file mode 100644 index 00000000..76a20f74 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/espn.go.com.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://espn.go.com/nfl/story/_/id/13388208/jason-whitlock-chip-kelly-controversy', + 'body' => array( + '//p', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/exocomics.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/exocomics.com.php new file mode 100644 index 00000000..5adc59f8 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/exocomics.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'body' => array('//a[@class="comic"]/img'), + 'strip' => array(), + 'test_url' => 'http://www.exocomics.com/379', + ), + ), + 'filter' => array( + '%.*%' => array( + '%title="(.+)" */>%' => '/><br/>$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/explosm.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/explosm.net.php new file mode 100644 index 00000000..3fdf02c0 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/explosm.net.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://explosm.net/comics/3803/', + 'body' => array( + '//div[@id="comic-container"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/extrafabulouscomics.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/extrafabulouscomics.com.php new file mode 100644 index 00000000..12697ccb --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/extrafabulouscomics.com.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%-150x150%' => '', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/factroom.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/factroom.ru.php new file mode 100644 index 00000000..a572061d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/factroom.ru.php @@ -0,0 +1,27 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.factroom.ru/life/20-facts-about-oil', + 'body' => array( + '//div[@class="post"]', + ), + 'strip' => array( + '//script', + '//form', + '//style', + '//h1', + '//div[@id="yandex_ad2"]', + '//*[@class="jp-relatedposts"]', + '//div[contains(@class, "likely-desktop")]', + '//div[contains(@class, "likely-mobile")]', + '//p[last()]', + '//div[contains(@class, "facebook")]', + '//div[contains(@class, "desktop-underpost-direct")]', + '//div[contains(@class, "source-box")]', + '//div[contains(@class, "under-likely-desktop")]', + '//div[contains(@class, "mobile-down-post")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcodesign.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcodesign.com.php new file mode 100644 index 00000000..74e70a86 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcodesign.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.fastcodesign.com/3026548/exposure/peek-inside-the-worlds-forbidden-subway-tunnels', + 'body' => array( + '//article[contains(@class, "body prose")]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcoexist.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcoexist.com.php new file mode 100644 index 00000000..6916f280 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcoexist.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.fastcoexist.com/3026114/take-a-seat-on-this-gates-funded-future-toilet-that-will-change-how-we-think-about-poop', + 'body' => array( + '//article[contains(@class, "body prose")]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcompany.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcompany.com.php new file mode 100644 index 00000000..e0869a29 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fastcompany.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.fastcompany.com/3026712/fast-feed/elon-musk-an-apple-tesla-merger-is-very-unlikely', + 'body' => array( + '//article[contains(@class, "body prose")]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ffworld.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ffworld.com.php new file mode 100644 index 00000000..20a47b2d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ffworld.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.ffworld.com/?rub=news&page=voir&id=2709', + 'body' => array( + '//div[@class="news_body"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/foreignpolicy.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/foreignpolicy.com.php new file mode 100644 index 00000000..3cbcddc4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/foreignpolicy.com.php @@ -0,0 +1,21 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://foreignpolicy.com/2016/01/09/networking-giant-pulls-nsa-linked-code-exploited-by-hackers/', + 'body' => array( + '//article', + ), + 'strip' => array( + '//div[@id="post-category"]', + '//div[@id="desktop-right"]', + '//h1', + '//section[@class="article-meta"]', + '//div[@class="side-panel-wrapper"]', + '//*[contains(@class, "share-")]', + '//*[contains(@id, "taboola-")]', + '//div[@class="comments"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fossbytes.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fossbytes.com.php new file mode 100644 index 00000000..6ce47256 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fossbytes.com.php @@ -0,0 +1,18 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://fossbytes.com/fbi-hacked-1000-computers-to-shut-down-largest-child-pornography-site-on-the-dark-web/', + 'body' => array( + '//div[@class="entry-inner"]', + ), + 'strip' => array( + '//*[@class="at-above-post addthis_default_style addthis_toolbox at-wordpress-hide"]', + '//*[@class="at-below-post addthis_default_style addthis_toolbox at-wordpress-hide"]', + '//*[@class="at-below-post-recommended addthis_default_style addthis_toolbox at-wordpress-hide"]', + '//*[@class="code-block code-block-12 ai-desktop"]', + '//*[@class="code-block code-block-13 ai-tablet-phone"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fototelegraf.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fototelegraf.ru.php new file mode 100644 index 00000000..ca2f85aa --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fototelegraf.ru.php @@ -0,0 +1,19 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://fototelegraf.ru/?p=348232', + 'body' => array( + '//div[@class="post-content"]', + ), + 'strip' => array( + '//script', + '//form', + '//style', + '//div[@class="imageButtonsBlock"]', + '//div[@class="adOnPostBtwImg"]', + '//div[contains(@class, "post-tags")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fowllanguagecomics.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fowllanguagecomics.com.php new file mode 100644 index 00000000..3f62f071 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/fowllanguagecomics.com.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'body' => array('//*[@id="comic"] | //*[@class="post-image"]'), + 'strip' => array(), + 'test_url' => 'http://www.fowllanguagecomics.com/comic/working-out/', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/geek.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/geek.com.php new file mode 100644 index 00000000..d9ccecc2 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/geek.com.php @@ -0,0 +1,17 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.geek.com/news/the-11-best-ways-to-eat-eggs-1634076/', + 'body' => array( + '//div[@class="articleinfo"]/figure', + '//div[@class="articleinfo"]/article', + '//span[@class="by"]', + ), + 'strip' => array( + '//span[@class="red"]', + '//div[@class="on-target"]' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/geektimes.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/geektimes.ru.php new file mode 100644 index 00000000..19541386 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/geektimes.ru.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://geektimes.ru/post/289151/', + 'body' => array( + "//div[contains(concat(' ',normalize-space(@class),' '),' content ')]" + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gerbilwithajetpack.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gerbilwithajetpack.com.php new file mode 100644 index 00000000..44013b3b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gerbilwithajetpack.com.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'body' => array( + '//div[@id="comic-1"]', + '//div[@class="entry"]', + ), + 'test_url' => 'http://gerbilwithajetpack.com/passing-the-digital-buck/', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/giantitp.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/giantitp.com.php new file mode 100644 index 00000000..d9c3ae5d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/giantitp.com.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%/comics/oots.*%' => array( + 'test_url' => 'http://www.giantitp.com/comics/oots0989.html', + 'body' => array( + '//td[@align="center"]/img', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/github.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/github.com.php new file mode 100644 index 00000000..726634f9 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/github.com.php @@ -0,0 +1,14 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://github.com/audreyr/favicon-cheat-sheet', + 'body' => array( + '//article[contains(@class, "entry-content")]', + ), + 'strip' => array( + '//h1', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gocomics.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gocomics.com.php new file mode 100644 index 00000000..32960f0e --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gocomics.com.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.gocomics.com/pearlsbeforeswine/2015/05/30', + 'body' => array( + '//div[1]/p[1]/a[1]/img', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/golem.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/golem.de.php new file mode 100644 index 00000000..84224830 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/golem.de.php @@ -0,0 +1,20 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.golem.de/news/breko-telekom-verzoegert-gezielt-den-vectoring-ausbau-1311-102974.html', + 'body' => array( + '//header[@class="cluster-header"]', + '//header[@class="paged-cluster-header"]', + '//div[@class="formatted"]', + ), + 'next_page' => array( + '//a[@id="atoc_next"]' + ), + 'strip' => array( + '//header[@class="cluster-header"]/a', + '//div[@id="iqadtile4"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gorabbit.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gorabbit.ru.php new file mode 100644 index 00000000..4e432481 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/gorabbit.ru.php @@ -0,0 +1,19 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://gorabbit.ru/article/10-oshchushcheniy-za-rulem-kogda-tolko-poluchil-voditelskie-prava', + 'body' => array( + '//div[@class="detail_text"]', + ), + 'strip' => array( + '//script', + '//form', + '//style', + '//div[@class="socials"]', + '//div[@id="cr_1"]', + '//div[@class="related_items"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/habrahabr.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/habrahabr.ru.php new file mode 100644 index 00000000..3f1ec165 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/habrahabr.ru.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://habrahabr.ru/company/pentestit/blog/328606/', + 'body' => array( + "//div[contains(concat(' ',normalize-space(@class),' '),' content ')]" + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/happletea.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/happletea.com.php new file mode 100644 index 00000000..75b0b83d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/happletea.com.php @@ -0,0 +1,18 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'body' => array( + '//div[@id="comic"]', + '//div[@class="entry"]', + ), + 'strip' => array('//div[@class="ssba"]'), + 'test_url' => 'http://www.happletea.com/comic/mans-best-friend/', + ), + ), + 'filter' => array( + '%.*%' => array( + '%title="(.+)" */>%' => '/><br/>$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/hardware.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/hardware.fr.php new file mode 100644 index 00000000..56aec4f4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/hardware.fr.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%^/news.*%' => array( + 'test_url' => 'http://www.hardware.fr/news/14760/intel-lance-nouveaux-ssd-nand-3d.html', + 'body' => array( + '//div[@class="content_actualite"]/div[@class="md"]', + ) + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/heise.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/heise.de.php new file mode 100644 index 00000000..2055b3bc --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/heise.de.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.heise.de/security/meldung/BND-300-Millionen-Euro-fuer-Fruehwarnsystem-gegen-Cyber-Attacken-2192237.html', + 'body' => array( + '//div[@class="meldung_wrapper"]', + '//div[@class="artikel_content"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/hotshowlife.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/hotshowlife.com.php new file mode 100644 index 00000000..faf01f3d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/hotshowlife.com.php @@ -0,0 +1,23 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://hotshowlife.com/top-10-chempionov-produktov-po-szhiganiyu-kalorij/', + 'body' => array( + '//div[@class="entry-content"]', + ), + 'strip' => array( + '//script', + '//form', + '//style', + '//div[@class="ads2"]', + '//div[@class="mistape_caption"]', + '//div[contains(@class, "et_social_media_hidden")]', + '//div[contains(@class, "et_social_inline_bottom")]', + '//div[contains(@class, "avatar")]', + '//ul[contains(@class, "entry-tags")]', + '//div[contains(@class, "entry-meta")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/huffingtonpost.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/huffingtonpost.com.php new file mode 100644 index 00000000..b52b07b5 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/huffingtonpost.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.huffingtonpost.com/2014/02/20/centscere-social-media-syracuse_n_4823848.html', + 'body' => array( + '//article[@class="content")]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/imogenquest.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/imogenquest.net.php new file mode 100644 index 00000000..3214c62a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/imogenquest.net.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%title="(.+)" */>%' => '/><br/>$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/indiehaven.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/indiehaven.com.php new file mode 100644 index 00000000..a40ce694 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/indiehaven.com.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://indiehaven.com/no-mans-sky-is-a-solo-space-adventure-and-im-ok-with-that/', + 'body' => array( + '//section[contains(@class, "entry-content")]', + ) + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ing.dk.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ing.dk.php new file mode 100644 index 00000000..5a021a08 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ing.dk.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://ing.dk/artikel/smart-husisolering-og-styring-skal-mindske-japans-energikrise-164517', + 'body' => array( + '//section[contains(@class, "teaser")]', + '//section[contains(@class, "body")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/invisiblebread.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/invisiblebread.com.php new file mode 100644 index 00000000..90f87597 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/invisiblebread.com.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%(<img.+(\\d{4}-\\d{2}-\\d{2}).+/>)%' => '$1<img src="http://invisiblebread.com/eps/$2-extrapanel.png"/>', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ir.amd.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ir.amd.com.php new file mode 100644 index 00000000..af99fe99 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ir.amd.com.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'body' => array('//span[@class="ccbnTxt"]'), + 'strip' => array(), + 'test_url' => 'http://ir.amd.com/phoenix.zhtml?c=74093&p=RssLanding&cat=news&id=2055819', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/japantimes.co.jp.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/japantimes.co.jp.php new file mode 100644 index 00000000..9959441d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/japantimes.co.jp.php @@ -0,0 +1,21 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.japantimes.co.jp/news/2015/09/27/world/social-issues-world/pope-meets-sex-abuse-victims-philadelphia-promises-accountability/', + 'body' => array( + '//article[@role="main"]', + ), + 'strip' => array( + '//script', + '//header', + '//div[contains(@class, "meta")]', + '//div[@class="clearfix"]', + '//div[@class="OUTBRAIN"]', + '//ul[@id="content_footer_menu"]', + '//div[@class="article_footer_ad"]', + '//div[@id="disqus_thread"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/japantoday.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/japantoday.com.php new file mode 100644 index 00000000..22485d69 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/japantoday.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.japantoday.com/category/politics/view/japan-u-s-to-sign-new-base-environment-pact', + 'body' => array( + '//div[@id="article_container"]', + ), + 'strip' => array( + '//h2', + '//div[@id="article_info"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/journaldugeek.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/journaldugeek.com.php new file mode 100644 index 00000000..876b2698 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/journaldugeek.com.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www./2014/05/20/le-playstation-now-arrive-en-beta-fermee-aux-etats-unis/', + 'body' => array( + '//div[@class="post-content"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/jsonline.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/jsonline.com.php new file mode 100644 index 00000000..5895256e --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/jsonline.com.php @@ -0,0 +1,37 @@ +<?php +return array( + 'grabber' => array( + '%.%/picture-gallery/%' => array( + 'test_url' => 'http://www.jsonline.com/picture-gallery/news/local/milwaukee/2017/02/22/photos-aclu-sues-milwaukee-police-over-profiling-stop-and-frisk/98250836/', + 'body' => array( + '//div[@class="priority-asset-gallery galleries standalone hasendslate"]', + ), + 'strip' => array( + '//div[@class="buy-photo-btn"]', + '//div[@class="gallery-thumbs thumbs pag-thumbs")]', + ), + ), + '%.*%' => array( + 'test_url' => 'http://www.jsonline.com/news/usandworld/as-many-as-a-million-expected-for-popes-last-mass-in-us-b99585180z1-329688131.html', + 'body' => array( + '//div[@itemprop="articleBody"]', + ), + 'strip' => array( + '//h1', + '//iframe', + '//span[@class="mycapture-small-btn mycapture-btn-with-text mycapture-expandable-photo-btn-small js-mycapture-btn-small"]', + '//div[@class="close-wrap"]', + '//div[contains(@class,"ui-video-wrapper")]', + '//div[contains(@class,"media-mob")]', + '//div[contains(@class,"left-mob")]', + '//div[contains(@class,"nerdbox")]', + '//p/span', + '//div[contains(@class,"oembed-asset")]', + '//*[contains(@class,"share")]', + '//div[contains(@class,"gallery-asset")]', + '//div[contains(@class,"oembed-asset")]', + '//div[@class="article-print-url"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/justcoolidea.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/justcoolidea.ru.php new file mode 100644 index 00000000..089ff29c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/justcoolidea.ru.php @@ -0,0 +1,19 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://justcoolidea.ru/idealnyj-sad-samodelnye-proekty-dlya-berezhlivogo-domovladeltsa/', + 'body' => array( + '//section[@class="entry-content"]', + ), + 'strip' => array( + '//script', + '//form', + '//style', + '//*[contains(@class, "essb_links")]', + '//*[contains(@rel, "nofollow")]', + '//*[contains(@class, "ads")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/kanpai.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/kanpai.fr.php new file mode 100644 index 00000000..c3a1abc4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/kanpai.fr.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.kanpai.fr/japon/comment-donner-lheure-en-japonais.html', + 'body' => array( + '//div[@class="single-left"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/karriere.jobfinder.dk.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/karriere.jobfinder.dk.php new file mode 100644 index 00000000..25d6dfa3 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/karriere.jobfinder.dk.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://karriere.jobfinder.dk/artikel/dansk-professor-skal-lede-smart-grid-forskning-20-millioner-dollars-763', + 'body' => array( + '//section[contains(@class, "teaser")]', + '//section[contains(@class, "body")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/kodi.tv.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/kodi.tv.php new file mode 100644 index 00000000..439fc907 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/kodi.tv.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://kodi.tv/article/andwere-baaaaack', + 'body' => array( + '//div[@class="l-region--content"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/koreaherald.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/koreaherald.com.php new file mode 100644 index 00000000..96510560 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/koreaherald.com.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.koreaherald.com/view.php?ud=20150926000018', + 'body' => array( + '//div[@id="articleText"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/koreatimes.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/koreatimes.php new file mode 100644 index 00000000..f274b4a9 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/koreatimes.php @@ -0,0 +1,14 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.koreatimes.co.kr/www/news/nation/2015/12/116_192409.html', + 'body' => array( + '//div[@id="p"]', + ), + 'strip' => array( + '//div[@id="webtalks_btn_listenDiv"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lastplacecomics.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lastplacecomics.com.php new file mode 100644 index 00000000..12697ccb --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lastplacecomics.com.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%-150x150%' => '', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/legorafi.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/legorafi.fr.php new file mode 100644 index 00000000..e6aae46b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/legorafi.fr.php @@ -0,0 +1,22 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => array( + 'http://www.legorafi.fr/2016/12/16/gorafi-magazine-bravo-vous-avez-bientot-presque-survecu-a-2016/', + 'http://www.legorafi.fr/2016/12/15/manuel-valls-promet-quune-fois-elu-il-debarrassera-la-france-de-manuel-valls/', + ), + 'body' => array( + '//section[@id="banner_magazine"]', + '//figure[@class="main_picture"]', + '//div[@class="content"]', + ), + 'strip' => array( + '//figcaption', + '//div[@class="sharebox"]', + '//div[@class="tags"]', + '//section[@class="taboola_article"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lejapon.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lejapon.fr.php new file mode 100644 index 00000000..8f2b2932 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lejapon.fr.php @@ -0,0 +1,17 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://lejapon.fr/guide-voyage-japon/5223/tokyo-sous-la-neige.htm', + 'body' => array( + '//div[@class="entry"]', + ), + 'strip' => array( + '//*[contains(@class, "addthis_toolbox")]', + '//*[contains(@class, "addthis_default_style")]', + '//*[@class="navigation small"]', + '//*[@id="related"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lesjoiesducode.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lesjoiesducode.fr.php new file mode 100644 index 00000000..369206ab --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lesjoiesducode.fr.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://lesjoiesducode.fr/post/75576211207/quand-lappli-ne-fonctionne-plus-sans-aucune-raison', + 'body' => array( + '//div[@class="blog-post-content"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lfg.co.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lfg.co.php new file mode 100644 index 00000000..d978a5fc --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lfg.co.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.lfg.co/page/871/?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+LookingForGroup+%28Looking+For+Group%29&utm_content=FeedBurner', + 'body' => array( + '//*[@id="comic"]/img | //*[@class="content"]', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lifehacker.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lifehacker.com.php new file mode 100644 index 00000000..b9a69338 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lifehacker.com.php @@ -0,0 +1,18 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://lifehacker.com/bring-water-bottle-caps-into-concerts-to-protect-your-d-1269334973', + 'body' => array( + '//div[contains(@class, "row")/img', + '//div[contains(@class, "content-column")]', + ), + 'strip' => array( + '//*[contains(@class, "meta")]', + '//span[contains(@class, "icon")]', + '//h1', + '//aside', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lifehacker.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lifehacker.ru.php new file mode 100644 index 00000000..bc140f67 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lifehacker.ru.php @@ -0,0 +1,22 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://lifehacker.ru/2016/03/03/polymail/', + 'body' => array( + '//div[@class="post-content"]', + ), + 'strip' => array( + '//script', + '//form', + '//style', + '//*[@class="wp-thumbnail-caption"]', + '//*[contains(@class, "social-likes")]', + '//*[@class="jp-relatedposts"]', + '//*[contains(@class, "wpappbox")]', + '//*[contains(@class, "icon__image")]', + '//div[@id="hypercomments_widget"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linux.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linux.org.php new file mode 100644 index 00000000..2520d0d0 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linux.org.php @@ -0,0 +1,14 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.linux.org/threads/lua-the-scripting-interpreter.8352/', + 'body' => array( + '//div[@class="messageContent"]', + ), + 'strip' => array( + '//aside', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linux.org.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linux.org.ru.php new file mode 100644 index 00000000..7fa02497 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linux.org.ru.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.linux.org/threads/lua-the-scripting-interpreter.8352/', + 'body' => array( + '//div[@itemprop="articleBody"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linuxinsider.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linuxinsider.com.php new file mode 100644 index 00000000..4e0a4cc1 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/linuxinsider.com.php @@ -0,0 +1,20 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.linuxinsider.com/story/82526.html?rss=1', + 'body' => array( + '//div[@id="story"]', + ), + 'strip' => array( + '//script', + '//h1', + '//div[@id="story-toolbox1"]', + '//div[@id="story-byline"]', + '//div[@id="story"]/p', + '//div[@class="story-advertisement"]', + '//iframe', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lists.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lists.php new file mode 100644 index 00000000..c7051a20 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lists.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://lists.freebsd.org/pipermail/freebsd-announce/2013-September/001504.html', + 'body' => array( + '//pre', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/loadingartist.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/loadingartist.com.php new file mode 100644 index 00000000..d06ed124 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/loadingartist.com.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%-150x150%' => '', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/loldwell.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/loldwell.com.php new file mode 100644 index 00000000..d358e156 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/loldwell.com.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://loldwell.com/?comic=food-math-101', + 'body' => array('//*[@id="comic"]'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lukesurl.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lukesurl.com.php new file mode 100644 index 00000000..816233dd --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/lukesurl.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'body' => array('//div[@id="comic"]//img'), + 'strip' => array(), + 'test_url' => 'http://www.lukesurl.com/archives/comic/665-3-of-clubs', + ), + ), + 'filter' => array( + '%.*%' => array( + '%title="(.+)" */>%' => '/><br/>$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/macg.co.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/macg.co.php new file mode 100644 index 00000000..bbe6dbcd --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/macg.co.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.macg.co//logiciels/2014/05/feedly-sameliore-un-petit-peu-sur-mac-82205', + 'body' => array( + '//div[contains(@class, "field-name-body")]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marc.info.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marc.info.php new file mode 100644 index 00000000..5f582a6d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marc.info.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://marc.info/?l=openbsd-misc&m=141987113202061&w=2', + 'body' => array( + '//pre', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marriedtothesea.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marriedtothesea.com.php new file mode 100644 index 00000000..469640df --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marriedtothesea.com.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.marriedtothesea.com/index.php?date=052915', + 'body' => array( + '//div[@align]/a/img', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marycagle.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marycagle.com.php new file mode 100644 index 00000000..b8665e35 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/marycagle.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'body' => array( + '//img[@id="cc-comic"]', + '//div[@class="cc-newsbody"]', + ), + 'strip' => array(), + 'test_url' => 'http://www.marycagle.com/letsspeakenglish/74-grim-reality/', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/maximumble.thebookofbiff.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/maximumble.thebookofbiff.com.php new file mode 100644 index 00000000..88800546 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/maximumble.thebookofbiff.com.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://maximumble.thebookofbiff.com/2015/04/20/1084-change/', + 'body' => array('//div[@id="comic"]/div/a/img'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/medium.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/medium.com.php new file mode 100644 index 00000000..e20860e0 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/medium.com.php @@ -0,0 +1,19 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://medium.com/lessons-learned/917b8b63ae3e', + 'body' => array( + '//div[@class="section-content"]', + ), + 'strip' => array( + '//div[contains(@class,"metabar")]', + '//img[contains(@class,"thumbnail")]', + '//h1', + '//blockquote', + '//div[@class="aspectRatioPlaceholder-fill"]', + '//footer' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mercworks.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mercworks.net.php new file mode 100644 index 00000000..c7a27dea --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mercworks.net.php @@ -0,0 +1,17 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'body' => array('//div[@id="comic"]', + '//div[contains(@class,"entry-content")]', + ), + 'strip' => array(), + 'test_url' => 'http://mercworks.net/comicland/healthy-choice/', + ), + ), + 'filter' => array( + '%.*%' => array( + '%title="(.+)" */>%' => '/><br/>$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/metronieuws.nl.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/metronieuws.nl.php new file mode 100644 index 00000000..5011169f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/metronieuws.nl.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.metronieuws.nl/sport/2015/04/broer-fellaini-zorgde-bijna-voor-paniek-bij-mourinho', + 'body' => array('//div[contains(@class,"article-top")]/div[contains(@class,"image-component")] | //div[@class="article-full-width"]/div[1]'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/milwaukeenns.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/milwaukeenns.php new file mode 100644 index 00000000..ddb29a56 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/milwaukeenns.php @@ -0,0 +1,14 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://milwaukeenns.org/2016/01/08/united-way-grant-enables-sdc-to-restore-free-tax-assistance-program/', + 'body' => array( + '//div[@class="pf-content"]', + ), + 'strip' => array( + '//div[@class="printfriendly"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mokepon.smackjeeves.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mokepon.smackjeeves.com.php new file mode 100644 index 00000000..1ddcd407 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mokepon.smackjeeves.com.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://mokepon.smackjeeves.com/comics/2120096/chapter-9-page-68/', + 'body' => array('//*[@id="comic_area_inner"]/img | //*[@id="comic_area_inner"]/a/img'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/monandroid.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/monandroid.com.php new file mode 100644 index 00000000..f87560e2 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/monandroid.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.monandroid.com/blog/tutoriel-avance-activer-le-stockage-fusionne-sur-android-6-marshamallow-t12.html', + 'body' => array( + '//div[@class="blog-post-body"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/monwindows.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/monwindows.com.php new file mode 100644 index 00000000..b2b24d74 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/monwindows.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.monwindows.com/tout-savoir-sur-le-centre-d-action-de-windows-phone-8-1-t40574.html', + 'body' => array( + '//div[@class="blog-post-body"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/moya-planeta.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/moya-planeta.ru.php new file mode 100644 index 00000000..dd842844 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/moya-planeta.ru.php @@ -0,0 +1,21 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.moya-planeta.ru/travel/view/chto_yaponcu_horosho_russkomu_ne_ponyat_20432/', + 'body' => array( + '//div[@class="full_object"]', + ), + 'strip' => array( + '//div[@class="full_object_panel object_panel"]', + '//div[@class="full_object_panel_geo object_panel"]', + '//div[@class="full_object_title"]', + '//div[@class="full_object_social_likes"]', + '//div[@class="full_object_planeta_likes"]', + '//div[@class="full_object_go2comments"]', + '//div[@id="yandex_ad_R-163191-3"]', + '//div[@class="full_object_shop_article_recommend"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mrlovenstein.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mrlovenstein.com.php new file mode 100644 index 00000000..b971091f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mrlovenstein.com.php @@ -0,0 +1,9 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%alt="(.+)" */>%' => '/><br/>$1', + '%\.png%' => '_rollover.png', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/muckrock.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/muckrock.com.php new file mode 100644 index 00000000..9e354a36 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/muckrock.com.php @@ -0,0 +1,20 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://www.muckrock.com/news/archives/2016/jan/13/5-concerns-private-prisons/', + 'body' => array( + '//div[@class="content"]', + ), + 'strip' => array( + '//div[@class="newsletter-widget"]', + '//div[@class="contributors"]', + '//time', + '//h1', + '//div[@class="secondary"]', + '//aside', + '//div[@class="articles__related"]' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mynorthshorenow.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mynorthshorenow.com.php new file mode 100644 index 00000000..b6309157 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/mynorthshorenow.com.php @@ -0,0 +1,27 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.mynorthshorenow.com/story/news/local/fox-point/2017/04/04/fox-point-building-board-approves-dunwood-commons-project/99875570/', + 'body' => array( + '//div[@itemprop="articleBody"]', + ), + 'strip' => array( + '//h1', + '//iframe', + '//span[@class="mycapture-small-btn mycapture-btn-with-text mycapture-expandable-photo-btn-small js-mycapture-btn-small"]', + '//div[@class="close-wrap"]', + '//div[contains(@class,"ui-video-wrapper")]', + '//div[contains(@class,"media-mob")]', + '//div[contains(@class,"left-mob")]', + '//div[contains(@class,"nerdbox")]', + '//p/span', + '//div[contains(@class,"oembed-asset")]', + '//*[contains(@class,"share")]', + '//div[contains(@class,"gallery-asset")]', + '//div[contains(@class,"oembed-asset")]', + '//div[@class="article-print-url"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nakedCapitalism.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nakedCapitalism.php new file mode 100644 index 00000000..ec2d5fd5 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nakedCapitalism.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://feedproxy.google.com/~r/NakedCapitalism/~3/JOBxEHxN8ZI/mark-blyth-liberalism-undermined-democracy-failure-democratic-party.html', + 'body' => array( + '//div[@class="pf-content"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nasa.gov.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nasa.gov.php new file mode 100644 index 00000000..c6692d07 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nasa.gov.php @@ -0,0 +1,14 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://www.nasa.gov/image-feature/jpl/pia20514/coy-dione', + 'body' => array( + '//div[@class="article-body"]', + ), + 'strip' => array( + '//div[@class="title-bar"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nat-geo.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nat-geo.ru.php new file mode 100644 index 00000000..1a42d997 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nat-geo.ru.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.nat-geo.ru/fact/868093-knidos-antichnyy-naukograd/', + 'body' => array( + '//div[@class="article-inner-text"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nationaljournal.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nationaljournal.com.php new file mode 100644 index 00000000..5e612bef --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nationaljournal.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.nationaljournal.com/s/354962/south-carolina-evangelicals-outstrip-establishment?mref=home_top_main', + 'body' => array( + '//div[@class="section-body"]', + ), + 'strip' => array( + '//*[contains(@class, "-related")]', + '//*[contains(@class, "social")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nature.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nature.com.php new file mode 100644 index 00000000..6b9e87f4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nature.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.nature.com/doifinder/10.1038/nature.2015.18340', + 'body' => array( + '//div[contains(@class,"main-content")]', + ), + 'strip' => array(), + ), + ), +); + diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nba.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nba.com.php new file mode 100644 index 00000000..c8ea926f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nba.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.nba.com/2015/news/09/25/knicks-jackson-to-spend-more-time-around-coaching-staff.ap/index.html?rss=true', + 'body' => array( + '//div[@class="paragraphs"]', + ), + 'strip' => array( + '//div[@id="nbaArticleSocialWrapper_bot"]', + '//h5', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nedroid.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nedroid.com.php new file mode 100644 index 00000000..3214c62a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nedroid.com.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%title="(.+)" */>%' => '/><br/>$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/networkworld.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/networkworld.com.php new file mode 100644 index 00000000..18524354 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/networkworld.com.php @@ -0,0 +1,20 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.networkworld.com/article/3020585/security/the-incident-response-fab-five.html', + 'body' => array( + '//figure/img[@class="hero-img"]', + '//section[@class="deck"]', + '//div[@itemprop="articleBody"] | //div[@itemprop="reviewBody"]', + '//div[@class="carousel-inside-crop"]', + ), + 'strip' => array( + '//script', + '//aside', + '//div[@class="credit"]', + '//div[@class="view-large"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/neustadt-ticker.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/neustadt-ticker.de.php new file mode 100644 index 00000000..e0c0d19d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/neustadt-ticker.de.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.neustadt-ticker.de/41302/alltag/kultur/demo-auf-der-boehmischen', + 'body' => array( + '//div[@class="entry-content"]', + ), + 'strip' => array( + '//*[contains(@class, "sharedaddy")]', + '//*[contains(@class, "yarpp-related")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nextinpact.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nextinpact.com.php new file mode 100644 index 00000000..29dd9d6b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nextinpact.com.php @@ -0,0 +1,18 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.nextinpact.com/news/101122-3d-nand-intel-lance-six-nouvelles-gammes-ssd-pour-tous-usages.htm', + 'body' => array( + '//div[@class="container_article"]', + ), + 'strip' => array( + '//div[@class="infos_article"]', + '//div[@id="actu_auteur"]', + '//div[@id="soutenir_journaliste"]', + '//section[@id="bandeau_abonnez_vous"]', + '//br' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/niceteethcomic.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/niceteethcomic.com.php new file mode 100644 index 00000000..f41e4438 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/niceteethcomic.com.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%/archives.*%' => array( + 'test_url' => 'http://niceteethcomic.com/archives/page119/', + 'body' => array('//*[@class="comicpane"]/a/img'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nichtlustig.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nichtlustig.de.php new file mode 100644 index 00000000..4d083f98 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/nichtlustig.de.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%.*static.nichtlustig.de/comics/full/(\\d+).*%s' => '<img src="http://static.nichtlustig.de/comics/full/$1.jpg" />', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/oglaf.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/oglaf.com.php new file mode 100644 index 00000000..8b2b5b65 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/oglaf.com.php @@ -0,0 +1,19 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'body' => array( + '//img[@id="strip"]', + '//a/div[@id="nx"]/..', + ), + 'strip' => array(), + 'test_url' => 'http://oglaf.com/slodging/', + ), + ), + 'filter' => array( + '%.*%' => array( + '%alt="(.+)" title="(.+)" */>%' => '/><br/>$1<br/>$2<br/>', + '%</a>%' => 'Next page</a>', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/onhax.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/onhax.net.php new file mode 100644 index 00000000..213849d8 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/onhax.net.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://onhax.net/process-lasso-8-9-1-4-pro-key-portable-is-here-latest', + 'body' => array( + '//div[@class="postcontent"]', + ), + 'strip' => array( + '//*[@class="sharedaddy sd-sharing-enabled"]', + '//*[@class="yarpp-related"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/onmilwaukee.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/onmilwaukee.php new file mode 100644 index 00000000..f66ac4b8 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/onmilwaukee.php @@ -0,0 +1,24 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://onmilwaukee.com/movies/articles/downerspelunking.html', + 'body' => array( + '//article[contains(@class, "show")]', + ), + 'strip' => array( + '//h1', + '//div[contains(@class,"-ad")]', + '//div[contains(@class,"_ad")]', + '//div[@id="pub_wrapper"]', + '//div[contains(@class,"share_tools")]', + '//div[@class="clearfix"]', + '//div[contains(@class,"image_control")]', + '//section[@class="ribboned"]', + '//div[contains(@class,"sidebar")]', + '//aside[@class="article_tag_list"]', + '//section[contains(@id,"more_posts")]' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/openculture.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/openculture.com.php new file mode 100644 index 00000000..84f2beed --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/openculture.com.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.openculture.com/2017/03/are-we-living-inside-a-computer-simulation-watch-the-simulation-argument.html', + 'body' => array( + '//div[@class="entry"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/opennet.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/opennet.ru.php new file mode 100644 index 00000000..1fb7722b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/opennet.ru.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://www.opennet.ru/opennews/art.shtml?num=46549', + 'body' => array( + '//*[@id="r_memo"]', + ), + 'strip' => array( + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/openrightsgroup.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/openrightsgroup.org.php new file mode 100644 index 00000000..94139a72 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/openrightsgroup.org.php @@ -0,0 +1,20 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://www.openrightsgroup.org/blog/2014/3-days-to-go-till-orgcon2014', + 'body' => array( + '//div[contains(@class, "content")]/div', + ), + 'strip' => array( + '//h2[1]', + '//div[@class="info"]', + '//div[@class="tags"]', + '//div[@class="comments"]', + '//div[@class="breadcrumbs"]', + '//h1[@class="pageTitle"]', + '//p[@class="bookmarkThis"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/opensource.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/opensource.com.php new file mode 100644 index 00000000..60f3577b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/opensource.com.php @@ -0,0 +1,22 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://opensource.com/life/15/10/how-internet-things-will-change-way-we-think', + 'body' => array( + '//div[@id="article-template"]', + ), + 'strip' => array( + '//div[contains(@class,"os-article__sidebar")]', + '//div[@class="panel-pane pane-node-title"]', + '//div[@class="panel-pane pane-os-article-byline"]', + '//ul', + '//div[contains(@class,"-license")]', + '//div[contains(@class,"-tags")]', + '//div[@class="panel-pane pane-os-article-byline"]', + '//div[@class="os-article__content-below"]', + '//div[@id="comments"]' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/optipess.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/optipess.com.php new file mode 100644 index 00000000..3214c62a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/optipess.com.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%title="(.+)" */>%' => '/><br/>$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/osnews.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/osnews.com.php new file mode 100644 index 00000000..1d1396c8 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/osnews.com.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://osnews.com/story/28863/Google_said_to_be_under_US_antitrust_scrutiny_over_Android', + 'body' => array( + '//div[@class="newscontent1"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/pastebin.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/pastebin.com.php new file mode 100644 index 00000000..b20bf41c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/pastebin.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://pastebin.com/ed1pP9Ak', + 'body' => array( + '//div[@class="text"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/peebleslab.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/peebleslab.com.php new file mode 100644 index 00000000..ce4891d1 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/peebleslab.com.php @@ -0,0 +1,9 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + // the extra space is required to strip the title cleanly + '%title="(.+) " */>%' => '/><br/>$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/penny-arcade.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/penny-arcade.com.php new file mode 100644 index 00000000..dd39983b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/penny-arcade.com.php @@ -0,0 +1,21 @@ +<?php +return array( + 'grabber' => array( + '%/news/.*%' => array( + 'test_url' => 'http://penny-arcade.com/news/post/2015/04/15/101-part-two', + 'body' => array( + '//*[@class="postBody"]/*', + ), + 'strip' => array( + ), + ), + '%/comic/.*%' => array( + 'test_url' => 'http://penny-arcade.com/comic/2015/04/15', + 'body' => array( + '//*[@id="comicFrame"]/a/img', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/pixelbeat.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/pixelbeat.org.php new file mode 100644 index 00000000..fa9052ea --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/pixelbeat.org.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.pixelbeat.org/programming/sigpipe_handling.html#1425573246', + 'body' => array( + '//div[@class="contentText"]', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/plus.google.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/plus.google.com.php new file mode 100644 index 00000000..5e48a6cf --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/plus.google.com.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://plus.google.com/+LarryPage/posts/Lh8SKC6sED1', + 'body' => array( + '//div[@role="article"]/div[contains(@class, "eE")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/popstrip.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/popstrip.com.php new file mode 100644 index 00000000..801a2814 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/popstrip.com.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%(<img.+/s/[^"]+/)(.+)%' => '$1$2$1bonus.png"/>', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/publicpolicyforum.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/publicpolicyforum.org.php new file mode 100644 index 00000000..5dc8be88 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/publicpolicyforum.org.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://publicpolicyforum.org/blog/going-extra-mile', + 'body' => array( + '//div[contains(@class,"field-name-post-date")]', + '//div[contains(@class,"field-name-body")]', + ), + 'strip' => array( + '//img[@src="http://publicpolicyforum.org/sites/default/files/logo3.jpg"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/publy.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/publy.ru.php new file mode 100644 index 00000000..bcfeeb99 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/publy.ru.php @@ -0,0 +1,24 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.publy.ru/post/19988', + 'body' => array( + '//div[@class="singlepost"]', + ), + 'strip' => array( + '//script', + '//form', + '//style', + '//*[@class="featured"]', + '//*[@class="toc_white no_bullets"]', + '//*[@class="toc_title"]', + '//*[@class="pba"]', + '//*[@class="comments"]', + '//*[contains(@class, "g-single")]', + '//*[@class="ts-fab-wrapper"]', + '//*[contains(@class, "wp_rp_wrap")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/putaindecode.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/putaindecode.fr.php new file mode 100644 index 00000000..9fa5568c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/putaindecode.fr.php @@ -0,0 +1,16 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://putaindecode.fr/posts/js/etat-lieux-js-modulaire-front/', + 'body' => array( + '//*[@class="putainde-Post-md"]', + ), + 'strip' => array( + '//*[contains(@class, "inlineimg")]', + '//*[contains(@class, "comment-respond")]', + '//header', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/recode.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/recode.net.php new file mode 100644 index 00000000..343cd12f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/recode.net.php @@ -0,0 +1,20 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://recode.net/2015/09/26/big-tech-rolls-out-red-carpet-for-indian-prime-minister-lobbies-behind-closed-doors/', + 'body' => array( + '//img[contains(@class,"attachment-large")]', + '//div[contains(@class,"postarea")]', + '//li[@class,"author"]', + ), + 'strip' => array( + '//script', + '//div[contains(@class,"sharedaddy")]', + '//div[@class="post-send-off"]', + '//div[@class="large-12 columns"]', + '//div[contains(@class,"inner-related-article")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/retractionwatch.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/retractionwatch.com.php new file mode 100644 index 00000000..b97c73ed --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/retractionwatch.com.php @@ -0,0 +1,18 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://retractionwatch.com/2015/11/12/psychologist-jens-forster-settles-case-by-agreeing-to-2-retractions/', + 'body' => array( + '//*[@class="main"]', + '//*[@class="entry-content"]', + ), + 'strip' => array( + '//*[contains(@class, "sharedaddy")]', + '//*[contains(@class, "jp-relatedposts")]', + '//p[@class="p1"]', + ) + ) + ) +); + diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rockpapershotgun.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rockpapershotgun.com.php new file mode 100644 index 00000000..71110786 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rockpapershotgun.com.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://www.rockpapershotgun.com/2016/08/26/the-divisions-expansions-delayed-to-improve-the-game/', + 'body' => array( + '//div[@class="entry"]', + ) + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rue89.nouvelobs.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rue89.nouvelobs.com.php new file mode 100644 index 00000000..cb9116a3 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rue89.nouvelobs.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://rue89.feedsportal.com/c/33822/f/608948/s/30999fa0/sc/24/l/0L0Srue890N0C20A130C0A80C30A0Cfaisait0Eboris0Eboillon0Eex0Esarko0Eboy0E350A0E0A0A0A0Eeuros0Egare0Enord0E245315/story01.htm', + 'body' => array( + '//*[@id="article"]/div[contains(@class, "content")]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rugbyrama.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rugbyrama.fr.php new file mode 100644 index 00000000..9915c234 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/rugbyrama.fr.php @@ -0,0 +1,20 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.rugbyrama.fr/rugby/top-14/2015-2016/top-14-hayman-coupe-du-monde-finale-2012-lutte.-voici-levan-chilachava-toulon_sto5283863/story.shtml', + 'body' => array( + '//div[@class="storyfull__content"]', + ), + 'strip' => array( + '//script', + '//form', + '//style', + '//*[@class="share-buttons"]', + '//*[@class="ad"]', + '//*[@class="hide-desktop"]', + '//*[@id="tracking_img"]', + ) + ) + ) +);
\ No newline at end of file diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/satwcomic.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/satwcomic.com.php new file mode 100644 index 00000000..d63fc11c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/satwcomic.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://satwcomic.com/day-at-the-beach', + 'body' => array( + '//div[@class="container"]/center/a/img', + '//span[@itemprop="articleBody"]', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/scrumalliance.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/scrumalliance.org.php new file mode 100644 index 00000000..7835fd9e --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/scrumalliance.org.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://www.scrumalliance.org/community/articles/2015/march/an-introduction-to-agile-project-intake?feed=articles', + 'body' => array( + '//div[@class="article_content"]', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/securityfocus.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/securityfocus.com.php new file mode 100644 index 00000000..01045148 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/securityfocus.com.php @@ -0,0 +1,17 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.securityfocus.com/archive/1/540139', + 'body' => array( + '//div[@id="vulnerability"]', + '//div[@class="comments_reply"]', + ), + 'strip' => array( + '//span[@class="title"]', + '//div[@id="logo_new"]', + '//div[@id="bannerAd"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sentfromthemoon.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sentfromthemoon.com.php new file mode 100644 index 00000000..f4354179 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sentfromthemoon.com.php @@ -0,0 +1,18 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'body' => array( + '//div[@class="comicpane"]/a/img', + '//div[@class="entry"]', + ), + 'strip' => array(), + 'test_url' => 'http://sentfromthemoon.com/archives/1417', + ), + ), + 'filter' => array( + '%.*%' => array( + '%title="(.+)" */>%' => '/><br/>$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sitepoint.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sitepoint.com.php new file mode 100644 index 00000000..ab0eb7d4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sitepoint.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.sitepoint.com/creating-hello-world-app-swift/', + 'body' => array( + '//section[@class="article_body"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/slashdot.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/slashdot.org.php new file mode 100644 index 00000000..89ced8b6 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/slashdot.org.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://science.slashdot.org/story/15/04/20/0528253/pull-top-can-tabs-at-50-reach-historic-archaeological-status', + 'body' => array( + '//article/div[@class="body"] | //article[@class="layout-article"]/div[@class="elips"]', ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smallhousebliss.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smallhousebliss.com.php new file mode 100644 index 00000000..8c13c44c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smallhousebliss.com.php @@ -0,0 +1,19 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://smallhousebliss.com/2013/08/29/house-g-by-lode-architecture/', + 'body' => array( + '//div[@class="post-content"]', + ), + 'strip' => array( + '//*[contains(@class, "gallery")]', + '//*[contains(@class, "share")]', + '//*[contains(@class, "wpcnt")]', + '//*[contains(@class, "meta")]', + '//*[contains(@class, "postitle")]', + '//*[@id="nav-below"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smarthomewelt.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smarthomewelt.de.php new file mode 100644 index 00000000..7463abc7 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smarthomewelt.de.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://smarthomewelt.de/apple-tv-amazon-echo-smart-home/', + 'body' => array('//div[@class="entry-inner"]/p | //div[@class="entry-inner"]/div[contains(@class,"wp-caption")]'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smashingmagazine.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smashingmagazine.com.php new file mode 100644 index 00000000..cbe10726 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smashingmagazine.com.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.smashingmagazine.com/2015/04/17/using-sketch-for-responsive-web-design-case-study/', + 'body' => array('//article[contains(@class,"post")]/p'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smbc-comics.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smbc-comics.com.php new file mode 100644 index 00000000..42262dc9 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/smbc-comics.com.php @@ -0,0 +1,14 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.smbc-comics.com/comic/the-troll-toll', + 'body' => array( + '//div[@id="cc-comicbody"]', + '//div[@id="aftercomic"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/snopes.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/snopes.com.php new file mode 100644 index 00000000..b0fe6557 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/snopes.com.php @@ -0,0 +1,22 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.snopes.com/bacca-brides-on-tour/', + 'body' => array( + '//article', + ), + 'strip' => array( + '//span[@itemprop="author"]', + '//div[contains(@class,"author-")]', + '//h1', + '//style', + '//div[contains(@class,"socialShares")]', + '//div[contains(@class,"ad-unit")]', + '//aside', + '//div[contains(@class,"boomtrain")]', + '//footer' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/soundandvision.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/soundandvision.com.php new file mode 100644 index 00000000..6448bb05 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/soundandvision.com.php @@ -0,0 +1,21 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.soundandvision.com/content/james-guthrie-mixing-roger-waters-and-pink-floyd-51', + 'body' => array( + '//div[@id="left"]', + ), + 'strip' => array( + '//div[@class="meta"]', + '//div[@class="ratingsbox"]', + '//h1', + '//h2', + '//addthis', + '//comment-links', + '//div[@class="book-navigation"]', + '//div[@class="comment-links"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/spiegel.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/spiegel.de.php new file mode 100644 index 00000000..e2e3db00 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/spiegel.de.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.spiegel.de/politik/ausland/afrika-angola-geht-gegen-islam-vor-und-schliesst-moscheen-a-935788.html', + 'body' => array( + '//div[contains(@class, "article-section")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/stereophile.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/stereophile.com.php new file mode 100644 index 00000000..8e410be4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/stereophile.com.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.stereophile.com/content/2015-rocky-mountain-audio-fest-starts-friday', + 'body' => array( + '//div[@class="content clear-block"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/stupidfox.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/stupidfox.net.php new file mode 100644 index 00000000..61182d72 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/stupidfox.net.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://stupidfox.net/134-sleepy-time', + 'body' => array( + '//div[@class="comicmid"]/center/a/img', + '//div[@class="stand_high"]', + ), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/subtraction.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/subtraction.com.php new file mode 100644 index 00000000..6d744277 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/subtraction.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.subtraction.com/2015/06/06/time-lapse-video-of-one-world-trade-center/', + 'body' => array('//article/div[@class="entry-content"]'), + 'strip' => array(), + ), + ), + 'filter' => array( + '%.*%' => array( + '%\+<h3.*/ul>%' => '', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sz.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sz.de.php new file mode 100644 index 00000000..90bde5a9 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/sz.de.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://sz.de/1.2443161', + 'body' => array('//article[@id="sitecontent"]/section[@class="topenrichment"]//img | //article[@id="sitecontent"]/section[@class="body"]/section[@class="authors"]/preceding-sibling::*[not(contains(@class, "ad"))]'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/takprosto.cc.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/takprosto.cc.php new file mode 100644 index 00000000..624ef907 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/takprosto.cc.php @@ -0,0 +1,21 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://takprosto.cc/kokteyl-dlya-pohudeniya-v-domashnih-usloviyah/', + 'body' => array( + '//div[contains(@class, "entry-contentt")]', + ), + 'strip' => array( + '//script', + '//form', + '//style', + '//*[@class="views_post"]', + '//*[contains(@class, "mailchimp-box")]', + '//*[contains(@class, "essb_links")]', + '//*[contains(@rel, "nofollow")]', + '//*[contains(@class, "ads")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/techcrunch.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/techcrunch.com.php new file mode 100644 index 00000000..d6e620f6 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/techcrunch.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://techcrunch.com/2013/08/31/indias-visa-maze/', + 'body' => array( + '//div[contains(@class, "media-container")]', + '//div[@class="body-copy"]', + ), + 'strip' => array( + '//*[contains(@class, "module-crunchbase")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/the-ebook-reader.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/the-ebook-reader.com.php new file mode 100644 index 00000000..3b01eb96 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/the-ebook-reader.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://blog.the-ebook-reader.com/2015/09/25/kobo-glo-hd-and-kobo-touch-2-0-covers-and-cases-roundup/', + 'body' => array( + '//div[@class="entry"]', + ), + 'strip' => array( + '//div[@id="share"]', + '//div[contains(@class,"ItemCenter")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theatlantic.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theatlantic.com.php new file mode 100644 index 00000000..bfad4ab2 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theatlantic.com.php @@ -0,0 +1,23 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.theatlantic.com/politics/archive/2015/09/what-does-it-mean-to-lament-the-poor-inside-panem/407317/', + 'body' => array( + '//picture[@class="img"]', + '//figure/figcaption/span', + '//div/p[@itemprop="description"]', + '//div[@class="article-body"]', + '//ul[@class="photos"]', + ), + 'strip' => array( + '//aside[@class="callout"]', + '//span[@class="credit"]', + '//figcaption[@class="credit"]', + '//aside[contains(@class,"partner-box")]', + '//div[contains(@class,"ad")]', + '//a[contains(@class,"social-icon")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theawkwardyeti.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theawkwardyeti.com.php new file mode 100644 index 00000000..fd4f3d50 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theawkwardyeti.com.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%/comic/.*%' => array( + 'test_url' => 'http://theawkwardyeti.com/comic/things-to-do/', + 'body' => array( + '//div[@id="comic"]' + ), + 'strip' => array() + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thecodinglove.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thecodinglove.com.php new file mode 100644 index 00000000..c6ec5bf5 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thecodinglove.com.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://thecodinglove.com/post/116897934767', + 'body' => array('//div[@class="bodytype"]'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thedoghousediaries.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thedoghousediaries.com.php new file mode 100644 index 00000000..d2f840d7 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thedoghousediaries.com.php @@ -0,0 +1,18 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'body' => array( + '//div[@class="comicpane"]/a/img', + '//div[@class="entry"]', + ), + 'strip' => array(), + 'test_url' => 'http://thedoghousediaries.com/6023', + ), + ), + 'filter' => array( + '%.*%' => array( + '%title="(.+)" */>%' => '/><br/>$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thegamercat.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thegamercat.com.php new file mode 100644 index 00000000..f9b4637b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thegamercat.com.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.thegamercat.com/comic/just-no/', + 'body' => array('//div[@id="comic"] | //div[@class="post-content"]/div[@class="entry"]/p'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thehindu.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thehindu.com.php new file mode 100644 index 00000000..1e6735ba --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thehindu.com.php @@ -0,0 +1,19 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.thehindu.com/sci-tech/science/why-is-the-shape-of-cells-in-a-honeycomb-always-hexagonal/article7692306.ece?utm_source=RSS_Feed&utm_medium=RSS&utm_campaign=RSS_Syndication', + 'body' => array( + '//div/img[@class="main-image"]', + '//div[@class="photo-caption"]', + '//div[@class="articleLead"]', + '//p', + '//span[@class="upper"]', + ), + 'strip' => array( + '//div[@id="articleKeywords"]', + '//div[@class="photo-source"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thelocal.se.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thelocal.se.php new file mode 100644 index 00000000..c3ec250c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thelocal.se.php @@ -0,0 +1,17 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'www.thelocal.se/20161219/this-swede-can-memorize-hundreds-of-numbers-in-only-five-minutes', + 'body' => array( + '//div[@id="article-photo"]', + '//div[@id="article-description"]', + '//div[@id="article-body"]', + ), + 'strip' => array( + '//div[@id="article-info-middle"]', + ) + ) + ) +); + diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/themerepublic.net.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/themerepublic.net.php new file mode 100644 index 00000000..bc47b278 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/themerepublic.net.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.themerepublic.net/2015/04/david-lopez-pitoko.html?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+blogspot%2FDngUJ+%28Theme+Republic%29&utm_content=FeedBurner', + 'body' => array('//*[@class="post-body"]'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/themoscowtimes.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/themoscowtimes.com.php new file mode 100644 index 00000000..0f5bf75e --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/themoscowtimes.com.php @@ -0,0 +1,18 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.themoscowtimes.com/business/article/535500.html', + 'body' => array( + '//div[@class="article_main_img"]', + '//div[@class="article_text"]', + ), + 'strip' => array( + '//div[@class="articlebottom"]', + '//p/b', + '//p/a[contains(@href, "/article.php?id=")]', + '//div[@class="disqus_wrap"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thenewslens.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thenewslens.com.php new file mode 100644 index 00000000..75381707 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thenewslens.com.php @@ -0,0 +1,21 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://international.thenewslens.com/post/255032/', + 'body' => array( + '//div[@class="article-section"]', + ), + 'strip' => array( + '//div[contains(@class,"ad-")]', + '//div[@class="article-title-box"]', + '//div[@class="function-box"]', + '//p/span', + '//aside', + '//footer', + '//div[@class="article-infoBot-box"]', + '//div[contains(@class,"standard-container")]' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theodd1sout.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theodd1sout.com.php new file mode 100644 index 00000000..d06ed124 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theodd1sout.com.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%-150x150%' => '', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theonion.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theonion.com.php new file mode 100644 index 00000000..acbfd36b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/theonion.com.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.theonion.com/article/wild-eyed-jim-harbaugh-informs-players-they-must-k-51397?utm_medium=RSS&utm_campaign=feeds', + 'body' => array( + '//div[@class="content-masthead"]/figure/div/noscript/img', + '//div[@class="content-text"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thestandard.com.hk.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thestandard.com.hk.php new file mode 100644 index 00000000..1163b345 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/thestandard.com.hk.php @@ -0,0 +1,22 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.thestandard.com.hk/breaking_news_detail.asp?id=67156', + 'body' => array( + '//table/tr/td/span[@class="bodyCopy"]', + ), + 'strip' => array( + '//script', + '//br', + '//map[@name="gif_bar"]', + '//img[contains(@usemap,"gif_bar")]', + '//a', + '//span[@class="bodyHeadline"]', + '//i', + '//b', + '//table', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/threepanelsoul.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/threepanelsoul.com.php new file mode 100644 index 00000000..4af6196e --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/threepanelsoul.com.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'body' => array( + '//img[@id="cc-comic"]', + ), + 'test_url' => 'http://www.threepanelsoul.com/comic/uncloaking', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/timesofindia.indiatimes.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/timesofindia.indiatimes.com.php new file mode 100644 index 00000000..19246806 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/timesofindia.indiatimes.com.php @@ -0,0 +1,14 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://timesofindia.indiatimes.com/city/mangaluru/Adani-UPCL-to-release-CSR-grant-of-Rs-3-74-crore-to-YellurGram-Panchayat/articleshow/50512116.cms', + 'body' => array( + '//div[@class="article_content clearfix"]', + '//section[@class="highlight clearfix"]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/travel-dealz.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/travel-dealz.de.php new file mode 100644 index 00000000..4ee4fcdc --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/travel-dealz.de.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%^/blog.*%' => array( + 'test_url' => 'http://travel-dealz.de/blog/venere-gutschein/', + 'body' => array('//div[@class="post-entry"]'), + 'strip' => array( + '//*[@id="jp-relatedposts"]', + '//*[@class="post-meta"]', + '//*[@class="post-data"]', + '//*[@id="author-meta"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/treehugger.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/treehugger.com.php new file mode 100644 index 00000000..55eb7e01 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/treehugger.com.php @@ -0,0 +1,14 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.treehugger.com/uncategorized/top-ten-posts-week-bunnies-2.html', + 'body' => array( + '//div[contains(@class, "promo-image")]', + '//div[contains(@id, "entry-body")]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/treelobsters.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/treelobsters.com.php new file mode 100644 index 00000000..3214c62a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/treelobsters.com.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%title="(.+)" */>%' => '/><br/>$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/twogag.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/twogag.com.php new file mode 100644 index 00000000..79f4f62e --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/twogag.com.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%http://www.twogag.com/comics-rss/([^.]+)\\.jpg%' => 'http://www.twogag.com/comics/$1.jpg', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/twokinds.keenspot.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/twokinds.keenspot.com.php new file mode 100644 index 00000000..3428fcb4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/twokinds.keenspot.com.php @@ -0,0 +1,10 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://twokinds.keenspot.com/archive.php?p=0', + 'body' => array('//*[@class="comic"]/div/a/img | //*[@class="comic"]/div/img | //*[@id="cg_img"]/img | //*[@id="cg_img"]/a/img'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/undeadly.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/undeadly.org.php new file mode 100644 index 00000000..8b15900a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/undeadly.org.php @@ -0,0 +1,14 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://undeadly.org/cgi?action=article&sid=20141101181155', + 'body' => array( + '/html/body/table[3]/tbody/tr/td[1]/table[2]/tr/td[1]', + ), + 'strip' => array( + '//font', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/upi.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/upi.com.php new file mode 100644 index 00000000..ec8d1a1a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/upi.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.upi.com/Top_News/US/2015/09/26/Tech-giants-Hollywood-stars-among-guests-at-state-dinner-for-Chinas-Xi-Jinping/4541443281006/', + 'body' => array( + '//div[@class="img"]', + '//div/article[@itemprop="articleBody"]', + ), + 'strip' => array( + '//div[@align="center"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/usatoday.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/usatoday.com.php new file mode 100644 index 00000000..edd6aa44 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/usatoday.com.php @@ -0,0 +1,27 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.usatoday.com/story/life/music/2017/02/13/things-you-should-know-happened-grammy-awards-2017/97833734/', + 'body' => array( + '//div[@itemprop="articleBody"]', + ), + 'strip' => array( + '//script', + '//h1', + '//iframe', + '//span[@class="mycapture-small-btn mycapture-btn-with-text mycapture-expandable-photo-btn-small js-mycapture-btn-small"]', + '//div[@class="close-wrap"]', + '//div[contains(@class,"ui-video-wrapper")]', + '//div[contains(@class,"media-mob")]', + '//div[contains(@class,"left-mob")]', + '//div[contains(@class,"nerdbox")]', + '//div[contains(@class,"oembed-asset")]', + '//*[contains(@class,"share")]', + '//div[contains(@class,"gallery-asset")]', + '//div[contains(@class,"oembed-asset")]', + '//div[@class="article-print-url"]' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/version2.dk.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/version2.dk.php new file mode 100644 index 00000000..a6d49f2e --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/version2.dk.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.version2.dk/artikel/surface-pro-2-fungerer-bedre-til-arbejde-end-fornoejelse-55195', + 'body' => array( + '//section[contains(@class, "teaser")]', + '//section[contains(@class, "body")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/vgcats.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/vgcats.com.php new file mode 100644 index 00000000..b2830a37 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/vgcats.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%/comics.*%' => array( + 'test_url' => 'http://www.vgcats.com/comics/?strip_id=358', + 'body' => array('//*[@align="center"]/img'), + 'strip' => array(), + ), + '%/super.*%' => array( + 'test_url' => 'http://www.vgcats.com/super/?strip_id=84', + 'body' => array('//*[@align="center"]/p/img'), + 'strip' => array(), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/vuxml.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/vuxml.org.php new file mode 100644 index 00000000..b9bef7a4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/vuxml.org.php @@ -0,0 +1,17 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.vuxml.org/freebsd/a5f160fa-deee-11e4-99f8-080027ef73ec.html', + 'body' => array( + '//body', + ), + 'strip' => array( + '//h1', + '//div[@class="blurb"]', + '//hr', + '//p[@class="copyright"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/wausaudailyherald.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/wausaudailyherald.com.php new file mode 100644 index 00000000..58aceeaf --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/wausaudailyherald.com.php @@ -0,0 +1,27 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.wausaudailyherald.com/story/news/2017/04/01/hundreds-gather-remember-attorney-killed-shooting-spree/99826062/?from=global&sessionKey=&autologin=', + 'body' => array( + '//div[@itemprop="articleBody"]', + ), + 'strip' => array( + '//h1', + '//iframe', + '//span[@class="mycapture-small-btn mycapture-btn-with-text mycapture-expandable-photo-btn-small js-mycapture-btn-small"]', + '//div[@class="close-wrap"]', + '//div[contains(@class,"ui-video-wrapper")]', + '//div[contains(@class,"media-mob")]', + '//div[contains(@class,"left-mob")]', + '//div[contains(@class,"nerdbox")]', + '//p/span', + '//div[contains(@class,"oembed-asset")]', + '//*[contains(@class,"share")]', + '//div[contains(@class,"gallery-asset")]', + '//div[contains(@class,"oembed-asset")]', + '//div[@class="article-print-url"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bbc.co.uk.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bbc.co.uk.php new file mode 100644 index 00000000..98fc368a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bbc.co.uk.php @@ -0,0 +1,33 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.bbc.co.uk/news/world-middle-east-23911833', + 'body' => array( + '//div[@class="story-body__inner"] | //div[@class="article"]', + '//div[@class="indPost"]', + ), + 'strip' => array( + '//form', + '//div[@id="headline"]', + '//*[@class="warning"]', + '//span[@class="off-screen"]', + '//span[@class="story-image-copyright"]', + '//ul[@class="story-body__unordered-list"]', + '//div[@class="ad_wrapper"]', + '//div[@id="article-sidebar"]', + '//div[@class="data-table-outer"]', + '//*[@class="story-date"]', + '//*[@class="story-header"]', + '//figure[contains(@class,"has-caption")]', + '//*[@class="story-related"]', + '//*[contains(@class, "byline")]', + '//p[contains(@class, "media-message")]', + '//*[contains(@class, "story-feature")]', + '//*[@id="video-carousel-container"]', + '//*[@id="also-related-links"]', + '//*[contains(@class, "share") or contains(@class, "hidden") or contains(@class, "hyper")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bdgest.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bdgest.com.php new file mode 100644 index 00000000..41ef68d4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bdgest.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.bdgest.com/chronique-6027-BD-Adrastee-Tome-2.html', + 'body' => array( + '//*[contains(@class, "chronique")]', + ), + 'strip' => array( + '//*[contains(@class, "post-review")]', + '//*[contains(@class, "footer-review")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bgr.in.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bgr.in.php new file mode 100644 index 00000000..63ca069e --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.bgr.in.php @@ -0,0 +1,23 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.bgr.in/news/xiaomi-redmi-3-with-13-megapixel-camera-snapdragon-616-launched-price-specifications-and-features/', + 'body' => array( + '//div[@class="article-content"]', + ), + 'strip' => array( + '//*[@class="article-meta"]', + '//*[@class="contentAdsense300"]', + '//*[@class="iwpl-social-hide"]', + '//iframe[@class="iframeads"]', + '//*[@class="disqus_thread"]', + '//*[@class="outb-mobile OUTBRAIN"]', + '//*[@class="wdt_smart_alerts"]', + '//*[@class="footnote"]', + '//*[@id="gadget-widget"]', + '//header[@class="article-title entry-header"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.businessweek.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.businessweek.com.php new file mode 100644 index 00000000..0acc44ec --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.businessweek.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.businessweek.com/articles/2013-09-18/elon-musks-hyperloop-will-work-says-some-very-smart-software', + 'body' => array( + '//div[@id="lead_graphic"]', + '//div[@id="article_body"]', + ), + 'strip' => array( + '//*[contains(@class, "related_item")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.cnn.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.cnn.com.php new file mode 100644 index 00000000..31d03ed9 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.cnn.com.php @@ -0,0 +1,24 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.cnn.com/2013/08/31/world/meast/syria-civil-war/index.html?hpt=hp_t1', + 'body' => array( + '//div[@class="cnn_strycntntlft"]', + ), + 'strip' => array( + '//div[@class="cnn_stryshrwdgtbtm"]', + '//div[@class="cnn_strybtmcntnt"]', + '//div[@class="cnn_strylftcntnt"]', + '//div[contains(@class, "cnnGalleryContainer")]', + '//div[contains(@class, "cnn_strylftcexpbx")]', + '//div[contains(@class, "articleGalleryNavContainer")]', + '//div[contains(@class, "cnnArticleGalleryCaptionControl")]', + '//div[contains(@class, "cnnArticleGalleryNavPrevNextDisabled")]', + '//div[contains(@class, "cnnArticleGalleryNavPrevNext")]', + '//div[contains(@class, "cnn_html_media_title_new")]', + '//div[contains(@id, "disqus")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.developpez.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.developpez.com.php new file mode 100644 index 00000000..1535e437 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.developpez.com.php @@ -0,0 +1,21 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.developpez.com/actu/81757/Mozilla-annonce-la-disponibilite-de-Firefox-36-qui-passe-au-HTTP-2-et-permet-la-synchronisation-de-son-ecran-d-accueil/', + 'body' => array( + '//*[@itemprop="articleBody"]', + ), + 'strip' => array( + '//form', + '//div[@class="content"]/img', + '//a[last()]/following-sibling::*', + '//*[contains(@class,"actuTitle")]', + '//*[contains(@class,"date")]', + '//*[contains(@class,"inlineimg")]', + '//*[@id="signaler"]', + '//*[@id="signalerFrame"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.egscomics.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.egscomics.com.php new file mode 100644 index 00000000..263f0755 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.egscomics.com.php @@ -0,0 +1,12 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.egscomics.com/index.php?id=1690', + 'title' => '/html/head/title', + 'body' => array( + '//img[@id="comic"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.fakingnews.firstpost.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.fakingnews.firstpost.com.php new file mode 100644 index 00000000..c948c77b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.fakingnews.firstpost.com.php @@ -0,0 +1,17 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.fakingnews.firstpost.com/2016/01/engineering-student-creates-record-in-a-decade-becomes-the-first-to-completely-exhaust-ball-pen-refill/', + 'body' => array( + '//div[@class="entry"]', + ), + 'strip' => array( + '//*[@class="socialshare_bar"]', + '//*[@class="authorbox"]', + '//*[@class="cf5_rps"]', + '//*[@class="60563 fb-comments fb-social-plugin"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.forbes.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.forbes.com.php new file mode 100644 index 00000000..fd16ed57 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.forbes.com.php @@ -0,0 +1,20 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.forbes.com/sites/andygreenberg/2013/09/05/follow-the-bitcoins-how-we-got-busted-buying-drugs-on-silk-roads-black-market/', + 'body' => array( + '//div[@id="leftRail"]/div[contains(@class, body)]', + ), + 'strip' => array( + '//aside', + '//div[contains(@class, "entity_block")]', + '//div[contains(@class, "vestpocket") and not contains(@class, "body")]', + '//div[contains(@style, "display")]', + '//div[contains(@id, "comment")]', + '//div[contains(@class, "widget")]', + '//div[contains(@class, "pagination")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.franceculture.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.franceculture.fr.php new file mode 100644 index 00000000..f7ec0d8d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.franceculture.fr.php @@ -0,0 +1,14 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.franceculture.fr/emission-culture-eco-la-finance-aime-toujours-la-france-2016-01-08', + 'body' => array( + '//div[@class="text-zone"]', + ), + 'strip' => array( + '//ul[@class="tags"]', + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.futura-sciences.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.futura-sciences.com.php new file mode 100644 index 00000000..ea94a0fb --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.futura-sciences.com.php @@ -0,0 +1,19 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.futura-sciences.com/magazines/espace/infos/actu/d/astronautique-curiosity-franchi-succes-dune-dingo-gap-52289/#xtor=RSS-8', + 'body' => array( + '//div[contains(@class, "content fiche-")]', + ), + 'strip' => array( + '//h1', + '//*[contains(@class, "content-date")]', + '//*[contains(@class, "diaporama")]', + '//*[contains(@class, "slider")]', + '//*[contains(@class, "cartouche")]', + '//*[contains(@class, "noprint")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.geekculture.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.geekculture.com.php new file mode 100644 index 00000000..3d0b6c75 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.geekculture.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.geekculture.com/joyoftech/joyarchives/2180.html', + 'body' => array( + '//p[contains(@class,"Maintext")][2]/a/img[contains(@src,"joyimages")]', + ), + 'strip' => array(), + ), + ), +); + diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.howtogeek.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.howtogeek.com.php new file mode 100644 index 00000000..6879e767 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.howtogeek.com.php @@ -0,0 +1,14 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.howtogeek.com/235283/what-is-a-wireless-hard-drive-and-should-i-get-one/', + 'body' => array( + '//div[@class="thecontent"]', + ), + 'strip' => array( + '//*[@class="relatedside"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.lepoint.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.lepoint.fr.php new file mode 100644 index 00000000..dcb7e484 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.lepoint.fr.php @@ -0,0 +1,18 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.lepoint.fr/c-est-arrive-aujourd-hui/19-septembre-1783-pour-la-premiere-fois-un-mouton-un-canard-et-un-coq-s-envoient-en-l-air-devant-louis-xvi-18-09-2012-1507704_494.php', + 'body' => array( + '//article', + ), + 'strip' => array( + '//*[contains(@class, "info_article")]', + '//*[contains(@class, "fildariane_titre")]', + '//*[contains(@class, "entete2_article")]', + '//*[contains(@class, "signature_article")]', + '//*[contains(@id, "share")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.lesnumeriques.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.lesnumeriques.com.php new file mode 100644 index 00000000..0137e209 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.lesnumeriques.com.php @@ -0,0 +1,25 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.lesnumeriques.com/blender/kitchenaid-diamond-5ksb1585-p27473/test.html', + 'body' => array( + '//*[@id="product-content"]', + '//*[@id="news-content"]', + '//*[@id="article-content"]', + ), + 'strip' => array( + '//form', + '//div[contains(@class, "price-v4"])', + '//div[contains(@class, "authors-and-date")]', + '//div[contains(@class, "mini-product")]', + '//div[@id="articles-related-authors"]', + '//div[@id="tags-socials"]', + '//div[@id="user-reviews"]', + '//div[@id="product-reviews"]', + '//div[@id="publication-breadcrumbs-and-date"]', + '//div[@id="publication-breadcrumbs-and-date"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.mac4ever.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.mac4ever.com.php new file mode 100644 index 00000000..60bc1bd4 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.mac4ever.com.php @@ -0,0 +1,13 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.mac4ever.com/actu/87392_video-quand-steve-jobs-et-bill-gates-jouaient-au-bachelor-avec-le-mac', + 'body' => array( + '//div[contains(@class, "news-news-content")]', + ), + 'strip' => array( + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.makeuseof.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.makeuseof.com.php new file mode 100644 index 00000000..a274564a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.makeuseof.com.php @@ -0,0 +1,18 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.makeuseof.com/tag/having-problems-with-audio-in-windows-10-heres-a-likely-fix/', + 'body' => array( + '//div[@class="entry"]', + ), + 'strip' => array( + '//*[@class="new_sharebar"]', + '//*[@class="author"]', + '//*[@class="wdt_grouvi"]', + '//*[@class="wdt_smart_alerts"]', + '//*[@class="modal fade grouvi"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.monsieur-le-chien.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.monsieur-le-chien.fr.php new file mode 100644 index 00000000..5f5e987b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.monsieur-le-chien.fr.php @@ -0,0 +1,11 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.monsieur-le-chien.fr/index.php?planche=672', + 'body' => array( + '//img[starts-with(@src, "i/planches/")]', + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.npr.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.npr.org.php new file mode 100644 index 00000000..ecc0213a --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.npr.org.php @@ -0,0 +1,28 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.npr.org/blogs/thesalt/2013/09/17/223345977/auto-brewery-syndrome-apparently-you-can-make-beer-in-your-gut', + 'body' => array( + '//article[contains(@class,"story")]', + ), + 'strip' => array( + '//div[@class="story-tools"]', + '//h3[@class="slug"]', + '//div[@class="storytitle"]', + '//div[@id="story-meta"]', + '//a[@id="mainContent"]', + '//div[@class="credit-caption"]', + '//div[@class="enlarge_html"]', + '//button', + '//div[contains(@id,"pullquote")]', + '//div[contains(@class,"internallink")]', + '//div[contains(@class,"video")]', + '//div[@class="simplenodate"]', + '//div[contains(@class,"share-")]', + '//div[@class="tags"]', + '//aside' + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.numerama.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.numerama.com.php new file mode 100644 index 00000000..fe4971c5 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.numerama.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.numerama.com/sciences/125959-recherches-ladn-recompensees-nobel-de-chimie.html', + 'body' => array( + '//article', + ), + 'strip' => array( + '//footer', + '//section[@class="related-article"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.oneindia.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.oneindia.com.php new file mode 100644 index 00000000..320c2147 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.oneindia.com.php @@ -0,0 +1,14 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.oneindia.com/india/b-luru-govt-likely-remove-word-eunuch-from-sec-36-a-karnataka-police-act-1981173.html', + 'body' => array( + '//div[@class="ecom-ad-content"]', + ), + 'strip' => array( + '//*[@id="view_cmtns"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.pseudo-sciences.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.pseudo-sciences.org.php new file mode 100644 index 00000000..9e467edf --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.pseudo-sciences.org.php @@ -0,0 +1,16 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.pseudo-sciences.org/spip.php?article2275', + 'body' => array( + '//div[@id="art_main"]', + ), + 'strip' => array( + '//div[@id="art_print"]', + '//div[@id="art_chapo"]', + '//img[@class="puce"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.sciencemag.org.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.sciencemag.org.php new file mode 100644 index 00000000..ae7a93ac --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.sciencemag.org.php @@ -0,0 +1,16 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.sciencemag.org/news/2016/01/could-bright-foamy-wak$', + 'body' => array( + '//div[@class="row--hero"]', + '//article[contains(@class,"primary")]', + ), + 'strip' => array( + '//header[@class="article__header"]', + '//footer[@class="article__foot"]', + ), + ), + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.slate.fr.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.slate.fr.php new file mode 100644 index 00000000..8c8dc893 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.slate.fr.php @@ -0,0 +1,19 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.slate.fr/monde/77034/allemagne-2013-couacs-campagne', + 'body' => array( + '//div[@class="article_content"]', + ), + 'strip' => array( + '//*[@id="slate_associated_bn"]', + '//*[@id="ligatus-article"]', + '//*[@id="article_sidebar"]', + '//div[contains(@id, "reseaux")]', + '//*[contains(@class, "smart") or contains(@class, "article_tags") or contains(@class, "article_reactions")]', + '//*[contains(@class, "OUTBRAIN") or contains(@class, "related_item") or contains(@class, "share")]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.universfreebox.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.universfreebox.com.php new file mode 100644 index 00000000..0747d0fb --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.universfreebox.com.php @@ -0,0 +1,15 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://www.universfreebox.com/article/24305/4G-Bouygues-Telecom-lance-une-vente-flash-sur-son-forfait-Sensation-3Go', + 'body' => array( + '//div[@id="corps_corps"]', + ), + 'strip' => array( + '//*[@id="formulaire"]', + '//*[@id="commentaire"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.zeit.de.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.zeit.de.php new file mode 100644 index 00000000..316c2656 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/www.zeit.de.php @@ -0,0 +1,41 @@ +<?php +return array( + 'grabber' => array( + '%^/zeit-magazin.*%' => array( + 'test_url' => 'http://www.zeit.de/zeit-magazin/2015/15/pegida-kathrin-oertel-lutz-bachmann', + 'body' => array( + '//article[@class="article"]', + ), + 'strip' => array( + '//header/div/h1', + '//header/div/div[@class="article__head__subtitle"]', + '//header/div/div[@class="article__column__author"]', + '//header/div/div[@class="article__column__author"]', + '//header/div/span[@class="article__head__meta-wrap"]', + '//form', + '//style', + '//div[contains(@class, "ad-tile")]', + '//div[@class="iqd-mobile-adplace"]', + '//div[@id="iq-artikelanker"]', + '//div[@id="js-social-services"]', + '//section[@id="js-comments"]', + '//aside', + ), + ), + '%.*%' => array( + 'test_url' => 'http://www.zeit.de/politik/ausland/2015-04/thessaloniki-krise-griechenland-yannis-boutaris/', + 'body' => array( + '//div[@class="article-body"]', + ), + 'strip' => array( + '//*[@class="articleheader"]', + '//*[@class="excerpt"]', + '//div[contains(@class, "ad")]', + '//div[@itemprop="video"]', + '//*[@class="articlemeta"]', + '//*[@class="articlemeta-clear"]', + '//*[@class="zol_inarticletools"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/xkcd.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/xkcd.com.php new file mode 100644 index 00000000..84957268 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/xkcd.com.php @@ -0,0 +1,8 @@ +<?php +return array( + 'filter' => array( + '%.*%' => array( + '%alt="(.+)" */>%' => '/><br/>$1', + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ymatuhin.ru.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ymatuhin.ru.php new file mode 100644 index 00000000..9fd83f18 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/ymatuhin.ru.php @@ -0,0 +1,21 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'https://ymatuhin.ru/tools/git-default-editor/', + 'body' => array( + '//section', + ), + 'strip' => array( + "//script", + "//style", + "//h1", + "//time", + "//aside", + "/html/body/section/ul", + "//amp-iframe", + "/html/body/section/h4" + ), + ) + ) +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Rules/zdnet.com.php b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/zdnet.com.php new file mode 100644 index 00000000..79b35ddb --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Rules/zdnet.com.php @@ -0,0 +1,23 @@ +<?php +return array( + 'grabber' => array( + '%.*%' => array( + 'test_url' => 'http://zdnet.com.feedsportal.com/c/35462/f/675637/s/4a33c93e/sc/11/l/0L0Szdnet0N0Carticle0Cchina0Eus0Eagree0Eon0Ecybercrime0Ecooperation0Eamid0Econtinued0Etension0C0Tftag0FRSSbaffb68/story01.htm', + 'body' => array( + '//p[@class="summary"]', + '//div[contains(@class,"storyBody")]', + ), + 'strip' => array( + '//*[contains(@class,"ad-")]', + '//p/span', + '//script', + '//p[@class="summary"]', + '//div[contains(@class,"relatedContent")]', + '//div[contains(@class,"loader")]', + '//p[@class="photoDetails"]', + '//div[@class="thumbnailSlider"]', + '//div[@class="shortcodeGalleryWrapper"]', + ), + ), + ), +); diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/CandidateParser.php b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/CandidateParser.php new file mode 100644 index 00000000..6c53a289 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/CandidateParser.php @@ -0,0 +1,283 @@ +<?php + +namespace PicoFeed\Scraper; + +use DomDocument; +use DOMXPath; +use PicoFeed\Logging\Logger; +use PicoFeed\Parser\XmlParser; + +/** + * Candidate Parser. + * + * @author Frederic Guillot + */ +class CandidateParser implements ParserInterface +{ + private $dom; + private $xpath; + + /** + * List of attributes to try to get the content, order is important, generic terms at the end. + * + * @var array + */ + private $candidatesAttributes = array( + 'articleBody', + 'articlebody', + 'article-body', + 'articleContent', + 'articlecontent', + 'article-content', + 'articlePage', + 'post-content', + 'post_content', + 'entry-content', + 'entry-body', + 'main-content', + 'story_content', + 'storycontent', + 'entryBox', + 'entrytext', + 'comic', + 'post', + 'article', + 'content', + 'main', + ); + + /** + * List of attributes to strip. + * + * @var array + */ + private $stripAttributes = array( + 'comment', + 'share', + 'links', + 'toolbar', + 'fb', + 'footer', + 'credit', + 'bottom', + 'nav', + 'header', + 'social', + 'tag', + 'metadata', + 'entry-utility', + 'related-posts', + 'tweet', + 'categories', + 'post_title', + 'by_line', + 'byline', + 'sponsors', + ); + + /** + * Tags to remove. + * + * @var array + */ + private $stripTags = array( + 'nav', + 'header', + 'footer', + 'aside', + 'form', + ); + + /** + * Constructor. + * + * @param string $html + */ + public function __construct($html) + { + $this->dom = XmlParser::getHtmlDocument('<?xml version="1.0" encoding="UTF-8">'.$html); + $this->xpath = new DOMXPath($this->dom); + } + + /** + * Get the relevant content with the list of potential attributes. + * + * @return string + */ + public function execute() + { + $content = $this->findContentWithCandidates(); + + if (strlen($content) < 200) { + $content = $this->findContentWithArticle(); + } + + if (strlen($content) < 50) { + $content = $this->findContentWithBody(); + } + + return $this->stripGarbage($content); + } + + /** + * Find content based on the list of tag candidates. + * + * @return string + */ + public function findContentWithCandidates() + { + foreach ($this->candidatesAttributes as $candidate) { + Logger::setMessage(get_called_class().': Try this candidate: "'.$candidate.'"'); + + $nodes = $this->xpath->query('//*[(contains(@class, "'.$candidate.'") or @id="'.$candidate.'") and not (contains(@class, "nav") or contains(@class, "page"))]'); + + if ($nodes !== false && $nodes->length > 0) { + Logger::setMessage(get_called_class().': Find candidate "'.$candidate.'"'); + + return $this->dom->saveXML($nodes->item(0)); + } + } + + return ''; + } + + /** + * Find <article/> tag. + * + * @return string + */ + public function findContentWithArticle() + { + $nodes = $this->xpath->query('//article'); + + if ($nodes !== false && $nodes->length > 0) { + Logger::setMessage(get_called_class().': Find <article/> tag'); + + return $this->dom->saveXML($nodes->item(0)); + } + + return ''; + } + + /** + * Find <body/> tag. + * + * @return string + */ + public function findContentWithBody() + { + $nodes = $this->xpath->query('//body'); + + if ($nodes !== false && $nodes->length > 0) { + Logger::setMessage(get_called_class().' Find <body/>'); + + return $this->dom->saveXML($nodes->item(0)); + } + + return ''; + } + + /** + * Strip useless tags. + * + * @param string $content + * + * @return string + */ + public function stripGarbage($content) + { + $dom = XmlParser::getDomDocument($content); + + if ($dom !== false) { + $xpath = new DOMXPath($dom); + + $this->stripTags($xpath); + $this->stripAttributes($dom, $xpath); + + $content = $dom->saveXML($dom->documentElement); + } + + return $content; + } + + /** + * Remove blacklisted tags. + * + * @param DOMXPath $xpath + */ + public function stripTags(DOMXPath $xpath) + { + foreach ($this->stripTags as $tag) { + $nodes = $xpath->query('//'.$tag); + + if ($nodes !== false && $nodes->length > 0) { + Logger::setMessage(get_called_class().': Strip tag: "'.$tag.'"'); + + foreach ($nodes as $node) { + $node->parentNode->removeChild($node); + } + } + } + } + + /** + * Remove blacklisted attributes. + * + * @param DomDocument $dom + * @param DOMXPath $xpath + */ + public function stripAttributes(DomDocument $dom, DOMXPath $xpath) + { + foreach ($this->stripAttributes as $attribute) { + $nodes = $xpath->query('//*[contains(@class, "'.$attribute.'") or contains(@id, "'.$attribute.'")]'); + + if ($nodes !== false && $nodes->length > 0) { + Logger::setMessage(get_called_class().': Strip attribute: "'.$attribute.'"'); + + foreach ($nodes as $node) { + if ($this->shouldRemove($dom, $node)) { + $node->parentNode->removeChild($node); + } + } + } + } + } + + /** + * Find link for next page of the article. + * + * @return string + */ + public function findNextLink() + { + return null; + } + + /** + * Return false if the node should not be removed. + * + * @param DomDocument $dom + * @param DomNode $node + * + * @return bool + */ + public function shouldRemove(DomDocument $dom, $node) + { + $document_length = strlen($dom->textContent); + $node_length = strlen($node->textContent); + + if ($document_length === 0) { + return true; + } + + $ratio = $node_length * 100 / $document_length; + + if ($ratio >= 90) { + Logger::setMessage(get_called_class().': Should not remove this node ('.$node->nodeName.') ratio: '.$ratio.'%'); + + return false; + } + + return true; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/ParserInterface.php b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/ParserInterface.php new file mode 100644 index 00000000..3ded4b1c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/ParserInterface.php @@ -0,0 +1,20 @@ +<?php + +namespace PicoFeed\Scraper; + +interface ParserInterface +{ + /** + * Execute the parser and return the contents. + * + * @return string + */ + public function execute(); + + /** + * Find link for next page of the article. + * + * @return string + */ + public function findNextLink(); +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/RuleLoader.php b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/RuleLoader.php new file mode 100644 index 00000000..6650682d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/RuleLoader.php @@ -0,0 +1,107 @@ +<?php + +namespace PicoFeed\Scraper; + +use PicoFeed\Base; +use PicoFeed\Logging\Logger; + +/** + * RuleLoader class. + * + * @author Frederic Guillot + * @author Bernhard Posselt + */ +class RuleLoader extends Base +{ + /** + * Get the rules for an URL. + * + * @param string $url the URL that should be looked up + * + * @return array the array containing the rules + */ + public function getRules($url) + { + $hostname = parse_url($url, PHP_URL_HOST); + + if ($hostname !== false) { + $files = $this->getRulesFileList($hostname); + + foreach ($this->getRulesFolders() as $folder) { + $rule = $this->loadRuleFile($folder, $files); + + if (!empty($rule)) { + return $rule; + } + } + } + + return array(); + } + + /** + * Get the list of possible rules file names for a given hostname. + * + * @param string $hostname Hostname + * + * @return array + */ + public function getRulesFileList($hostname) + { + $files = array($hostname); // subdomain.domain.tld + $parts = explode('.', $hostname); + $len = count($parts); + + if ($len > 2) { + $subdomain = array_shift($parts); + $files[] = implode('.', $parts); // domain.tld + $files[] = '.'.implode('.', $parts); // .domain.tld + $files[] = $subdomain; // subdomain + } elseif ($len === 2) { + $files[] = '.'.implode('.', $parts); // .domain.tld + $files[] = $parts[0]; // domain + } + + return $files; + } + + /** + * Load a rule file from the defined folder. + * + * @param string $folder Rule directory + * @param array $files List of possible file names + * + * @return array + */ + public function loadRuleFile($folder, array $files) + { + foreach ($files as $file) { + $filename = $folder.'/'.$file.'.php'; + if (file_exists($filename)) { + Logger::setMessage(get_called_class().' Load rule: '.$file); + + return include $filename; + } + } + + return array(); + } + + /** + * Get the list of folders that contains rules. + * + * @return array + */ + public function getRulesFolders() + { + $folders = array(); + + if ($this->config !== null && $this->config->getGrabberRulesFolder() !== null) { + $folders[] = $this->config->getGrabberRulesFolder(); + } + + $folders[] = __DIR__ . '/../Rules'; + + return $folders; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/RuleParser.php b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/RuleParser.php new file mode 100644 index 00000000..9beb59c1 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/RuleParser.php @@ -0,0 +1,102 @@ +<?php + +namespace PicoFeed\Scraper; + +use DOMXPath; +use PicoFeed\Parser\XmlParser; + +/** + * Rule Parser. + * + * @author Frederic Guillot + */ +class RuleParser implements ParserInterface +{ + private $dom; + private $xpath; + private $rules = array(); + + /** + * Constructor. + * + * @param string $html + * @param array $rules + */ + public function __construct($html, array $rules) + { + $this->rules = $rules; + $this->dom = XmlParser::getHtmlDocument('<?xml version="1.0" encoding="UTF-8">'.$html); + $this->xpath = new DOMXPath($this->dom); + } + + /** + * Get the relevant content with predefined rules. + * + * @return string + */ + public function execute() + { + $this->stripTags(); + + return $this->findContent(); + } + + /** + * Remove HTML tags. + */ + public function stripTags() + { + if (isset($this->rules['strip']) && is_array($this->rules['strip'])) { + foreach ($this->rules['strip'] as $pattern) { + $nodes = $this->xpath->query($pattern); + + if ($nodes !== false && $nodes->length > 0) { + foreach ($nodes as $node) { + $node->parentNode->removeChild($node); + } + } + } + } + } + + /** + * Fetch content based on Xpath rules. + */ + public function findContent() + { + $content = ''; + if (isset($this->rules['body']) && is_array($this->rules['body'])) { + foreach ($this->rules['body'] as $pattern) { + $nodes = $this->xpath->query($pattern); + + if ($nodes !== false && $nodes->length > 0) { + foreach ($nodes as $node) { + $content .= $this->dom->saveXML($node); + } + } + } + } + + return $content; + } + + /** + * Fetch next link based on Xpath rules. + * + * @return string + */ + public function findNextLink() + { + if (isset($this->rules['next_page']) && is_array($this->rules['next_page'])) { + foreach ($this->rules['next_page'] as $pattern) { + $nodes = $this->xpath->query($pattern); + if ($nodes !== false && $nodes->length > 0) { + foreach ($nodes as $node) { + return $node->getAttribute('href'); + } + } + } + } + return null; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/Scraper.php b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/Scraper.php new file mode 100644 index 00000000..e5b9817f --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Scraper/Scraper.php @@ -0,0 +1,279 @@ +<?php + +namespace PicoFeed\Scraper; + +use PicoFeed\Base; +use PicoFeed\Client\Client; +use PicoFeed\Client\ClientException; +use PicoFeed\Client\Url; +use PicoFeed\Encoding\Encoding; +use PicoFeed\Filter\Filter; +use PicoFeed\Logging\Logger; +use PicoFeed\Parser\XmlParser; + +/** + * Scraper class. + * + * @author Frederic Guillot + */ +class Scraper extends Base +{ + /** + * URL. + * + * @var string + */ + private $url = ''; + + /** + * Relevant content. + * + * @var string + */ + private $content = ''; + + /** + * HTML content. + * + * @var string + */ + private $html = ''; + + /** + * HTML content encoding. + * + * @var string + */ + private $encoding = ''; + + /** + * Flag to enable candidates parsing. + * + * @var bool + */ + private $enableCandidateParser = true; + + /** + * Disable candidates parsing. + * + * @return Scraper + */ + public function disableCandidateParser() + { + $this->enableCandidateParser = false; + return $this; + } + + /** + * Get encoding. + * + * @return string + */ + public function getEncoding() + { + return $this->encoding; + } + + /** + * Set encoding. + * + * @param string $encoding + * + * @return Scraper + */ + public function setEncoding($encoding) + { + $this->encoding = $encoding; + + return $this; + } + + /** + * Get URL to download. + * + * @return string + */ + public function getUrl() + { + return $this->url; + } + + /** + * Set URL to download. + * + * @param string $url URL + * + * @return Scraper + */ + public function setUrl($url) + { + $this->url = $url; + + return $this; + } + + /** + * Return true if the scraper found relevant content. + * + * @return bool + */ + public function hasRelevantContent() + { + return !empty($this->content); + } + + /** + * Get relevant content. + * + * @return string + */ + public function getRelevantContent() + { + return $this->content; + } + + /** + * Get raw content (unfiltered). + * + * @return string + */ + public function getRawContent() + { + return $this->html; + } + + /** + * Set raw content (unfiltered). + * + * @param string $html + * + * @return Scraper + */ + public function setRawContent($html) + { + $this->html = $html; + + return $this; + } + + /** + * Get filtered relevant content. + * + * @return string + */ + public function getFilteredContent() + { + $filter = Filter::html($this->content, $this->url); + $filter->setConfig($this->config); + + return $filter->execute(); + } + + /** + * Download the HTML content. + * + * @return bool + */ + public function download() + { + if (!empty($this->url)) { + + // Clear everything + $this->html = ''; + $this->content = ''; + $this->encoding = ''; + + try { + $client = Client::getInstance(); + $client->setConfig($this->config); + $client->setTimeout($this->config->getGrabberTimeout()); + $client->setUserAgent($this->config->getGrabberUserAgent()); + $client->execute($this->url); + + $this->url = $client->getUrl(); + $this->html = $client->getContent(); + $this->encoding = $client->getEncoding(); + + return true; + } catch (ClientException $e) { + Logger::setMessage(get_called_class().': '.$e->getMessage()); + } + } + + return false; + } + + /** + * Execute the scraper. + */ + public function execute($pageContent = '', $recursionDepth = 0) + { + $this->html = ''; + $this->encoding = ''; + $this->content = ''; + $this->download(); + $this->prepareHtml(); + + $parser = $this->getParser(); + + if ($parser !== null) { + $maxRecursions = $this->config->getMaxRecursions(); + if(!isset($maxRecursions)){ + $maxRecursions = 25; + } + $pageContent .= $parser->execute(); + // check if there is a link to next page and recursively get content (max 25 pages) + if((($nextLink = $parser->findNextLink()) !== null) && $recursionDepth < $maxRecursions){ + $nextLink = Url::resolve($nextLink,$this->url); + $this->setUrl($nextLink); + $this->execute($pageContent,$recursionDepth+1); + } + else{ + $this->content = $pageContent; + } + Logger::setMessage(get_called_class().': Content length: '.strlen($this->content).' bytes'); + } + } + + /** + * Get the parser. + * + * @return ParserInterface + */ + public function getParser() + { + $ruleLoader = new RuleLoader($this->config); + $rules = $ruleLoader->getRules($this->url); + + if (!empty($rules['grabber'])) { + Logger::setMessage(get_called_class().': Parse content with rules'); + + foreach ($rules['grabber'] as $pattern => $rule) { + $url = new Url($this->url); + $sub_url = $url->getFullPath(); + + if (preg_match($pattern, $sub_url)) { + Logger::setMessage(get_called_class().': Matched url '.$sub_url); + return new RuleParser($this->html, $rule); + } + } + } elseif ($this->enableCandidateParser) { + Logger::setMessage(get_called_class().': Parse content with candidates'); + } + + return new CandidateParser($this->html); + } + + /** + * Normalize encoding and strip head tag. + */ + public function prepareHtml() + { + $html_encoding = XmlParser::getEncodingFromMetaTag($this->html); + + $this->html = Encoding::convert($this->html, $html_encoding ?: $this->encoding); + $this->html = Filter::stripHeadTags($this->html); + + Logger::setMessage(get_called_class().': HTTP Encoding "'.$this->encoding.'" ; HTML Encoding "'.$html_encoding.'"'); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/Subscription.php b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/Subscription.php new file mode 100644 index 00000000..12eccfd5 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/Subscription.php @@ -0,0 +1,175 @@ +<?php + +namespace PicoFeed\Serialization; + +/** + * Class Subscription + * + * @package PicoFeed\Serialization + * @author Frederic Guillot + */ +class Subscription +{ + protected $title = ''; + protected $feedUrl = ''; + protected $siteUrl = ''; + protected $category = ''; + protected $description = ''; + protected $type = ''; + + /** + * Create object instance + * + * @static + * @access public + * @return Subscription + */ + public static function create() + { + return new static(); + } + + /** + * Set title + * + * @access public + * @param string $title + * @return Subscription + */ + public function setTitle($title) + { + $this->title = $title; + return $this; + } + + /** + * Get title + * + * @access public + * @return string + */ + public function getTitle() + { + return $this->title; + } + + /** + * Set feed URL + * + * @access public + * @param string $feedUrl + * @return Subscription + */ + public function setFeedUrl($feedUrl) + { + $this->feedUrl = $feedUrl; + return $this; + } + + /** + * Get feed URL + * + * @access public + * @return string + */ + public function getFeedUrl() + { + return $this->feedUrl; + } + + /** + * Set site URL + * + * @access public + * @param string $siteUrl + * @return Subscription + */ + public function setSiteUrl($siteUrl) + { + $this->siteUrl = $siteUrl; + return $this; + } + + /** + * Get site URL + * + * @access public + * @return string + */ + public function getSiteUrl() + { + return $this->siteUrl; + } + + /** + * Set category + * + * @access public + * @param string $category + * @return Subscription + */ + public function setCategory($category) + { + $this->category = $category; + return $this; + } + + /** + * Get category + * + * @access public + * @return string + */ + public function getCategory() + { + return $this->category; + } + + /** + * Set description + * + * @access public + * @param string $description + * @return Subscription + */ + public function setDescription($description) + { + $this->description = $description; + return $this; + } + + /** + * Get description + * + * @access public + * @return string + */ + public function getDescription() + { + return $this->description; + } + + /** + * Set type + * + * @access public + * @param string $type + * @return Subscription + */ + public function setType($type) + { + $this->type = $type; + return $this; + } + + /** + * Get type + * + * @access public + * @return string + */ + public function getType() + { + return $this->type; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionList.php b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionList.php new file mode 100644 index 00000000..b173f89b --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionList.php @@ -0,0 +1,75 @@ +<?php + +namespace PicoFeed\Serialization; + +/** + * Class SubscriptionList + * + * @package PicoFeed\Serialization + * @author Frederic Guillot + */ +class SubscriptionList +{ + /** + * OPML entries + * + * @var Subscription[] + */ + public $subscriptions = array(); + + /** + * Title + * + * @var string + */ + protected $title = ''; + + /** + * Create object instance + * + * @static + * @access public + * @return SubscriptionList + */ + public static function create() + { + return new static(); + } + + /** + * Set title + * + * @access public + * @param string $title + * @return SubscriptionList + */ + public function setTitle($title) + { + $this->title = $title; + return $this; + } + + /** + * Get title + * + * @access public + * @return string + */ + public function getTitle() + { + return $this->title; + } + + /** + * Add subscription + * + * @access public + * @param Subscription $subscription + * @return SubscriptionList + */ + public function addSubscription(Subscription $subscription) + { + $this->subscriptions[] = $subscription; + return $this; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListBuilder.php b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListBuilder.php new file mode 100644 index 00000000..838e4cb5 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListBuilder.php @@ -0,0 +1,204 @@ +<?php + +namespace PicoFeed\Serialization; + +use DOMDocument; +use DOMElement; + +/** + * Class SubscriptionListBuilder + * + * @package PicoFeed\Serialization + * @author Frederic Guillot + */ +class SubscriptionListBuilder +{ + /** + * @var SubscriptionList + */ + protected $subscriptionList; + + /** + * @var DOMDocument + */ + protected $document; + + /** + * Constructor. + * + * @access public + * @param SubscriptionList $subscriptionList + */ + public function __construct(SubscriptionList $subscriptionList) + { + $this->subscriptionList = $subscriptionList; + } + + /** + * Get object instance + * + * @static + * @access public + * @param SubscriptionList $subscriptionList + * @return SubscriptionListBuilder + */ + public static function create(SubscriptionList $subscriptionList) + { + return new static($subscriptionList); + } + + /** + * Build OPML feed + * + * @access public + * @param string $filename + * @return string + */ + public function build($filename = '') + { + $this->document = new DomDocument('1.0', 'UTF-8'); + $this->document->formatOutput = true; + + $opmlElement = $this->document->createElement('opml'); + $opmlElement->setAttribute('version', '1.0'); + + $headElement = $this->document->createElement('head'); + + if ($this->subscriptionList->getTitle() !== '') { + $titleElement = $this->document->createElement('title'); + $titleElement->appendChild($this->document->createTextNode($this->subscriptionList->getTitle())); + $headElement->appendChild($titleElement); + } + + $opmlElement->appendChild($headElement); + $opmlElement->appendChild($this->buildBody()); + $this->document->appendChild($opmlElement); + + if ($filename !== '') { + $this->document->save($filename); + return ''; + } + + return $this->document->saveXML(); + } + + /** + * Return true if the list has categories + * + * @access public + * @return bool + */ + public function hasCategories() + { + foreach ($this->subscriptionList->subscriptions as $subscription) { + if ($subscription->getCategory() !== '') { + return true; + } + } + + return false; + } + + /** + * Build OPML body + * + * @access protected + * @return DOMElement + */ + protected function buildBody() + { + $bodyElement = $this->document->createElement('body'); + + if ($this->hasCategories()) { + $this->buildCategories($bodyElement); + return $bodyElement; + } + + foreach ($this->subscriptionList->subscriptions as $subscription) { + $bodyElement->appendChild($this->buildSubscription($subscription)); + } + + return $bodyElement; + } + + /** + * Build categories section + * + * @access protected + * @param DOMElement $bodyElement + */ + protected function buildCategories(DOMElement $bodyElement) + { + $categories = $this->groupByCategories(); + + foreach ($categories as $category => $subscriptions) { + $bodyElement->appendChild($this->buildCategory($category, $subscriptions)); + } + } + + /** + * Build category tag + * + * @access protected + * @param string $category + * @param array $subscriptions + * @return DOMElement + */ + protected function buildCategory($category, array $subscriptions) + { + $outlineElement = $this->document->createElement('outline'); + $outlineElement->setAttribute('text', $category); + + foreach ($subscriptions as $subscription) { + $outlineElement->appendChild($this->buildSubscription($subscription)); + } + + return $outlineElement; + } + + /** + * Build subscription entry + * + * @access public + * @param Subscription $subscription + * @return DOMElement + */ + protected function buildSubscription(Subscription $subscription) + { + $outlineElement = $this->document->createElement('outline'); + $outlineElement->setAttribute('type', $subscription->getType() ?: 'rss'); + $outlineElement->setAttribute('text', $subscription->getTitle() ?: $subscription->getFeedUrl()); + $outlineElement->setAttribute('xmlUrl', $subscription->getFeedUrl()); + + if ($subscription->getTitle() !== '') { + $outlineElement->setAttribute('title', $subscription->getTitle()); + } + + if ($subscription->getDescription() !== '') { + $outlineElement->setAttribute('description', $subscription->getDescription()); + } + + if ($subscription->getSiteUrl() !== '') { + $outlineElement->setAttribute('htmlUrl', $subscription->getSiteUrl()); + } + + return $outlineElement; + } + + /** + * Group subscriptions by category + * + * @access private + * @return array + */ + private function groupByCategories() + { + $categories = array(); + + foreach ($this->subscriptionList->subscriptions as $subscription) { + $categories[$subscription->getCategory()][] = $subscription; + } + + return $categories; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListParser.php b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListParser.php new file mode 100644 index 00000000..9085588c --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionListParser.php @@ -0,0 +1,100 @@ +<?php + +namespace PicoFeed\Serialization; + +use PicoFeed\Parser\MalformedXmlException; +use PicoFeed\Parser\XmlParser; +use SimpleXMLElement; + +/** + * Class SubscriptionListParser + * + * @package PicoFeed\Serialization + * @author Frederic Guillot + */ +class SubscriptionListParser +{ + /** + * @var SubscriptionList + */ + protected $subscriptionList; + + /** + * @var string + */ + protected $data; + + /** + * Constructor + * + * @access public + * @param string $data + */ + public function __construct($data) + { + $this->subscriptionList = new SubscriptionList(); + $this->data = trim($data); + } + + /** + * Get object instance + * + * @static + * @access public + * @param string $data + * @return SubscriptionListParser + */ + public static function create($data) + { + return new static($data); + } + + /** + * Parse a subscription list entry + * + * @access public + * @throws MalformedXmlException + * @return SubscriptionList + */ + public function parse() + { + $xml = XmlParser::getSimpleXml($this->data); + + if (! $xml || !isset($xml->head) || !isset($xml->body)) { + throw new MalformedXmlException('Unable to parse OPML file: invalid XML'); + } + + $this->parseTitle($xml->head); + $this->parseEntries($xml->body); + + return $this->subscriptionList; + } + + /** + * Parse title + * + * @access protected + * @param SimpleXMLElement $xml + */ + protected function parseTitle(SimpleXMLElement $xml) + { + $this->subscriptionList->setTitle((string) $xml->title); + } + + /** + * Parse entries + * + * @access protected + * @param SimpleXMLElement $body + */ + private function parseEntries(SimpleXMLElement $body) + { + foreach ($body->outline as $outlineElement) { + if (isset($outlineElement->outline)) { + $this->parseEntries($outlineElement); + } else { + $this->subscriptionList->subscriptions[] = SubscriptionParser::create($body, $outlineElement)->parse(); + } + } + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionParser.php b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionParser.php new file mode 100644 index 00000000..caff07c2 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Serialization/SubscriptionParser.php @@ -0,0 +1,142 @@ +<?php + +namespace PicoFeed\Serialization; + +use SimpleXMLElement; + +/** + * Class SubscriptionParser + * + * @package PicoFeed\Serialization + * @author Frederic Guillot + */ +class SubscriptionParser +{ + /** + * @var Subscription + */ + protected $subscription; + + /** + * @var SimpleXMLElement + */ + private $outlineElement; + + /** + * @var SimpleXMLElement + */ + private $parentElement; + + /** + * Constructor + * + * @access public + * @param SimpleXMLElement $parentElement + * @param SimpleXMLElement $outlineElement + */ + public function __construct(SimpleXMLElement $parentElement, SimpleXMLElement $outlineElement) + { + $this->parentElement = $parentElement; + $this->outlineElement = $outlineElement; + $this->subscription = new Subscription(); + } + + /** + * Get object instance + * + * @static + * @access public + * @param SimpleXMLElement $parentElement + * @param SimpleXMLElement $outlineElement + * @return SubscriptionParser + */ + public static function create(SimpleXMLElement $parentElement, SimpleXMLElement $outlineElement) + { + return new static($parentElement, $outlineElement); + } + + /** + * Parse subscription entry + * + * @access public + * @return Subscription + */ + public function parse() + { + $this->subscription->setCategory($this->findCategory()); + $this->subscription->setTitle($this->findTitle()); + $this->subscription->setFeedUrl($this->findFeedUrl()); + $this->subscription->setSiteUrl($this->findSiteUrl()); + $this->subscription->setType($this->findType()); + $this->subscription->setDescription($this->findDescription()); + + return $this->subscription; + } + + /** + * Find category. + * + * @access protected + * @return string + */ + protected function findCategory() + { + return isset($this->parentElement['text']) ? (string) $this->parentElement['text'] : ''; + } + + /** + * Find title. + * + * @access protected + * @return string + */ + protected function findTitle() + { + return isset($this->outlineElement['title']) ? (string) $this->outlineElement['title'] : (string) $this->outlineElement['text']; + } + + /** + * Find feed url. + * + * @access protected + * @return string + */ + protected function findFeedUrl() + { + return (string) $this->outlineElement['xmlUrl']; + } + + /** + * Find site url. + * + * @access protected + * @return string + */ + protected function findSiteUrl() + { + return isset($this->outlineElement['htmlUrl']) ? (string) $this->outlineElement['htmlUrl'] : $this->findFeedUrl(); + } + + /** + * Find type. + * + * @access protected + * @return string + */ + protected function findType() + { + return isset($this->outlineElement['version']) ? (string) $this->outlineElement['version'] : + isset($this->outlineElement['type']) ? (string) $this->outlineElement['type'] : 'rss'; + } + + /** + * Find description. + * + * @access protected + * @return string + */ + protected function findDescription() + { + return isset($this->outlineElement['description']) ? (string) $this->outlineElement['description'] : $this->findTitle(); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomFeedBuilder.php b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomFeedBuilder.php new file mode 100644 index 00000000..34f37800 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomFeedBuilder.php @@ -0,0 +1,65 @@ +<?php + +namespace PicoFeed\Syndication; + +use DOMAttr; +use DOMElement; + +/** + * Atom Feed Builder + * + * @package PicoFeed\Syndication + * @author Frederic Guillot + */ +class AtomFeedBuilder extends FeedBuilder +{ + /** + * @var DOMElement + */ + protected $feedElement; + + /** + * @var AtomHelper + */ + protected $helper; + + /** + * Build feed + * + * @access public + * @param string $filename + * @return string + */ + public function build($filename = '') + { + $this->helper = new AtomHelper($this->getDocument()); + + $this->feedElement = $this->getDocument()->createElement('feed'); + $this->feedElement->setAttributeNodeNS(new DomAttr('xmlns', 'http://www.w3.org/2005/Atom')); + + $generator = $this->getDocument()->createElement('generator', 'PicoFeed'); + $generator->setAttribute('uri', 'https://github.com/miniflux/picoFeed'); + $this->feedElement->appendChild($generator); + + $this->helper + ->buildTitle($this->feedElement, $this->feedTitle) + ->buildId($this->feedElement, $this->feedUrl) + ->buildDate($this->feedElement, $this->feedDate) + ->buildLink($this->feedElement, $this->siteUrl) + ->buildLink($this->feedElement, $this->feedUrl, 'self', 'application/atom+xml') + ->buildAuthor($this->feedElement, $this->authorName, $this->authorEmail, $this->authorUrl) + ; + + foreach ($this->items as $item) { + $this->feedElement->appendChild($item->build()); + } + + $this->getDocument()->appendChild($this->feedElement); + + if ($filename !== '') { + $this->getDocument()->save($filename); + } + + return $this->getDocument()->saveXML(); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomHelper.php b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomHelper.php new file mode 100644 index 00000000..def6b0b9 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomHelper.php @@ -0,0 +1,139 @@ +<?php + +namespace PicoFeed\Syndication; + +use DateTime; +use DOMDocument; +use DOMElement; + +/** + * Class AtomHelper + * + * @package PicoFeed\Syndication + * @author Frederic Guillot + */ +class AtomHelper +{ + /** + * @var DOMDocument + */ + protected $document; + + /** + * Constructor + * + * @param DOMDocument $document + */ + public function __construct(DOMDocument $document) + { + $this->document = $document; + } + + /** + * Build node + * + * @access public + * @param DOMElement $element + * @param string $tag + * @param string $value + * @return AtomHelper + */ + public function buildNode(DOMElement $element, $tag, $value) + { + $node = $this->document->createElement($tag); + $node->appendChild($this->document->createTextNode($value)); + $element->appendChild($node); + return $this; + } + + /** + * Build title + * + * @access public + * @param DOMElement $element + * @param string $title + * @return AtomHelper + */ + public function buildTitle(DOMElement $element, $title) + { + return $this->buildNode($element, 'title', $title); + } + + /** + * Build id + * + * @access public + * @param DOMElement $element + * @param string $id + * @return AtomHelper + */ + public function buildId(DOMElement $element, $id) + { + return $this->buildNode($element, 'id', $id); + } + + /** + * Build date element + * + * @access public + * @param DOMElement $element + * @param DateTime $date + * @param string $type + * @return AtomHelper + */ + public function buildDate(DOMElement $element, DateTime $date, $type = 'updated') + { + return $this->buildNode($element, $type, $date->format(DateTime::ATOM)); + } + + /** + * Build link element + * + * @access public + * @param DOMElement $element + * @param string $url + * @param string $rel + * @param string $type + * @return AtomHelper + */ + public function buildLink(DOMElement $element, $url, $rel = 'alternate', $type = 'text/html') + { + $node = $this->document->createElement('link'); + $node->setAttribute('rel', $rel); + $node->setAttribute('type', $type); + $node->setAttribute('href', $url); + $element->appendChild($node); + + return $this; + } + + /** + * Build author element + * + * @access public + * @param DOMElement $element + * @param string $authorName + * @param string $authorEmail + * @param string $authorUrl + * @return AtomHelper + */ + public function buildAuthor(DOMElement $element, $authorName, $authorEmail, $authorUrl) + { + if (!empty($authorName)) { + $author = $this->document->createElement('author'); + $this->buildNode($author, 'name', $authorName); + + if (!empty($authorEmail)) { + $this->buildNode($author, 'email', $authorEmail); + } + + if (!empty($authorUrl)) { + $this->buildNode($author, 'uri', $authorUrl); + } + + $element->appendChild($author); + } + + return $this; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomItemBuilder.php b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomItemBuilder.php new file mode 100644 index 00000000..dfdfe68d --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/AtomItemBuilder.php @@ -0,0 +1,63 @@ +<?php + +namespace PicoFeed\Syndication; + +use DOMElement; + +/** + * Atom Item Builder + * + * @package PicoFeed\Syndication + * @author Frederic Guillot + */ +class AtomItemBuilder extends ItemBuilder +{ + /** + * @var DOMElement + */ + protected $itemElement; + + /** + * @var AtomHelper + */ + protected $helper; + + /** + * Build item + * + * @access public + * @return DOMElement + */ + public function build() + { + $this->itemElement = $this->feedBuilder->getDocument()->createElement('entry'); + $this->helper = new AtomHelper($this->feedBuilder->getDocument()); + + if (!empty($this->itemId)) { + $this->helper->buildId($this->itemElement, $this->itemId); + } else { + $this->helper->buildId($this->itemElement, $this->itemUrl); + } + + $this->helper + ->buildTitle($this->itemElement, $this->itemTitle) + ->buildLink($this->itemElement, $this->itemUrl) + ->buildDate($this->itemElement, $this->itemUpdatedDate, 'updated') + ->buildDate($this->itemElement, $this->itemPublishedDate, 'published') + ->buildAuthor($this->itemElement, $this->authorName, $this->authorEmail, $this->authorUrl) + ; + + if (!empty($this->itemSummary)) { + $this->helper->buildNode($this->itemElement, 'summary', $this->itemSummary); + } + + if (!empty($this->itemContent)) { + $node = $this->feedBuilder->getDocument()->createElement('content'); + $node->setAttribute('type', 'html'); + $node->appendChild($this->feedBuilder->getDocument()->createCDATASection($this->itemContent)); + $this->itemElement->appendChild($node); + } + + return $this->itemElement; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/FeedBuilder.php b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/FeedBuilder.php new file mode 100644 index 00000000..cf9d024e --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/FeedBuilder.php @@ -0,0 +1,185 @@ +<?php + +namespace PicoFeed\Syndication; + +use DateTime; +use DOMDocument; + +/** + * Class FeedBuilder + * + * @package PicoFeed\Syndication + * @author Frederic Guillot + */ +abstract class FeedBuilder +{ + /** + * @var DOMDocument + */ + protected $document; + + /** + * @var string + */ + protected $feedTitle; + + /** + * @var string + */ + protected $feedUrl; + + /** + * @var string + */ + protected $siteUrl; + + /** + * @var string + */ + protected $authorName; + + /** + * @var string + */ + protected $authorEmail; + + /** + * @var string + */ + protected $authorUrl; + + /** + * @var DateTime + */ + protected $feedDate; + + /** + * @var ItemBuilder[] + */ + protected $items = array(); + + /** + * Constructor + * + * @access public + */ + public function __construct() + { + $this->document = new DomDocument('1.0', 'UTF-8'); + $this->document->formatOutput = true; + } + + /** + * Get new object instance + * + * @access public + * @return static + */ + public static function create() + { + return new static(); + } + + /** + * Add feed title + * + * @access public + * @param string $title + * @return $this + */ + public function withTitle($title) + { + $this->feedTitle = $title; + return $this; + } + + /** + * Add feed url + * + * @access public + * @param string $url + * @return $this + */ + public function withFeedUrl($url) + { + $this->feedUrl = $url; + return $this; + } + + /** + * Add website url + * + * @access public + * @param string $url + * @return $this + */ + public function withSiteUrl($url) + { + $this->siteUrl = $url; + return $this; + } + + /** + * Add feed date + * + * @access public + * @param DateTime $date + * @return $this + */ + public function withDate(DateTime $date) + { + $this->feedDate = $date; + return $this; + } + + /** + * Add feed author + * + * @access public + * @param string $name + * @param string $email + * @param string $url + * @return $this + */ + public function withAuthor($name, $email = '', $url ='') + { + $this->authorName = $name; + $this->authorEmail = $email; + $this->authorUrl = $url; + return $this; + } + + /** + * Add feed item + * + * @access public + * @param ItemBuilder $item + * @return $this + */ + public function withItem(ItemBuilder $item) + { + $this->items[] = $item; + return $this; + } + + /** + * Get DOM document + * + * @access public + * @return DOMDocument + */ + public function getDocument() + { + return $this->document; + } + + /** + * Build feed + * + * @abstract + * @access public + * @param string $filename + * @return string + */ + abstract public function build($filename = ''); +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/ItemBuilder.php b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/ItemBuilder.php new file mode 100644 index 00000000..86985bc7 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/ItemBuilder.php @@ -0,0 +1,209 @@ +<?php + +namespace PicoFeed\Syndication; + +use DateTime; +use DOMElement; + +/** + * Class ItemBuilder + * + * @package PicoFeed\Syndication + * @author Frederic Guillot + */ +abstract class ItemBuilder +{ + /** + * @var string + */ + protected $itemTitle; + + /** + * @var string + */ + protected $itemId; + + /** + * @var string + */ + protected $itemSummary; + + /** + * @var string + */ + protected $authorName; + + /** + * @var string + */ + protected $authorEmail; + + /** + * @var string + */ + protected $authorUrl; + + /** + * @var DateTime + */ + protected $itemPublishedDate; + + /** + * @var DateTime + */ + protected $itemUpdatedDate; + + /** + * @var string + */ + protected $itemContent; + + /** + * @var string + */ + protected $itemUrl; + + /** + * @var FeedBuilder + */ + protected $feedBuilder; + + /** + * Constructor + * + * @param FeedBuilder $feedBuilder + */ + public function __construct(FeedBuilder $feedBuilder) + { + $this->feedBuilder = $feedBuilder; + } + + /** + * Get new object instance + * + * @access public + * @param FeedBuilder $feedBuilder + * @return static + */ + public static function create(FeedBuilder $feedBuilder) + { + return new static($feedBuilder); + } + + /** + * Add item title + * + * @access public + * @param string $title + * @return $this + */ + public function withTitle($title) + { + $this->itemTitle = $title; + return $this; + } + + /** + * Add item id + * + * @access public + * @param string $id + * @return $this + */ + public function withId($id) + { + $this->itemId = $id; + return $this; + } + + /** + * Add item url + * + * @access public + * @param string $url + * @return $this + */ + public function withUrl($url) + { + $this->itemUrl = $url; + return $this; + } + + /** + * Add item summary + * + * @access public + * @param string $summary + * @return $this + */ + public function withSummary($summary) + { + $this->itemSummary = $summary; + return $this; + } + + /** + * Add item content + * + * @access public + * @param string $content + * @return $this + */ + public function withContent($content) + { + $this->itemContent = $content; + return $this; + } + + /** + * Add item updated date + * + * @access public + * @param DateTime $date + * @return $this + */ + public function withUpdatedDate(DateTime $date) + { + $this->itemUpdatedDate = $date; + return $this; + } + + /** + * Add item published date + * + * @access public + * @param DateTime $date + * @return $this + */ + public function withPublishedDate(DateTime $date) + { + $this->itemPublishedDate = $date; + return $this; + } + + /** + * Add item author + * + * @access public + * @param string $name + * @param string $email + * @param string $url + * @return $this + */ + public function withAuthor($name, $email = '', $url ='') + { + $this->authorName = $name; + $this->authorEmail = $email; + $this->authorUrl = $url; + return $this; + } + + /** + * Build item + * + * @abstract + * @access public + * @return DOMElement + */ + abstract public function build(); +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20FeedBuilder.php b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20FeedBuilder.php new file mode 100644 index 00000000..bc3f5135 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20FeedBuilder.php @@ -0,0 +1,76 @@ +<?php + +namespace PicoFeed\Syndication; + +use DOMAttr; +use DOMElement; + +/** + * Rss20 Feed Builder + * + * @package PicoFeed\Syndication + * @author Frederic Guillot + */ +class Rss20FeedBuilder extends FeedBuilder +{ + /** + * @var DOMElement + */ + protected $rssElement; + + /** + * @var Rss20Helper + */ + protected $helper; + + /** + * @var DOMElement + */ + protected $channelElement; + + /** + * Build feed + * + * @access public + * @param string $filename + * @return string + */ + public function build($filename = '') + { + $this->helper = new Rss20Helper($this->getDocument()); + + $this->rssElement = $this->getDocument()->createElement('rss'); + $this->rssElement->setAttribute('version', '2.0'); + $this->rssElement->setAttributeNodeNS(new DomAttr('xmlns:content', 'http://purl.org/rss/1.0/modules/content/')); + $this->rssElement->setAttributeNodeNS(new DomAttr('xmlns:atom', 'http://www.w3.org/2005/Atom')); + + $this->channelElement = $this->getDocument()->createElement('channel'); + $this->helper + ->buildNode($this->channelElement, 'generator', 'PicoFeed (https://github.com/miniflux/picoFeed)') + ->buildTitle($this->channelElement, $this->feedTitle) + ->buildNode($this->channelElement, 'description', $this->feedTitle) + ->buildDate($this->channelElement, $this->feedDate) + ->buildAuthor($this->channelElement, 'webMaster', $this->authorName, $this->authorEmail) + ->buildLink($this->channelElement, $this->siteUrl) + ; + + $link = $this->getDocument()->createElement('atom:link'); + $link->setAttribute('href', $this->feedUrl); + $link->setAttribute('rel', 'self'); + $link->setAttribute('type', 'application/rss+xml'); + $this->channelElement->appendChild($link); + + foreach ($this->items as $item) { + $this->channelElement->appendChild($item->build()); + } + + $this->rssElement->appendChild($this->channelElement); + $this->getDocument()->appendChild($this->rssElement); + + if ($filename !== '') { + $this->getDocument()->save($filename); + } + + return $this->getDocument()->saveXML(); + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20Helper.php b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20Helper.php new file mode 100644 index 00000000..72a19e56 --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20Helper.php @@ -0,0 +1,115 @@ +<?php + +namespace PicoFeed\Syndication; + +use DateTime; +use DOMDocument; +use DOMElement; + +/** + * Class Rss20Helper + * + * @package PicoFeed\Syndication + * @author Frederic Guillot + */ +class Rss20Helper +{ + /** + * @var DOMDocument + */ + protected $document; + + /** + * Constructor + * + * @param DOMDocument $document + */ + public function __construct(DOMDocument $document) + { + $this->document = $document; + } + + /** + * Build node + * + * @access public + * @param DOMElement $element + * @param string $tag + * @param string $value + * @return $this + */ + public function buildNode(DOMElement $element, $tag, $value) + { + $node = $this->document->createElement($tag); + $node->appendChild($this->document->createTextNode($value)); + $element->appendChild($node); + return $this; + } + + /** + * Build title + * + * @access public + * @param DOMElement $element + * @param string $title + * @return $this + */ + public function buildTitle(DOMElement $element, $title) + { + return $this->buildNode($element, 'title', $title); + } + + /** + * Build date element + * + * @access public + * @param DOMElement $element + * @param DateTime $date + * @param string $type + * @return $this + */ + public function buildDate(DOMElement $element, DateTime $date, $type = 'pubDate') + { + return $this->buildNode($element, $type, $date->format(DateTime::RSS)); + } + + /** + * Build link element + * + * @access public + * @param DOMElement $element + * @param string $url + * @return $this + */ + public function buildLink(DOMElement $element, $url) + { + return $this->buildNode($element, 'link', $url); + } + + /** + * Build author element + * + * @access public + * @param DOMElement $element + * @param string $tag + * @param string $authorName + * @param string $authorEmail + * @return $this + */ + public function buildAuthor(DOMElement $element, $tag, $authorName, $authorEmail) + { + if (!empty($authorName)) { + $value = ''; + + if (!empty($authorEmail)) { + $value .= $authorEmail.' ('.$authorName.')'; + } else { + $value = $authorName; + } + + $this->buildNode($element, $tag, $value); + } + + return $this; + } +} diff --git a/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20ItemBuilder.php b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20ItemBuilder.php new file mode 100644 index 00000000..125dc6ac --- /dev/null +++ b/vendor/miniflux/picofeed/lib/PicoFeed/Syndication/Rss20ItemBuilder.php @@ -0,0 +1,67 @@ +<?php + +namespace PicoFeed\Syndication; + +use DOMElement; + +/** + * Rss20 Item Builder + * + * @package PicoFeed\Syndication + * @author Frederic Guillot + */ +class Rss20ItemBuilder extends ItemBuilder +{ + /** + * @var DOMElement + */ + protected $itemElement; + + /** + * @var Rss20Helper + */ + protected $helper; + + /** + * Build item + * + * @access public + * @return DOMElement + */ + public function build() + { + $this->itemElement = $this->feedBuilder->getDocument()->createElement('item'); + $this->helper = new Rss20Helper($this->feedBuilder->getDocument()); + + if (!empty($this->itemId)) { + $guid = $this->feedBuilder->getDocument()->createElement('guid'); + $guid->setAttribute('isPermaLink', 'false'); + $guid->appendChild($this->feedBuilder->getDocument()->createTextNode($this->itemId)); + $this->itemElement->appendChild($guid); + } else { + $guid = $this->feedBuilder->getDocument()->createElement('guid'); + $guid->setAttribute('isPermaLink', 'true'); + $guid->appendChild($this->feedBuilder->getDocument()->createTextNode($this->itemUrl)); + $this->itemElement->appendChild($guid); + } + + $this->helper + ->buildTitle($this->itemElement, $this->itemTitle) + ->buildLink($this->itemElement, $this->itemUrl) + ->buildDate($this->itemElement, $this->itemPublishedDate) + ->buildAuthor($this->itemElement, 'author', $this->authorName, $this->authorEmail) + ; + + if (!empty($this->itemSummary)) { + $this->helper->buildNode($this->itemElement, 'description', $this->itemSummary); + } + + if (!empty($this->itemContent)) { + $node = $this->feedBuilder->getDocument()->createElement('content:encoded'); + $node->appendChild($this->feedBuilder->getDocument()->createCDATASection($this->itemContent)); + $this->itemElement->appendChild($node); + } + + return $this->itemElement; + } +} diff --git a/vendor/miniflux/picofeed/picofeed b/vendor/miniflux/picofeed/picofeed new file mode 100755 index 00000000..8f35737a --- /dev/null +++ b/vendor/miniflux/picofeed/picofeed @@ -0,0 +1,135 @@ +#!/usr/bin/env php +<?php + +require_once 'vendor/autoload.php'; + +use PicoFeed\Config\Config; +use PicoFeed\Reader\Favicon; +use PicoFeed\Scraper\Scraper; +use PicoFeed\Reader\Reader; +use PicoFeed\Logging\Logger; +use PicoFeed\PicoFeedException; + +Logger::enable(); + +function get_feed($url, $disable_filtering = false) +{ + try { + + $reader = new Reader; + $resource = $reader->discover($url); + + $parser = $reader->getParser( + $resource->getUrl(), + $resource->getContent(), + $resource->getEncoding() + ); + + if ($disable_filtering) { + $parser->disableContentFiltering(); + } + + return $parser->execute(); + } + catch (PicoFeedException $e) { + echo 'Exception thrown ===> "'.$e->getMessage().'"'.PHP_EOL; + return false; + } +} + +function get_item($feed, $item_id) +{ + foreach ($feed->items as $item) { + if ($item->getId() === $item_id) { + echo $item; + echo "============= CONTENT ================\n"; + echo $item->getContent(); + echo "\n============= CONTENT ================\n"; + break; + } + } +} + +function dump_feed($url) +{ + $feed = get_feed($url); + echo $feed; +} + +function debug_feed($url) +{ + get_feed($url); + print_r(Logger::getMessages()); +} + +function dump_item($url, $item_id) +{ + $feed = get_feed($url); + + if ($feed !== false) { + get_item($feed, $item_id); + } +} + +function nofilter_item($url, $item_id) +{ + $feed = get_feed($url, true); + + if ($feed !== false) { + get_item($feed, $item_id); + } +} + +function grabber($url) +{ + $grabber = new Scraper(new Config); + $grabber->setUrl($url); + $grabber->execute(); + + print_r(Logger::getMessages()); + echo "============= CONTENT ================\n"; + echo $grabber->getRelevantContent().PHP_EOL; + echo "============= FILTERED ================\n"; + echo $grabber->getFilteredContent().PHP_EOL; +} + +function fetch_favicon($url) +{ + $favicon = new Favicon(); + echo $favicon->find($url) . PHP_EOL; +} + +// Parse command line arguments +if ($argc === 4) { + switch ($argv[1]) { + case 'item': + dump_item($argv[2], $argv[3]); + die; + case 'nofilter': + nofilter_item($argv[2], $argv[3]); + die; + } +} else if ($argc === 3) { + switch ($argv[1]) { + case 'feed': + dump_feed($argv[2]); + die; + case 'debug': + debug_feed($argv[2]); + die; + case 'grabber': + grabber($argv[2]); + die; + case 'favicon': + fetch_favicon($argv[2]); + die; + } +} + +printf("Usage:\n"); +printf("%s feed <feed-url>\n", $argv[0]); +printf("%s debug <feed-url>\n", $argv[0]); +printf("%s item <feed-url> <item-id>\n", $argv[0]); +printf("%s nofilter <feed-url> <item-id>\n", $argv[0]); +printf("%s grabber <url>\n", $argv[0]); +printf("%s favicon <url>\n", $argv[0]); |