mirror of
https://github.com/getgrav/grav-plugin-admin.git
synced 2025-11-01 02:46:04 +01:00
two factor authentication essental elements
This commit is contained in:
@@ -5,6 +5,7 @@ theme: grav
|
||||
logo_text: ''
|
||||
body_classes: ''
|
||||
content_padding: true
|
||||
twofa_enabled: true
|
||||
sidebar:
|
||||
activate: tab
|
||||
hover_delay: 100
|
||||
|
||||
@@ -48,6 +48,18 @@ form:
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
twofa_enabled:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.2FA_TITLE
|
||||
help: PLUGIN_ADMIN.2FA_ENABLED_HELP
|
||||
default: 0
|
||||
highlight: 0
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
route:
|
||||
type: text
|
||||
label: Administrator path
|
||||
|
||||
@@ -27,6 +27,7 @@ use RocketTheme\Toolbox\Session\Session;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use Composer\Semver\Semver;
|
||||
use PicoFeed\Reader\Reader;
|
||||
use RobThree\Auth\TwoFactorAuth;
|
||||
|
||||
define('LOGIN_REDIRECT_COOKIE', 'grav-login-redirect');
|
||||
|
||||
@@ -377,6 +378,17 @@ class Admin
|
||||
$action = [];
|
||||
|
||||
if ($user->authorize('admin.login')) {
|
||||
|
||||
$twofa_admin_enabled = $this->grav['config']->get('plugins.admin.twofa_enabled', false);
|
||||
|
||||
if ($twofa_admin_enabled && isset($user->twofa_enabled) && $user->twofa_enabled == true) {
|
||||
$twofa = $this->get2FA();
|
||||
$secret = isset($user->twofa_secret) ? $user->twofa_secret : null;
|
||||
if (!(isset($data['2fa_code']) && $data['2fa_code'] == $twofa->getCode($secret))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$this->user = $this->session->user = $user;
|
||||
|
||||
/** @var Grav $grav */
|
||||
@@ -1709,4 +1721,9 @@ class Admin
|
||||
|
||||
return $pagesWithFiles;
|
||||
}
|
||||
|
||||
public function get2FA()
|
||||
{
|
||||
return new TwoFactorAuth($this->grav['config']->get('site.title'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"require": {
|
||||
"composer/semver": "^1.4",
|
||||
"fguillot/picofeed": "@stable"
|
||||
"fguillot/picofeed": "@stable",
|
||||
"robthree/twofactorauth": "^1.6"
|
||||
},
|
||||
"require-dev": {
|
||||
"codeception/codeception": "^2.1",
|
||||
|
||||
933
composer.lock
generated
933
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -682,3 +682,9 @@ PLUGIN_ADMIN:
|
||||
CONTENT_PADDING_HELP: "Enable/Disable content padding around content area to provide more space"
|
||||
ENABLE_AUTO_METADATA: "Auto metadata from Exif"
|
||||
ENABLE_AUTO_METADATA_HELP: "Automatically generate metadata files for images with exif information"
|
||||
2FA_TITLE: "2-Factor Authentication"
|
||||
2FA_LABEL: "Admin Access"
|
||||
2FA_ENABLED: "2FA Enabled"
|
||||
2FA_CODE_INPUT: "2FA Code (if enabled)"
|
||||
2FA_SECRET: "2FA Secret"
|
||||
2FA_SECRET_HELP: "Scan this code into your Authenticator App or enter the code manually. Check the Grav docs for more information"
|
||||
|
||||
@@ -7,16 +7,26 @@ form:
|
||||
method: post
|
||||
|
||||
fields:
|
||||
- name: username
|
||||
type: text
|
||||
placeholder: PLUGIN_ADMIN.USERNAME_EMAIL
|
||||
autofocus: true
|
||||
validate:
|
||||
required: true
|
||||
username:
|
||||
type: text
|
||||
placeholder: PLUGIN_ADMIN.USERNAME_EMAIL
|
||||
autofocus: true
|
||||
validate:
|
||||
required: true
|
||||
|
||||
- name: password
|
||||
type: password
|
||||
placeholder: PLUGIN_ADMIN.PASSWORD
|
||||
validate:
|
||||
required: true
|
||||
password:
|
||||
type: password
|
||||
placeholder: PLUGIN_ADMIN.PASSWORD
|
||||
validate:
|
||||
required: true
|
||||
|
||||
twofa_check:
|
||||
type: conditional
|
||||
condition: config.plugins.admin.twofa_enabled
|
||||
|
||||
fields:
|
||||
|
||||
2fa_code:
|
||||
type: text
|
||||
placeholder: PLUGIN_ADMIN.2FA_CODE_INPUT
|
||||
---
|
||||
|
||||
2
themes/grav/css-compiled/preset.css
vendored
2
themes/grav/css-compiled/preset.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
4
themes/grav/css-compiled/template.css
vendored
4
themes/grav/css-compiled/template.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -967,8 +967,8 @@ form {
|
||||
}
|
||||
|
||||
&.switch-toggle input:checked + label {
|
||||
background: #777;
|
||||
color: #fff;
|
||||
color: $content-bg;
|
||||
background: $content-text;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -635,8 +635,10 @@ textarea.frontmatter {
|
||||
height: 39px;
|
||||
}
|
||||
|
||||
.switch-toggle label {
|
||||
white-space: nowrap;
|
||||
.switch-toggle {
|
||||
line-height: 37px;
|
||||
margin: 0 5px 5px 0;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -131,3 +131,17 @@
|
||||
padding: 1rem 3rem;
|
||||
}
|
||||
}
|
||||
|
||||
.twofa-secret {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 600ms, visibility 600ms;
|
||||
|
||||
&.show {
|
||||
position: static;
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
padding: 0 15px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
{% extends "forms/field.html.twig" %}
|
||||
|
||||
{% block input %}
|
||||
<div class="form-input-wrapper 2fa-wrapper">
|
||||
{% set two_fa = admin.get2FA() %}
|
||||
{% set value = (value is null ? two_fa.createSecret(160) : value) %}
|
||||
|
||||
<img style="border: 1px solid #ddd" src="{{ two_fa.getQRCodeImageAsDataUri(grav.user.email, value) }}" />
|
||||
<div class="2fa-secret">{{ value|chunkSplit(4, ' ') }}</div>
|
||||
|
||||
<input type="text" style="display:none;" name="{{ (scope ~ field.name)|fieldName }}" value="{{ value|join(', ') }}" />
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function twofaVisiblity() {
|
||||
console.log('clicked');
|
||||
if ($('#toggle_twofa_enabled1').is(':checked')) {
|
||||
console.log('checked');
|
||||
$('.twofa-secret').addClass("show");
|
||||
} else {
|
||||
console.log('unchecked');
|
||||
$('.twofa-secret').removeClass("show");
|
||||
}
|
||||
}
|
||||
|
||||
$( document ).ready(function() {
|
||||
twofaVisiblity();
|
||||
$('.twofa-toggle input').click(twofaVisiblity);
|
||||
});
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
{% block input %}
|
||||
|
||||
<div class="switch-toggle switch-grav {{ field.size }} switch-{{ field.options|length }}">
|
||||
<div class="switch-toggle switch-grav {{ field.size }} switch-{{ field.options|length }} {{ field.classes }}">
|
||||
{% set maxLen = 0 %}
|
||||
{% for text in field.options %}
|
||||
{% set translation = grav.twig.twig.filters['tu'] is defined ? text|tu : text|t %}
|
||||
|
||||
@@ -41,7 +41,7 @@ class AdminTwigExtension extends \Twig_Extension
|
||||
new \Twig_SimpleFilter('toYaml', [$this, 'toYamlFilter']),
|
||||
new \Twig_SimpleFilter('fromYaml', [$this, 'fromYamlFilter']),
|
||||
new \Twig_SimpleFilter('adminNicetime', [$this, 'adminNicetimeFilter']),
|
||||
|
||||
new \Twig_SimpleFilter('chunkSplit', [$this, 'chunkSplitFilter']),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -181,4 +181,9 @@ class AdminTwigExtension extends \Twig_Extension
|
||||
|
||||
return "$difference $periods[$j] {$tense}";
|
||||
}
|
||||
|
||||
public function chunkSplitFilter($value, $chars, $split = '-')
|
||||
{
|
||||
return chunk_split($value, $chars, $split);
|
||||
}
|
||||
}
|
||||
|
||||
4
vendor/autoload.php
vendored
4
vendor/autoload.php
vendored
@@ -2,6 +2,6 @@
|
||||
|
||||
// autoload.php @generated by Composer
|
||||
|
||||
require_once __DIR__ . '/composer' . '/autoload_real.php';
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInitef6e9937a63bd796f32542b02941ee0e::getLoader();
|
||||
return ComposerAutoloaderInitf3438a4bfc092aad40a104edf0a3eb02::getLoader();
|
||||
|
||||
58
vendor/composer/ClassLoader.php
vendored
58
vendor/composer/ClassLoader.php
vendored
@@ -53,8 +53,9 @@ class ClassLoader
|
||||
|
||||
private $useIncludePath = false;
|
||||
private $classMap = array();
|
||||
|
||||
private $classMapAuthoritative = false;
|
||||
private $missingClasses = array();
|
||||
private $apcuPrefix;
|
||||
|
||||
public function getPrefixes()
|
||||
{
|
||||
@@ -271,6 +272,26 @@ class ClassLoader
|
||||
return $this->classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
||||
*
|
||||
* @param string|null $apcuPrefix
|
||||
*/
|
||||
public function setApcuPrefix($apcuPrefix)
|
||||
{
|
||||
$this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The APCu prefix in use, or null if APCu caching is not enabled.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getApcuPrefix()
|
||||
{
|
||||
return $this->apcuPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers this instance as an autoloader.
|
||||
*
|
||||
@@ -313,29 +334,34 @@ class ClassLoader
|
||||
*/
|
||||
public function findFile($class)
|
||||
{
|
||||
// work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
|
||||
if ('\\' == $class[0]) {
|
||||
$class = substr($class, 1);
|
||||
}
|
||||
|
||||
// class map lookup
|
||||
if (isset($this->classMap[$class])) {
|
||||
return $this->classMap[$class];
|
||||
}
|
||||
if ($this->classMapAuthoritative) {
|
||||
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
||||
return false;
|
||||
}
|
||||
if (null !== $this->apcuPrefix) {
|
||||
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
||||
if ($hit) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
$file = $this->findFileWithExtension($class, '.php');
|
||||
|
||||
// Search for Hack files if we are running on HHVM
|
||||
if ($file === null && defined('HHVM_VERSION')) {
|
||||
if (false === $file && defined('HHVM_VERSION')) {
|
||||
$file = $this->findFileWithExtension($class, '.hh');
|
||||
}
|
||||
|
||||
if ($file === null) {
|
||||
if (null !== $this->apcuPrefix) {
|
||||
apcu_add($this->apcuPrefix.$class, $file);
|
||||
}
|
||||
|
||||
if (false === $file) {
|
||||
// Remember that this class does not exist.
|
||||
return $this->classMap[$class] = false;
|
||||
$this->missingClasses[$class] = true;
|
||||
}
|
||||
|
||||
return $file;
|
||||
@@ -348,9 +374,13 @@ class ClassLoader
|
||||
|
||||
$first = $class[0];
|
||||
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||
foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
|
||||
$subPath = $class;
|
||||
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
||||
$subPath = substr($subPath, 0, $lastPos);
|
||||
$search = $subPath.'\\';
|
||||
if (isset($this->prefixDirsPsr4[$search])) {
|
||||
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
||||
$length = $this->prefixLengthsPsr4[$first][$search];
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
|
||||
return $file;
|
||||
}
|
||||
@@ -399,6 +429,8 @@ class ClassLoader
|
||||
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
2
vendor/composer/LICENSE
vendored
2
vendor/composer/LICENSE
vendored
@@ -1,5 +1,5 @@
|
||||
|
||||
Copyright (c) 2016 Nils Adermann, Jordi Boggiano
|
||||
Copyright (c) Nils Adermann, Jordi Boggiano
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
1
vendor/composer/autoload_psr4.php
vendored
1
vendor/composer/autoload_psr4.php
vendored
@@ -6,5 +6,6 @@ $vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'RobThree\\Auth\\' => array($vendorDir . '/robthree/twofactorauth/lib'),
|
||||
'Composer\\Semver\\' => array($vendorDir . '/composer/semver/src'),
|
||||
);
|
||||
|
||||
10
vendor/composer/autoload_real.php
vendored
10
vendor/composer/autoload_real.php
vendored
@@ -2,7 +2,7 @@
|
||||
|
||||
// autoload_real.php @generated by Composer
|
||||
|
||||
class ComposerAutoloaderInitef6e9937a63bd796f32542b02941ee0e
|
||||
class ComposerAutoloaderInitf3438a4bfc092aad40a104edf0a3eb02
|
||||
{
|
||||
private static $loader;
|
||||
|
||||
@@ -19,15 +19,15 @@ class ComposerAutoloaderInitef6e9937a63bd796f32542b02941ee0e
|
||||
return self::$loader;
|
||||
}
|
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInitef6e9937a63bd796f32542b02941ee0e', 'loadClassLoader'), true, true);
|
||||
spl_autoload_register(array('ComposerAutoloaderInitf3438a4bfc092aad40a104edf0a3eb02', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInitef6e9937a63bd796f32542b02941ee0e', 'loadClassLoader'));
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInitf3438a4bfc092aad40a104edf0a3eb02', 'loadClassLoader'));
|
||||
|
||||
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION');
|
||||
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
||||
if ($useStaticLoader) {
|
||||
require_once __DIR__ . '/autoload_static.php';
|
||||
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInitef6e9937a63bd796f32542b02941ee0e::getInitializer($loader));
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInitf3438a4bfc092aad40a104edf0a3eb02::getInitializer($loader));
|
||||
} else {
|
||||
$map = require __DIR__ . '/autoload_namespaces.php';
|
||||
foreach ($map as $namespace => $path) {
|
||||
|
||||
16
vendor/composer/autoload_static.php
vendored
16
vendor/composer/autoload_static.php
vendored
@@ -4,9 +4,13 @@
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
class ComposerStaticInitef6e9937a63bd796f32542b02941ee0e
|
||||
class ComposerStaticInitf3438a4bfc092aad40a104edf0a3eb02
|
||||
{
|
||||
public static $prefixLengthsPsr4 = array (
|
||||
'R' =>
|
||||
array (
|
||||
'RobThree\\Auth\\' => 14,
|
||||
),
|
||||
'C' =>
|
||||
array (
|
||||
'Composer\\Semver\\' => 16,
|
||||
@@ -14,6 +18,10 @@ class ComposerStaticInitef6e9937a63bd796f32542b02941ee0e
|
||||
);
|
||||
|
||||
public static $prefixDirsPsr4 = array (
|
||||
'RobThree\\Auth\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/robthree/twofactorauth/lib',
|
||||
),
|
||||
'Composer\\Semver\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/composer/semver/src',
|
||||
@@ -40,9 +48,9 @@ class ComposerStaticInitef6e9937a63bd796f32542b02941ee0e
|
||||
public static function getInitializer(ClassLoader $loader)
|
||||
{
|
||||
return \Closure::bind(function () use ($loader) {
|
||||
$loader->prefixLengthsPsr4 = ComposerStaticInitef6e9937a63bd796f32542b02941ee0e::$prefixLengthsPsr4;
|
||||
$loader->prefixDirsPsr4 = ComposerStaticInitef6e9937a63bd796f32542b02941ee0e::$prefixDirsPsr4;
|
||||
$loader->prefixesPsr0 = ComposerStaticInitef6e9937a63bd796f32542b02941ee0e::$prefixesPsr0;
|
||||
$loader->prefixLengthsPsr4 = ComposerStaticInitf3438a4bfc092aad40a104edf0a3eb02::$prefixLengthsPsr4;
|
||||
$loader->prefixDirsPsr4 = ComposerStaticInitf3438a4bfc092aad40a104edf0a3eb02::$prefixDirsPsr4;
|
||||
$loader->prefixesPsr0 = ComposerStaticInitf3438a4bfc092aad40a104edf0a3eb02::$prefixesPsr0;
|
||||
|
||||
}, null, ClassLoader::class);
|
||||
}
|
||||
|
||||
172
vendor/composer/installed.json
vendored
172
vendor/composer/installed.json
vendored
@@ -1,51 +1,4 @@
|
||||
[
|
||||
{
|
||||
"name": "zendframework/zendxml",
|
||||
"version": "1.0.2",
|
||||
"version_normalized": "1.0.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/zendframework/ZendXml.git",
|
||||
"reference": "7b64507bc35d841c9c5802d67f6f87ef8e1a58c9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/zendframework/ZendXml/zipball/7b64507bc35d841c9c5802d67f6f87ef8e1a58c9",
|
||||
"reference": "7b64507bc35d841c9c5802d67f6f87ef8e1a58c9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^5.3.3 || ^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^3.7 || ^4.0",
|
||||
"squizlabs/php_codesniffer": "^1.5"
|
||||
},
|
||||
"time": "2016-02-04 21:02:08",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0-dev"
|
||||
}
|
||||
},
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"ZendXml\\": "library/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
"description": "Utility library for XML usage, best practices, and security in PHP",
|
||||
"homepage": "http://packages.zendframework.com/",
|
||||
"keywords": [
|
||||
"security",
|
||||
"xml",
|
||||
"zf2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "composer/semver",
|
||||
"version": "1.4.2",
|
||||
@@ -68,7 +21,7 @@
|
||||
"phpunit/phpunit": "^4.5 || ^5.0.5",
|
||||
"phpunit/phpunit-mock-objects": "2.3.0 || ^3.0"
|
||||
},
|
||||
"time": "2016-08-30 16:08:34",
|
||||
"time": "2016-08-30T16:08:34+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
@@ -111,18 +64,65 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "fguillot/picofeed",
|
||||
"version": "v0.1.25",
|
||||
"version_normalized": "0.1.25.0",
|
||||
"name": "zendframework/zendxml",
|
||||
"version": "1.0.2",
|
||||
"version_normalized": "1.0.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/fguillot/picoFeed.git",
|
||||
"reference": "2bf5bc40361e788eda6b1bd5d444630986721e69"
|
||||
"url": "https://github.com/zendframework/ZendXml.git",
|
||||
"reference": "7b64507bc35d841c9c5802d67f6f87ef8e1a58c9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/fguillot/picoFeed/zipball/2bf5bc40361e788eda6b1bd5d444630986721e69",
|
||||
"reference": "2bf5bc40361e788eda6b1bd5d444630986721e69",
|
||||
"url": "https://api.github.com/repos/zendframework/ZendXml/zipball/7b64507bc35d841c9c5802d67f6f87ef8e1a58c9",
|
||||
"reference": "7b64507bc35d841c9c5802d67f6f87ef8e1a58c9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^5.3.3 || ^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^3.7 || ^4.0",
|
||||
"squizlabs/php_codesniffer": "^1.5"
|
||||
},
|
||||
"time": "2016-02-04T21:02:08+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0-dev"
|
||||
}
|
||||
},
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"ZendXml\\": "library/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
"description": "Utility library for XML usage, best practices, and security in PHP",
|
||||
"homepage": "http://packages.zendframework.com/",
|
||||
"keywords": [
|
||||
"security",
|
||||
"xml",
|
||||
"zf2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "fguillot/picofeed",
|
||||
"version": "v0.1.35",
|
||||
"version_normalized": "0.1.35.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/miniflux/picoFeed.git",
|
||||
"reference": "3a27b47de31eedec075c719f961783c5db7a7b08"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/miniflux/picoFeed/zipball/3a27b47de31eedec075c719f961783c5db7a7b08",
|
||||
"reference": "3a27b47de31eedec075c719f961783c5db7a7b08",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -134,10 +134,15 @@
|
||||
"php": ">=5.3.0",
|
||||
"zendframework/zendxml": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpdocumentor/reflection-docblock": "2.0.4",
|
||||
"phpunit/phpunit": "4.8.26",
|
||||
"symfony/yaml": "2.8.7"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-curl": "PicoFeed will use cURL if present"
|
||||
},
|
||||
"time": "2016-08-30 01:33:18",
|
||||
"time": "2017-06-20T22:54:47+00:00",
|
||||
"bin": [
|
||||
"picofeed"
|
||||
],
|
||||
@@ -158,6 +163,59 @@
|
||||
}
|
||||
],
|
||||
"description": "Modern library to handle RSS/Atom feeds",
|
||||
"homepage": "https://github.com/fguillot/picoFeed"
|
||||
"homepage": "https://github.com/miniflux/picoFeed"
|
||||
},
|
||||
{
|
||||
"name": "robthree/twofactorauth",
|
||||
"version": "1.6",
|
||||
"version_normalized": "1.6.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/RobThree/TwoFactorAuth.git",
|
||||
"reference": "5093ab230cd8f1296d792afb6a49545f37e7fd5a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/RobThree/TwoFactorAuth/zipball/5093ab230cd8f1296d792afb6a49545f37e7fd5a",
|
||||
"reference": "5093ab230cd8f1296d792afb6a49545f37e7fd5a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "@stable"
|
||||
},
|
||||
"time": "2017-02-17T15:24:54+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"RobThree\\Auth\\": "lib"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Rob Janssen",
|
||||
"homepage": "http://robiii.me",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Two Factor Authentication",
|
||||
"homepage": "https://github.com/RobThree/TwoFactorAuth",
|
||||
"keywords": [
|
||||
"Authentication",
|
||||
"MFA",
|
||||
"Multi Factor Authentication",
|
||||
"Two Factor Authentication",
|
||||
"authenticator",
|
||||
"authy",
|
||||
"php",
|
||||
"tfa"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -31,4 +31,8 @@ abstract class Base
|
||||
$this->config = $config ?: new Config();
|
||||
Logger::setTimezone($this->config->getTimezone());
|
||||
}
|
||||
|
||||
public function setConfig(Config $config) {
|
||||
$this->config = $config;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace PicoFeed\Client;
|
||||
|
||||
use DateTime;
|
||||
use Exception;
|
||||
use LogicException;
|
||||
use PicoFeed\Logging\Logger;
|
||||
use PicoFeed\Config\Config;
|
||||
@@ -55,6 +57,13 @@ abstract class Client
|
||||
*/
|
||||
protected $last_modified = '';
|
||||
|
||||
/**
|
||||
* Expiration DateTime
|
||||
*
|
||||
* @var DateTime
|
||||
*/
|
||||
protected $expiration = null;
|
||||
|
||||
/**
|
||||
* Proxy hostname.
|
||||
*
|
||||
@@ -97,6 +106,13 @@ abstract class Client
|
||||
*/
|
||||
protected $password = '';
|
||||
|
||||
/**
|
||||
* CURL options.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $additional_curl_options = array();
|
||||
|
||||
/**
|
||||
* Client connection timeout.
|
||||
*
|
||||
@@ -109,7 +125,7 @@ abstract class Client
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $user_agent = 'PicoFeed (https://github.com/fguillot/picoFeed)';
|
||||
protected $user_agent = 'PicoFeed (https://github.com/miniflux/picoFeed)';
|
||||
|
||||
/**
|
||||
* Real URL used (can be changed after a HTTP redirect).
|
||||
@@ -214,6 +230,9 @@ abstract class Client
|
||||
$this->handleErrorResponse($response);
|
||||
$this->handleNormalResponse($response);
|
||||
|
||||
$this->expiration = $this->parseExpiration($response['headers']);
|
||||
Logger::setMessage(get_called_class().' Expiration: '.$this->expiration->format(DATE_ISO8601));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -241,6 +260,9 @@ abstract class Client
|
||||
* Handle Http Error codes
|
||||
*
|
||||
* @param array $response Client response
|
||||
* @throws ForbiddenException
|
||||
* @throws InvalidUrlException
|
||||
* @throws UnauthorizedException
|
||||
*/
|
||||
protected function handleErrorResponse(array $response)
|
||||
{
|
||||
@@ -308,7 +330,6 @@ abstract class Client
|
||||
* Find content type from response headers.
|
||||
*
|
||||
* @param array $response Client response
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function findContentType(array $response)
|
||||
@@ -324,7 +345,6 @@ abstract class Client
|
||||
public function findCharset()
|
||||
{
|
||||
$result = explode('charset=', $this->content_type);
|
||||
|
||||
return isset($result[1]) ? $result[1] : '';
|
||||
}
|
||||
|
||||
@@ -333,7 +353,6 @@ abstract class Client
|
||||
*
|
||||
* @param array $response Client response
|
||||
* @param string $header Header name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getHeader(array $response, $header)
|
||||
@@ -345,13 +364,11 @@ abstract class Client
|
||||
* Set the Last-Modified HTTP header.
|
||||
*
|
||||
* @param string $last_modified Header value
|
||||
*
|
||||
* @return \PicoFeed\Client\Client
|
||||
* @return $this
|
||||
*/
|
||||
public function setLastModified($last_modified)
|
||||
{
|
||||
$this->last_modified = $last_modified;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -369,13 +386,11 @@ abstract class Client
|
||||
* Set the value of the Etag HTTP header.
|
||||
*
|
||||
* @param string $etag Etag HTTP header value
|
||||
*
|
||||
* @return \PicoFeed\Client\Client
|
||||
* @return $this
|
||||
*/
|
||||
public function setEtag($etag)
|
||||
{
|
||||
$this->etag = $etag;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -402,13 +417,12 @@ abstract class Client
|
||||
/**
|
||||
* Set the url.
|
||||
*
|
||||
* @param $url
|
||||
* @return string
|
||||
* @return \PicoFeed\Client\Client
|
||||
*/
|
||||
public function setUrl($url)
|
||||
{
|
||||
$this->url = $url;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -476,13 +490,11 @@ abstract class Client
|
||||
* Set connection timeout.
|
||||
*
|
||||
* @param int $timeout Connection timeout
|
||||
*
|
||||
* @return \PicoFeed\Client\Client
|
||||
* @return $this
|
||||
*/
|
||||
public function setTimeout($timeout)
|
||||
{
|
||||
$this->timeout = $timeout ?: $this->timeout;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -490,13 +502,11 @@ abstract class Client
|
||||
* Set a custom user agent.
|
||||
*
|
||||
* @param string $user_agent User Agent
|
||||
*
|
||||
* @return \PicoFeed\Client\Client
|
||||
* @return $this
|
||||
*/
|
||||
public function setUserAgent($user_agent)
|
||||
{
|
||||
$this->user_agent = $user_agent ?: $this->user_agent;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -504,13 +514,11 @@ abstract class Client
|
||||
* Set the maximum number of HTTP redirections.
|
||||
*
|
||||
* @param int $max Maximum
|
||||
*
|
||||
* @return \PicoFeed\Client\Client
|
||||
* @return $this
|
||||
*/
|
||||
public function setMaxRedirections($max)
|
||||
{
|
||||
$this->max_redirects = $max ?: $this->max_redirects;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -518,13 +526,11 @@ abstract class Client
|
||||
* Set the maximum size of the HTTP body.
|
||||
*
|
||||
* @param int $max Maximum
|
||||
*
|
||||
* @return \PicoFeed\Client\Client
|
||||
* @return $this
|
||||
*/
|
||||
public function setMaxBodySize($max)
|
||||
{
|
||||
$this->max_body_size = $max ?: $this->max_body_size;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -532,13 +538,11 @@ abstract class Client
|
||||
* Set the proxy hostname.
|
||||
*
|
||||
* @param string $hostname Proxy hostname
|
||||
*
|
||||
* @return \PicoFeed\Client\Client
|
||||
* @return $this
|
||||
*/
|
||||
public function setProxyHostname($hostname)
|
||||
{
|
||||
$this->proxy_hostname = $hostname ?: $this->proxy_hostname;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -546,13 +550,11 @@ abstract class Client
|
||||
* Set the proxy port.
|
||||
*
|
||||
* @param int $port Proxy port
|
||||
*
|
||||
* @return \PicoFeed\Client\Client
|
||||
* @return $this
|
||||
*/
|
||||
public function setProxyPort($port)
|
||||
{
|
||||
$this->proxy_port = $port ?: $this->proxy_port;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -560,13 +562,11 @@ abstract class Client
|
||||
* Set the proxy username.
|
||||
*
|
||||
* @param string $username Proxy username
|
||||
*
|
||||
* @return \PicoFeed\Client\Client
|
||||
* @return $this
|
||||
*/
|
||||
public function setProxyUsername($username)
|
||||
{
|
||||
$this->proxy_username = $username ?: $this->proxy_username;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -574,13 +574,11 @@ abstract class Client
|
||||
* Set the proxy password.
|
||||
*
|
||||
* @param string $password Password
|
||||
*
|
||||
* @return \PicoFeed\Client\Client
|
||||
* @return $this
|
||||
*/
|
||||
public function setProxyPassword($password)
|
||||
{
|
||||
$this->proxy_password = $password ?: $this->proxy_password;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -589,12 +587,11 @@ abstract class Client
|
||||
*
|
||||
* @param string $username Basic Auth username
|
||||
*
|
||||
* @return \PicoFeed\Client\Client
|
||||
* @return $this
|
||||
*/
|
||||
public function setUsername($username)
|
||||
{
|
||||
$this->username = $username ?: $this->username;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -603,36 +600,46 @@ abstract class Client
|
||||
*
|
||||
* @param string $password Basic Auth Password
|
||||
*
|
||||
* @return \PicoFeed\Client\Client
|
||||
* @return $this
|
||||
*/
|
||||
public function setPassword($password)
|
||||
{
|
||||
$this->password = $password ?: $this->password;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the CURL options.
|
||||
*
|
||||
* @param array $options
|
||||
* @return $this
|
||||
*/
|
||||
public function setAdditionalCurlOptions(array $options)
|
||||
{
|
||||
$this->additional_curl_options = $options ?: $this->additional_curl_options;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enable the passthrough mode.
|
||||
*
|
||||
* @return \PicoFeed\Client\Client
|
||||
* @return $this
|
||||
*/
|
||||
public function enablePassthroughMode()
|
||||
{
|
||||
$this->passthrough = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the passthrough mode.
|
||||
*
|
||||
* @return \PicoFeed\Client\Client
|
||||
* @return $this
|
||||
*/
|
||||
public function disablePassthroughMode()
|
||||
{
|
||||
$this->passthrough = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -640,8 +647,7 @@ abstract class Client
|
||||
* Set config object.
|
||||
*
|
||||
* @param \PicoFeed\Config\Config $config Config instance
|
||||
*
|
||||
* @return \PicoFeed\Client\Client
|
||||
* @return $this
|
||||
*/
|
||||
public function setConfig(Config $config)
|
||||
{
|
||||
@@ -654,6 +660,7 @@ abstract class Client
|
||||
$this->setProxyPort($config->getProxyPort());
|
||||
$this->setProxyUsername($config->getProxyUsername());
|
||||
$this->setProxyPassword($config->getProxyPassword());
|
||||
$this->setAdditionalCurlOptions($config->getAdditionalCurlOptions() ?: array());
|
||||
}
|
||||
|
||||
return $this;
|
||||
@@ -670,4 +677,36 @@ abstract class Client
|
||||
{
|
||||
return $code == 301 || $code == 302 || $code == 303 || $code == 307;
|
||||
}
|
||||
|
||||
public function parseExpiration(HttpHeaders $headers)
|
||||
{
|
||||
try {
|
||||
|
||||
if (isset($headers['Cache-Control'])) {
|
||||
if (preg_match('/s-maxage=(\d+)/', $headers['Cache-Control'], $matches)) {
|
||||
return new DateTime('+' . $matches[1] . ' seconds');
|
||||
} else if (preg_match('/max-age=(\d+)/', $headers['Cache-Control'], $matches)) {
|
||||
return new DateTime('+' . $matches[1] . ' seconds');
|
||||
}
|
||||
}
|
||||
|
||||
if (! empty($headers['Expires'])) {
|
||||
return new DateTime($headers['Expires']);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
Logger::setMessage('Unable to parse expiration date: '.$e->getMessage());
|
||||
}
|
||||
|
||||
return new DateTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get expiration date time from "Expires" or "Cache-Control" headers
|
||||
*
|
||||
* @return DateTime
|
||||
*/
|
||||
public function getExpiration()
|
||||
{
|
||||
return $this->expiration ?: new DateTime();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ use PicoFeed\Logging\Logger;
|
||||
*/
|
||||
class Curl extends Client
|
||||
{
|
||||
protected $nbRedirects = 0;
|
||||
|
||||
/**
|
||||
* HTTP response body.
|
||||
*
|
||||
@@ -108,8 +110,6 @@ class Curl extends Client
|
||||
return $this->handleRedirection($headers['Location']);
|
||||
}
|
||||
|
||||
header(':', true, $status);
|
||||
|
||||
if (isset($headers['Content-Type'])) {
|
||||
header('Content-Type:' .$headers['Content-Type']);
|
||||
}
|
||||
@@ -136,6 +136,7 @@ class Curl extends Client
|
||||
|
||||
if ($this->etag) {
|
||||
$headers[] = 'If-None-Match: '.$this->etag;
|
||||
$headers[] = 'A-IM: feed';
|
||||
}
|
||||
|
||||
if ($this->last_modified) {
|
||||
@@ -199,6 +200,9 @@ class Curl extends Client
|
||||
*/
|
||||
private function prepareDownloadMode($ch)
|
||||
{
|
||||
$this->body = '';
|
||||
$this->response_headers = array();
|
||||
$this->response_headers_count = 0;
|
||||
$write_function = 'readBody';
|
||||
$header_function = 'readHeaders';
|
||||
|
||||
@@ -212,6 +216,20 @@ class Curl extends Client
|
||||
return $ch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set additional CURL options.
|
||||
*
|
||||
* @param resource $ch
|
||||
*
|
||||
* @return resource $ch
|
||||
*/
|
||||
private function prepareAdditionalCurlOptions($ch){
|
||||
foreach( $this->additional_curl_options as $c_op => $c_val ){
|
||||
curl_setopt($ch, $c_op, $c_val);
|
||||
}
|
||||
return $ch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare curl context.
|
||||
*
|
||||
@@ -245,6 +263,7 @@ class Curl extends Client
|
||||
$ch = $this->prepareDownloadMode($ch);
|
||||
$ch = $this->prepareProxyContext($ch);
|
||||
$ch = $this->prepareAuthContext($ch);
|
||||
$ch = $this->prepareAdditionalCurlOptions($ch);
|
||||
|
||||
return $ch;
|
||||
}
|
||||
@@ -290,7 +309,11 @@ class Curl extends Client
|
||||
list($status, $headers) = HttpHeaders::parse(explode("\n", $this->response_headers[$this->response_headers_count - 1]));
|
||||
|
||||
if ($this->isRedirection($status)) {
|
||||
return $this->handleRedirection($headers['Location']);
|
||||
if (empty($headers['Location'])) {
|
||||
$status = 200;
|
||||
} else {
|
||||
return $this->handleRedirection($headers['Location']);
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
@@ -304,12 +327,11 @@ class Curl extends Client
|
||||
* Handle HTTP redirects
|
||||
*
|
||||
* @param string $location Redirected URL
|
||||
*
|
||||
* @return array
|
||||
* @throws MaxRedirectException
|
||||
*/
|
||||
private function handleRedirection($location)
|
||||
{
|
||||
$nb_redirects = 0;
|
||||
$result = array();
|
||||
$this->url = Url::resolve($location, $this->url);
|
||||
$this->body = '';
|
||||
@@ -318,9 +340,9 @@ class Curl extends Client
|
||||
$this->response_headers_count = 0;
|
||||
|
||||
while (true) {
|
||||
++$nb_redirects;
|
||||
$this->nbRedirects++;
|
||||
|
||||
if ($nb_redirects >= $this->max_redirects) {
|
||||
if ($this->nbRedirects >= $this->max_redirects) {
|
||||
throw new MaxRedirectException('Maximum number of redirections reached');
|
||||
}
|
||||
|
||||
@@ -349,6 +371,11 @@ class Curl extends Client
|
||||
* @see http://curl.haxx.se/libcurl/c/libcurl-errors.html
|
||||
*
|
||||
* @param int $errno cURL error code
|
||||
* @throws InvalidCertificateException
|
||||
* @throws InvalidUrlException
|
||||
* @throws MaxRedirectException
|
||||
* @throws MaxSizeException
|
||||
* @throws TimeoutException
|
||||
*/
|
||||
private function handleError($errno)
|
||||
{
|
||||
@@ -372,8 +399,7 @@ class Curl extends Client
|
||||
case 66: // CURLE_SSL_ENGINE_INITFAILED
|
||||
case 77: // CURLE_SSL_CACERT_BADFILE
|
||||
case 83: // CURLE_SSL_ISSUER_ERROR
|
||||
$msg = 'Invalid SSL certificate caused by CURL error number ' .
|
||||
$errno;
|
||||
$msg = 'Invalid SSL certificate caused by CURL error number ' . $errno;
|
||||
throw new InvalidCertificateException($msg, $errno);
|
||||
case 47: // CURLE_TOO_MANY_REDIRECTS
|
||||
throw new MaxRedirectException('Maximum number of redirections reached', $errno);
|
||||
|
||||
@@ -24,7 +24,7 @@ class HttpHeaders implements ArrayAccess
|
||||
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return $this->headers[strtolower($offset)];
|
||||
return $this->offsetExists($offset) ? $this->headers[strtolower($offset)] : '';
|
||||
}
|
||||
|
||||
public function offsetSet($offset, $value)
|
||||
|
||||
@@ -31,6 +31,7 @@ class Stream extends Client
|
||||
|
||||
if ($this->etag) {
|
||||
$headers[] = 'If-None-Match: '.$this->etag;
|
||||
$headers[] = 'A-IM: feed';
|
||||
}
|
||||
|
||||
if ($this->last_modified) {
|
||||
@@ -104,6 +105,9 @@ class Stream extends Client
|
||||
* Do the HTTP request.
|
||||
*
|
||||
* @return array HTTP response ['body' => ..., 'status' => ..., 'headers' => ...]
|
||||
* @throws InvalidUrlException
|
||||
* @throws MaxSizeException
|
||||
* @throws TimeoutException
|
||||
*/
|
||||
public function doRequest()
|
||||
{
|
||||
|
||||
@@ -50,10 +50,8 @@ class Url
|
||||
* Shortcut method to get an absolute url from relative url.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @param mixed $item_url Unknown url (can be relative or not)
|
||||
* @param mixed $website_url Website url
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function resolve($item_url, $website_url)
|
||||
@@ -78,9 +76,7 @@ class Url
|
||||
* Shortcut method to get a base url.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @param string $url
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function base($url)
|
||||
@@ -94,7 +90,6 @@ class Url
|
||||
* Get the base URL.
|
||||
*
|
||||
* @param string $suffix Add a suffix to the url
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getBaseUrl($suffix = '')
|
||||
@@ -106,7 +101,6 @@ class Url
|
||||
* Get the absolute URL.
|
||||
*
|
||||
* @param string $base_url Use this url as base url
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAbsoluteUrl($base_url = '')
|
||||
@@ -150,7 +144,8 @@ class Url
|
||||
* Imported from Guzzle library: https://github.com/guzzle/psr7/blob/master/src/Uri.php#L568-L582
|
||||
*
|
||||
* @param $path
|
||||
*
|
||||
* @param string $charUnreserved
|
||||
* @param string $charSubDelims
|
||||
* @return string
|
||||
*/
|
||||
public function filterPath($path, $charUnreserved = 'a-zA-Z0-9_\-\.~', $charSubDelims = '!\$&\'\(\)\*\+,;=')
|
||||
@@ -226,7 +221,6 @@ class Url
|
||||
* Get the scheme.
|
||||
*
|
||||
* @param string $suffix Suffix to add when there is a scheme
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getScheme($suffix = '')
|
||||
@@ -238,12 +232,12 @@ class Url
|
||||
* Set the scheme.
|
||||
*
|
||||
* @param string $scheme Set a scheme
|
||||
*
|
||||
* @return string
|
||||
* @return $this
|
||||
*/
|
||||
public function setScheme($scheme)
|
||||
{
|
||||
$this->components['scheme'] = $scheme;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -260,7 +254,6 @@ class Url
|
||||
* Get the port.
|
||||
*
|
||||
* @param string $prefix Prefix to add when there is a port
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPort($prefix = '')
|
||||
|
||||
@@ -7,9 +7,11 @@ namespace PicoFeed\Config;
|
||||
*
|
||||
* @author Frederic Guillot
|
||||
*
|
||||
* @method \PicoFeed\Config\Config setAdditionalCurlOptions(array $options)
|
||||
* @method \PicoFeed\Config\Config setClientTimeout(integer $value)
|
||||
* @method \PicoFeed\Config\Config setClientUserAgent(string $value)
|
||||
* @method \PicoFeed\Config\Config setMaxRedirections(integer $value)
|
||||
* @method \PicoFeed\Config\Config setMaxRecursions(integer $value)
|
||||
* @method \PicoFeed\Config\Config setMaxBodySize(integer $value)
|
||||
* @method \PicoFeed\Config\Config setProxyHostname(string $value)
|
||||
* @method \PicoFeed\Config\Config setProxyPort(integer $value)
|
||||
@@ -36,6 +38,7 @@ namespace PicoFeed\Config;
|
||||
* @method integer getClientTimeout()
|
||||
* @method string getClientUserAgent()
|
||||
* @method integer getMaxRedirections()
|
||||
* @method integer getMaxRecursions()
|
||||
* @method integer getMaxBodySize()
|
||||
* @method string getProxyHostname()
|
||||
* @method integer getProxyPort()
|
||||
@@ -59,6 +62,7 @@ namespace PicoFeed\Config;
|
||||
* @method string getFilterImageProxyUrl()
|
||||
* @method \Closure getFilterImageProxyCallback()
|
||||
* @method string getFilterImageProxyProtocol()
|
||||
* @method array getAdditionalCurlOptions()
|
||||
*/
|
||||
class Config
|
||||
{
|
||||
@@ -92,5 +96,7 @@ class Config
|
||||
|
||||
return isset($this->container[$parameter]) ? $this->container[$parameter] : $default_value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ class Attribute
|
||||
'td' => array(),
|
||||
'tbody' => array(),
|
||||
'thead' => array(),
|
||||
'h1' => array(),
|
||||
'h2' => array(),
|
||||
'h3' => array(),
|
||||
'h4' => array(),
|
||||
|
||||
@@ -13,10 +13,8 @@ class Filter
|
||||
* Get the Html filter instance.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @param string $html HTML content
|
||||
* @param string $website Site URL (used to build absolute URL)
|
||||
*
|
||||
* @return Html
|
||||
*/
|
||||
public static function html($html, $website)
|
||||
@@ -30,7 +28,7 @@ class Filter
|
||||
* Escape HTML content.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @param string $content
|
||||
* @return string
|
||||
*/
|
||||
public static function escape($content)
|
||||
@@ -42,7 +40,6 @@ class Filter
|
||||
* Remove HTML tags.
|
||||
*
|
||||
* @param string $data Input data
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function removeHTMLTags($data)
|
||||
@@ -54,9 +51,7 @@ class Filter
|
||||
* Remove the XML tag from a document.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @param string $data Input data
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function stripXmlTag($data)
|
||||
@@ -80,9 +75,7 @@ class Filter
|
||||
* Strip head tag from the HTML content.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @param string $data Input data
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function stripHeadTags($data)
|
||||
@@ -94,9 +87,7 @@ class Filter
|
||||
* Trim whitespace from the begining, the end and inside a string and don't break utf-8 string.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @param string $value Raw data
|
||||
*
|
||||
* @return string Normalized data
|
||||
*/
|
||||
public static function stripWhiteSpace($value)
|
||||
@@ -112,9 +103,7 @@ class Filter
|
||||
* Fixes before XML parsing.
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @param string $data Raw data
|
||||
*
|
||||
* @return string Normalized data
|
||||
*/
|
||||
public static function normalizeData($data)
|
||||
|
||||
@@ -90,7 +90,6 @@ class Html
|
||||
* Set config object.
|
||||
*
|
||||
* @param \PicoFeed\Config\Config $config Config instance
|
||||
*
|
||||
* @return \PicoFeed\Filter\Html
|
||||
*/
|
||||
public function setConfig($config)
|
||||
@@ -160,7 +159,8 @@ class Html
|
||||
/**
|
||||
* Called after XML parsing.
|
||||
*
|
||||
* @param string $content the content that should be filtered
|
||||
* @param string $content
|
||||
* @return string
|
||||
*/
|
||||
public function filterRules($content)
|
||||
{
|
||||
|
||||
@@ -42,6 +42,7 @@ class Tag extends Base
|
||||
'td',
|
||||
'tbody',
|
||||
'thead',
|
||||
'h1',
|
||||
'h2',
|
||||
'h3',
|
||||
'h4',
|
||||
@@ -67,6 +68,8 @@ class Tag extends Base
|
||||
'abbr',
|
||||
'iframe',
|
||||
'q',
|
||||
'sup',
|
||||
'sub',
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -74,7 +77,6 @@ class Tag extends Base
|
||||
*
|
||||
* @param string $tag Tag name
|
||||
* @param array $attributes Attributes dictionary
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isAllowed($tag, array $attributes)
|
||||
@@ -87,7 +89,6 @@ class Tag extends Base
|
||||
*
|
||||
* @param string $tag Tag name
|
||||
* @param string $attributes Attributes converted in html
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function openHtmlTag($tag, $attributes = '')
|
||||
@@ -99,7 +100,6 @@ class Tag extends Base
|
||||
* Return the HTML closing tag.
|
||||
*
|
||||
* @param string $tag Tag name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function closeHtmlTag($tag)
|
||||
@@ -111,7 +111,6 @@ class Tag extends Base
|
||||
* Return true is the tag is self-closing.
|
||||
*
|
||||
* @param string $tag Tag name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isSelfClosingTag($tag)
|
||||
@@ -123,7 +122,6 @@ class Tag extends Base
|
||||
* Check if a tag is on the whitelist.
|
||||
*
|
||||
* @param string $tag Tag name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isAllowedTag($tag)
|
||||
@@ -139,7 +137,6 @@ class Tag extends Base
|
||||
*
|
||||
* @param string $tag Tag name
|
||||
* @param array $attributes Tag attributes
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isPixelTracker($tag, array $attributes)
|
||||
@@ -153,7 +150,6 @@ class Tag extends Base
|
||||
* Remove script tags.
|
||||
*
|
||||
* @param string $data Input data
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function removeBlacklistedTags($data)
|
||||
@@ -179,7 +175,6 @@ class Tag extends Base
|
||||
* Remove empty tags.
|
||||
*
|
||||
* @param string $data Input data
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function removeEmptyTags($data)
|
||||
@@ -191,7 +186,6 @@ class Tag extends Base
|
||||
* Replace <br/><br/> by only one.
|
||||
*
|
||||
* @param string $data Input data
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function removeMultipleBreakTags($data)
|
||||
@@ -203,7 +197,6 @@ class Tag extends Base
|
||||
* Set whitelisted tags adn attributes for each tag.
|
||||
*
|
||||
* @param array $values List of tags: ['video' => ['src', 'cover'], 'img' => ['src']]
|
||||
*
|
||||
* @return Tag
|
||||
*/
|
||||
public function setWhitelistedTags(array $values)
|
||||
|
||||
@@ -25,8 +25,7 @@ class Atom extends Parser
|
||||
* Get the path to the items XML tree.
|
||||
*
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
*
|
||||
* @return SimpleXMLElement
|
||||
* @return SimpleXMLElement[]
|
||||
*/
|
||||
public function getItemsTree(SimpleXMLElement $xml)
|
||||
{
|
||||
@@ -288,12 +287,25 @@ class Atom extends Parser
|
||||
$item->setLanguage(XmlParser::getValue($language) ?: $feed->getLanguage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the item categories.
|
||||
*
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param Item $item Item object
|
||||
* @param Feed $feed Feed object
|
||||
*/
|
||||
public function findItemCategories(SimpleXMLElement $entry, Item $item, Feed $feed)
|
||||
{
|
||||
$categories = XmlParser::getXPathResult($entry, 'atom:category/@term', $this->namespaces)
|
||||
?: XmlParser::getXPathResult($entry, 'category/@term');
|
||||
$item->setCategoriesFromXml($categories);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL from a link tag.
|
||||
*
|
||||
* @param SimpleXMLElement $xml XML tag
|
||||
* @param string $rel Link relationship: alternate, enclosure, related, self, via
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getUrl(SimpleXMLElement $xml, $rel, $fallback = false)
|
||||
@@ -317,7 +329,6 @@ class Atom extends Parser
|
||||
*
|
||||
* @param SimpleXMLElement $xml XML tag
|
||||
* @param string $rel Link relationship: alternate, enclosure, related, self, via
|
||||
*
|
||||
* @return SimpleXMLElement|null
|
||||
*/
|
||||
private function findLink(SimpleXMLElement $xml, $rel)
|
||||
@@ -338,7 +349,6 @@ class Atom extends Parser
|
||||
* Get the entry content.
|
||||
*
|
||||
* @param SimpleXMLElement $entry XML Entry
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getContent(SimpleXMLElement $entry)
|
||||
|
||||
@@ -38,6 +38,7 @@ class DateParser extends Base
|
||||
DATE_RFC1123 => null,
|
||||
DATE_RFC2822 => null,
|
||||
DATE_RFC3339 => null,
|
||||
'l, d M Y H:i:s' => null,
|
||||
'D, d M Y H:i:s' => 25,
|
||||
'D, d M Y h:i:s' => 25,
|
||||
'D M d Y H:i:s' => 24,
|
||||
|
||||
@@ -13,7 +13,7 @@ class Feed
|
||||
/**
|
||||
* Feed items.
|
||||
*
|
||||
* @var array
|
||||
* @var Item[]
|
||||
*/
|
||||
public $items = array();
|
||||
|
||||
|
||||
@@ -103,6 +103,13 @@ class Item
|
||||
*/
|
||||
public $language = '';
|
||||
|
||||
/**
|
||||
* Item categories.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $categories = array();
|
||||
|
||||
/**
|
||||
* Raw XML.
|
||||
*
|
||||
@@ -169,10 +176,13 @@ class Item
|
||||
$publishedDate = $this->publishedDate != null ? $this->publishedDate->format(DATE_RFC822) : null;
|
||||
$updatedDate = $this->updatedDate != null ? $this->updatedDate->format(DATE_RFC822) : null;
|
||||
|
||||
$categoryString = $this->categories != null ? implode(',', $this->categories) : null;
|
||||
|
||||
$output .= 'Item::date = '.$this->date->format(DATE_RFC822).PHP_EOL;
|
||||
$output .= 'Item::publishedDate = '.$publishedDate.PHP_EOL;
|
||||
$output .= 'Item::updatedDate = '.$updatedDate.PHP_EOL;
|
||||
$output .= 'Item::isRTL() = '.($this->isRTL() ? 'true' : 'false').PHP_EOL;
|
||||
$output .= 'Item::categories = ['.$categoryString.']'.PHP_EOL;
|
||||
$output .= 'Item::content = '.strlen($this->content).' bytes'.PHP_EOL;
|
||||
|
||||
return $output;
|
||||
@@ -305,6 +315,16 @@ class Item
|
||||
return $this->language;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get categories.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCategories()
|
||||
{
|
||||
return $this->categories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get author.
|
||||
*
|
||||
@@ -433,6 +453,40 @@ class Item
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set item categories.
|
||||
*
|
||||
* @param array $categories
|
||||
* @return Item
|
||||
*/
|
||||
public function setCategories($categories)
|
||||
{
|
||||
$this->categories = $categories;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set item categories from xml.
|
||||
*
|
||||
* @param |SimpleXMLElement[] $categories
|
||||
* @return Item
|
||||
*/
|
||||
public function setCategoriesFromXml($categories)
|
||||
{
|
||||
if ($categories !== false) {
|
||||
$this->setCategories(
|
||||
array_map(
|
||||
function ($element) {
|
||||
return trim((string) $element);
|
||||
},
|
||||
$categories
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set raw XML.
|
||||
*
|
||||
|
||||
@@ -103,8 +103,8 @@ abstract class Parser implements ParserInterface
|
||||
|
||||
/**
|
||||
* Parse the document.
|
||||
*
|
||||
* @return \PicoFeed\Parser\Feed
|
||||
* @return Feed
|
||||
* @throws MalformedXmlException
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
@@ -163,6 +163,7 @@ abstract class Parser implements ParserInterface
|
||||
$this->findItemDate($entry, $item, $feed);
|
||||
$this->findItemEnclosure($entry, $item, $feed);
|
||||
$this->findItemLanguage($entry, $item, $feed);
|
||||
$this->findItemCategories($entry, $item, $feed);
|
||||
|
||||
$this->itemPostProcessor->execute($feed, $item);
|
||||
$feed->items[] = $item;
|
||||
@@ -222,18 +223,20 @@ abstract class Parser implements ParserInterface
|
||||
public function findItemDate(SimpleXMLElement $entry, Item $item, Feed $feed)
|
||||
{
|
||||
$this->findItemPublishedDate($entry, $item, $feed);
|
||||
$published = $item->getPublishedDate();
|
||||
|
||||
$this->findItemUpdatedDate($entry, $item, $feed);
|
||||
$updated = $item->getUpdatedDate();
|
||||
|
||||
if ($published === null && $updated === null) {
|
||||
$item->setDate($feed->getDate()); // We use the feed date if there is no date for the item
|
||||
} elseif ($published !== null && $updated !== null) {
|
||||
$item->setDate(max($published, $updated)); // We use the most recent date between published and updated
|
||||
} else {
|
||||
$item->setDate($updated ?: $published);
|
||||
if ($item->getPublishedDate() === null) {
|
||||
// Use the updated date if available, otherwise use the feed date
|
||||
$item->setPublishedDate($item->getUpdatedDate() ?: $feed->getDate());
|
||||
}
|
||||
|
||||
if ($item->getUpdatedDate() === null) {
|
||||
// Use the published date as fallback
|
||||
$item->setUpdatedDate($item->getPublishedDate());
|
||||
}
|
||||
|
||||
// Use the most recent of published and updated dates
|
||||
$item->setDate(max($item->getPublishedDate(), $item->getUpdatedDate()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -256,7 +259,7 @@ abstract class Parser implements ParserInterface
|
||||
public function getDateParser()
|
||||
{
|
||||
if ($this->dateParser === null) {
|
||||
return new DateParser($this->config);
|
||||
$this->dateParser = new DateParser($this->config);
|
||||
}
|
||||
|
||||
return $this->dateParser;
|
||||
@@ -276,9 +279,7 @@ abstract class Parser implements ParserInterface
|
||||
* Return true if the given language is "Right to Left".
|
||||
*
|
||||
* @static
|
||||
*
|
||||
* @param string $language Language: fr-FR, en-US
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isLanguageRTL($language)
|
||||
@@ -321,12 +322,12 @@ abstract class Parser implements ParserInterface
|
||||
* Set config object.
|
||||
*
|
||||
* @param \PicoFeed\Config\Config $config Config instance
|
||||
*
|
||||
* @return \PicoFeed\Parser\Parser
|
||||
*/
|
||||
public function setConfig($config)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->itemPostProcessor->setConfig($config);
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -348,7 +349,6 @@ abstract class Parser implements ParserInterface
|
||||
* scraped
|
||||
* @param null|\Closure $scraperCallback Callback function that gets called for each
|
||||
* scraper execution
|
||||
*
|
||||
* @return \PicoFeed\Parser\Parser
|
||||
*/
|
||||
public function enableContentGrabber($needsRuleFile = false, $scraperCallback = null)
|
||||
@@ -371,7 +371,6 @@ abstract class Parser implements ParserInterface
|
||||
* Set ignored URLs for the content grabber.
|
||||
*
|
||||
* @param array $urls URLs
|
||||
*
|
||||
* @return \PicoFeed\Parser\Parser
|
||||
*/
|
||||
public function setGrabberIgnoreUrls(array $urls)
|
||||
@@ -384,7 +383,6 @@ abstract class Parser implements ParserInterface
|
||||
* Register all supported namespaces to be used within an xpath query.
|
||||
*
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
*
|
||||
* @return SimpleXMLElement
|
||||
*/
|
||||
public function registerSupportedNamespaces(SimpleXMLElement $xml)
|
||||
@@ -395,6 +393,4 @@ abstract class Parser implements ParserInterface
|
||||
|
||||
return $xml;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -170,4 +170,13 @@ interface ParserInterface
|
||||
* @param Feed $feed Feed object
|
||||
*/
|
||||
public function findItemLanguage(SimpleXMLElement $entry, Item $item, Feed $feed);
|
||||
|
||||
/**
|
||||
* Find the item categories.
|
||||
*
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param Item $item Item object
|
||||
* @param Feed $feed Feed object
|
||||
*/
|
||||
public function findItemCategories(SimpleXMLElement $entry, Item $item, Feed $feed);
|
||||
}
|
||||
|
||||
@@ -27,8 +27,7 @@ class Rss10 extends Parser
|
||||
* Get the path to the items XML tree.
|
||||
*
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
*
|
||||
* @return SimpleXMLElement
|
||||
* @return SimpleXMLElement[]
|
||||
*/
|
||||
public function getItemsTree(SimpleXMLElement $xml)
|
||||
{
|
||||
@@ -290,4 +289,17 @@ class Rss10 extends Parser
|
||||
|
||||
$item->setLanguage(XmlParser::getValue($language) ?: $feed->getLanguage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the item categories.
|
||||
*
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param Item $item Item object
|
||||
* @param Feed $feed Feed object
|
||||
*/
|
||||
public function findItemCategories(SimpleXMLElement $entry, Item $item, Feed $feed)
|
||||
{
|
||||
$categories = XmlParser::getXPathResult($entry, 'dc:subject', $this->namespaces);
|
||||
$item->setCategoriesFromXml($categories);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,7 @@ class Rss20 extends Parser
|
||||
* Get the path to the items XML tree.
|
||||
*
|
||||
* @param SimpleXMLElement $xml Feed xml
|
||||
*
|
||||
* @return SimpleXMLElement
|
||||
* @return SimpleXMLElement[]
|
||||
*/
|
||||
public function getItemsTree(SimpleXMLElement $xml)
|
||||
{
|
||||
@@ -302,4 +301,17 @@ class Rss20 extends Parser
|
||||
$language = XmlParser::getXPathResult($entry, 'dc:language', $this->namespaces);
|
||||
$item->setLanguage(XmlParser::getValue($language) ?: $feed->getLanguage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the item categories.
|
||||
*
|
||||
* @param SimpleXMLElement $entry Feed item
|
||||
* @param Item $item Item object
|
||||
* @param Feed $feed Feed object
|
||||
*/
|
||||
public function findItemCategories(SimpleXMLElement $entry, Item $item, Feed $feed)
|
||||
{
|
||||
$categories = XmlParser::getXPathResult($entry, 'category');
|
||||
$item->setCategoriesFromXml($categories);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ class XmlParser
|
||||
*
|
||||
* @static
|
||||
* @param string $input XML content
|
||||
* @return DOMDocument
|
||||
* @return DOMDocument|bool
|
||||
*/
|
||||
public static function getDomDocument($input)
|
||||
{
|
||||
|
||||
@@ -33,5 +33,7 @@ class ContentFilterProcessor extends Base implements ItemProcessorInterface
|
||||
} else {
|
||||
Logger::setMessage(get_called_class().': Content filtering disabled');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace PicoFeed\Processor;
|
||||
use PicoFeed\Base;
|
||||
use PicoFeed\Parser\Feed;
|
||||
use PicoFeed\Parser\Item;
|
||||
use PicoFeed\Config\Config;
|
||||
|
||||
/**
|
||||
* Item Post Processor
|
||||
@@ -71,7 +72,7 @@ class ItemPostProcessor extends Base
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks wheather a specific processor is registered or not
|
||||
* Checks whether a specific processor is registered or not
|
||||
*
|
||||
* @access public
|
||||
* @param string $class
|
||||
@@ -93,4 +94,13 @@ class ItemPostProcessor extends Base
|
||||
{
|
||||
return isset($this->processors[$class]) ? $this->processors[$class] : null;
|
||||
}
|
||||
|
||||
public function setConfig(Config $config)
|
||||
{
|
||||
foreach ($this->processors as $processor) {
|
||||
$processor->setConfig($config);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ class Favicon extends Base
|
||||
'image/x-icon',
|
||||
'image/jpeg',
|
||||
'image/jpg',
|
||||
'image/svg+xml'
|
||||
'image/svg+xml',
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -95,8 +95,7 @@ class Favicon extends Base
|
||||
* Download and check if a resource exists.
|
||||
*
|
||||
* @param string $url URL
|
||||
*
|
||||
* @return \PicoFeed\Client Client instance
|
||||
* @return \PicoFeed\Client\Client Client instance
|
||||
*/
|
||||
public function download($url)
|
||||
{
|
||||
@@ -118,7 +117,6 @@ class Favicon extends Base
|
||||
* Check if a remote file exists.
|
||||
*
|
||||
* @param string $url URL
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function exists($url)
|
||||
@@ -131,7 +129,6 @@ class Favicon extends Base
|
||||
*
|
||||
* @param string $website_link URL
|
||||
* @param string $favicon_link optional URL
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function find($website_link, $favicon_link = '')
|
||||
@@ -165,7 +162,6 @@ class Favicon extends Base
|
||||
* Extract the icon links from the HTML.
|
||||
*
|
||||
* @param string $html HTML
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function extract($html)
|
||||
@@ -179,7 +175,7 @@ class Favicon extends Base
|
||||
$dom = XmlParser::getHtmlDocument($html);
|
||||
|
||||
$xpath = new DOMXpath($dom);
|
||||
$elements = $xpath->query('//link[@rel="icon" or @rel="shortcut icon" or @rel="icon shortcut"]');
|
||||
$elements = $xpath->query('//link[@rel="icon" or @rel="shortcut icon" or @rel="Shortcut Icon" or @rel="icon shortcut"]');
|
||||
|
||||
for ($i = 0; $i < $elements->length; ++$i) {
|
||||
$icons[] = $elements->item($i)->getAttribute('href');
|
||||
|
||||
@@ -56,13 +56,13 @@ class Reader extends Base
|
||||
/**
|
||||
* Discover and download a feed.
|
||||
*
|
||||
* @param string $url Feed or website url
|
||||
* @param string $url Feed or website url
|
||||
* @param string $last_modified Last modified HTTP header
|
||||
* @param string $etag Etag HTTP header
|
||||
* @param string $username HTTP basic auth username
|
||||
* @param string $password HTTP basic auth password
|
||||
*
|
||||
* @return \PicoFeed\Client\Client
|
||||
* @param string $etag Etag HTTP header
|
||||
* @param string $username HTTP basic auth username
|
||||
* @param string $password HTTP basic auth password
|
||||
* @return Client
|
||||
* @throws SubscriptionNotFoundException
|
||||
*/
|
||||
public function discover($url, $last_modified = '', $etag = '', $username = '', $password = '')
|
||||
{
|
||||
@@ -127,11 +127,11 @@ class Reader extends Base
|
||||
/**
|
||||
* Get a parser instance.
|
||||
*
|
||||
* @param string $url Site url
|
||||
* @param string $content Feed content
|
||||
* @param string $url Site url
|
||||
* @param string $content Feed content
|
||||
* @param string $encoding HTTP encoding
|
||||
*
|
||||
* @return \PicoFeed\Parser\Parser
|
||||
* @throws UnsupportedFeedFormatException
|
||||
*/
|
||||
public function getParser($url, $content, $encoding)
|
||||
{
|
||||
@@ -154,7 +154,6 @@ class Reader extends Base
|
||||
* Detect the feed format.
|
||||
*
|
||||
* @param string $content Feed content
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function detectFormat($content)
|
||||
@@ -177,7 +176,7 @@ class Reader extends Base
|
||||
* Add the prefix "http://" if the end-user just enter a domain name.
|
||||
*
|
||||
* @param string $url Url
|
||||
* @retunr string
|
||||
* @return string
|
||||
*/
|
||||
public function prependScheme($url)
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@ return array(
|
||||
'body' => array(
|
||||
'//div[@class="content"]',
|
||||
),
|
||||
'strip' => array()
|
||||
)
|
||||
)
|
||||
'strip' => array(),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -4,28 +4,41 @@ return array(
|
||||
'%.*%' => array(
|
||||
'test_url' => 'http://www.wired.com/gamelife/2013/09/ouya-free-the-games/',
|
||||
'body' => array(
|
||||
'//div[@data-js="gallerySlides"]',
|
||||
'//article',
|
||||
'//div[@data-js="gallerySlides"]',
|
||||
'//div[starts-with(@class,"post")]',
|
||||
),
|
||||
'strip' => array(
|
||||
'//*[@id="linker_widget"]',
|
||||
'//*[@class="credit"]',
|
||||
'//div[@data-js="slideCount"]',
|
||||
'//*[contains(@class="visually-hidden")]',
|
||||
'//*[@data-slide-number="_endslate"]',
|
||||
'//*[@id="related"]',
|
||||
'//*[contains(@class, "bio")]',
|
||||
'//*[contains(@class, "entry-footer")]',
|
||||
'//*[contains(@class, "mobify_backtotop_link")]',
|
||||
'//*[contains(@class, "gallery-navigation")]',
|
||||
'//*[contains(@class, "gallery-thumbnail")]',
|
||||
'//img[contains(@src, "1x1")]',
|
||||
'//a[contains(@href, "creativecommons")]',
|
||||
'//a[@href="#start-of-content"]',
|
||||
'//h1',
|
||||
'//nav',
|
||||
'//button',
|
||||
'//figure[starts-with(@class,"rad-slide")]',
|
||||
'//figure[starts-with(@class,"end-slate")]',
|
||||
'//div[contains(@class,"mobile-")]',
|
||||
'//div[starts-with(@class,"mob-gallery-launcher")]',
|
||||
'//div[contains(@id,"mobile-")]',
|
||||
'//span[contains(@class,"slide-count")]',
|
||||
'//div[contains(@class,"show-ipad")]',
|
||||
'//img[contains(@id,"-hero-bg")]',
|
||||
'//div[@data-js="overlayWrap"]',
|
||||
'//ul[contains(@class,"metadata")]',
|
||||
'//div[@class="opening center"]',
|
||||
'//p[contains(@class="byline-mob"]',
|
||||
'//div[@id="o-gallery"]',
|
||||
'//div[starts-with(@class,"sm-col")]',
|
||||
'//div[contains(@class,"pad-b-huge")]',
|
||||
'//a[contains(@class,"visually-hidden")]',
|
||||
'//*[@class="social"]',
|
||||
'//i',
|
||||
'//div[@data-js="mobGalleryAd"]',
|
||||
'//div[contains(@class,"footer")]',
|
||||
'//div[contains(@data-js,"fader")]',
|
||||
'//div[@id="sharing"]',
|
||||
'//div[contains(@id,"related")]',
|
||||
'//div[@id="most-pop"]',
|
||||
'//ul[@id="article-tags"]',
|
||||
'//style',
|
||||
'//section[contains(@class,"footer")]'
|
||||
),
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'filter' => array(
|
||||
'%.*%' => array(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%^/news.*%' => array(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
'test_url' => 'http://www.aljazeera.com/news/2015/09/xi-jinping-seattle-china-150922230118373.html',
|
||||
'body' => array(
|
||||
'//figure[@class="article-content"]',
|
||||
'//div[@class="article-body"]',
|
||||
'//article[@id="main-story"]',
|
||||
),
|
||||
'strip' => array(
|
||||
'//h1',
|
||||
'//h3',
|
||||
'//script',
|
||||
'//header',
|
||||
'//ul',
|
||||
'//table[contains(@class, "in-article-item")]',
|
||||
'//section[contains(@class,"profile")]',
|
||||
'//a[@target="_self"]',
|
||||
'//div[contains(@id,"_2")]',
|
||||
'//div[contains(@id,"_3")]',
|
||||
'//img[@class="viewMode"]',
|
||||
'//table[contains(@class,"in-article-item")]',
|
||||
'//div[@data-embed-type="Brightcove"]',
|
||||
'//div[@class="QuoteContainer"]',
|
||||
'//div[@class="BottomByLine"]',
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
@@ -10,6 +9,7 @@ return array(
|
||||
'strip' => array(
|
||||
'//p[@class="kindofstory"]',
|
||||
'//cite[@class="byline"]',
|
||||
'//div[@class="useful-top"]',
|
||||
'//div[contains(@class,"related-topics")]',
|
||||
'//links',
|
||||
'//sharebar',
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'filter' => array(
|
||||
'%.*%' => array(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
'test_url' => 'http://arstechnica.com/tech-policy/2015/09/judge-warners-2m-happy-birthday-copyright-is-bogus/',
|
||||
'body' => array(
|
||||
'//header/h2',
|
||||
'//section[@id="article-guts"]',
|
||||
'//div[@class="superscroll-content show"]',
|
||||
'//div[@class="gallery"]',
|
||||
'//article',
|
||||
),
|
||||
'next_page' => '//span[@class="numbers"]/a',
|
||||
'strip' => array(
|
||||
'//h4[@class="post-upperdek"]',
|
||||
'//h1',
|
||||
'//ul[@class="lSPager lSGallery"]',
|
||||
'//div[@class="lSAction"]',
|
||||
'//section[@class="post-meta"]',
|
||||
'//figcaption',
|
||||
'//div[@class="post-meta"]',
|
||||
'//div[@class="gallery-image-credit"]',
|
||||
'//aside',
|
||||
'//div[@class="article-expander"]',
|
||||
'//div[@class="gallery-image-credit"]',
|
||||
'//section[@class="article-author"]',
|
||||
'//*[contains(@id,"social-")]',
|
||||
'//div[contains(@id,"footer")]',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%/index.php.*comic=.*%' => array(
|
||||
|
||||
18
vendor/fguillot/picofeed/lib/PicoFeed/Rules/backchannel.com.php
vendored
Normal file
18
vendor/fguillot/picofeed/lib/PicoFeed/Rules/backchannel.com.php
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
'test_url' => 'https://medium.com/lessons-learned/917b8b63ae3e',
|
||||
'body' => array(
|
||||
'//div[contains(@class,"section-inner")]',
|
||||
),
|
||||
'strip' => array(
|
||||
'//div[contains(@class,"metabar")]',
|
||||
'//img[contains(@class,"thumbnail")]',
|
||||
'//h1',
|
||||
'//blockquote',
|
||||
'//p[contains(@class,"graf-after--h4")]'
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -1,11 +1,10 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
'test_url' => 'http://www.bangkokpost.com/news/politics/704204/new-us-ambassador-arrives-in-bangkok',
|
||||
'body' => array(
|
||||
'//div[@class="articleContents"]',
|
||||
'//article/div[@class="articleContents"]',
|
||||
),
|
||||
'strip' => array(
|
||||
'//h2',
|
||||
@@ -13,7 +12,6 @@ return array(
|
||||
'//div[@class="text-size"]',
|
||||
'//div[@class="relate-story"]',
|
||||
'//div[@class="text-ads"]',
|
||||
'//script',
|
||||
'//ul',
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'filter' => array(
|
||||
'%.*%' => array(
|
||||
|
||||
31
vendor/fguillot/picofeed/lib/PicoFeed/Rules/bigpicture.ru.php
vendored
Normal file
31
vendor/fguillot/picofeed/lib/PicoFeed/Rules/bigpicture.ru.php
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
'test_url' => 'http://bigpicture.ru/?p=556658',
|
||||
'body' => array(
|
||||
'//div[@class="article container"]',
|
||||
),
|
||||
'strip' => array(
|
||||
'//script',
|
||||
'//form',
|
||||
'//style',
|
||||
'//h1',
|
||||
'//*[@class="wp-smiley"]',
|
||||
'//div[@class="ipmd"]',
|
||||
'//div[@class="tags"]',
|
||||
'//div[@class="social-button"]',
|
||||
'//div[@class="bottom-share"]',
|
||||
'//div[@class="raccoonbox"]',
|
||||
'//div[@class="yndadvert"]',
|
||||
'//div[@class="we-recommend"]',
|
||||
'//div[@class="relap-bigpicture_ru-wrapper"]',
|
||||
'//div[@id="mmail"]',
|
||||
'//div[@id="mobile-ads-cut"]',
|
||||
'//div[@id="liquidstorm-alt-html"]',
|
||||
'//div[contains(@class, "post-tags")]',
|
||||
'//*[contains(text(),"Смотрите также")]',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
|
||||
22
vendor/fguillot/picofeed/lib/PicoFeed/Rules/biztimes.com.php
vendored
Normal file
22
vendor/fguillot/picofeed/lib/PicoFeed/Rules/biztimes.com.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
'test_url' => 'https://www.biztimes.com/2017/02/10/settlement-would-revive-fowler-lake-condo-project-in-oconomowoc/',
|
||||
'body' => array(
|
||||
'//h2/span[@class="subhead"]',
|
||||
'//div[contains(@class,"article-content")]',
|
||||
),
|
||||
'strip' => array(
|
||||
'//script',
|
||||
'//div[contains(@class,"mobile-article-content")]',
|
||||
'//div[contains(@class,"sharedaddy")]',
|
||||
'//div[contains(@class,"author-details")]',
|
||||
'//div[@class="row ad"]',
|
||||
'//div[contains(@class,"relatedposts")]',
|
||||
'//div[@class="col-lg-12"]',
|
||||
'//div[contains(@class,"widget")]',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
15
vendor/fguillot/picofeed/lib/PicoFeed/Rules/bleepingcomputer.com.php
vendored
Normal file
15
vendor/fguillot/picofeed/lib/PicoFeed/Rules/bleepingcomputer.com.php
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
'test_url' => 'https://www.bleepingcomputer.com/news/google/chromes-sandbox-feature-infringes-on-three-patents-so-google-must-now-pay-20m/',
|
||||
'body' => array(
|
||||
'//div[@class="article_section"]',
|
||||
),
|
||||
'strip' => array(
|
||||
'//*[@itemprop="headline"]',
|
||||
'//div[@class="cz-news-story-title-section"]'
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
|
||||
22
vendor/fguillot/picofeed/lib/PicoFeed/Rules/brewers.mlb.com.php
vendored
Normal file
22
vendor/fguillot/picofeed/lib/PicoFeed/Rules/brewers.mlb.com.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
'test_url' => 'http://m.brewers.mlb.com/news/article/161364798',
|
||||
'body' => array(
|
||||
'//article[contains(@class,"article")]',
|
||||
),
|
||||
'strip' => array(
|
||||
'//div[contains(@class,"ad-slot")]',
|
||||
'//h1',
|
||||
'//span[@class="timestamp"]',
|
||||
'//div[contains(@class,"contributor-bottom")]',
|
||||
'//div[contains(@class,"video")]',
|
||||
'//ul[contains(@class,"social")]',
|
||||
'//p[@class="tagline"]',
|
||||
'//div[contains(@class,"social")]',
|
||||
'//div[@class="button-wrap"]',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%/cad/.+%' => array(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
|
||||
18
vendor/fguillot/picofeed/lib/PicoFeed/Rules/chinafile.com.php
vendored
Normal file
18
vendor/fguillot/picofeed/lib/PicoFeed/Rules/chinafile.com.php
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
'test_url' => 'http://www.chinafile.com/books/shanghai-faithful?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+chinafile%2FAll+%28ChinaFile%29',
|
||||
'body' => array(
|
||||
'//div[contains(@class,"pane-featured-photo-panel-pane-1")]',
|
||||
'//div[contains(@class,"video-above-fold")]',
|
||||
'//div[@class="sc-media"]',
|
||||
'//div[contains(@class,"field-name-body")]',
|
||||
),
|
||||
'strip' => array(
|
||||
'//div[contains(@class,"cboxes")]',
|
||||
'//div[contains(@class,"l-middle")]',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%/comic.*%' => array(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%^/products.*%' => array(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'filter' => array(
|
||||
'%.*%' => array(
|
||||
|
||||
28
vendor/fguillot/picofeed/lib/PicoFeed/Rules/crash.net.php
vendored
Normal file
28
vendor/fguillot/picofeed/lib/PicoFeed/Rules/crash.net.php
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
'test_url' => 'http://www.crash.net/motogp/interview/247550/1/exclusive-andrea-dovizioso-interview.html',
|
||||
'body' => array(
|
||||
'//div[@id="content"]',
|
||||
),
|
||||
'strip' => array(
|
||||
'//script',
|
||||
'//style',
|
||||
'//*[@title="Social Networking"]',
|
||||
'//*[@class="crash-ad2"]',
|
||||
'//*[@class="clearfix"]',
|
||||
'//*[@class="crash-ad2"]',
|
||||
'//*[contains(@id, "divCB"]',
|
||||
'//*[@class="pnlComment"]',
|
||||
'//*[@class="comments-tabs"]',
|
||||
'//*[contains(@class, "ad-twocol"]',
|
||||
'//*[@class="stories-list"]',
|
||||
'//*[contains(@class, "btn")]',
|
||||
'//*[@class="content"]',
|
||||
'//h3',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
'test_url' => 'http://www.csmonitor.com/USA/Politics/2015/0925/John-Boehner-steps-down-Self-sacrificing-but-will-it-lead-to-better-government',
|
||||
'body' => array(
|
||||
'//figure[@id="image-top-1"]',
|
||||
'//div[@id="story-body"]',
|
||||
'//h2[@id="summary"]',
|
||||
'//div[@class="flex-video youtube"]',
|
||||
'//div[contains(@class,"eza-body")]',
|
||||
),
|
||||
'strip' => array(
|
||||
'//script',
|
||||
'//img[@title="hide caption"]',
|
||||
'//*[contains(@class,"promo_link")]',
|
||||
'//div[@id="story-embed-column"]',
|
||||
'//span[@id="breadcrumb"]',
|
||||
'//div[@id="byline-wrapper"]',
|
||||
'//div[@class="injection"]',
|
||||
'//*[contains(@class,"promo_link")]',
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
|
||||
@@ -1,17 +1,25 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
'test_url' => 'http://blogs.discovermagazine.com/the-extremo-files/2015/09/11/have-scientists-found-the-worlds-deepest-fish/',
|
||||
'test_url' => 'http://blogs.discovermagazine.com/neuroskeptic/2017/01/25/publishers-jeffrey-beall/',
|
||||
'body' => array(
|
||||
'//div[@class="entry"]',
|
||||
'//div[@class="contentWell"]',
|
||||
),
|
||||
'strip' => array(
|
||||
'//h1',
|
||||
'//div[@class="breadcrumbs"]',
|
||||
'//div[@class="mobile"]',
|
||||
'//div[@class="fromIssue"]',
|
||||
'//div[contains(@class,"belowDeck")]',
|
||||
'//div[@class="meta"]',
|
||||
'//div[@class="shareIcons"]',
|
||||
'//div[@class="categories"]',
|
||||
'//div[@class="navigation"]',
|
||||
'//div[@class="heading"]',
|
||||
'//div[contains(@id,"-ad")]',
|
||||
'//div[@class="relatedArticles"]',
|
||||
'//div[@id="disqus_thread"]'
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
|
||||
22
vendor/fguillot/picofeed/lib/PicoFeed/Rules/e-w-e.ru.php
vendored
Normal file
22
vendor/fguillot/picofeed/lib/PicoFeed/Rules/e-w-e.ru.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
'test_url' => 'http://e-w-e.ru/16-prekrasnyx-izobretenij-zhenshhin/',
|
||||
'body' => array(
|
||||
'//div[contains(@class, "post_text")]',
|
||||
),
|
||||
'strip' => array(
|
||||
'//script',
|
||||
'//form',
|
||||
'//style',
|
||||
'//*[@class="views_post"]',
|
||||
'//*[@class="adman_mobile"]',
|
||||
'//*[@class="adman_desctop"]',
|
||||
'//*[contains(@rel, "nofollow")]',
|
||||
'//*[contains(@class, "wp-smiley")]',
|
||||
'//*[contains(text(),"Источник:")]',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
25
vendor/fguillot/picofeed/lib/PicoFeed/Rules/economist.com.php
vendored
Normal file
25
vendor/fguillot/picofeed/lib/PicoFeed/Rules/economist.com.php
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
return array(
|
||||
'grabber' => array(
|
||||
'%.*%' => array(
|
||||
'test_url' => 'http://www.economist.com/blogs/buttonwood/2017/02/mixed-signals?fsrc=rss',
|
||||
'body' => array(
|
||||
'//article',
|
||||
),
|
||||
'strip' => array(
|
||||
'//span[@class="blog-post__siblings-list-header "]',
|
||||
'//h1',
|
||||
'//aside',
|
||||
'//div[@class="blog-post__asideable-wrapper"]',
|
||||
'//div[@class="share_inline_header"]',
|
||||
'//div[@id="column-right"]',
|
||||
'//div[contains(@class,"blog-post__siblings-list-aside")]',
|
||||
'//div[@class="video-player__wrapper"]',
|
||||
'//div[@class="blog-post__bottom-panel"]',
|
||||
'//div[contains(@class,"latest-updates-panel__container")]',
|
||||
'//div[contains(@class,"blog-post__asideable-content")]',
|
||||
'//div[@aria-label="Advertisement"]'
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -1,5 +1,4 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'filter' => array(
|
||||
'%.*%' => array(
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user