Include PicoFarad into Miniflux

This commit is contained in:
Frederic Guillot 2015-08-28 21:34:34 -04:00
parent 8457b83739
commit 1c0d14bc94
182 changed files with 190 additions and 24037 deletions

View File

@ -3,11 +3,10 @@
"preferred-install": "dist" "preferred-install": "dist"
}, },
"require": { "require": {
"fguillot/simple-validator": "v0.0.3", "fguillot/simple-validator": "v1.0.0",
"fguillot/json-rpc": "v1.0.1", "fguillot/json-rpc": "v1.0.1",
"fguillot/picodb": "v1.0.1", "fguillot/picodb": "v1.0.1",
"fguillot/picofeed": "v0.1.8", "fguillot/picofeed": "v0.1.9"
"fguillot/picofarad": "dev-master"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "4.8.3", "phpunit/phpunit": "4.8.3",
@ -18,6 +17,11 @@
"files": [ "files": [
"lib/helpers.php", "lib/helpers.php",
"lib/Translator.php", "lib/Translator.php",
"lib/Request.php",
"lib/Response.php",
"lib/Router.php",
"lib/Session.php",
"lib/Template.php",
"models/config.php", "models/config.php",
"models/service.php", "models/service.php",
"models/user.php", "models/user.php",

View File

@ -1,9 +1,5 @@
<?php <?php
use PicoFarad\Router;
use PicoFarad\Response;
use PicoFarad\Request;
use PicoFarad\Template;
use PicoFeed\Syndication\Atom; use PicoFeed\Syndication\Atom;
// Ajax call to add or remove a bookmark // Ajax call to add or remove a bookmark

View File

@ -1,11 +1,5 @@
<?php <?php
use PicoFarad\Router;
use PicoFarad\Response;
use PicoFarad\Request;
use PicoFarad\Session;
use PicoFarad\Template;
// Called before each action // Called before each action
Router\before(function($action) { Router\before(function($action) {

View File

@ -1,10 +1,5 @@
<?php <?php
use PicoFarad\Router;
use PicoFarad\Response;
use PicoFarad\Request;
use PicoFarad\Session;
use PicoFarad\Template;
use PicoDb\Database; use PicoDb\Database;
// Display a form to add a new database // Display a form to add a new database

View File

@ -1,9 +1,5 @@
<?php <?php
use PicoFarad\Router;
use PicoFarad\Response;
use PicoFarad\Template;
// Flush console messages // Flush console messages
Router\get_action('flush-console', function() { Router\get_action('flush-console', function() {

View File

@ -1,11 +1,5 @@
<?php <?php
use PicoFarad\Router;
use PicoFarad\Response;
use PicoFarad\Request;
use PicoFarad\Session;
use PicoFarad\Template;
// Refresh all feeds, used when Javascript is disabled // Refresh all feeds, used when Javascript is disabled
Router\get_action('refresh-all', function() { Router\get_action('refresh-all', function() {

View File

@ -1,10 +1,5 @@
<?php <?php
use PicoFarad\Router;
use PicoFarad\Response;
use PicoFarad\Request;
use PicoFarad\Template;
// Display history page // Display history page
Router\get_action('history', function() { Router\get_action('history', function() {

View File

@ -1,10 +1,5 @@
<?php <?php
use PicoFarad\Router;
use PicoFarad\Response;
use PicoFarad\Request;
use PicoFarad\Template;
// Display unread items // Display unread items
Router\get_action('unread', function() { Router\get_action('unread', function() {

View File

@ -1,10 +1,5 @@
<?php <?php
use PicoFarad\Router;
use PicoFarad\Response;
use PicoFarad\Request;
use PicoFarad\Template;
// Logout and destroy session // Logout and destroy session
Router\get_action('logout', function() { Router\get_action('logout', function() {

View File

@ -1,14 +1,6 @@
<?php <?php
require __DIR__.'/common.php'; require __DIR__.'/common.php';
require __DIR__.'/vendor/fguillot/picofarad/lib/PicoFarad/Template.php';
require __DIR__.'/vendor/fguillot/picofarad/lib/PicoFarad/Response.php';
require __DIR__.'/vendor/fguillot/picofarad/lib/PicoFarad/Request.php';
require __DIR__.'/vendor/fguillot/picofarad/lib/PicoFarad/Session.php';
require __DIR__.'/vendor/fguillot/picofarad/lib/PicoFarad/Router.php';
use PicoFarad\Router;
use PicoFarad\Response;
Router\bootstrap(__DIR__.'/controllers', 'common', 'console', 'user', 'config', 'item', 'history', 'bookmark', 'feed'); Router\bootstrap(__DIR__.'/controllers', 'common', 'console', 'user', 'config', 'item', 'history', 'bookmark', 'feed');

View File

@ -1,6 +1,6 @@
<?php <?php
namespace PicoFarad\Request; namespace Request;
function param($name, $default_value = null) function param($name, $default_value = null)

View File

@ -1,6 +1,6 @@
<?php <?php
namespace PicoFarad\Response; namespace Response;
function force_download($filename) function force_download($filename)

View File

@ -1,6 +1,6 @@
<?php <?php
namespace PicoFarad\Router; namespace Router;
// Load controllers: bootstrap('controllers', 'controller1', 'controller2') // Load controllers: bootstrap('controllers', 'controller1', 'controller2')
function bootstrap() function bootstrap()

View File

@ -1,6 +1,6 @@
<?php <?php
namespace PicoFarad\Session; namespace Session;
const SESSION_LIFETIME = 2678400; const SESSION_LIFETIME = 2678400;

View File

@ -1,6 +1,6 @@
<?php <?php
namespace PicoFarad\Template; namespace Template;
const PATH = 'templates/'; const PATH = 'templates/';

View File

@ -5,7 +5,7 @@ namespace Model\User;
use SimpleValidator\Validator; use SimpleValidator\Validator;
use SimpleValidator\Validators; use SimpleValidator\Validators;
use PicoDb\Database; use PicoDb\Database;
use PicoFarad\Session; use Session;
use Model\Config; use Model\Config;
use Model\RememberMe; use Model\RememberMe;
use Model\Database as DatabaseModel; use Model\Database as DatabaseModel;

View File

@ -12,7 +12,7 @@
<section class="items" id="listing"> <section class="items" id="listing">
<?php foreach ($items as $item): ?> <?php foreach ($items as $item): ?>
<?= \PicoFarad\Template\load('item', array( <?= \Template\load('item', array(
'item' => $item, 'item' => $item,
'menu' => $menu, 'menu' => $menu,
'offset' => $offset, 'offset' => $offset,
@ -23,7 +23,7 @@
)) ?> )) ?>
<?php endforeach ?> <?php endforeach ?>
<?= \PicoFarad\Template\load('paging', array('menu' => $menu, 'nb_items' => $nb_items, 'items_per_page' => $items_per_page, 'offset' => $offset, 'order' => $order, 'direction' => $direction)) ?> <?= \Template\load('paging', array('menu' => $menu, 'nb_items' => $nb_items, 'items_per_page' => $items_per_page, 'offset' => $offset, 'order' => $order, 'direction' => $direction)) ?>
</section> </section>
<?php endif ?> <?php endif ?>

View File

@ -27,7 +27,7 @@
<section class="items" id="listing" data-feed-id="<?= $feed['id'] ?>"> <section class="items" id="listing" data-feed-id="<?= $feed['id'] ?>">
<?php foreach ($items as $item): ?> <?php foreach ($items as $item): ?>
<?= \PicoFarad\Template\load('item', array( <?= \Template\load('item', array(
'feed' => $feed, 'feed' => $feed,
'item' => $item, 'item' => $item,
'menu' => $menu, 'menu' => $menu,
@ -43,7 +43,7 @@
<a href="?action=mark-feed-as-read&amp;feed_id=<?= $feed['id'] ?>" data-action="mark-feed-read"><?= t('mark all as read') ?></a> <a href="?action=mark-feed-as-read&amp;feed_id=<?= $feed['id'] ?>" data-action="mark-feed-read"><?= t('mark all as read') ?></a>
</div> </div>
<?= \PicoFarad\Template\load('paging', array('menu' => $menu, 'nb_items' => $nb_items, 'items_per_page' => $items_per_page, 'offset' => $offset, 'order' => $order, 'direction' => $direction, 'feed_id' => $feed['id'])) ?> <?= \Template\load('paging', array('menu' => $menu, 'nb_items' => $nb_items, 'items_per_page' => $items_per_page, 'offset' => $offset, 'order' => $order, 'direction' => $direction, 'feed_id' => $feed['id'])) ?>
</section> </section>
<?php endif ?> <?php endif ?>

View File

@ -12,7 +12,7 @@
</nav> </nav>
</div> </div>
<section> <section>
<?= \PicoFarad\Template\load('keyboard_shortcuts') ?> <?= \Template\load('keyboard_shortcuts') ?>
</section> </section>
<div class="page-section"> <div class="page-section">
<h2><?= t('Documentation') ?></h2> <h2><?= t('Documentation') ?></h2>

View File

@ -15,7 +15,7 @@
<section class="items" id="listing"> <section class="items" id="listing">
<?php foreach ($items as $item): ?> <?php foreach ($items as $item): ?>
<?= \PicoFarad\Template\load('item', array( <?= \Template\load('item', array(
'item' => $item, 'item' => $item,
'menu' => $menu, 'menu' => $menu,
'offset' => $offset, 'offset' => $offset,
@ -26,7 +26,7 @@
)) ?> )) ?>
<?php endforeach ?> <?php endforeach ?>
<?= \PicoFarad\Template\load('paging', array('menu' => $menu, 'nb_items' => $nb_items, 'items_per_page' => $items_per_page, 'offset' => $offset, 'order' => $order, 'direction' => $direction)) ?> <?= \Template\load('paging', array('menu' => $menu, 'nb_items' => $nb_items, 'items_per_page' => $items_per_page, 'offset' => $offset, 'order' => $order, 'direction' => $direction)) ?>
</section> </section>
<?php endif ?> <?php endif ?>

View File

@ -51,7 +51,7 @@
<?php endif ?> <?php endif ?>
</li> </li>
<?php endif ?> <?php endif ?>
<?= \PicoFarad\Template\load('bookmark_links', array('item' => $item, 'menu' => $menu, 'offset' => $offset, 'source' => '')) ?> <?= \Template\load('bookmark_links', array('item' => $item, 'menu' => $menu, 'offset' => $offset, 'source' => '')) ?>
<?= \PicoFarad\Template\load('status_links', array('item' => $item, 'redirect' => $menu, 'offset' => $offset)) ?> <?= \Template\load('status_links', array('item' => $item, 'redirect' => $menu, 'offset' => $offset)) ?>
</ul> </ul>
</article> </article>

View File

@ -12,7 +12,7 @@
<h2><?= t('Help') ?></h2> <h2><?= t('Help') ?></h2>
</div> </div>
<section> <section>
<?= \PicoFarad\Template\load('keyboard_shortcuts') ?> <?= \Template\load('keyboard_shortcuts') ?>
</section> </section>
</section> </section>
</body> </body>

View File

@ -29,7 +29,7 @@
<p class="alert alert-info"><?= t('Nothing to read') ?></p> <p class="alert alert-info"><?= t('Nothing to read') ?></p>
<?php else: ?> <?php else: ?>
<?php foreach ($items as $item): ?> <?php foreach ($items as $item): ?>
<?= \PicoFarad\Template\load('item', array( <?= \Template\load('item', array(
'item' => $item, 'item' => $item,
'menu' => $menu, 'menu' => $menu,
'offset' => $offset, 'offset' => $offset,
@ -44,6 +44,6 @@
<a href="?action=mark-all-read<?= is_null($group_id) ? '' : '&amp;group_id='.$group_id ?>"><?= t('mark all as read') ?></a> <a href="?action=mark-all-read<?= is_null($group_id) ? '' : '&amp;group_id='.$group_id ?>"><?= t('mark all as read') ?></a>
</div> </div>
<?= \PicoFarad\Template\load('paging', array('menu' => $menu, 'nb_items' => $nb_items, 'items_per_page' => $items_per_page, 'offset' => $offset, 'order' => $order, 'direction' => $direction, 'group_id' => $group_id)) ?> <?= \Template\load('paging', array('menu' => $menu, 'nb_items' => $nb_items, 'items_per_page' => $items_per_page, 'offset' => $offset, 'order' => $order, 'direction' => $direction, 'group_id' => $group_id)) ?>
<?php endif ?> <?php endif ?>
</section> </section>

View File

@ -70,10 +70,10 @@ return array(
'PicoFeed\\Syndication\\Atom' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Syndication/Atom.php', 'PicoFeed\\Syndication\\Atom' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Syndication/Atom.php',
'PicoFeed\\Syndication\\Rss20' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Syndication/Rss20.php', 'PicoFeed\\Syndication\\Rss20' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Syndication/Rss20.php',
'PicoFeed\\Syndication\\Writer' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Syndication/Writer.php', 'PicoFeed\\Syndication\\Writer' => $vendorDir . '/fguillot/picofeed/lib/PicoFeed/Syndication/Writer.php',
'SimpleValidator\\Base' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Base.php',
'SimpleValidator\\Validator' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validator.php', 'SimpleValidator\\Validator' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validator.php',
'SimpleValidator\\Validators\\Alpha' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Alpha.php', 'SimpleValidator\\Validators\\Alpha' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Alpha.php',
'SimpleValidator\\Validators\\AlphaNumeric' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/AlphaNumeric.php', 'SimpleValidator\\Validators\\AlphaNumeric' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/AlphaNumeric.php',
'SimpleValidator\\Validators\\Base' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Base.php',
'SimpleValidator\\Validators\\Date' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Date.php', 'SimpleValidator\\Validators\\Date' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Date.php',
'SimpleValidator\\Validators\\Email' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Email.php', 'SimpleValidator\\Validators\\Email' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Email.php',
'SimpleValidator\\Validators\\Equals' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Equals.php', 'SimpleValidator\\Validators\\Equals' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Equals.php',
@ -83,7 +83,6 @@ return array(
'SimpleValidator\\Validators\\Integer' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Integer.php', 'SimpleValidator\\Validators\\Integer' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Integer.php',
'SimpleValidator\\Validators\\Ip' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Ip.php', 'SimpleValidator\\Validators\\Ip' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Ip.php',
'SimpleValidator\\Validators\\Length' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Length.php', 'SimpleValidator\\Validators\\Length' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Length.php',
'SimpleValidator\\Validators\\MacAddress' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/MacAddress.php',
'SimpleValidator\\Validators\\MaxLength' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/MaxLength.php', 'SimpleValidator\\Validators\\MaxLength' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/MaxLength.php',
'SimpleValidator\\Validators\\MinLength' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/MinLength.php', 'SimpleValidator\\Validators\\MinLength' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/MinLength.php',
'SimpleValidator\\Validators\\NotEquals' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/NotEquals.php', 'SimpleValidator\\Validators\\NotEquals' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/NotEquals.php',
@ -92,5 +91,4 @@ return array(
'SimpleValidator\\Validators\\Range' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Range.php', 'SimpleValidator\\Validators\\Range' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Range.php',
'SimpleValidator\\Validators\\Required' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Required.php', 'SimpleValidator\\Validators\\Required' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Required.php',
'SimpleValidator\\Validators\\Unique' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Unique.php', 'SimpleValidator\\Validators\\Unique' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Unique.php',
'SimpleValidator\\Validators\\Version' => $vendorDir . '/fguillot/simple-validator/src/SimpleValidator/Validators/Version.php',
); );

View File

@ -8,6 +8,11 @@ $baseDir = dirname($vendorDir);
return array( return array(
$baseDir . '/lib/helpers.php', $baseDir . '/lib/helpers.php',
$baseDir . '/lib/Translator.php', $baseDir . '/lib/Translator.php',
$baseDir . '/lib/Request.php',
$baseDir . '/lib/Response.php',
$baseDir . '/lib/Router.php',
$baseDir . '/lib/Session.php',
$baseDir . '/lib/Template.php',
$baseDir . '/models/config.php', $baseDir . '/models/config.php',
$baseDir . '/models/service.php', $baseDir . '/models/service.php',
$baseDir . '/models/user.php', $baseDir . '/models/user.php',

View File

@ -8,7 +8,6 @@ $baseDir = dirname($vendorDir);
return array( return array(
'SimpleValidator' => array($vendorDir . '/fguillot/simple-validator/src'), 'SimpleValidator' => array($vendorDir . '/fguillot/simple-validator/src'),
'PicoFeed' => array($vendorDir . '/fguillot/picofeed/lib'), 'PicoFeed' => array($vendorDir . '/fguillot/picofeed/lib'),
'PicoFarad' => array($vendorDir . '/fguillot/picofarad/lib'),
'PicoDb' => array($vendorDir . '/fguillot/picodb/lib'), 'PicoDb' => array($vendorDir . '/fguillot/picodb/lib'),
'JsonRPC' => array($vendorDir . '/fguillot/json-rpc/src'), 'JsonRPC' => array($vendorDir . '/fguillot/json-rpc/src'),
); );

View File

@ -1,130 +1,4 @@
[ [
{
"name": "fguillot/picofarad",
"version": "dev-master",
"version_normalized": "9999999-dev",
"source": {
"type": "git",
"url": "https://github.com/fguillot/picoFarad.git",
"reference": "a5817c49ca3037829ec1509d14724be5f29c35a0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fguillot/picoFarad/zipball/a5817c49ca3037829ec1509d14724be5f29c35a0",
"reference": "a5817c49ca3037829ec1509d14724be5f29c35a0",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"time": "2015-04-14 01:53:02",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-0": {
"PicoFarad": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Frédéric Guillot",
"homepage": "http://fredericguillot.com"
}
],
"description": "Minimalist micro-framework",
"homepage": "https://github.com/fguillot/picoFarad"
},
{
"name": "fguillot/simple-validator",
"version": "v0.0.3",
"version_normalized": "0.0.3.0",
"source": {
"type": "git",
"url": "https://github.com/fguillot/simpleValidator.git",
"reference": "2f30078bb6e688cf123c150d58fda322792a1532"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fguillot/simpleValidator/zipball/2f30078bb6e688cf123c150d58fda322792a1532",
"reference": "2f30078bb6e688cf123c150d58fda322792a1532",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"time": "2015-04-14 02:03:43",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-0": {
"SimpleValidator": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Frédéric Guillot"
}
],
"description": "The most easy to use validator library for PHP :)",
"homepage": "https://github.com/fguillot/simpleValidator"
},
{
"name": "fguillot/picofeed",
"version": "v0.1.8",
"version_normalized": "0.1.8.0",
"source": {
"type": "git",
"url": "https://github.com/fguillot/picoFeed.git",
"reference": "a6d01a62514530dea91b2c8da5e194d680e1d91b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fguillot/picoFeed/zipball/a6d01a62514530dea91b2c8da5e194d680e1d91b",
"reference": "a6d01a62514530dea91b2c8da5e194d680e1d91b",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-iconv": "*",
"ext-libxml": "*",
"ext-simplexml": "*",
"ext-xml": "*",
"php": ">=5.3.0"
},
"suggest": {
"ext-curl": "PicoFeed will use cURL if present"
},
"time": "2015-08-13 23:56:50",
"bin": [
"picofeed"
],
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-0": {
"PicoFeed": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Frédéric Guillot"
}
],
"description": "Modern library to handle RSS/Atom feeds",
"homepage": "https://github.com/fguillot/picoFeed"
},
{ {
"name": "fguillot/json-rpc", "name": "fguillot/json-rpc",
"version": "v1.0.1", "version": "v1.0.1",
@ -201,5 +75,92 @@
], ],
"description": "Minimalist database query builder", "description": "Minimalist database query builder",
"homepage": "https://github.com/fguillot/picoDb" "homepage": "https://github.com/fguillot/picoDb"
},
{
"name": "fguillot/simple-validator",
"version": "1.0.0",
"version_normalized": "1.0.0.0",
"source": {
"type": "git",
"url": "https://github.com/fguillot/simpleValidator.git",
"reference": "9579993f3dd0f03053b28fec1e7b9990edc3947b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fguillot/simpleValidator/zipball/9579993f3dd0f03053b28fec1e7b9990edc3947b",
"reference": "9579993f3dd0f03053b28fec1e7b9990edc3947b",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"time": "2015-08-29 00:44:37",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-0": {
"SimpleValidator": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Frédéric Guillot"
}
],
"description": "Simple validator library",
"homepage": "https://github.com/fguillot/simpleValidator"
},
{
"name": "fguillot/picofeed",
"version": "v0.1.9",
"version_normalized": "0.1.9.0",
"source": {
"type": "git",
"url": "https://github.com/fguillot/picoFeed.git",
"reference": "b1dc17e00215c6ffb6ff2fbc6d193d6403c8bb71"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fguillot/picoFeed/zipball/b1dc17e00215c6ffb6ff2fbc6d193d6403c8bb71",
"reference": "b1dc17e00215c6ffb6ff2fbc6d193d6403c8bb71",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-iconv": "*",
"ext-libxml": "*",
"ext-simplexml": "*",
"ext-xml": "*",
"php": ">=5.3.0"
},
"suggest": {
"ext-curl": "PicoFeed will use cURL if present"
},
"time": "2015-08-27 23:26:39",
"bin": [
"picofeed"
],
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-0": {
"PicoFeed": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Frédéric Guillot"
}
],
"description": "Modern library to handle RSS/Atom feeds",
"homepage": "https://github.com/fguillot/picoFeed"
} }
] ]

View File

@ -1,38 +0,0 @@
# Compiled source #
###################
*.com
*.class
*.dll
*.exe
*.o
*.so
# Packages #
############
# it's better to unpack these files and commit the raw source
# git has its own built in compression methods
*.7z
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip
# Logs and databases #
######################
*.log
*.sql
*.sqlite
# OS generated files #
######################
.DS_Store
.DS_Store?
ehthumbs.db
Icon?
Thumbs.db
*.swp
*~
*.lock

View File

@ -1,21 +0,0 @@
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.

View File

@ -1,312 +0,0 @@
PicoFarad
=========
PicoFarad is a minimalist micro-framework for PHP.
Perfect to build a REST API or a small webapp.
Features
--------
- No dependency
- Easy to use, fast and very lightweight
- Only 4 files: Request, Response, Router and Session
- License: MIT
Requirements
------------
- PHP >= 5.3
Router
------
### Example for a single file webapp:
```php
<?php
use PicoFarad\Router;
use PicoFarad\Response;
use PicoFarad\Request;
use PicoFarad\Session;
// Called before each action
Router\before(function($action) {
// Open a session only for the specified directory
Session\open(dirname($_SERVER['PHP_SELF']));
// HTTP secure headers
Response\csp();
Response\xframe();
Response\xss();
Response\nosniff();
});
// GET ?action=show-help
Router\get_action('show-help', function() {
Response\text('help me!');
});
// POST ?action=hello (with a form value "name")
Router\post_action('show-help', function() {
Response\text('Hello '.Request\value('name'));
});
// Default action executed
Router\notfound(function() {
Response\text('Sorry, page not found!');
})
```
### Split your webapp in different files:
```php
<?php
use PicoFarad\Router;
use PicoFarad\Response;
// Include automatically those files:
// __DIR__.'/controllers/controller1.php'
// __DIR__.'/controllers/controller2.php'
Router\bootstrap(__DIR__.'/controllers', 'controller1', 'controller2');
// Page not found
Router\notfound(function() {
Response\redirect('?action=unread');
});
```
### Example for a REST API:
```php
<?php
// POST /foo
Router\post('/foo', function() {
$values = Request\values();
...
Response\json(['result' => true], 201);
});
// GET /foo/123
Router\get('/foo/:id', function($id) {
Response\json(['result' => true]);
});
// PUT /foo/123
Router\put('/foo/:id', function($id) {
$values = Request\values();
...
Response\json(['result' => true]);
});
// DELETE /foo/123
Router\delete('/foo/:id', function($id) {
Response\json(['result' => true]);
});
```
Response
--------
### Send a JSON response
```php
<?php
use PicoFarad\Response;
$data = array(....);
// Output the encoded JSON data with a HTTP status 200 Ok
Response\json($data);
// Change the default HTTP status code by a 400 Bad Request
Response\json($data, 400);
```
### Send text response
```php
Response\text('my text data');
```
### Send HTML response
```php
Response\html('<html...>');
```
### Send XML response
```php
Response\xml('<xml ... >');
```
### Send a binary response
```php
Response\binary($my_file_content);
```
### Send a raw response (no content-type)
```php
Response\raw($content);
```
### Force browser download
```php
Response\force_download('The name of the ouput file');
```
### Modify the HTTP status code
```php
Response\status(403);
```
### Temporary redirection
```php
Response\redirect('http://....');
```
### Permanent redirection
```php
Response\redirect('http://....', 301);
```
### Secure headers
```php
// Send the header X-Content-Type-Options: nosniff
Response\nosniff();
// Send the header X-XSS-Protection: 1; mode=block
Response\xss()
// Send the header Strict-Transport-Security: max-age=31536000
Response\hsts();
// Send the header X-Frame-Options: DENY
Response\xframe();
```
### Content Security Policies
```php
Response\csp(array(
'img-src' => '*'
));
// Send these headers:
Content-Security-Policy: img-src *; default-src 'self';
X-Content-Security-Policy: img-src *; default-src 'self';
X-WebKit-CSP: img-src *; default-src 'self';
```
Request
-------
### Get querystring variables
```php
use PicoFarad\Request;
// Get from the URL: ?toto=value
echo Request\param('toto');
// Get only integer value: ?toto=2
echo Request\int_param('toto');
```
### Get the raw body
```php
echo Request\body();
```
### Get decoded JSON body or form values
If a form is submited, you got an array of values.
If the body is a JSON encoded string you got an array of the decoded JSON.
```php
print_r(Request\values());
```
### Get a form variable
```php
echo Request\value('myvariable');
```
### Get the content of a uploaded file
```php
echo Request\file_content('field_form_name');
```
### Check if the request is a POST
```php
if (Request\is_post()) {
...
}
```
### Check if the request is a GET
```php
if (Request\is_get()) {
...
}
```
### Get the request uri
```php
echo Request\uri();
```
Session
-------
### Open and close a session
The session cookie have the following settings:
- Cookie lifetime: 2678400 seconds (31 days)
- Limited to a specified path (http://domain/mywebapp/) or not (http://domain/)
- If the connection is HTTPS, the cookie use the secure flag
- The cookie is HttpOnly, not available from Javascript
Example:
```php
use PicoFarad\Session;
// Session start
Session\open('mywebappdirectory');
// Destroy the session
Session\close();
```
### Flash messages
Set the session variables: `$_SESSION['flash_message']` and `$_SESSION['flash_error_message']`.
In your template, use a helper to display and delete these messages.
```php
// Standard message
Session\flash('My message');
// Error message
Session\flash_error('My error message');
```

View File

@ -1,19 +0,0 @@
{
"name": "fguillot/picofarad",
"description": "Minimalist micro-framework",
"homepage": "https://github.com/fguillot/picoFarad",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Frédéric Guillot",
"homepage": "http://fredericguillot.com"
}
],
"require": {
"php": ">=5.3.0"
},
"autoload": {
"psr-0": {"PicoFarad": "lib/"}
}
}

View File

@ -1,3 +0,0 @@
.DS_Store
vendor/
*.py

View File

@ -1,19 +0,0 @@
language: php
php:
- 7.0
- 5.6
- 5.5
- 5.4
- 5.3
matrix:
fast_finish: true
allow_failures:
- php: 7.0
before_script:
- composer dump-autoload
script:
- phpunit

View File

@ -1,67 +0,0 @@
PicoFeed
========
PicoFeed was originally developed for [Miniflux](http://miniflux.net), a minimalist and open source news reader.
However, this library can be used inside any project.
PicoFeed is tested with a lot of different feeds and it's simple and easy to use.
[![Build Status](https://travis-ci.org/fguillot/picoFeed.svg?branch=master)](https://travis-ci.org/fguillot/picoFeed)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/fguillot/picoFeed/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/fguillot/picoFeed/?branch=master)
Features
--------
- Simple and fast
- Feed parser for Atom 1.0 and RSS 0.91, 0.92, 1.0 and 2.0
- Feed writer for Atom 1.0 and RSS 2.0
- Favicon fetcher
- Import/Export OPML subscriptions
- Content filter: HTML cleanup, remove pixel trackers and Ads
- Multiple HTTP client adapters: cURL or Stream Context
- Proxy support
- Content grabber: download from the original website the full content
- Enclosure detection
- RTL languages support
- License: MIT
Requirements
------------
- PHP >= 5.3
- libxml >= 2.7
- XML PHP extensions: DOM and SimpleXML
- cURL or Stream Context (`allow_url_fopen=On`)
- iconv extension
Authors
-------
- Original author: Frédéric Guillot
- Major Contributors:
- [Bernhard Posselt](https://github.com/Raydiation)
- [David Pennington](https://github.com/Xeoncross)
- [Mathias Kresin](https://github.com/mkresin)
Real world usage
----------------
- [Miniflux](http://miniflux.net)
- [Owncloud News](https://github.com/owncloud/news)
Documentation
-------------
- [Installation](docs/installation.markdown)
- [Running unit tests](docs/tests.markdown)
- [Feed parsing](docs/feed-parsing.markdown)
- [Feed creation](docs/feed-creation.markdown)
- [Favicon fetcher](docs/favicon.markdown)
- [OPML file importation](docs/opml-import.markdown)
- [OPML file exportation](docs/opml-export.markdown)
- [Image proxy](docs/image-proxy.markdown) (avoid SSL mixed content warnings)
- [Web scraping](docs/grabber.markdown)
- [Exceptions](docs/exceptions.markdown)
- [Debugging](docs/debugging.markdown)
- [Configuration](docs/config.markdown)

View File

@ -1,29 +0,0 @@
{
"name": "fguillot/picofeed",
"description": "Modern library to handle RSS/Atom feeds",
"homepage": "https://github.com/fguillot/picoFeed",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Frédéric Guillot"
}
],
"require": {
"php": ">=5.3.0",
"ext-iconv": "*",
"ext-dom": "*",
"ext-xml": "*",
"ext-libxml": "*",
"ext-SimpleXML": "*"
},
"suggest": {
"ext-curl": "PicoFeed will use cURL if present"
},
"autoload": {
"psr-0": {"PicoFeed": "lib/"}
},
"bin" : [
"picofeed"
]
}

View File

@ -1,306 +0,0 @@
Configuration
=============
How to use the Config object
----------------------------
To change the default parameters, you have to use the Config class.
Create a new instance and pass it to the Reader object like that:
```php
use PicoFeed\Reader\Reader;
use PicoFeed\Config\Config;
$config = new Config;
$config->setClientUserAgent('My custom RSS Reader')
->setProxyHostname('127.0.0.1')
->setProxyPort(8118);
$reader = new Reader($config);
...
```
HTTP Client parameters
----------------------
### Connection timeout
- Method name: `setClientTimeout()`
- Default value: 10 seconds
- Argument value: number of seconds (integer)
```php
$config->setClientTimeout(20); // 20 seconds
```
### User Agent
- Method name: `setClientUserAgent()`
- Default value: `PicoFeed (https://github.com/fguillot/picoFeed)`
- Argument value: string
```php
$config->setClientUserAgent('My RSS reader');
```
### Maximum HTTP redirections
- Method name: `setMaxRedirections()`
- Default value: 5
- Argument value: integer
```php
$config->setMaxRedirections(10);
```
### Maximum HTTP body response size
- Method name: `setMaxBodySize()`
- Default value: 2097152 (2MB)
- Argument value: value in bytes (integer)
```php
$config->setMaxBodySize(10485760); // 10MB
```
### Proxy hostname
- Method name: `setProxyHostname()`
- Default value: empty
- Argument value: string
```php
$config->setProxyHostname('proxy.example.org');
```
### Proxy port
- Method name: `setProxyPort()`
- Default value: 3128
- Argument value: port number (integer)
```php
$config->setProxyPort(8118);
```
### Proxy username
- Method name: `setProxyUsername()`
- Default value: empty
- Argument value: string
```php
$config->setProxyUsername('myuser');
```
### Proxy password
- Method name: `setProxyPassword()`
- Default value: empty
- Argument value: string
```php
$config->setProxyPassword('mysecret');
```
Content grabber
---------------
### Connection timeout
- Method name: `setGrabberTimeout()`
- Default value: 10 seconds
- Argument value: number of seconds (integer)
```php
$config->setGrabberTimeout(20); // 20 seconds
```
### User Agent
- Method name: `setGrabberUserAgent()`
- Default value: `PicoFeed (https://github.com/fguillot/picoFeed)`
- Argument value: string
```php
$config->setGrabberUserAgent('My content scraper');
```
### Add a rules folder
- Method name: `setGrabberRulesFolder()`
- Default value: `null`
- Argument value: string
```php
$config->setGrabberRulesFolder('/path/to/my/grabber/rules');
```
Parser
------
### Hash algorithm used for item id generation
- Method name: `setParserHashAlgo()`
- Default value: `sha256`
- Argument value: any value returned by the function `hash_algos()` (string)
- See: http://php.net/hash_algos
```php
$config->setParserHashAlgo('sha1');
```
### Disable item content filtering
- Method name: `setContentFiltering()`
- Default value: true (filtering is enabled by default)
- Argument value: boolean
```php
$config->setContentFiltering(false);
```
### Timezone
- Method name: `setTimezone()`
- Default value: UTC
- Argument value: See https://php.net/manual/en/timezones.php (string)
- Note: define the timezone for items/feeds
```php
$config->setTimezone('Europe/Paris');
```
Logging
-------
### Timezone
- Method name: `setTimezone()`
- Default value: UTC
- Argument value: See https://php.net/manual/en/timezones.php (string)
- Note: define the timezone for the logging class
```php
$config->setTimezone('Europe/Paris');
```
Filter
------
### Set the iframe whitelist (allowed iframe sources)
- Method name: `setFilterIframeWhitelist()`
- Default value: See the Filter class source code
- Argument value: array
```php
$config->setFilterIframeWhitelist(['http://www.youtube.com', 'http://www.vimeo.com']);
```
### Define HTML integer attributes
- Method name: `setFilterIntegerAttributes()`
- Default value: See the Filter class source code
- Argument value: array
```php
$config->setFilterIntegerAttributes(['width', 'height']);
```
### Add HTML attributes automatically
- Method name: `setFilterAttributeOverrides()`
- Default value: See the Filter class source code
- Argument value: array
```php
$config->setFilterAttributeOverrides(['a' => ['target' => '_blank']);
```
### Set the list of required attributes for tags
- Method name: `setFilterRequiredAttributes()`
- Default value: See the Filter class source code
- Argument value: array
- Note: If the required attributes are not there, the tag is stripped
```php
$config->setFilterRequiredAttributes(['a' => 'href', 'img' => 'src']);
```
### Set the resource blacklist (Ads blocker)
- Method name: `setFilterMediaBlacklist()`
- Default value: See the Filter class source code
- Argument value: array
- Note: Tags are stripped if they have those URLs
```php
$config->setFilterMediaBlacklist(['feeds.feedburner.com', 'share.feedsportal.com']);
```
### Define which attributes are used for external resources
- Method name: `setFilterMediaAttributes()`
- Default value: See the Filter class source code
- Argument value: array
```php
$config->setFilterMediaAttributes(['src', 'href']);
```
### Define the scheme whitelist
- Method name: `setFilterSchemeWhitelist()`
- Default value: See the Filter class source code
- Argument value: array
- See: http://en.wikipedia.org/wiki/URI_scheme
```php
$config->setFilterSchemeWhitelist(['http://', 'ftp://']);
```
### Define the tags and attributes whitelist
- Method name: `setFilterWhitelistedTags()`
- Default value: See the Filter class source code
- Argument value: array
- Note: Only those tags are allowed everything else is stripped
```php
$config->setFilterWhitelistedTags(['a' => ['href'], 'img' => ['src', 'title']]);
```
### Define a image proxy url
- Method name: `setFilterImageProxyUrl()`
- Default value: Empty
- Argument value: string
```php
$config->setFilterImageProxyUrl('http://myproxy.example.org/?url=%s');
```
### Define a image proxy callback
- Method name: `setFilterImageProxyCallback()`
- Default value: null
- Argument value: Closure
```php
$config->setFilterImageProxyCallback(function ($image_url) {
$key = hash_hmac('sha1', $image_url, 'secret');
return 'https://mypublicproxy/'.$key.'/'.urlencode($image_url);
});
```
### Define image proxy protocol restriction
- Method name: `setFilterImageProxyProtocol()`
- Default value: Empty (all protocols)
- Argument value: string
```php
$config->setFilterImageProxyProtocol('http');
```

View File

@ -1,102 +0,0 @@
Debugging
=========
Logging
-------
PicoFeed can log **in memory** the execution flow, if a feed doesn't work correctly it's easy to see what is wrong.
### Enable/disable logging
The logging is **disabled by default** to avoid unnecessary memory usage.
Enable logging:
```php
use PicoFeed\Logging\Logger;
Logger::enable();
// or change the flag value
Logger::$enable = true;
```
### Reading messages
```php
use PicoFeed\Logging\Logger;
// All messages are stored inside an Array
print_r(Logger::getMessages());
```
You will got an output like that:
```php
Array
(
[0] => Fetch URL: http://petitcodeur.fr/feed.xml
[1] => Etag:
[2] => Last-Modified:
[3] => cURL total time: 0.711378
[4] => cURL dns lookup time: 0.001064
[5] => cURL connect time: 0.100733
[6] => cURL speed download: 74825
[7] => HTTP status code: 200
[8] => HTTP headers: Set-Cookie => start=R2701971637; path=/; expires=Sat, 06-Jul-2013 05:16:33 GMT
[9] => HTTP headers: Date => Sat, 06 Jul 2013 03:55:52 GMT
[10] => HTTP headers: Content-Type => application/xml
[11] => HTTP headers: Content-Length => 53229
[12] => HTTP headers: Connection => close
[13] => HTTP headers: Server => Apache
[14] => HTTP headers: Last-Modified => Tue, 02 Jul 2013 03:26:02 GMT
[15] => HTTP headers: ETag => "393e79c-cfed-4e07ee78b2680"
[16] => HTTP headers: Accept-Ranges => bytes
....
)
```
### Remove messages
All messages are stored in memory, if you need to clear them just call the method `Logger::deleteMessages()`:
```php
Logger::deleteMessages();
```
Command line utility
====================
PicoFeed provides a basic command line tool to debug feeds quickly.
The tool is located in the root directory project.
### Usage
```bash
$ ./picofeed
Usage:
./picofeed feed <feed-url> # Parse a feed a dump the ouput on stdout
./picofeed debug <feed-url> # Display all logging messages for a feed
./picofeed item <feed-url> <item-id> # Fetch only one item
./picofeed nofilter <feed-url> <item-id> # Fetch an item but with no content filtering
```
### Example
```bash
$ ./picofeed debug https://linuxfr.org/
Exception thrown ===> "Invalid SSL certificate"
Array
(
[0] => [2014-11-08 14:04:14] PicoFeed\Client\Curl Fetch URL: https://linuxfr.org/
[1] => [2014-11-08 14:04:14] PicoFeed\Client\Curl Etag provided:
[2] => [2014-11-08 14:04:14] PicoFeed\Client\Curl Last-Modified provided:
[3] => [2014-11-08 14:04:16] PicoFeed\Client\Curl cURL total time: 1.850634
[4] => [2014-11-08 14:04:16] PicoFeed\Client\Curl cURL dns lookup time: 0.00093
[5] => [2014-11-08 14:04:16] PicoFeed\Client\Curl cURL connect time: 0.115213
[6] => [2014-11-08 14:04:16] PicoFeed\Client\Curl cURL speed download: 0
[7] => [2014-11-08 14:04:16] PicoFeed\Client\Curl cURL effective url: https://linuxfr.org/
[8] => [2014-11-08 14:04:16] PicoFeed\Client\Curl cURL error: SSL certificate problem: Invalid certificate chain
)
```

View File

@ -1,28 +0,0 @@
Exceptions
==========
All exceptions inherits from the standard `Exception` class.
### Library Exceptions
- `PicoFeed\PicoFeedException`: Base class exception for the library
### Client Exceptions
- `PicoFeed\Client\ClientException`: Base exception class for the Client class
- `PicoFeed\Client\InvalidCertificateException`: Invalid SSL certificate
- `PicoFeed\Client\InvalidUrlException`: Malformed URL, page not found (404), unable to establish a connection
- `PicoFeed\Client\MaxRedirectException`: Maximum of HTTP redirections reached
- `PicoFeed\Client\MaxSizeException`: The response size exceeds to maximum allowed
- `PicoFeed\Client\TimeoutException`: Connection timeout
### Parser Exceptions
- `PicoFeed\Parser\ParserException`: Base exception class for the Parser class
- `PicoFeed\Parser\MalformedXmlException`: XML Parser error
### Reader Exceptions
- `PicoFeed\Reader\ReaderException`: Base exception class for the Reader
- `PicoFeed\Reader\SubscriptionNotFoundException`: Unable to find a feed for the given website
- `PicoFeed\Reader\UnsupportedFeedFormatException`: Unable to detect the feed format

View File

@ -1,96 +0,0 @@
Favicon fetcher
===============
Find and download the favicon
-----------------------------
```php
use PicoFeed\Reader\Favicon;
$favicon = new Favicon;
// The icon link is https://bits.wikimedia.org/favicon/wikipedia.ico
$icon_link = $favicon->find('https://en.wikipedia.org/');
$icon_content = $favicon->getContent();
```
PicoFeed will try first to find the favicon from the meta tags and fallback to the `favicon.ico` located in the website's root if nothing is found.
- `Favicon::find()` returns the favicon absolute url or an empty string if nothing is found.
- `Favicon::getContent()` returns the favicon file content (binary content)
When the HTML page is parsed, relative links and protocol relative links are converted to absolute url.
Download a known favicon
-----------------------
It's possible to download a known favicon using the second optional parameter of Favicon::find(). The link to the favicon can be a relative or protocol relative url as well, but it has to be relative to the specified website.
If the requested favicon could not be found, the HTML of the website is parsed instead, with the fallback to the `favicon.ico` located in the website's root.
```php
use PicoFeed\Reader\Favicon;
$favicon = new Favicon;
$icon_link = $favicon->find('https://en.wikipedia.org/','https://bits.wikimedia.org/favicon/wikipedia.ico');
$icon_content = $favicon->getContent();
```
Get Favicon file type
---------------------
It's possible to fetch the image type, this information come from the Content-Type HTTP header:
```php
$favicon = new Favicon;
$favicon->find('http://example.net/');
echo $favicon->getType();
// Will output the content type, by example "image/png"
```
Get the Favicon as Data URI
---------------------------
You can also get the whole image as Data URI.
It's useful if you want to store the icon in your database and avoid too many HTTP requests.
```php
$favicon = new Favicon;
$favicon->find('http://example.net/');
echo $favicon->getDataUri();
// Output something like that: data:image/png;base64,iVBORw0KGgoAAAANSUh.....
```
See: http://en.wikipedia.org/wiki/Data_URI_scheme
Check if a favicon link exists
------------------------------
```php
use PicoFeed\Reader\Favicon;
$favicon = new Favicon;
// Return true if the file exists
var_dump($favicon->exists('http://php.net/favicon.ico'));
```
Use personalized HTTP settings
------------------------------
Like other classes, the Favicon class support the Config object as constructor argument:
```php
use PicoFeed\Config\Config;
use PicoFeed\Reader\Favicon;
$config = new Config;
$config->setClientUserAgent('My RSS Reader');
$favicon = new Favicon($config);
$favicon->find('https://github.com');
```

View File

@ -1,74 +0,0 @@
Feed creation
=============
PicoFeed can also generate Atom and RSS feeds.
Generate RSS 2.0 feed
----------------------
```php
use PicoFeed\Syndication\Rss20;
$writer = new Rss20();
$writer->title = 'My site';
$writer->site_url = 'http://boo/';
$writer->feed_url = 'http://boo/feed.atom';
$writer->author = array(
'name' => 'Me',
'url' => 'http://me',
'email' => 'me@here'
);
$writer->items[] = array(
'title' => 'My article 1',
'updated' => strtotime('-2 days'),
'url' => 'http://foo/bar',
'summary' => 'Super summary',
'content' => '<p>content</p>'
);
$writer->items[] = array(
'title' => 'My article 2',
'updated' => strtotime('-1 day'),
'url' => 'http://foo/bar2',
'summary' => 'Super summary 2',
'content' => '<p>content 2 &nbsp; &copy; 2015</p>',
'author' => array(
'name' => 'Me too',
)
);
$writer->items[] = array(
'title' => 'My article 3',
'url' => 'http://foo/bar3'
);
echo $writer->execute();
```
Generate Atom feed
------------------
```php
use PicoFeed\Syndication\Atom;
$writer = new Atom();
$writer->title = 'My site';
$writer->site_url = 'http://boo/';
$writer->feed_url = 'http://boo/feed.atom';
$writer->author = array(
'name' => 'Me',
'url' => 'http://me',
'email' => 'me@here'
);
$writer->items[] = array(
'title' => 'My article 1',
'updated' => strtotime('-2 days'),
'url' => 'http://foo/bar',
'summary' => 'Super summary',
'content' => '<p>content</p>'
);
echo $writer->execute();
```

View File

@ -1,333 +0,0 @@
Feed parsing
============
Parsing a subscription
----------------------
```php
use PicoFeed\Reader\Reader;
use PicoFeed\PicoFeedException;
try {
$reader = new Reader;
// Return a resource
$resource = $reader->download('http://linuxfr.org/news.atom');
// Return the right parser instance according to the feed format
$parser = $reader->getParser(
$resource->getUrl(),
$resource->getContent(),
$resource->getEncoding()
);
// Return a Feed object
$feed = $parser->execute();
// Print the feed properties with the magic method __toString()
echo $feed;
}
catch (PicoFeedException $e) {
// Do Something...
}
```
- The Reader class is the entry point for feed reading
- The method `download()` fetch the remote content and return a resource, an instance of `PicoFeed\Client\Client`
- The method `getParser()` returns a Parser instance according to the feed format Atom, Rss 2.0...
- The parser itself returns a `Feed` object that contains feed and item properties
Output:
```bash
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::language = en-US
Feed::description =
Feed::logo =
Feed::date = Thu, 26 Feb 15 09:33:08 +0100
Feed::isRTL() = false
Feed::items = 15 items
----
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 = alenvers
Item::enclosure_url =
Item::enclosure_type =
Item::date = Thu, 26 Feb 15 09:33:08 +0100
Item::isRTL() = false
Item::content = 6452 bytes
....
```
Get the list of available subscriptions for a website
-----------------------------------------------------
The example below will returns all available subscriptions for the website:
```php
use PicoFeed\Reader\Reader;
try {
$reader = new Reader;
$resource = $reader->download('http://www.cnn.com');
$feeds = $reader->find(
$resource->getUrl(),
$resource->getContent()
);
print_r($feeds);
}
catch (PicoFeedException $e) {
// Do something...
}
```
Output:
```php
Array
(
[0] => http://rss.cnn.com/rss/cnn_topstories.rss
[1] => http://rss.cnn.com/rss/cnn_latest.rss
)
```
Feed discovery and parsing
--------------------------
This example will discover automatically the subscription and parse the feed:
```php
try {
$reader = new Reader;
$resource = $reader->discover('http://linuxfr.org');
$parser = $reader->getParser(
$resource->getUrl(),
$resource->getContent(),
$resource->getEncoding()
);
$feed = $parser->execute();
echo $feed;
}
catch (PicoFeedException $e) {
}
```
HTTP caching
------------
PicoFeed supports HTTP caching to avoid unnecessary processing.
1. After the first download, save in your database the values of the Etag and LastModified HTTP headers
2. For the next requests, provide those values to the `download()` method and check if the feed was modified or not
Here an example:
```php
try {
// Fetch from your database the previous values of the Etag and LastModified headers
$etag = '...';
$last_modified = '...';
$reader = new Reader;
// Provide those values to the download method
$resource = $reader->download('http://linuxfr.org/news.atom', $last_modified, $etag);
// Return true if the remote content has changed
if ($resource->isModified()) {
$parser = $reader->getParser(
$resource->getUrl(),
$resource->getContent(),
$resource->getEncoding()
);
$feed = $parser->execute();
// Save your feed in your database
// ...
// Store the Etag and the LastModified headers in your database for the next requests
$etag = $resource->getEtag();
$last_modified = $resource->getLastModified();
// ...
}
else {
echo 'Not modified, nothing to do!';
}
}
catch (PicoFeedException $e) {
// Do something...
}
```
HTTP basic auth
---------------
If a feed requires basic auth headers, you can pass them as parameters to the **download** method, e.g.:
```php
try {
$reader = new Reader;
$user = 'john';
$password = 'doe';
// Provide those values to the download method
$resource = $reader->download('http://linuxfr.org/news.atom', '', '', $user, $password);
// Return true if the remote content has changed
if ($resource->isModified()) {
$parser = $reader->getParser(
$resource->getUrl(),
$resource->getContent(),
$resource->getEncoding()
);
$feed = $parser->execute();
// Save your feed in your database
// ...
}
else {
echo 'Not modified, nothing to do!';
}
}
catch (PicoFeedException $e) {
// Do something...
}
```
Custom regex filters
--------------------
In case you want modify the content with a simple regex, you can create a rule file named after the domain of the feed's link attribute. For the feed pointing to **http://www.twogag.com/** the file is stored under **Rules/twogag.com.php**
For filtering, only the array with the key **filter** will be considered. The first level key is a preg_match regex that will match the sub url, e.g. to only match a feed whose link attribute points to **twogag.com/test**, the regex could look like **%/test.*%**. The second level array contains a list of search and replace strings, which will be passed to the preg\_replace function. The first string is the argument that should be matched, the second is the replacement.
To replace all occurences of links to smaller images for twogag, the following rule can be used:
```php
<?php
return array(
'filter' => array(
'%.*%' => array(
"%http://www.twogag.com/comics-rss/([^.]+)\\.jpg%" =>
"http://www.twogag.com/comics/$1.jpg"
)
)
);
```
Feed and item properties
------------------------
```php
// Feed object
$feed->getId(); // Unique feed id
$feed->getTitle(); // Feed title
$feed->getFeedUrl(); // Feed url
$feed->getSiteUrl(); // Website url
$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)
$feed->getItems(); // List of item objects
// Item object
$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 (DateTime object)
$feed->items[0]->getLanguage(); // Item language
$feed->items[0]->getAuthor(); // Item author
$feed->items[0]->getEnclosureUrl(); // Enclosure url
$feed->items[0]->getEnclosureType(); // Enclosure mime-type (audio/mp3, image/png...)
$feed->items[0]->getContent(); // Item content (filtered or raw)
$feed->items[0]->isRTL(); // Return true if the item language is Right-To-Left
```
Get raw XML tags/attributes or non standard tags for items
----------------------------------------------------------
The getTag function returns an array with all values of matching tags. If nothing can be found, an empty array is returned. In case of errors, the return value is false.
Get the original `guid` tag for RSS 2.0 feeds:
```php
$values = $feed->items[0]->getTag('guid');
print_r ($values);
```
Get a specific attribute value:
```php
$values = $feed->items[1]->getTag('category', 'term');
print_r ($values);
```
Get value of namespaced tag:
```php
if (array_key_exists('wfw', $feed->items[0]->namespaces)) {
$values = $feed->items[1]->getTag('wfw:commentRss');
print_r ($values);
}
```
Get attribute value of a namespaced tag:
```php
if (array_key_exists('media', $feed->items[0]->namespaces)) {
$values = $feed->items[0]->getTag('media:content', 'url');
print_r ($values);
}
```
Get the xml of the item (returns a SimpleXMLElement instance):
```php
$simplexml = $feed->items[0]->xml;
```
Get the list of namespaces:
```php
print_r($feed->items[0]->namespaces);
```
RTL language detection
----------------------
Use the method `Item::isRTL()` to test if an item is RTL or not:
```php
var_dump($item->isRTL()); // true or false
```
Known RTL languages are:
- Arabic (ar-**)
- Farsi (fa-**)
- Urdu (ur-**)
- Pashtu (ps-**)
- Syriac (syr-**)
- Divehi (dv-**)
- Hebrew (he-**)
- Yiddish (yi-**)

View File

@ -1,195 +0,0 @@
Web scraper
===========
The web scraper is useful for feeds that display only a summary of articles, the scraper can download and parse the full content from the original website.
How the content grabber works?
------------------------------
1. Try with rules first (XPath queries) for the domain name (see `PicoFeed\Rules\`)
2. Try to find the text content by using common attributes for class and id
3. Finally, if nothing is found, the feed content is displayed
**The best results are obtained with XPath rules file.**
Standalone usage
----------------
Fetch remote content:
```php
<?php
use PicoFeed\Config\Config;
use PicoFeed\Scraper\Scraper;
$config = new Config;
$grabber = new Scraper($config)
$grabber->setUrl($url);
$grabber->execute();
// Get raw HTML content
echo $grabber->getRawContent();
// Get relevant content
echo $grabber->getRelevantContent();
// Get filtered relevant content
echo $grabber->getFilteredContent();
// Return true if there is relevant content
var_dump($grabber->hasRelevantContent());
```
Parse HTML content:
```php
<?php
$grabber = new Scraper($config);
$grabber->setRawContent($html);
$grabber->execute();
```
Fetch full item contents during feed parsing
--------------------------------------------
Before parsing all items, just call the method `$parser->enableContentGrabber()`:
```php
<?php
use PicoFeed\Reader\Reader;
use PicoFeed\PicoFeedException;
try {
$reader = new Reader;
// Return a resource
$resource = $reader->download('http://www.egscomics.com/rss.php');
// Return the right parser instance according to the feed format
$parser = $reader->getParser(
$resource->getUrl(),
$resource->getContent(),
$resource->getEncoding()
);
// Enable content grabber before parsing items
$parser->enableContentGrabber();
// Return a Feed object
$feed = $parser->execute();
}
catch (PicoFeedException $e) {
// Do Something...
}
```
When the content scraper is enabled, everything will be slower.
**For each item a new HTTP request is made** and the HTML downloaded is parsed with XML/XPath.
Configuration
-------------
### Enable content grabber for items
- Method name: `enableContentGrabber()`
- Default value: false (also fetch content if no rule file exist)
- Argument value: bool (true scrape only webpages which have a rule file)
```php
$parser->enableContentGrabber(false);
```
### Ignore item urls for the content grabber
- Method name: `setGrabberIgnoreUrls()`
- Default value: empty (fetch all item urls)
- Argument value: array (list of item urls to ignore)
```php
$parser->setGrabberIgnoreUrls(['http://foo', 'http://bar']);
```
How to write a grabber rules file?
----------------------------------
Add a PHP file to the directory `PicoFeed\Rules`, the filename must be the same as the domain name:
Example with the BBC website, `www.bbc.co.uk.php`:
```php
<?php
return array(
'grabber' => array(
'%.*%' => array(
'test_url' => 'http://www.bbc.co.uk/news/world-middle-east-23911833',
'body' => array(
'//div[@class="story-body"]',
),
'strip' => array(
'//script',
'//form',
'//style',
'//*[@class="story-date"]',
'//*[@class="story-header"]',
'//*[@class="story-related"]',
'//*[contains(@class, "byline")]',
'//*[contains(@class, "story-feature")]',
'//*[@id="video-carousel-container"]',
'//*[@id="also-related-links"]',
'//*[contains(@class, "share") or contains(@class, "hidden") or contains(@class, "hyper")]',
)
)
)
);
```
Each rule file can contain multiple rules, based so links to different website URLs can be handled differently. The first level key is a regex, which will be matched against the full path of the URL using **preg_match**, e.g. for **http://www.bbc.co.uk/news/world-middle-east-23911833?test=1** the URL that would be matched is **/news/world-middle-east-23911833?test=1**
Each rule has the following keys:
* **body**: An array of xpath expressions which will be extracted from the page
* **strip**: An array of xpath expressions which will be removed from the matched content
* **test_url**: A test url to a matching page to test the grabber
Don't forget to send a pull request or a ticket to share your contribution with everybody,
**A more complex example**:
Let's say you wanted to extract a div with the id **video** if the article points to an URL like **http://comix.com/videos/423**, **audio** if the article points to an URL like **http://comix.com/podcasts/5** and all other links to the page should instead take the div with the id **content**. The following rulefile would fit that requirement and would be stored in a file called **lib/PicoFeed/Rules/comix.com.php**:
```php
return array(
'grabber' => array(
'%^/videos.*%' => array(
'test_url' => 'http://comix.com/videos/423',
'body' => array(
'//div[@id="video"]',
),
'strip' => array()
),
'%^/podcasts.*%' => array(
'test_url' => 'http://comix.com/podcasts/5',
'body' => array(
'//div[@id="audio"]',
),
'strip' => array()
),
'%.*%' => array(
'test_url' => 'http://comix.com/blog/1',
'body' => array(
'//div[@id="content"]',
),
'strip' => array()
)
)
);
```
List of content grabber rules
-----------------------------
Rules are stored inside the directory [lib/PicoFeed/Rules](https://github.com/fguillot/picoFeed/tree/master/lib/PicoFeed/Rules)

View File

@ -1,67 +0,0 @@
Image Proxy
===========
To prevent mixed content warnings on SSL pages served from your RSS reader you might want to use an assets proxy.
Images url will be rewritten to be downloaded through the proxy.
Example:
```html
<img src="http://example.org/image.png"/>
```
Can be rewritten like that:
```html
<img src="http://myproxy.example.org/?url=http%3A%2F%2Fexample.org%2Fimage.png"/>
```
Currently this feature is only compatible with images.
There is several open source SSL image proxy available like [Camo](https://github.com/atmos/camo).
You can also write your own proxy.
Usage
-----
There two different ways to use this feature, define a proxy url or a callback.
### Define a proxy url
A proxy url must be defined with a placeholder `%s`.
The placeholder will be replaced by the image source url encoded (RFC 3986).
In PHP, the url can be decoded with the function `rawurldecode()`.
```php
$config = new Config;
$config->setFilterImageProxyUrl('http://myproxy.example.org/?url=%s');
```
Will rewrite the image source like that:
```html
<img src="http://myproxy.example.org/?url=http%3A%2F%2Fexample.org%2Fimage.png"/>
```
### Define a callback
Your callback will be called each time an image url need to be rewritten.
The first argument is the original image url and your function must returns the new image url.
Here an example if your proxy need a shared secret key:
```php
$config = new Config;
$config->setFilterImageProxyCallback(function ($image_url) {
$key = hash_hmac('sha1', $image_url, 'secret');
return 'https://mypublicproxy/'.$key.'/'.rawurlencode($image_url);
});
```
Will generate an image url like that:
```html
<img src="https://mypublicproxy/4924964043f3119b3cf2b07b1922d491bcc20092/http%3A%2F%2Ffoo%2Fimage.png"/>
```

View File

@ -1,50 +0,0 @@
Installation
============
Versions
--------
- Development version: master
- Stable version: use the last tag
Installation with Composer
--------------------------
```bash
composer require fguillot/picofeed @stable
```
And download the code:
```bash
composer install # or update
```
Usage example with the Composer autoloader:
```php
<?php
require 'vendor/autoload.php';
use PicoFeed\Reader\Reader;
try {
$reader = new Reader;
$resource = $reader->download('http://linuxfr.org/news.atom');
$parser = $reader->getParser(
$resource->getUrl(),
$resource->getContent(),
$resource->getEncoding()
);
$feed = $parser->execute();
echo $feed;
}
catch (Exception $e) {
// Do something...
}
```

View File

@ -1,46 +0,0 @@
OPML export
===========
Example with no categories
--------------------------
```php
use PicoFeed\Serialization\Export;
$feeds = array(
array(
'title' => 'Site title',
'description' => 'Optional description',
'site_url' => 'http://petitcodeur.fr/',
'site_feed' => 'http://petitcodeur.fr/feed.xml'
)
);
$export = new Export($feeds);
$opml = $export->execute();
echo $opml; // XML content
```
Example with categories
-----------------------
```php
use PicoFeed\Serialization\Export;
$feeds = array(
'my category' => array(
array(
'title' => 'Site title',
'description' => 'Optional description',
'site_url' => 'http://petitcodeur.fr/',
'site_feed' => 'http://petitcodeur.fr/feed.xml'
)
)
);
$export = new Export($feeds);
$opml = $export->execute();
echo $opml; // XML content
```

View File

@ -1,19 +0,0 @@
Import OPML file
================
Importing a list of subscriptions is pretty straightforward:
```php
use PicoFeed\Serialization\Import;
$opml = file_get_contents('mySubscriptions.opml');
$import = new Import($opml);
$entries = $import->execute();
if ($entries !== false) {
print_r($entries);
}
```
The method `execute()` return `false` if there is a parsing error.

View File

@ -1,14 +0,0 @@
Running unit tests
==================
If the autoloader is not yet installed run:
```php
composer dump-autoload
```
Then run:
```php
phpunit tests
```

View File

@ -4,7 +4,7 @@ return array(
'%.*%' => array( '%.*%' => array(
'test_url' => 'http://www.phoronix.com/scan.php?page=article&item=amazon_ec2_bare&num=1', 'test_url' => 'http://www.phoronix.com/scan.php?page=article&item=amazon_ec2_bare&num=1',
'body' => array( 'body' => array(
'//div[@class="KonaBody"]', '//div[@class="content"]',
), ),
'strip' => array() 'strip' => array()
) )

View File

@ -0,0 +1,8 @@
<?php
return array(
'filter' => array(
'%.*%' => array(
'%-150x150%' => '',
)
)
);

View File

@ -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")]'
)
)
)
);

View File

@ -2,7 +2,7 @@
return array( return array(
'filter' => array( 'filter' => array(
'%.*%' => array( '%.*%' => array(
'%-\\d+x\\d+%' => "", '%-150x150%' => '',
) )
) )
); );

View File

@ -2,7 +2,7 @@
return array( return array(
'filter' => array( 'filter' => array(
'%.*%' => array( '%.*%' => array(
'%-\\d+x\\d+%' => "", '%-150x150%' => '',
) )
) )
); );

View File

@ -2,7 +2,7 @@
return array( return array(
'filter' => array( 'filter' => array(
'%.*%' => array( '%.*%' => array(
'%-\\d+x\\d+%' => "", '%-150x150%' => '',
) )
) )
); );

View File

@ -2,7 +2,7 @@
return array( return array(
'filter' => array( 'filter' => array(
'%.*%' => array( '%.*%' => array(
'%-\\d+x\\d+%' => "", '%-150x150%' => '',
) )
) )
); );

View File

@ -5,8 +5,9 @@ return array(
'test_url' => 'http://www.neustadt-ticker.de/36480/aktuell/nachrichten/buergerbuero-neustadt-ab-heute-wieder-geoeffnet', 'test_url' => 'http://www.neustadt-ticker.de/36480/aktuell/nachrichten/buergerbuero-neustadt-ab-heute-wieder-geoeffnet',
'body' => array('//div[contains(@class,"article")]/div[@class="PostContent" and *[not(contains(@class, "navigation"))]]'), 'body' => array('//div[contains(@class,"article")]/div[@class="PostContent" and *[not(contains(@class, "navigation"))]]'),
'strip' => array( 'strip' => array(
'//*[@id="wp_rp_first"]' '//*[@id="wp_rp_first"]',
'//*[@class="yarpp-related"]'
), ),
) )
) )
); );

View File

@ -2,7 +2,7 @@
return array( return array(
'filter' => array( 'filter' => array(
'%.*%' => array( '%.*%' => array(
'%-\\d+x\\d+%' => "", '%-150x150%' => '',
) )
) )
); );

View File

@ -1,10 +0,0 @@
<?php
return array(
'grabber' => array(
'%.*%' => array(
'body' => array('//div[@class="entry-content"]'),
'strip' => array(),
'test_url' => 'http://voz.vn/2015/06/06/muon-trai-nghiem-bphone-hay-den-fpt-shop/',
)
),
);

View File

@ -1,7 +0,0 @@
<phpunit bootstrap="./vendor/autoload.php">
<testsuites>
<testsuite name="PicoFeed">
<directory>tests</directory>
</testsuite>
</testsuites>
</phpunit>

View File

@ -1,145 +0,0 @@
<?php
namespace PicoFeed\Client;
use PHPUnit_Framework_TestCase;
class ClientTest extends PHPUnit_Framework_TestCase
{
/**
* @group online
*/
public function testDownload()
{
$client = Client::getInstance();
$client->setUrl('http://php.net/robots.txt');
$client->execute();
$this->assertTrue($client->isModified());
$this->assertNotEmpty($client->getContent());
$this->assertNotEmpty($client->getEtag());
$this->assertNotEmpty($client->getLastModified());
}
/**
* @runInSeparateProcess
* @group online
*/
public function testPassthrough()
{
$client = Client::getInstance();
$client->setUrl('https://miniflux.net/favicon.ico');
$client->enablePassthroughMode();
$client->execute();
$this->expectOutputString(file_get_contents('tests/fixtures/miniflux_favicon.ico'));
}
/**
* @group online
*/
public function testCacheBothHaveToMatch()
{
$client = Client::getInstance();
$client->setUrl('http://php.net/robots.txt');
$client->execute();
$etag = $client->getEtag();
$client = Client::getInstance();
$client->setUrl('http://php.net/robots.txt');
$client->setEtag($etag);
$client->execute();
$this->assertTrue($client->isModified());
}
/**
* @group online
*/
public function testCacheEtag()
{
$client = Client::getInstance();
$client->setUrl('http://php.net/robots.txt');
$client->execute();
$etag = $client->getEtag();
$lastModified = $client->getLastModified();
$client = Client::getInstance();
$client->setUrl('http://php.net/robots.txt');
$client->setEtag($etag);
$client->setLastModified($lastModified);
$client->execute();
$this->assertFalse($client->isModified());
}
/**
* @group online
*/
public function testCacheLastModified()
{
$client = Client::getInstance();
$client->setUrl('http://miniflux.net/humans.txt');
$client->execute();
$lastmod = $client->getLastModified();
$client = Client::getInstance();
$client->setUrl('http://miniflux.net/humans.txt');
$client->setLastModified($lastmod);
$client->execute();
$this->assertFalse($client->isModified());
}
/**
* @group online
*/
public function testCacheBoth()
{
$client = Client::getInstance();
$client->setUrl('http://miniflux.net/humans.txt');
$client->execute();
$lastmod = $client->getLastModified();
$etag = $client->getEtag();
$client = Client::getInstance();
$client->setUrl('http://miniflux.net/humans.txt');
$client->setLastModified($lastmod);
$client->setEtag($etag);
$client->execute();
$this->assertFalse($client->isModified());
}
/**
* @group online
*/
public function testCharset()
{
$client = Client::getInstance();
$client->setUrl('http://php.net/');
$client->execute();
$this->assertEquals('utf-8', $client->getEncoding());
$client = Client::getInstance();
$client->setUrl('http://php.net/robots.txt');
$client->execute();
$this->assertEquals('', $client->getEncoding());
}
/**
* @group online
*/
public function testContentType()
{
$client = Client::getInstance();
$client->setUrl('http://miniflux.net/assets/img/favicon.png');
$client->execute();
$this->assertEquals('image/png', $client->getContentType());
$client = Client::getInstance();
$client->setUrl('http://miniflux.net/');
$client->execute();
$this->assertEquals('text/html; charset=utf-8', $client->getContentType());
}
}

View File

@ -1,74 +0,0 @@
<?php
namespace PicoFeed\Client;
use PHPUnit_Framework_TestCase;
class CurlTest extends PHPUnit_Framework_TestCase
{
/**
* @group online
*/
public function testDownload()
{
$client = new Curl;
$client->setUrl('http://miniflux.net/index.html');
$result = $client->doRequest();
$this->assertTrue(is_array($result));
$this->assertEquals(200, $result['status']);
$this->assertEquals('<!DOC', substr($result['body'], 0, 5));
$this->assertEquals('text/html; charset=utf-8', $result['headers']['Content-Type']);
}
/**
* @runInSeparateProcess
* @group online
*/
public function testPassthrough()
{
$client = new Curl;
$client->setUrl('https://miniflux.net/favicon.ico');
$client->enablePassthroughMode();
$client->doRequest();
$this->expectOutputString(file_get_contents('tests/fixtures/miniflux_favicon.ico'));
}
/**
* @group online
*/
public function testRedirect()
{
$client = new Curl;
$client->setUrl('http://rss.feedsportal.com/c/629/f/502199/s/42e50391/sc/44/l/0L0S0A1net0N0Ceditorial0C6437220Candroid0Egoogle0Enow0Es0Eouvre0Eaux0Eapplications0Etierces0C0T0Dxtor0FRSS0E16/story01.htm');
$result = $client->doRequest();
$this->assertTrue(is_array($result));
$this->assertEquals(200, $result['status']);
$this->assertEquals('<!DOCTYPE', substr($result['body'], 0, 9));
$this->assertEquals('text/html; charset=utf-8', $result['headers']['Content-Type']);
$this->assertEquals('http://www.01net.com/actualites/android-google-now-s-ouvre-aux-applications-tierces-643722.html', $client->getUrl());
}
/**
* @expectedException PicoFeed\Client\InvalidCertificateException
* @group online
*/
public function testSSL()
{
$client = new Curl;
$client->setUrl('https://www.mjvmobile.com.br');
$client->doRequest();
}
/**
* @expectedException PicoFeed\Client\InvalidUrlException
*/
public function testBadUrl()
{
$client = new Curl;
$client->setUrl('http://12345gfgfgf');
$client->doRequest();
}
}

View File

@ -1,19 +0,0 @@
<?php
namespace PicoFeed\Client;
use PHPUnit_Framework_TestCase;
class HttpHeadersTest extends PHPUnit_Framework_TestCase
{
public function testHttpHeadersSet() {
$headers = new HttpHeaders(array('Content-Type' => 'test'));
$this->assertEquals('test', $headers['content-typE']);
$this->assertTrue(isset($headers['ConTent-Type']));
unset($headers['Content-Type']);
$this->assertFalse(isset($headers['ConTent-Type']));
}
}

View File

@ -1,91 +0,0 @@
<?php
namespace PicoFeed\Client;
use PHPUnit_Framework_TestCase;
class StreamTest extends PHPUnit_Framework_TestCase
{
/**
* @group online
*/
public function testChunkedResponse()
{
$client = new Stream;
$client->setUrl('http://www.reddit.com/r/dwarffortress/.rss');
$result = $client->doRequest();
$this->assertEquals('</rss>', substr($result['body'], -6));
}
/**
* @group online
*/
public function testDownload()
{
$client = new Stream;
$client->setUrl('https://github.com/fguillot/picoFeed');
$result = $client->doRequest();
$this->assertEquals(200, $result['status']);
$this->assertEquals('text/html; charset=utf-8', $result['headers']['Content-Type']);
$this->assertEquals('<!DOCTYPE html>', substr(trim($result['body']), 0, 15));
$this->assertEquals('</html>', substr(trim($result['body']), -7));
}
/**
* @runInSeparateProcess
* @group online
*/
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'));
}
/**
* @group online
*/
public function testRedirect()
{
$client = new Stream;
$client->setUrl('http://rss.feedsportal.com/c/629/f/502199/s/42e50391/sc/44/l/0L0S0A1net0N0Ceditorial0C6437220Candroid0Egoogle0Enow0Es0Eouvre0Eaux0Eapplications0Etierces0C0T0Dxtor0FRSS0E16/story01.htm');
$result = $client->doRequest();
$this->assertTrue(is_array($result));
$this->assertEquals(200, $result['status']);
$this->assertEquals('<!DOCTYPE', substr($result['body'], 0, 9));
$this->assertEquals('text/html; charset=utf-8', $result['headers']['Content-Type']);
$this->assertEquals('http://www.01net.com/actualites/android-google-now-s-ouvre-aux-applications-tierces-643722.html', $client->getUrl());
}
/**
* @expectedException PicoFeed\Client\InvalidUrlException
*/
public function testBadUrl()
{
$client = new Stream;
$client->setUrl('http://12345gfgfgf');
$client->setTimeout(1);
$client->doRequest();
}
/**
* @group online
*/
public function testDecodeGzip()
{
if (function_exists('gzdecode')) {
$client = new Stream;
$client->setUrl('https://github.com/fguillot/picoFeed');
$result = $client->doRequest();
$this->assertEquals('gzip', $result['headers']['Content-Encoding']);
$this->assertEquals('<!DOC', substr(trim($result['body']), 0, 5));
}
}
}

View File

@ -1,298 +0,0 @@
<?php
namespace PicoFeed\Client;
use PHPUnit_Framework_TestCase;
class UrlTest extends PHPUnit_Framework_TestCase
{
public function testHasScheme()
{
$url = new Url('http://www.google.fr/');
$this->assertTrue($url->hasScheme());
$url = new Url('//www.google.fr/');
$this->assertFalse($url->hasScheme());
$url = new Url('/path');
$this->assertFalse($url->hasScheme());
$url = new Url('anything');
$this->assertFalse($url->hasScheme());
}
public function testHasPort()
{
$url = new Url('http://127.0.0.1:8000/');
$this->assertTrue($url->hasPort());
$url = new Url('http://127.0.0.1/');
$this->assertFalse($url->hasPort());
}
public function testIsProtocolRelative()
{
$url = new Url('http://www.google.fr/');
$this->assertFalse($url->isProtocolRelative());
$url = new Url('//www.google.fr/');
$this->assertTrue($url->isProtocolRelative());
$url = new Url('/path');
$this->assertFalse($url->isProtocolRelative());
$url = new Url('anything');
$this->assertFalse($url->isProtocolRelative());
}
public function testBaseUrl()
{
$url = new Url('../bla');
$this->assertEquals('', $url->getBaseUrl());
$url = new Url('github.com');
$this->assertEquals('', $url->getBaseUrl());
$url = new Url('http://127.0.0.1:8000');
$this->assertEquals('http://127.0.0.1:8000', $url->getBaseUrl());
$url = new Url('http://127.0.0.1:8000/test?123');
$this->assertEquals('http://127.0.0.1:8000', $url->getBaseUrl());
$url = new Url('http://localhost/test');
$this->assertEquals('http://localhost', $url->getBaseUrl());
$url = new Url('https://localhost/test');
$this->assertEquals('https://localhost', $url->getBaseUrl());
$url = new Url('//localhost/test?truc');
$this->assertEquals('http://localhost', $url->getBaseUrl());
$url = new Url('//localhost/test?truc');
$this->assertEquals('http://localhost', $url->getBaseUrl());
}
public function testIsRelativeUrl()
{
$url = new Url('http://www.google.fr/');
$this->assertFalse($url->isRelativeUrl());
$url = new Url('//www.google.fr/');
$this->assertFalse($url->isRelativeUrl());
$url = new Url('/path');
$this->assertTrue($url->isRelativeUrl());
$url = new Url('../../path');
$this->assertTrue($url->isRelativeUrl());
$url = new Url('anything');
$this->assertTrue($url->isRelativeUrl());
$url = new Url('/2014/08/03/4668-noisettes');
$this->assertTrue($url->isRelativeUrl());
$url = new Url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA
AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO
9TXL0Y4OHwAAAABJRU5ErkJggg==');
$this->assertFalse($url->isRelativeUrl());
}
public function testGetFullPath()
{
$url = new Url('http://www.google.fr/');
$this->assertEquals('/', $url->getFullPath());
$url = new Url('//www.google.fr/search');
$this->assertEquals('/search', $url->getFullPath());
$url = new Url('/path');
$this->assertEquals('/path', $url->getFullPath());
$url = new Url('/path#test');
$this->assertEquals('/path#test', $url->getFullPath());
$url = new Url('anything');
$this->assertEquals('/anything', $url->getFullPath());
$url = new Url('foo/bar');
$this->assertEquals('/foo/bar', $url->getFullPath());
$url = new Url('index.php?foo=bar&test=1');
$this->assertEquals('/index.php?foo=bar&test=1', $url->getFullPath());
}
public function testAbsoluteUrl()
{
$url = new Url('http://google.fr/');
$this->assertEquals('http://google.fr/', $url->getAbsoluteUrl());
$url = new Url('http://google.ca');
$this->assertEquals('http://google.ca/', $url->getAbsoluteUrl());
$url = new Url('../bla');
$this->assertEquals('', $url->getAbsoluteUrl(''));
$url = new Url('/2014/08/03/4668-noisettes');
$this->assertEquals('http://www.la-grange.net/2014/08/03/4668-noisettes', $url->getAbsoluteUrl('http://www.la-grange.net/'));
$url = new Url('http://www.google.fr/../bla');
$this->assertEquals('http://www.google.fr/../bla', $url->getAbsoluteUrl('http://www.google.fr/'));
$url = new Url('http://www.google.fr/');
$this->assertEquals('http://www.google.fr/', $url->getAbsoluteUrl('http://www.google.fr/'));
$url = new Url('//www.google.fr/search');
$this->assertEquals('http://www.google.fr/search', $url->getAbsoluteUrl('//www.google.fr/'));
$url = new Url('//www.google.fr/search');
$this->assertEquals('http://www.google.fr/search', $url->getAbsoluteUrl());
$url = new Url('/path');
$this->assertEquals('http://www.google.fr/path', $url->getAbsoluteUrl('http://www.google.fr/'));
$url = new Url('/path#test');
$this->assertEquals('http://www.google.fr/path#test', $url->getAbsoluteUrl('http://www.google.fr/'));
$url = new Url('anything');
$this->assertEquals('http://www.google.fr/anything', $url->getAbsoluteUrl('http://www.google.fr/'));
$url = new Url('index.php?foo=bar&test=1');
$this->assertEquals('http://www.google.fr/index.php?foo=bar&test=1', $url->getAbsoluteUrl('http://www.google.fr/'));
$url = new Url('index.php?foo=bar&test=1');
$this->assertEquals('', $url->getAbsoluteUrl());
$url = new Url('https://127.0.0.1:8000/here/test?v=3');
$this->assertEquals('https://127.0.0.1:8000/here/test?v=3', $url->getAbsoluteUrl());
$url = new Url('http://www.lofibucket.com/articles/oscilloscope_quake.html');
$this->assertEquals('http://www.lofibucket.com/articles/oscilloscope_quake.html', $url->getAbsoluteUrl());
$url = new Url('test?v=3');
$this->assertEquals('https://127.0.0.1:8000/here/test?v=3', $url->getAbsoluteUrl('https://127.0.0.1:8000/here/'));
}
public function testIsRelativePath()
{
$url = new Url('');
$this->assertTrue($url->isRelativePath());
$url = new Url('http://google.fr');
$this->assertTrue($url->isRelativePath());
$url = new Url('filename.json');
$this->assertTrue($url->isRelativePath());
$url = new Url('folder/filename.json');
$this->assertTrue($url->isRelativePath());
$url = new Url('/filename.json');
$this->assertFalse($url->isRelativePath());
$url = new Url('/folder/filename.json');
$this->assertFalse($url->isRelativePath());
}
public function testGetBasePath()
{
$url = new Url('img/quakescope.jpg');
$this->assertEquals('/img/', $url->getBasePath());
$url = new Url('http://foo/img/quakescope.jpg');
$this->assertEquals('/img/', $url->getBasePath());
$url = new Url('http://foo/bar.html');
$this->assertEquals('/', $url->getBasePath());
$url = new Url('http://foo/bar');
$this->assertEquals('/', $url->getBasePath());
$url = new Url('http://foo/bar/');
$this->assertEquals('/bar/', $url->getBasePath());
$url = new Url('http://website/subfolder/img/foo.png');
$this->assertEquals('/subfolder/img/', $url->getBasePath());
}
public function testResolve()
{
// relative link
$this->assertEquals(
'http://miniflux.net/assets/img/favicon.png',
Url::resolve('assets/img/favicon.png', 'http://miniflux.net')
);
// relative link + HTTPS
$this->assertEquals(
'https://miniflux.net/assets/img/favicon.png',
Url::resolve('assets/img/favicon.png', 'https://miniflux.net')
);
// absolute link
$this->assertEquals(
'http://miniflux.net/assets/img/favicon.png',
Url::resolve('/assets/img/favicon.png', 'http://miniflux.net')
);
// absolute link + HTTPS
$this->assertEquals(
'https://miniflux.net/assets/img/favicon.png',
Url::resolve('/assets/img/favicon.png', 'https://miniflux.net')
);
// Protocol relative link
$this->assertEquals(
'http://google.com/assets/img/favicon.png',
Url::resolve('//google.com/assets/img/favicon.png', 'http://miniflux.net')
);
// Protocol relative link + HTTPS
$this->assertEquals(
'https://google.com/assets/img/favicon.png',
Url::resolve('//google.com/assets/img/favicon.png', 'https://miniflux.net')
);
// URL same fqdn
$this->assertEquals(
'http://miniflux.net/assets/img/favicon.png',
Url::resolve('http://miniflux.net/assets/img/favicon.png', 'https://miniflux.net')
);
// URL different fqdn
$this->assertEquals(
'https://www.google.com/assets/img/favicon.png',
Url::resolve('https://www.google.com/assets/img/favicon.png', 'https://miniflux.net')
);
// HTTPS URL
$this->assertEquals(
'https://miniflux.net/assets/img/favicon.png',
Url::resolve('https://miniflux.net/assets/img/favicon.png', 'https://miniflux.net')
);
// empty string on missing website parameter
$this->assertEquals(
'',
Url::resolve('favicon.png', '')
);
// website only on missing icon parameter
$this->assertEquals(
'https://miniflux.net/',
Url::resolve('', 'https://miniflux.net')
);
// empty string on missing website and icon parameter
$this->assertEquals(
'',
Url::resolve('', '')
);
// Test no-ascii paths
$this->assertEquals(
'http://lesjoiesducode.fr/post/125336534020/quand-la-page-doit-%C3%AAtre-pixel-perfect',
Url::resolve('http://lesjoiesducode.fr/post/125336534020/quand-la-page-doit-être-pixel-perfect', 'http://lesjoiesducode.fr/post/125336534020')
);
}
}

View File

@ -1,324 +0,0 @@
<?php
namespace PicoFeed\Filter;
use PHPUnit_Framework_TestCase;
use PicoFeed\Client\Url;
use PicoFeed\Config\Config;
class AttributeFilterTest extends PHPUnit_Framework_TestCase
{
public function testFilterEmptyAttribute()
{
$filter = new Attribute(new Url('http://google.com'));
$this->assertTrue($filter->filterEmptyAttribute('abbr', 'title', 'test'));
$this->assertFalse($filter->filterEmptyAttribute('abbr', 'title', ''));
$this->assertEquals(array('title' => 'test'), $filter->filter('abbr', array('title' => 'test')));
$this->assertEquals(array(), $filter->filter('abbr', array('title' => '')));
}
public function testFilterAllowedAttribute()
{
$filter = new Attribute(new Url('http://google.com'));
$this->assertTrue($filter->filterAllowedAttribute('abbr', 'title', 'test'));
$this->assertFalse($filter->filterAllowedAttribute('script', 'type', 'text/javascript'));
$this->assertEquals(array(), $filter->filter('script', array('type' => 'text/javascript')));
$this->assertEquals(array(), $filter->filter('a', array('onclick' => 'javascript')));
$this->assertEquals(array('href' => 'http://google.com/'), $filter->filter('a', array('href' => 'http://google.com')));
}
public function testFilterIntegerAttribute()
{
$filter = new Attribute(new Url('http://google.com'));
$this->assertTrue($filter->filterIntegerAttribute('abbr', 'title', 'test'));
$this->assertTrue($filter->filterIntegerAttribute('iframe', 'width', '0'));
$this->assertTrue($filter->filterIntegerAttribute('iframe', 'width', '450'));
$this->assertFalse($filter->filterIntegerAttribute('iframe', 'width', 'test'));
$this->assertEquals(array('width' => '10', 'src' => 'https://www.youtube.com/test'), $filter->filter('iframe', array('width' => '10', 'src' => 'http://www.youtube.com/test')));
$this->assertEquals(array('src' => 'https://www.youtube.com/test'), $filter->filter('iframe', array('width' => 'test', 'src' => 'http://www.youtube.com/test')));
}
public function testRewriteProxyImageUrl()
{
$filter = new Attribute(new Url('http://www.la-grange.net'));
$url = '/2014/08/03/4668-noisettes';
$this->assertTrue($filter->rewriteImageProxyUrl('a', 'href', $url));
$this->assertEquals('/2014/08/03/4668-noisettes', $url);
$filter = new Attribute(new Url('http://www.la-grange.net'));
$url = '/2014/08/03/4668-noisettes';
$this->assertTrue($filter->rewriteImageProxyUrl('img', 'alt', $url));
$this->assertEquals('/2014/08/03/4668-noisettes', $url);
$filter = new Attribute(new Url('http://www.la-grange.net'));
$url = '/2014/08/03/4668-noisettes';
$this->assertTrue($filter->rewriteImageProxyUrl('img', 'src', $url));
$this->assertEquals('/2014/08/03/4668-noisettes', $url);
$filter = new Attribute(new Url('http://www.la-grange.net'));
$filter->setImageProxyUrl('https://myproxy/?u=%s');
$url = 'http://example.net/image.png';
$this->assertTrue($filter->rewriteImageProxyUrl('img', 'src', $url));
$this->assertEquals('https://myproxy/?u='.rawurlencode('http://example.net/image.png'), $url);
$filter = new Attribute(new Url('http://www.la-grange.net'));
$filter->setImageProxyCallback(function ($image_url) {
$key = hash_hmac('sha1', $image_url, 'secret');
return 'https://mypublicproxy/'.$key.'/'.rawurlencode($image_url);
});
$url = 'http://example.net/image.png';
$this->assertTrue($filter->rewriteImageProxyUrl('img', 'src', $url));
$this->assertEquals('https://mypublicproxy/d9701029b054f6e178ef88fcd3c789365e52a26d/'.rawurlencode('http://example.net/image.png'), $url);
}
public function testRewriteAbsoluteUrl()
{
$filter = new Attribute(new Url('http://www.la-grange.net'));
$url = '/2014/08/03/4668-noisettes';
$this->assertTrue($filter->rewriteAbsoluteUrl('a', 'href', $url));
$this->assertEquals('http://www.la-grange.net/2014/08/03/4668-noisettes', $url);
$filter = new Attribute(new Url('http://google.com'));
$url = 'test';
$this->assertTrue($filter->rewriteAbsoluteUrl('a', 'href', $url));
$this->assertEquals('http://google.com/test', $url);
$url = 'http://127.0.0.1:8000/test';
$this->assertTrue($filter->rewriteAbsoluteUrl('img', 'src', $url));
$this->assertEquals('http://127.0.0.1:8000/test', $url);
$url = '//example.com';
$this->assertTrue($filter->rewriteAbsoluteUrl('a', 'href', $url));
$this->assertEquals('http://example.com/', $url);
$filter = new Attribute(new Url('https://google.com'));
$url = '//example.com/?youpi';
$this->assertTrue($filter->rewriteAbsoluteUrl('a', 'href', $url));
$this->assertEquals('https://example.com/?youpi', $url);
$filter = new Attribute(new Url('https://127.0.0.1:8000/here/'));
$url = 'image.png?v=2';
$this->assertTrue($filter->rewriteAbsoluteUrl('a', 'href', $url));
$this->assertEquals('https://127.0.0.1:8000/here/image.png?v=2', $url);
$filter = new Attribute(new Url('https://truc/'));
$this->assertEquals(array('src' => 'https://www.youtube.com/test'), $filter->filter('iframe', array('width' => 'test', 'src' => '//www.youtube.com/test')));
$filter = new Attribute(new Url('http://truc/'));
$this->assertEquals(array('href' => 'http://google.fr/'), $filter->filter('a', array('href' => '//google.fr')));
}
public function testFilterIframeAttribute()
{
$filter = new Attribute(new Url('http://google.com'));
$this->assertTrue($filter->filterIframeAttribute('iframe', 'src', 'http://www.youtube.com/test'));
$this->assertTrue($filter->filterIframeAttribute('iframe', 'src', 'https://www.youtube.com/test'));
$this->assertFalse($filter->filterIframeAttribute('iframe', 'src', '//www.youtube.com/test'));
$this->assertFalse($filter->filterIframeAttribute('iframe', 'src', '//www.bidule.com/test'));
$this->assertEquals(array('src' => 'https://www.youtube.com/test'), $filter->filter('iframe', array('src' => '//www.youtube.com/test')));
}
public function testRemoveYouTubeAutoplay()
{
$filter = new Attribute(new Url('http://google.com'));
$urls = array(
'https://www.youtube.com/something/?autoplay=1' => 'https://www.youtube.com/something/?autoplay=0',
'https://www.youtube.com/something/?test=s&autoplay=1&a=2' => 'https://www.youtube.com/something/?test=s&autoplay=0&a=2',
'https://www.youtube.com/something/?test=s' => 'https://www.youtube.com/something/?test=s',
'https://youtube.com/something/?autoplay=1' => 'https://youtube.com/something/?autoplay=0',
'https://youtube.com/something/?test=s&autoplay=1&a=2' => 'https://youtube.com/something/?test=s&autoplay=0&a=2',
'https://youtube.com/something/?test=s' => 'https://youtube.com/something/?test=s',
);
foreach ($urls as $before => $after) {
$filter->removeYouTubeAutoplay('iframe', 'src', $before);
$this->assertEquals($after, $before);
}
}
public function testFilterBlacklistAttribute()
{
$filter = new Attribute(new Url('http://google.com'));
$this->assertTrue($filter->filterBlacklistResourceAttribute('a', 'href', 'http://google.fr/'));
$this->assertFalse($filter->filterBlacklistResourceAttribute('a', 'href', 'http://res3.feedsportal.com/truc'));
$this->assertEquals(array('href' => 'http://google.fr/'), $filter->filter('a', array('href' => 'http://google.fr/')));
$this->assertEquals(array(), $filter->filter('a', array('href' => 'http://res3.feedsportal.com/')));
}
public function testFilterProtocolAttribute()
{
$filter = new Attribute(new Url('http://google.com'));
$this->assertTrue($filter->filterProtocolUrlAttribute('a', 'href', 'http://google.fr/'));
$this->assertFalse($filter->filterProtocolUrlAttribute('a', 'href', 'bla://google.fr/'));
$this->assertFalse($filter->filterProtocolUrlAttribute('a', 'href', 'javascript:alert("test")'));
$this->assertEquals(array('href' => 'http://google.fr/'), $filter->filter('a', array('href' => 'http://google.fr/')));
$this->assertEquals(array(), $filter->filter('a', array('href' => 'bla://google.fr/')));
}
public function testRequiredAttribute()
{
$filter = new Attribute(new Url('http://google.com'));
$this->assertTrue($filter->hasRequiredAttributes('a', array('href' => 'bla')));
$this->assertTrue($filter->hasRequiredAttributes('img', array('src' => 'bla')));
$this->assertTrue($filter->hasRequiredAttributes('source', array('src' => 'bla')));
$this->assertTrue($filter->hasRequiredAttributes('audio', array('src' => 'bla')));
$this->assertTrue($filter->hasRequiredAttributes('iframe', array('src' => 'bla')));
$this->assertTrue($filter->hasRequiredAttributes('p', array('class' => 'bla')));
$this->assertFalse($filter->hasRequiredAttributes('a', array('title' => 'bla')));
}
public function testHtml()
{
$filter = new Attribute(new Url('http://google.com'));
$this->assertEquals('title="A &amp; B"', $filter->toHtml(array('title' => 'A & B')));
$this->assertEquals('title="&quot;a&quot;"', $filter->toHtml(array('title' => '"a"')));
$this->assertEquals('title="ç" alt="b"', $filter->toHtml(array('title' => 'ç', 'alt' => 'b')));
}
public function testNoImageProxySet()
{
$f = Filter::html('<p>Image <img src="/image.png" alt="My Image"/></p>', 'http://foo');
$this->assertEquals(
'<p>Image <img src="http://foo/image.png" alt="My Image"/></p>',
$f->execute()
);
}
public function testImageProxyWithHTTPLink()
{
$config = new Config;
$config->setFilterImageProxyUrl('http://myproxy/?url=%s');
$f = Filter::html('<p>Image <img src="http://localhost/image.png" alt="My Image"/></p>', 'http://foo');
$f->setConfig($config);
$this->assertEquals(
'<p>Image <img src="http://myproxy/?url='.rawurlencode('http://localhost/image.png').'" alt="My Image"/></p>',
$f->execute()
);
}
public function testImageProxyWithHTTPSLink()
{
$config = new Config;
$config->setFilterImageProxyUrl('http://myproxy/?url=%s');
$f = Filter::html('<p>Image <img src="https://localhost/image.png" alt="My Image"/></p>', 'http://foo');
$f->setConfig($config);
$this->assertEquals(
'<p>Image <img src="http://myproxy/?url='.rawurlencode('https://localhost/image.png').'" alt="My Image"/></p>',
$f->execute()
);
}
public function testImageProxyLimitedToUnknownProtocol()
{
$config = new Config;
$config->setFilterImageProxyUrl('http://myproxy/?url=%s');
$config->setFilterImageProxyProtocol('tripleX');
$f = Filter::html('<p>Image <img src="http://localhost/image.png" alt="My Image"/></p>', 'http://foo');
$f->setConfig($config);
$this->assertEquals(
'<p>Image <img src="http://localhost/image.png" alt="My Image"/></p>',
$f->execute()
);
}
public function testImageProxyLimitedToHTTPwithHTTPLink()
{
$config = new Config;
$config->setFilterImageProxyUrl('http://myproxy/?url=%s');
$config->setFilterImageProxyProtocol('http');
$f = Filter::html('<p>Image <img src="http://localhost/image.png" alt="My Image"/></p>', 'http://foo');
$f->setConfig($config);
$this->assertEquals(
'<p>Image <img src="http://myproxy/?url='.rawurlencode('http://localhost/image.png').'" alt="My Image"/></p>',
$f->execute()
);
}
public function testImageProxyLimitedToHTTPwithHTTPSLink()
{
$config = new Config;
$config->setFilterImageProxyUrl('http://myproxy/?url=%s');
$config->setFilterImageProxyProtocol('http');
$f = Filter::html('<p>Image <img src="https://localhost/image.png" alt="My Image"/></p>', 'http://foo');
$f->setConfig($config);
$this->assertEquals(
'<p>Image <img src="https://localhost/image.png" alt="My Image"/></p>',
$f->execute()
);
}
public function testImageProxyLimitedToHTTPSwithHTTPLink()
{
$config = new Config;
$config->setFilterImageProxyUrl('http://myproxy/?url=%s');
$config->setFilterImageProxyProtocol('https');
$f = Filter::html('<p>Image <img src="http://localhost/image.png" alt="My Image"/></p>', 'http://foo');
$f->setConfig($config);
$this->assertEquals(
'<p>Image <img src="http://localhost/image.png" alt="My Image"/></p>',
$f->execute()
);
}
public function testImageProxyLimitedToHTTPSwithHTTPSLink()
{
$config = new Config;
$config->setFilterImageProxyUrl('http://myproxy/?url=%s');
$config->setFilterImageProxyProtocol('https');
$f = Filter::html('<p>Image <img src="https://localhost/image.png" alt="My Image"/></p>', 'http://foo');
$f->setConfig($config);
$this->assertEquals(
'<p>Image <img src="http://myproxy/?url='.rawurlencode('https://localhost/image.png').'" alt="My Image"/></p>',
$f->execute()
);
}
public function testsetFilterImageProxyCallback()
{
$config = new Config;
$config->setFilterImageProxyCallback(function ($image_url) {
$key = hash_hmac('sha1', $image_url, 'secret');
return 'https://mypublicproxy/'.$key.'/'.rawurlencode($image_url);
});
$f = Filter::html('<p>Image <img src="/image.png" alt="My Image"/></p>', 'http://foo');
$f->setConfig($config);
$this->assertEquals(
'<p>Image <img src="https://mypublicproxy/4924964043f3119b3cf2b07b1922d491bcc20092/'.rawurlencode('http://foo/image.png').'" alt="My Image"/></p>',
$f->execute()
);
}
}

View File

@ -1,122 +0,0 @@
<?php
namespace PicoFeed\Filter;
use PHPUnit_Framework_TestCase;
use PicoFeed\Config\Config;
class FilterTest extends PHPUnit_Framework_TestCase
{
public function testStripHeadTag()
{
$input = '<html><head><title>test</title></head><body><h1>boo</h1></body>';
$expected = '<html><body><h1>boo</h1></body>';
$this->assertEquals($expected, Filter::stripHeadTags($input));
$input = file_get_contents('tests/fixtures/html4_page.html');
$expected = file_get_contents('tests/fixtures/html4_head_stripped_page.html');
$this->assertEquals($expected, Filter::stripHeadTags($input));
$input = file_get_contents('tests/fixtures/html_page.html');
$expected = file_get_contents('tests/fixtures/html_head_stripped_page.html');
$this->assertEquals($expected, Filter::stripHeadTags($input));
}
public function testStripXmlTag()
{
$data = file_get_contents('tests/fixtures/jeux-linux.fr.xml');
$this->assertEquals('<rss', substr(Filter::stripXmlTag($data), 0, 4));
$data = file_get_contents('tests/fixtures/ezrss.it');
$this->assertEquals('<!DOC', substr(Filter::stripXmlTag($data), 0, 5));
$data = file_get_contents('tests/fixtures/fulltextrss.xml');
$this->assertEquals('<rss', substr(Filter::stripXmlTag($data), 0, 4));
$data = file_get_contents('tests/fixtures/sametmax.xml');
$this->assertEquals('<rss', substr(Filter::stripXmlTag($data), 0, 4));
$data = file_get_contents('tests/fixtures/ibash.ru.xml');
$this->assertEquals('<rss', substr(Filter::stripXmlTag($data), 0, 4));
$data = file_get_contents('tests/fixtures/pcinpact.xml');
$this->assertEquals('<rss', substr(Filter::stripXmlTag($data), 0, 4));
$data = file_get_contents('tests/fixtures/resorts.xml');
$this->assertEquals('<rss', substr(Filter::stripXmlTag($data), 0, 4));
$data = file_get_contents('tests/fixtures/rue89.xml');
$this->assertEquals('<rss', substr(Filter::stripXmlTag($data), 0, 4));
$data = file_get_contents('tests/fixtures/cercle.psy.xml');
$this->assertEquals('<rss', substr(Filter::stripXmlTag($data), 0, 4));
$data = file_get_contents('tests/fixtures/lagrange.xml');
$this->assertEquals('<feed', substr(Filter::stripXmlTag($data), 0, 5));
$data = file_get_contents('tests/fixtures/googleblog.xml');
$this->assertEquals('<feed', substr(trim(Filter::stripXmlTag($data)), 0, 5));
$data = file_get_contents('tests/fixtures/atomsample.xml');
$this->assertEquals('<feed', substr(trim(Filter::stripXmlTag($data)), 0, 5));
$data = file_get_contents('tests/fixtures/planete-jquery.xml');
$this->assertEquals('<rdf:RDF', trim(substr(trim(Filter::stripXmlTag($data)), 0, 8)));
}
public function testOverrideFilters()
{
$data = '<iframe src="http://www.kickstarter.com/projects/lefnire/habitrpg-mobile/widget/video.html" height="480" width="640" frameborder="0"></iframe>';
$expected = '<iframe src="https://www.kickstarter.com/projects/lefnire/habitrpg-mobile/widget/video.html" height="480" width="640" frameborder="0"></iframe>';
$f = Filter::html($data, 'http://blabla');
$f->attribute->setIframeWhitelist(array('http://www.kickstarter.com'));
$this->assertEquals($expected, $f->execute());
$data = '<iframe src="http://www.youtube.com/bla" height="480" width="640" frameborder="0"></iframe>';
$f = Filter::html($data, 'http://blabla');
$f->attribute->setIframeWhitelist(array('http://www.kickstarter.com'));
$this->assertEmpty($f->execute());
$config = new Config;
$config->setFilterWhitelistedTags(array('p' => array('title')));
$f = Filter::html('<p>Test<strong>boo</strong></p>', 'http://blabla');
$f->setConfig($config);
$this->assertEquals('<p>Testboo</p>', $f->execute());
}
public function testNormalizeData()
{
// invalid data link escape control character
$this->assertEquals('<xml>random text</xml>', Filter::normalizeData("<xml>random\x10 text</xml>"));
$this->assertEquals('<xml>random text</xml>', Filter::normalizeData("<xml>random&#x10; text</xml>"));
$this->assertEquals('<xml>random text</xml>', Filter::normalizeData("<xml>random&#16; text</xml>"));
// invalid unit seperator control character (lower and upper case)
$this->assertEquals('<xml>random text</xml>', Filter::normalizeData("<xml>random\x1f text</xml>"));
$this->assertEquals('<xml>random text</xml>', Filter::normalizeData("<xml>random\x1F text</xml>"));
$this->assertEquals('<xml>random text</xml>', Filter::normalizeData("<xml>random&#x1f; text</xml>"));
$this->assertEquals('<xml>random text</xml>', Filter::normalizeData("<xml>random&#x1F; text</xml>"));
$this->assertEquals('<xml>random text</xml>', Filter::normalizeData("<xml>random&#31; text</xml>"));
/*
* Do not test invalid multibyte characters. The output depends on php
* version and character.
*
* php 5.3: always null
* php >5.3: sometime null, sometimes the stripped string
*/
// invalid backspace control character + valid multibyte character
$this->assertEquals('<xml>“random“ text</xml>', Filter::normalizeData("<xml>\xe2\x80\x9crandom\xe2\x80\x9c\x08 text</xml>"));
$this->assertEquals('<xml>&#x201C;random&#x201C; text</xml>', Filter::normalizeData("<xml>&#x201C;random&#x201C;&#x08; text</xml>"));
$this->assertEquals('<xml>&#8220;random&#8220; text</xml>', Filter::normalizeData("<xml>&#8220;random&#8220;&#08; text</xml>"));
// do not convert valid entities to utf-8 character
$this->assertEquals('<xml attribute="&#34;value&#34;">random text</xml>', Filter::normalizeData('<xml attribute="&#34;value&#34;">random text</xml>'));
$this->assertEquals('<xml attribute="&#x22;value&#x22;">random text</xml>', Filter::normalizeData('<xml attribute="&#x22;value&#x22;">random text</xml>'));
}
}

View File

@ -1,173 +0,0 @@
<?php
namespace PicoFeed\Filter;
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>
<meta name="title" content="test">
</head><body><p>boo<br/><strong>foo</strong>.</p></body></html>';
$filter = new Html($html, 'http://www.google.ca/');
$this->assertEquals('<p>boo<br/><strong>foo</strong>.</p>', $filter->execute());
}
public function testFilterRules()
{
$html = '<p><a href="http://www.twogag.com/archives/3455" title="559 &#8211; The Cookie">' .
'<img src="http://www.twogag.com/comics-rss/2015-04-17-TGAG_559_The_Cookie.jpg" alt="559 &#8211; The Cookie" class="comicthumbnail" title="559 &#8211; The Cookie" /></a></p>'.
'I always throw up in hindsight if I find out something I ate was vegan. Twogag&#8217;s super free but if you want to support the comic look no further than the Twogag patreon!';
$filter = new Html($html, 'http://www.twogag.com/');
$expected = '<p><a href="http://www.twogag.com/archives/3455" rel="noreferrer" target="_blank">' .
'<img src="http://www.twogag.com/comics/2015-04-17-TGAG_559_The_Cookie.jpg" alt="559 The Cookie" title="559 The Cookie"/></a></p>'.
'I always throw up in hindsight if I find out something I ate was vegan. Twogags super free but if you want to support the comic look no further than the Twogag patreon!';
$this->assertEquals($expected, $filter->execute());
}
public function testIframe()
{
$data = '<iframe src="http://www.kickstarter.com/projects/lefnire/habitrpg-mobile/widget/video.html" height="480" width="640" frameborder="0"></iframe>';
$f = new Html($data, 'http://blabla');
$this->assertEmpty($f->execute());
$data = '<iframe src="http://www.youtube.com/bla" height="480" width="640" frameborder="0"></iframe>';
$expected = '<iframe src="https://www.youtube.com/bla" height="480" width="640" frameborder="0"></iframe>';
$f = new Html($data, 'http://blabla');
$this->assertEquals($expected, $f->execute());
}
public function testClearScriptAttributes()
{
$data = '<div><script>this is the content</script><script>blubb content</script><p>something</p></div><p>hi</p>';
$f = new Html($data, 'http://blabla');
$expected = '<p>something</p><p>hi</p>';
$this->assertEquals($expected, $f->execute());
}
public function testClearStyleAttributes()
{
$data = '<div><style>this is the content</style><style>blubb content</style><p>something</p></div><p>hi</p>';
$f = new Html($data, 'http://blabla');
$expected = '<p>something</p><p>hi</p>';
$this->assertEquals($expected, $f->execute());
}
public function testEmptyTags()
{
$data = <<<EOD
<div>
<a href="file://path/to/a/file">
<img src="http://example.com/image.png" />
</a>
</div>
EOD;
$f = new Html($data, 'http://blabla');
$output = $f->execute();
$this->assertEquals('<img src="http://example.com/image.png"/>', $output);
}
public function testBadAttributes()
{
$data = '<iframe src="http://www.youtube.com/bla" height="480px" width="100%" frameborder="0"></iframe>';
$f = new Html($data, 'http://blabla');
$this->assertEquals('<iframe src="https://www.youtube.com/bla" frameborder="0"></iframe>', $f->execute());
}
public function testRelativeScheme()
{
$f = new Html('<a href="//linuxfr.org">link</a>', 'http://blabla');
$this->assertEquals('<a href="http://linuxfr.org/" rel="noreferrer" target="_blank">link</a>', $f->execute());
}
public function testAttributes()
{
$f = new Html('<img src="foo" title="\'quote" alt="\'quote" data-src="bar" data-truc="boo"/>', 'http://blabla');
$this->assertEquals('<img src="http://blabla/foo" title="&#039;quote" alt="&#039;quote"/>', $f->execute());
$f = new Html('<img src="foo&bar=\'quote"/>', 'http://blabla');
$this->assertEquals('<img src="http://blabla/foo&amp;bar=&#039;quote"/>', $f->execute());
$f = new Html("<time datetime='quote\"here'>bla</time>", 'http://blabla');
$this->assertEquals('<time datetime="quote&quot;here">bla</time>', $f->execute());
}
public function testCode()
{
$data = '<pre><code>HEAD / HTTP/1.1
Accept: text/html
Accept-Encoding: gzip, deflate, compress
Host: www.amazon.com
User-Agent: HTTPie/0.6.0
<strong>HTTP/1.1 405 MethodNotAllowed</strong>
Content-Encoding: gzip
Content-Type: text/html; charset=ISO-8859-1
Date: Mon, 15 Jul 2013 02:05:59 GMT
Server: Server
Set-Cookie: skin=noskin; path=/; domain=.amazon.com; expires=Mon, 15-Jul-2013 02:05:59 GMT
Vary: Accept-Encoding,User-Agent
<strong>allow: POST, GET</strong>
x-amz-id-1: 11WD3K15FC268R5GBJY5
x-amz-id-2: DDjqfqz2ZJufzqRAcj1mh+9XvSogrPohKHwXlo8IlkzH67G6w4wnjn9HYgbs4uI0
</code></pre>';
$f = new Html($data, 'http://blabla');
$this->assertEquals($data, $f->execute());
}
public function testRemoveNoBreakingSpace()
{
$f = new Html('<p>&nbsp;&nbsp;truc</p>', 'http://blabla');
$this->assertEquals('<p> truc</p>', $f->execute());
}
public function testRemoveEmptyTags()
{
$f = new Html('<p>toto</p><p></p><br/>', 'http://blabla');
$this->assertEquals('<p>toto</p><br/>', $f->execute());
$f = new Html('<p> </p>', 'http://blabla');
$this->assertEquals('', $f->execute());
$f = new Html('<p>&nbsp;</p>', 'http://blabla');
$this->assertEquals('', $f->execute());
}
public function testRemoveEmptyTable()
{
$f = new Html('<table><tr><td> </td></tr></table>', 'http://blabla');
$this->assertEquals('', $f->execute());
$f = new Html('<table><tr></tr></table>', 'http://blabla');
$this->assertEquals('', $f->execute());
}
public function testRemoveMultipleTags()
{
$f = new Html('<br/><br/><p>toto</p><br/><br/><br/><p>momo</p><br/><br/><br/><br/>', 'http://blabla');
$this->assertEquals('<br/><p>toto</p><br/><p>momo</p><br/>', $f->execute());
}
}

View File

@ -1,33 +0,0 @@
<?php
namespace PicoFeed\Filter;
use PHPUnit_Framework_TestCase;
class TagFilterTest extends PHPUnit_Framework_TestCase
{
public function testAllowedTag()
{
$tag = new Tag;
$this->assertTrue($tag->isAllowed('p', array('class' => 'test')));
$this->assertTrue($tag->isAllowed('img', array('class' => 'test')));
$this->assertFalse($tag->isAllowed('script', array('class' => 'test')));
$this->assertFalse($tag->isAllowed('img', array('width' => '1', 'height' => '1')));
}
public function testHtml()
{
$tag = new Tag;
$this->assertEquals('<p>', $tag->openHtmlTag('p'));
$this->assertEquals('<img src="test" alt="truc"/>', $tag->openHtmlTag('img', 'src="test" alt="truc"'));
$this->assertEquals('<img/>', $tag->openHtmlTag('img'));
$this->assertEquals('<br/>', $tag->openHtmlTag('br'));
$this->assertEquals('</p>', $tag->closeHtmlTag('p'));
$this->assertEquals('', $tag->closeHtmlTag('img'));
$this->assertEquals('', $tag->closeHtmlTag('br'));
}
}

View File

@ -1,463 +0,0 @@
<?php
namespace PicoFeed\Parser;
use PHPUnit_Framework_TestCase;
class AtomParserTest extends PHPUnit_Framework_TestCase
{
/**
* @expectedException PicoFeed\Parser\MalformedXmlException
*/
public function testBadInput()
{
$parser = new Atom('boo');
$parser->execute();
}
public function testGetItemsTree()
{
$parser = new Atom(file_get_contents('tests/fixtures/atom.xml'));
$feed = $parser->execute();
$this->assertCount(4, $feed->items);
$parser = new Atom(file_get_contents('tests/fixtures/atom_no_default_namespace.xml'));
$feed = $parser->execute();
$this->assertCount(4, $feed->items);
$parser = new Atom(file_get_contents('tests/fixtures/atom_prefixed.xml'));
$feed = $parser->execute();
$this->assertCount(4, $feed->items);
$parser = new Atom(file_get_contents('tests/fixtures/atom_empty_feed.xml'));
$feed = $parser->execute();
$this->assertEquals(array(), $feed->items);
}
public function testFindFeedTitle()
{
$parser = new Atom(file_get_contents('tests/fixtures/atom.xml'));
$feed = $parser->execute();
$this->assertEquals('литература на русском языке, либо написанная русскими авторами', $feed->getTitle());
$parser = new Atom(file_get_contents('tests/fixtures/atom_no_default_namespace.xml'));
$feed = $parser->execute();
$this->assertEquals('литература на русском языке, либо написанная русскими авторами', $feed->getTitle());
$parser = new Atom(file_get_contents('tests/fixtures/atom_prefixed.xml'));
$feed = $parser->execute();
$this->assertEquals('литература на русском языке, либо написанная русскими авторами', $feed->getTitle());
$parser = new Atom(file_get_contents('tests/fixtures/atom_fallback_on_invalid_feed_values.xml'));
$feed = $parser->execute();
$this->assertEquals('https://en.wikipedia.org/wiki/Category:Russian-language_literature', $feed->getTitle());
$parser = new Atom(file_get_contents('tests/fixtures/atom_empty_feed.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->getTitle());
}
public function testFindFeedDescription()
{
$parser = new Atom(file_get_contents('tests/fixtures/atom.xml'));
$feed = $parser->execute();
$this->assertEquals("Зародилась во второй половине X века, однако до XIX века,\nкогда начался её «золотой век», была практически неизвестна\nв мире.", $feed->getDescription());
$parser = new Atom(file_get_contents('tests/fixtures/atom_no_default_namespace.xml'));
$feed = $parser->execute();
$this->assertEquals("Зародилась во второй половине X века, однако до XIX века,\nкогда начался её «золотой век», была практически неизвестна\nв мире.", $feed->getDescription());
$parser = new Atom(file_get_contents('tests/fixtures/atom_prefixed.xml'));
$feed = $parser->execute();
$this->assertEquals("Зародилась во второй половине X века, однако до XIX века,\nкогда начался её «золотой век», была практически неизвестна\nв мире.", $feed->getDescription());
$parser = new Atom(file_get_contents('tests/fixtures/atom_empty_feed.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->getDescription());
}
public function testFindFeedLogo()
{
$parser = new Atom(file_get_contents('tests/fixtures/atom.xml'));
$feed = $parser->execute();
$this->assertEquals('https://ru.wikipedia.org/static/images/project-logos/ruwiki.png', $feed->getLogo());
$parser = new Atom(file_get_contents('tests/fixtures/atom_no_default_namespace.xml'));
$feed = $parser->execute();
$this->assertEquals('https://ru.wikipedia.org/static/images/project-logos/ruwiki.png', $feed->getLogo());
$parser = new Atom(file_get_contents('tests/fixtures/atom_prefixed.xml'));
$feed = $parser->execute();
$this->assertEquals('https://ru.wikipedia.org/static/images/project-logos/ruwiki.png', $feed->getLogo());
$parser = new Atom(file_get_contents('tests/fixtures/atom_empty_feed.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->getLogo());
}
public function testFindFeedIcon()
{
$parser = new Atom(file_get_contents('tests/fixtures/atom.xml'));
$feed = $parser->execute();
$this->assertEquals('https://ru.wikipedia.org/static/favicon/wikipedia.ico', $feed->getIcon());
$parser = new Atom(file_get_contents('tests/fixtures/atom_no_default_namespace.xml'));
$feed = $parser->execute();
$this->assertEquals('https://ru.wikipedia.org/static/favicon/wikipedia.ico', $feed->getIcon());
$parser = new Atom(file_get_contents('tests/fixtures/atom_prefixed.xml'));
$feed = $parser->execute();
$this->assertEquals('https://ru.wikipedia.org/static/favicon/wikipedia.ico', $feed->getIcon());
$parser = new Atom(file_get_contents('tests/fixtures/atom_empty_feed.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->getIcon());
}
public function testFindFeedUrl()
{
$parser = new Atom(file_get_contents('tests/fixtures/atom.xml'));
$feed = $parser->execute();
$this->assertEquals('https://feeds.wikipedia.org/category/Russian-language_literature.xml', $feed->getFeedUrl());
$parser = new Atom(file_get_contents('tests/fixtures/atom_extra.xml'), '', 'https://feeds.wikipedia.org/category/Russian-language_literature.xml'); // relative url
$feed = $parser->execute();
$this->assertEquals('https://feeds.wikipedia.org/category/Russian-language_literature.xml', $feed->getFeedUrl());
$parser = new Atom(file_get_contents('tests/fixtures/atom_no_default_namespace.xml'));
$feed = $parser->execute();
$this->assertEquals('https://feeds.wikipedia.org/category/Russian-language_literature.xml', $feed->getFeedUrl());
$parser = new Atom(file_get_contents('tests/fixtures/atom_prefixed.xml'));
$feed = $parser->execute();
$this->assertEquals('https://feeds.wikipedia.org/category/Russian-language_literature.xml', $feed->getFeedUrl());
$parser = new Atom(file_get_contents('tests/fixtures/atom_empty_feed.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->getFeedUrl());
}
public function testFindSiteUrl()
{
$parser = new Atom(file_get_contents('tests/fixtures/atom.xml')); // rel="alternate"
$feed = $parser->execute();
$this->assertEquals('https://en.wikipedia.org/wiki/Category:Russian-language_literature', $feed->getSiteUrl());
$parser = new Atom(file_get_contents('tests/fixtures/atom_extra.xml'), '', 'https://feeds.wikipedia.org/category/Russian-language_literature.xml'); // no rel + relative url
$feed = $parser->execute();
$this->assertEquals('https://feeds.wikipedia.org/wiki/Category:Russian-language_literature', $feed->getSiteUrl());
$parser = new Atom(file_get_contents('tests/fixtures/atom_no_default_namespace.xml'));
$feed = $parser->execute();
$this->assertEquals('https://en.wikipedia.org/wiki/Category:Russian-language_literature', $feed->getSiteUrl());
$parser = new Atom(file_get_contents('tests/fixtures/atom_prefixed.xml'));
$feed = $parser->execute();
$this->assertEquals('https://en.wikipedia.org/wiki/Category:Russian-language_literature', $feed->getSiteUrl());
$parser = new Atom(file_get_contents('tests/fixtures/atom_empty_feed.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->getSiteUrl());
}
public function testFindFeedId()
{
$parser = new Atom(file_get_contents('tests/fixtures/atom.xml'));
$feed = $parser->execute();
$this->assertEquals('urn:uuid:bd0b2c90-35a3-44e9-a491-4e15508f6d83', $feed->getId());
$parser = new Atom(file_get_contents('tests/fixtures/atom_no_default_namespace.xml'));
$feed = $parser->execute();
$this->assertEquals('urn:uuid:bd0b2c90-35a3-44e9-a491-4e15508f6d83', $feed->getId());
$parser = new Atom(file_get_contents('tests/fixtures/atom_prefixed.xml'));
$feed = $parser->execute();
$this->assertEquals('urn:uuid:bd0b2c90-35a3-44e9-a491-4e15508f6d83', $feed->getId());
$parser = new Atom(file_get_contents('tests/fixtures/atom_empty_feed.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->getId());
}
public function testFindFeedDate()
{
$parser = new Atom(file_get_contents('tests/fixtures/atom.xml'));
$feed = $parser->execute();
$this->assertEquals(1433451900, $feed->getDate()->getTimestamp());
$parser = new Atom(file_get_contents('tests/fixtures/atom_no_default_namespace.xml'));
$feed = $parser->execute();
$this->assertEquals(1433451900, $feed->getDate()->getTimestamp());
$parser = new Atom(file_get_contents('tests/fixtures/atom_prefixed.xml'));
$feed = $parser->execute();
$this->assertEquals(1433451900, $feed->getDate()->getTimestamp());
$parser = new Atom(file_get_contents('tests/fixtures/atom_empty_feed.xml'));
$feed = $parser->execute();
$this->assertEquals(time(), $feed->getDate()->getTimestamp(), 1);
}
public function testFindFeedLanguage()
{
$parser = new Atom(file_get_contents('tests/fixtures/atom.xml'));
$feed = $parser->execute();
$this->assertEquals('ru', $feed->getLanguage());
$parser = new Atom(file_get_contents('tests/fixtures/atom_extra.xml'));
$feed = $parser->execute();
$this->assertEquals('ru', $feed->getLanguage());
// do not use lang from entry or descendant of entry
$parser = new Atom('<feed xmlns="http://www.w3.org/2005/Atom"><entry xml:lang="ru"><title xml:lang="ru"/></entry></feed>');
$feed = $parser->execute();
$this->assertEquals('', $feed->getLanguage());
// do not use lang from entry or descendant of entry (prefixed)
$parser = new Atom('<feed xmlns:atom="http://www.w3.org/2005/Atom"><atom:entry xml:lang="ru"><atom:title xml:lang="ru"/></atom:entry></feed>');
$feed = $parser->execute();
$this->assertEquals('', $feed->getLanguage());
$parser = new Atom(file_get_contents('tests/fixtures/atom_no_default_namespace.xml'));
$feed = $parser->execute();
$this->assertEquals('ru', $feed->getLanguage());
$parser = new Atom(file_get_contents('tests/fixtures/atom_prefixed.xml'));
$feed = $parser->execute();
$this->assertEquals('ru', $feed->getLanguage());
$parser = new Atom(file_get_contents('tests/fixtures/atom_empty_feed.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->getLanguage());
}
public function testFindItemId()
{
// items[0] === alternate generation
// items[1] === id element
$parser = new Atom(file_get_contents('tests/fixtures/atom.xml'));
$feed = $parser->execute();
$this->assertEquals('eb6f2d388a77e1f7d067a924970622d630031365fd444abe776d974d95b21990', $feed->items[0]->getId());
$this->assertEquals('b64b5e0ce422566fa768e8c66da61ab5759c00b2289adbe8fe2f35ecfe211184', $feed->items[1]->getId());
$parser = new Atom(file_get_contents('tests/fixtures/atom_no_default_namespace.xml'));
$feed = $parser->execute();
$this->assertEquals('eb6f2d388a77e1f7d067a924970622d630031365fd444abe776d974d95b21990', $feed->items[0]->getId());
$this->assertEquals('b64b5e0ce422566fa768e8c66da61ab5759c00b2289adbe8fe2f35ecfe211184', $feed->items[1]->getId());
$parser = new Atom(file_get_contents('tests/fixtures/atom_prefixed.xml'));
$feed = $parser->execute();
$this->assertEquals('eb6f2d388a77e1f7d067a924970622d630031365fd444abe776d974d95b21990', $feed->items[0]->getId());
$this->assertEquals('b64b5e0ce422566fa768e8c66da61ab5759c00b2289adbe8fe2f35ecfe211184', $feed->items[1]->getId());
$parser = new Atom(file_get_contents('tests/fixtures/atom_empty_item.xml'));
$feed = $parser->execute();
$this->assertEquals('e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', $feed->items[0]->getId());
}
public function testFindItemUrl()
{
// items[0] === rel="alternate"
// items[1] === no rel
$parser = new Atom(file_get_contents('tests/fixtures/atom.xml'));
$feed = $parser->execute();
$this->assertEquals('https://en.wikipedia.org/wiki/War_and_Peace', $feed->items[0]->getUrl());
$this->assertEquals('https://en.wikipedia.org/wiki/Crime_and_Punishment', $feed->items[1]->getUrl());
// relative url
$parser = new Atom(file_get_contents('tests/fixtures/atom_extra.xml'), '', 'https://feeds.wikipedia.org/category/Russian-language_literature.xml');
$feed = $parser->execute();
$this->assertEquals('https://feeds.wikipedia.org/wiki/War_and_Peace', $feed->items[0]->getUrl());
$this->assertEquals('https://feeds.wikipedia.org/wiki/Crime_and_Punishment', $feed->items[1]->getUrl());
$parser = new Atom(file_get_contents('tests/fixtures/atom_no_default_namespace.xml'));
$feed = $parser->execute();
$this->assertEquals('https://en.wikipedia.org/wiki/War_and_Peace', $feed->items[0]->getUrl());
$this->assertEquals('https://en.wikipedia.org/wiki/Crime_and_Punishment', $feed->items[1]->getUrl());
$parser = new Atom(file_get_contents('tests/fixtures/atom_prefixed.xml'));
$feed = $parser->execute();
$this->assertEquals('https://en.wikipedia.org/wiki/War_and_Peace', $feed->items[0]->getUrl());
$this->assertEquals('https://en.wikipedia.org/wiki/Crime_and_Punishment', $feed->items[1]->getUrl());
$parser = new Atom(file_get_contents('tests/fixtures/atom_empty_item.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->items[0]->getUrl());
}
public function testFindItemTitle()
{
$parser = new Atom(file_get_contents('tests/fixtures/atom.xml'));
$feed = $parser->execute();
$this->assertEquals('Война и мир', $feed->items[0]->getTitle());
$parser = new Atom(file_get_contents('tests/fixtures/atom_no_default_namespace.xml'));
$feed = $parser->execute();
$this->assertEquals('Война и мир', $feed->items[0]->getTitle());
$parser = new Atom(file_get_contents('tests/fixtures/atom_prefixed.xml'));
$feed = $parser->execute();
$this->assertEquals('Война и мир', $feed->items[0]->getTitle());
$parser = new Atom(file_get_contents('tests/fixtures/atom_fallback_on_invalid_item_values.xml'));
$feed = $parser->execute();
$this->assertEquals('https://en.wikipedia.org/wiki/Doctor_Zhivago_(novel)', $feed->items[2]->getTitle());
$parser = new Atom(file_get_contents('tests/fixtures/atom_empty_item.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->items[0]->getTitle());
}
public function testItemDate()
{
// items[0] === updated element
// items[1] === published element
// items[2] === fallback to feed date
$parser = new Atom(file_get_contents('tests/fixtures/atom.xml'));
$feed = $parser->execute();
$this->assertEquals(1433451720, $feed->items[0]->getDate()->getTimestamp());
$this->assertEquals(1433451720, $feed->items[1]->getDate()->getTimestamp());
$this->assertEquals(1433451900, $feed->items[2]->getDate()->getTimestamp());
$parser = new Atom(file_get_contents('tests/fixtures/atom_no_default_namespace.xml'));
$feed = $parser->execute();
$this->assertEquals(1433451720, $feed->items[0]->getDate()->getTimestamp());
$this->assertEquals(1433451720, $feed->items[1]->getDate()->getTimestamp());
$this->assertEquals(1433451900, $feed->items[2]->getDate()->getTimestamp());
$parser = new Atom(file_get_contents('tests/fixtures/atom_prefixed.xml'));
$feed = $parser->execute();
$this->assertEquals(1433451720, $feed->items[0]->getDate()->getTimestamp());
$this->assertEquals(1433451720, $feed->items[1]->getDate()->getTimestamp());
$this->assertEquals(1433451900, $feed->items[2]->getDate()->getTimestamp());
// prefer most recent date and not a particular date element
$parser = new Atom(file_get_contents('tests/fixtures/atom_element_preference.xml'));
$feed = $parser->execute();
$this->assertEquals(1433455500, $feed->items[0]->getDate()->getTimestamp());
$parser = new Atom(file_get_contents('tests/fixtures/atom_empty_item.xml'));
$feed = $parser->execute();
$this->assertEquals(time(), $feed->getDate()->getTimestamp(), '', 1);
}
public function testItemLanguage()
{
// items[0] === language tag on Language-Sensitive element (title)
// items[1] === language tag on root node
// items[2] === fallback to feed language
$parser = new Atom(file_get_contents('tests/fixtures/atom.xml'));
$feed = $parser->execute();
$this->assertEquals('bg', $feed->items[0]->getLanguage());
$this->assertEquals('bg', $feed->items[1]->getLanguage());
$this->assertEquals('ru', $feed->items[2]->getLanguage());
$parser = new Atom(file_get_contents('tests/fixtures/atom_no_default_namespace.xml'));
$feed = $parser->execute();
$this->assertEquals('bg', $feed->items[0]->getLanguage());
$this->assertEquals('bg', $feed->items[1]->getLanguage());
$this->assertEquals('ru', $feed->items[2]->getLanguage());
$parser = new Atom(file_get_contents('tests/fixtures/atom_prefixed.xml'));
$feed = $parser->execute();
$this->assertEquals('bg', $feed->items[0]->getLanguage());
$this->assertEquals('bg', $feed->items[1]->getLanguage());
$this->assertEquals('ru', $feed->items[2]->getLanguage());
$parser = new Atom(file_get_contents('tests/fixtures/atom_empty_item.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->items[0]->getLanguage());
}
public function testItemAuthor()
{
// items[0] === item author
// items[1] === feed author via empty fallback
$parser = new Atom(file_get_contents('tests/fixtures/atom.xml'));
$feed = $parser->execute();
$this->assertEquals('Лев Николаевич Толсто́й', $feed->items[0]->getAuthor());
$this->assertEquals('Вики педии - свободной энциклопедии', $feed->items[1]->getAuthor());
$parser = new Atom(file_get_contents('tests/fixtures/atom_no_default_namespace.xml'));
$feed = $parser->execute();
$this->assertEquals('Лев Николаевич Толсто́й', $feed->items[0]->getAuthor());
$this->assertEquals('Вики педии - свободной энциклопедии', $feed->items[1]->getAuthor());
$parser = new Atom(file_get_contents('tests/fixtures/atom_prefixed.xml'));
$feed = $parser->execute();
$this->assertEquals('Лев Николаевич Толсто́й', $feed->items[0]->getAuthor());
$this->assertEquals('Вики педии - свободной энциклопедии', $feed->items[1]->getAuthor());
$parser = new Atom(file_get_contents('tests/fixtures/atom_empty_item.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->items[0]->getAuthor());
}
public function testItemContent()
{
// items[0] === <summary>
// items[1] === <content> CDATA raw html
// items[2] === <content> escaped html
// items[3] === <content> raw html
$parser = new Atom(file_get_contents('tests/fixtures/atom.xml'));
$parser->disableContentFiltering();
$feed = $parser->execute();
$this->assertTrue(strpos($feed->items[0]->getContent(), "В наброске предисловия к «Войне и миру» Толстой\nписал, что в 1856 г.") === 0);
$this->assertTrue(strpos($feed->items[1]->getContent(), "<h1>\nИстория создания\n</h1>\n<p>\nОсенью \n<a href=\"/wiki/1865_%D0%B3%D0%BE%D0%B4\"") === 0);
$this->assertTrue(strpos($feed->items[2]->getContent(), "<h1>\nДоктор Живаго\n</h1>\n<p>\n<b>«До́ктор Жива́го»</b> ") === 0);
$this->assertTrue(strpos($feed->items[3]->getContent(), "<h1>\nГерой нашего времени\n</h1><p>\n<b>«Геро́й на́шего вре́мени»</b> \n(написан в 1838—1840) — знаменитый роман \n<a href=\"/wiki/%D0%9B") === 0);
$parser = new Atom(file_get_contents('tests/fixtures/atom_no_default_namespace.xml'));
$parser->disableContentFiltering();
$feed = $parser->execute();
$this->assertTrue(strpos($feed->items[0]->getContent(), "В наброске предисловия к «Войне и миру» Толстой\nписал, что в 1856 г.") === 0);
$this->assertTrue(strpos($feed->items[1]->getContent(), "<h1>\nИстория создания\n</h1>\n<p>\nОсенью \n<a href=\"/wiki/1865_%D0%B3%D0%BE%D0%B4\"") === 0);
$this->assertTrue(strpos($feed->items[2]->getContent(), "<h1>\nДоктор Живаго\n</h1>\n<p>\n<b>«До́ктор Жива́го»</b> ") === 0);
$this->assertTrue(strpos($feed->items[3]->getContent(), "<h1>\nГерой нашего времени\n</h1><p>\n<b>«Геро́й на́шего вре́мени»</b> \n(написан в 1838—1840) — знаменитый роман \n<a href=\"/wiki/%D0%9B") === 0);
$parser = new Atom(file_get_contents('tests/fixtures/atom_prefixed.xml'));
$parser->disableContentFiltering();
$feed = $parser->execute();
$this->assertTrue(strpos($feed->items[0]->getContent(), "В наброске предисловия к «Войне и миру» Толстой\nписал, что в 1856 г.") === 0);
$this->assertTrue(strpos($feed->items[1]->getContent(), "<h1>\nИстория создания\n</h1>\n<p>\nОсенью \n<a href=\"/wiki/1865_%D0%B3%D0%BE%D0%B4\"") === 0);
$this->assertTrue(strpos($feed->items[2]->getContent(), "<h1>\nДоктор Живаго\n</h1>\n<p>\n<b>«До́ктор Жива́го»</b> ") === 0);
$this->assertTrue(strpos($feed->items[3]->getContent(), "<h1>\nГерой нашего времени\n</h1><p>\n<b>«Геро́й на́шего вре́мени»</b> \n(написан в 1838—1840) — знаменитый роман \n<a href=\"/wiki/%D0%9B") === 0);
// <content> is preferred over <summary>
$parser = new Atom(file_get_contents('tests/fixtures/atom_element_preference.xml'));
$parser->disableContentFiltering();
$feed = $parser->execute();
$this->assertTrue(strpos($feed->items[1]->getContent(), "<h1>\nИстория создания\n</h1>\n<p>\nОсенью \n<a href=\"/wiki/1865_%D0%B3%D0%BE%D0%B4\"") === 0);
$parser = new Atom(file_get_contents('tests/fixtures/atom_fallback_on_invalid_item_values.xml'));
$parser->disableContentFiltering();
$feed = $parser->execute();
$this->assertTrue(strpos($feed->items[1]->getContent(), "Осенью 1865 года, потеряв все свои\nденьги в казино") === 0); // <content> => <summary>
$parser = new Atom(file_get_contents('tests/fixtures/atom_empty_item.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->items[0]->getContent());
}
public function testFindItemEnclosure()
{
$parser = new Atom(file_get_contents('tests/fixtures/atom.xml'));
$feed = $parser->execute();
$this->assertEquals('https://upload.wikimedia.org/wikipedia/commons/4/41/War-and-peace_1873.gif', $feed->items[0]->getEnclosureUrl());
$this->assertEquals('image/gif', $feed->items[0]->getEnclosureType());
$parser = new Atom(file_get_contents('tests/fixtures/atom_no_default_namespace.xml'));
$feed = $parser->execute();
$this->assertEquals('https://upload.wikimedia.org/wikipedia/commons/4/41/War-and-peace_1873.gif', $feed->items[0]->getEnclosureUrl());
$this->assertEquals('image/gif', $feed->items[0]->getEnclosureType());
$parser = new Atom(file_get_contents('tests/fixtures/atom_prefixed.xml'));
$feed = $parser->execute();
$this->assertEquals('https://upload.wikimedia.org/wikipedia/commons/4/41/War-and-peace_1873.gif', $feed->items[0]->getEnclosureUrl());
$this->assertEquals('image/gif', $feed->items[0]->getEnclosureType());
$parser = new Atom(file_get_contents('tests/fixtures/atom_empty_item.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->items[0]->getEnclosureUrl());
$this->assertEquals('', $feed->items[0]->getEnclosureType());
}
}

View File

@ -1,49 +0,0 @@
<?php
namespace PicoFeed\Parser;
use PHPUnit_Framework_TestCase;
class DateParserTest extends PHPUnit_Framework_TestCase
{
public function testParseDate()
{
$parser = new DateParser;
date_default_timezone_set('UTC');
$this->assertEquals('2013-04-12', $parser->getDateTime('Fri, 12 Apr 2013 15:38:15 +0000')->format('Y-m-d'));
$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'));
}
}

View File

@ -1,24 +0,0 @@
<?php
namespace PicoFeed\Parser;
use PHPUnit_Framework_TestCase;
class FeedTest extends PHPUnit_Framework_TestCase
{
public function testLangRTL()
{
$item = new Feed;
$item->language = 'fr_FR';
$this->assertFalse($item->isRTL());
$item->language = 'ur';
$this->assertTrue($item->isRTL());
$item->language = 'syr-**';
$this->assertTrue($item->isRTL());
$item->language = 'ru';
$this->assertFalse($item->isRTL());
}
}

View File

@ -1,36 +0,0 @@
<?php
namespace PicoFeed\Parser;
use PHPUnit_Framework_TestCase;
class ItemTest extends PHPUnit_Framework_TestCase
{
public function testLangRTL()
{
$item = new Item;
$item->language = 'fr_FR';
$this->assertFalse($item->isRTL());
$item->language = 'ur';
$this->assertTrue($item->isRTL());
$item->language = 'syr-**';
$this->assertTrue($item->isRTL());
$item->language = 'ru';
$this->assertFalse($item->isRTL());
}
public function testGetTag()
{
$parser = new Rss20(file_get_contents('tests/fixtures/podbean.xml'));
$feed = $parser->execute();
$this->assertEquals(array('http://aroundthebloc.podbean.com/e/s03e11-finding-nemo-rocco/'), $feed->items[0]->getTag('guid'));
$this->assertEquals(array('false'), $feed->items[0]->getTag('guid', 'isPermaLink'));
$this->assertEquals(array('http://aroundthebloc.podbean.com/mf/web/28bcnk/ATBLogo-BlackBackground.png'), $feed->items[0]->getTag('media:content', 'url'));
$this->assertEquals(array('http://aroundthebloc.podbean.com/e/s03e11-finding-nemo-rocco/feed/'), $feed->items[0]->getTag('wfw:commentRss'));
$this->assertEquals(array(), $feed->items[0]->getTag('wfw:notExistent'));
$this->assertCount(7, $feed->items[0]->getTag('itunes:*'));
}
}

View File

@ -1,71 +0,0 @@
<?php
namespace PicoFeed\Parser;
use PHPUnit_Framework_TestCase;
class ParserTest extends PHPUnit_Framework_TestCase
{
public function testChangeHashAlgo()
{
$parser = new Rss20('');
$this->assertEquals('fb8e20fc2e4c3f248c60c39bd652f3c1347298bb977b8b4d5903b85055620603', $parser->generateId('a', 'b'));
$parser->setHashAlgo('sha1');
$this->assertEquals('da23614e02469a0d7c7bd1bdab5c9c474b1904dc', $parser->generateId('a', 'b'));
}
public function testLangRTL()
{
$this->assertFalse(Parser::isLanguageRTL('fr-FR'));
$this->assertTrue(Parser::isLanguageRTL('ur'));
$this->assertTrue(Parser::isLanguageRTL('syr-**'));
$this->assertFalse(Parser::isLanguageRTL('ru'));
}
public function testFeedsWithInvalidCharacters()
{
$parser = new Rss20(file_get_contents('tests/fixtures/lincoln_loop.xml'));
$feed = $parser->execute();
$this->assertNotEmpty($feed->items);
$parser = new Rss20(file_get_contents('tests/fixtures/next_inpact_full.xml'));
$feed = $parser->execute();
$this->assertNotEmpty($feed->items);
}
public function testFeedEncodingsAreSupported()
{
// windows-1251
$parser = new Rss20(file_get_contents('tests/fixtures/ibash.ru.xml'));
$feed = $parser->execute();
$this->assertEquals('<p>Хабр, обсуждение фейлов на работе: reaferon: Интернет-магазин с оборотом более 1 млн. в месяц. При округлении цены до двух знаков после запятой: $price = round($price,2); была допущена досадная опечатка $price = rand($price,2);</p>', $feed->items[0]->getContent());
// CP1251
$parser = new Rss20(file_get_contents('tests/fixtures/xakep.ru.xml'));
$feed = $parser->execute();
$this->assertEquals('Bug Bounty — другая сторона медали', $feed->items[23]->title);
$this->assertEquals('<p>Бывший директор АНБ, генерал Майкл Хэйден снова показал себя во всей красе.</p>', $feed->items[0]->getContent());
}
public function testXMLTagStrippingIsUsed()
{
$parser = new Rss20(file_get_contents('tests/fixtures/jeux-linux.fr.xml'));
$feed = $parser->execute();
$this->assertNotEmpty($feed->items);
}
public function testHTTPEncodingFallbackIsUsed()
{
$parser = new Rss20(file_get_contents('tests/fixtures/cercle.psy.xml'), 'iso-8859-1');
$feed = $parser->execute();
$this->assertNotEmpty($feed->items);
}
public function testFeedURLFallbackIsUsed()
{
$parser = new Atom(file_get_contents('tests/fixtures/atom_empty_feed.xml'), '', 'https://feeds.wikipedia.org/category/Russian-language_literature.xml');
$feed = $parser->execute();
$this->assertEquals('https://feeds.wikipedia.org/category/Russian-language_literature.xml', $feed->getFeedUrl());
}
}

View File

@ -1,352 +0,0 @@
<?php
namespace PicoFeed\Parser;
use PHPUnit_Framework_TestCase;
class Rss10ParserTest extends PHPUnit_Framework_TestCase
{
/**
* @expectedException PicoFeed\Parser\MalformedXmlException
*/
public function testBadInput()
{
$parser = new Rss10('boo');
$parser->execute();
}
public function testGetItemsTree()
{
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10.xml'));
$feed = $parser->execute();
$this->assertCount(2, $feed->items);
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_no_default_namespace.xml'));
$feed = $parser->execute();
$this->assertCount(3, $feed->items);
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_prefixed.xml'));
$feed = $parser->execute();
$this->assertCount(1, $feed->items);
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_empty_feed.xml'));
$feed = $parser->execute();
$this->assertEquals(array(), $feed->items);
}
public function testFindFeedTitle()
{
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10.xml'));
$feed = $parser->execute();
$this->assertEquals('литература на русском языке, либо написанная русскими авторами', $feed->getTitle());
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_no_default_namespace.xml'));
$feed = $parser->execute();
$this->assertEquals('литература на русском языке, либо написанная русскими авторами', $feed->getTitle());
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_prefixed.xml'));
$feed = $parser->execute();
$this->assertEquals('литература на русском языке, либо написанная русскими авторами', $feed->getTitle());
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_fallback_on_invalid_feed_values.xml'));
$feed = $parser->execute();
$this->assertEquals('https://en.wikipedia.org/wiki/Category:Russian-language_literature', $feed->getTitle());
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_empty_channel.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->getTitle());
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_empty_feed.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->getTitle());
}
public function testFindFeedDescription()
{
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10.xml'));
$feed = $parser->execute();
$this->assertEquals("Зародилась во второй половине X века, однако до XIX века,\nкогда начался её «золотой век», была практически неизвестна\nв мире.", $feed->getDescription());
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_no_default_namespace.xml'));
$feed = $parser->execute();
$this->assertEquals("Зародилась во второй половине X века, однако до XIX века,\nкогда начался её «золотой век», была практически неизвестна\nв мире.", $feed->getDescription());
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_prefixed.xml'));
$feed = $parser->execute();
$this->assertEquals("Зародилась во второй половине X века, однако до XIX века,\nкогда начался её «золотой век», была практически неизвестна\nв мире.", $feed->getDescription());
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_empty_channel.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->getDescription());
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_empty_feed.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->getDescription());
}
public function testFindFeedLogo()
{
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10.xml'));
$feed = $parser->execute();
$this->assertEquals('https://ru.wikipedia.org/static/images/project-logos/ruwiki.png', $feed->getLogo());
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_no_default_namespace.xml'));
$feed = $parser->execute();
$this->assertEquals('https://ru.wikipedia.org/static/images/project-logos/ruwiki.png', $feed->getLogo());
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_prefixed.xml'));
$feed = $parser->execute();
$this->assertEquals('https://ru.wikipedia.org/static/images/project-logos/ruwiki.png', $feed->getLogo());
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_empty_channel.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->getLogo());
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_empty_feed.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->getLogo());
}
public function testFindFeedIcon()
{
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->getIcon());
}
public function testFindFeedUrl()
{
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->getFeedUrl());
}
public function testFindSiteUrl()
{
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10.xml'));
$feed = $parser->execute();
$this->assertEquals('https://en.wikipedia.org/wiki/Category:Russian-language_literature', $feed->getSiteUrl());
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_extra.xml'), '', 'https://feeds.wikipedia.org/category/Russian-language_literature.xml'); // relative url
$feed = $parser->execute();
$this->assertEquals('https://feeds.wikipedia.org/wiki/Category:Russian-language_literature', $feed->getSiteUrl());
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_no_default_namespace.xml'));
$feed = $parser->execute();
$this->assertEquals('https://en.wikipedia.org/wiki/Category:Russian-language_literature', $feed->getSiteUrl());
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_prefixed.xml'));
$feed = $parser->execute();
$this->assertEquals('https://en.wikipedia.org/wiki/Category:Russian-language_literature', $feed->getSiteUrl());
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_empty_channel.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->getSiteUrl());
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_empty_feed.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->getSiteUrl());
}
public function testFindFeedId()
{
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10.xml'));
$feed = $parser->execute();
$this->assertEquals('https://en.wikipedia.org/wiki/Category:Russian-language_literature', $feed->getId());
}
public function testFindFeedDate()
{
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10.xml'));
$feed = $parser->execute();
$this->assertEquals(1433451900, $feed->getDate()->getTimestamp());
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_no_default_namespace.xml'));
$feed = $parser->execute();
$this->assertEquals(1433451900, $feed->getDate()->getTimestamp());
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_prefixed.xml'));
$feed = $parser->execute();
$this->assertEquals(1433451900, $feed->getDate()->getTimestamp());
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_empty_channel.xml'));
$feed = $parser->execute();
$this->assertEquals(time(), $feed->getDate()->getTimestamp(), '', 1);
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_empty_feed.xml'));
$feed = $parser->execute();
$this->assertEquals(time(), $feed->getDate()->getTimestamp(), '', 1);
}
public function testFindFeedLanguage()
{
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10.xml'));
$feed = $parser->execute();
$this->assertEquals('ru', $feed->getLanguage());
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_no_default_namespace.xml'));
$feed = $parser->execute();
$this->assertEquals('ru', $feed->getLanguage());
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_prefixed.xml'));
$feed = $parser->execute();
$this->assertEquals('ru', $feed->getLanguage());
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_empty_channel.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->getTitle());
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_empty_feed.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->getTitle());
}
public function testFindItemId()
{
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10.xml'));
$feed = $parser->execute();
$this->assertEquals('eb6f2d388a77e1f7d067a924970622d630031365fd444abe776d974d95b21990', $feed->items[0]->getId());
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_empty_item.xml'));
$feed = $parser->execute();
$this->assertEquals('e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', $feed->items[0]->getId());
}
public function testFindItemUrl()
{
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10.xml'));
$feed = $parser->execute();
$this->assertEquals('https://en.wikipedia.org/wiki/War_and_Peace', $feed->items[0]->getUrl()); // <rss:link>
$this->assertEquals('https://en.wikipedia.org/wiki/Crime_and_Punishment', $feed->items[1]->getUrl()); // <feedburner:origLink>
// relative urls
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_extra.xml'), '', 'https://feeds.wikipedia.org/category/Russian-language_literature.xml');
$feed = $parser->execute();
$this->assertEquals('https://feeds.wikipedia.org/wiki/War_and_Peace', $feed->items[0]->getUrl()); // <rss:link>
$this->assertEquals('https://feeds.wikipedia.org/wiki/Crime_and_Punishment', $feed->items[1]->getUrl()); // <feedburner:origLink>
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_no_default_namespace.xml'));
$feed = $parser->execute();
$this->assertEquals('https://en.wikipedia.org/wiki/War_and_Peace', $feed->items[0]->getUrl()); // <rss:link>
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_prefixed.xml'));
$feed = $parser->execute();
$this->assertEquals('https://en.wikipedia.org/wiki/War_and_Peace', $feed->items[0]->getUrl()); // <rss:link>
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_element_preference.xml'));
$feed = $parser->execute();
$this->assertEquals('https://en.wikipedia.org/wiki/War_and_Peace', $feed->items[0]->getUrl()); // <feedburner:origLink> is preferred over <rss:link>
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_empty_item.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->items[0]->getUrl());
}
public function testFindItemTitle()
{
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10.xml'));
$feed = $parser->execute();
$this->assertEquals('Война и мир', $feed->items[0]->getTitle());
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_no_default_namespace.xml'));
$feed = $parser->execute();
$this->assertEquals('Война и мир', $feed->items[0]->getTitle());
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_prefixed.xml'));
$feed = $parser->execute();
$this->assertEquals('Война и мир', $feed->items[0]->getTitle());
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_fallback_on_invalid_item_values.xml'));
$feed = $parser->execute();
$this->assertEquals('https://en.wikipedia.org/wiki/Doctor_Zhivago_(novel)', $feed->items[2]->getTitle());
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_empty_item.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->items[0]->getTitle());
}
/*
* TODO: Add test of feed date fallback
*/
public function testFindItemDate()
{
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10.xml'));
$feed = $parser->execute();
$this->assertEquals(1433451720, $feed->items[0]->getDate()->getTimestamp()); // item date
$this->assertEquals(1433451900, $feed->items[1]->getDate()->getTimestamp()); // fallback to feed date
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_empty_item.xml'));
$feed = $parser->execute();
$this->assertEquals(time(), $feed->items[0]->getDate()->getTimestamp(), 1);
}
public function testFindItemLanguage()
{
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10.xml'));
$feed = $parser->execute();
$this->assertEquals('bg', $feed->items[0]->getLanguage()); // item language
$this->assertEquals('ru', $feed->items[1]->getLanguage()); // fallback to feed language
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_empty_item.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->items[0]->getAuthor());
}
public function testFindItemAuthor()
{
// items[0] === item author
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10.xml'));
$feed = $parser->execute();
$this->assertEquals('Лев Николаевич Толсто́й', $feed->items[0]->getAuthor());
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_empty_item.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->items[0]->getAuthor());
}
public function testFindItemContent()
{
// items[0] === <description>
// items[1] === <content:encoded>
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10.xml'));
$parser->disableContentFiltering();
$feed = $parser->execute();
$this->assertTrue(strpos($feed->items[0]->getContent(), "В наброске предисловия к «Войне и миру» Толстой\nписал, что в 1856 г.") === 0);
$this->assertTrue(strpos($feed->items[1]->getContent(), "<h1>\nИстория создания\n</h1>\n<p>\nОсенью \n<a href=\"/wiki/1865_%D0%B3%D0%BE%D0%B4\"") === 0);
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_no_default_namespace.xml'));
$parser->disableContentFiltering();
$feed = $parser->execute();
$this->assertTrue(strpos($feed->items[0]->getContent(), "В наброске предисловия к «Войне и миру» Толстой\nписал, что в 1856 г.") === 0);
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_prefixed.xml'));
$parser->disableContentFiltering();
$feed = $parser->execute();
$this->assertTrue(strpos($feed->items[0]->getContent(), "В наброске предисловия к «Войне и миру» Толстой\nписал, что в 1856 г.") === 0);
// <content:encoding> is preferred over <description>
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_element_preference.xml'));
$parser->disableContentFiltering();
$feed = $parser->execute();
$this->assertTrue(strpos($feed->items[1]->getContent(), "<h1>\nИстория создания\n</h1>\n<p>\nОсенью \n<a href=\"/wiki/1865_%D0%B3%D0%BE%D0%B4\"") === 0);
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_fallback_on_invalid_item_values.xml'));
$parser->disableContentFiltering();
$feed = $parser->execute();
$this->assertTrue(strpos($feed->items[1]->getContent(), "Осенью 1865 года, потеряв все свои\nденьги в казино") === 0); // <content:encoded> => <description>
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10_empty_item.xml'));
$parser->disableContentFiltering();
$feed = $parser->execute();
$this->assertEquals('', $feed->items[0]->getContent());
}
public function testFindItemEnclosure()
{
$parser = new Rss10(file_get_contents('tests/fixtures/rss_10.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->items[0]->getEnclosureUrl());
$this->assertEquals('', $feed->items[0]->getEnclosureType());
}
}

View File

@ -1,311 +0,0 @@
<?php
namespace PicoFeed\Parser;
use PHPUnit_Framework_TestCase;
class Rss20ParserTest extends PHPUnit_Framework_TestCase
{
/**
* @expectedException PicoFeed\Parser\MalformedXmlException
*/
public function testBadInput()
{
$parser = new Rss20('boo');
$parser->execute();
}
public function testGetItemsTree()
{
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20.xml'));
$feed = $parser->execute();
$this->assertCount(4, $feed->items);
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20_empty_feed.xml'));
$feed = $parser->execute();
$this->assertEquals(array(), $feed->items);
}
public function testFindFeedTitle()
{
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20.xml'));
$feed = $parser->execute();
$this->assertEquals('литература на русском языке, либо написанная русскими авторами', $feed->getTitle());
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20_fallback_on_invalid_feed_values.xml'));
$feed = $parser->execute();
$this->assertEquals('https://en.wikipedia.org/wiki/Category:Russian-language_literature', $feed->getTitle());
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20_empty_channel.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->getTitle());
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20_empty_feed.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->getTitle());
}
public function testFindFeedDescription()
{
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20.xml'));
$feed = $parser->execute();
$this->assertEquals("Зародилась во второй половине X века, однако до XIX века,\nкогда начался её «золотой век», была практически неизвестна\nв мире.", $feed->getDescription());
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20_empty_channel.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->getDescription());
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20_empty_feed.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->getDescription());
}
public function testFindFeedLogo()
{
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20.xml'));
$feed = $parser->execute();
$this->assertEquals('https://ru.wikipedia.org/static/images/project-logos/ruwiki.png', $feed->getLogo());
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20_empty_channel.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->getLogo());
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20_empty_feed.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->getLogo());
}
public function testFindFeedIcon()
{
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->getIcon());
}
public function testFindFeedUrl()
{
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->getFeedUrl());
}
public function testFindSiteUrl()
{
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20.xml'));
$feed = $parser->execute();
$this->assertEquals('https://en.wikipedia.org/wiki/Category:Russian-language_literature', $feed->getSiteUrl());
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20_extra.xml'), '', 'https://feeds.wikipedia.org/category/Russian-language_literature.xml'); // relative url
$feed = $parser->execute();
$this->assertEquals('https://feeds.wikipedia.org/wiki/Category:Russian-language_literature', $feed->getSiteUrl());
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20_empty_channel.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->getSiteUrl());
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20_empty_feed.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->getSiteUrl());
}
public function testFindFeedId()
{
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20.xml'));
$feed = $parser->execute();
$this->assertEquals('https://en.wikipedia.org/wiki/Category:Russian-language_literature', $feed->getId());
}
public function testFindFeedDate()
{
// pubDate
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20.xml'));
$feed = $parser->execute();
$this->assertEquals(1433451900, $feed->getDate()->getTimestamp());
// lastBuildDate
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20_extra.xml'));
$feed = $parser->execute();
$this->assertEquals(1433451900, $feed->getDate()->getTimestamp());
// prefer most recent date and not a particular date element
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20_element_preference.xml'));
$feed = $parser->execute();
$this->assertEquals(1433455500, $feed->getDate()->getTimestamp());
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20_empty_channel.xml'));
$feed = $parser->execute();
$this->assertEquals(time(), $feed->getDate()->getTimestamp(), '', 1);
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20_empty_feed.xml'));
$feed = $parser->execute();
$this->assertEquals(time(), $feed->getDate()->getTimestamp(), '', 1);
}
public function testFindFeedLanguage()
{
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20.xml'));
$feed = $parser->execute();
$this->assertEquals('ru', $feed->getLanguage());
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20_empty_channel.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->getTitle());
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20_empty_feed.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->getTitle());
}
public function testFindItemId()
{
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20.xml')); // <guid>
$feed = $parser->execute();
$this->assertEquals('06e53052cd17cdfb264d9c37d495cc3746ac43f79488c7ce67894e718f674bd5', $feed->items[1]->getId());
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20.xml')); // alternate generation
$feed = $parser->execute();
$this->assertEquals('eb6f2d388a77e1f7d067a924970622d630031365fd444abe776d974d95b21990', $feed->items[0]->getId());
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20_empty_item.xml'));
$feed = $parser->execute();
$this->assertEquals('e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', $feed->items[0]->getId());
}
public function testFindItemUrl()
{
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20.xml'));
$feed = $parser->execute();
$this->assertEquals('https://en.wikipedia.org/wiki/War_and_Peace', $feed->items[0]->getUrl()); // <rss:link>
$this->assertEquals('https://en.wikipedia.org/wiki/Crime_and_Punishment', $feed->items[1]->getUrl()); // <atom:link>
$this->assertEquals('https://en.wikipedia.org/wiki/Doctor_Zhivago_(novel)', $feed->items[2]->getUrl()); // <feedburner:origLink>
$this->assertEquals('https://guid.wikipedia.org/wiki/A_Hero_of_Our_Time', $feed->items[3]->getUrl()); // <guid>
// relative urls
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20_extra.xml'), '', 'https://feeds.wikipedia.org/category/Russian-language_literature.xml');
$feed = $parser->execute();
$this->assertEquals('https://feeds.wikipedia.org/wiki/War_and_Peace', $feed->items[0]->getUrl()); // <rss:link>
$this->assertEquals('https://feeds.wikipedia.org/wiki/Crime_and_Punishment', $feed->items[1]->getUrl()); // <atom:link>
$this->assertEquals('https://feeds.wikipedia.org/wiki/Doctor_Zhivago_(novel)', $feed->items[2]->getUrl()); // <feedburner:origLink>
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20_element_preference.xml'));
$feed = $parser->execute();
$this->assertEquals('https://en.wikipedia.org/wiki/War_and_Peace', $feed->items[0]->getUrl()); // <feedburner:origLink> is preferred over <rss:link>, <atom:link>, <guid>
$this->assertEquals('https://en.wikipedia.org/wiki/Crime_and_Punishment', $feed->items[1]->getUrl()); // <rss:link> is preferred over <atom:link>, <guid>
$this->assertEquals('https://en.wikipedia.org/wiki/Doctor_Zhivago_(novel)', $feed->items[2]->getUrl()); // <atom:link> is preferred over <guid>
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20_fallback_on_invalid_item_values.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->items[0]->getUrl()); // <guid> is invalid URI
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20_empty_item.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->items[0]->getUrl());
}
public function testFindItemTitle()
{
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20.xml'));
$feed = $parser->execute();
$this->assertEquals('Война и мир', $feed->items[0]->getTitle());
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20_fallback_on_invalid_item_values.xml'));
$feed = $parser->execute();
$this->assertEquals('https://en.wikipedia.org/wiki/Doctor_Zhivago_(novel)', $feed->items[2]->getTitle());
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20_empty_item.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->items[0]->getTitle());
}
public function testFindItemDate()
{
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20.xml'));
$feed = $parser->execute();
$this->assertEquals(1433451720, $feed->items[0]->getDate()->getTimestamp()); // item date
$this->assertEquals(1433451900, $feed->items[1]->getDate()->getTimestamp()); // fallback to feed date
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20_empty_item.xml'));
$feed = $parser->execute();
$this->assertEquals(time(), $feed->items[0]->getDate()->getTimestamp(), 1);
}
public function testFindItemLanguage()
{
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20.xml'));
$feed = $parser->execute();
$this->assertEquals('bg', $feed->items[0]->getLanguage()); // item language
$this->assertEquals('ru', $feed->items[1]->getLanguage()); // fallback to feed language
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20_empty_item.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->items[0]->getLanguage());
}
public function testFindItemAuthor()
{
// items[0] === item author
// items[1] === feed author via empty fallback (channel/managingEditor)
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20.xml'));
$feed = $parser->execute();
$this->assertEquals('Лев Николаевич Толсто́й', $feed->items[0]->getAuthor());
$this->assertEquals('Вики педии - свободной энциклопедии', $feed->items[1]->getAuthor());
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20_dc.xml'));
$feed = $parser->execute();
$this->assertEquals('Лев Николаевич Толсто́й', $feed->items[0]->getAuthor());
$this->assertEquals('Вики педии - свободной энциклопедии', $feed->items[1]->getAuthor());
// <dc:creator> is preferred over <author>
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20_element_preference.xml'));
$feed = $parser->execute();
$this->assertEquals('Лев Николаевич Толсто́й', $feed->items[0]->getAuthor());
$this->assertEquals('Вики педии - свободной энциклопедии', $feed->items[1]->getAuthor());
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20_empty_item.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->items[0]->getAuthor());
}
public function testFindItemContent()
{
// items[0] === <description>
// items[1] === <content:encoded>
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20.xml'));
$parser->disableContentFiltering();
$feed = $parser->execute();
$this->assertTrue(strpos($feed->items[0]->getContent(), "В наброске предисловия к «Войне и миру» Толстой\nписал, что в 1856 г.") === 0);
$this->assertTrue(strpos($feed->items[1]->getContent(), "<h1>\nИстория создания\n</h1>\n<p>\nОсенью \n<a href=\"/wiki/1865_%D0%B3%D0%BE%D0%B4\"") === 0);
// <content:encoding> is preferred over <description>
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20_element_preference.xml'));
$parser->disableContentFiltering();
$feed = $parser->execute();
$this->assertTrue(strpos($feed->items[1]->getContent(), "<h1>\nИстория создания\n</h1>\n<p>\nОсенью \n<a href=\"/wiki/1865_%D0%B3%D0%BE%D0%B4\"") === 0);
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20_fallback_on_invalid_item_values.xml'));
$parser->disableContentFiltering();
$feed = $parser->execute();
$this->assertTrue(strpos($feed->items[1]->getContent(), "Осенью 1865 года, потеряв все свои\nденьги в казино") === 0); // <content:encoded> => <description>
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20_empty_item.xml'));
$parser->disableContentFiltering();
$feed = $parser->execute();
$this->assertEquals('', $feed->items[0]->getContent());
}
public function testFindItemEnclosure()
{
// Test tests covers the preference of <feedburner:origEnclosureLink> over <enclosure> as well
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20.xml'));
$feed = $parser->execute();
$this->assertEquals('https://upload.wikimedia.org/wikipedia/commons/4/41/War-and-peace_1873.gif', $feed->items[0]->getEnclosureUrl()); // <enclosure>
$this->assertEquals('image/gif', $feed->items[0]->getEnclosureType());
$this->assertEquals('https://upload.wikimedia.org/wikipedia/commons/7/7b/Crime_and_Punishment-1.png', $feed->items[1]->getEnclosureUrl()); // <feedburner:origEnclosureLink>
$parser = new Rss20(file_get_contents('tests/fixtures/rss_20_empty_item.xml'));
$feed = $parser->execute();
$this->assertEquals('', $feed->items[0]->getEnclosureUrl());
$this->assertEquals('', $feed->items[0]->getEnclosureType());
}
}

View File

@ -1,31 +0,0 @@
<?php
namespace PicoFeed\Parser;
use PHPUnit_Framework_TestCase;
class Rss91ParserTest extends PHPUnit_Framework_TestCase
{
public function testFormatOk()
{
$parser = new Rss91(file_get_contents('tests/fixtures/rss_0.91.xml'));
$feed = $parser->execute();
$this->assertNotFalse($feed);
$this->assertNotEmpty($feed->items);
$this->assertEquals('WriteTheWeb', $feed->getTitle());
$this->assertEquals('', $feed->getFeedUrl());
$this->assertEquals('http://writetheweb.com/', $feed->getSiteUrl());
$this->assertEquals('http://writetheweb.com/', $feed->getId());
$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()->getTimestamp(), '', 1);
$this->assertEquals('editor@writetheweb.com', $feed->items[0]->getAuthor());
$this->assertTrue(strpos($feed->items[1]->getContent(), '<p>After a period of dormancy') === 0);
}
}

View File

@ -1,29 +0,0 @@
<?php
namespace PicoFeed\Parser;
use PHPUnit_Framework_TestCase;
class Rss92ParserTest extends PHPUnit_Framework_TestCase
{
public function testFormatOk()
{
$parser = new Rss92(file_get_contents('tests/fixtures/univers_freebox.xml'));
$feed = $parser->execute();
$this->assertNotFalse($feed);
$this->assertNotEmpty($feed->items);
$this->assertEquals('Univers Freebox', $feed->getTitle());
$this->assertEquals('', $feed->getFeedUrl());
$this->assertEquals('http://www.universfreebox.com/', $feed->getSiteUrl());
$this->assertEquals('http://www.universfreebox.com/', $feed->getId());
$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);
$this->assertEquals('http://www.universfreebox.com/article20302.html', $feed->items[0]->getUrl());
$this->assertEquals('ad23a45af194cc46d5151a9a062c5841b03405e456595c30b742d827e08af2e0', $feed->items[0]->getId());
$this->assertEquals('', $feed->items[0]->getAuthor());
}
}

View File

@ -1,197 +0,0 @@
<?php
namespace PicoFeed\Parser;
use DOMDocument;
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('<meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1"/>'));
$this->assertEquals('iso-8859-1', XmlParser::getEncodingFromMetaTag('<meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1" />'));
$this->assertEquals('iso-8859-1', XmlParser::getEncodingFromMetaTag('<meta http-equiv=\'Content-Type\' content=\'text/html;charset=iso-8859-1\'/>'));
$this->assertEquals('iso-8859-1', XmlParser::getEncodingFromMetaTag('<meta http-equiv=\'Content-Type\' content=\'text/html;charset=iso-8859-1\' />'));
$this->assertEquals('iso-8859-1', XmlParser::getEncodingFromMetaTag('<meta http-equiv=Content-Type content=text/html;charset=iso-8859-1/>'));
$this->assertEquals('iso-8859-1', XmlParser::getEncodingFromMetaTag('<meta http-equiv=Content-Type content=text/html;charset=iso-8859-1 />'));
$this->assertEquals('iso-8859-1', XmlParser::getEncodingFromMetaTag('<meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1">'));
$this->assertEquals('iso-8859-1', XmlParser::getEncodingFromMetaTag('<meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1" >'));
$this->assertEquals('iso-8859-1', XmlParser::getEncodingFromMetaTag('<meta http-equiv=\'Content-Type\' content=\'text/html;charset=iso-8859-1\'>'));
$this->assertEquals('iso-8859-1', XmlParser::getEncodingFromMetaTag('<meta http-equiv=\'Content-Type\' content=\'text/html;charset=iso-8859-1\' >'));
$this->assertEquals('iso-8859-1', XmlParser::getEncodingFromMetaTag('<meta http-equiv=Content-Type content=text/html;charset=iso-8859-1>'));
$this->assertEquals('iso-8859-1', XmlParser::getEncodingFromMetaTag('<meta http-equiv=Content-Type content=text/html;charset=iso-8859-1 >'));
$this->assertEquals('iso-8859-1', XmlParser::getEncodingFromMetaTag('<meta http-equiv="Content-Type" content="text/html;charset=\'iso-8859-1\'">'));
$this->assertEquals('iso-8859-1', XmlParser::getEncodingFromMetaTag('<meta http-equiv="Content-Type" content="\'text/html;charset=iso-8859-1\'">'));
$this->assertEquals('iso-8859-1', XmlParser::getEncodingFromMetaTag('<meta http-equiv="Content-Type" content="\'text/html\';charset=\'iso-8859-1\'">'));
$this->assertEquals('iso-8859-1', XmlParser::getEncodingFromMetaTag('<meta http-equiv=\'Content-Type\' content=\'text/html;charset="iso-8859-1"\'>'));
$this->assertEquals('iso-8859-1', XmlParser::getEncodingFromMetaTag('<meta http-equiv=\'Content-Type\' content=\'"text/html;charset=iso-8859-1"\'>'));
$this->assertEquals('iso-8859-1', XmlParser::getEncodingFromMetaTag('<meta http-equiv=\'Content-Type\' content=\'"text/html";charset="iso-8859-1"\'>'));
$this->assertEquals('iso-8859-1', XmlParser::getEncodingFromMetaTag('<meta http-equiv="Content-Type" content="text/html;;;charset=iso-8859-1">'));
$this->assertEquals('iso-8859-1', XmlParser::getEncodingFromMetaTag('<meta http-equiv="Content-Type" content="text/html;;;charset=\'iso-8859-1\'">'));
$this->assertEquals('iso-8859-1', XmlParser::getEncodingFromMetaTag('<meta http-equiv="Content-Type" content="\'text/html;;;charset=iso-8859-1\'">'));
$this->assertEquals('iso-8859-1', XmlParser::getEncodingFromMetaTag('<meta http-equiv="Content-Type" content="\'text/html\';;;charset=\'iso-8859-1\'">'));
$this->assertEquals('iso-8859-1', XmlParser::getEncodingFromMetaTag('<meta http-equiv=\'Content-Type\' content=\'text/html;;;charset=iso-8859-1\'>'));
$this->assertEquals('windows-1251', XmlParser::getEncodingFromMetaTag('<meta http-equiv=\'Content-Type\' content=\'text/html;;;charset="windows-1251"\'>'));
$this->assertEquals('iso-8859-1', XmlParser::getEncodingFromMetaTag('<meta http-equiv=\'Content-Type\' content=\'"text/html;;;charset=iso-8859-1"\'>'));
$this->assertEquals('iso-8859-1', XmlParser::getEncodingFromMetaTag('<meta http-equiv=\'Content-Type\' content=\'"text/html";;;charset="iso-8859-1"\'>'));
$this->assertEquals('iso-8859-1', XmlParser::getEncodingFromMetaTag('<meta http-equiv = Content-Type content = text/html;charset=iso-8859-1 >'));
$this->assertEquals('iso-8859-1', XmlParser::getEncodingFromMetaTag('<meta content = text/html;charset=iso-8859-1 http-equiv = Content-Type >'));
$this->assertEquals('iso-8859-1', XmlParser::getEncodingFromMetaTag('<meta http-equiv = Content-Type content = text/html ; charset = iso-8859-1 >'));
$this->assertEquals('iso-8859-1', XmlParser::getEncodingFromMetaTag('<meta content = text/html ; charset = iso-8859-1 http-equiv = Content-Type >'));
$this->assertEquals('iso-8859-1', XmlParser::getEncodingFromMetaTag('<meta http-equiv = Content-Type content = text/html ;;; charset = iso-8859-1 >'));
$this->assertEquals('iso-8859-1', XmlParser::getEncodingFromMetaTag('<meta content = text/html ;;; charset = iso-8859-1 http-equiv = Content-Type >'));
$this->assertEquals('iso-8859-1', XmlParser::getEncodingFromMetaTag('<meta http-equiv = Content-Type content = text/html ; ; ; charset = iso-8859-1 >'));
$this->assertEquals('iso-8859-1', XmlParser::getEncodingFromMetaTag('<meta content = text/html ; ; ; charset = iso-8859-1 http-equiv = Content-Type >'));
$this->assertEquals('utf-8', XmlParser::getEncodingFromMetaTag('<meta charset="uTf-8"/>'));
$this->assertEquals('utf-8', XmlParser::getEncodingFromMetaTag('<meta charset="utf-8" />'));
$this->assertEquals('utf-8', XmlParser::getEncodingFromMetaTag('<meta charset=\'Utf-8\'/>'));
$this->assertEquals('utf-8', XmlParser::getEncodingFromMetaTag('<meta charset=\'utf-8\' />'));
$this->assertEquals('utf-8', XmlParser::getEncodingFromMetaTag('<meta charset=utf-8/>'));
$this->assertEquals('utf-8', XmlParser::getEncodingFromMetaTag('<meta charset=utf-8 />'));
$this->assertEquals('utf-8', XmlParser::getEncodingFromMetaTag('<meta charset="utf-8">'));
$this->assertEquals('utf-8', XmlParser::getEncodingFromMetaTag('<meta charset="utf-8" >'));
$this->assertEquals('utf-8', XmlParser::getEncodingFromMetaTag('<meta charset=\'utf-8\'>'));
$this->assertEquals('utf-8', XmlParser::getEncodingFromMetaTag('<meta charset=\'utf-8\' >'));
$this->assertEquals('utf-8', XmlParser::getEncodingFromMetaTag('<meta charset=utf-8>'));
$this->assertEquals('utf-8', XmlParser::getEncodingFromMetaTag('<meta charset=utf-8 >'));
$this->assertEquals('utf-8', XmlParser::getEncodingFromMetaTag('<meta charset = " utf-8 " >'));
$this->assertEquals('utf-8', XmlParser::getEncodingFromMetaTag('<meta charset = \' utf-8 \' >'));
$this->assertEquals('utf-8', XmlParser::getEncodingFromMetaTag('<meta charset = " utf-8 \' >'));
$this->assertEquals('utf-8', XmlParser::getEncodingFromMetaTag('<meta charset = \' utf-8 " >'));
$this->assertEquals('utf-8', XmlParser::getEncodingFromMetaTag('<meta charset = " utf-8 >'));
$this->assertEquals('utf-8', XmlParser::getEncodingFromMetaTag('<meta charset = \' utf-8 >'));
$this->assertEquals('utf-8', XmlParser::getEncodingFromMetaTag('<meta charset = utf-8 \' >'));
$this->assertEquals('utf-8', XmlParser::getEncodingFromMetaTag('<meta charset = utf-8 " >'));
$this->assertEquals('utf-8', XmlParser::getEncodingFromMetaTag('<meta charset = utf-8 >'));
$this->assertEquals('utf-8', XmlParser::getEncodingFromMetaTag('<meta charset = utf-8 />'));
$this->assertEquals('utf-8', XmlParser::getEncodingFromMetaTag('<meta name="title" value="charset=utf-8 — is it really useful (yep)?">'));
$this->assertEquals('utf-8', XmlParser::getEncodingFromMetaTag('<meta value="charset=utf-8 — is it really useful (yep)?" name="title">'));
$this->assertEquals('utf-8', XmlParser::getEncodingFromMetaTag('<meta name="title" content="charset=utf-8 — is it really useful (yep)?">'));
$this->assertEquals('utf-8', XmlParser::getEncodingFromMetaTag('<meta name="charset=utf-8" content="charset=utf-8 — is it really useful (yep)?">'));
$this->assertEquals('utf-8', XmlParser::getEncodingFromMetaTag('<meta content="charset=utf-8 — is it really useful (nope, not here, but gotta admit pretty robust otherwise)?" name="title">'));
$this->assertEquals('iso-8859-1', XmlParser::getEncodingFromMetaTag('<meta http-equiv="Content-Type" content="text/html;charset=iSo-8859-1"/><meta charset="invalid" />'));
}
public function testGetEncodingFromXmlTag()
{
$this->assertEquals('utf-8', XmlParser::getEncodingFromXmlTag("<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet"));
$this->assertEquals('utf-8', XmlParser::getEncodingFromXmlTag('<?xml version="1.0" encoding="UTF-8"?><feed xml:'));
$this->assertEquals('windows-1251', XmlParser::getEncodingFromXmlTag('<?xml version="1.0" encoding="Windows-1251"?><rss version="2.0">'));
$this->assertEquals('', XmlParser::getEncodingFromXmlTag("<?xml version='1.0'?><?xml-stylesheet"));
}
public function testScanForXEE()
{
$xml = <<<XML
<?xml version="1.0"?>
<!DOCTYPE results [<!ENTITY harmless "completely harmless">]>
<results>
<result>This result is &harmless;</result>
</results>
XML;
$this->assertFalse(XmlParser::getDomDocument($xml));
}
public function testScanForXXE()
{
$file = tempnam(sys_get_temp_dir(), 'PicoFeed_XmlParser');
file_put_contents($file, 'Content Injection');
$xml = <<<XML
<?xml version="1.0"?>
<!DOCTYPE root
[
<!ENTITY foo SYSTEM "file://$file">
]>
<results>
<result>&foo;</result>
</results>
XML;
$this->assertFalse(XmlParser::getDomDocument($xml));
unlink($file);
}
public function testScanSimpleXML()
{
return <<<XML
<?xml version="1.0"?>
<results>
<result>test</result>
</results>
XML;
$result = XmlParser::getSimpleXml($xml);
$this->assertTrue($result instanceof SimpleXMLElement);
$this->assertEquals($result->result, 'test');
}
public function testScanDomDocument()
{
return <<<XML
<?xml version="1.0"?>
<results>
<result>test</result>
</results>
XML;
$result = XmlParser::getDomDocument($xml);
$this->assertTrue($result instanceof DOMDocument);
$node = $result->getElementsByTagName('result')->item(0);
$this->assertEquals($node->nodeValue, 'test');
}
public function testScanInvalidXml()
{
$xml = <<<XML
<foo>test</bar>
XML;
$this->assertFalse(XmlParser::getDomDocument($xml));
$this->assertFalse(XmlParser::getSimpleXml($xml));
}
public function testScanXmlWithDTD()
{
$xml = <<<XML
<?xml version="1.0"?>
<!DOCTYPE results [
<!ELEMENT results (result+)>
<!ELEMENT result (#PCDATA)>
]>
<results>
<result>test</result>
</results>
XML;
$result = XmlParser::getDomDocument($xml);
$this->assertTrue($result instanceof DOMDocument);
$this->assertTrue($result->validate());
}
public function testReplaceXPathPrefixWithNamespaceURI()
{
$ns = array('lorem' => 'https://en.wikipedia.org/wiki/Lorem');
$query = '//lorem:title';
$expected = '//*[namespace-uri()="https://en.wikipedia.org/wiki/Lorem" and local-name()="title"]';
$this->assertEquals($expected, XmlParser::replaceXPathPrefixWithNamespaceURI($query, $ns));
$ns = array('lorem' => 'https://en.wikipedia.org/wiki/Lorem', 'ipsum' => 'https://en.wikipedia.org/wiki/Ipsum');
$query = '//lorem:title/ipsum:name';
$expected = '//*[namespace-uri()="https://en.wikipedia.org/wiki/Lorem" and local-name()="title"]/*[namespace-uri()="https://en.wikipedia.org/wiki/Ipsum" and local-name()="name"]';
$this->assertEquals($expected, XmlParser::replaceXPathPrefixWithNamespaceURI($query, $ns));
$ns = array('lorem' => 'https://en.wikipedia.org/wiki/Lorem', 'ipsum' => 'https://en.wikipedia.org/wiki/Ipsum');
$query = '//lorem:title/ipsum:name/@xml:lang';
$expected = '//*[namespace-uri()="https://en.wikipedia.org/wiki/Lorem" and local-name()="title"]/*[namespace-uri()="https://en.wikipedia.org/wiki/Ipsum" and local-name()="name"]/@xml:lang';
$this->assertEquals($expected, XmlParser::replaceXPathPrefixWithNamespaceURI($query, $ns));
}
}

View File

@ -1,188 +0,0 @@
<?php
namespace PicoFeed\Reader;
use PHPUnit_Framework_TestCase;
use PicoFeed\Client\Url;
class FaviconTest extends PHPUnit_Framework_TestCase
{
public function testExtract()
{
$favicon = new Favicon;
$html = '<!DOCTYPE html><html><head>
<link rel="icon" href="http://example.com/myicon.ico" />
</head><body><p>boo</p></body></html>';
$this->assertEquals(array('http://example.com/myicon.ico'), $favicon->extract($html));
// multiple values in rel attribute
$html = '<!DOCTYPE html><html><head>
<link rel="shortcut icon" href="http://example.com/myicon.ico" />
</head><body><p>boo</p></body></html>';
$this->assertEquals(array('http://example.com/myicon.ico'), $favicon->extract($html));
// icon part of another string
$html = '<!DOCTYPE html><html><head>
<link rel="fluid-icon" href="http://example.com/myicon.ico" />
</head><body><p>boo</p></body></html>';
$this->assertEquals(array('http://example.com/myicon.ico'), $favicon->extract($html));
// with other attributes present
$html = '<!DOCTYPE html><html><head>
<link rel="icon" type="image/vnd.microsoft.icon" href="http://example.com/image.ico" />
</head><body><p>boo</p></body></html>';
$this->assertEquals(array('http://example.com/image.ico'), $favicon->extract($html));
// ignore icon in other attribute
$html = '<!DOCTYPE html><html><head>
<link type="icon" href="http://example.com/image.ico" />
</head><body><p>boo</p></body></html>';
// ignores apple icon
$html = '<!DOCTYPE html><html><head>
<link rel="apple-touch-icon" href="assets/img/touch-icon-iphone.png">
<link rel="icon" type="image/png" href="http://example.com/image.png" />
</head><body><p>boo</p></body></html>';
$this->assertEquals(array('http://example.com/image.png'), $favicon->extract($html));
// allows multiple icons
$html = '<!DOCTYPE html><html><head>
<link rel="icon" type="image/png" href="http://example.com/image.png" />
<link rel="icon" type="image/x-icon" href="http://example.com/image.ico"/>
</head><body><p>boo</p></body></html>';
$this->assertEquals(array('http://example.com/image.png', 'http://example.com/image.ico'), $favicon->extract($html));
// empty array with broken html
$html = '!DOCTYPE html html head
link rel="icon" type="image/png" href="http://example.com/image.png" /
link rel="icon" type="image/x-icon" href="http://example.com/image.ico"/
/head body /p boo /p body /html';
$this->assertEquals(array(), $favicon->extract($html));
// empty array on no input
$this->assertEquals(array(), $favicon->extract(''));
// empty array on no icon found
$html = '<!DOCTYPE html><html><head>
</head><body><p>boo</p></body></html>';
$this->assertEquals(array(), $favicon->extract($html));
}
/**
* @group online
*/
public function testExists()
{
$favicon = new Favicon;
$this->assertTrue($favicon->exists('https://miniflux.net/favicon.ico'));
$this->assertFalse($favicon->exists('http://minicoders.com/favicon.ico'));
$this->assertFalse($favicon->exists('http://blabla'));
$this->assertFalse($favicon->exists(''));
}
/**
* @group online
*/
public function testFind_inMeta()
{
$favicon = new Favicon;
// favicon in meta
$this->assertEquals(
'http://miniflux.net/assets/img/favicon.png',
$favicon->find('http://miniflux.net')
);
$this->assertNotEmpty($favicon->getContent());
}
// public function testFind_inRootDir()
// {
// // favicon not in meta, only in website root (need example page)
// $favicon = new Favicon;
//
// $this->assertEquals(
// 'http://minicoders.com/favicon.ico',
// $favicon->find('http://minicoders.com')
// );
// }
public function testFind_noIcons()
{
$favicon = new Favicon;
$this->assertEquals(
'',
$favicon->find('http://minicoders.com')
);
$this->assertEmpty($favicon->getContent());
}
/**
* @group online
*/
public function testFind_directLinkFirst()
{
$favicon = new Favicon;
$this->assertEquals(
'http://miniflux.net/assets/img/touch-icon-ipad.png',
$favicon->find('http://miniflux.net', '/assets/img/touch-icon-ipad.png')
);
$this->assertNotEmpty($favicon->getContent());
}
/**
* @group online
*/
public function testFind_fallsBackToExtract()
{
$favicon = new Favicon;
$this->assertEquals(
'http://miniflux.net/assets/img/favicon.png',
$favicon->find('http://miniflux.net','/nofavicon.ico')
);
$this->assertNotEmpty($favicon->getContent());
}
/**
* @group online
*/
public function testDataUri()
{
$favicon = new Favicon;
$this->assertEquals(
'http://miniflux.net/assets/img/favicon.png',
$favicon->find('http://miniflux.net')
);
$expected = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAGJwAABicBTVTYxwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAALMSURBVHic7Zo7a1RRFIW/I8YXaBBEJRJEU8RqQBBBQRBEWxHBwlZUsLRWUFBsA4L4G4IY0TaF2PhEEQwmhuADJIkRUUOMr2RZ3Em8mcxkzrkPtjhnwS7msveadT/Ofc44SbSyllkHsFYEYB3AWhGAdQBrRQDWAawVAVgHsFYEYB3AWhGAdQBrLS/L2Dm3CdgFbK3WDPC6Wi8kjWX03QBUgG3AdmAN8LFaT4CnCnjEdbW9zrk+YL3n/AVJd2vmDwKngMNAW4O538BNoEfSfa+gzu0DzgBHl/AFGAN6gcuSPjQ1lrSggHFAnnUsNdcO3AiYnas7wNraHCnfLcC9DL6TwNlGvvP+RQAAdgIjGULO1XOgs06WQ8BEDl8BPVRXeikAgK4CQgp4B7SnchwnOW/k9RVwviwAp4HBgkIKuJ5aUd8K9P0JVMoA8LnAkAJmgSPA24J9BfTXA1DvKjAObOT/k4BuScPpjWXcCM0Co8CnErynSFbHTIZZB5xYtDXnIZCuCeAkqUsa0AlcyeiXrtvAnpTvamA/8CbQ50HR54C5egV0LHEtv5hj588t4dsBvA/wmgbaigbwneTYanyzkayELDvf2/RGBi4FelaKBnC1Wciq70Cg7y+gy8O3O9D3QHq+iJPgNc++R4G+/ZJGPPqGSU68vlqX/pAXwKCkl569XwK9b/k0SZoleRL0VaEAngX0TgZ6Pw7obf7U91cr0x/yAhgK6A0BIMB3ZUFyq5tJeQGELL2vAb1TkqYD+lcF9C5QXgAhO/WjJF/I8WYrL4CQnfoXfBep5V+KRgDWAawVAVgHsFYEYB3AWhGAdQBrRQDWAawVAVgHsFYEYB3AWi0PoN6Po3uBFZ7zA5ImvL7Iuc3ADk/faUkPPXtxzu0m+a+Qj4Ykjc7P1gJoNbX8IRABWAewVgRgHcBaEYB1AGtFANYBrBUBWAewVssD+AMBy6wzsaDiAwAAAABJRU5ErkJggg==';
$this->assertEquals($expected, $favicon->getDataUri());
}
/**
* @group online
*/
public function testDataUri_withBadContentType()
{
$favicon = new Favicon;
$this->assertNotEmpty($favicon->find('http://www.lemonde.fr/'));
$expected = 'data:image/x-icon;base64,AAABAAIAICAAAAEACACoCAAAJgAAABAQEAABAAQAKAEAAM4IAAAoAAAAIAAAAEAAAAABAAgAAAAAAAAEAAASCwAAEgsAAAABAAAAAQAAAAAAAAEBAQACAgIAAwMDAAQEBAAFBQUABgYGAAcHBwAICAgACQkJAAoKCgALCwsADAwMAA0NDQAODg4ADw8PABAQEAAREREAEhISABMTEwAUFBQAFRUVABYWFgAXFxcAGBgYABkZGQAaGhoAGxsbABwcHAAdHR0AHh4eAB8fHwAgICAAISEhACIiIgAjIyMAJCQkACUlJQAmJiYAJycnACgoKAApKSkAKioqACsrKwAsLCwALS0tAC4uLgAvLy8AMDAwADExMQAyMjIAMzMzADQ0NAA1NTUANjY2ADc3NwA4ODgAOTk5ADo6OgA7OzsAPDw8AD09PQA+Pj4APz8/AEBAQABBQUEAQkJCAENDQwBEREQARUVFAEZGRgBHR0cASEhIAElJSQBKSkoAS0tLAExMTABNTU0ATk5OAE9PTwBQUFAAUVFRAFJSUgBTU1MAVFRUAFVVVQBWVlYAV1dXAFhYWABZWVkAWlpaAFtbWwBcXFwAXV1dAF5eXgBfX18AYGBgAGFhYQBiYmIAY2NjAGRkZABlZWUAZmZmAGdnZwBoaGgAaWlpAGpqagBra2sAbGxsAG1tbQBubm4Ab29vAHBwcABxcXEAcnJyAHNzcwB0dHQAdXV1AHZ2dgB3d3cAeHh4AHl5eQB6enoAe3t7AHx8fAB9fX0Afn5+AH9/fwCAgIAAgYGBAIKCggCDg4MAhISEAIWFhQCGhoYAh4eHAIiIiACJiYkAioqKAIuLiwCMjIwAjY2NAI6OjgCPj48AkJCQAJGRkQCSkpIAk5OTAJSUlACVlZUAlpaWAJeXlwCYmJgAmZmZAJqamgCbm5sAnJycAJ2dnQCenp4An5+fAKCgoAChoaEAoqKiAKOjowCkpKQApaWlAKampgCnp6cAqKioAKmpqQCqqqoAq6urAKysrACtra0Arq6uAK+vrwCwsLAAsbGxALKysgCzs7MAtLS0ALW1tQC2trYAt7e3ALi4uAC5ubkAurq6ALu7uwC8vLwAvb29AL6+vgC/v78AwMDAAMHBwQDCwsIAw8PDAMTExADFxcUAxsbGAMfHxwDIyMgAycnJAMrKygDLy8sAzMzMAM3NzQDOzs4Az8/PANDQ0ADR0dEA0tLSANPT0wDU1NQA1dXVANbW1gDX19cA2NjYANnZ2QDa2toA29vbANzc3ADd3d0A3t7eAN/f3wDg4OAA4eHhAOLi4gDj4+MA5OTkAOXl5QDm5uYA5+fnAOjo6ADp6ekA6urqAOvr6wDs7OwA7e3tAO7u7gDv7+8A8PDwAPHx8QDy8vIA8/PzAPT09AD19fUA9vb2APf39wD4+PgA+fn5APr6+gD7+/sA/Pz8AP39/QD+/v4A////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAElIAAAAAMo5CAAAAQo5CAAAAADK+QgAAAAAAAAAAAAASzr5SAEKuzv6uIgBCvv6+MgASzs7+3kIAAAAAAAAAAAASjv7+/v7+/v7ugkK+/v7uAEKu/v7+/q4AAAAAAAAAAAAAQq7+/v7+vlJyQr7+/v4AQr7+/v4iUgAAAAAAAAAAAAAAABJizjIAAABCvv7+/gBCvv7+/gAAAAAAAAAAAAAAAAAAADLO3lIAAEK+/v7+AEK+/v7+AAAAAAAAAAAAAAAAAAAAQq7+/oIAQr7+/v4AQr7+/v4AAAAAAAAAAAAAAAAAAABCvv7+7gBCvv7+/gBCvv7+/gAAAAAAAAAAAAAAAAAAAEK+/v7+AEK+/v7+AEK+/v7+AAAAAAAAAAAAAAAAAAAAQr7+/v4AQr7+/v4AQr7+/v4AAAAAAAAAAAAAAAAAAABCvv7+/gBCvv7+/gBCvv7+/gAAAAAAAAAAAAAAAAAAAEK+/v7+AEK+/v7+AEK+/v7+AAAAAAAAAAAAAAAAAAAAQr7+/v4AQr7+/v4AQo7+/v4AAAAAAAAAAAAAAAAAAABCvv7+/gBCvv7+/gAAnv7+/gAAAAAAAAAAAAAAAAAAAEK+/v7+AEK+/v7+AAAArv7+AAAAAAAAAAAAAABSciIAUp7+/v4Anp7+/v4AQr6e/v5iAAAAAAAAAAAAAM7e/v7e7v7+/q6+7v7+/q6+3v7+/u5yAAAAAAAAAAAAgu7+/v7+/v7+rv7+/u5irv7+/v6+gt4yAAAAAAAAAAAAju4igt7+/mIAQs7OIgAAQt7eQgAAAAAAAAAAAAAAAAAAvkIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAAAQAAAAIAAAAAEABAAAAAAAgAAAABILAAASCwAAEAAAABAAAAAAAAAAEhISACIiIgAyMjIAQkJCAFJSUgBiYmIAcnJyAIKCggCOjo4Anp6eAK6urgDOzs4A3t7eAO7u7gD+/v4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGBKI5IaYAAAbf/p/0/7AAABfCT/T/AAAABPxP9P8AAAAE/0/0/wAAAAT/T/T/AAAABP9P9P8AAAEk/1/x3xAACP//7+7/wQABt7k6JKIQAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA';
$this->assertEquals($expected, $favicon->getDataUri());
}
}

View File

@ -1,269 +0,0 @@
<?php
namespace PicoFeed\Reader;
use PHPUnit_Framework_TestCase;
class ReaderTest extends PHPUnit_Framework_TestCase
{
public function testPrependScheme()
{
$reader = new Reader;
$this->assertEquals('http://http.com', $reader->prependScheme('http.com'));
$this->assertEquals('http://boo.com', $reader->prependScheme('boo.com'));
$this->assertEquals('http://google.com', $reader->prependScheme('http://google.com'));
$this->assertEquals('https://google.com', $reader->prependScheme('https://google.com'));
}
/**
* @group online
*/
public function testDownloadHTTP()
{
$reader = new Reader;
$feed = $reader->download('http://wordpress.org/news/feed/')->getContent();
$this->assertNotEmpty($feed);
}
/**
* @group online
*/
public function testDownloadHTTPS()
{
$reader = new Reader;
$feed = $reader->download('https://wordpress.org/news/feed/')->getContent();
$this->assertNotEmpty($feed);
}
/**
* @group online
*/
public function testDownloadCache()
{
$reader = new Reader;
$resource = $reader->download('http://linuxfr.org/robots.txt');
$this->assertTrue($resource->isModified());
$lastModified = $resource->getLastModified();
$etag = $resource->getEtag();
$reader = new Reader;
$resource = $reader->download('http://linuxfr.org/robots.txt', $lastModified, $etag);
$this->assertFalse($resource->isModified());
}
public function testDetectFormat()
{
$reader = new Reader;
$this->assertEquals('Rss20', $reader->detectFormat(file_get_contents('tests/fixtures/podbean.xml')));
$reader = new Reader;
$this->assertEquals('Rss20', $reader->detectFormat(file_get_contents('tests/fixtures/jeux-linux.fr.xml')));
$reader = new Reader;
$this->assertEquals('Rss20', $reader->detectFormat(file_get_contents('tests/fixtures/sametmax.xml')));
$reader = new Reader;
$this->assertEquals('Rss92', $reader->detectFormat(file_get_contents('tests/fixtures/rss_0.92.xml')));
$reader = new Reader;
$this->assertEquals('Rss91', $reader->detectFormat(file_get_contents('tests/fixtures/rss_0.91.xml')));
$reader = new Reader;
$this->assertEquals('Rss10', $reader->detectFormat(file_get_contents('tests/fixtures/planete-jquery.xml')));
$reader = new Reader;
$this->assertEquals('Rss20', $reader->detectFormat(file_get_contents('tests/fixtures/rss2sample.xml')));
$reader = new Reader;
$this->assertEquals('Atom', $reader->detectFormat(file_get_contents('tests/fixtures/atomsample.xml')));
$reader = new Reader;
$this->assertEquals('Rss20', $reader->detectFormat(file_get_contents('tests/fixtures/cercle.psy.xml')));
$reader = new Reader;
$this->assertEquals('Rss20', $reader->detectFormat(file_get_contents('tests/fixtures/ezrss.it')));
$content = '<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2titles.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemtitles.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:media="http://search.yahoo.com/mrss/" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">';
$reader = new Reader;
$this->assertEquals('Rss20', $reader->detectFormat($content));
}
public function testFindRssFeed()
{
$reader = new Reader;
$html = '<!DOCTYPE html><html><head>
<link type="application/rss+xml" href="http://miniflux.net/feed">
</head><body><p>boo</p></body></html>';
$feeds = $reader->find('http://miniflux.net/', $html);
$this->assertEquals(array('http://miniflux.net/feed'), $feeds);
}
public function testFindAtomFeed()
{
$reader = new Reader;
$html = '<!DOCTYPE html><html><head>
<link type="application/atom+xml" href="http://miniflux.net/feed">
</head><body><p>boo</p></body></html>';
$feeds = $reader->find('http://miniflux.net/', $html);
$this->assertEquals(array('http://miniflux.net/feed'), $feeds);
}
public function testFindFeedNotInHead()
{
$reader = new Reader;
$html = '<!DOCTYPE html><html><head></head>
<body>
<link type="application/atom+xml" href="http://miniflux.net/feed">
<p>boo</p></body></html>';
$feeds = $reader->find('http://miniflux.net/', $html);
$this->assertEquals(array('http://miniflux.net/feed'), $feeds);
}
public function testFindNoFeedPresent()
{
$reader = new Reader;
$html = '<!DOCTYPE html><html><head>
</head><body><p>boo</p></body></html>';
$feeds = $reader->find('http://miniflux.net/', $html);
$this->assertEquals(array(), $feeds);
}
public function testFindIgnoreUnknownType()
{
$reader = new Reader;
$html = '<!DOCTYPE html><html><head>
<link type="application/flux+xml" href="http://miniflux.net/feed">
</head><body><p>boo</p></body></html>';
$feeds = $reader->find('http://miniflux.net/', $html);
$this->assertEquals(array(), $feeds);
}
public function testFindIgnoreTypeInOtherAttribute()
{
$reader = new Reader;
$html = '<!DOCTYPE html><html><head>
<link rel="application/rss+xml" href="http://miniflux.net/feed">
</head><body><p>boo</p></body></html>';
$feeds = $reader->find('http://miniflux.net/', $html);
$this->assertEquals(array(), $feeds);
}
public function testFindWithOtherAttributesPresent()
{
$reader = new Reader;
$html = '<!DOCTYPE html><html><head>
<link rel="alternate" type="application/rss+xml" title="RSS" href="http://miniflux.net/feed">
</head><body><p>boo</p></body></html>';
$feeds = $reader->find('http://miniflux.net/', $html);
$this->assertEquals(array('http://miniflux.net/feed'), $feeds);
}
public function testFindMultipleFeeds()
{
$reader = new Reader;
$html = '<!DOCTYPE html><html><head>
<link rel="alternate" type="application/rss+xml" title="CNN International: Top Stories" href="http://rss.cnn.com/rss/edition.rss"/>
<link rel="alternate" type="application/rss+xml" title="Connect The World" href="http://rss.cnn.com/rss/edition_connecttheworld.rss"/>
<link rel="alternate" type="application/rss+xml" title="World Sport" href="http://rss.cnn.com/rss/edition_worldsportblog.rss"/>
</head><body><p>boo</p></body></html>';
$feeds = $reader->find('http://www.cnn.com/services/rss/', $html);
$this->assertEquals(
array(
'http://rss.cnn.com/rss/edition.rss',
'http://rss.cnn.com/rss/edition_connecttheworld.rss',
'http://rss.cnn.com/rss/edition_worldsportblog.rss'
),
$feeds
);
}
public function testFindWithInvalidHTML()
{
$reader = new Reader;
$html = '!DOCTYPE html html head
link type="application/rss+xml" href="http://miniflux.net/feed"
/head body /p boo /p body /html';
$feeds = $reader->find('http://miniflux.net/', '');
$this->assertEquals(array(), $feeds);
}
public function testFindWithHtmlParamEmptyString()
{
$reader = new Reader;
$feeds = $reader->find('http://miniflux.net/', '');
$this->assertEquals(array(), $feeds);
}
/**
* @group online
*/
public function testDiscover()
{
$reader = new Reader;
$client = $reader->discover('http://www.universfreebox.com/');
$this->assertEquals('http://www.universfreebox.com/backend.php', $client->getUrl());
$this->assertInstanceOf('PicoFeed\Parser\Rss20', $reader->getParser($client->getUrl(), $client->getContent(), $client->getEncoding()));
$reader = new Reader;
$client = $reader->discover('http://planete-jquery.fr');
$this->assertInstanceOf('PicoFeed\Parser\Rss10', $reader->getParser($client->getUrl(), $client->getContent(), $client->getEncoding()));
$reader = new Reader;
$client = $reader->discover('http://cabinporn.com/');
$this->assertEquals('http://cabinporn.com/rss', $client->getUrl());
$this->assertInstanceOf('PicoFeed\Parser\Rss20', $reader->getParser($client->getUrl(), $client->getContent(), $client->getEncoding()));
$reader = new Reader;
$client = $reader->discover('http://linuxfr.org/');
$this->assertEquals('http://linuxfr.org/news.atom', $client->getUrl());
$this->assertInstanceOf('PicoFeed\Parser\Atom', $reader->getParser($client->getUrl(), $client->getContent(), $client->getEncoding()));
}
public function testGetParserUsesHTTPEncoding()
{
$reader = new Reader;
$parser = $reader->getParser('http://blah', file_get_contents('tests/fixtures/cercle.psy.xml'), 'iso-8859-1');
$feed = $parser->execute();
$this->assertInstanceOf('PicoFeed\Parser\Rss20', $parser);
$this->assertNotEmpty($feed->items);
}
public function testGetParserUsesSiteURL()
{
$reader = new Reader;
$parser = $reader->getParser('http://groovehq.com/', file_get_contents('tests/fixtures/groovehq.xml'), '');
$feed = $parser->execute();
$this->assertEquals('http://groovehq.com/articles.xml', $feed->getFeedUrl());
}
public function testFeedsReportedAsNotWorking()
{
$reader = new Reader;
$parser = $reader->getParser('http://blah', file_get_contents('tests/fixtures/ezrss.it'), '');
$feed = $parser->execute();
$this->assertNotEmpty($feed->items);
}
}

View File

@ -1,86 +0,0 @@
<?php
namespace PicoFeed\Scraper;
use PHPUnit_Framework_TestCase;
use PicoFeed\Config\Config;
class RuleLoaderTest extends PHPUnit_Framework_TestCase
{
public function testGetRulesFolders()
{
// No custom path
$loader = new RuleLoader(new Config);
$dirs = $loader->getRulesFolders();
$this->assertNotEmpty($dirs);
$this->assertCount(1, $dirs);
$this->assertTrue(strpos($dirs[0], '/../Rules') !== false);
// Custom path
$config = new Config;
$config->setGrabberRulesFolder('/foobar/rules');
$loader = new RuleLoader($config);
$dirs = $loader->getRulesFolders();
$this->assertNotEmpty($dirs);
$this->assertCount(2, $dirs);
$this->assertTrue(strpos($dirs[0], '/../Rules') !== false);
$this->assertEquals('/foobar/rules', $dirs[1]);
// No custom path with empty config object
$loader = new RuleLoader(new Config);
$dirs = $loader->getRulesFolders();
$this->assertNotEmpty($dirs);
$this->assertCount(1, $dirs);
$this->assertTrue(strpos($dirs[0], '/../Rules') !== false);
}
public function testLoadRuleFile()
{
$loader = new RuleLoader(new Config);
$dirs = $loader->getRulesFolders();
$this->assertEmpty($loader->loadRuleFile($dirs[0], array('test')));
$this->assertNotEmpty($loader->loadRuleFile($dirs[0], array('test', 'xkcd.com')));
}
public function testGetRulesFileList()
{
$loader = new RuleLoader(new Config);
$this->assertEquals(
array('www.google.ca', 'google.ca', '.google.ca', 'www'),
$loader->getRulesFileList('www.google.ca')
);
$loader = new RuleLoader(new Config);
$this->assertEquals(
array('google.ca', '.google.ca', 'google'),
$loader->getRulesFileList('google.ca')
);
$loader = new RuleLoader(new Config);
$this->assertEquals(
array('a.b.c.d', 'b.c.d', '.b.c.d', 'a'),
$loader->getRulesFileList('a.b.c.d')
);
$loader = new RuleLoader(new Config);
$this->assertEquals(
array('localhost'),
$loader->getRulesFileList('localhost')
);
}
public function testGetRules()
{
$loader = new RuleLoader(new Config);
$this->assertNotEmpty($loader->getRules('http://www.egscomics.com/index.php?id=1690'));
$loader = new RuleLoader(new Config);
$this->assertEmpty($loader->getRules('http://localhost/foobar'));
}
}

View File

@ -1,89 +0,0 @@
<?php
namespace PicoFeed\Scraper;
use PHPUnit_Framework_TestCase;
use PicoFeed\Reader\Reader;
use PicoFeed\Config\Config;
class ScraperTest extends PHPUnit_Framework_TestCase
{
/**
* @group online
*/
public function testUrlScraper()
{
$grabber = new Scraper(new Config);
$grabber->setUrl('http://theonion.com.feedsportal.com/c/34529/f/632231/s/309a7fe4/sc/20/l/0L0Stheonion0N0Carticles0Cobama0Ethrows0Eup0Eright0Ethere0Eduring0Esyria0Emeeting0H336850C/story01.htm');
$grabber->execute();
$this->assertTrue($grabber->hasRelevantContent());
$grabber = new Scraper(new Config);
$grabber->setUrl('http://www.lemonde.fr/proche-orient/article/2013/08/30/la-france-nouvelle-plus-ancienne-alliee-des-etats-unis_3469218_3218.html');
$grabber->execute();
$this->assertTrue($grabber->hasRelevantContent());
$grabber = new Scraper(new Config);
$grabber->setUrl('http://www.inc.com/suzanne-lucas/why-employee-turnover-is-so-costly.html');
$grabber->execute();
$this->assertTrue($grabber->hasRelevantContent());
$grabber = new Scraper(new Config);
$grabber->setUrl('http://arstechnica.com/information-technology/2013/08/sysadmin-security-fail-nsa-finds-snowden-hijacked-officials-logins/');
$grabber->execute();
$this->assertTrue($grabber->hasRelevantContent());
$grabber = new Scraper(new Config);
$grabber->disableCandidateParser();
$grabber->setUrl('http://linuxfr.org/news/grammalecte-correcteur-grammatical');
$grabber->execute();
$this->assertFalse($grabber->hasRelevantContent());
}
/**
* @group online
*/
public function testRuleParser()
{
$grabber = new Scraper(new Config);
$grabber->setUrl('http://www.egscomics.com/index.php?id=1690');
$grabber->execute();
$this->assertTrue($grabber->hasRelevantContent());
$this->assertEquals('<img title="2013-08-22" src="comics/../comics/1377151029-2013-08-22.png" id="comic" border="0" />', $grabber->getRelevantContent());
}
/**
* @group online
*/
public function testGrabContentRegex()
{
$grabber = new Scraper(new Config);
$grabber->setUrl('http://penny-arcade.com/comic/2015/04/13/101-part-one');
$grabber->execute();
$this->assertTrue($grabber->hasRelevantContent());
$this->assertEquals('<img src="http://art.penny-arcade.com/photos/i-tBMHkzG/0/1050x10000/i-tBMHkzG-1050x10000.jpg" alt="101, Part One"/>', $grabber->getRelevantContent());
$grabber->setUrl('http://penny-arcade.com/news/post/2015/04/15/101-part-two');
$grabber->execute();
$this->assertTrue($grabber->hasRelevantContent());
$this->assertContains('101, Part Two', $grabber->getRelevantContent());
}
/**
* @group online
*/
public function testRssGrabContent()
{
$reader = new Reader;
$client = $reader->download('http://www.egscomics.com/rss.php');
$parser = $reader->getParser($client->getUrl(), $client->getContent(), $client->getEncoding());
$parser->enableContentGrabber();
$feed = $parser->execute();
$this->assertTrue(is_array($feed->items));
$this->assertTrue(strpos($feed->items[0]->content, '<img') >= 0);
}
}

View File

@ -1,70 +0,0 @@
<?php
namespace PicoFeed\Serialization;
use PHPUnit_Framework_TestCase;
class ExportTest extends PHPUnit_Framework_TestCase
{
public function testOuput()
{
$feeds = array(
array(
'title' => 'Site title',
'description' => 'Optional description',
'site_url' => 'http://blabla.fr/',
),
array(
'title' => 'Site title',
'description' => 'Optional description',
'site_url' => 'http://petitcodeur.fr/',
'feed_url' => 'http://petitcodeur.fr/feed.xml',
)
);
$export = new Export($feeds);
$opml = $export->execute();
$expected = '<?xml version="1.0" encoding="utf-8"?>
<opml><head><title>OPML Export</title></head><body><outline xmlUrl="http://petitcodeur.fr/feed.xml" htmlUrl="http://petitcodeur.fr/" title="Site title" text="Site title" description="Optional description" type="rss" version="RSS"/></body></opml>
';
$this->assertEquals($expected, $opml);
}
public function testCategoryOuput()
{
$feeds = array(
'my category' => array(
array(
'title' => 'Site title',
'description' => 'Optional description',
'site_url' => 'http://blabla.fr/',
),
array(
'title' => 'Site title',
'description' => 'Optional description',
'site_url' => 'http://petitcodeur.fr/',
'feed_url' => 'http://petitcodeur.fr/feed.xml',
)
),
'another category' => array(
array(
'title' => 'Site title',
'description' => 'Optional description',
'site_url' => 'http://youpi.ici/',
'feed_url' => 'http://youpi.ici/feed.xml',
)
)
);
$export = new Export($feeds);
$opml = $export->execute();
$expected = '<?xml version="1.0" encoding="utf-8"?>
<opml><head><title>OPML Export</title></head><body><outline text="my category"><outline xmlUrl="http://petitcodeur.fr/feed.xml" htmlUrl="http://petitcodeur.fr/" title="Site title" text="Site title" description="Optional description" type="rss" version="RSS"/></outline><outline text="another category"><outline xmlUrl="http://youpi.ici/feed.xml" htmlUrl="http://youpi.ici/" title="Site title" text="Site title" description="Optional description" type="rss" version="RSS"/></outline></body></opml>
';
$this->assertEquals($expected, $opml);
}
}

View File

@ -1,61 +0,0 @@
<?php
namespace PicoFeed\Serialization;
use PHPUnit_Framework_TestCase;
class ImportTest extends PHPUnit_Framework_TestCase
{
public function testMalFormedFormat()
{
$import = new Import('boo');
$this->assertFalse($import->execute());
}
public function testFormat()
{
$import = new Import(file_get_contents('tests/fixtures/subscriptionList.opml'));
$entries = $import->execute();
$this->assertEquals(14, count($entries));
$this->assertEquals('CNET News.com', $entries[0]->title);
$this->assertEquals('http://news.com.com/2547-1_3-0-5.xml', $entries[0]->feed_url);
$this->assertEquals('http://news.com.com/', $entries[0]->site_url);
}
public function testGoogleReader()
{
$import = new Import(file_get_contents('tests/fixtures/google-reader.opml'));
$entries = $import->execute();
$this->assertEquals(22, count($entries));
$this->assertEquals('Code', $entries[21]->category);
$this->assertEquals('Vimeo / CocoaheadsRNS', $entries[21]->title);
$this->assertEquals('http://vimeo.com/cocoaheadsrns/videos/rss', $entries[21]->feed_url);
$this->assertEquals('http://vimeo.com/cocoaheadsrns/videos', $entries[21]->site_url);
}
public function testTinyTinyRss()
{
$import = new Import(file_get_contents('tests/fixtures/tinytinyrss.opml'));
$entries = $import->execute();
$this->assertEquals(2, count($entries));
$this->assertEquals('coding', $entries[1]->category);
$this->assertEquals('Planète jQuery', $entries[1]->title);
$this->assertEquals('http://feeds.feedburner.com/PlaneteJqueryFr', $entries[1]->feed_url);
$this->assertEquals('http://planete-jquery.fr', $entries[1]->site_url);
}
public function testNewsBeuter()
{
$import = new Import(file_get_contents('tests/fixtures/newsbeuter.opml'));
$entries = $import->execute();
$this->assertEquals(35, count($entries));
$this->assertEquals('', $entries[1]->category);
$this->assertEquals('code.flickr.com', $entries[1]->title);
$this->assertEquals('http://code.flickr.net/feed/', $entries[1]->feed_url);
$this->assertEquals('http://code.flickr.net', $entries[1]->site_url);
}
}

View File

@ -1,90 +0,0 @@
<?php
namespace PicoFeed\Syndication;
use PHPUnit_Framework_TestCase;
class AtomWriterTest extends PHPUnit_Framework_TestCase
{
public function testWriter()
{
$writer = new Atom();
$writer->title = 'My site';
$writer->site_url = 'http://boo/';
$writer->feed_url = 'http://boo/feed.atom';
$writer->author = array(
'name' => 'Me',
'url' => 'http://me',
'email' => 'me@here'
);
$writer->items[] = array(
'title' => 'My article 1',
'updated' => strtotime('-2 days'),
'url' => 'http://foo/bar',
'summary' => 'Super summary',
'content' => '<p>content</p>'
);
$writer->items[] = array(
'title' => 'My article 2',
'updated' => strtotime('-1 day'),
'url' => 'http://foo/bar2',
'summary' => 'Super summary 2',
'content' => '<p>content 2 &nbsp; &copy; 2015</p>',
'author' => array(
'name' => 'Me too',
)
);
$writer->items[] = array(
'title' => 'My article 3',
'url' => 'http://foo/bar3'
);
$generated_output = $writer->execute();
$expected_output = '<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<generator uri="https://github.com/fguillot/picoFeed">PicoFeed</generator>
<title>My site</title>
<id>http://boo/</id>
<updated>'.date(DATE_ATOM).'</updated>
<link rel="alternate" type="text/html" href="http://boo/"/>
<link rel="self" type="application/atom+xml" href="http://boo/feed.atom"/>
<author>
<name>Me</name>
<email>me@here</email>
<uri>http://me</uri>
</author>
<entry>
<title>My article 1</title>
<id>http://foo/bar</id>
<updated>'.date(DATE_ATOM, strtotime('-2 days')).'</updated>
<link rel="alternate" type="text/html" href="http://foo/bar"/>
<summary>Super summary</summary>
<content type="html"><![CDATA[<p>content</p>]]></content>
</entry>
<entry>
<title>My article 2</title>
<id>http://foo/bar2</id>
<updated>'.date(DATE_ATOM, strtotime('-1 day')).'</updated>
<link rel="alternate" type="text/html" href="http://foo/bar2"/>
<summary>Super summary 2</summary>
<content type="html"><![CDATA[<p>content 2 &nbsp; &copy; 2015</p>]]></content>
<author>
<name>Me too</name>
</author>
</entry>
<entry>
<title>My article 3</title>
<id>http://foo/bar3</id>
<updated>'.date(DATE_ATOM).'</updated>
<link rel="alternate" type="text/html" href="http://foo/bar3"/>
</entry>
</feed>
';
$this->assertEquals($expected_output, $generated_output);
}
}

View File

@ -1,85 +0,0 @@
<?php
namespace PicoFeed\Syndication;
use PHPUnit_Framework_TestCase;
class Rss20WriterTest extends PHPUnit_Framework_TestCase
{
public function testWriter()
{
$writer = new Rss20();
$writer->title = 'My site';
$writer->site_url = 'http://boo/';
$writer->feed_url = 'http://boo/feed.atom';
$writer->author = array(
'name' => 'Me',
'url' => 'http://me',
'email' => 'me@here'
);
$writer->items[] = array(
'title' => 'My article 1',
'updated' => strtotime('-2 days'),
'url' => 'http://foo/bar',
'summary' => 'Super summary',
'content' => '<p>content</p>'
);
$writer->items[] = array(
'title' => 'My article 2',
'updated' => strtotime('-1 day'),
'url' => 'http://foo/bar2',
'summary' => 'Super summary 2',
'content' => '<p>content 2 &nbsp; &copy; 2015</p>',
'author' => array(
'name' => 'Me too',
)
);
$writer->items[] = array(
'title' => 'My article 3',
'url' => 'http://foo/bar3'
);
$generated_output = $writer->execute();
$expected_output = '<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<generator>PicoFeed (https://github.com/fguillot/picoFeed)</generator>
<title>My site</title>
<description>My site</description>
<pubDate>'.date(DATE_RSS).'</pubDate>
<atom:link href="http://boo/feed.atom" rel="self" type="application/rss+xml"/>
<link>http://boo/</link>
<webMaster>me@here (Me)</webMaster>
<item>
<title>My article 1</title>
<link>http://foo/bar</link>
<guid isPermaLink="true">http://foo/bar</guid>
<pubDate>'.date(DATE_RSS, strtotime('-2 days')).'</pubDate>
<description>Super summary</description>
<content:encoded><![CDATA[<p>content</p>]]></content:encoded>
</item>
<item>
<title>My article 2</title>
<link>http://foo/bar2</link>
<guid isPermaLink="true">http://foo/bar2</guid>
<pubDate>'.date(DATE_RSS, strtotime('-1 day')).'</pubDate>
<description>Super summary 2</description>
<content:encoded><![CDATA[<p>content 2 &nbsp; &copy; 2015</p>]]></content:encoded>
</item>
<item>
<title>My article 3</title>
<link>http://foo/bar3</link>
<guid isPermaLink="true">http://foo/bar3</guid>
<pubDate>'.date(DATE_RSS).'</pubDate>
</item>
</channel>
</rss>
';
$this->assertEquals($expected_output, $generated_output);
}
}

View File

@ -1,93 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<feed
xmlns="http://www.w3.org/2005/Atom"
xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/"
xmlns:blogger="http://schemas.google.com/blogger/2008"
xmlns:georss="http://www.georss.org/georss"
xmlns:gd="http://schemas.google.com/g/2005"
xmlns:thr="http://purl.org/syndication/thread/1.0"
xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;DUYHQns6cCp7ImA9WhBTEUo.&quot;">
<title xml:lang="ru">
литература на русском языке,
либо написанная русскими авторами
</title>
<subtitle type="html">Зародилась во второй половине X века, однако до XIX века,
когда начался её «золотой век», была практически неизвестна
в мире.</subtitle>
<logo>https://ru.wikipedia.org/static/images/project-logos/ruwiki.png</logo>
<icon>https://ru.wikipedia.org/static/favicon/wikipedia.ico</icon>
<link rel="alternate" href="https://en.wikipedia.org/wiki/Category:Russian-language_literature"/>
<link rel="self" href="https://feeds.wikipedia.org/category/Russian-language_literature.xml"/>
<id>urn:uuid:bd0b2c90-35a3-44e9-a491-4e15508f6d83</id>
<updated>2015-06-05T00:05:00+03:00</updated>
<author>
<name>Вики педии - свободной энциклопедии</name>
</author>
<entry>
<title>
Война и
мир
</title>
<author>
<name>Лев Николаевич Толсто́й</name>
</author>
<link rel="alternate" href="https://en.wikipedia.org/wiki/War_and_Peace"/>
<updated>2015-06-05T00:02:00+03:00</updated>
<summary xml:lang="bg">В наброске предисловия к «Войне и миру» Толстой
писал, что в 1856 г. начал писать повесть, «герой
которой должен был быть декабрист, возвращающийся
с семейством в Россию.</summary>
<link rel="enclosure" href="https://upload.wikimedia.org/wikipedia/commons/4/41/War-and-peace_1873.gif" type="image/gif" length="10889"/>
</entry>
<entry xml:lang="bg">
<id>urn:uuid:32117a95-0962-4826-8aee-1ea7ddd94bec</id>
<link href="https://en.wikipedia.org/wiki/Crime_and_Punishment"/>
<published>2015-06-05T00:02:00+03:00</published>
<content type="html"><![CDATA[<h1>
История создания
</h1>
<p>
Осенью
<a href="/wiki/1865_%D0%B3%D0%BE%D0%B4" title="1865 год">
1865
года
</a>
, потеряв все свои деньги в
<a href="/wiki/%D0%9A%D0%B0%D0%B7%D0%B8%D0%BD%D0%BE" title="Казино">
казино
</a>
, не в состоянии оплатить долги кредиторам, и стараясь помочь семье своего брата Михаила, который умер в июле
<a href="/wiki/1864_%D0%B3%D0%BE%D0%B4" title="1864 год">
1864 года
</a>
, Достоевский планирует создание романа с центральным образом семьи Мармеладовых под названием «Пьяненькая».
</p>]]></content>
</entry>
<entry>
<content type="html">&lt;h1&gt;
Доктор Живаго
&lt;/h1&gt;
&lt;p&gt;
&lt;b&gt;«До́ктор Жива́го»&lt;/b&gt;
— роман Бориса Пастернака. «Доктор Живаго» создавался в течение десяти лет, с
&lt;a href="/wiki/1945" title="1945" class="mw-redirect"&gt;1945&lt;/a&gt; по
&lt;a href="/wiki/1955_%D0%B3%D0%BE%D0%B4" title="1955 год"&gt;1955 год&lt;/a&gt;, и
является вершиной его творчества как прозаика. Роман сопровождён стихами
главного героя — Юрия Андреевича Живаго.
&lt;/p&gt;</content>
</entry>
<entry>
<content><h1>
Герой нашего времени
</h1>
<p>
<b>«Геро́й на́шего вре́мени»</b>
(написан в 1838—1840) — знаменитый роман
<a href="/wiki/%D0%9B%D0%B5%D1%80%D0%BC%D0%BE%D0%BD%D1%82%D0%BE%D0%B2,_%D0%9C%D0%B8%D1%85%D0%B0%D0%B8%D0%BB_%D0%AE%D1%80%D1%8C%D0%B5%D0%B2%D0%B8%D1%87" title="Лермонтов, Михаил Юрьевич">Михаила Юрьевича Лермонтова</a>, классика русской литературы. Впервые роман был издан в
<a href="/wiki/%D0%A1%D0%B0%D0%BD%D0%BA%D1%82-%D0%9F%D0%B5%D1%82%D0%B5%D1%80%D0%B1%D1%83%D1%80%D0%B3" title="Санкт-Петербург">Санкт-Петербурге</a>,
в типографии Ильи Глазунова и Кº, в <a href="/wiki/1840_%D0%B3%D0%BE%D0%B4_%D0%B2_%D0%BB%D0%B8%D1%82%D0%B5%D1%80%D0%B0%D1%82%D1%83%D1%80%D0%B5" title="1840 год в литературе">1840</a> г., в 2 книгах. Тираж 1000 экземпляров
<sup id="cite_ref-1" class="reference"><a href="#cite_note-1">[1]</a></sup>.
</p></content>
</entry>
</feed>

View File

@ -1,38 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<feed
xmlns="http://www.w3.org/2005/Atom"
xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/"
xmlns:blogger="http://schemas.google.com/blogger/2008"
xmlns:georss="http://www.georss.org/georss"
xmlns:gd="http://schemas.google.com/g/2005"
xmlns:thr="http://purl.org/syndication/thread/1.0"
xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;DUYHQns6cCp7ImA9WhBTEUo.&quot;">
<entry>
<updated>2015-06-05T00:05:00+03:00</updated>
<published>2015-06-05T01:05:00+03:00</published>
</entry>
<entry>
<summary>summary pre</summary>
<content type="html"><![CDATA[<h1>
История создания
</h1>
<p>
Осенью
<a href="/wiki/1865_%D0%B3%D0%BE%D0%B4" title="1865 год">
1865
года
</a>
, потеряв все свои деньги в
<a href="/wiki/%D0%9A%D0%B0%D0%B7%D0%B8%D0%BD%D0%BE" title="Казино">
казино
</a>
, не в состоянии оплатить долги кредиторам, и стараясь помочь семье своего брата Михаила, который умер в июле
<a href="/wiki/1864_%D0%B3%D0%BE%D0%B4" title="1864 год">
1864 года
</a>
, Достоевский планирует создание романа с центральным образом семьи Мармеладовых под названием «Пьяненькая».
</p>]]></content>
<summary>summary post</summary>
</entry>
</feed>

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<feed
xmlns="http://www.w3.org/2005/Atom"
xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/"
xmlns:blogger="http://schemas.google.com/blogger/2008"
xmlns:georss="http://www.georss.org/georss"
xmlns:gd="http://schemas.google.com/g/2005"
xmlns:thr="http://purl.org/syndication/thread/1.0"
xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;DUYHQns6cCp7ImA9WhBTEUo.&quot;"/>

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<entry/>
</feed>

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<feed
xmlns="http://www.w3.org/2005/Atom"
xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/"
xmlns:blogger="http://schemas.google.com/blogger/2008"
xmlns:georss="http://www.georss.org/georss"
xmlns:gd="http://schemas.google.com/g/2005"
xmlns:thr="http://purl.org/syndication/thread/1.0"
xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;DUYHQns6cCp7ImA9WhBTEUo.&quot;" xml:lang="ru">
<link href="/wiki/Category:Russian-language_literature"/>
<link rel="self" href="/category/Russian-language_literature.xml"/>
<entry>
<link rel="alternate" href="/wiki/War_and_Peace"/>
</entry>
<entry xml:lang="bg">
<link href="/wiki/Crime_and_Punishment"/>
</entry>
</feed>

View File

@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<feed
xmlns="http://www.w3.org/2005/Atom"
xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/"
xmlns:blogger="http://schemas.google.com/blogger/2008"
xmlns:georss="http://www.georss.org/georss"
xmlns:gd="http://schemas.google.com/g/2005"
xmlns:thr="http://purl.org/syndication/thread/1.0"
xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;DUYHQns6cCp7ImA9WhBTEUo.&quot;">
<title>
</title>
<link rel="alternate" href="https://en.wikipedia.org/wiki/Category:Russian-language_literature"/>
</feed>

View File

@ -1,28 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<feed
xmlns="http://www.w3.org/2005/Atom"
xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/"
xmlns:blogger="http://schemas.google.com/blogger/2008"
xmlns:georss="http://www.georss.org/georss"
xmlns:gd="http://schemas.google.com/g/2005"
xmlns:thr="http://purl.org/syndication/thread/1.0"
xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;DUYHQns6cCp7ImA9WhBTEUo.&quot;">
<entry/>
<entry>
<content type="html"><![CDATA[
]]>
</content>
<summary>Осенью 1865 года, потеряв все свои
деньги в казино, не в состоянии оплатить долги кредиторам,
и стараясь помочь семье своего брата Михаила, который умер в
июле 1864 года, Достоевский планирует создание романа с
центральным образом семьи Мармеладовых под названием «Пьяненькая».</summary>
</entry>
<entry>
<link rel="alternate" href="https://en.wikipedia.org/wiki/Doctor_Zhivago_(novel)"/>
<title>
</title>
</entry>
</feed>

View File

@ -1,92 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<feed
xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/"
xmlns:blogger="http://schemas.google.com/blogger/2008"
xmlns:georss="http://www.georss.org/georss"
xmlns:gd="http://schemas.google.com/g/2005"
xmlns:thr="http://purl.org/syndication/thread/1.0"
xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;DUYHQns6cCp7ImA9WhBTEUo.&quot;">
<title xml:lang="ru">
литература на русском языке,
либо написанная русскими авторами
</title>
<subtitle type="html">Зародилась во второй половине X века, однако до XIX века,
когда начался её «золотой век», была практически неизвестна
в мире.</subtitle>
<logo>https://ru.wikipedia.org/static/images/project-logos/ruwiki.png</logo>
<icon>https://ru.wikipedia.org/static/favicon/wikipedia.ico</icon>
<link rel="alternate" href="https://en.wikipedia.org/wiki/Category:Russian-language_literature"/>
<link rel="self" href="https://feeds.wikipedia.org/category/Russian-language_literature.xml"/>
<id>urn:uuid:bd0b2c90-35a3-44e9-a491-4e15508f6d83</id>
<updated>2015-06-05T00:05:00+03:00</updated>
<author>
<name>Вики педии - свободной энциклопедии</name>
</author>
<entry>
<title>
Война и
мир
</title>
<author>
<name>Лев Николаевич Толсто́й</name>
</author>
<link rel="alternate" href="https://en.wikipedia.org/wiki/War_and_Peace"/>
<updated>2015-06-05T00:02:00+03:00</updated>
<summary xml:lang="bg">В наброске предисловия к «Войне и миру» Толстой
писал, что в 1856 г. начал писать повесть, «герой
которой должен был быть декабрист, возвращающийся
с семейством в Россию.</summary>
<link rel="enclosure" href="https://upload.wikimedia.org/wikipedia/commons/4/41/War-and-peace_1873.gif" type="image/gif" length="10889"/>
</entry>
<entry xml:lang="bg">
<id>urn:uuid:32117a95-0962-4826-8aee-1ea7ddd94bec</id>
<link href="https://en.wikipedia.org/wiki/Crime_and_Punishment"/>
<published>2015-06-05T00:02:00+03:00</published>
<content><![CDATA[<h1>
История создания
</h1>
<p>
Осенью
<a href="/wiki/1865_%D0%B3%D0%BE%D0%B4" title="1865 год">
1865
года
</a>
, потеряв все свои деньги в
<a href="/wiki/%D0%9A%D0%B0%D0%B7%D0%B8%D0%BD%D0%BE" title="Казино">
казино
</a>
, не в состоянии оплатить долги кредиторам, и стараясь помочь семье своего брата Михаила, который умер в июле
<a href="/wiki/1864_%D0%B3%D0%BE%D0%B4" title="1864 год">
1864 года
</a>
, Достоевский планирует создание романа с центральным образом семьи Мармеладовых под названием «Пьяненькая».
</p>]]></content>
</entry>
<entry>
<content>&lt;h1&gt;
Доктор Живаго
&lt;/h1&gt;
&lt;p&gt;
&lt;b&gt;«До́ктор Жива́го»&lt;/b&gt;
— роман Бориса Пастернака. «Доктор Живаго» создавался в течение десяти лет, с
&lt;a href="/wiki/1945" title="1945" class="mw-redirect"&gt;1945&lt;/a&gt; по
&lt;a href="/wiki/1955_%D0%B3%D0%BE%D0%B4" title="1955 год"&gt;1955 год&lt;/a&gt;, и
является вершиной его творчества как прозаика. Роман сопровождён стихами
главного героя — Юрия Андреевича Живаго.
&lt;/p&gt;</content>
</entry>
<entry>
<content><h1>
Герой нашего времени
</h1>
<p>
<b>«Геро́й на́шего вре́мени»</b>
(написан в 1838—1840) — знаменитый роман
<a href="/wiki/%D0%9B%D0%B5%D1%80%D0%BC%D0%BE%D0%BD%D1%82%D0%BE%D0%B2,_%D0%9C%D0%B8%D1%85%D0%B0%D0%B8%D0%BB_%D0%AE%D1%80%D1%8C%D0%B5%D0%B2%D0%B8%D1%87" title="Лермонтов, Михаил Юрьевич">Михаила Юрьевича Лермонтова</a>, классика русской литературы. Впервые роман был издан в
<a href="/wiki/%D0%A1%D0%B0%D0%BD%D0%BA%D1%82-%D0%9F%D0%B5%D1%82%D0%B5%D1%80%D0%B1%D1%83%D1%80%D0%B3" title="Санкт-Петербург">Санкт-Петербурге</a>,
в типографии Ильи Глазунова и Кº, в <a href="/wiki/1840_%D0%B3%D0%BE%D0%B4_%D0%B2_%D0%BB%D0%B8%D1%82%D0%B5%D1%80%D0%B0%D1%82%D1%83%D1%80%D0%B5" title="1840 год в литературе">1840</a> г., в 2 книгах. Тираж 1000 экземпляров
<sup id="cite_ref-1" class="reference"><a href="#cite_note-1">[1]</a></sup>.
</p></content>
</entry>
</feed>

View File

@ -1,93 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<feed
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/"
xmlns:blogger="http://schemas.google.com/blogger/2008"
xmlns:georss="http://www.georss.org/georss"
xmlns:gd="http://schemas.google.com/g/2005"
xmlns:thr="http://purl.org/syndication/thread/1.0"
xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;DUYHQns6cCp7ImA9WhBTEUo.&quot;">
<atom:title xml:lang="ru">
литература на русском языке,
либо написанная русскими авторами
</atom:title>
<atom:subtitle type="html">Зародилась во второй половине X века, однако до XIX века,
когда начался её «золотой век», была практически неизвестна
в мире.</atom:subtitle>
<atom:logo>https://ru.wikipedia.org/static/images/project-logos/ruwiki.png</atom:logo>
<atom:icon>https://ru.wikipedia.org/static/favicon/wikipedia.ico</atom:icon>
<atom:link rel="alternate" href="https://en.wikipedia.org/wiki/Category:Russian-language_literature"/>
<atom:link rel="self" href="https://feeds.wikipedia.org/category/Russian-language_literature.xml"/>
<atom:id>urn:uuid:bd0b2c90-35a3-44e9-a491-4e15508f6d83</atom:id>
<atom:updated>2015-06-05T00:05:00+03:00</atom:updated>
<atom:author>
<atom:name>Вики педии - свободной энциклопедии</atom:name>
</atom:author>
<atom:entry>
<atom:title>
Война и
мир
</atom:title>
<atom:author>
<atom:name>Лев Николаевич Толсто́й</atom:name>
</atom:author>
<atom:link rel="alternate" href="https://en.wikipedia.org/wiki/War_and_Peace"/>
<atom:updated>2015-06-05T00:02:00+03:00</atom:updated>
<atom:summary xml:lang="bg">В наброске предисловия к «Войне и миру» Толстой
писал, что в 1856 г. начал писать повесть, «герой
которой должен был быть декабрист, возвращающийся
с семейством в Россию.</atom:summary>
<atom:link rel="enclosure" href="https://upload.wikimedia.org/wikipedia/commons/4/41/War-and-peace_1873.gif" type="image/gif" length="10889"/>
</atom:entry>
<atom:entry xml:lang="bg">
<atom:id>urn:uuid:32117a95-0962-4826-8aee-1ea7ddd94bec</atom:id>
<atom:link href="https://en.wikipedia.org/wiki/Crime_and_Punishment"/>
<atom:published>2015-06-05T00:02:00+03:00</atom:published>
<atom:content type="html"><![CDATA[<h1>
История создания
</h1>
<p>
Осенью
<a href="/wiki/1865_%D0%B3%D0%BE%D0%B4" title="1865 год">
1865
года
</a>
, потеряв все свои деньги в
<a href="/wiki/%D0%9A%D0%B0%D0%B7%D0%B8%D0%BD%D0%BE" title="Казино">
казино
</a>
, не в состоянии оплатить долги кредиторам, и стараясь помочь семье своего брата Михаила, который умер в июле
<a href="/wiki/1864_%D0%B3%D0%BE%D0%B4" title="1864 год">
1864 года
</a>
, Достоевский планирует создание романа с центральным образом семьи Мармеладовых под названием «Пьяненькая».
</p>]]></atom:content>
</atom:entry>
<atom:entry>
<atom:content type="html">&lt;h1&gt;
Доктор Живаго
&lt;/h1&gt;
&lt;p&gt;
&lt;b&gt;«До́ктор Жива́го»&lt;/b&gt;
— роман Бориса Пастернака. «Доктор Живаго» создавался в течение десяти лет, с
&lt;a href="/wiki/1945" title="1945" class="mw-redirect"&gt;1945&lt;/a&gt; по
&lt;a href="/wiki/1955_%D0%B3%D0%BE%D0%B4" title="1955 год"&gt;1955 год&lt;/a&gt;, и
является вершиной его творчества как прозаика. Роман сопровождён стихами
главного героя — Юрия Андреевича Живаго.
&lt;/p&gt;</atom:content>
</atom:entry>
<atom:entry>
<atom:content><h1>
Герой нашего времени
</h1>
<p>
<b>«Геро́й на́шего вре́мени»</b>
(написан в 1838—1840) — знаменитый роман
<a href="/wiki/%D0%9B%D0%B5%D1%80%D0%BC%D0%BE%D0%BD%D1%82%D0%BE%D0%B2,_%D0%9C%D0%B8%D1%85%D0%B0%D0%B8%D0%BB_%D0%AE%D1%80%D1%8C%D0%B5%D0%B2%D0%B8%D1%87" title="Лермонтов, Михаил Юрьевич">Михаила Юрьевича Лермонтова</a>, классика русской литературы. Впервые роман был издан в
<a href="/wiki/%D0%A1%D0%B0%D0%BD%D0%BA%D1%82-%D0%9F%D0%B5%D1%82%D0%B5%D1%80%D0%B1%D1%83%D1%80%D0%B3" title="Санкт-Петербург">Санкт-Петербурге</a>,
в типографии Ильи Глазунова и Кº, в <a href="/wiki/1840_%D0%B3%D0%BE%D0%B4_%D0%B2_%D0%BB%D0%B8%D1%82%D0%B5%D1%80%D0%B0%D1%82%D1%83%D1%80%D0%B5" title="1840 год в литературе">1840</a> г., в 2 книгах. Тираж 1000 экземпляров
<sup id="cite_ref-1" class="reference"><a href="#cite_note-1">[1]</a></sup>.
</p></atom:content>
</atom:entry>
</feed>

View File

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Example Feed</title>
<link href="http://example.org/"/>
<updated>2003-12-13T18:30:02Z</updated>
<author>
<name>John Doe</name>
</author>
<id>urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6</id>
<entry>
<title>Atom-Powered Robots Run Amok</title>
<link href="http://example.org/2003/12/13/atom03"/>
<id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id>
<updated>2003-12-13T18:30:02Z</updated>
<summary>Some text.</summary>
</entry>
</feed>

View File

@ -1,58 +0,0 @@
<rss version="2.0" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<channel>
<title>Le cercle psy, le magazine de toutes les psychologies</title>
<link>http://le-cercle-psy.scienceshumaines.com/rss</link>
<description>Flux RSS du magazine de psychologie Le Cercle Psy, site de toutes les psychologies.</description>
<copyright>Le Cercle Psy</copyright>
<item>
<title>Stéphane Gumpper : Qu'est-ce que la psychologie des religions ?</title>
<link>http://le-cercle-psy.scienceshumaines.com/stephane-gumpper-qu-est-ce-que-la-psychologie-des-religions_sh_30765</link>
<pubDate>Thu, 23 May 2013 10:30:00 GMT</pubDate>
<description>D'où vient le sentiment religieux ? Où est la frontière entre mysticisme et folie ? Depuis plus d'un siècle, la psychologie des religions tente de répondre à ces questions. Panorama d'un champ de recherches aussi difficile que foisonnant avec Stéphane Gumpper, docteur en psychologie clinique et en psychopathologie, et directeur, avec Franklin Rausky, du Dictionnaire de psychologie et de psychopathologie des religions (Bayard, 2013).</description>
</item>
<item>
<title>Mentir aux recruteurs ? Facile !</title>
<link>http://le-cercle-psy.scienceshumaines.com/mentir-aux-recruteurs-facile_sh_30768</link>
<pubDate>Thu, 23 May 2013 10:30:00 GMT</pubDate>
<description>Lors d'un entretien d'embauche, les recruteurs expérimentés ne seraient pas plus doués que les novices pour repérer un candidat qui bluffe...</description>
</item>
<item>
<title>Papa, où tu es ?...</title>
<link>http://le-cercle-psy.scienceshumaines.com/papa-ou-tu-es_sh_30769</link>
<pubDate>Thu, 23 May 2013 10:30:00 GMT</pubDate>
<description>Plus d'un enfant sur dix ne revoit jamais son père après la séparation de ses parents. Ce qui continue à les affecter à l'âge adulte.</description>
</item>
<item>
<title>Journal d'une psychanalyste heureuse</title>
<link>http://le-cercle-psy.scienceshumaines.com/journal-d-une-psychanalyste-heureuse_sh_30766</link>
<pubDate>Thu, 23 May 2013 10:30:00 GMT</pubDate>
<description>Voilà un ouvrage qui peut se vanter de ne pas laisser indifférent dès son titre : Journal d'une psychanalyste heureuse. On en tient une ! Car outre la crise que connaît cette profession attaquée sur tous les fronts, l'idée de bonheur est généralement suspecte aux analystes. L'auteure assume aimer son métier, considérer comme un privilège d'aider autrui, et irradie d'une allégresse communicative quand ses interlocuteurs semblent se portent mieux. Ajoutez à cela des croquis rapides de certains aspects du métier, et vous obtenez un témoignage vivant et sans prétention d'une vocation assouvie.</description>
</item>
<item>
<title>De la psychose maniacodépressive au trouble bipolaire</title>
<link>http://le-cercle-psy.scienceshumaines.com/de-la-psychose-maniacodepressive-au-trouble-bipolaire_sh_29291</link>
<pubDate>Thu, 23 May 2013 10:30:00 GMT</pubDate>
<description>Le trouble bipolaire se veut une alternance d'états de dépression et d'exaltation, frappant, depuis trente ans, une frange de plus en plus massive de la population. Pourquoi cette bonne fortune auprès des psychiatres? Par Mikkel Borch-Jacobsen.</description>
</item>
<item>
<title>Dossier : Etre femme aujourd'hui</title>
<link>http://le-cercle-psy.scienceshumaines.com/dossier-web/76</link>
<pubDate>Thu, 23 May 2013 10:30:00 GMT</pubDate>
<description>Quels sont les modèles contemporains de la femme ? Peut-on parler d'identité féminine ? Où en sont les rapports hommes-femmes, que ce soit au travail, à la maison, dans la vie quotidienne ? La sexualité féminine s'est-elle vraiment libérée ? Quels sont les nouveaux combats à mener ? Les références théoriques à mobiliser quand on aborde ces questions ?</description>
</item>
<item>
<title>Exprimez-vous avec le Cercle Psy</title>
<link>http://le-cercle-psy.scienceshumaines.com/exprimez-vous-avec-le-cercle-psy_sh_28824</link>
<pubDate>Thu, 23 May 2013 10:30:00 GMT</pubDate>
<description>Vous avez un enfant autiste ? Ou un proche ? Vous êtes autiste vous-même ? Le Cercle Psy vous propose de raconter, en toute liberté, votre histoire. Comment le diagnostic a-t-il été posé ? En quoi consiste la prise en charge ? Quels rapports entretenez-vous avec les soignants ? Que pensez-vous des polémiques actuelles relatives à l'autisme ? Vous avez carte blanche.</description>
</item>
</channel>
</rss>

View File

@ -1,496 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE torrent PUBLIC "-//bitTorrent//DTD torrent 0.1//EN" "http://xmlns.ezrss.it/0.1/dtd/">
<rss version="2.0">
<channel>
<title>ezRSS - Latest torrent releases</title>
<ttl>15</ttl>
<link>http://ezrss.it/feed/</link>
<image>
<title>ezRSS - Latest torrent releases</title>
<url>http://ezrss.it/images/ezrssit.png</url>
<link>http://ezrss.it/feed/</link>
</image>
<description>The latest 30 torrent releases.</description>
<item>
<title><![CDATA[National Geographic Access - Great Barrier Reef 3x60 [PDTV - MVGROUP]]]></title>
<link>http://torrent.zoink.it/National.Geographic.Access.360.Great.Barrier.Reef.PDTV.x264.AAC.[MVGroup.org].torrent</link>
<category domain="http://eztv.it/shows/187/mv-group-documentaries/"><![CDATA[TV Show / MV Group Documentaries]]></category>
<pubDate>Wed, 29 May 2013 17:09:52 -0500</pubDate>
<description><![CDATA[Show Name: National Geographic Access; Episode Title: Great Barrier Reef; Season: 3; Episode: 60]]></description>
<enclosure url="http://torrent.zoink.it/National.Geographic.Access.360.Great.Barrier.Reef.PDTV.x264.AAC.[MVGroup.org].torrent" length="721366766" type="application/x-bittorrent" />
<comments>http://eztv.it/forum/discuss/45237/</comments>
<guid>http://eztv.it/ep/45237/national-geographic-access-360-great-barrier-reef-pdtv-x264-aac-mvgroup/</guid>
<torrent xmlns="http://xmlns.ezrss.it/0.1/">
<fileName><![CDATA[National.Geographic.Access.360.Great.Barrier.Reef.PDTV.x264.AAC-MVGroup.[MVGroup.org].torrent]]></fileName>
<contentLength>721366766</contentLength>
<infoHash>81E21C0A53B138A349FE4AFDC10A56086B7CBD15</infoHash>
<magnetURI><![CDATA[magnet:?xt=urn:btih:81E21C0A53B138A349FE4AFDC10A56086B7CBD15&dn=National.Geographic.Access.360.Great.Barrier.Reef.PDTV.x264.AAC-MVGroup]]></magnetURI>
</torrent>
</item>
<item>
<title><![CDATA[The Valleys 2x5 [HDTV - C4TV]]]></title>
<link>http://torrent.zoink.it/The.Valleys.S02E05.HDTV.x264-C4TV.[eztv].torrent</link>
<category domain="http://eztv.it/shows/726/the-valleys/"><![CDATA[TV Show / The Valleys]]></category>
<pubDate>Wed, 29 May 2013 16:13:51 -0500</pubDate>
<description><![CDATA[Show Name: The Valleys; Episode Title: N/A; Season: 2; Episode: 5]]></description>
<enclosure url="http://torrent.zoink.it/The.Valleys.S02E05.HDTV.x264-C4TV.[eztv].torrent" length="344393593" type="application/x-bittorrent" />
<comments>http://eztv.it/forum/discuss/45236/</comments>
<guid>http://eztv.it/ep/45236/the-valleys-s02e05-hdtv-x264-c4tv/</guid>
<torrent xmlns="http://xmlns.ezrss.it/0.1/">
<fileName><![CDATA[The.Valleys.S02E05.HDTV.x264-C4TV.[eztv].torrent]]></fileName>
<contentLength>344393593</contentLength>
<infoHash>B4E86B2E26B7A22191092B2BC3E683E77CED0F5F</infoHash>
<magnetURI><![CDATA[magnet:?xt=urn:btih:WTUGWLRGW6RCDEIJFMV4HZUD456O2D27&dn=The.Valleys.S02E05.HDTV.x264-C4TV]]></magnetURI>
</torrent>
</item>
<item>
<title><![CDATA[BBC Trawlermen Series 1 - The Storm 1x2 [DVDRIP - MVGROUP]]]></title>
<link>http://torrent.zoink.it/BBC.Trawlermen.Series.1.2of5.The.Storm.DVDRip.x264.AAC.[MVGroup.org].torrent</link>
<category domain="http://eztv.it/shows/187/mv-group-documentaries/"><![CDATA[TV Show / MV Group Documentaries]]></category>
<pubDate>Wed, 29 May 2013 15:13:54 -0500</pubDate>
<description><![CDATA[Show Name: BBC Trawlermen Series 1; Episode Title: The Storm; Season: 1; Episode: 2]]></description>
<enclosure url="http://torrent.zoink.it/BBC.Trawlermen.Series.1.2of5.The.Storm.DVDRip.x264.AAC.[MVGroup.org].torrent" length="373884323" type="application/x-bittorrent" />
<comments>http://eztv.it/forum/discuss/45235/</comments>
<guid>http://eztv.it/ep/45235/bbc-trawlermen-series-1-2of5-the-storm-dvdrip-x264-aac-mvgroup/</guid>
<torrent xmlns="http://xmlns.ezrss.it/0.1/">
<fileName><![CDATA[BBC.Trawlermen.Series.1.2of5.The.Storm.DVDRip.x264.AAC-MVGroup.[MVGroup.org].torrent]]></fileName>
<contentLength>373884323</contentLength>
<infoHash>90776630AA06E87F5C717C2A1CFCF2F216A7B8CB</infoHash>
<magnetURI><![CDATA[magnet:?xt=urn:btih:90776630AA06E87F5C717C2A1CFCF2F216A7B8CB&dn=BBC.Trawlermen.Series.1.2of5.The.Storm.DVDRip.x264.AAC-MVGroup]]></magnetURI>
</torrent>
</item>
<item>
<title><![CDATA[Thames Television The Life and Times of Lord Mountbatten - Full Circle 1x11]]></title>
<link>http://torrent.zoink.it/Thames.Television.The.Life.and.Times.of.Lord.Mountbatten.11of12.Full.Circle.XviD.AC3.[MVGroup.org].torrent</link>
<category domain="http://eztv.it/shows/187/mv-group-documentaries/"><![CDATA[TV Show / MV Group Documentaries]]></category>
<pubDate>Wed, 29 May 2013 14:13:53 -0500</pubDate>
<description><![CDATA[Show Name: Thames Television The Life and Times of Lord Mountbatten; Episode Title: Full Circle; Season: 1; Episode: 11]]></description>
<enclosure url="http://torrent.zoink.it/Thames.Television.The.Life.and.Times.of.Lord.Mountbatten.11of12.Full.Circle.XviD.AC3.[MVGroup.org].torrent" length="823406592" type="application/x-bittorrent" />
<comments>http://eztv.it/forum/discuss/45234/</comments>
<guid>http://eztv.it/ep/45234/thames-television-the-life-and-times-of-lord-mountbatten-11of12-full-circle-xvid-ac3-mvgroup/</guid>
<torrent xmlns="http://xmlns.ezrss.it/0.1/">
<fileName><![CDATA[Thames.Television.The.Life.and.Times.of.Lord.Mountbatten.11of12.Full.Circle.XviD.AC3-MVGroup.[MVGroup.org].torrent]]></fileName>
<contentLength>823406592</contentLength>
<infoHash>67EE2926109A2B557CD7DB65E6E945EA7296FA79</infoHash>
<magnetURI><![CDATA[magnet:?xt=urn:btih:67EE2926109A2B557CD7DB65E6E945EA7296FA79&dn=Thames.Television.The.Life.and.Times.of.Lord.Mountbatten.11of12.Full.Circle.XviD.AC3-MVGroup]]></magnetURI>
</torrent>
</item>
<item>
<title><![CDATA[BBC Trawlermen Series 1 - The Great Prawn Hunt 1x1 [DVDRIP - MVGROUP]]]></title>
<link>http://torrent.zoink.it/BBC.Trawlermen.Series.1.1of5.The.Great.Prawn.Hunt.DVDRip.x264.AAC.[MVGroup.org].torrent</link>
<category domain="http://eztv.it/shows/187/mv-group-documentaries/"><![CDATA[TV Show / MV Group Documentaries]]></category>
<pubDate>Wed, 29 May 2013 11:32:46 -0500</pubDate>
<description><![CDATA[Show Name: BBC Trawlermen Series 1; Episode Title: The Great Prawn Hunt; Season: 1; Episode: 1]]></description>
<enclosure url="http://torrent.zoink.it/BBC.Trawlermen.Series.1.1of5.The.Great.Prawn.Hunt.DVDRip.x264.AAC.[MVGroup.org].torrent" length="388242999" type="application/x-bittorrent" />
<comments>http://eztv.it/forum/discuss/45232/</comments>
<guid>http://eztv.it/ep/45232/bbc-trawlermen-series-1-1of5-the-great-prawn-hunt-dvdrip-x264-aac-mvgroup/</guid>
<torrent xmlns="http://xmlns.ezrss.it/0.1/">
<fileName><![CDATA[BBC.Trawlermen.Series.1.1of5.The.Great.Prawn.Hunt.DVDRip.x264.AAC-MVGroup.[MVGroup.org].torrent]]></fileName>
<contentLength>388242999</contentLength>
<infoHash>5C975B193279707E71D7942154F64A862B9145CA</infoHash>
<magnetURI><![CDATA[magnet:?xt=urn:btih:5C975B193279707E71D7942154F64A862B9145CA&dn=BBC.Trawlermen.Series.1.1of5.The.Great.Prawn.Hunt.DVDRip.x264.AAC-MVGroup]]></magnetURI>
</torrent>
</item>
<item>
<title><![CDATA[National Geographic Meet the Natives USA - The Big Apple 1x2 [PDTV - MVGROUP]]]></title>
<link>http://torrent.zoink.it/National.Geographic.Meet.the.Natives.USA.2of5.The.Big.Apple.PDTV.x264.AAC.[MVGroup.org].torrent</link>
<category domain="http://eztv.it/shows/187/mv-group-documentaries/"><![CDATA[TV Show / MV Group Documentaries]]></category>
<pubDate>Wed, 29 May 2013 09:53:11 -0500</pubDate>
<description><![CDATA[Show Name: National Geographic Meet the Natives USA; Episode Title: The Big Apple; Season: 1; Episode: 2]]></description>
<enclosure url="http://torrent.zoink.it/National.Geographic.Meet.the.Natives.USA.2of5.The.Big.Apple.PDTV.x264.AAC.[MVGroup.org].torrent" length="513588301" type="application/x-bittorrent" />
<comments>http://eztv.it/forum/discuss/45229/</comments>
<guid>http://eztv.it/ep/45229/national-geographic-meet-the-natives-usa-2of5-the-big-apple-pdtv-x264-aac-mvgroup/</guid>
<torrent xmlns="http://xmlns.ezrss.it/0.1/">
<fileName><![CDATA[National.Geographic.Meet.the.Natives.USA.2of5.The.Big.Apple.PDTV.x264.AAC-MVGroup.[MVGroup.org].torrent]]></fileName>
<contentLength>513588301</contentLength>
<infoHash>16C719F7983D8303A3A5EF32111B6A8A6B24BE87</infoHash>
<magnetURI><![CDATA[magnet:?xt=urn:btih:16C719F7983D8303A3A5EF32111B6A8A6B24BE87&dn=National.Geographic.Meet.the.Natives.USA.2of5.The.Big.Apple.PDTV.x264.AAC-MVGroup]]></magnetURI>
</torrent>
</item>
<item>
<title><![CDATA[Thames Television The Life and Times of Lord Mountbatten - Fresh Fields 1x10]]></title>
<link>http://torrent.zoink.it/Thames.Television.The.Life.and.Times.of.Lord.Mountbatten.10of12.Fresh.Fields.XviD.AC3.[MVGroup.org].torrent</link>
<category domain="http://eztv.it/shows/187/mv-group-documentaries/"><![CDATA[TV Show / MV Group Documentaries]]></category>
<pubDate>Wed, 29 May 2013 04:58:32 -0500</pubDate>
<description><![CDATA[Show Name: Thames Television The Life and Times of Lord Mountbatten; Episode Title: Fresh Fields; Season: 1; Episode: 10]]></description>
<enclosure url="http://torrent.zoink.it/Thames.Television.The.Life.and.Times.of.Lord.Mountbatten.10of12.Fresh.Fields.XviD.AC3.[MVGroup.org].torrent" length="824440832" type="application/x-bittorrent" />
<comments>http://eztv.it/forum/discuss/45227/</comments>
<guid>http://eztv.it/ep/45227/thames-television-the-life-and-times-of-lord-mountbatten-10of12-fresh-fields-xvid-ac3-mvgroup/</guid>
<torrent xmlns="http://xmlns.ezrss.it/0.1/">
<fileName><![CDATA[Thames.Television.The.Life.and.Times.of.Lord.Mountbatten.10of12.Fresh.Fields.XviD.AC3-MVGroup.[MVGroup.org].torrent]]></fileName>
<contentLength>824440832</contentLength>
<infoHash>AC5CB1F0E6E213CAB237EFF2301EC881E6793646</infoHash>
<magnetURI><![CDATA[magnet:?xt=urn:btih:AC5CB1F0E6E213CAB237EFF2301EC881E6793646&dn=Thames.Television.The.Life.and.Times.of.Lord.Mountbatten.10of12.Fresh.Fields.XviD.AC3-MVGroup]]></magnetURI>
</torrent>
</item>
<item>
<title><![CDATA[BBS The Documentary - Baud 1x1 [DVDRIP - MVGROUP]]]></title>
<link>http://torrent.zoink.it/BBS.The.Documentary.1of8.Baud.DVDRip.x264.AAC.[MVGroup.org].torrent</link>
<category domain="http://eztv.it/shows/187/mv-group-documentaries/"><![CDATA[TV Show / MV Group Documentaries]]></category>
<pubDate>Wed, 29 May 2013 03:46:02 -0500</pubDate>
<description><![CDATA[Show Name: BBS The Documentary; Episode Title: Baud; Season: 1; Episode: 1]]></description>
<enclosure url="http://torrent.zoink.it/BBS.The.Documentary.1of8.Baud.DVDRip.x264.AAC.[MVGroup.org].torrent" length="343630179" type="application/x-bittorrent" />
<comments>http://eztv.it/forum/discuss/45226/</comments>
<guid>http://eztv.it/ep/45226/bbs-the-documentary-1of8-baud-dvdrip-x264-aac-mvgroup/</guid>
<torrent xmlns="http://xmlns.ezrss.it/0.1/">
<fileName><![CDATA[BBS.The.Documentary.1of8.Baud.DVDRip.x264.AAC-MVGroup.[MVGroup.org].torrent]]></fileName>
<contentLength>343630179</contentLength>
<infoHash>405271D5BC3A84A76885591031EB9CEEDD84918E</infoHash>
<magnetURI><![CDATA[magnet:?xt=urn:btih:405271D5BC3A84A76885591031EB9CEEDD84918E&dn=BBS.The.Documentary.1of8.Baud.DVDRip.x264.AAC-MVGroup]]></magnetURI>
</torrent>
</item>
<item>
<title><![CDATA[Some Assembly Required Series 2 - Tennis Balls 1x11 [DVDRIP - MVGROUP]]]></title>
<link>http://torrent.zoink.it/Some.Assembly.Required.Series.2.11of12.Tennis.Balls.DVDRip.x264.AAC.[MVGroup.org].torrent</link>
<category domain="http://eztv.it/shows/187/mv-group-documentaries/"><![CDATA[TV Show / MV Group Documentaries]]></category>
<pubDate>Wed, 29 May 2013 01:44:05 -0500</pubDate>
<description><![CDATA[Show Name: Some Assembly Required Series 2; Episode Title: Tennis Balls; Season: 1; Episode: 11]]></description>
<enclosure url="http://torrent.zoink.it/Some.Assembly.Required.Series.2.11of12.Tennis.Balls.DVDRip.x264.AAC.[MVGroup.org].torrent" length="253700799" type="application/x-bittorrent" />
<comments>http://eztv.it/forum/discuss/45224/</comments>
<guid>http://eztv.it/ep/45224/some-assembly-required-series-2-11of12-tennis-balls-dvdrip-x264-aac-mvgroup/</guid>
<torrent xmlns="http://xmlns.ezrss.it/0.1/">
<fileName><![CDATA[Some.Assembly.Required.Series.2.11of12.Tennis.Balls.DVDRip.x264.AAC-MVGroup.[MVGroup.org].torrent]]></fileName>
<contentLength>253700799</contentLength>
<infoHash>865132710EDD6151EF4C69A06DE309FF893864C6</infoHash>
<magnetURI><![CDATA[magnet:?xt=urn:btih:865132710EDD6151EF4C69A06DE309FF893864C6&dn=Some.Assembly.Required.Series.2.11of12.Tennis.Balls.DVDRip.x264.AAC-MVGroup]]></magnetURI>
</torrent>
</item>
<item>
<title><![CDATA[Body of Proof 3x13 [HDTV - LOL]]]></title>
<link>http://torrent.zoink.it/Body.of.Proof.S03E13.HDTV.x264-LOL.[eztv].torrent</link>
<category domain="http://eztv.it/shows/472/body-of-proof/"><![CDATA[TV Show / Body of Proof]]></category>
<pubDate>Tue, 28 May 2013 21:09:43 -0500</pubDate>
<description><![CDATA[Show Name: Body of Proof; Episode Title: N/A; Season: 3; Episode: 13]]></description>
<enclosure url="http://torrent.zoink.it/Body.of.Proof.S03E13.HDTV.x264-LOL.[eztv].torrent" length="213910002" type="application/x-bittorrent" />
<comments>http://eztv.it/forum/discuss/45223/</comments>
<guid>http://eztv.it/ep/45223/body-of-proof-s03e13-hdtv-x264-lol/</guid>
<torrent xmlns="http://xmlns.ezrss.it/0.1/">
<fileName><![CDATA[Body.of.Proof.S03E13.HDTV.x264-LOL.[eztv].torrent]]></fileName>
<contentLength>213910002</contentLength>
<infoHash>B0224ED14632F2A1EB6C14D8542FE672E434D426</infoHash>
<magnetURI><![CDATA[magnet:?xt=urn:btih:WARE5UKGGLZKD23MCTMFIL7GOLSDJVBG&dn=Body.of.Proof.S03E13.HDTV.x264-LOL]]></magnetURI>
</torrent>
</item>
<item>
<title><![CDATA[Awkward 3x8 [HDTV - 2HD]]]></title>
<link>http://torrent.zoink.it/Awkward.S03E08.HDTV.x264-2HD.[eztv].torrent</link>
<category domain="http://eztv.it/shows/508/awkward/"><![CDATA[TV Show / Awkward]]></category>
<pubDate>Tue, 28 May 2013 20:44:21 -0500</pubDate>
<description><![CDATA[Show Name: Awkward; Episode Title: N/A; Season: 3; Episode: 8]]></description>
<enclosure url="http://torrent.zoink.it/Awkward.S03E08.HDTV.x264-2HD.[eztv].torrent" length="153665671" type="application/x-bittorrent" />
<comments>http://eztv.it/forum/discuss/45222/</comments>
<guid>http://eztv.it/ep/45222/awkward-s03e08-hdtv-x264-2hd/</guid>
<torrent xmlns="http://xmlns.ezrss.it/0.1/">
<fileName><![CDATA[Awkward.S03E08.HDTV.x264-2HD.[eztv].torrent]]></fileName>
<contentLength>153665671</contentLength>
<infoHash>C063116FC299E6E5476058CDE3302E393F4CC587</infoHash>
<magnetURI><![CDATA[magnet:?xt=urn:btih:YBRRC36CTHTOKR3ALDG6GMBOHE7UZRMH&dn=Awkward.S03E08.HDTV.x264-2HD]]></magnetURI>
</torrent>
</item>
<item>
<title><![CDATA[Deadliest Catch 9x7 [HDTV - KILLERS]]]></title>
<link>http://torrent.zoink.it/Deadliest.Catch.S09E07.HDTV.x264-KILLERS.[eztv].torrent</link>
<category domain="http://eztv.it/shows/72/deadliest-catch/"><![CDATA[TV Show / Deadliest Catch]]></category>
<pubDate>Tue, 28 May 2013 20:23:24 -0500</pubDate>
<description><![CDATA[Show Name: Deadliest Catch; Episode Title: N/A; Season: 9; Episode: 7]]></description>
<enclosure url="http://torrent.zoink.it/Deadliest.Catch.S09E07.HDTV.x264-KILLERS.[eztv].torrent" length="459832370" type="application/x-bittorrent" />
<comments>http://eztv.it/forum/discuss/45221/</comments>
<guid>http://eztv.it/ep/45221/deadliest-catch-s09e07-hdtv-x264-killers/</guid>
<torrent xmlns="http://xmlns.ezrss.it/0.1/">
<fileName><![CDATA[Deadliest.Catch.S09E07.HDTV.x264-KILLERS.[eztv].torrent]]></fileName>
<contentLength>459832370</contentLength>
<infoHash>6832DAE0AE55D9291734AE70A3ED68B95887354F</infoHash>
<magnetURI><![CDATA[magnet:?xt=urn:btih:NAZNVYFOKXMSSFZUVZYKH3LIXFMIONKP&dn=Deadliest.Catch.S09E07.HDTV.x264-KILLERS]]></magnetURI>
</torrent>
</item>
<item>
<title><![CDATA[The Voice 4x22 [HDTV - 2HD]]]></title>
<link>http://torrent.zoink.it/The.Voice.S04E22.HDTV.x264-2HD.[eztv].torrent</link>
<category domain="http://eztv.it/shows/490/the-voice/"><![CDATA[TV Show / The Voice]]></category>
<pubDate>Tue, 28 May 2013 20:20:29 -0500</pubDate>
<description><![CDATA[Show Name: The Voice; Episode Title: N/A; Season: 4; Episode: 22]]></description>
<enclosure url="http://torrent.zoink.it/The.Voice.S04E22.HDTV.x264-2HD.[eztv].torrent" length="405384450" type="application/x-bittorrent" />
<comments>http://eztv.it/forum/discuss/45220/</comments>
<guid>http://eztv.it/ep/45220/the-voice-s04e22-hdtv-x264-2hd/</guid>
<torrent xmlns="http://xmlns.ezrss.it/0.1/">
<fileName><![CDATA[The.Voice.S04E22.HDTV.x264-2HD.[eztv].torrent]]></fileName>
<contentLength>405384450</contentLength>
<infoHash>2102F479672BAEC3CA47F011A6703BAFF2613236</infoHash>
<magnetURI><![CDATA[magnet:?xt=urn:btih:EEBPI6LHFOXMHSSH6AI2M4B3V7ZGCMRW&dn=The.Voice.S04E22.HDTV.x264-2HD]]></magnetURI>
</torrent>
</item>
<item>
<title><![CDATA[Some Assembly Required Series 2 - Honda ATV factory 1x10 [DVDRIP - MVGROUP]]]></title>
<link>http://torrent.zoink.it/Some.Assembly.Required.Series.2.10of12.Honda.ATV.factory.DVDRip.x264.AAC.[MVGroup.org].torrent</link>
<category domain="http://eztv.it/shows/187/mv-group-documentaries/"><![CDATA[TV Show / MV Group Documentaries]]></category>
<pubDate>Tue, 28 May 2013 17:13:11 -0500</pubDate>
<description><![CDATA[Show Name: Some Assembly Required Series 2; Episode Title: Honda ATV factory; Season: 1; Episode: 10]]></description>
<enclosure url="http://torrent.zoink.it/Some.Assembly.Required.Series.2.10of12.Honda.ATV.factory.DVDRip.x264.AAC.[MVGroup.org].torrent" length="247036226" type="application/x-bittorrent" />
<comments>http://eztv.it/forum/discuss/45219/</comments>
<guid>http://eztv.it/ep/45219/some-assembly-required-series-2-10of12-honda-atv-factory-dvdrip-x264-aac-mvgroup/</guid>
<torrent xmlns="http://xmlns.ezrss.it/0.1/">
<fileName><![CDATA[Some.Assembly.Required.Series.2.10of12.Honda.ATV.factory.DVDRip.x264.AAC-MVGroup.[MVGroup.org].torrent]]></fileName>
<contentLength>247036226</contentLength>
<infoHash>4DA1A3D40B38FACBD2A8CDEF02EE273E8AD4343D</infoHash>
<magnetURI><![CDATA[magnet:?xt=urn:btih:4DA1A3D40B38FACBD2A8CDEF02EE273E8AD4343D&dn=Some.Assembly.Required.Series.2.10of12.Honda.ATV.factory.DVDRip.x264.AAC-MVGroup]]></magnetURI>
</torrent>
</item>
<item>
<title><![CDATA[The Wright Way 1x6 [HDTV - RIVER]]]></title>
<link>http://torrent.zoink.it/The.Wright.Way.S01E06.HDTV.x264-RiVER.[eztv].torrent</link>
<category domain="http://eztv.it/shows/840/the-wright-way/"><![CDATA[TV Show / The Wright Way]]></category>
<pubDate>Tue, 28 May 2013 16:34:37 -0500</pubDate>
<description><![CDATA[Show Name: The Wright Way; Episode Title: N/A; Season: 1; Episode: 6]]></description>
<enclosure url="http://torrent.zoink.it/The.Wright.Way.S01E06.HDTV.x264-RiVER.[eztv].torrent" length="265597160" type="application/x-bittorrent" />
<comments>http://eztv.it/forum/discuss/45218/</comments>
<guid>http://eztv.it/ep/45218/the-wright-way-s01e06-hdtv-x264-river/</guid>
<torrent xmlns="http://xmlns.ezrss.it/0.1/">
<fileName><![CDATA[The.Wright.Way.S01E06.HDTV.x264-RiVER.[eztv].torrent]]></fileName>
<contentLength>265597160</contentLength>
<infoHash>22B0D5F2EF4E2D8A7DB9934B1AC4F236AF51635C</infoHash>
<magnetURI><![CDATA[magnet:?xt=urn:btih:EKYNL4XPJYWYU7NZSNFRVRHSG2XVCY24&dn=The.Wright.Way.S01E06.HDTV.x264-RiVER]]></magnetURI>
</torrent>
</item>
<item>
<title><![CDATA[The Apprentice UK 9x5 [HDTV - ANGELIC]]]></title>
<link>http://torrent.zoink.it/The.Apprentice.UK.S09E05.HDTV.x264-ANGELiC.[eztv].torrent</link>
<category domain="http://eztv.it/shows/14/the-apprentice-uk/"><![CDATA[TV Show / The Apprentice (UK)]]></category>
<pubDate>Tue, 28 May 2013 15:52:44 -0500</pubDate>
<description><![CDATA[Show Name: The Apprentice UK; Episode Title: N/A; Season: 9; Episode: 5]]></description>
<enclosure url="http://torrent.zoink.it/The.Apprentice.UK.S09E05.HDTV.x264-ANGELiC.[eztv].torrent" length="309110658" type="application/x-bittorrent" />
<comments>http://eztv.it/forum/discuss/45216/</comments>
<guid>http://eztv.it/ep/45216/the-apprentice-uk-s09e05-hdtv-x264-angelic/</guid>
<torrent xmlns="http://xmlns.ezrss.it/0.1/">
<fileName><![CDATA[The.Apprentice.UK.S09E05.HDTV.x264-ANGELiC.[eztv].torrent]]></fileName>
<contentLength>309110658</contentLength>
<infoHash>ECC009BF4B403F1676898836A8F9B57B013DFC21</infoHash>
<magnetURI><![CDATA[magnet:?xt=urn:btih:5TAATP2LIA7RM5UJRA3KR6NVPMAT37BB&dn=The.Apprentice.UK.S09E05.HDTV.x264-ANGELiC]]></magnetURI>
</torrent>
</item>
<item>
<title><![CDATA[Thames Television The Life and Times of Lord Mountbatten - The Last Viceroy 1x9]]></title>
<link>http://torrent.zoink.it/Thames.Television.The.Life.and.Times.of.Lord.Mountbatten.09of12.The.Last.Viceroy.XviD.AC3.[MVGroup.org].torrent</link>
<category domain="http://eztv.it/shows/187/mv-group-documentaries/"><![CDATA[TV Show / MV Group Documentaries]]></category>
<pubDate>Tue, 28 May 2013 15:10:45 -0500</pubDate>
<description><![CDATA[Show Name: Thames Television The Life and Times of Lord Mountbatten; Episode Title: The Last Viceroy; Season: 1; Episode: 9]]></description>
<enclosure url="http://torrent.zoink.it/Thames.Television.The.Life.and.Times.of.Lord.Mountbatten.09of12.The.Last.Viceroy.XviD.AC3.[MVGroup.org].torrent" length="824094720" type="application/x-bittorrent" />
<comments>http://eztv.it/forum/discuss/45215/</comments>
<guid>http://eztv.it/ep/45215/thames-television-the-life-and-times-of-lord-mountbatten-09of12-the-last-viceroy-xvid-ac3-mvgroup/</guid>
<torrent xmlns="http://xmlns.ezrss.it/0.1/">
<fileName><![CDATA[Thames.Television.The.Life.and.Times.of.Lord.Mountbatten.09of12.The.Last.Viceroy.XviD.AC3-MVGroup.[MVGroup.org].torrent]]></fileName>
<contentLength>824094720</contentLength>
<infoHash>C768C6BE484B688B9BF3C3F07CC03BF5F6D24F7A</infoHash>
<magnetURI><![CDATA[magnet:?xt=urn:btih:C768C6BE484B688B9BF3C3F07CC03BF5F6D24F7A&dn=Thames.Television.The.Life.and.Times.of.Lord.Mountbatten.09of12.The.Last.Viceroy.XviD.AC3-MVGroup]]></magnetURI>
</torrent>
</item>
<item>
<title><![CDATA[Zero Hour US 1x9 [HDTV - SKGTV]]]></title>
<link>http://torrent.zoink.it/Zero.Hour.US.S01E09.HDTV.X264-SKGTV.[eztv].torrent</link>
<category domain="http://eztv.it/shows/786/zero-hour-us/"><![CDATA[TV Show / Zero Hour (US)]]></category>
<pubDate>Tue, 28 May 2013 14:31:39 -0500</pubDate>
<description><![CDATA[Show Name: Zero Hour US; Episode Title: N/A; Season: 1; Episode: 9]]></description>
<enclosure url="http://torrent.zoink.it/Zero.Hour.US.S01E09.HDTV.X264-SKGTV.[eztv].torrent" length="269843422" type="application/x-bittorrent" />
<comments>http://eztv.it/forum/discuss/45214/</comments>
<guid>http://eztv.it/ep/45214/zero-hour-us-s01e09-hdtv-x264-skgtv/</guid>
<torrent xmlns="http://xmlns.ezrss.it/0.1/">
<fileName><![CDATA[Zero.Hour.US.S01E09.HDTV.X264-SKGTV.[eztv].torrent]]></fileName>
<contentLength>269843422</contentLength>
<infoHash>41E01EA7C07E9C5251D1AA5A3D34E20A177FB746</infoHash>
<magnetURI><![CDATA[magnet:?xt=urn:btih:IHQB5J6AP2OFEUORVJND2NHCBILX7N2G&dn=Zero.Hour.US.S01E09.HDTV.X264-SKGTV]]></magnetURI>
</torrent>
</item>
<item>
<title><![CDATA[Some Assembly Required Series 2 - Making Bread 1x9 [DVDRIP - MVGROUP]]]></title>
<link>http://torrent.zoink.it/Some.Assembly.Required.Series.2.09of12.Making.Bread.DVDRip.x264.AAC.[MVGroup.org].torrent</link>
<category domain="http://eztv.it/shows/187/mv-group-documentaries/"><![CDATA[TV Show / MV Group Documentaries]]></category>
<pubDate>Tue, 28 May 2013 13:58:54 -0500</pubDate>
<description><![CDATA[Show Name: Some Assembly Required Series 2; Episode Title: Making Bread; Season: 1; Episode: 9]]></description>
<enclosure url="http://torrent.zoink.it/Some.Assembly.Required.Series.2.09of12.Making.Bread.DVDRip.x264.AAC.[MVGroup.org].torrent" length="247696085" type="application/x-bittorrent" />
<comments>http://eztv.it/forum/discuss/45213/</comments>
<guid>http://eztv.it/ep/45213/some-assembly-required-series-2-09of12-making-bread-dvdrip-x264-aac-mvgroup/</guid>
<torrent xmlns="http://xmlns.ezrss.it/0.1/">
<fileName><![CDATA[Some.Assembly.Required.Series.2.09of12.Making.Bread.DVDRip.x264.AAC-MVGroup.[MVGroup.org].torrent]]></fileName>
<contentLength>247696085</contentLength>
<infoHash>F177745F2B3F16F336CF6F0155D7C6CD42DC6FBD</infoHash>
<magnetURI><![CDATA[magnet:?xt=urn:btih:F177745F2B3F16F336CF6F0155D7C6CD42DC6FBD&dn=Some.Assembly.Required.Series.2.09of12.Making.Bread.DVDRip.x264.AAC-MVGroup]]></magnetURI>
</torrent>
</item>
<item>
<title><![CDATA[BBC Archaeology A Secret History - The Search for Civilisation 1x2 [PDTV - MVGROUP]]]></title>
<link>http://torrent.zoink.it/BBC.Archaeology.A.Secret.History.2of3.The.Search.for.Civilisation.PDTV.x264.AAC.[MVGroup.org].torrent</link>
<category domain="http://eztv.it/shows/187/mv-group-documentaries/"><![CDATA[TV Show / MV Group Documentaries]]></category>
<pubDate>Tue, 28 May 2013 12:58:41 -0500</pubDate>
<description><![CDATA[Show Name: BBC Archaeology A Secret History; Episode Title: The Search for Civilisation; Season: 1; Episode: 2]]></description>
<enclosure url="http://torrent.zoink.it/BBC.Archaeology.A.Secret.History.2of3.The.Search.for.Civilisation.PDTV.x264.AAC.[MVGroup.org].torrent" length="776705361" type="application/x-bittorrent" />
<comments>http://eztv.it/forum/discuss/45212/</comments>
<guid>http://eztv.it/ep/45212/bbc-archaeology-a-secret-history-2of3-the-search-for-civilisation-pdtv-x264-aac-mvgroup/</guid>
<torrent xmlns="http://xmlns.ezrss.it/0.1/">
<fileName><![CDATA[BBC.Archaeology.A.Secret.History.2of3.The.Search.for.Civilisation.PDTV.x264.AAC-MVGroup.[MVGroup.org].torrent]]></fileName>
<contentLength>776705361</contentLength>
<infoHash>0B852C511FD6D5F6D3EB15930D2B2F37DC4324C6</infoHash>
<magnetURI><![CDATA[magnet:?xt=urn:btih:0B852C511FD6D5F6D3EB15930D2B2F37DC4324C6&dn=BBC.Archaeology.A.Secret.History.2of3.The.Search.for.Civilisation.PDTV.x264.AAC-MVGroup]]></magnetURI>
</torrent>
</item>
<item>
<title><![CDATA[BBC Blackadder Rides Again [576P - HDTV - MVGROUP]]]></title>
<link>http://torrent.zoink.it/BBC.Blackadder.Rides.Again.576p.x264.AAC.HDTV.[MVGroup.org].torrent</link>
<category domain="http://eztv.it/shows/187/mv-group-documentaries/"><![CDATA[TV Show / MV Group Documentaries]]></category>
<pubDate>Tue, 28 May 2013 11:29:18 -0500</pubDate>
<description><![CDATA[Show Name: BBC Blackadder Rides Again; Episode Title: N/A; Episode Date: ]]></description>
<enclosure url="http://torrent.zoink.it/BBC.Blackadder.Rides.Again.576p.x264.AAC.HDTV.[MVGroup.org].torrent" length="663622048" type="application/x-bittorrent" />
<comments>http://eztv.it/forum/discuss/45211/</comments>
<guid>http://eztv.it/ep/45211/bbc-blackadder-rides-again-576p-x264-aac-hdtv-mvgroup/</guid>
<torrent xmlns="http://xmlns.ezrss.it/0.1/">
<fileName><![CDATA[BBC.Blackadder.Rides.Again.576p.x264.AAC.HDTV-MVGroup.[MVGroup.org].torrent]]></fileName>
<contentLength>663622048</contentLength>
<infoHash>84B793AA94AD8A752F661BD4CBC0460DA494E397</infoHash>
<magnetURI><![CDATA[magnet:?xt=urn:btih:84B793AA94AD8A752F661BD4CBC0460DA494E397&dn=BBC.Blackadder.Rides.Again.576p.x264.AAC.HDTV-MVGroup]]></magnetURI>
</torrent>
</item>
<item>
<title><![CDATA[BBC Soillse - 20 in the Med 20x13 [PDTV - MVGROUP]]]></title>
<link>http://torrent.zoink.it/BBC.Soillse.2013.20.in.the.Med.PDTV.x264.AAC.[MVGroup.org].torrent</link>
<category domain="http://eztv.it/shows/187/mv-group-documentaries/"><![CDATA[TV Show / MV Group Documentaries]]></category>
<pubDate>Tue, 28 May 2013 10:13:57 -0500</pubDate>
<description><![CDATA[Show Name: BBC Soillse; Episode Title: 20 in the Med; Season: 20; Episode: 13]]></description>
<enclosure url="http://torrent.zoink.it/BBC.Soillse.2013.20.in.the.Med.PDTV.x264.AAC.[MVGroup.org].torrent" length="705438782" type="application/x-bittorrent" />
<comments>http://eztv.it/forum/discuss/45210/</comments>
<guid>http://eztv.it/ep/45210/bbc-soillse-2013-20-in-the-med-pdtv-x264-aac-mvgroup/</guid>
<torrent xmlns="http://xmlns.ezrss.it/0.1/">
<fileName><![CDATA[BBC.Soillse.2013.20.in.the.Med.PDTV.x264.AAC-MVGroup.[MVGroup.org].torrent]]></fileName>
<contentLength>705438782</contentLength>
<infoHash>961E91A4C404E7F039E512E32BCE7C4F6BCED3C8</infoHash>
<magnetURI><![CDATA[magnet:?xt=urn:btih:961E91A4C404E7F039E512E32BCE7C4F6BCED3C8&dn=BBC.Soillse.2013.20.in.the.Med.PDTV.x264.AAC-MVGroup]]></magnetURI>
</torrent>
</item>
<item>
<title><![CDATA[Revolution 2012 1x19 [720P - HDTV - DIMENSION]]]></title>
<link>http://torrent.zoink.it/Revolution.2012.S01E19.720p.HDTV.X264-DIMENSION.[eztv].torrent</link>
<category domain="http://eztv.it/shows/681/revolution-2012/"><![CDATA[TV Show / Revolution (2012)]]></category>
<pubDate>Tue, 28 May 2013 08:54:50 -0500</pubDate>
<description><![CDATA[Show Name: Revolution 2012; Episode Title: N/A; Season: 1; Episode: 19]]></description>
<enclosure url="http://torrent.zoink.it/Revolution.2012.S01E19.720p.HDTV.X264-DIMENSION.[eztv].torrent" length="789642929" type="application/x-bittorrent" />
<comments>http://eztv.it/forum/discuss/45208/</comments>
<guid>http://eztv.it/ep/45208/revolution-2012-s01e19-720p-hdtv-x264-dimension/</guid>
<torrent xmlns="http://xmlns.ezrss.it/0.1/">
<fileName><![CDATA[Revolution.2012.S01E19.720p.HDTV.X264-DIMENSION.[eztv].torrent]]></fileName>
<contentLength>789642929</contentLength>
<infoHash>E4B13D1FC3EAC17A713E9E70A3C4A60B417BEE2B</infoHash>
<magnetURI><![CDATA[magnet:?xt=urn:btih:4SYT2H6D5LAXU4J6TZYKHRFGBNAXX3RL&dn=Revolution.2012.S01E19.720p.HDTV.X264-DIMENSION]]></magnetURI>
</torrent>
</item>
<item>
<title><![CDATA[5 inch Floppy - Blade Symphony And Assault Android Cactus 4x12 [HDTV - 5IF]]]></title>
<link>http://torrent.zoink.it/5.inch.Floppy.S04E12.Blade.Symphony.And.Assault.Android.Cactus.HDTV.H264-5IF.[eztv].torrent</link>
<category domain="http://eztv.it/shows/482/5-inch-floppy/"><![CDATA[TV Show / 5 inch Floppy]]></category>
<pubDate>Tue, 28 May 2013 08:47:28 -0500</pubDate>
<description><![CDATA[Show Name: 5 inch Floppy; Episode Title: Blade Symphony And Assault Android Cactus; Season: 4; Episode: 12]]></description>
<enclosure url="http://torrent.zoink.it/5.inch.Floppy.S04E12.Blade.Symphony.And.Assault.Android.Cactus.HDTV.H264-5IF.[eztv].torrent" length="113188270" type="application/x-bittorrent" />
<comments>http://eztv.it/forum/discuss/45207/</comments>
<guid>http://eztv.it/ep/45207/5-inch-floppy-s04e12-blade-symphony-and-assault-android-cactus-hdtv-h264-5if/</guid>
<torrent xmlns="http://xmlns.ezrss.it/0.1/">
<fileName><![CDATA[5.inch.Floppy.S04E12.Blade.Symphony.And.Assault.Android.Cactus.HDTV.H264-5IF.[eztv].torrent]]></fileName>
<contentLength>113188270</contentLength>
<infoHash>18A1997F0AFBE26F2F9F28C1E3BD52F2032DAB58</infoHash>
<magnetURI><![CDATA[magnet:?xt=urn:btih:DCQZS7YK7PRG6L47FDA6HPKS6IBS3K2Y&dn=5.inch.Floppy.S04E12.Blade.Symphony.And.Assault.Android.Cactus.HDTV.H264-5IF]]></magnetURI>
</torrent>
</item>
<item>
<title><![CDATA[Craig Ferguson - Sir Ben Kingsley 2013-05-27 [HDTV - 2HD]]]></title>
<link>http://torrent.zoink.it/Craig.Ferguson.2013.05.27.Sir.Ben.Kingsley.HDTV.x264-2HD.[eztv].torrent</link>
<category domain="http://eztv.it/shows/358/craig-ferguson/"><![CDATA[TV Show / Craig Ferguson]]></category>
<pubDate>Tue, 28 May 2013 08:36:11 -0500</pubDate>
<description><![CDATA[Show Name: Craig Ferguson; Episode Title: Sir Ben Kingsley; Episode Date: 2013-05-27]]></description>
<enclosure url="http://torrent.zoink.it/Craig.Ferguson.2013.05.27.Sir.Ben.Kingsley.HDTV.x264-2HD.[eztv].torrent" length="324366904" type="application/x-bittorrent" />
<comments>http://eztv.it/forum/discuss/45205/</comments>
<guid>http://eztv.it/ep/45205/craig-ferguson-2013-05-27-sir-ben-kingsley-hdtv-x264-2hd/</guid>
<torrent xmlns="http://xmlns.ezrss.it/0.1/">
<fileName><![CDATA[Craig.Ferguson.2013.05.27.Sir.Ben.Kingsley.HDTV.x264-2HD.[eztv].torrent]]></fileName>
<contentLength>324366904</contentLength>
<infoHash>22598950F3E656BD975F7C3CB9A5F3B8159AD908</infoHash>
<magnetURI><![CDATA[magnet:?xt=urn:btih:EJMYSUHT4ZLL3F27PQ6LTJPTXAKZVWII&dn=Craig.Ferguson.2013.05.27.Sir.Ben.Kingsley.HDTV.x264-2HD]]></magnetURI>
</torrent>
</item>
<item>
<title><![CDATA[National Geographic Meet the Natives USA - The Cowboy People 1x1 [PDTV - MVGROUP]]]></title>
<link>http://torrent.zoink.it/National.Geographic.Meet.the.Natives.USA.1of5.The.Cowboy.People.PDTV.x264.AAC.[MVGroup.org].torrent</link>
<category domain="http://eztv.it/shows/187/mv-group-documentaries/"><![CDATA[TV Show / MV Group Documentaries]]></category>
<pubDate>Tue, 28 May 2013 08:09:06 -0500</pubDate>
<description><![CDATA[Show Name: National Geographic Meet the Natives USA; Episode Title: The Cowboy People; Season: 1; Episode: 1]]></description>
<enclosure url="http://torrent.zoink.it/National.Geographic.Meet.the.Natives.USA.1of5.The.Cowboy.People.PDTV.x264.AAC.[MVGroup.org].torrent" length="485301890" type="application/x-bittorrent" />
<comments>http://eztv.it/forum/discuss/45204/</comments>
<guid>http://eztv.it/ep/45204/national-geographic-meet-the-natives-usa-1of5-the-cowboy-people-pdtv-x264-aac-mvgroup/</guid>
<torrent xmlns="http://xmlns.ezrss.it/0.1/">
<fileName><![CDATA[National.Geographic.Meet.the.Natives.USA.1of5.The.Cowboy.People.PDTV.x264.AAC-MVGroup.[MVGroup.org].torrent]]></fileName>
<contentLength>485301890</contentLength>
<infoHash>7B0A99439B49837874E17A83C5260A6216D4ADBC</infoHash>
<magnetURI><![CDATA[magnet:?xt=urn:btih:7B0A99439B49837874E17A83C5260A6216D4ADBC&dn=National.Geographic.Meet.the.Natives.USA.1of5.The.Cowboy.People.PDTV.x264.AAC-MVGroup]]></magnetURI>
</torrent>
</item>
<item>
<title><![CDATA[BSkyB Ladyboys Series 2 1x5 [PDTV - MVGROUP]]]></title>
<link>http://torrent.zoink.it/BSkyB.Ladyboys.Series.2.5of5.PDTV.x264.AAC.[MVGroup.org].torrent</link>
<category domain="http://eztv.it/shows/187/mv-group-documentaries/"><![CDATA[TV Show / MV Group Documentaries]]></category>
<pubDate>Tue, 28 May 2013 06:15:01 -0500</pubDate>
<description><![CDATA[Show Name: BSkyB Ladyboys Series 2; Episode Title: N/A; Season: 1; Episode: 5]]></description>
<enclosure url="http://torrent.zoink.it/BSkyB.Ladyboys.Series.2.5of5.PDTV.x264.AAC.[MVGroup.org].torrent" length="703038026" type="application/x-bittorrent" />
<comments>http://eztv.it/forum/discuss/45203/</comments>
<guid>http://eztv.it/ep/45203/bskyb-ladyboys-series-2-5of5-pdtv-x264-aac-mvgroup/</guid>
<torrent xmlns="http://xmlns.ezrss.it/0.1/">
<fileName><![CDATA[BSkyB.Ladyboys.Series.2.5of5.PDTV.x264.AAC-MVGroup.[MVGroup.org].torrent]]></fileName>
<contentLength>703038026</contentLength>
<infoHash>1CBBA05C502482BDA5DB8C4D417C11750B6E8590</infoHash>
<magnetURI><![CDATA[magnet:?xt=urn:btih:1CBBA05C502482BDA5DB8C4D417C11750B6E8590&dn=BSkyB.Ladyboys.Series.2.5of5.PDTV.x264.AAC-MVGroup]]></magnetURI>
</torrent>
</item>
<item>
<title><![CDATA[Thames Television The Life and Times of Lord Mountbatten - The Meaning of Victory 1x8]]></title>
<link>http://torrent.zoink.it/Thames.Television.The.Life.and.Times.of.Lord.Mountbatten.08of12.The.Meaning.of.Victory.XviD.AC3.[MVGroup.org].torrent</link>
<category domain="http://eztv.it/shows/187/mv-group-documentaries/"><![CDATA[TV Show / MV Group Documentaries]]></category>
<pubDate>Tue, 28 May 2013 04:57:21 -0500</pubDate>
<description><![CDATA[Show Name: Thames Television The Life and Times of Lord Mountbatten; Episode Title: The Meaning of Victory; Season: 1; Episode: 8]]></description>
<enclosure url="http://torrent.zoink.it/Thames.Television.The.Life.and.Times.of.Lord.Mountbatten.08of12.The.Meaning.of.Victory.XviD.AC3.[MVGroup.org].torrent" length="824188928" type="application/x-bittorrent" />
<comments>http://eztv.it/forum/discuss/45202/</comments>
<guid>http://eztv.it/ep/45202/thames-television-the-life-and-times-of-lord-mountbatten-08of12-the-meaning-of-victory-xvid-ac3-mvgroup/</guid>
<torrent xmlns="http://xmlns.ezrss.it/0.1/">
<fileName><![CDATA[Thames.Television.The.Life.and.Times.of.Lord.Mountbatten.08of12.The.Meaning.of.Victory.XviD.AC3-MVGroup.[MVGroup.org].torrent]]></fileName>
<contentLength>824188928</contentLength>
<infoHash>0AD782B37724BD8F8988FE277CE7103752BB7EC4</infoHash>
<magnetURI><![CDATA[magnet:?xt=urn:btih:0AD782B37724BD8F8988FE277CE7103752BB7EC4&dn=Thames.Television.The.Life.and.Times.of.Lord.Mountbatten.08of12.The.Meaning.of.Victory.XviD.AC3-MVGroup]]></magnetURI>
</torrent>
</item>
<item>
<title><![CDATA[Some Assembly Required Series 2 - Peterbilt Trucks 1x8 [DVDRIP - MVGROUP]]]></title>
<link>http://torrent.zoink.it/Some.Assembly.Required.Series.2.08of12.Peterbilt.Trucks.DVDRip.x264.AAC.[MVGroup.org].torrent</link>
<category domain="http://eztv.it/shows/187/mv-group-documentaries/"><![CDATA[TV Show / MV Group Documentaries]]></category>
<pubDate>Tue, 28 May 2013 03:55:43 -0500</pubDate>
<description><![CDATA[Show Name: Some Assembly Required Series 2; Episode Title: Peterbilt Trucks; Season: 1; Episode: 8]]></description>
<enclosure url="http://torrent.zoink.it/Some.Assembly.Required.Series.2.08of12.Peterbilt.Trucks.DVDRip.x264.AAC.[MVGroup.org].torrent" length="247606900" type="application/x-bittorrent" />
<comments>http://eztv.it/forum/discuss/45201/</comments>
<guid>http://eztv.it/ep/45201/some-assembly-required-series-2-08of12-peterbilt-trucks-dvdrip-x264-aac-mvgroup/</guid>
<torrent xmlns="http://xmlns.ezrss.it/0.1/">
<fileName><![CDATA[Some.Assembly.Required.Series.2.08of12.Peterbilt.Trucks.DVDRip.x264.AAC-MVGroup.[MVGroup.org].torrent]]></fileName>
<contentLength>247606900</contentLength>
<infoHash>C1A7AB6E61A1BD0E5B6FD1274AA07A1A5CC63038</infoHash>
<magnetURI><![CDATA[magnet:?xt=urn:btih:C1A7AB6E61A1BD0E5B6FD1274AA07A1A5CC63038&dn=Some.Assembly.Required.Series.2.08of12.Peterbilt.Trucks.DVDRip.x264.AAC-MVGroup]]></magnetURI>
</torrent>
</item>
<item>
<title><![CDATA[BBC Secret Knowledge - The Art of the Vikings 810p 20x13 [HDTV - MVGROUP]]]></title>
<link>http://torrent.zoink.it/BBC.Secret.Knowledge.2013.The.Art.of.the.Vikings.810p.x264.AAC.HDTV.[MVGroup.org].torrent</link>
<category domain="http://eztv.it/shows/187/mv-group-documentaries/"><![CDATA[TV Show / MV Group Documentaries]]></category>
<pubDate>Tue, 28 May 2013 02:55:47 -0500</pubDate>
<description><![CDATA[Show Name: BBC Secret Knowledge; Episode Title: The Art of the Vikings 810p; Season: 20; Episode: 13]]></description>
<enclosure url="http://torrent.zoink.it/BBC.Secret.Knowledge.2013.The.Art.of.the.Vikings.810p.x264.AAC.HDTV.[MVGroup.org].torrent" length="508772179" type="application/x-bittorrent" />
<comments>http://eztv.it/forum/discuss/45200/</comments>
<guid>http://eztv.it/ep/45200/bbc-secret-knowledge-2013-the-art-of-the-vikings-810p-x264-aac-hdtv-mvgroup/</guid>
<torrent xmlns="http://xmlns.ezrss.it/0.1/">
<fileName><![CDATA[BBC.Secret.Knowledge.2013.The.Art.of.the.Vikings.810p.x264.AAC.HDTV-MVGroup.[MVGroup.org].torrent]]></fileName>
<contentLength>508772179</contentLength>
<infoHash>52635B9BAA1EF90FD070E78317DDBE29603AD7A1</infoHash>
<magnetURI><![CDATA[magnet:?xt=urn:btih:52635B9BAA1EF90FD070E78317DDBE29603AD7A1&dn=BBC.Secret.Knowledge.2013.The.Art.of.the.Vikings.810p.x264.AAC.HDTV-MVGroup]]></magnetURI>
</torrent>
</item>
</channel>
</rss>
<!-- live -->

View File

@ -1,88 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
<link rel="self" href="http://fulltextrssfeed.com/www.numerama.com/rss/news.rss" xmlns="http://www.w3.org/2005/Atom" />
<title>Numerama.com - Magazine</title>
<link>http://www.numerama.com/</link>
<description>Actualite informatique et numerique // via fulltextrssfeed.com</description>
<item>
<link>http://www.numerama.com/magazine/25669-brevets-un-juge-doute-de-la-bonne-volonte-de-google-et-apple.html</link>
<guid isPermaLink="true" >http://www.numerama.com/magazine/25669-brevets-un-juge-doute-de-la-bonne-volonte-de-google-et-apple.html</guid>
<title>Brevets : un juge doute de la bonne volonté de Google et Apple</title>
<pubDate>Fri, 12 Apr 2013 15:38:15 +0000</pubDate>
<description>&lt;div id=&quot;newstext&quot;&gt;
&lt;p&gt;&lt;span&gt;&lt;img alt=&quot;&quot; src=&quot;http://www.numerama.com/media/attach/classaction.jpg&quot; class=&quot;c50&quot;/&gt; Apple et Google souhaitent-ils vraiment en finir avec leur interminable querelle engagée au nom de la propriété industrielle ? Robert Scola en doute. Juge fédéral officiant au tribunal de Miami, il a rédigé une ordonnance dans laquelle il s'interroge sur les buts réels poursuivis par les deux entreprises high-tech. Le magistrat les suspecte en effet de chercher à faire traîner la procédure.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&quot;&lt;em&gt;Les deux parties n'ont aucun intérêt à résoudre efficacement et rapidement ce différend ; elles paraissent plutôt décidées à utiliser ce litige dans le monde entier comme une stratégie commerciale qui ne semble ne pas avoir de fin&lt;/em&gt;&quot;, a-t-il écrit, avant de considérer que cette manière de procéder n'est pas une utilisation convenable des moyens judiciaires.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;À ses yeux, cette attitude trahit donc une stratégie commerciale. Mais l'ordonnance de Robert Scola, consultée par &lt;a href=&quot;http://redirect.viglink.com?key=11fe087258b6fc0532a5ccfc924805c0&amp;u=http%3A%2F%2Fwww.bloomberg.com%2Fnews%2F2013-04-10%2Fapple-google-not-interested-in-settlement-judge-says.html&quot; target=&quot;_blank&quot;&gt;Bloomberg&lt;/a&gt;, ne s'arrête pas là. Le juge trouve en outre le comportement des deux firmes quelque peu cavalier. &quot;&lt;em&gt;Sans la moindre gêne, [elles] demandent maintenant au tribunal de nettoyer leur bazar en tenant une audience afin de diminuer la taille et la complexité de l'affaire. Le tribunal refuse cette suggestion&lt;/em&gt;&quot;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;En conséquence, le juge laisse un délai de quatre mois à Google et Apple pour s'entendre. Si elles échouent à s'accorder, Robert Scola est prêt à suspendre la procédure indéfiniment, le temps que les deux entreprises trouvent une solution. Selon le juge, le litige porte sur 12 brevets. 180 réclamations ont été signalées et les deux groupes s'opposent sur plus de 100 termes.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Depuis bientôt trois ans, les principales entreprises high-tech s'écharpent au nom de la propriété industrielle. Si certaines actions en justice sont vraisemblablement légitimes, d'autres en revanche sont déposées dans le seul but d'entraver les activités commerciales d'un concurrent. D'ailleurs, il n'est pas rare de lire des demandes de retrait visant un produit ou une gamme d'un rival.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;&lt;img src=&quot;http://pixel.quantserve.com/pixel/p-89EKCgBk8MZdE.gif&quot; border=&quot;0&quot; height=&quot;1&quot; width=&quot;1&quot; /&gt;</description>
</item>
<item>
<link>http://www.numerama.com/magazine/25671-la-taxe-copie-privee-remplacee-par-une-taxe-sur-la-high-tech.html</link>
<guid isPermaLink="true" >http://www.numerama.com/magazine/25671-la-taxe-copie-privee-remplacee-par-une-taxe-sur-la-high-tech.html</guid>
<title>La taxe copie privée remplacée par une taxe sur la high-tech ?</title>
<pubDate>Fri, 12 Apr 2013 15:15:20 +0000</pubDate>
<description>&lt;div id=&quot;newstext&quot;&gt;
&lt;p&gt;&lt;span&gt;Pierre Lescure va-t-il complètement chambouler le fonctionnement de la rémunération copie privée, qui permet aux ayants droit d'encaisser collectivement près de 200 millions d'euros par an ? Au moment où la Commission Européenne menace d&lt;a href=&quot;http://redirect.viglink.com?key=11fe087258b6fc0532a5ccfc924805c0&amp;u=http%3A%2F%2Fwww.numerama.com%2Fmagazine%2F24998-l-harmonisation-europeenne-de-la-copie-privee-rejetee-par-les-ayants-droit.html&quot;&gt;'imposer une harmonisation des taux&lt;/a&gt; qui serait défavorable aux industries culturelles françaises, où &lt;a href=&quot;http://redirect.viglink.com?key=11fe087258b6fc0532a5ccfc924805c0&amp;u=http%3A%2F%2Fwww.numerama.com%2Fmagazine%2F24785-copie-privee-le-conseil-constitutionnel-sanctionne-l-abus-de-pouvoir.html&quot;&gt;la commission copie privée vole en éclats&lt;/a&gt;, et où il apparaît très difficile de trouver les textes juridiques permettant de &lt;a href=&quot;http://redirect.viglink.com?key=11fe087258b6fc0532a5ccfc924805c0&amp;u=http%3A%2F%2Fwww.numerama.com%2Fmagazine%2F24097-le-cloud-permet-des-copies-privees-qu-il-faut-taxer-tranche-le-cspla.html&quot;&gt;taxer le cloud au nom d'une copie privée&lt;/a&gt; qui n'existe plus vraiment, l'ancien patron de Canal+ pourrait proposer une solution que la plupart des ayants droit applaudiront des deux mains.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;En effet, selon le site &lt;a href=&quot;http://redirect.viglink.com?key=11fe087258b6fc0532a5ccfc924805c0&amp;u=http%3A%2F%2Felectronlibre.info%2Fla-copie-privee-la-grosse-reforme-mijotee-par-pierre-lescure%2F&quot; target=&quot;_blank&quot;&gt;Electron Libre&lt;/a&gt; (sur abonnement), Pierre Lescure pourrait proposer de supprimer techniquement la rémunération pour copie privée, pour la remplacer par une véritable taxe au sens fiscal du terme, assise sur le chiffre d'affaires des industries high-tech. L'actuelle commission copie privée, qui ne sert que d'arène d'affrontements entre ayants droit, industriels et consommateurs, serait alors supprimée.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&quot;&lt;em&gt;A la place, la mission préconise un comité, sorte de commission réduite aux seules organisations de perceptions et de répartition qui conserveraient comme prérogative les clefs de répartition de la collecte&lt;/em&gt;&quot;, précise Electron Libre. En clair, l'impôt serait prélevé au bénéfice de l'industrie du disque et du cinéma, selon des clés déterminées par l'Etat, et c'est l'industrie elle-même qui se partagerait les fruits.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&quot;&lt;em&gt;Le cabinet dAurélie Filippeti est favorable à cette réforme, la DGmic (direction générale des médias et des industries culturelles, ndlr) aussi, et quelques uns des ayants-droit concernés&lt;/em&gt;&quot;, assurent nos confrères. Seule la Sacem aurait émis des réserves, peut-être parce qu'elle craint de perdre le rôle clé qu'elle détient au sein de la commission copie privée.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Electron Libre dit même que les industries high-tech seraient &quot;&lt;em&gt;plutôt rassurés, car la taxe nouvelle sappliquerait sur lensemble de leur activité&lt;/em&gt;&quot;, de façon lisse, et pas uniquement sur les seuls produits éligibles à la rémunération copie privée. Actuellement par exemple, les ordinateurs de bureau ne sont pas taxés, mais les tablettes le sont.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Notons que si cette idée, qui semble difficile à mettre en oeuvre, voyait réellement le jour, elle ferait peser un risque juridique sur l'existence-même du droit à la copie privée. En effet, le Conseil constitutionnel impose que le bénéfice de l'exception au droit d'auteur pour copie privée soit indemnisé par une rémunération. Donc si la taxe est déconnectée de la copie privée, l'exception copie privée n'existerait plus. &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Réponse le 6 mai prochain, &lt;a href=&quot;http://redirect.viglink.com?key=11fe087258b6fc0532a5ccfc924805c0&amp;u=http%3A%2F%2Fwww.numerama.com%2Fmagazine%2F25605-pierre-lescure-retarde-au-6-mai-son-rapport-sur-l-exception-culturelle.html&quot;&gt;quand Pierre Lescure rendra son rapport post-Hadopi&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;&lt;img src=&quot;http://pixel.quantserve.com/pixel/p-89EKCgBk8MZdE.gif&quot; border=&quot;0&quot; height=&quot;1&quot; width=&quot;1&quot; /&gt;</description>
</item>
<item>
<link>http://www.numerama.com/magazine/25668-l-affiche-de-la-taxe-d-eco-participation-prolonge-jusqu-en-2020.html</link>
<guid isPermaLink="true" >http://www.numerama.com/magazine/25668-l-affiche-de-la-taxe-d-eco-participation-prolonge-jusqu-en-2020.html</guid>
<title>L'affiche de la taxe d'éco-participation prolongé jusqu'en 2020</title>
<pubDate>Fri, 12 Apr 2013 14:50:45 +0000</pubDate>
<description>&lt;div id=&quot;newstext&quot;&gt;
&lt;p&gt;&lt;span&gt;&lt;img align=&quot;right&quot; alt=&quot;&quot; hspace=&quot;10&quot; src=&quot;http://www.numerama.com/media/attach/200px-Recycling_symbol2.svg.png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;En matière de taxes, rares sont les dispositifs provisoires qui ne deviennent pas définitifs. Mais pour une fois, l'application de l'adage devrait être bien accepté par les consommateurs, avec l'adoption de la &lt;a href=&quot;http://redirect.viglink.com?key=11fe087258b6fc0532a5ccfc924805c0&amp;u=http%3A%2F%2Fwww.assemblee-nationale.fr%2F14%2Fpropositions%2Fpion0715.asp&quot; target=&quot;_blank&quot;&gt;proposition de loi&lt;/a&gt; socialiste &quot;&lt;em&gt;relative à la prorogation du mécanisme de l'éco-participation répercutée à l'identique et affichée pour les équipements électriques et électroniques ménagers&lt;/em&gt;&quot;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Jeudi, les sénateurs ont en effet adopté un texte qui avait été voté par l'Assemblée Nationale le 12 février dernier, qui prévoit comme son intitulé l'indique de reconduire l'affichage de la taxe d'éco-participation, prélevée notamment sur les écrans et téléviseurs, et autres appareils électroniques. La proposition de loi ayant été votée à l'identique dans les deux chambres lors de sa première lecture, elle pourra être immédiatement promulguée.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Lorsqu'il est entré en vigueur en 2006, le dispositif d'éco-participation créé par l'&lt;a href=&quot;http://redirect.viglink.com?key=11fe087258b6fc0532a5ccfc924805c0&amp;u=http%3A%2F%2Fwww.legifrance.gouv.fr%2FaffichCodeArticle.do%3Bjsessionid%3D28285FDD520E60261CFBFA8C53A85DC9.tpdjo07v_1%3FidArticle%3DLEGIARTI000025144834%26amp%3BcidTexte%3DLEGITEXT000006074220%26amp%3BcategorieLien%3Did%26amp%3BdateTexte%3D20130701&quot; target=&quot;_blank&quot;&gt;article L514-10-2&lt;/a&gt; du code de l'environnement prévoyait que le montant de la contribution prélevée sur &quot;tout nouvel équipement électrique et électronique ménager&quot; ne soit affiché aux consommateurs que jusqu'au 13 février 2013. La nouvelle loi étend le dispositif jusqu'au 1er janvier 2020, et conserve le principe selon lequel &quot;&lt;em&gt;le coût unitaire supporté pour la gestion des déchets collectés sélectivement issus des équipements électriques et électroniques ménagers mis sur le marché avant le 13 août 2005 (...) est strictement égal au coût de la gestion desdits déchets&lt;/em&gt;&quot;. Elle impose que ce coût soit répercuté à l'identique jusqu'au client final.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;En 2011, selon un rapport de l'Ademe, 193 millions d'euros ont été perçus par les quatre éco-organismes à but non-lucratif agréés par les pouvoirs publics :  Eco-systèmes, Ecologic et ERP et Récylum (ce dernier étant spécialisé dans les lampes usagées). 448 000 tonnes ont été collectées, soit 6,9 kg par habitant. &lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;&lt;img src=&quot;http://pixel.quantserve.com/pixel/p-89EKCgBk8MZdE.gif&quot; border=&quot;0&quot; height=&quot;1&quot; width=&quot;1&quot; /&gt;</description>
</item>
<item>
<link>http://www.numerama.com/magazine/25667-twitter-music-est-officiel.html</link>
<guid isPermaLink="true" >http://www.numerama.com/magazine/25667-twitter-music-est-officiel.html</guid>
<title>Twitter Music est officiel</title>
<pubDate>Fri, 12 Apr 2013 14:03:23 +0000</pubDate>
<description>&lt;div&gt;
&lt;p&gt;L'ouverture de Twitter Music est imminente. Sur le site web du réseau social, une page dédiée est disponible et il est d'ores et déjà possible d'autoriser l'application à accéder à son compte.&lt;/p&gt;
&lt;/div&gt;&lt;div id=&quot;newstext&quot;&gt;
&lt;p&gt;&lt;span&gt;&lt;img alt=&quot;&quot; src=&quot;http://www.numerama.com/media/attach/twittermusic.png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Comme prévu, Twitter se lance dans la musique. Le réseau social a mis en ligne une page accessible à l'adresse &lt;a href=&quot;http://redirect.viglink.com?key=11fe087258b6fc0532a5ccfc924805c0&amp;u=https%3A%2F%2Fmusic.twitter.com%2F&quot; target=&quot;_blank&quot;&gt;music.twitter.com&lt;/a&gt;. La plateforme est pour l'instant inaccessible. Elle devrait toutefois ouvrir ses portes dans les prochaines heures. En attendant, il est d'ores et déjà possible d'autoriser le service &quot;Trending Music Web&quot; à accéder à son compte.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Le mois dernier, nous avions signalé que le réseau social américain &lt;a href=&quot;http://redirect.viglink.com?key=11fe087258b6fc0532a5ccfc924805c0&amp;u=http%3A%2F%2Fwww.numerama.com%2Fmagazine%2F25400-twitter-developperait-une-application-musicale.html&quot;&gt;avait fait main basse sur We Are Hunted&lt;/a&gt;, une startup spécialisée dans la recommandation musicale. Le service devrait être utilisable depuis un navigateur web, mais également via une application mobile. Une application iOS serait d'ores et déjà prête, tandis que celle prévue pour Android arriverait prochainement.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;&lt;img src=&quot;http://pixel.quantserve.com/pixel/p-89EKCgBk8MZdE.gif&quot; border=&quot;0&quot; height=&quot;1&quot; width=&quot;1&quot; /&gt;</description>
</item>
<item>
<link>http://www.numerama.com/magazine/25666-bruxelles-rejette-la-plainte-de-sfr-sur-l-itinerance-entre-orange-et-free.html</link>
<guid isPermaLink="true" >http://www.numerama.com/magazine/25666-bruxelles-rejette-la-plainte-de-sfr-sur-l-itinerance-entre-orange-et-free.html</guid>
<title>Bruxelles rejette la plainte de SFR sur l'itinérance entre Orange et Free</title>
<pubDate>Fri, 12 Apr 2013 13:49:23 +0000</pubDate>
<description>&lt;div id=&quot;newstext&quot;&gt;
&lt;p&gt;&lt;span&gt;&lt;img alt=&quot;&quot; src=&quot;http://www.numerama.com/media/attach/nouveau-logo-sfr.jpg&quot; class=&quot;c50&quot;/&gt; C'est une épine de moins dans le pied de Free. La Commission européenne a balayé le recours déposé par SFR, &lt;a href=&quot;http://redirect.viglink.com?key=11fe087258b6fc0532a5ccfc924805c0&amp;u=http%3A%2F%2Fwww.numerama.com%2Fmagazine%2F24553-l-itinerance-entre-orange-et-free-attaquee-par-sfr-a-bruxelles.html&quot;&gt;qui tentait d'invalider&lt;/a&gt; le contrat d'itinérance mobile signé entre Orange et le jeune opérateur mobile. La filiale de Vivendi avait opté pour une thèse audacieuse, puisqu'elle cherchait à démontrer la capacité, pour l'opérateur historique, de contrôler son partenaire.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&quot;&lt;em&gt;La Commission a classé cette plainte&lt;/em&gt;&quot;, a confié une porte-parole à &lt;a href=&quot;http://redirect.viglink.com?key=11fe087258b6fc0532a5ccfc924805c0&amp;u=http%3A%2F%2Fwww.bfmtv.com%2Feconomie%2Fexclusif-bruxelles-rejette-plainte-sfr-contre-orange-free-490258.html&quot; target=&quot;_blank&quot;&gt;BFM&lt;/a&gt;. &quot;&lt;em&gt;Nous sommes arrivés à la conclusion que ce contrat d'itinérance n'était pas un rachat tel que défini dans le droit des rachats&lt;/em&gt;&quot;. Cependant, la tactique inhabituelle employée par SFR pour contester le contrat d'itinérance entre Orange et Dree a conduit certains observateurs à s'interroger sur les buts réels poursuivis par l'opérateur.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;L'accord entre Orange et Free est en effet confidentiel. Seuls les deux parties connaissent chaque modalité des liens qui les unissent. Or, c'est cette opacité que dénonçait Jean-Bernard Lévy, l'ancien président du directoire de Vivendi. &lt;a href=&quot;http://redirect.viglink.com?key=11fe087258b6fc0532a5ccfc924805c0&amp;u=http%3A%2F%2Fwww.numerama.com%2Fmagazine%2F18205-free-et-orange-signent-un-accord-d-itinerance-pour-le-mobile.html&quot;&gt;Signé en mars 2011&lt;/a&gt;, cet accord permet à Free de louer les infrastructures d'Orange pour faire transiter les communications en 2G et 3G et offrir une couverture similaire à celle d'Orange.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Si SFR n'a pas réussi à casser le partenariat entre Orange et Free, ni même à en exposer le détail, l'opérateur peut au moins se consoler sur un point : celui-ci n'a pas &lt;a href=&quot;http://redirect.viglink.com?key=11fe087258b6fc0532a5ccfc924805c0&amp;u=http%3A%2F%2Fwww.numerama.com%2Fmagazine%2F23181-orange-l-itinerance-avec-free-mobile-n-a-pas-vocation-a-rester-eternelle.html&quot;&gt;vocation à durer éternellement&lt;/a&gt;. Il a même une date de fin : 2018. À cette date, Free Mobile devra exclusivement compter sur son propre réseau pour acheminer les télécommunications de ses clients. Et pour l'instant, le &lt;a href=&quot;http://redirect.viglink.com?key=11fe087258b6fc0532a5ccfc924805c0&amp;u=http%3A%2F%2Fwww.numerama.com%2Fmagazine%2F25654-free-mobile-couvre-pratiquement-un-francais-sur-deux-selon-l-arcep.html&quot;&gt;déploiement suit son cours&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;&lt;img src=&quot;http://pixel.quantserve.com/pixel/p-89EKCgBk8MZdE.gif&quot; border=&quot;0&quot; height=&quot;1&quot; width=&quot;1&quot; /&gt;</description>
</item>
<item>
<link>http://www.numerama.com/magazine/25664-linkedin-s-approprie-l-agregateur-pulse-pour-90-millions-de-dollars.html</link>
<guid isPermaLink="true" >http://www.numerama.com/magazine/25664-linkedin-s-approprie-l-agregateur-pulse-pour-90-millions-de-dollars.html</guid>
<title>LinkedIn s'approprie l'agrégateur Pulse pour 90 millions de dollars</title>
<pubDate>Fri, 12 Apr 2013 12:37:22 +0000</pubDate>
<description>&lt;div id=&quot;newstext&quot;&gt;
&lt;p&gt;&lt;span&gt;&lt;img alt=&quot;&quot; src=&quot;http://www.numerama.com/media/attach/pulselinkedin.png&quot; class=&quot;c50&quot;/&gt; LinkedIn fait son entrée dans le petit monde des agrégateurs. Le réseau social américain vient en effet de s'emparer de Pulse, une plateforme conçue par Alphonso Labs qui permet d'agglomérer divers flux d'information. Et le site spécialisé dans les relations professionnelles n'a pas hésité à y mettre le prix : pour prendre le contrôle du service, LinkedIn &lt;a href=&quot;http://redirect.viglink.com?key=11fe087258b6fc0532a5ccfc924805c0&amp;u=http%3A%2F%2Fwww.lightreading.com%2Flinkedin-to-mobilize-content-with-90m-pulse-acquistion%2F240152799&quot; target=&quot;_blank&quot;&gt;va débourser&lt;/a&gt; 90 millions de dollars.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Cette acquisition ne changera absolument rien pour les usagers de Pulse. Dans un &lt;a href=&quot;http://redirect.viglink.com?key=11fe087258b6fc0532a5ccfc924805c0&amp;u=http%3A%2F%2Fblog.pulse.me%2Fpost%2F47721164686%2Fpulse-joins-the-linkedin-family&quot; target=&quot;_blank&quot;&gt;communiqué&lt;/a&gt;, les responsables du projet assurent que les applications mobiles continueront de fonctionner comme autrefois. Du moins, pour l'instant. Il est assez évident que LinkedIn va chercher à intégrer parfaitement sa nouvelle prise au reste de son écosystème, quitte à modifier la plateforme.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Fondé en 2010, Pulse est disponible sur iOS et Android. Le service peut également être utilisé depuis un navigateur web. Selon les statistiques fournies par LinkedIn, plus de 30 millions d'usagers utilisent Pulse, qui est disponible en neuf langues différentes. LinkedIn note en effet qu'une part importante des membres (40 %) se situe en dehors des États-Unis.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;L'achat de Pulse survient dans un contexte bien particulier. Google a en effet choisi &lt;a href=&quot;http://redirect.viglink.com?key=11fe087258b6fc0532a5ccfc924805c0&amp;u=http%3A%2F%2Fwww.numerama.com%2Fmagazine%2F25379-google-va-fermer-google-reader-le-1er-juillet.html&quot;&gt;de fermer Reader&lt;/a&gt; le 1er juillet prochain. En conséquence, les &lt;a href=&quot;http://redirect.viglink.com?key=11fe087258b6fc0532a5ccfc924805c0&amp;u=http%3A%2F%2Fwww.numerama.com%2Fmagazine%2F25398-la-disparition-prochaine-de-google-reader-agite-la-concurrence.html&quot;&gt;rivaux de l'agrégateur s'activent&lt;/a&gt; pour récupérer les anciens utilisateurs de la plateforme. Or, Pulse fait justement partie des alternatives qui sont recommandées par les médias et les internautes. Un point qui n'a pas dû échapper non plus à LinkedIn.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;&lt;img src=&quot;http://pixel.quantserve.com/pixel/p-89EKCgBk8MZdE.gif&quot; border=&quot;0&quot; height=&quot;1&quot; width=&quot;1&quot; /&gt;</description>
</item>
</channel>
</rss>

View File

@ -1,78 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<opml version="1.0">
<head>
<title>Abonnements dans Google Reader</title>
</head>
<body>
<outline title="Presse" text="Presse">
<outline text="Reflets" title="Reflets" type="rss"
xmlUrl="http://reflets.info/feed/" htmlUrl="http://reflets.info"/>
</outline>
<outline title="Culture" text="Culture">
<outline text="Omnilogismes du jour"
title="Omnilogismes du jour" type="rss"
xmlUrl="http://omnilogie.fr/flux.rss" htmlUrl="http://omnilogie.fr"/>
<outline text="Pourquoi Comment Combien"
title="Pourquoi Comment Combien" type="rss"
xmlUrl="http://viewtext.org/article?url=http%3A%2F%2Ffeeds.feedburner.com%2Fdrgoulu&amp;rl=false" htmlUrl="http://drgoulu.com"/>
<outline text="Science étonnante" title="Science étonnante"
type="rss"
xmlUrl="http://sciencetonnante.wordpress.com/feed/" htmlUrl="http://sciencetonnante.wordpress.com"/>
<outline text="Se Coucher Moins Bête"
title="Se Coucher Moins Bête" type="rss"
xmlUrl="http://viewtext.org/article?url=http%3A%2F%2Fwww.secouchermoinsbete.fr%2Frss%2Fanecdotes.rss.xml&amp;rl=false" htmlUrl="http://www.secouchermoinsbete.fr"/>
</outline>
<outline title="Droits du net" text="Droits du net">
<outline text="La Quadrature du Net"
title="La Quadrature du Net" type="rss"
xmlUrl="http://www.laquadrature.net/fr/rss.xml" htmlUrl="http://www.laquadrature.net"/>
</outline>
<outline title="Bloggers" text="Bloggers">
<outline text="Bluetouff" title="Bluetouff" type="rss"
xmlUrl="http://bluetouff.com/feed/" htmlUrl="http://bluetouff.com"/>
<outline text="Fredzone" title="Fredzone" type="rss"
xmlUrl="http://www.fredzone.org/feed/" htmlUrl="http://www.fredzone.org"/>
<outline text="HTeuMeuLeu" title="HTeuMeuLeu" type="rss"
xmlUrl="http://viewtext.org/article?url=www.hteumeuleu.fr%2Ffeed%2F&amp;rl=false" htmlUrl="http://www.hteumeuleu.fr"/>
<outline text="Korben" title="Korben" type="rss"
xmlUrl="https://korben.info/feed/atom" htmlUrl="https://korben.info/"/>
<outline text="Le Hollandais Volant"
title="Le Hollandais Volant" type="rss"
xmlUrl="http://lehollandaisvolant.net/rss.php?full" htmlUrl="http://lehollandaisvolant.net/"/>
<outline text="SebSauvage" title="SebSauvage" type="rss"
xmlUrl="http://sebsauvage.net/rhaa/rss_fulltext.php" htmlUrl="http://sebsauvage.net/rhaa/"/>
</outline>
<outline title="Tech" text="Tech">
<outline text="gHacks Technology News"
title="gHacks Technology News" type="rss"
xmlUrl="http://www.ghacks.net/feed/atom/" htmlUrl="http://www.ghacks.net/"/>
</outline>
<outline title="Officiel" text="Officiel">
<outline text="Gandi" title="Gandi" type="rss"
xmlUrl="https://www.gandi.net/feed/news/en/block/hp/" htmlUrl="http://www.gandi.net:80/news/block/en/hp"/>
<outline text="GitHub - Engineering"
title="GitHub - Engineering" type="rss"
xmlUrl="https://github.com/blog/engineering.atom" htmlUrl="https://github.com/blog"/>
<outline text="GitHub - New Features"
title="GitHub - New Features" type="rss"
xmlUrl="https://github.com/blog/ship.atom" htmlUrl="https://github.com/blog"/>
<outline text="jQuery" title="jQuery" type="rss"
xmlUrl="http://blog.jquery.com/feed/" htmlUrl="http://blog.jquery.com"/>
</outline>
<outline title="Code" text="Code">
<outline text="Alsacreations" title="Alsacreations"
type="rss"
xmlUrl="http://www.alsacreations.com/rss/actualites.xml" htmlUrl="http://www.alsacreations.com/"/>
<outline text="CodingSpot" title="CodingSpot" type="rss"
xmlUrl="http://codingspot.com/feed/" htmlUrl="http://codingspot.com"/>
<outline text="Fabien Sanglard" title="Fabien Sanglard"
type="rss" xmlUrl="http://fabiensanglard.net/rss.xml" htmlUrl="http://fabiensanglard.net"/>
<outline text="Ray Wenderlich" title="Ray Wenderlich"
type="rss"
xmlUrl="http://viewtext.org/article?url=http%3A%2F%2Ffeeds.feedburner.com%2FRayWenderlich&amp;rl=false" htmlUrl="http://www.raywenderlich.com"/>
<outline text="Vimeo / CocoaheadsRNS"
title="Vimeo / CocoaheadsRNS" type="rss"
xmlUrl="http://vimeo.com/cocoaheadsrns/videos/rss" htmlUrl="http://vimeo.com/cocoaheadsrns/videos"/>
</outline>
</body>
</opml>

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More