зеркало из https://github.com/nextcloud/news.git
update picofeed
This commit is contained in:
Родитель
790f0a0a70
Коммит
ea4ecf501f
|
@ -57,12 +57,12 @@
|
|||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/fguillot/picoFeed.git",
|
||||
"reference": "aa83e0c66525251cb6c6acab3babbc9e1879527b"
|
||||
"reference": "035eed9d5d5b32441f44d59a9a75ea7e1c07a3aa"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/fguillot/picoFeed/zipball/aa83e0c66525251cb6c6acab3babbc9e1879527b",
|
||||
"reference": "aa83e0c66525251cb6c6acab3babbc9e1879527b",
|
||||
"url": "https://api.github.com/repos/fguillot/picoFeed/zipball/035eed9d5d5b32441f44d59a9a75ea7e1c07a3aa",
|
||||
"reference": "035eed9d5d5b32441f44d59a9a75ea7e1c07a3aa",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -91,7 +91,7 @@
|
|||
],
|
||||
"description": "Modern library to write or read feeds (RSS/Atom)",
|
||||
"homepage": "http://fguillot.github.io/picoFeed",
|
||||
"time": "2015-02-24 03:00:15"
|
||||
"time": "2015-02-27 02:45:04"
|
||||
},
|
||||
{
|
||||
"name": "pear/net_url2",
|
||||
|
|
|
@ -149,7 +149,7 @@ class FeedFetcher implements IFeedFetcher {
|
|||
$item->setUrl($parsedItem->getUrl());
|
||||
$item->setGuid($parsedItem->getId());
|
||||
$item->setGuidHash($item->getGuid());
|
||||
$item->setPubDate($parsedItem->getDate());
|
||||
$item->setPubDate($parsedItem->getDate()->getTimestamp());
|
||||
$item->setLastModified($this->time->getTime());
|
||||
|
||||
// unescape content because angularjs helps against XSS
|
||||
|
|
|
@ -203,7 +203,10 @@ class FeedFetcherTest extends \PHPUnit_Framework_TestCase {
|
|||
|
||||
$item = new Item();
|
||||
|
||||
$this->expectItem('getDate', $this->pub);
|
||||
date_default_timezone_set('America/Los_Angeles');
|
||||
$date = new \DateTime();
|
||||
$date->setTimestamp($this->pub);
|
||||
$this->expectItem('getDate', $date);
|
||||
$item->setPubDate($this->pub);
|
||||
|
||||
$item->setStatus(0);
|
||||
|
|
|
@ -4,4 +4,4 @@
|
|||
|
||||
require_once __DIR__ . '/composer' . '/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInitfee7a50d745041658cc0946610dc5951::getLoader();
|
||||
return ComposerAutoloaderInit878b2c9b24ed043c2ed72a45ff7ae283::getLoader();
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
// autoload_real.php @generated by Composer
|
||||
|
||||
class ComposerAutoloaderInitfee7a50d745041658cc0946610dc5951
|
||||
class ComposerAutoloaderInit878b2c9b24ed043c2ed72a45ff7ae283
|
||||
{
|
||||
private static $loader;
|
||||
|
||||
|
@ -19,9 +19,9 @@ class ComposerAutoloaderInitfee7a50d745041658cc0946610dc5951
|
|||
return self::$loader;
|
||||
}
|
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInitfee7a50d745041658cc0946610dc5951', 'loadClassLoader'), true, true);
|
||||
spl_autoload_register(array('ComposerAutoloaderInit878b2c9b24ed043c2ed72a45ff7ae283', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInitfee7a50d745041658cc0946610dc5951', 'loadClassLoader'));
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInit878b2c9b24ed043c2ed72a45ff7ae283', 'loadClassLoader'));
|
||||
|
||||
$includePaths = require __DIR__ . '/include_paths.php';
|
||||
array_push($includePaths, get_include_path());
|
||||
|
@ -46,14 +46,14 @@ class ComposerAutoloaderInitfee7a50d745041658cc0946610dc5951
|
|||
|
||||
$includeFiles = require __DIR__ . '/autoload_files.php';
|
||||
foreach ($includeFiles as $file) {
|
||||
composerRequirefee7a50d745041658cc0946610dc5951($file);
|
||||
composerRequire878b2c9b24ed043c2ed72a45ff7ae283($file);
|
||||
}
|
||||
|
||||
return $loader;
|
||||
}
|
||||
}
|
||||
|
||||
function composerRequirefee7a50d745041658cc0946610dc5951($file)
|
||||
function composerRequire878b2c9b24ed043c2ed72a45ff7ae283($file)
|
||||
{
|
||||
require $file;
|
||||
}
|
||||
|
|
|
@ -119,12 +119,12 @@
|
|||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/fguillot/picoFeed.git",
|
||||
"reference": "aa83e0c66525251cb6c6acab3babbc9e1879527b"
|
||||
"reference": "035eed9d5d5b32441f44d59a9a75ea7e1c07a3aa"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/fguillot/picoFeed/zipball/aa83e0c66525251cb6c6acab3babbc9e1879527b",
|
||||
"reference": "aa83e0c66525251cb6c6acab3babbc9e1879527b",
|
||||
"url": "https://api.github.com/repos/fguillot/picoFeed/zipball/035eed9d5d5b32441f44d59a9a75ea7e1c07a3aa",
|
||||
"reference": "035eed9d5d5b32441f44d59a9a75ea7e1c07a3aa",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -135,7 +135,7 @@
|
|||
"ext-xml": "*",
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"time": "2015-02-24 03:00:15",
|
||||
"time": "2015-02-27 02:45:04",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
|
|
|
@ -45,23 +45,23 @@ Feed::id = tag:linuxfr.org,2005:/news
|
|||
Feed::title = LinuxFr.org : les dépêches
|
||||
Feed::feed_url = http://linuxfr.org/news.atom
|
||||
Feed::site_url = http://linuxfr.org/news
|
||||
Feed::date = 1415138079
|
||||
Feed::language = en-US
|
||||
Feed::description =
|
||||
Feed::logo =
|
||||
Feed::items = 15 items
|
||||
Feed::date = Thu, 26 Feb 15 09:33:08 +0100
|
||||
Feed::isRTL() = false
|
||||
Feed::items = 15 items
|
||||
----
|
||||
Item::id = 38d8f48284fb03940cbb3aff9101089b81e44efb1281641bdd7c3e7e4bf3b0cd
|
||||
Item::title = openSUSE 13.2 : nouvelle version du caméléon disponible !
|
||||
Item::url = http://linuxfr.org/news/opensuse-13-2-nouvelle-version-du-cameleon-disponible
|
||||
Item::date = 1415122640
|
||||
Item::id = 56198c98ae852d21c369bfb5ffbc2ad13db2f3227236dde3e21ca1a9eb943faf
|
||||
Item::title = Les brevets logiciels : un frein à l'innovation et la recherche (un nouvel exemple aux États-Unis)
|
||||
Item::url = http://linuxfr.org/news/les-brevets-logiciels-un-frein-a-l-innovation-et-la-recherche-un-nouvel-exemple-aux-etats-unis
|
||||
Item::language = en-US
|
||||
Item::author = Syvolc
|
||||
Item::author = alenvers
|
||||
Item::enclosure_url =
|
||||
Item::enclosure_type =
|
||||
Item::date = Thu, 26 Feb 15 09:33:08 +0100
|
||||
Item::isRTL() = false
|
||||
Item::content = 18307 bytes
|
||||
Item::content = 6452 bytes
|
||||
....
|
||||
```
|
||||
|
||||
|
@ -186,7 +186,7 @@ $feed->getId(); // Unique feed id
|
|||
$feed->getTitle(); // Feed title
|
||||
$feed->getFeedUrl(); // Feed url
|
||||
$feed->getSiteUrl(); // Website url
|
||||
$feed->getDate(); // Feed last updated date
|
||||
$feed->getDate(); // Feed last updated date (DateTime object)
|
||||
$feed->getLanguage(); // Feed language
|
||||
$feed->getDescription(); // Feed description
|
||||
$feed->getLogo(); // Feed logo (can be a large image, different from icon)
|
||||
|
@ -196,7 +196,7 @@ $feed->getItems(); // List of item objects
|
|||
$feed->items[0]->getId(); // Item unique id (hash)
|
||||
$feed->items[0]->getTitle(); // Item title
|
||||
$feed->items[0]->getUrl(); // Item url
|
||||
$feed->items[0]->getDate(); // Item published date (timestamp)
|
||||
$feed->items[0]->getDate(); // Item published date (DateTime object)
|
||||
$feed->items[0]->getLanguage(); // Item language
|
||||
$feed->items[0]->getAuthor(); // Item author
|
||||
$feed->items[0]->getEnclosureUrl(); // Enclosure url
|
||||
|
|
|
@ -6,7 +6,8 @@ Versions
|
|||
|
||||
- Development version: master
|
||||
- Available versions:
|
||||
- v0.1.1 (stable)
|
||||
- v0.1.2 (stable)
|
||||
- v0.1.1
|
||||
- v0.1.0
|
||||
- v0.0.2
|
||||
- v0.0.1
|
||||
|
@ -21,7 +22,7 @@ Configure your `composer.json`:
|
|||
```json
|
||||
{
|
||||
"require": {
|
||||
"fguillot/picofeed": "0.1.1"
|
||||
"fguillot/picofeed": "0.1.2"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -29,7 +30,7 @@ Configure your `composer.json`:
|
|||
Or simply:
|
||||
|
||||
```bash
|
||||
composer require fguillot/picofeed:0.1.1
|
||||
composer require fguillot/picofeed:0.1.2
|
||||
```
|
||||
|
||||
And download the code:
|
||||
|
|
|
@ -123,8 +123,6 @@ class Grabber
|
|||
* @var array
|
||||
*/
|
||||
private $stripTags = array(
|
||||
'script',
|
||||
'style',
|
||||
'nav',
|
||||
'header',
|
||||
'footer',
|
||||
|
@ -276,11 +274,11 @@ class Grabber
|
|||
}
|
||||
|
||||
if ($this->html) {
|
||||
$html_encoding = XmlParser::getEncodingFromMetaTag($this->html);
|
||||
|
||||
Logger::setMessage(get_called_class().': Fix encoding');
|
||||
Logger::setMessage(get_called_class().': HTTP Encoding "'.$this->encoding.'"');
|
||||
|
||||
$this->html = Encoding::convert($this->html, $this->encoding);
|
||||
// Encode everything in UTF-8
|
||||
Logger::setMessage(get_called_class().': HTTP Encoding "'.$this->encoding.'" ; HTML Encoding "'.$html_encoding.'"');
|
||||
$this->html = Encoding::convert($this->html, $html_encoding ?: $this->encoding);
|
||||
$this->html = Filter::stripHeadTags($this->html);
|
||||
|
||||
Logger::setMessage(get_called_class().': Content length: '.strlen($this->html).' bytes');
|
||||
|
|
|
@ -158,6 +158,11 @@ class Tag
|
|||
public function removeBlacklistedTags($data)
|
||||
{
|
||||
$dom = XmlParser::getDomDocument($data);
|
||||
|
||||
if ($dom === false) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$xpath = new DOMXpath($dom);
|
||||
|
||||
$nodes = $xpath->query(implode(' | ', $this->tag_blacklist));
|
||||
|
|
|
@ -131,7 +131,7 @@ class Atom extends Parser
|
|||
*/
|
||||
public function findFeedDate(SimpleXMLElement $xml, Feed $feed)
|
||||
{
|
||||
$feed->date = $this->date->getTimestamp((string) $xml->updated);
|
||||
$feed->date = $this->date->getDateTime((string) $xml->updated);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -143,10 +143,15 @@ class Atom extends Parser
|
|||
*/
|
||||
public function findItemDate(SimpleXMLElement $entry, Item $item)
|
||||
{
|
||||
$published = isset($entry->published) ? $this->date->getTimestamp((string) $entry->published) : 0;
|
||||
$updated = isset($entry->updated) ? $this->date->getTimestamp((string) $entry->updated) : 0;
|
||||
$published = isset($entry->published) ? $this->date->getDateTime((string) $entry->published) : null;
|
||||
$updated = isset($entry->updated) ? $this->date->getDateTime((string) $entry->updated) : null;
|
||||
|
||||
$item->date = max($published, $updated) ?: time();
|
||||
if ($published !== null && $updated !== null) {
|
||||
$item->date = max($published, $updated);
|
||||
}
|
||||
else {
|
||||
$item->date = $updated ?: $published;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -21,7 +21,7 @@ class DateParser
|
|||
*/
|
||||
public $timezone = 'UTC';
|
||||
|
||||
/**
|
||||
/**
|
||||
* Supported formats [ 'format' => length ]
|
||||
*
|
||||
* @access public
|
||||
|
@ -60,9 +60,9 @@ class DateParser
|
|||
*
|
||||
* @access public
|
||||
* @param string $value Original date format
|
||||
* @return integer Timestamp
|
||||
* @return DateTime
|
||||
*/
|
||||
public function getTimestamp($value)
|
||||
public function getDateTime($value)
|
||||
{
|
||||
$value = trim($value);
|
||||
|
||||
|
@ -73,14 +73,13 @@ class DateParser
|
|||
$truncated_value = substr($truncated_value, 0, $length);
|
||||
}
|
||||
|
||||
$timestamp = $this->getValidDate($format, $truncated_value);
|
||||
if ($timestamp > 0) {
|
||||
return $timestamp;
|
||||
$date = $this->getValidDate($format, $truncated_value);
|
||||
if ($date !== false) {
|
||||
return $date;
|
||||
}
|
||||
}
|
||||
|
||||
$date = new DateTime('now', new DateTimeZone($this->timezone));
|
||||
return $date->getTimestamp();
|
||||
return $this->getCurrentDateTime();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -89,7 +88,7 @@ class DateParser
|
|||
* @access public
|
||||
* @param string $format Date format
|
||||
* @param string $value Original date value
|
||||
* @return integer Timestamp
|
||||
* @return DateTime
|
||||
*/
|
||||
public function getValidDate($format, $value)
|
||||
{
|
||||
|
@ -100,10 +99,21 @@ class DateParser
|
|||
$errors = DateTime::getLastErrors();
|
||||
|
||||
if ($errors['error_count'] === 0 && $errors['warning_count'] === 0) {
|
||||
return $date->getTimestamp();
|
||||
return $date;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current datetime
|
||||
*
|
||||
* @access public
|
||||
* @return DateTime
|
||||
*/
|
||||
public function getCurrentDateTime()
|
||||
{
|
||||
return new DateTime('now', new DateTimeZone($this->timezone));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,9 +62,9 @@ class Feed
|
|||
* Feed date
|
||||
*
|
||||
* @access public
|
||||
* @var integer
|
||||
* @var \DateTime
|
||||
*/
|
||||
public $date = 0;
|
||||
public $date = null;
|
||||
|
||||
/**
|
||||
* Feed language
|
||||
|
@ -100,10 +100,11 @@ class Feed
|
|||
{
|
||||
$output = '';
|
||||
|
||||
foreach (array('id', 'title', 'feed_url', 'site_url', 'date', 'language', 'description', 'logo') as $property) {
|
||||
foreach (array('id', 'title', 'feed_url', 'site_url', '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;
|
||||
|
||||
|
|
|
@ -63,9 +63,9 @@ class Item
|
|||
* Item date
|
||||
*
|
||||
* @access public
|
||||
* @var integer
|
||||
* @var \DateTime
|
||||
*/
|
||||
public $date = 0;
|
||||
public $date = null;
|
||||
|
||||
/**
|
||||
* Item content
|
||||
|
@ -109,10 +109,11 @@ class Item
|
|||
{
|
||||
$output = '';
|
||||
|
||||
foreach (array('id', 'title', 'url', 'date', 'language', 'author', 'enclosure_url', 'enclosure_type') as $property) {
|
||||
foreach (array('id', 'title', 'url', 'language', 'author', 'enclosure_url', 'enclosure_type') as $property) {
|
||||
$output .= 'Item::'.$property.' = '.$this->$property.PHP_EOL;
|
||||
}
|
||||
|
||||
$output .= 'Item::date = '.$this->date->format(DATE_RFC822).PHP_EOL;
|
||||
$output .= 'Item::isRTL() = '.($this->isRTL() ? 'true' : 'false').PHP_EOL;
|
||||
$output .= 'Item::content = '.strlen($this->content).' bytes'.PHP_EOL;
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ class Rss10 extends Rss20
|
|||
*/
|
||||
public function findFeedDate(SimpleXMLElement $xml, Feed $feed)
|
||||
{
|
||||
$feed->date = $this->date->getTimestamp(XmlParser::getNamespaceValue($xml->channel, $this->namespaces, 'date'));
|
||||
$feed->date = $this->date->getDateTime(XmlParser::getNamespaceValue($xml->channel, $this->namespaces, 'date'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -134,7 +134,7 @@ class Rss20 extends Parser
|
|||
public function findFeedDate(SimpleXMLElement $xml, Feed $feed)
|
||||
{
|
||||
$date = isset($xml->channel->pubDate) ? $xml->channel->pubDate : $xml->channel->lastBuildDate;
|
||||
$feed->date = $this->date->getTimestamp((string) $date);
|
||||
$feed->date = $this->date->getDateTime((string) $date);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -156,7 +156,7 @@ class Rss20 extends Parser
|
|||
$date = (string) $entry->pubDate;
|
||||
}
|
||||
|
||||
$item->date = $this->date->getTimestamp($date);
|
||||
$item->date = $this->date->getDateTime($date);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -90,10 +90,14 @@ class XmlParser
|
|||
* @static
|
||||
* @access public
|
||||
* @param string $input XML content
|
||||
* @return \DOMNode
|
||||
* @return \DOMNDocument
|
||||
*/
|
||||
public static function getDomDocument($input)
|
||||
{
|
||||
if (empty($input)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$dom = self::scanInput($input, function ($in) {
|
||||
$dom = new DomDocument;
|
||||
$dom->loadXml($in, LIBXML_NONET);
|
||||
|
@ -207,6 +211,49 @@ class XmlParser
|
|||
return $encoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract charset from meta tag
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @param string $data meta tag content
|
||||
* @return string
|
||||
*/
|
||||
public static function findCharset($data)
|
||||
{
|
||||
$result = explode('charset=', $data);
|
||||
return isset($result[1]) ? $result[1] : $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the encoding from a xml tag
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @param string $data Input data
|
||||
* @return string
|
||||
*/
|
||||
public static function getEncodingFromMetaTag($data)
|
||||
{
|
||||
$encoding = '';
|
||||
|
||||
$dom = static::getHtmlDocument($data);
|
||||
$xpath = new DOMXPath($dom);
|
||||
|
||||
$tags = array(
|
||||
'/html/head/meta[translate(@http-equiv, "CENOPTY", "cenopty")="content-type"]/@content', //HTML4, convert upper to lower-case
|
||||
'/html/head/meta/@charset', //HTML5
|
||||
);
|
||||
|
||||
$nodes = $xpath->query(implode(' | ', $tags));
|
||||
|
||||
foreach ($nodes as $node) {
|
||||
$encoding = static::findCharset($node->nodeValue);
|
||||
}
|
||||
|
||||
return $encoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get xml:lang value
|
||||
*
|
||||
|
|
|
@ -5,8 +5,6 @@ return array(
|
|||
'//div[@class="entry"]',
|
||||
),
|
||||
'strip' => array(
|
||||
'//script',
|
||||
'//style',
|
||||
'//*[@id="linker_widget"]',
|
||||
'//*[contains(@class, "bio")]',
|
||||
'//*[contains(@class, "entry-footer")]',
|
||||
|
|
|
@ -5,7 +5,6 @@ return array(
|
|||
'//div[@class="article_ventre_box"]',
|
||||
),
|
||||
'strip' => array(
|
||||
'//script',
|
||||
'//link',
|
||||
'//*[contains(@class, "article_navigation")]',
|
||||
'//h1',
|
||||
|
|
|
@ -3,8 +3,5 @@ return array(
|
|||
'test_url' => 'http://www./2014/05/20/le-playstation-now-arrive-en-beta-fermee-aux-etats-unis/',
|
||||
'body' => array(
|
||||
'//div[@class="post-content"]',
|
||||
),
|
||||
'strip' => array(
|
||||
'//style'
|
||||
)
|
||||
);
|
|
@ -5,7 +5,6 @@ return array(
|
|||
'//div[@class="entry"]'
|
||||
),
|
||||
'strip' => array(
|
||||
'//script',
|
||||
'//*[contains(@class, "addthis_toolbox")]',
|
||||
'//*[contains(@class, "addthis_default_style")]',
|
||||
'//*[@class="navigation small"]',
|
||||
|
|
|
@ -5,8 +5,6 @@ return array(
|
|||
'//div[@class="post-content"]',
|
||||
),
|
||||
'strip' => array(
|
||||
'//style',
|
||||
'//script',
|
||||
'//*[contains(@class, "gallery")]',
|
||||
'//*[contains(@class, "share")]',
|
||||
'//*[contains(@class, "wpcnt")]',
|
||||
|
|
|
@ -2,6 +2,6 @@
|
|||
return array(
|
||||
'test_url' => 'http://www.spiegel.de/politik/ausland/afrika-angola-geht-gegen-islam-vor-und-schliesst-moscheen-a-935788.html',
|
||||
'body' => array(
|
||||
'//h2[contains(@class, "article-section")]'
|
||||
'//div[contains(@class, "article-section")]'
|
||||
)
|
||||
);
|
|
@ -6,8 +6,6 @@ return array(
|
|||
'//div[@class="body-copy"]',
|
||||
),
|
||||
'strip' => array(
|
||||
'//script',
|
||||
'//style',
|
||||
'//*[contains(@class, "module-crunchbase")]'
|
||||
)
|
||||
);
|
||||
|
|
|
@ -5,7 +5,6 @@ return array(
|
|||
'/html/body/table[3]/tbody/tr/td[1]/table[2]/tr/td[1]'
|
||||
),
|
||||
'strip' => array(
|
||||
'//style',
|
||||
'//font',
|
||||
),
|
||||
);
|
|
@ -6,9 +6,7 @@ return array(
|
|||
'//div[@class="indPost"]'
|
||||
),
|
||||
'strip' => array(
|
||||
'//script',
|
||||
'//form',
|
||||
'//style',
|
||||
'//*[@class="warning"]',
|
||||
'//*[@class="story-date"]',
|
||||
'//*[@class="story-header"]',
|
||||
|
|
|
@ -5,8 +5,6 @@ return array(
|
|||
'//div[@class="cnn_strycntntlft"]',
|
||||
),
|
||||
'strip' => array(
|
||||
'//script',
|
||||
'//style',
|
||||
'//div[@class="cnn_stryshrwdgtbtm"]',
|
||||
'//div[@class="cnn_strybtmcntnt"]',
|
||||
'//div[@class="cnn_strylftcntnt"]',
|
||||
|
|
|
@ -5,8 +5,6 @@ return array(
|
|||
'//article',
|
||||
),
|
||||
'strip' => array(
|
||||
'//script',
|
||||
'//style',
|
||||
'//*[contains(@class, "info_article")]',
|
||||
'//*[contains(@class, "fildariane_titre")]',
|
||||
'//*[contains(@class, "entete2_article")]',
|
||||
|
|
|
@ -5,8 +5,6 @@ return array(
|
|||
'//div[@id="storytext"]',
|
||||
),
|
||||
'strip' => array(
|
||||
'//script',
|
||||
'//style',
|
||||
'//*[@class="bucket img"]',
|
||||
'//*[@class="creditwrap"]',
|
||||
'//*[@class="captionwrap"]',
|
||||
|
|
|
@ -5,8 +5,6 @@ return array(
|
|||
'//div[@class="article_content"]',
|
||||
),
|
||||
'strip' => array(
|
||||
'//script',
|
||||
'//style',
|
||||
'//*[@id="slate_associated_bn"]',
|
||||
'//*[@id="ligatus-article"]',
|
||||
'//*[@id="article_sidebar"]',
|
||||
|
|
|
@ -18,19 +18,18 @@ class ClientTest extends PHPUnit_Framework_TestCase
|
|||
$this->assertNotEmpty($client->getLastModified());
|
||||
}
|
||||
|
||||
// // disabled due to https://github.com/sebastianbergmann/phpunit/issues/1452
|
||||
// /**
|
||||
// * @runInSeparateProcess
|
||||
// */
|
||||
// public function testPassthrough()
|
||||
// {
|
||||
// $client = Client::getInstance();
|
||||
// $client->setUrl('http://miniflux.net/favicon.ico');
|
||||
// $client->enablePassthroughMode();
|
||||
// $client->execute();
|
||||
//
|
||||
// $this->expectOutputString('no_to_be_defined');
|
||||
// }
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
*/
|
||||
public function testPassthrough()
|
||||
{
|
||||
$client = Client::getInstance();
|
||||
$client->setUrl('http://miniflux.net/favicon.ico');
|
||||
$client->enablePassthroughMode();
|
||||
$client->execute();
|
||||
|
||||
$this->expectOutputString(file_get_contents('tests/fixtures/miniflux_favicon.ico'));
|
||||
}
|
||||
|
||||
public function testCacheBothHaveToMatch()
|
||||
{
|
||||
|
|
|
@ -18,19 +18,18 @@ class CurlTest extends PHPUnit_Framework_TestCase
|
|||
$this->assertEquals('text/html; charset=utf-8', $result['headers']['Content-Type']);
|
||||
}
|
||||
|
||||
// // disabled due to https://github.com/sebastianbergmann/phpunit/issues/1452
|
||||
// /**
|
||||
// * @runInSeparateProcess
|
||||
// */
|
||||
// public function testPassthrough()
|
||||
// {
|
||||
// $client = new Curl;
|
||||
// $client->setUrl('http://miniflux.net/favicon.ico');
|
||||
// $client->enablePassthroughMode();
|
||||
// $client->doRequest();
|
||||
//
|
||||
// $this->expectOutputString('no_to_be_defined');
|
||||
// }
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
*/
|
||||
public function testPassthrough()
|
||||
{
|
||||
$client = new Curl;
|
||||
$client->setUrl('http://miniflux.net/favicon.ico');
|
||||
$client->enablePassthroughMode();
|
||||
$client->doRequest();
|
||||
|
||||
$this->expectOutputString(file_get_contents('tests/fixtures/miniflux_favicon.ico'));
|
||||
}
|
||||
|
||||
public function testRedirect()
|
||||
{
|
||||
|
|
|
@ -27,19 +27,18 @@ class StreamTest extends PHPUnit_Framework_TestCase
|
|||
$this->assertEquals('</html>', substr(trim($result['body']), -7));
|
||||
}
|
||||
|
||||
// // disabled due to https://github.com/sebastianbergmann/phpunit/issues/1452
|
||||
// /**
|
||||
// * @runInSeparateProcess
|
||||
// */
|
||||
// public function testPassthrough()
|
||||
// {
|
||||
// $client = new Stream;
|
||||
// $client->setUrl('http://miniflux.net/favicon.ico');
|
||||
// $client->enablePassthroughMode();
|
||||
// $client->doRequest();
|
||||
//
|
||||
// $this->expectOutputString('no_to_be_defined');
|
||||
// }
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
*/
|
||||
public function testPassthrough()
|
||||
{
|
||||
$client = new Stream;
|
||||
$client->setUrl('http://miniflux.net/favicon.ico');
|
||||
$client->enablePassthroughMode();
|
||||
$client->doRequest();
|
||||
|
||||
$this->expectOutputString(file_get_contents('tests/fixtures/miniflux_favicon.ico'));
|
||||
}
|
||||
|
||||
public function testRedirect()
|
||||
{
|
||||
|
|
|
@ -6,6 +6,12 @@ use PHPUnit_Framework_TestCase;
|
|||
|
||||
class HtmlFilterTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testEmpty()
|
||||
{
|
||||
$filter = new Html('', 'http://www.google.ca/');
|
||||
$this->assertEquals('', $filter->execute());
|
||||
}
|
||||
|
||||
public function testExecute()
|
||||
{
|
||||
$html = '<!DOCTYPE html><html><head>
|
||||
|
|
|
@ -119,11 +119,11 @@ class AtomParserTest extends PHPUnit_Framework_TestCase
|
|||
{
|
||||
$parser = new Atom(file_get_contents('tests/fixtures/atom.xml'));
|
||||
$feed = $parser->execute();
|
||||
$this->assertEquals(1360148333, $feed->getDate());
|
||||
$this->assertEquals(1360148333, $feed->getDate()->getTimestamp(), '', 1);
|
||||
|
||||
$parser = new Atom(file_get_contents('tests/fixtures/atomsample.xml'));
|
||||
$feed = $parser->execute();
|
||||
$this->assertEquals(1071340202, $feed->getDate());
|
||||
$this->assertEquals(1071340202, $feed->getDate()->getTimestamp(), '', 1);
|
||||
}
|
||||
|
||||
public function testFeedLanguage()
|
||||
|
@ -196,17 +196,17 @@ class AtomParserTest extends PHPUnit_Framework_TestCase
|
|||
$parser = new Atom(file_get_contents('tests/fixtures/atom.xml'));
|
||||
$feed = $parser->execute();
|
||||
$this->assertNotEmpty($feed->items);
|
||||
$this->assertEquals(1360011661, $feed->items[1]->getDate());
|
||||
$this->assertEquals(1360011661, $feed->items[1]->getDate()->getTimestamp(), '', 1);
|
||||
|
||||
$parser = new Atom(file_get_contents('tests/fixtures/atomsample.xml'));
|
||||
$feed = $parser->execute();
|
||||
$this->assertNotEmpty($feed->items);
|
||||
$this->assertEquals(1071340202, $feed->items[0]->getDate());
|
||||
$this->assertEquals(1071340202, $feed->items[0]->getDate()->getTimestamp(), '', 1);
|
||||
|
||||
$parser = new Atom(file_get_contents('tests/fixtures/youtube.xml'));
|
||||
$feed = $parser->execute();
|
||||
$this->assertNotEmpty($feed->items);
|
||||
$this->assertEquals(1336825342, $feed->items[1]->getDate()); // Should return the published date
|
||||
$this->assertEquals(1336825342, $feed->items[1]->getDate()->getTimestamp(), '', 1); // Should return the published date
|
||||
}
|
||||
|
||||
public function testItemLanguage()
|
||||
|
|
|
@ -12,37 +12,37 @@ class DateParserTest extends PHPUnit_Framework_TestCase
|
|||
|
||||
date_default_timezone_set('UTC');
|
||||
|
||||
$this->assertEquals(1359066183, $parser->getTimestamp('Thu, 24 Jan 2013 22:23:03 +0000'));
|
||||
$this->assertEquals(1362992761, $parser->getTimestamp('2013-03-11T09:06:01+00:00'));
|
||||
$this->assertEquals(1363752990, $parser->getTimestamp('2013-03-20T04:16:30+00:00'));
|
||||
$this->assertEquals(1359066183, $parser->getTimestamp('Thu, 24 Jan 2013 22:23:03 +0000'));
|
||||
$this->assertEquals(1380929699, $parser->getTimestamp('Sat, 04 Oct 2013 02:34:59 +0300'));
|
||||
$this->assertEquals(1054633161, $parser->getTimestamp('Tue, 03 Jun 2003 09:39:21 GMT'));
|
||||
$this->assertEquals(1071340202, $parser->getTimestamp('2003-12-13T18:30:02Z'));
|
||||
$this->assertEquals(1364234797, $parser->getTimestamp('Mon, 25 Mar 2013 19:06:37 +0100'));
|
||||
$this->assertEquals(1360054941, $parser->getTimestamp('2013-02-05T09:02:21.880-08:00'));
|
||||
$this->assertEquals(1286834400, $parser->getTimestamp('Tue, 12 Oct 2010 00:00:00 IST'));
|
||||
$this->assertEquals('2014-12-15 19:49', date('Y-m-d H:i', $parser->getTimestamp('15 Dec 2014 19:49:07 +0100')));
|
||||
$this->assertEquals('2012-05-15', date('Y-m-d', $parser->getTimestamp('Tue, 15 May 2012 24:05:00 UTC')));
|
||||
$this->assertEquals('2013-09-12', date('Y-m-d', $parser->getTimestamp('Thu, 12 Sep 2013 7:00:00 UTC')));
|
||||
$this->assertEquals('2012-01-31', date('Y-m-d', $parser->getTimestamp('01.31.2012')));
|
||||
$this->assertEquals('2012-01-31', date('Y-m-d', $parser->getTimestamp('01/31/2012')));
|
||||
$this->assertEquals('2012-01-31', date('Y-m-d', $parser->getTimestamp('2012-01-31')));
|
||||
$this->assertEquals('2010-02-24', date('Y-m-d', $parser->getTimestamp('2010-02-245T15:27:52Z')));
|
||||
$this->assertEquals('2010-08-20', date('Y-m-d', $parser->getTimestamp('2010-08-20Thh:08:ssZ')));
|
||||
$this->assertEquals(1288648057, $parser->getTimestamp('Mon, 01 Nov 2010 21:47:37 UT'));
|
||||
$this->assertEquals(1346069615, $parser->getTimestamp('Mon Aug 27 2012 12:13:35 GMT-0700 (PDT)'));
|
||||
$this->assertEquals(time(), $parser->getTimestamp('Tue, 3 Febuary 2010 00:00:00 IST'), '', 1);
|
||||
$this->assertEquals(time(), $parser->getTimestamp('############# EST'), '', 1);
|
||||
$this->assertEquals(time(), $parser->getTimestamp('Wed, 30 Nov -0001 00:00:00 +0000'), '', 1);
|
||||
$this->assertEquals(time(), $parser->getTimestamp('čet, 24 maj 2012 00:00:00'), '', 1);
|
||||
$this->assertEquals(time(), $parser->getTimestamp('-0-0T::Z'), '', 1);
|
||||
$this->assertEquals(time(), $parser->getTimestamp('Wed, 18 2012'), '', 1);
|
||||
$this->assertEquals(time(), $parser->getTimestamp("'2009-09-30 CDT16:09:54"), '', 1);
|
||||
$this->assertEquals(time(), $parser->getTimestamp('ary 8 Jan 2013 00:00:00 GMT'), '', 1);
|
||||
$this->assertEquals(time(), $parser->getTimestamp('Sat, 11 00:00:01 GMT'), '', 1);
|
||||
$this->assertEquals(1370631743, $parser->getTimestamp('Fri Jun 07 2013 19:02:23 GMT+0000 (UTC)'));
|
||||
$this->assertEquals(1377412225, $parser->getTimestamp('25/08/2013 06:30:25 م'));
|
||||
$this->assertEquals(time(), $parser->getTimestamp('+0400'), '', 1);
|
||||
$this->assertEquals(1359066183, $parser->getDateTime('Thu, 24 Jan 2013 22:23:03 +0000')->getTimestamp(), '', 1);
|
||||
$this->assertEquals(1362992761, $parser->getDateTime('2013-03-11T09:06:01+00:00')->getTimestamp(), '', 1);
|
||||
$this->assertEquals(1363752990, $parser->getDateTime('2013-03-20T04:16:30+00:00')->getTimestamp(), '', 1);
|
||||
$this->assertEquals(1359066183, $parser->getDateTime('Thu, 24 Jan 2013 22:23:03 +0000')->getTimestamp(), '', 1);
|
||||
$this->assertEquals(1380929699, $parser->getDateTime('Sat, 04 Oct 2013 02:34:59 +0300')->getTimestamp(), '', 1);
|
||||
$this->assertEquals(1054633161, $parser->getDateTime('Tue, 03 Jun 2003 09:39:21 GMT')->getTimestamp(), '', 1);
|
||||
$this->assertEquals(1071340202, $parser->getDateTime('2003-12-13T18:30:02Z')->getTimestamp(), '', 1);
|
||||
$this->assertEquals(1364234797, $parser->getDateTime('Mon, 25 Mar 2013 19:06:37 +0100')->getTimestamp(), '', 1);
|
||||
$this->assertEquals(1360054941, $parser->getDateTime('2013-02-05T09:02:21.880-08:00')->getTimestamp(), '', 1);
|
||||
$this->assertEquals(1286834400, $parser->getDateTime('Tue, 12 Oct 2010 00:00:00 IST')->getTimestamp(), '', 1);
|
||||
$this->assertEquals('2014-12-15 19:49', $parser->getDateTime('15 Dec 2014 19:49:07 +0100')->format('Y-m-d H:i'));
|
||||
$this->assertEquals('2012-05-15', $parser->getDateTime('Tue, 15 May 2012 24:05:00 UTC')->format('Y-m-d'));
|
||||
$this->assertEquals('2013-09-12', $parser->getDateTime('Thu, 12 Sep 2013 7:00:00 UTC')->format('Y-m-d'));
|
||||
$this->assertEquals('2012-01-31', $parser->getDateTime('01.31.2012')->format('Y-m-d'));
|
||||
$this->assertEquals('2012-01-31', $parser->getDateTime('01/31/2012')->format('Y-m-d'));
|
||||
$this->assertEquals('2012-01-31', $parser->getDateTime('2012-01-31')->format('Y-m-d'));
|
||||
$this->assertEquals('2010-02-24', $parser->getDateTime('2010-02-245T15:27:52Z')->format('Y-m-d'));
|
||||
$this->assertEquals('2010-08-20', $parser->getDateTime('2010-08-20Thh:08:ssZ')->format('Y-m-d'));
|
||||
$this->assertEquals(1288648057, $parser->getDateTime('Mon, 01 Nov 2010 21:47:37 UT')->getTimestamp(), '', 1);
|
||||
$this->assertEquals(1346069615, $parser->getDateTime('Mon Aug 27 2012 12:13:35 GMT-0700 (PDT)')->getTimestamp(), '', 1);
|
||||
$this->assertEquals($parser->getCurrentDateTime(), $parser->getDateTime('Tue, 3 Febuary 2010 00:00:00 IST'));
|
||||
$this->assertEquals($parser->getCurrentDateTime(), $parser->getDateTime('############# EST'));
|
||||
$this->assertEquals($parser->getCurrentDateTime(), $parser->getDateTime('Wed, 30 Nov -0001 00:00:00 +0000'));
|
||||
$this->assertEquals($parser->getCurrentDateTime(), $parser->getDateTime('čet, 24 maj 2012 00:00:00'));
|
||||
$this->assertEquals($parser->getCurrentDateTime(), $parser->getDateTime('-0-0T::Z'));
|
||||
$this->assertEquals($parser->getCurrentDateTime(), $parser->getDateTime('Wed, 18 2012'));
|
||||
$this->assertEquals($parser->getCurrentDateTime(), $parser->getDateTime("'2009-09-30 CDT16:09:54"));
|
||||
$this->assertEquals($parser->getCurrentDateTime(), $parser->getDateTime('ary 8 Jan 2013 00:00:00 GMT'));
|
||||
$this->assertEquals($parser->getCurrentDateTime(), $parser->getDateTime('Sat, 11 00:00:01 GMT'));
|
||||
$this->assertEquals(1370631743, $parser->getDateTime('Fri Jun 07 2013 19:02:23 GMT+0000 (UTC)')->getTimestamp(), '', 1);
|
||||
$this->assertEquals(1377412225, $parser->getDateTime('25/08/2013 06:30:25 م')->getTimestamp(), '', 1);
|
||||
$this->assertEquals($parser->getCurrentDateTime(), $parser->getDateTime('+0400'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ class Rss10ParserTest extends PHPUnit_Framework_TestCase
|
|||
{
|
||||
$parser = new Rss10(file_get_contents('tests/fixtures/planete-jquery.xml'));
|
||||
$feed = $parser->execute();
|
||||
$this->assertEquals(1363752990, $feed->getDate());
|
||||
$this->assertEquals(1363752990, $feed->getDate()->getTimestamp(), '', 1);
|
||||
}
|
||||
|
||||
public function testFeedLanguage()
|
||||
|
@ -90,7 +90,7 @@ class Rss10ParserTest extends PHPUnit_Framework_TestCase
|
|||
$parser = new Rss10(file_get_contents('tests/fixtures/planete-jquery.xml'));
|
||||
$feed = $parser->execute();
|
||||
$this->assertNotEmpty($feed->items);
|
||||
$this->assertEquals(1362647700, $feed->items[1]->getDate());
|
||||
$this->assertEquals(1362647700, $feed->items[1]->getDate()->getTimestamp(), '', 1);
|
||||
}
|
||||
|
||||
public function testItemLanguage()
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace PicoFeed\Parser;
|
||||
|
||||
use PHPUnit_Framework_TestCase;
|
||||
|
||||
use DateTime;
|
||||
|
||||
class Rss20ParserTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
|
@ -89,11 +90,11 @@ class Rss20ParserTest extends PHPUnit_Framework_TestCase
|
|||
{
|
||||
$parser = new Rss20(file_get_contents('tests/fixtures/rss20.xml'));
|
||||
$feed = $parser->execute();
|
||||
$this->assertEquals(1359066183, $feed->getDate());
|
||||
$this->assertEquals(1359066183, $feed->getDate()->getTimestamp());
|
||||
|
||||
$parser = new Rss20(file_get_contents('tests/fixtures/fulltextrss.xml'));
|
||||
$feed = $parser->execute();
|
||||
$this->assertEquals(time(), $feed->getDate(), '', 1);
|
||||
$this->assertEquals(new DateTime, $feed->getDate());
|
||||
}
|
||||
|
||||
public function testFeedLanguage()
|
||||
|
@ -171,12 +172,12 @@ class Rss20ParserTest extends PHPUnit_Framework_TestCase
|
|||
$parser = new Rss20(file_get_contents('tests/fixtures/rss20.xml'));
|
||||
$feed = $parser->execute();
|
||||
$this->assertNotEmpty($feed->items);
|
||||
$this->assertEquals(1357006940, $feed->items[1]->getDate());
|
||||
$this->assertEquals(1357006940, $feed->items[1]->getDate()->getTimestamp());
|
||||
|
||||
$parser = new Rss20(file_get_contents('tests/fixtures/fulltextrss.xml'));
|
||||
$feed = $parser->execute();
|
||||
$this->assertNotEmpty($feed->items);
|
||||
$this->assertEquals(1365781095, $feed->items[0]->getDate());
|
||||
$this->assertEquals(1365781095, $feed->items[0]->getDate()->getTimestamp());
|
||||
}
|
||||
|
||||
public function testItemLanguage()
|
||||
|
|
|
@ -18,13 +18,13 @@ class Rss91ParserTest extends PHPUnit_Framework_TestCase
|
|||
$this->assertEquals('', $feed->getFeedUrl());
|
||||
$this->assertEquals('http://writetheweb.com/', $feed->getSiteUrl());
|
||||
$this->assertEquals('http://writetheweb.com/', $feed->getId());
|
||||
$this->assertEquals(time(), $feed->getDate(), '', 1);
|
||||
$this->assertEquals(time(), $feed->getDate()->getTimestamp(), '', 1);
|
||||
$this->assertEquals(6, count($feed->items));
|
||||
|
||||
$this->assertEquals('Giving the world a pluggable Gnutella', $feed->items[0]->getTitle());
|
||||
$this->assertEquals('http://writetheweb.com/read.php?item=24', $feed->items[0]->getUrl());
|
||||
$this->assertEquals('085a9133a75542f878fa73ee2afbb6a2350b6c4fb125e6d8ca09478c47702111', $feed->items[0]->getId());
|
||||
$this->assertEquals(time(), $feed->items[0]->getDate(), '', 1);
|
||||
$this->assertEquals(time(), $feed->items[0]->getDate()->getTimestamp(), '', 1);
|
||||
$this->assertEquals('webmaster@writetheweb.com', $feed->items[0]->getAuthor());
|
||||
$this->assertTrue(strpos($feed->items[1]->getContent(), '<p>After a period of dormancy') === 0);
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ class Rss92ParserTest extends PHPUnit_Framework_TestCase
|
|||
$this->assertEquals('', $feed->getFeedUrl());
|
||||
$this->assertEquals('http://www.universfreebox.com/', $feed->getSiteUrl());
|
||||
$this->assertEquals('http://www.universfreebox.com/', $feed->getId());
|
||||
$this->assertEquals(time(), $feed->date, '', 1);
|
||||
$this->assertEquals(time(), $feed->getDate()->getTimestamp(), '', 1);
|
||||
$this->assertEquals(30, count($feed->items));
|
||||
|
||||
$this->assertEquals('Retour de Xavier Niel sur Twitter, « sans initiative privée, pas de révolution #Born2code »', $feed->items[0]->title);
|
||||
|
|
|
@ -8,6 +8,19 @@ use PHPUnit_Framework_TestCase;
|
|||
|
||||
class XmlParserTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testEmpty()
|
||||
{
|
||||
$this->assertFalse(XmlParser::getDomDocument(''));
|
||||
$this->assertFalse(XmlParser::getSimpleXml(''));
|
||||
$this->assertNotFalse(XmlParser::getHtmlDocument(''));
|
||||
}
|
||||
|
||||
public function testGetEncodingFromMetaTag()
|
||||
{
|
||||
$this->assertEquals('iso-8859-1', XmlParser::getEncodingFromMetaTag('<html><head><meta content="text/html; charset=iso-8859-1" http-equiv="Content-Type"/></head></html>'));
|
||||
$this->assertEquals('iso-8859-1', XmlParser::getEncodingFromMetaTag('<html><head><meta charset="iso-8859-1"></head></html>'));
|
||||
}
|
||||
|
||||
public function testGetEncodingFromXmlTag()
|
||||
{
|
||||
$this->assertEquals('utf-8', XmlParser::getEncodingFromXmlTag("<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet"));
|
||||
|
|
Двоичные данные
vendor/fguillot/picofeed/tests/fixtures/miniflux_favicon.ico
поставляемый
Normal file
Двоичные данные
vendor/fguillot/picofeed/tests/fixtures/miniflux_favicon.ico
поставляемый
Normal file
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 17 KiB |
Загрузка…
Ссылка в новой задаче