mirror of
https://github.com/RSS-Bridge/rss-bridge.git
synced 2025-08-16 05:24:08 +02:00
Compare commits
42 Commits
2018-10-15
...
revert-909
Author | SHA1 | Date | |
---|---|---|---|
|
db25a68072 | ||
|
8c97953211 | ||
|
d987ceec73 | ||
|
392e3ff6c7 | ||
|
e295dc5a79 | ||
|
b9f6bc8197 | ||
|
9c1c0f2974 | ||
|
65da157fff | ||
|
5fe943562a | ||
|
c58331f74d | ||
|
145a46ae1d | ||
|
1a7a7bad98 | ||
|
27d6a22675 | ||
|
b55ec51e0e | ||
|
07b4c72d5d | ||
|
2e6cbd1ce7 | ||
|
2ac2f3dc66 | ||
|
e2dfea2b77 | ||
|
c4896c7791 | ||
|
7621784598 | ||
|
1cfe939927 | ||
|
c56f7abc2a | ||
|
e3030cbbfd | ||
|
953c6e1022 | ||
|
dbd44f64dd | ||
|
89ca42da54 | ||
|
b4b5340b7e | ||
|
a508dddb36 | ||
|
cb488d9d8c | ||
|
9820ad5c0f | ||
|
ea2d54523d | ||
|
87d218296e | ||
|
afd5ef0f1d | ||
|
30bc5179c2 | ||
|
7596be65f2 | ||
|
16f0ee7104 | ||
|
e0323f06cd | ||
|
717b0bdd9c | ||
|
62d737efe2 | ||
|
6fce03daa7 | ||
|
7561c0685d | ||
|
f48eac854f |
@@ -1,47 +1,47 @@
|
||||
### Pull request policy
|
||||
Fix one issue per pull request.
|
||||
Squash commits before opening a pull request.
|
||||
Respect the coding style policy.
|
||||
Name your PR like the following :
|
||||
|
||||
* When correcting a single bridge, use `[BridgeName] Feature`.
|
||||
* When fixing a problem in a specific file, use `[FileName] Feature`.
|
||||
* When fixing a general problem, use `category : feature`.
|
||||
* [Fix one issue per pull request](https://github.com/RSS-Bridge/rss-bridge/wiki/Pull-request-policy#fix-one-issue-per-pull-request)
|
||||
* [Respect the coding style policy](https://github.com/RSS-Bridge/rss-bridge/wiki/Pull-request-policy#respect-the-coding-style-policy)
|
||||
* [Properly name your commits](https://github.com/RSS-Bridge/rss-bridge/wiki/Pull-request-policy#properly-name-your-commits)
|
||||
* When fixing a bridge (located in the `bridges` directory), write `[BridgeName] Feature` <br>(i.e. `[YoutubeBridge] Fix typo in video titles`).
|
||||
* When fixing other files, use `[FileName] Feature` <br>(i.e. `[index.php] Add multilingual support`).
|
||||
* When fixing a general problem that applies to multiple files, write `category: feature` <br>(i.e. `bridges: Fix various typos`).
|
||||
|
||||
Note that all pull-requests should pass the unit tests before they can be merged.
|
||||
Note that all pull-requests must pass all tests before they can be merged.
|
||||
|
||||
### Coding style
|
||||
|
||||
Use `camelCase` for variables and methods.
|
||||
Use `UPPERCASE` for constants.
|
||||
Use `PascalCase` for class names. When creating a bridge, your class and PHP file should be named `MyImplementationBridge`.
|
||||
Use tabs for indentation.
|
||||
Add an empty line at the end of your file.
|
||||
|
||||
Use `''` to encapsulate strings, including in arrays.
|
||||
Prefer lines shorter than 80 chars, no line longer than 120 chars.
|
||||
PHP constants should be in lower case (`true, false, null`...)
|
||||
|
||||
|
||||
* Add spaces between the logical operator and your expressions (not needed for the `!` operator).
|
||||
* Use `||` and `&&` instead of `or` and `and`.
|
||||
* Add space between your condition and the opening bracket/closing bracket.
|
||||
* Don't put a space between `if` and your bracket.
|
||||
* Use `elseif` instead of `else if`.
|
||||
* Add new lines in your conditions if they are containing more than one line.
|
||||
* Example :
|
||||
|
||||
```PHP
|
||||
if($a == true && $b) {
|
||||
print($a);
|
||||
} else if(!$b) {
|
||||
|
||||
$a = !$a;
|
||||
$b = $b >> $a;
|
||||
print($b);
|
||||
|
||||
} else {
|
||||
print($b);
|
||||
}
|
||||
```
|
||||
|
||||
* [Whitespace](https://github.com/RSS-Bridge/rss-bridge/wiki/Whitespace)
|
||||
* [Add a new line at the end of a file](https://github.com/RSS-Bridge/rss-bridge/wiki/Whitespace#add-a-new-line-at-the-end-of-a-file)
|
||||
* [Do not add a whitespace before a semicolon](https://github.com/RSS-Bridge/rss-bridge/wiki/Whitespace#add-a-new-line-at-the-end-of-a-file)
|
||||
* [Do not add whitespace at start or end of a file or end of a line](https://github.com/RSS-Bridge/rss-bridge/wiki/Whitespace#do-not-add-whitespace-at-start-or-end-of-a-file-or-end-of-a-line)
|
||||
* [Indentation](https://github.com/RSS-Bridge/rss-bridge/wiki/Indentation)
|
||||
* [Use tabs for indentation](https://github.com/RSS-Bridge/rss-bridge/wiki/Indentation#use-tabs-for-indentation)
|
||||
* [Maximum line length](https://github.com/RSS-Bridge/rss-bridge/wiki/Maximum-line-length)
|
||||
* [The maximum line length should not exceed 80 characters](https://github.com/RSS-Bridge/rss-bridge/wiki/Maximum-line-length#the-maximum-line-length-should-not-exceed-80-characters)
|
||||
* [Strings](https://github.com/RSS-Bridge/rss-bridge/wiki/Strings)
|
||||
* [Whenever possible use single quoted strings](https://github.com/RSS-Bridge/rss-bridge/wiki/Strings#whenever-possible-use-single-quote-strings)
|
||||
* [Add spaces around the concatenation operator](https://github.com/RSS-Bridge/rss-bridge/wiki/Strings#add-spaces-around-the-concatenation-operator)
|
||||
* [Use a single string instead of concatenating](https://github.com/RSS-Bridge/rss-bridge/wiki/Strings#use-a-single-string-instead-of-concatenating)
|
||||
* [Constants](https://github.com/RSS-Bridge/rss-bridge/wiki/Constants)
|
||||
* [Use UPPERCASE for constants](https://github.com/RSS-Bridge/rss-bridge/wiki/Constants#use-uppercase-for-constants)
|
||||
* [Keywords](https://github.com/RSS-Bridge/rss-bridge/wiki/Keywords)
|
||||
* [Use lowercase for `true`, `false` and `null`](https://github.com/RSS-Bridge/rss-bridge/wiki/Keywords#use-lowercase-for-true-false-and-null)
|
||||
* [Operators](https://github.com/RSS-Bridge/rss-bridge/wiki/Operators)
|
||||
* [Operators must have a space around them](https://github.com/RSS-Bridge/rss-bridge/wiki/Operators#operators-must-have-a-space-around-them)
|
||||
* [Functions](https://github.com/RSS-Bridge/rss-bridge/wiki/Functions)
|
||||
* [Parameters with default values must appear last in functions](https://github.com/RSS-Bridge/rss-bridge/wiki/Functions#parameters-with-default-values-must-appear-last-in-functions)
|
||||
* [Calling functions](https://github.com/RSS-Bridge/rss-bridge/wiki/Functions#calling-functions)
|
||||
* [Do not add spaces after opening or before closing bracket](https://github.com/RSS-Bridge/rss-bridge/wiki/Functions#do-not-add-spaces-after-opening-or-before-closing-bracket)
|
||||
* [Structures](https://github.com/RSS-Bridge/rss-bridge/wiki/Structures)
|
||||
* [Structures must always be formatted as multi-line blocks](https://github.com/RSS-Bridge/rss-bridge/wiki/Structures#structures-must-always-be-formatted-as-multi-line-blocks)
|
||||
* [If-Statement](https://github.com/RSS-Bridge/rss-bridge/wiki/if-Statement)
|
||||
* [Use `elseif` instead of `else if`](https://github.com/RSS-Bridge/rss-bridge/wiki/if-Statement#use-elseif-instead-of-else-if)
|
||||
* [Do not write empty statements](https://github.com/RSS-Bridge/rss-bridge/wiki/if-Statement#do-not-write-empty-statements)
|
||||
* [Do not write unconditional if-statements](https://github.com/RSS-Bridge/rss-bridge/wiki/if-Statement#do-not-write-unconditional-if-statements)
|
||||
* [Classes](https://github.com/RSS-Bridge/rss-bridge/wiki/Classes)
|
||||
* [Use PascalCase for class names](https://github.com/RSS-Bridge/rss-bridge/wiki/Classes#use-pascalcase-for-class-names)
|
||||
* [Do not use final statements inside final classes](https://github.com/RSS-Bridge/rss-bridge/wiki/Classes#do-not-use-final-statements-inside-final-classes)
|
||||
* [Do not override methods to call their parent](https://github.com/RSS-Bridge/rss-bridge/wiki/Classes#do-not-override-methods-to-call-their-parent)
|
||||
* [Casting](https://github.com/RSS-Bridge/rss-bridge/wiki/Casting)
|
||||
* [Do not add spaces when casting](https://github.com/RSS-Bridge/rss-bridge/wiki/Casting#do-not-add-spaces-when-casting)
|
||||
|
@@ -4,6 +4,8 @@ rss-bridge
|
||||
|
||||
RSS-Bridge is a PHP project capable of generating RSS and Atom feeds for websites which don't have one. It can be used on webservers or as stand alone application in CLI mode.
|
||||
|
||||
**Important**: RSS-Bridge is __not__ a feed reader or feed aggregator, but a tool to generate feeds that are consumed by feed readers and feed aggregators. Find a list of feed aggregators on [Wikipedia](https://en.wikipedia.org/wiki/Comparison_of_feed_aggregators).
|
||||
|
||||
Supported sites/pages (examples)
|
||||
===
|
||||
|
||||
|
@@ -8,7 +8,7 @@ class ABCTabsBridge extends BridgeAbstract {
|
||||
|
||||
public function collectData(){
|
||||
$html = '';
|
||||
$html = getSimpleHTMLDOM(static::URI.'tablatures/nouveautes.html')
|
||||
$html = getSimpleHTMLDOM(static::URI . 'tablatures/nouveautes.html')
|
||||
or returnClientError('No results for this query.');
|
||||
|
||||
$table = $html->find('table#myTable', 0)->children(1);
|
||||
|
@@ -45,7 +45,7 @@ class AllocineFRBridge extends BridgeAbstract {
|
||||
public function getName(){
|
||||
if(!is_null($this->getInput('category'))) {
|
||||
return self::NAME . ' : '
|
||||
.array_search(
|
||||
. array_search(
|
||||
$this->getInput('category'),
|
||||
self::PARAMETERS[$this->queriedContext]['category']['values']
|
||||
);
|
||||
|
@@ -52,7 +52,7 @@ class AmazonBridge extends BridgeAbstract {
|
||||
|
||||
public function getName(){
|
||||
if(!is_null($this->getInput('tld')) && !is_null($this->getInput('q'))) {
|
||||
return 'Amazon.'.$this->getInput('tld').': '.$this->getInput('q');
|
||||
return 'Amazon.' . $this->getInput('tld') . ': ' . $this->getInput('q');
|
||||
}
|
||||
|
||||
return parent::getName();
|
||||
@@ -60,8 +60,8 @@ class AmazonBridge extends BridgeAbstract {
|
||||
|
||||
public function collectData() {
|
||||
|
||||
$uri = 'https://www.amazon.'.$this->getInput('tld').'/';
|
||||
$uri .= 's/?field-keywords='.urlencode($this->getInput('q')).'&sort='.$this->getInput('sort');
|
||||
$uri = 'https://www.amazon.' . $this->getInput('tld') . '/';
|
||||
$uri .= 's/?field-keywords=' . urlencode($this->getInput('q')) . '&sort=' . $this->getInput('sort');
|
||||
|
||||
$html = getSimpleHTMLDOM($uri)
|
||||
or returnServerError('Could not request Amazon.');
|
||||
@@ -86,7 +86,7 @@ class AmazonBridge extends BridgeAbstract {
|
||||
$price = $element->find('span.s-price', 0);
|
||||
$price = ($price) ? $price->innertext : '';
|
||||
|
||||
$item['content'] = '<img src="'.$image->getAttribute('src').'" /><br />'.$price;
|
||||
$item['content'] = '<img src="' . $image->getAttribute('src') . '" /><br />' . $price;
|
||||
|
||||
$this->items[] = $item;
|
||||
}
|
||||
|
@@ -140,7 +140,7 @@ class AnidexBridge extends BridgeAbstract {
|
||||
if (strpos($link->href, '/torrent/') === 0 && !in_array($link->href, $results))
|
||||
$results[] = $link->href;
|
||||
if (empty($results) && empty($this->getInput('q')))
|
||||
returnServerError('No results from Anidex: '.$search_url);
|
||||
returnServerError('No results from Anidex: ' . $search_url);
|
||||
|
||||
//Process each item individually
|
||||
foreach ($results as $element) {
|
||||
@@ -156,7 +156,7 @@ class AnidexBridge extends BridgeAbstract {
|
||||
if ($torrent_id != 0 && ctype_digit($torrent_id)) {
|
||||
|
||||
//Retrieve data for this torrent ID
|
||||
$item_uri = self::URI . 'torrent/'.$torrent_id;
|
||||
$item_uri = self::URI . 'torrent/' . $torrent_id;
|
||||
|
||||
//Retrieve full description from torrent page
|
||||
if ($item_html = getSimpleHTMLDOMCached($item_uri)) {
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
class AskfmBridge extends BridgeAbstract {
|
||||
|
||||
const MAINTAINER = 'az5he6ch';
|
||||
const MAINTAINER = 'az5he6ch, logmanoriginal';
|
||||
const NAME = 'Ask.fm Answers';
|
||||
const URI = 'https://ask.fm/';
|
||||
const CACHE_TIMEOUT = 300; //5 min
|
||||
@@ -19,39 +19,39 @@ class AskfmBridge extends BridgeAbstract {
|
||||
$html = getSimpleHTMLDOM($this->getURI())
|
||||
or returnServerError('Requested username can\'t be found.');
|
||||
|
||||
foreach($html->find('div.streamItem-answer') as $element) {
|
||||
$html = defaultLinkTo($html, self::URI);
|
||||
|
||||
foreach($html->find('article.streamItem-answer') as $element) {
|
||||
$item = array();
|
||||
$item['uri'] = self::URI . $element->find('a.streamItemsAge', 0)->href;
|
||||
$question = trim($element->find('h1.streamItemContent-question', 0)->innertext);
|
||||
$item['uri'] = $element->find('a.streamItem_meta', 0)->href;
|
||||
$question = trim($element->find('header.streamItem_header', 0)->innertext);
|
||||
|
||||
$item['title'] = trim(
|
||||
htmlspecialchars_decode($element->find('h1.streamItemContent-question', 0)->plaintext,
|
||||
htmlspecialchars_decode($element->find('header.streamItem_header', 0)->plaintext,
|
||||
ENT_QUOTES
|
||||
)
|
||||
);
|
||||
|
||||
$answer = trim($element->find('p.streamItemContent-answer', 0)->innertext);
|
||||
$item['timestamp'] = strtotime($element->find('time', 0)->datetime);
|
||||
|
||||
// Doesn't work, DOM parser doesn't seem to like data-hint, dunno why
|
||||
#$item['update'] = $element->find('a.streamitemsage',0)->data-hint;
|
||||
$answer = trim($element->find('div.streamItem_content', 0)->innertext);
|
||||
|
||||
// This probably should be cleaned up, especially for YouTube embeds
|
||||
$visual = $element->find('div.streamItemContent-visual', 0)->innertext;
|
||||
//Fix tracking links, also doesn't work
|
||||
if($visual = $element->find('div.streamItem_visual', 0)) {
|
||||
$visual = $visual->innertext;
|
||||
}
|
||||
|
||||
// Fix tracking links, also doesn't work
|
||||
foreach($element->find('a') as $link) {
|
||||
if(strpos($link->href, 'l.ask.fm') !== false) {
|
||||
|
||||
// Too slow
|
||||
#$link->href = str_replace('#_=_', '', get_headers($link->href, 1)['Location']);
|
||||
|
||||
$link->href = $link->plaintext;
|
||||
}
|
||||
}
|
||||
|
||||
$content = '<p>' . $question . '</p><p>' . $answer . '</p><p>' . $visual . '</p>';
|
||||
// Fix relative links without breaking // scheme used by YouTube stuff
|
||||
$content = preg_replace('#href="\/(?!\/)#', 'href="' . self::URI, $content);
|
||||
$item['content'] = $content;
|
||||
$item['content'] = '<p>' . $question
|
||||
. '</p><p>' . $answer
|
||||
. '</p><p>' . $visual . '</p>';
|
||||
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
@@ -66,7 +66,7 @@ class AskfmBridge extends BridgeAbstract {
|
||||
|
||||
public function getURI(){
|
||||
if(!is_null($this->getInput('u'))) {
|
||||
return self::URI . urlencode($this->getInput('u')) . '/answers/more?page=0';
|
||||
return self::URI . urlencode($this->getInput('u'));
|
||||
}
|
||||
|
||||
return parent::getURI();
|
||||
|
@@ -19,6 +19,10 @@ class AutoJMBridge extends BridgeAbstract {
|
||||
);
|
||||
const CACHE_TIMEOUT = 3600;
|
||||
|
||||
public function getIcon() {
|
||||
return self::URI . 'assets/images/favicon.ico';
|
||||
}
|
||||
|
||||
public function collectData() {
|
||||
$html = getSimpleHTMLDOM(self::URI . $this->getInput('url'))
|
||||
or returnServerError('Could not request AutoJM.');
|
||||
@@ -43,7 +47,7 @@ class AutoJMBridge extends BridgeAbstract {
|
||||
$item = array();
|
||||
$item['uri'] = $url;
|
||||
$item['title'] = $serie;
|
||||
$item['content'] = '<p><img style="vertical-align:middle ; padding: 10px" src="' . $image . '" />'. $serie . '</p>';
|
||||
$item['content'] = '<p><img style="vertical-align:middle ; padding: 10px" src="' . $image . '" />' . $serie . '</p>';
|
||||
$item['content'] .= '<ul><li>Disponibilité : ' . $dispo . '</li>';
|
||||
$item['content'] .= '<li>Carburant : ' . $carburant . '</li>';
|
||||
$item['content'] .= '<li>Transmission : ' . $transmission . '</li>';
|
||||
@@ -59,4 +63,3 @@ class AutoJMBridge extends BridgeAbstract {
|
||||
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
265
bridges/BAEBridge.php
Normal file
265
bridges/BAEBridge.php
Normal file
@@ -0,0 +1,265 @@
|
||||
<?php
|
||||
class BAEBridge extends BridgeAbstract {
|
||||
const MAINTAINER = 'couraudt';
|
||||
const NAME = 'Bourse Aux Equipiers Bridge';
|
||||
const URI = 'https://www.bourse-aux-equipiers.com';
|
||||
const DESCRIPTION = 'Returns the newest sailing offers.';
|
||||
const PARAMETERS = array(
|
||||
array(
|
||||
'keyword' => array(
|
||||
'name' => 'Filtrer par mots clés',
|
||||
'title' => 'Entrez le mot clé à filtrer ici'
|
||||
),
|
||||
'type' => array(
|
||||
'name' => 'Type de recherche',
|
||||
'title' => 'Afficher seuleument un certain type d\'annonce',
|
||||
'type' => 'list',
|
||||
'values' => array(
|
||||
'Toutes les annonces' => false,
|
||||
'Les embarquements' => 'boat',
|
||||
'Les skippers' => 'skipper',
|
||||
'Les équipiers' => 'crew'
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
public function collectData() {
|
||||
$url = $this->getURI();
|
||||
$html = getSimpleHTMLDOM($url) or returnClientError('No results for this query.');
|
||||
|
||||
$annonces = $html->find('main article');
|
||||
foreach ($annonces as $annonce) {
|
||||
$detail = $annonce->find('footer a', 0);
|
||||
|
||||
$htmlDetail = getSimpleHTMLDOMCached(parent::getURI() . $detail->href);
|
||||
if (!$htmlDetail)
|
||||
continue;
|
||||
|
||||
$item = array();
|
||||
|
||||
$item['title'] = $annonce->find('header h2', 0)->plaintext;
|
||||
$item['uri'] = parent::getURI() . $detail->href;
|
||||
|
||||
$content = $htmlDetail->find('article p', 0)->innertext;
|
||||
if (!empty($this->getInput('keyword'))) {
|
||||
$keyword = $this->remove_accents(strtolower($this->getInput('keyword')));
|
||||
$cleanTitle = $this->remove_accents(strtolower($item['title']));
|
||||
if (strpos($cleanTitle, $keyword) === false) {
|
||||
$cleanContent = $this->remove_accents(strtolower($content));
|
||||
if (strpos($cleanContent, $keyword) === false) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$content .= '<hr>';
|
||||
$content .= $htmlDetail->find('section', 0)->innertext;
|
||||
$content = str_replace('src="/', 'src="' . parent::getURI() . '/', $content);
|
||||
$content = str_replace('href="/', 'href="' . parent::getURI() . '/', $content);
|
||||
$item['content'] = $content;
|
||||
$image = $htmlDetail->find('#zoom', 0);
|
||||
if ($image) {
|
||||
$item['enclosures'] = array(parent::getURI() . $image->getAttribute('src'));
|
||||
}
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
public function getURI() {
|
||||
$uri = parent::getURI();
|
||||
if (!empty($this->getInput('type'))) {
|
||||
if ($this->getInput('type') == 'boat') {
|
||||
$uri .= '/embarquements.html';
|
||||
} elseif ($this->getInput('type') == 'skipper') {
|
||||
$uri .= '/skippers.html';
|
||||
} else {
|
||||
$uri .= '/equipiers.html';
|
||||
}
|
||||
}
|
||||
|
||||
return $uri;
|
||||
}
|
||||
|
||||
private function remove_accents($string) {
|
||||
$chars = array(
|
||||
// Decompositions for Latin-1 Supplement
|
||||
'ª' => 'a', 'º' => 'o',
|
||||
'À' => 'A', 'Á' => 'A',
|
||||
'Â' => 'A', 'Ã' => 'A',
|
||||
'Ä' => 'A', 'Å' => 'A',
|
||||
'Æ' => 'AE', 'Ç' => 'C',
|
||||
'È' => 'E', 'É' => 'E',
|
||||
'Ê' => 'E', 'Ë' => 'E',
|
||||
'Ì' => 'I', 'Í' => 'I',
|
||||
'Î' => 'I', 'Ï' => 'I',
|
||||
'Ð' => 'D', 'Ñ' => 'N',
|
||||
'Ò' => 'O', 'Ó' => 'O',
|
||||
'Ô' => 'O', 'Õ' => 'O',
|
||||
'Ö' => 'O', 'Ù' => 'U',
|
||||
'Ú' => 'U', 'Û' => 'U',
|
||||
'Ü' => 'U', 'Ý' => 'Y',
|
||||
'Þ' => 'TH', 'ß' => 's',
|
||||
'à' => 'a', 'á' => 'a',
|
||||
'â' => 'a', 'ã' => 'a',
|
||||
'ä' => 'a', 'å' => 'a',
|
||||
'æ' => 'ae', 'ç' => 'c',
|
||||
'è' => 'e', 'é' => 'e',
|
||||
'ê' => 'e', 'ë' => 'e',
|
||||
'ì' => 'i', 'í' => 'i',
|
||||
'î' => 'i', 'ï' => 'i',
|
||||
'ð' => 'd', 'ñ' => 'n',
|
||||
'ò' => 'o', 'ó' => 'o',
|
||||
'ô' => 'o', 'õ' => 'o',
|
||||
'ö' => 'o', 'ø' => 'o',
|
||||
'ù' => 'u', 'ú' => 'u',
|
||||
'û' => 'u', 'ü' => 'u',
|
||||
'ý' => 'y', 'þ' => 'th',
|
||||
'ÿ' => 'y', 'Ø' => 'O',
|
||||
// Decompositions for Latin Extended-A
|
||||
'Ā' => 'A', 'ā' => 'a',
|
||||
'Ă' => 'A', 'ă' => 'a',
|
||||
'Ą' => 'A', 'ą' => 'a',
|
||||
'Ć' => 'C', 'ć' => 'c',
|
||||
'Ĉ' => 'C', 'ĉ' => 'c',
|
||||
'Ċ' => 'C', 'ċ' => 'c',
|
||||
'Č' => 'C', 'č' => 'c',
|
||||
'Ď' => 'D', 'ď' => 'd',
|
||||
'Đ' => 'D', 'đ' => 'd',
|
||||
'Ē' => 'E', 'ē' => 'e',
|
||||
'Ĕ' => 'E', 'ĕ' => 'e',
|
||||
'Ė' => 'E', 'ė' => 'e',
|
||||
'Ę' => 'E', 'ę' => 'e',
|
||||
'Ě' => 'E', 'ě' => 'e',
|
||||
'Ĝ' => 'G', 'ĝ' => 'g',
|
||||
'Ğ' => 'G', 'ğ' => 'g',
|
||||
'Ġ' => 'G', 'ġ' => 'g',
|
||||
'Ģ' => 'G', 'ģ' => 'g',
|
||||
'Ĥ' => 'H', 'ĥ' => 'h',
|
||||
'Ħ' => 'H', 'ħ' => 'h',
|
||||
'Ĩ' => 'I', 'ĩ' => 'i',
|
||||
'Ī' => 'I', 'ī' => 'i',
|
||||
'Ĭ' => 'I', 'ĭ' => 'i',
|
||||
'Į' => 'I', 'į' => 'i',
|
||||
'İ' => 'I', 'ı' => 'i',
|
||||
'IJ' => 'IJ', 'ij' => 'ij',
|
||||
'Ĵ' => 'J', 'ĵ' => 'j',
|
||||
'Ķ' => 'K', 'ķ' => 'k',
|
||||
'ĸ' => 'k', 'Ĺ' => 'L',
|
||||
'ĺ' => 'l', 'Ļ' => 'L',
|
||||
'ļ' => 'l', 'Ľ' => 'L',
|
||||
'ľ' => 'l', 'Ŀ' => 'L',
|
||||
'ŀ' => 'l', 'Ł' => 'L',
|
||||
'ł' => 'l', 'Ń' => 'N',
|
||||
'ń' => 'n', 'Ņ' => 'N',
|
||||
'ņ' => 'n', 'Ň' => 'N',
|
||||
'ň' => 'n', 'ʼn' => 'n',
|
||||
'Ŋ' => 'N', 'ŋ' => 'n',
|
||||
'Ō' => 'O', 'ō' => 'o',
|
||||
'Ŏ' => 'O', 'ŏ' => 'o',
|
||||
'Ő' => 'O', 'ő' => 'o',
|
||||
'Œ' => 'OE', 'œ' => 'oe',
|
||||
'Ŕ' => 'R', 'ŕ' => 'r',
|
||||
'Ŗ' => 'R', 'ŗ' => 'r',
|
||||
'Ř' => 'R', 'ř' => 'r',
|
||||
'Ś' => 'S', 'ś' => 's',
|
||||
'Ŝ' => 'S', 'ŝ' => 's',
|
||||
'Ş' => 'S', 'ş' => 's',
|
||||
'Š' => 'S', 'š' => 's',
|
||||
'Ţ' => 'T', 'ţ' => 't',
|
||||
'Ť' => 'T', 'ť' => 't',
|
||||
'Ŧ' => 'T', 'ŧ' => 't',
|
||||
'Ũ' => 'U', 'ũ' => 'u',
|
||||
'Ū' => 'U', 'ū' => 'u',
|
||||
'Ŭ' => 'U', 'ŭ' => 'u',
|
||||
'Ů' => 'U', 'ů' => 'u',
|
||||
'Ű' => 'U', 'ű' => 'u',
|
||||
'Ų' => 'U', 'ų' => 'u',
|
||||
'Ŵ' => 'W', 'ŵ' => 'w',
|
||||
'Ŷ' => 'Y', 'ŷ' => 'y',
|
||||
'Ÿ' => 'Y', 'Ź' => 'Z',
|
||||
'ź' => 'z', 'Ż' => 'Z',
|
||||
'ż' => 'z', 'Ž' => 'Z',
|
||||
'ž' => 'z', 'ſ' => 's',
|
||||
// Decompositions for Latin Extended-B
|
||||
'Ș' => 'S', 'ș' => 's',
|
||||
'Ț' => 'T', 'ț' => 't',
|
||||
// Euro Sign
|
||||
'€' => 'E',
|
||||
// GBP (Pound) Sign
|
||||
'£' => '',
|
||||
// Vowels with diacritic (Vietnamese)
|
||||
// unmarked
|
||||
'Ơ' => 'O', 'ơ' => 'o',
|
||||
'Ư' => 'U', 'ư' => 'u',
|
||||
// grave accent
|
||||
'Ầ' => 'A', 'ầ' => 'a',
|
||||
'Ằ' => 'A', 'ằ' => 'a',
|
||||
'Ề' => 'E', 'ề' => 'e',
|
||||
'Ồ' => 'O', 'ồ' => 'o',
|
||||
'Ờ' => 'O', 'ờ' => 'o',
|
||||
'Ừ' => 'U', 'ừ' => 'u',
|
||||
'Ỳ' => 'Y', 'ỳ' => 'y',
|
||||
// hook
|
||||
'Ả' => 'A', 'ả' => 'a',
|
||||
'Ẩ' => 'A', 'ẩ' => 'a',
|
||||
'Ẳ' => 'A', 'ẳ' => 'a',
|
||||
'Ẻ' => 'E', 'ẻ' => 'e',
|
||||
'Ể' => 'E', 'ể' => 'e',
|
||||
'Ỉ' => 'I', 'ỉ' => 'i',
|
||||
'Ỏ' => 'O', 'ỏ' => 'o',
|
||||
'Ổ' => 'O', 'ổ' => 'o',
|
||||
'Ở' => 'O', 'ở' => 'o',
|
||||
'Ủ' => 'U', 'ủ' => 'u',
|
||||
'Ử' => 'U', 'ử' => 'u',
|
||||
'Ỷ' => 'Y', 'ỷ' => 'y',
|
||||
// tilde
|
||||
'Ẫ' => 'A', 'ẫ' => 'a',
|
||||
'Ẵ' => 'A', 'ẵ' => 'a',
|
||||
'Ẽ' => 'E', 'ẽ' => 'e',
|
||||
'Ễ' => 'E', 'ễ' => 'e',
|
||||
'Ỗ' => 'O', 'ỗ' => 'o',
|
||||
'Ỡ' => 'O', 'ỡ' => 'o',
|
||||
'Ữ' => 'U', 'ữ' => 'u',
|
||||
'Ỹ' => 'Y', 'ỹ' => 'y',
|
||||
// acute accent
|
||||
'Ấ' => 'A', 'ấ' => 'a',
|
||||
'Ắ' => 'A', 'ắ' => 'a',
|
||||
'Ế' => 'E', 'ế' => 'e',
|
||||
'Ố' => 'O', 'ố' => 'o',
|
||||
'Ớ' => 'O', 'ớ' => 'o',
|
||||
'Ứ' => 'U', 'ứ' => 'u',
|
||||
// dot below
|
||||
'Ạ' => 'A', 'ạ' => 'a',
|
||||
'Ậ' => 'A', 'ậ' => 'a',
|
||||
'Ặ' => 'A', 'ặ' => 'a',
|
||||
'Ẹ' => 'E', 'ẹ' => 'e',
|
||||
'Ệ' => 'E', 'ệ' => 'e',
|
||||
'Ị' => 'I', 'ị' => 'i',
|
||||
'Ọ' => 'O', 'ọ' => 'o',
|
||||
'Ộ' => 'O', 'ộ' => 'o',
|
||||
'Ợ' => 'O', 'ợ' => 'o',
|
||||
'Ụ' => 'U', 'ụ' => 'u',
|
||||
'Ự' => 'U', 'ự' => 'u',
|
||||
'Ỵ' => 'Y', 'ỵ' => 'y',
|
||||
// Vowels with diacritic (Chinese, Hanyu Pinyin)
|
||||
'ɑ' => 'a',
|
||||
// macron
|
||||
'Ǖ' => 'U', 'ǖ' => 'u',
|
||||
// acute accent
|
||||
'Ǘ' => 'U', 'ǘ' => 'u',
|
||||
// caron
|
||||
'Ǎ' => 'A', 'ǎ' => 'a',
|
||||
'Ǐ' => 'I', 'ǐ' => 'i',
|
||||
'Ǒ' => 'O', 'ǒ' => 'o',
|
||||
'Ǔ' => 'U', 'ǔ' => 'u',
|
||||
'Ǚ' => 'U', 'ǚ' => 'u',
|
||||
// grave accent
|
||||
'Ǜ' => 'U', 'ǜ' => 'u',
|
||||
);
|
||||
|
||||
$string = strtr($string, $chars);
|
||||
|
||||
return $string;
|
||||
}
|
||||
}
|
@@ -14,6 +14,10 @@ class BandcampBridge extends BridgeAbstract {
|
||||
)
|
||||
));
|
||||
|
||||
public function getIcon() {
|
||||
return 'https://s4.bcbits.com/img/bc_favicon.ico';
|
||||
}
|
||||
|
||||
public function collectData(){
|
||||
$html = getSimpleHTMLDOM($this->getURI())
|
||||
or returnServerError('No results for this query.');
|
||||
|
@@ -7,6 +7,10 @@ class BlaguesDeMerdeBridge extends BridgeAbstract {
|
||||
const CACHE_TIMEOUT = 7200; // 2h
|
||||
const DESCRIPTION = 'Blagues De Merde';
|
||||
|
||||
public function getIcon() {
|
||||
return self::URI . 'assets/img/favicon.ico';
|
||||
}
|
||||
|
||||
public function collectData(){
|
||||
|
||||
$html = getSimpleHTMLDOM(self::URI)
|
||||
|
@@ -31,6 +31,10 @@ class BloombergBridge extends BridgeAbstract
|
||||
return parent::getName();
|
||||
}
|
||||
|
||||
public function getIcon() {
|
||||
return 'https://assets.bwbx.io/s3/javelin/public/hub/images/favicon-black-63fe5249d3.png';
|
||||
}
|
||||
|
||||
public function collectData()
|
||||
{
|
||||
switch($this->queriedContext) {
|
||||
|
@@ -27,6 +27,10 @@ class BundesbankBridge extends BridgeAbstract {
|
||||
)
|
||||
);
|
||||
|
||||
public function getIcon() {
|
||||
return self::URI . 'resource/crblob/1890/a7f48ee0ae35348748121770ba3ca009/mL/favicon-ico-data.ico';
|
||||
}
|
||||
|
||||
public function getURI() {
|
||||
switch($this->getInput(self::PARAM_LANG)) {
|
||||
case self::LANG_EN: return self::URI . 'en/publications/reports/studies';
|
||||
|
@@ -52,9 +52,9 @@ class CNETBridge extends BridgeAbstract {
|
||||
returnClientError('Invalid topic: ' . $topic);
|
||||
|
||||
// Retrieve webpage
|
||||
$pageUrl = self::URI . (empty($topic) ? 'news/' : $topic.'/');
|
||||
$pageUrl = self::URI . (empty($topic) ? 'news/' : $topic . '/');
|
||||
$html = getSimpleHTMLDOM($pageUrl)
|
||||
or returnServerError('Could not request CNET: '.$pageUrl);
|
||||
or returnServerError('Could not request CNET: ' . $pageUrl);
|
||||
|
||||
// Process articles
|
||||
foreach($html->find('div.assetBody, div.riverPost') as $element) {
|
||||
|
@@ -7,6 +7,9 @@ class ChristianDailyReporterBridge extends BridgeAbstract {
|
||||
const DESCRIPTION = 'The Unofficial Christian Daily Reporter RSS';
|
||||
// const CACHE_TIMEOUT = 86400; // 1 day
|
||||
|
||||
public function getIcon() {
|
||||
return self::URI . 'images/cdrfavicon.png';
|
||||
}
|
||||
|
||||
public function collectData() {
|
||||
$uri = 'https://www.christiandailyreporter.com/';
|
||||
|
@@ -32,6 +32,10 @@ class ContainerLinuxReleasesBridge extends BridgeAbstract {
|
||||
return json_decode($json, true);
|
||||
}
|
||||
|
||||
public function getIcon() {
|
||||
return 'https://coreos.com/assets/ico/favicon.png';
|
||||
}
|
||||
|
||||
public function collectData() {
|
||||
$data = $this->getReleaseFeed($this->getJsonUri());
|
||||
|
||||
|
@@ -48,6 +48,10 @@ class DailymotionBridge extends BridgeAbstract {
|
||||
return $metadata;
|
||||
}
|
||||
|
||||
public function getIcon() {
|
||||
return 'https://static1-ssl.dmcdn.net/images/neon/favicons/android-icon-36x36.png.vf806ca4ed0deed812';
|
||||
}
|
||||
|
||||
public function collectData(){
|
||||
$html = '';
|
||||
$limit = 5;
|
||||
|
@@ -1074,10 +1074,10 @@ class PepperBridgeAbstract extends BridgeAbstract {
|
||||
$url = $this->i8n('bridge-uri')
|
||||
. '/search/advanced?q='
|
||||
. urlencode($q)
|
||||
. '&hide_expired='. $hide_expired
|
||||
. '&hide_local='. $hide_local
|
||||
. '&priceFrom='. $priceFrom
|
||||
. '&priceTo='. $priceTo
|
||||
. '&hide_expired=' . $hide_expired
|
||||
. '&hide_local=' . $hide_local
|
||||
. '&priceFrom=' . $priceFrom
|
||||
. '&priceTo=' . $priceTo
|
||||
/* Some default parameters
|
||||
* search_fields : Search in Titres & Descriptions & Codes
|
||||
* sort_by : Sort the search by new deals
|
||||
@@ -1152,30 +1152,30 @@ class PepperBridgeAbstract extends BridgeAbstract {
|
||||
foreach ($list as $deal) {
|
||||
$item = array();
|
||||
$item['uri'] = $deal->find('div[class=threadGrid-title]', 0)->find('a', 0)->href;
|
||||
$item['title'] = $deal->find('a[class*='. $selectorLink .']', 0
|
||||
$item['title'] = $deal->find('a[class*=' . $selectorLink . ']', 0
|
||||
)->plaintext;
|
||||
$item['author'] = $deal->find('span.thread-username', 0)->plaintext;
|
||||
$item['content'] = '<table><tr><td><a href="'
|
||||
. $deal->find(
|
||||
'a[class*='. $selectorImageLink .']', 0)->href
|
||||
'a[class*=' . $selectorImageLink . ']', 0)->href
|
||||
. '"><img src="'
|
||||
. $this->getImage($deal)
|
||||
. '"/></td><td><h2><a href="'
|
||||
. $deal->find('a[class*='. $selectorLink .']', 0)->href
|
||||
. $deal->find('a[class*=' . $selectorLink . ']', 0)->href
|
||||
. '">'
|
||||
. $deal->find('a[class*='. $selectorLink .']', 0)->innertext
|
||||
. $deal->find('a[class*=' . $selectorLink . ']', 0)->innertext
|
||||
. '</a></h2>'
|
||||
. $this->getPrice($deal)
|
||||
. $this->getDiscount($deal)
|
||||
. $this->getShipsFrom($deal)
|
||||
. $this->getShippingCost($deal)
|
||||
. $this->GetSource($deal)
|
||||
. $deal->find('div[class*='. $selectorDescription .']', 0)->innertext
|
||||
. $deal->find('div[class*=' . $selectorDescription . ']', 0)->innertext
|
||||
. '</td><td>'
|
||||
. $deal->find('div[class*='. $selectorHot .']', 0)
|
||||
. $deal->find('div[class*=' . $selectorHot . ']', 0)
|
||||
->find('span', 1)->outertext
|
||||
. '</td></table>';
|
||||
$dealDateDiv = $deal->find('div[class*='. $selectorDate .']', 0)
|
||||
$dealDateDiv = $deal->find('div[class*=' . $selectorDate . ']', 0)
|
||||
->find('span[class=hide--toW3]');
|
||||
$itemDate = end($dealDateDiv)->plaintext;
|
||||
// In case of a Local deal, there is no date, but we can use
|
||||
@@ -1214,7 +1214,7 @@ class PepperBridgeAbstract extends BridgeAbstract {
|
||||
{
|
||||
if ($deal->find(
|
||||
'span[class*=thread-price]', 0) != null) {
|
||||
return '<div>'.$this->i8n('price') .' : '
|
||||
return '<div>' . $this->i8n('price') . ' : '
|
||||
. $deal->find(
|
||||
'span[class*=thread-price]', 0
|
||||
)->plaintext
|
||||
@@ -1233,11 +1233,11 @@ class PepperBridgeAbstract extends BridgeAbstract {
|
||||
{
|
||||
if ($deal->find('span[class*=cept-shipping-price]', 0) != null) {
|
||||
if ($deal->find('span[class*=cept-shipping-price]', 0)->children(0) != null) {
|
||||
return '<div>'. $this->i8n('shipping') .' : '
|
||||
return '<div>' . $this->i8n('shipping') . ' : '
|
||||
. $deal->find('span[class*=cept-shipping-price]', 0)->children(0)->innertext
|
||||
. '</div>';
|
||||
} else {
|
||||
return '<div>'. $this->i8n('shipping') .' : '
|
||||
return '<div>' . $this->i8n('shipping') . ' : '
|
||||
. $deal->find('span[class*=cept-shipping-price]', 0)->innertext
|
||||
. '</div>';
|
||||
}
|
||||
@@ -1253,7 +1253,7 @@ class PepperBridgeAbstract extends BridgeAbstract {
|
||||
private function GetSource($deal)
|
||||
{
|
||||
if ($deal->find('a[class=text--color-greyShade]', 0) != null) {
|
||||
return '<div>'. $this->i8n('origin') .' : '
|
||||
return '<div>' . $this->i8n('origin') . ' : '
|
||||
. $deal->find('a[class=text--color-greyShade]', 0)->outertext
|
||||
. '</div>';
|
||||
} else {
|
||||
@@ -1274,7 +1274,7 @@ class PepperBridgeAbstract extends BridgeAbstract {
|
||||
} else {
|
||||
$discount = '';
|
||||
}
|
||||
return '<div>'. $this->i8n('discount') .' : <span style="text-decoration: line-through;">'
|
||||
return '<div>' . $this->i8n('discount') . ' : <span style="text-decoration: line-through;">'
|
||||
. $deal->find(
|
||||
'span[class*=mute--text text--lineThrough]', 0
|
||||
)->plaintext
|
||||
@@ -1315,13 +1315,13 @@ class PepperBridgeAbstract extends BridgeAbstract {
|
||||
'cept-thread-img'
|
||||
)
|
||||
);
|
||||
if ($deal->find('img[class='. $selectorLazy .']', 0) != null) {
|
||||
if ($deal->find('img[class=' . $selectorLazy . ']', 0) != null) {
|
||||
return json_decode(
|
||||
html_entity_decode(
|
||||
$deal->find('img[class='. $selectorLazy .']', 0)
|
||||
$deal->find('img[class=' . $selectorLazy . ']', 0)
|
||||
->getAttribute('data-lazy-img')))->{'src'};
|
||||
} else {
|
||||
return $deal->find('img[class*='. $selectorPlain .']', 0 )->src;
|
||||
return $deal->find('img[class*=' . $selectorPlain . ']', 0 )->src;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1340,9 +1340,9 @@ class PepperBridgeAbstract extends BridgeAbstract {
|
||||
'text--color-greyShade'
|
||||
)
|
||||
);
|
||||
if ($deal->find('span[class='. $selector .']', 0) != null) {
|
||||
if ($deal->find('span[class=' . $selector . ']', 0) != null) {
|
||||
return '<div>'
|
||||
. $deal->find('span[class='. $selector .']', 0)->children(2)->plaintext
|
||||
. $deal->find('span[class=' . $selector . ']', 0)->children(2)->plaintext
|
||||
. '</div>';
|
||||
} else {
|
||||
return '';
|
||||
@@ -1445,12 +1445,12 @@ class PepperBridgeAbstract extends BridgeAbstract {
|
||||
public function getName(){
|
||||
switch($this->queriedContext) {
|
||||
case $this->i8n('context-keyword'):
|
||||
return $this->i8n('bridge-name') . ' - '. $this->i8n('title-keyword') .' : '. $this->getInput('q');
|
||||
return $this->i8n('bridge-name') . ' - ' . $this->i8n('title-keyword') . ' : ' . $this->getInput('q');
|
||||
break;
|
||||
case $this->i8n('context-group'):
|
||||
$values = $this->getParameters()[$this->i8n('context-group')]['group']['values'];
|
||||
$group = array_search($this->getInput('group'), $values);
|
||||
return $this->i8n('bridge-name') . ' - '. $this->i8n('title-group'). ' : '. $group;
|
||||
return $this->i8n('bridge-name') . ' - ' . $this->i8n('title-group') . ' : ' . $group;
|
||||
break;
|
||||
default: // Return default value
|
||||
return static::NAME;
|
||||
|
@@ -75,6 +75,10 @@ class DiceBridge extends BridgeAbstract {
|
||||
),
|
||||
));
|
||||
|
||||
public function getIcon() {
|
||||
return 'https://assets.dice.com/techpro/img/favicons/favicon.ico';
|
||||
}
|
||||
|
||||
public function collectData() {
|
||||
$uri = 'https://www.dice.com/jobs/advancedResult.html';
|
||||
$uri .= '?for_one=' . urlencode($this->getInput('for_one'));
|
||||
|
@@ -81,7 +81,7 @@ class DiscogsBridge extends BridgeAbstract {
|
||||
. $this->getInput('username_folder')
|
||||
. '/collection/folders/'
|
||||
. $this->getInput('folderid')
|
||||
.'/releases?sort=added&sort_order=desc')
|
||||
. '/releases?sort=added&sort_order=desc')
|
||||
or returnServerError('Unable to query discogs !');
|
||||
$jsonData = json_decode($data, true)['releases'];
|
||||
}
|
||||
|
@@ -7,6 +7,11 @@ class DribbbleBridge extends BridgeAbstract {
|
||||
const CACHE_TIMEOUT = 1800;
|
||||
const DESCRIPTION = 'Returns the newest popular shots from Dribbble.';
|
||||
|
||||
public function getIcon() {
|
||||
return 'https://cdn.dribbble.com/assets/
|
||||
favicon-63b2904a073c89b52b19aa08cebc16a154bcf83fee8ecc6439968b1e6db569c7.ico';
|
||||
}
|
||||
|
||||
public function collectData(){
|
||||
$html = getSimpleHTMLDOM(self::URI . '/shots')
|
||||
or returnServerError('Error while downloading the website content');
|
||||
|
@@ -99,7 +99,7 @@ class ETTVBridge extends BridgeAbstract {
|
||||
public function collectData(){
|
||||
// No control on inputs, because all defaultValue are set
|
||||
$query_str = 'torrents-search.php';
|
||||
$query_str .= '?search=' . urlencode('+'.str_replace(' ', ' +', $this->getInput('query')));
|
||||
$query_str .= '?search=' . urlencode('+' . str_replace(' ', ' +', $this->getInput('query')));
|
||||
$query_str .= '&cat=' . $this->getInput('cat');
|
||||
$query_str .= '&incldead=' . $this->getInput('status');
|
||||
$query_str .= '&lang=' . $this->getInput('lang');
|
||||
|
@@ -7,6 +7,11 @@ class EliteDangerousGalnetBridge extends BridgeAbstract {
|
||||
const CACHE_TIMEOUT = 7200; // 2h
|
||||
const DESCRIPTION = 'Returns the latest page of news from Galnet';
|
||||
|
||||
public function getIcon() {
|
||||
return 'https://community.elitedangerous.com/sites/
|
||||
EDSITE_COMM/themes/bootstrap/bootstrap_community/favicon.ico';
|
||||
}
|
||||
|
||||
public function collectData(){
|
||||
$html = getSimpleHTMLDOM(self::URI)
|
||||
or returnServerError('Error while downloading the website content');
|
||||
|
@@ -57,6 +57,10 @@ class ElsevierBridge extends BridgeAbstract {
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getIcon() {
|
||||
return 'https://cdn.elsevier.io/verona/includes/favicons/favicon-32x32.png';
|
||||
}
|
||||
|
||||
public function collectData(){
|
||||
$uri = self::URI . $this->getInput('j') . '/recent-articles/';
|
||||
$html = getSimpleHTMLDOM($uri)
|
||||
|
@@ -17,7 +17,7 @@ class EtsyBridge extends BridgeAbstract {
|
||||
'queryextension' => array(
|
||||
'name' => 'Query extension',
|
||||
'type' => 'text',
|
||||
'requied' => false,
|
||||
'required' => false,
|
||||
'title' => 'Insert additional query parts here
|
||||
(anything after ?search=<your search query>)',
|
||||
'exampleValue' => '&explicit=1&locationQuery=2921044'
|
||||
@@ -25,9 +25,9 @@ class EtsyBridge extends BridgeAbstract {
|
||||
'showimage' => array(
|
||||
'name' => 'Show image in content',
|
||||
'type' => 'checkbox',
|
||||
'requrired' => false,
|
||||
'required' => false,
|
||||
'title' => 'Activate to show the image in the content',
|
||||
'defaultValue' => false
|
||||
'defaultValue' => 'checked'
|
||||
)
|
||||
)
|
||||
);
|
||||
@@ -36,26 +36,27 @@ class EtsyBridge extends BridgeAbstract {
|
||||
$html = getSimpleHTMLDOM($this->getURI())
|
||||
or returnServerError('Failed to receive ' . $this->getURI());
|
||||
|
||||
$results = $html->find('div.block-grid-item');
|
||||
$results = $html->find('li.block-grid-item');
|
||||
|
||||
foreach($results as $result) {
|
||||
// Skip banner cards (ads for categories)
|
||||
if($result->find('a.banner-card'))
|
||||
if($result->find('span.ad-indicator'))
|
||||
continue;
|
||||
|
||||
$item = array();
|
||||
|
||||
$item['title'] = $result->find('a', 0)->title;
|
||||
$item['uri'] = $result->find('a', 0)->href;
|
||||
$item['author'] = $result->find('div.card-shop-name', 0)->plaintext;
|
||||
$item['author'] = $result->find('p.text-gray-lighter', 0)->plaintext;
|
||||
|
||||
$item['content'] = '<p>'
|
||||
. $result->find('div.card-price', 0)->plaintext
|
||||
. $result->find('span.currency-value', 0)->plaintext . ' '
|
||||
. $result->find('span.currency-symbol', 0)->plaintext
|
||||
. '</p><p>'
|
||||
. $result->find('div.card-title', 0)->plaintext
|
||||
. $result->find('a', 0)->title
|
||||
. '</p>';
|
||||
|
||||
$image = $result->find('img.placeholder', 0)->src;
|
||||
$image = $result->find('img.display-block', 0)->src;
|
||||
|
||||
if($this->getInput('showimage')) {
|
||||
$item['content'] .= '<img src="' . $image . '">';
|
||||
|
@@ -15,6 +15,10 @@ class FB2Bridge extends BridgeAbstract {
|
||||
)
|
||||
));
|
||||
|
||||
public function getIcon() {
|
||||
return 'https://static.xx.fbcdn.net/rsrc.php/yo/r/iRmz9lCMBD2.ico';
|
||||
}
|
||||
|
||||
public function collectData(){
|
||||
|
||||
//Utility function for cleaning a Facebook link
|
||||
@@ -65,14 +69,14 @@ class FB2Bridge extends BridgeAbstract {
|
||||
if($this->getInput('u') !== null) {
|
||||
$page = 'https://touch.facebook.com/' . $this->getInput('u');
|
||||
$cookies = $this->getCookies($page);
|
||||
$pageID = $this->getPageID($page, $cookies);
|
||||
$pageInfo = $this->getPageInfos($page, $cookies);
|
||||
|
||||
if($pageID === null) {
|
||||
if($pageInfo['userId'] === null) {
|
||||
echo <<<EOD
|
||||
Unable to get the page id. You should consider getting the ID by hand, then importing it into FB2Bridge
|
||||
EOD;
|
||||
die();
|
||||
} elseif($pageID == -1) {
|
||||
} elseif($pageInfo['userId'] == -1) {
|
||||
echo <<<EOD
|
||||
This page is not accessible without being logged in.
|
||||
EOD;
|
||||
@@ -81,24 +85,31 @@ EOD;
|
||||
}
|
||||
|
||||
//Build the string for the first request
|
||||
$requestString = 'https://touch.facebook.com/pages_reaction_units/more/?page_id='
|
||||
. $pageID
|
||||
. '&cursor={"card_id"%3A"videos"%2C"has_next_page"%3Atrue}&surface=mobile_page_home&unit_count=8';
|
||||
|
||||
$requestString = 'https://touch.facebook.com/page_content_list_view/more/?page_id='
|
||||
. $pageInfo['userId']
|
||||
. '&start_cursor=1&num_to_fetch=105&surface_type=timeline';
|
||||
$fileContent = getContents($requestString);
|
||||
|
||||
$articleIndex = 0;
|
||||
$maxArticle = 3;
|
||||
|
||||
$html = $this->buildContent($fileContent);
|
||||
$author = $this->getInput('u');
|
||||
$author = $pageInfo['username'];
|
||||
|
||||
foreach($html->find('article') as $content) {
|
||||
|
||||
$item = array();
|
||||
//echo $content; die();
|
||||
preg_match('/publish_time\\\":([0-9]+),/', $content->getAttribute('data-store', 0), $match);
|
||||
if(isset($match[1]))
|
||||
$timestamp = $match[1];
|
||||
else
|
||||
$timestamp = 0;
|
||||
|
||||
$item['uri'] = 'http://touch.facebook.com'
|
||||
. $content->find("div[class='_52jc _5qc4 _24u0 _36xo']", 0)->find('a', 0)->getAttribute('href');
|
||||
$item['uri'] = html_entity_decode('http://touch.facebook.com'
|
||||
. $content->find("div[class='_52jc _5qc4 _24u0 _36xo']", 0)->find('a', 0)->getAttribute('href'), ENT_QUOTES);
|
||||
|
||||
//Decode images
|
||||
$imagecleaned = preg_replace_callback('/<i [^>]* style="[^"]*url\(\'(.*?)\'\).*?><\/i>/m', function ($matches) {
|
||||
return "<img src='" . str_replace(['\\3a ', '\\3d ', '\\26 '], [':', '=', '&'], $matches[1]) . "' />";
|
||||
}, $content);
|
||||
$content = str_get_html($imagecleaned);
|
||||
|
||||
if($content->find('header', 0) !== null) {
|
||||
$content->find('header', 0)->innertext = '';
|
||||
@@ -108,8 +119,13 @@ EOD;
|
||||
$content->find('footer', 0)->innertext = '';
|
||||
}
|
||||
|
||||
// Replace emoticon images by their textual representation (part of the span)
|
||||
foreach($content->find('span[title*="emoticon"]') as $emoticon) {
|
||||
$emoticon->innertext = $emoticon->find('span[aria-hidden="true"]', 0)->innertext;
|
||||
}
|
||||
|
||||
//Remove html nodes, keep only img, links, basic formatting
|
||||
$content = strip_tags($content, '<a><img><i><u><br><p><h3><h4>');
|
||||
$content = strip_tags($content, '<a><img><i><u><br><p><h3><h4><section>');
|
||||
|
||||
//Adapt link hrefs: convert relative links into absolute links and bypass external link redirection
|
||||
$content = preg_replace_callback('/ href=\"([^"]+)\"/i', $unescape_fb_link, $content);
|
||||
@@ -122,7 +138,6 @@ EOD;
|
||||
'ajaxify',
|
||||
'tabindex',
|
||||
'class',
|
||||
'style',
|
||||
'data-[^=]*',
|
||||
'aria-[^=]*',
|
||||
'role',
|
||||
@@ -135,7 +150,36 @@ EOD;
|
||||
// "<i><u>smile emoticon</u></i>" back to ASCII emoticons eg ":)"
|
||||
$content = preg_replace_callback('/<i><u>([^ <>]+) ([^<>]+)<\/u><\/i>/i', $unescape_fb_emote, $content);
|
||||
|
||||
$item['content'] = $content;
|
||||
//Remove the "...Plus" tag
|
||||
$content = preg_replace(
|
||||
'/… (<span>|)<a href="https:\/\/www\.facebook\.com\/story\.php\?story_fbid=.*?<\/a>/m',
|
||||
'', $content, 1);
|
||||
|
||||
//Remove tracking images
|
||||
$content = preg_replace('/<img src=\'.*?safe_image\.php.*?\' \/>/m', '', $content);
|
||||
|
||||
//Remove the double section tags
|
||||
$content = str_replace(['<section><section>', '</section></section>'], ['<section>', '</section>'], $content);
|
||||
|
||||
//Move the section tag link upper, if it is down
|
||||
$content = str_get_html($content);
|
||||
$sectionContent = $content->find('section', 0);
|
||||
if($sectionContent != null) {
|
||||
$sectionLink = $sectionContent->nextSibling();
|
||||
if($sectionLink != null) {
|
||||
$fullLink = '<a href="' . $sectionLink->getAttribute('href') . '">' . $sectionContent->innertext . '</a>';
|
||||
$sectionContent->innertext = $fullLink;
|
||||
}
|
||||
}
|
||||
|
||||
//Move the href tag upper if it is inside the section
|
||||
foreach($content->find('section > a') as $sectionToFix) {
|
||||
$sectionLink = $sectionToFix->getAttribute('href');
|
||||
$section = $sectionToFix->parent();
|
||||
$section->outertext = '<a href="' . $sectionLink . '">' . $section . '</a>';
|
||||
}
|
||||
|
||||
$item['content'] = html_entity_decode($content, ENT_QUOTES);
|
||||
|
||||
$title = $author;
|
||||
if (strlen($title) > 24)
|
||||
@@ -144,57 +188,29 @@ EOD;
|
||||
if (strlen($title) > 64)
|
||||
$title = substr($title, 0, strpos(wordwrap($title, 64), "\n")) . '...';
|
||||
|
||||
$item['title'] = $title;
|
||||
$item['author'] = $author;
|
||||
$item['title'] = html_entity_decode($title, ENT_QUOTES);
|
||||
$item['author'] = html_entity_decode($author, ENT_QUOTES);
|
||||
$item['timestamp'] = html_entity_decode($timestamp, ENT_QUOTES);
|
||||
|
||||
array_push($this->items, $item);
|
||||
if($item['timestamp'] != 0)
|
||||
array_push($this->items, $item);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Currently not used. Is used to get more than only 3 elements, as they appear on another page.
|
||||
private function computeNextLink($string, $pageID){
|
||||
|
||||
$regex = implode(
|
||||
'',
|
||||
array(
|
||||
'/timeline_unit',
|
||||
"\\\\\\\\u00253A1",
|
||||
"\\\\\\\\u00253A([0-9]*)",
|
||||
"\\\\\\\\u00253A([0-9]*)",
|
||||
"\\\\\\\\u00253A([0-9]*)",
|
||||
"\\\\\\\\u00253A([0-9]*)/"
|
||||
)
|
||||
);
|
||||
|
||||
preg_match($regex, $string, $result);
|
||||
|
||||
return implode(
|
||||
'',
|
||||
array(
|
||||
'https://touch.facebook.com/pages_reaction_units/more/?page_id=',
|
||||
$pageID,
|
||||
'&cursor=%7B%22timeline_cursor%22%3A%22timeline_unit%3A1%3A',
|
||||
$result[1],
|
||||
'%3A',
|
||||
$result[2],
|
||||
'%3A',
|
||||
$result[3],
|
||||
'%3A',
|
||||
$result[4],
|
||||
'%22%2C%22timeline_section_cursor%22%3A%7B%7D%2C%22',
|
||||
'has_next_page%22%3Atrue%7D&surface=mobile_page_home&unit_count=3'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
//Builds the HTML from the encoded JS that Facebook provides.
|
||||
private function buildContent($pageContent){
|
||||
// The html ends with:
|
||||
// /div>","replaceifexists
|
||||
$regex = '/\\"html\\":(\".+\/div>"),"replace/';
|
||||
preg_match($regex, $pageContent, $result);
|
||||
return str_get_html(html_entity_decode(json_decode($result[1])));
|
||||
|
||||
$htmlContent = json_decode($result[1]);
|
||||
$htmlContent = preg_replace('/(?<!style)="(.*?)"/', '=\'$1\'', $htmlContent);
|
||||
$htmlContent = html_entity_decode($htmlContent, ENT_QUOTES, 'UTF-8');
|
||||
|
||||
return str_get_html($htmlContent);
|
||||
}
|
||||
|
||||
|
||||
@@ -224,8 +240,8 @@ EOD;
|
||||
return substr($cookies, 1);
|
||||
}
|
||||
|
||||
//Get the page ID from the Facebook page.
|
||||
private function getPageID($page, $cookies){
|
||||
//Get the page ID and username from the Facebook page.
|
||||
private function getPageInfos($page, $cookies){
|
||||
|
||||
$context = stream_context_create(array(
|
||||
'http' => array(
|
||||
@@ -241,19 +257,28 @@ EOD;
|
||||
return -1;
|
||||
}
|
||||
|
||||
//Get the username
|
||||
$usernameRegex = '/data-nt=\"FB:TEXT4\">(.*?)<\/div>/m';
|
||||
preg_match($usernameRegex, $pageContent, $usernameMatches);
|
||||
if(count($usernameMatches) > 0) {
|
||||
$username = strip_tags($usernameMatches[1]);
|
||||
} else {
|
||||
$username = $this->getInput('u');
|
||||
}
|
||||
|
||||
//Get the page ID if we don't have a captcha
|
||||
$regex = '/page_id=([0-9]*)&/';
|
||||
preg_match($regex, $pageContent, $matches);
|
||||
|
||||
if(count($matches) > 0) {
|
||||
return $matches[1];
|
||||
return array('userId' => $matches[1], 'username' => $username);
|
||||
}
|
||||
|
||||
//Get the page ID if we do have a captcha
|
||||
$regex = '/"pageID":"([0-9]*)"/';
|
||||
preg_match($regex, $pageContent, $matches);
|
||||
|
||||
return $matches[1];
|
||||
return array('userId' => $matches[1], 'username' => $username);
|
||||
|
||||
}
|
||||
|
||||
|
@@ -19,6 +19,10 @@ class FDroidBridge extends BridgeAbstract {
|
||||
)
|
||||
));
|
||||
|
||||
public function getIcon() {
|
||||
return self::URI . 'assets/favicon.ico?v=8j6PKzW9Mk';
|
||||
}
|
||||
|
||||
public function collectData(){
|
||||
$url = self::URI;
|
||||
$html = getSimpleHTMLDOM($url)
|
||||
@@ -45,9 +49,9 @@ class FDroidBridge extends BridgeAbstract {
|
||||
$item['icon'] = $element->find('img', 0)->src;
|
||||
$item['summary'] = $element->find('span.package-summary', 0)->plaintext;
|
||||
$item['content'] = '
|
||||
<a href="'.$item['uri'].'">
|
||||
<img alt="" style="max-height:128px" src="'.$item['icon'].'">
|
||||
</a><br>'.$item['summary'];
|
||||
<a href="' . $item['uri'] . '">
|
||||
<img alt="" style="max-height:128px" src="' . $item['icon'] . '">
|
||||
</a><br>' . $item['summary'];
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
|
@@ -56,6 +56,10 @@ class FacebookBridge extends BridgeAbstract {
|
||||
private $authorName = '';
|
||||
private $groupName = '';
|
||||
|
||||
public function getIcon() {
|
||||
return 'https://static.xx.fbcdn.net/rsrc.php/yo/r/iRmz9lCMBD2.ico';
|
||||
}
|
||||
|
||||
public function getName(){
|
||||
|
||||
switch($this->queriedContext) {
|
||||
@@ -95,7 +99,7 @@ class FacebookBridge extends BridgeAbstract {
|
||||
$user = $this->sanitizeUser($this->getInput('u'));
|
||||
|
||||
if(!strpos($user, '/')) {
|
||||
$uri .= '/pg/' . urlencode($user) . '/posts';
|
||||
$uri .= urlencode($user) . '/posts';
|
||||
} else {
|
||||
$uri .= 'pages/' . $user;
|
||||
}
|
||||
@@ -506,8 +510,6 @@ EOD;
|
||||
returnServerError('You must be logged in to view this page. This is not supported by RSS-Bridge.');
|
||||
}
|
||||
|
||||
$html = defaultLinkTo($html, self::URI);
|
||||
|
||||
$element = $html
|
||||
->find('#pagelet_timeline_main_column')[0]
|
||||
->children(0)
|
||||
@@ -554,11 +556,7 @@ EOD;
|
||||
|
||||
if(count($post->find('abbr')) > 0) {
|
||||
|
||||
//Retrieve post contents
|
||||
$content = preg_replace(
|
||||
'/(?i)><div class=\"clearfix([^>]+)>(.+?)div\ class=\"userContent\"/i',
|
||||
'',
|
||||
$post);
|
||||
$content = $post->find('.userContentWrapper', 0);
|
||||
|
||||
$content = preg_replace(
|
||||
'/(?i)><div class=\"_59tj([^>]+)>(.+?)<\/div><\/div><a/i',
|
||||
@@ -607,6 +605,12 @@ EOD;
|
||||
|
||||
$this->unescape_fb_emote($content);
|
||||
|
||||
// Restore links in the post before further parsing
|
||||
$post = defaultLinkTo($post, self::URI);
|
||||
|
||||
// Restore links in the content before adding to the item
|
||||
$content = defaultLinkTo($content, self::URI);
|
||||
|
||||
// Retrieve date of the post
|
||||
$date = $post->find('abbr')[0];
|
||||
|
||||
|
@@ -7,6 +7,10 @@ class FierPandaBridge extends BridgeAbstract {
|
||||
const CACHE_TIMEOUT = 21600; // 6h
|
||||
const DESCRIPTION = 'Returns latest articles from Fier Panda.';
|
||||
|
||||
public function getIcon() {
|
||||
return self::URI . 'wp-content/themes/fier-panda/img/favicon.png';
|
||||
}
|
||||
|
||||
public function collectData(){
|
||||
|
||||
$html = getSimpleHTMLDOM(self::URI)
|
||||
|
@@ -69,7 +69,7 @@ class FourchanBridge extends BridgeAbstract {
|
||||
. '" src="'
|
||||
. $item['imageThumb']
|
||||
. '" /></a><br>'
|
||||
.$item['content'];
|
||||
. $item['content'];
|
||||
}
|
||||
$this->items[] = $item;
|
||||
}
|
||||
|
@@ -35,7 +35,7 @@ class GBAtempBridge extends BridgeAbstract {
|
||||
|
||||
private function cleanupPostContent($content, $site_url){
|
||||
$content = str_replace(':arrow:', '➤', $content);
|
||||
$content = str_replace('href="attachments/', 'href="'.$site_url.'attachments/', $content);
|
||||
$content = str_replace('href="attachments/', 'href="' . $site_url . 'attachments/', $content);
|
||||
$content = stripWithDelimiters($content, '<script', '</script>');
|
||||
return $content;
|
||||
}
|
||||
|
@@ -54,7 +54,7 @@ class GitHubGistBridge extends BridgeAbstract {
|
||||
DEFAULT_SPAN_TEXT)
|
||||
or returnServerError('Could not request ' . $this->getURI());
|
||||
|
||||
$html = defaultLinkTo($html, static::URI);
|
||||
$html = defaultLinkTo($html, $this->getURI());
|
||||
|
||||
$fileinfo = $html->find('[class="file-info"]', 0)
|
||||
or returnServerError('Could not find file info!');
|
||||
@@ -69,7 +69,7 @@ class GitHubGistBridge extends BridgeAbstract {
|
||||
|
||||
foreach($comments as $comment) {
|
||||
|
||||
$uri = $comment->find('a[href^=#gistcomment]', 0)
|
||||
$uri = $comment->find('a[href*=#gistcomment]', 0)
|
||||
or returnServerError('Could not find comment anchor!');
|
||||
|
||||
$title = $comment->find('div[class="unminimized-comment"] h3[class="timeline-comment-header-text"]', 0)
|
||||
@@ -86,7 +86,7 @@ class GitHubGistBridge extends BridgeAbstract {
|
||||
|
||||
$item = array();
|
||||
|
||||
$item['uri'] = $this->getURI() . $uri->href;
|
||||
$item['uri'] = $uri->href;
|
||||
$item['title'] = str_replace('commented', 'commented on', $title->plaintext);
|
||||
$item['timestamp'] = strtotime($datetime->datetime);
|
||||
$item['author'] = '<a href="' . $author->href . '">' . $author->plaintext . '</a>';
|
||||
|
@@ -22,6 +22,10 @@ class GooglePlusPostBridge extends BridgeAbstract{
|
||||
)
|
||||
));
|
||||
|
||||
public function getIcon() {
|
||||
return 'https://ssl.gstatic.com/images/branding/product/ico/google_plus_alldp.ico';
|
||||
}
|
||||
|
||||
public function collectData(){
|
||||
|
||||
$username = $this->getInput('username');
|
||||
|
@@ -28,7 +28,7 @@ class GoogleSearchBridge extends BridgeAbstract {
|
||||
$html = getSimpleHTMLDOM(self::URI
|
||||
. 'search?q='
|
||||
. urlencode($this->getInput('q'))
|
||||
.'&num=100&complete=0&tbs=qdr:y,sbd:1')
|
||||
. '&num=100&complete=0&tbs=qdr:y,sbd:1')
|
||||
or returnServerError('No results for this query.');
|
||||
|
||||
$emIsRes = $html->find('div[id=ires]', 0);
|
||||
|
@@ -3,7 +3,7 @@ class InstagramBridge extends BridgeAbstract {
|
||||
|
||||
const MAINTAINER = 'pauder';
|
||||
const NAME = 'Instagram Bridge';
|
||||
const URI = 'https://instagram.com/';
|
||||
const URI = 'https://www.instagram.com/';
|
||||
const DESCRIPTION = 'Returns the newest images';
|
||||
|
||||
const PARAMETERS = array(
|
||||
@@ -85,7 +85,7 @@ class InstagramBridge extends BridgeAbstract {
|
||||
$item['content'] = $data[0];
|
||||
$item['enclosures'] = $data[1];
|
||||
} else {
|
||||
$item['content'] = '<img src="' . htmlentities($media->display_url) . '" alt="'. $item['title'] . '" />';
|
||||
$item['content'] = '<img src="' . htmlentities($media->display_url) . '" alt="' . $item['title'] . '" />';
|
||||
$item['enclosures'] = array($media->display_url);
|
||||
}
|
||||
|
||||
@@ -101,16 +101,21 @@ class InstagramBridge extends BridgeAbstract {
|
||||
$mediaInfo = $data->entry_data->PostPage[0]->graphql->shortcode_media;
|
||||
|
||||
//Process the first element, that isn't in the node graph
|
||||
$caption = $mediaInfo->edge_media_to_caption->edges[0]->node->text;
|
||||
if (count($mediaInfo->edge_media_to_caption->edges) > 0) {
|
||||
$caption = $mediaInfo->edge_media_to_caption->edges[0]->node->text;
|
||||
} else {
|
||||
$caption = '';
|
||||
}
|
||||
|
||||
$enclosures = [$mediaInfo->display_url];
|
||||
$content = '<img src="' . htmlentities($mediaInfo->display_url) . '" alt="'. $caption . '" />';
|
||||
$content = '<img src="' . htmlentities($mediaInfo->display_url) . '" alt="' . $caption . '" />';
|
||||
|
||||
foreach($mediaInfo->edge_sidecar_to_children->edges as $media) {
|
||||
|
||||
$content .= '<img src="' . htmlentities($media->node->display_url) . '" alt="'. $caption . '" />';
|
||||
$enclosures[] = $media->node->display_url;
|
||||
|
||||
$display_url = $media->node->display_url;
|
||||
if(!in_array($display_url, $enclosures)) { // add only if not added yet
|
||||
$content .= '<img src="' . htmlentities($display_url) . '" alt="' . $caption . '" />';
|
||||
$enclosures[] = $display_url;
|
||||
}
|
||||
}
|
||||
|
||||
return [$content, $enclosures];
|
||||
@@ -139,7 +144,7 @@ class InstagramBridge extends BridgeAbstract {
|
||||
|
||||
public function getURI(){
|
||||
if(!is_null($this->getInput('u'))) {
|
||||
return self::URI . urlencode($this->getInput('u'));
|
||||
return self::URI . urlencode($this->getInput('u')) . '/';
|
||||
} elseif(!is_null($this->getInput('h'))) {
|
||||
return self::URI . 'explore/tags/' . urlencode($this->getInput('h'));
|
||||
}
|
||||
|
@@ -13,6 +13,10 @@ class JapanExpoBridge extends BridgeAbstract {
|
||||
)
|
||||
));
|
||||
|
||||
public function getIcon() {
|
||||
return 'https://s.japan-expo.com/katana/images/JES073/favicons/paris.png';
|
||||
}
|
||||
|
||||
public function collectData(){
|
||||
|
||||
function frenchPubDateToTimestamp($date_to_parse) {
|
||||
|
@@ -36,6 +36,11 @@ class KATBridge extends BridgeAbstract {
|
||||
'name' => 'Only get results from Elite or Verified uploaders ?',
|
||||
),
|
||||
));
|
||||
|
||||
public function getIcon() {
|
||||
return 'https://statuskatcrco-631f.kxcdn.com/assets/images/favicon.ico';
|
||||
}
|
||||
|
||||
public function collectData(){
|
||||
function parseDateTimestamp($element){
|
||||
$guessedDate = strptime($element, '%d-%m-%Y %H:%M:%S');
|
||||
|
@@ -38,6 +38,10 @@ class KernelBugTrackerBridge extends BridgeAbstract {
|
||||
private $bugid = '';
|
||||
private $bugdesc = '';
|
||||
|
||||
public function getIcon() {
|
||||
return self::URI . '/images/favicon.ico';
|
||||
}
|
||||
|
||||
public function collectData(){
|
||||
$limit = $this->getInput('limit');
|
||||
$sorting = $this->getInput('sorting');
|
||||
|
@@ -9,7 +9,7 @@ class LWNprevBridge extends BridgeAbstract{
|
||||
private $editionTimeStamp;
|
||||
|
||||
function getURI(){
|
||||
return self::URI.'free/bigpage';
|
||||
return self::URI . 'free/bigpage';
|
||||
}
|
||||
|
||||
private function jumpToNextTag(&$node){
|
||||
@@ -47,7 +47,7 @@ class LWNprevBridge extends BridgeAbstract{
|
||||
<html><head><title>LWN</title></head><body>{$content}</body></html>
|
||||
EOD;
|
||||
} else {
|
||||
$content = $content.'</body></html>';
|
||||
$content = $content . '</body></html>';
|
||||
}
|
||||
|
||||
libxml_use_internal_errors(true);
|
||||
@@ -172,7 +172,7 @@ EOD;
|
||||
|
||||
$prefix = '';
|
||||
if(!empty($cats[0])) {
|
||||
$prefix .= '['.$cats[0].($cats[1] ? '/'.$cats[1] : '').'] ';
|
||||
$prefix .= '[' . $cats[0] . ($cats[1] ? '/' . $cats[1] : '') . '] ';
|
||||
}
|
||||
return $prefix;
|
||||
}
|
||||
@@ -188,7 +188,7 @@ EOD;
|
||||
|
||||
$item = array();
|
||||
|
||||
$item['uri'] = self::URI.'#'.count($items);
|
||||
$item['uri'] = self::URI . '#' . count($items);
|
||||
|
||||
$item['timestamp'] = $this->editionTimeStamp;
|
||||
|
||||
@@ -197,7 +197,7 @@ EOD;
|
||||
$cat = $newsletters->previousSibling;
|
||||
$this->jumpToPreviousTag($cat);
|
||||
$prefix = $this->getItemPrefix($cat, $cats);
|
||||
$item['title'] = $prefix.' '.$newsletters->textContent;
|
||||
$item['title'] = $prefix . ' ' . $newsletters->textContent;
|
||||
|
||||
$node = $newsletters;
|
||||
$content = '';
|
||||
@@ -233,7 +233,7 @@ EOD;
|
||||
$cat = $cat->previousSibling;
|
||||
$this->jumpToPreviousTag($cat);
|
||||
$prefix = $this->getItemPrefix($cat, $cats);
|
||||
$item['title'] = $prefix.' '.$title->textContent;
|
||||
$item['title'] = $prefix . ' ' . $title->textContent;
|
||||
$items[] = array_merge($item, $this->getArticleContent($title));
|
||||
}
|
||||
|
||||
@@ -255,7 +255,7 @@ EOD;
|
||||
$cat = $cat->previousSibling;
|
||||
$this->jumpToPreviousTag($cat);
|
||||
$prefix = $this->getItemPrefix($cat, $cats);
|
||||
$item['title'] = $prefix.' '.$title->textContent;
|
||||
$item['title'] = $prefix . ' ' . $title->textContent;
|
||||
$items[] = array_merge($item, $this->getArticleContent($title));
|
||||
}
|
||||
|
||||
|
@@ -38,6 +38,10 @@ class LegifranceJOBridge extends BridgeAbstract {
|
||||
return $item;
|
||||
}
|
||||
|
||||
public function getIcon() {
|
||||
return 'https://www.legifrance.gouv.fr/img/favicon.ico';
|
||||
}
|
||||
|
||||
public function collectData(){
|
||||
$html = getSimpleHTMLDOM(self::URI)
|
||||
or $this->returnServer('Unable to download ' . self::URI);
|
||||
|
@@ -117,7 +117,7 @@ class NineGagBridge extends BridgeAbstract {
|
||||
$cursor = 'c=10';
|
||||
$posts = array();
|
||||
for ($i = 0; $i < $this->getPages(); ++$i) {
|
||||
$content = getContents($url.$cursor);
|
||||
$content = getContents($url . $cursor);
|
||||
$json = json_decode($content, true);
|
||||
$posts = array_merge($posts, $json['data']['posts']);
|
||||
$cursor = $json['data']['nextCursor'];
|
||||
@@ -156,7 +156,7 @@ class NineGagBridge extends BridgeAbstract {
|
||||
$uri = $this->getInput('t');
|
||||
}
|
||||
|
||||
return self::URI.$uri;
|
||||
return self::URI . $uri;
|
||||
}
|
||||
|
||||
protected function getGroup() {
|
||||
|
@@ -26,6 +26,10 @@ class NotAlwaysBridge extends BridgeAbstract {
|
||||
)
|
||||
));
|
||||
|
||||
public function getIcon() {
|
||||
return self::URI . 'favicon_nar.png';
|
||||
}
|
||||
|
||||
public function collectData(){
|
||||
$html = getSimpleHTMLDOM($this->getURI())
|
||||
or returnServerError('Could not request NotAlways.');
|
||||
|
@@ -54,6 +54,10 @@ class NyaaTorrentsBridge extends BridgeAbstract {
|
||||
)
|
||||
);
|
||||
|
||||
public function getIcon() {
|
||||
return self::URI . 'static/favicon.png';
|
||||
}
|
||||
|
||||
public function collectData() {
|
||||
|
||||
// Build Search URL from user-provided parameters
|
||||
|
@@ -75,7 +75,7 @@ class PikabuBridge extends BridgeAbstract {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$img->outertext = '<img src="'.$src.'">';
|
||||
$img->outertext = '<img src="' . $src . '">';
|
||||
}
|
||||
|
||||
$categories = array();
|
||||
|
@@ -25,6 +25,10 @@ class PinterestBridge extends FeedExpander {
|
||||
)
|
||||
);
|
||||
|
||||
public function getIcon() {
|
||||
return 'https://s.pinimg.com/webapp/style/images/favicon-9f8f9adf.png';
|
||||
}
|
||||
|
||||
public function collectData(){
|
||||
switch($this->queriedContext) {
|
||||
case 'By username and board':
|
||||
|
@@ -18,7 +18,7 @@ class PixivBridge extends BridgeAbstract {
|
||||
|
||||
public function collectData(){
|
||||
|
||||
$html = getContents(static::URI.'search.php?word=' . urlencode($this->getInput('tag')))
|
||||
$html = getContents(static::URI . 'search.php?word=' . urlencode($this->getInput('tag')))
|
||||
or returnClientError('Unable to query pixiv.net');
|
||||
$regex = '/<input type="hidden"id="js-mount-point-search-result-list"data-items="([^"]*)/';
|
||||
$timeRegex = '/img\/([0-9]{4})\/([0-9]{2})\/([0-9]{2})\/([0-9]{2})\/([0-9]{2})\/([0-9]{2})\//';
|
||||
|
@@ -58,7 +58,7 @@ class RTBFBridge extends BridgeAbstract {
|
||||
|
||||
public function getName(){
|
||||
if(!is_null($this->getInput('c'))) {
|
||||
return $this->getInput('c') .' - RTBF Bridge';
|
||||
return $this->getInput('c') . ' - RTBF Bridge';
|
||||
}
|
||||
|
||||
return parent::getName();
|
||||
|
@@ -5,6 +5,10 @@ class RadioMelodieBridge extends BridgeAbstract {
|
||||
const DESCRIPTION = 'Retourne les actualités publiées par Radio Melodie';
|
||||
const MAINTAINER = 'sysadminstory';
|
||||
|
||||
public function getIcon() {
|
||||
return self::URI . 'img/favicon.png';
|
||||
}
|
||||
|
||||
public function collectData(){
|
||||
$html = getSimpleHTMLDOM(self::URI . 'actu')
|
||||
or returnServerError('Could not request Radio Melodie.');
|
||||
@@ -23,7 +27,7 @@ class RadioMelodieBridge extends BridgeAbstract {
|
||||
$item['enclosures'] = array($pictureURL);
|
||||
$item['uri'] = self::URI . $element->parent()->href;
|
||||
$item['title'] = $element->find('h3', 0)->plaintext;
|
||||
$item['content'] = $element->find('p', 0)->plaintext . '<br/><img src="'.$pictureURL.'"/>';
|
||||
$item['content'] = $element->find('p', 0)->plaintext . '<br/><img src="' . $pictureURL . '"/>';
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
|
@@ -7,6 +7,10 @@ class RainbowSixSiegeBridge extends BridgeAbstract {
|
||||
const CACHE_TIMEOUT = 7200; // 2h
|
||||
const DESCRIPTION = 'Latest articles from the Rainbow Six Siege blog';
|
||||
|
||||
public function getIcon() {
|
||||
return 'https://ubistatic19-a.akamaihd.net/resource/en-us/game/rainbow6/siege-v3/r6s-favicon_316592.ico';
|
||||
}
|
||||
|
||||
public function collectData(){
|
||||
$dlUrl = 'https://prod-tridionservice.ubisoft.com/live/v1/News/Latest?templateId=tcm%3A152-7677';
|
||||
$dlUrl .= '8-32&pageIndex=0&pageSize=10&language=en-US&detailPageId=tcm%3A152-194572-64';
|
||||
|
@@ -13,6 +13,10 @@ class SupInfoBridge extends BridgeAbstract {
|
||||
)
|
||||
));
|
||||
|
||||
public function getIcon() {
|
||||
return self::URI . '/favicon.png';
|
||||
}
|
||||
|
||||
public function collectData() {
|
||||
|
||||
if(empty($this->getInput('tag'))) {
|
||||
|
@@ -25,7 +25,7 @@ class SuperSmashBlogBridge extends BridgeAbstract {
|
||||
|
||||
$video = $article['acf']['link_url'];
|
||||
if (strlen($video) != 0) {
|
||||
$video = str_get_html('<a href="' . $video .'">Youtube video</a>');
|
||||
$video = str_get_html('<a href="' . $video . '">Youtube video</a>');
|
||||
} else {
|
||||
$video = '';
|
||||
}
|
||||
|
@@ -14,6 +14,10 @@ class TagBoardBridge extends BridgeAbstract {
|
||||
)
|
||||
));
|
||||
|
||||
public function getIcon() {
|
||||
return 'https://static.tagboard.com/public/favicon-32x32.png';
|
||||
}
|
||||
|
||||
public function collectData(){
|
||||
$link = 'https://post-cache.tagboard.com/search/' . $this->getInput('u');
|
||||
|
||||
|
@@ -21,6 +21,10 @@ class TebeoBridge extends FeedExpander {
|
||||
)
|
||||
));
|
||||
|
||||
public function getIcon() {
|
||||
return self::URI . 'images/header_logo.png';
|
||||
}
|
||||
|
||||
public function collectData(){
|
||||
$url = self::URI . '/le-replay/' . $this->getInput('cat');
|
||||
$html = getSimpleHTMLDOM($url)
|
||||
@@ -31,7 +35,7 @@ class TebeoBridge extends FeedExpander {
|
||||
$item['uri'] = $element->find('a', 0)->href;
|
||||
$item['title'] = $element->find('h3', 0)->plaintext;
|
||||
$item['timestamp'] = strtotime($element->find('p.moment-format-day', 0)->plaintext);
|
||||
$item['content'] = '<a href="'.$item['uri'].'"><img alt="" src="'.$element->find('img', 0)->src.'"></a>';
|
||||
$item['content'] = '<a href="' . $item['uri'] . '"><img alt="" src="' . $element->find('img', 0)->src . '"></a>';
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
|
@@ -158,6 +158,10 @@ class TheTVDBBridge extends BridgeAbstract {
|
||||
}
|
||||
}
|
||||
|
||||
public function getIcon() {
|
||||
return self::URI . 'application/themes/thetvdb/images/logo.png';
|
||||
}
|
||||
|
||||
public function collectData(){
|
||||
$serie_id = $this->getInput('serie_id');
|
||||
$nbepisode = $this->getInput('nb_episode');
|
||||
|
@@ -25,12 +25,12 @@ class TheYeteeBridge extends BridgeAbstract {
|
||||
$item['author'] = $author;
|
||||
|
||||
$uri = $element->find('div[class=controls] a', 0)->href;
|
||||
$item['uri'] = static::URI.$uri;
|
||||
$item['uri'] = static::URI . $uri;
|
||||
|
||||
$content = '<p>'.$element->find('section[class=product-listing-info] p', -1)->plaintext.'</p>';
|
||||
$content = '<p>' . $element->find('section[class=product-listing-info] p', -1)->plaintext . '</p>';
|
||||
$photos = $element->find('a[class=js-modaal-gallery] img');
|
||||
foreach($photos as $photo) {
|
||||
$content = $content."<br /><img src='$photo->src' />";
|
||||
$content = $content . "<br /><img src='$photo->src' />";
|
||||
$item['enclosures'][] = $photo->src;
|
||||
}
|
||||
$item['content'] = $content;
|
||||
|
167
bridges/ThingiverseBridge.php
Normal file
167
bridges/ThingiverseBridge.php
Normal file
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
class ThingiverseBridge extends BridgeAbstract {
|
||||
|
||||
const NAME = 'Thingiverse Search';
|
||||
const URI = 'https://thingiverse.com';
|
||||
const DESCRIPTION = 'Returns feeds for search results';
|
||||
const MAINTAINER = 'AntoineTurmel';
|
||||
const PARAMETERS = array(
|
||||
array(
|
||||
'query' => array(
|
||||
'name' => 'Search query',
|
||||
'type' => 'text',
|
||||
'required' => true,
|
||||
'title' => 'Insert your search term here',
|
||||
'exampleValue' => 'Enter your search term'
|
||||
),
|
||||
'sortby' => array(
|
||||
'name' => 'Sort by',
|
||||
'type' => 'list',
|
||||
'required' => false,
|
||||
'values' => array(
|
||||
'Relevant' => 'relevant',
|
||||
'Text' => 'text',
|
||||
'Popular' => 'popular',
|
||||
'# of Makes' => 'makes',
|
||||
'Newest' => 'newest',
|
||||
),
|
||||
'defaultValue' => 'newest'
|
||||
),
|
||||
'category' => array(
|
||||
'name' => 'Category',
|
||||
'type' => 'list',
|
||||
'required' => false,
|
||||
'values' => array(
|
||||
'Any' => '',
|
||||
'3D Printing' => '73',
|
||||
'Art' => '63',
|
||||
'Fashion' => '64',
|
||||
'Gadgets' => '65',
|
||||
'Hobby' => '66',
|
||||
'Household' => '67',
|
||||
'Learning' => '69',
|
||||
'Models' => '70',
|
||||
'Tools' => '71',
|
||||
'Toys & Games' => '72',
|
||||
'2D Art' => '144',
|
||||
'Art Tools' => '75',
|
||||
'Coins & Badges' => '143',
|
||||
'Interactive Art' => '78',
|
||||
'Math Art' => '79',
|
||||
'Scans & Replicas' => '145',
|
||||
'Sculptures' => '80',
|
||||
'Signs & Logos' => '76',
|
||||
'Accessories' => '81',
|
||||
'Bracelets' => '82',
|
||||
'Costume' => '142',
|
||||
'Earrings' => '139',
|
||||
'Glasses' => '83',
|
||||
'Jewelry' => '84',
|
||||
'Keychains' => '130',
|
||||
'Rings' => '85',
|
||||
'Audio' => '141',
|
||||
'Camera' => '86',
|
||||
'Computer' => '87',
|
||||
'Mobile Phone' => '88',
|
||||
'Tablet' => '90',
|
||||
'Video Games' => '91',
|
||||
'Automotive' => '155',
|
||||
'DIY' => '93',
|
||||
'Electronics' => '92',
|
||||
'Music' => '94',
|
||||
'R/C Vehicles' => '95',
|
||||
'Robotics' => '96',
|
||||
'Sport & Outdoors' => '140',
|
||||
'Bathroom' => '147',
|
||||
'Containers' => '146',
|
||||
'Decor' => '97',
|
||||
'Household Supplies' => '99',
|
||||
'Kitchen & Dining' => '100',
|
||||
'Office' => '101',
|
||||
'Organization' => '102',
|
||||
'Outdoor & Garden' => '98',
|
||||
'Pets' => '103',
|
||||
'Replacement Parts' => '153',
|
||||
'Biology' => '106',
|
||||
'Engineering' => '104',
|
||||
'Math' => '105',
|
||||
'Physics & Astronomy' => '148',
|
||||
'Animals' => '107',
|
||||
'Buildings & Structures' => '108',
|
||||
'Creatures' => '109',
|
||||
'Food & Drink' => '110',
|
||||
'Model Furniture' => '111',
|
||||
'Model Robots' => '115',
|
||||
'People' => '112',
|
||||
'Props' => '114',
|
||||
'Vehicles' => '116',
|
||||
'Hand Tools' => '118',
|
||||
'Machine Tools' => '117',
|
||||
'Parts' => '119',
|
||||
'Tool Holders & Boxes' => '120',
|
||||
'Chess' => '151',
|
||||
'Construction Toys' => '121',
|
||||
'Dice' => '122',
|
||||
'Games' => '123',
|
||||
'Mechanical Toys' => '124',
|
||||
'Playsets' => '113',
|
||||
'Puzzles' => '125',
|
||||
'Toy & Game Accessories' => '149',
|
||||
'3D Printer Accessories' => '127',
|
||||
'3D Printer Extruders' => '152',
|
||||
'3D Printer Parts' => '128',
|
||||
'3D Printers' => '126',
|
||||
'3D Printing Tests' => '129',
|
||||
),
|
||||
'defaultValue' => ''
|
||||
),
|
||||
'showimage' => array(
|
||||
'name' => 'Show image in content',
|
||||
'type' => 'checkbox',
|
||||
'required' => false,
|
||||
'title' => 'Activate to show the image in the content',
|
||||
'defaultValue' => 'checked'
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
public function collectData(){
|
||||
$html = getSimpleHTMLDOM($this->getURI())
|
||||
or returnServerError('Failed to receive ' . $this->getURI());
|
||||
|
||||
$results = $html->find('div.thing-card');
|
||||
|
||||
foreach($results as $result) {
|
||||
|
||||
$item = array();
|
||||
|
||||
$item['title'] = $result->find('span.ellipsis', 0);
|
||||
$item['uri'] = self::URI . $result->find('a', 1)->href;
|
||||
$item['author'] = $result->find('span.item-creator', 0);
|
||||
$item['content'] = '';
|
||||
|
||||
$image = $result->find('img.card-img', 0)->src;
|
||||
|
||||
if($this->getInput('showimage')) {
|
||||
$item['content'] .= '<img src="' . $image . '">';
|
||||
}
|
||||
|
||||
$item['enclosures'] = array($image);
|
||||
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
public function getURI(){
|
||||
if(!is_null($this->getInput('query'))) {
|
||||
$uri = self::URI . '/search?q=' . urlencode($this->getInput('query'));
|
||||
$uri .= '&sort=' . $this->getInput('sortby');
|
||||
$uri .= '&category_id=' . $this->getInput('category');
|
||||
|
||||
return $uri;
|
||||
}
|
||||
|
||||
return parent::getURI();
|
||||
}
|
||||
|
||||
}
|
@@ -55,7 +55,7 @@ class UnsplashBridge extends BridgeAbstract {
|
||||
$item['uri'] = str_replace(
|
||||
array('q=75', 'w=400'),
|
||||
array("q=$quality", "w=$width"),
|
||||
$thumbnail->src).'.jpg'; // '.jpg' only for format hint
|
||||
$thumbnail->src) . '.jpg'; // '.jpg' only for format hint
|
||||
|
||||
$item['timestamp'] = time();
|
||||
$item['title'] = $thumbnail->alt;
|
||||
|
@@ -377,7 +377,7 @@ class VkBridge extends BridgeAbstract
|
||||
);
|
||||
$post_videos[] = $video_id;
|
||||
} else {
|
||||
$content_suffix .= '<br>Video: <a href="'.htmlspecialchars($video_link).'">'.$video_title.'</a>';
|
||||
$content_suffix .= '<br>Video: <a href="' . htmlspecialchars($video_link) . '">' . $video_title . '</a>';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -390,7 +390,7 @@ class VkBridge extends BridgeAbstract
|
||||
if (isset($result['error'])) return;
|
||||
|
||||
foreach($result['response']['items'] as $item) {
|
||||
$video_id = strval($item['owner_id']).'_'.strval($item['id']);
|
||||
$video_id = strval($item['owner_id']) . '_' . strval($item['id']);
|
||||
$this->videos[$video_id]['url'] = $item['player'];
|
||||
}
|
||||
|
||||
@@ -398,7 +398,7 @@ class VkBridge extends BridgeAbstract
|
||||
foreach($item['videos'] as $video_id) {
|
||||
$video_link = $this->videos[$video_id]['url'];
|
||||
$video_title = $this->videos[$video_id]['title'];
|
||||
$item['content'] .= '<br>Video: <a href="'.htmlspecialchars($video_link).'">'.$video_title.'</a>';
|
||||
$item['content'] .= '<br>Video: <a href="' . htmlspecialchars($video_link) . '">' . $video_title . '</a>';
|
||||
}
|
||||
unset($item['videos']);
|
||||
}
|
||||
@@ -408,6 +408,6 @@ class VkBridge extends BridgeAbstract
|
||||
{
|
||||
$params['v'] = '5.80';
|
||||
$params['access_token'] = $this->getAccessToken();
|
||||
return json_decode( getContents('https://api.vk.com/method/'.$method.'?'.http_build_query($params)), true );
|
||||
return json_decode( getContents('https://api.vk.com/method/' . $method . '?' . http_build_query($params)), true );
|
||||
}
|
||||
}
|
||||
|
@@ -16,6 +16,11 @@ class WhydBridge extends BridgeAbstract {
|
||||
|
||||
private $userName = '';
|
||||
|
||||
public function getIcon() {
|
||||
return self::URI . 'assets/favicons/
|
||||
32-6b62a9f14d5e1a9213090d8f00f286bba3a6022381a76390d1d0926493b12593.png?v=6';
|
||||
}
|
||||
|
||||
public function collectData(){
|
||||
$html = '';
|
||||
if(strlen(preg_replace('/[^0-9a-f]/', '', $this->getInput('u'))) == 24) {
|
||||
|
462
bridges/XenForoBridge.php
Normal file
462
bridges/XenForoBridge.php
Normal file
@@ -0,0 +1,462 @@
|
||||
<?php
|
||||
/**
|
||||
* This bridge generates feeds for threads from forums running XenForo version 2
|
||||
*
|
||||
* Examples:
|
||||
* - https://xenforo.com/community/
|
||||
* - http://www.ign.com/boards/
|
||||
*
|
||||
* Notice: XenForo does provide RSS feeds for forums. For example:
|
||||
* - https://xenforo.com/community/forums/-/index.rss
|
||||
*
|
||||
* For more information on XenForo, visit
|
||||
* - https://xenforo.com/
|
||||
* - https://en.wikipedia.org/wiki/XenForo
|
||||
*/
|
||||
class XenForoBridge extends BridgeAbstract {
|
||||
|
||||
// Bridge specific constants
|
||||
const CONTEXT_THREAD = 'Thread';
|
||||
const XENFORO_VERSION_1 = '1.0';
|
||||
const XENFORO_VERSION_2 = '2.0';
|
||||
|
||||
// RSS-Bridge constants
|
||||
const NAME = 'XenForo Bridge';
|
||||
const URI = 'https://xenforo.com/';
|
||||
const DESCRIPTION = 'Generates feeds for threads in forums powered by XenForo';
|
||||
const MAINTAINER = 'logmanoriginal';
|
||||
const PARAMETERS = array(
|
||||
self::CONTEXT_THREAD => array(
|
||||
'url' => array(
|
||||
'name' => 'Thread URL',
|
||||
'type' => 'text',
|
||||
'required' => true,
|
||||
'title' => 'Insert URL to the thread for which the feed should be generated',
|
||||
'exampleValue' => 'https://xenforo.com/community/threads/guide-to-suggestions.2285/'
|
||||
)
|
||||
),
|
||||
'global' => array(
|
||||
'limit' => array(
|
||||
'name' => 'Limit',
|
||||
'type' => 'number',
|
||||
'required' => false,
|
||||
'title' => 'Specify maximum number of elements to return in the feed',
|
||||
'defaultValue' => 10
|
||||
)
|
||||
)
|
||||
);
|
||||
const CACHE_TIMEOUT = 7200; // 10 minutes
|
||||
|
||||
private $title = '';
|
||||
private $threadurl = '';
|
||||
private $version; // Holds the XenForo version
|
||||
|
||||
public function getName() {
|
||||
|
||||
switch($this->queriedContext) {
|
||||
case self::CONTEXT_THREAD: return $this->title . ' - ' . static::NAME;
|
||||
}
|
||||
|
||||
return parent::getName();
|
||||
|
||||
}
|
||||
|
||||
public function getURI() {
|
||||
|
||||
switch($this->queriedContext) {
|
||||
case self::CONTEXT_THREAD: return $this->threadurl;
|
||||
}
|
||||
|
||||
return parent::getURI();
|
||||
|
||||
}
|
||||
|
||||
public function collectData() {
|
||||
|
||||
$this->threadurl = filter_var(
|
||||
$this->getInput('url'),
|
||||
FILTER_VALIDATE_URL,
|
||||
FILTER_FLAG_SCHEME_REQUIRED | FILTER_FLAG_HOST_REQUIRED | FILTER_FLAG_PATH_REQUIRED);
|
||||
|
||||
if($this->threadurl === false) {
|
||||
returnClientError('The URL you provided is invalid!');
|
||||
}
|
||||
|
||||
$urlparts = parse_url($this->threadurl, PHP_URL_SCHEME);
|
||||
|
||||
// Scheme must be "http" or "https"
|
||||
if(preg_match('/http[s]{0,1}/', parse_url($this->threadurl, PHP_URL_SCHEME)) == false) {
|
||||
returnClientError('The URL you provided doesn\'t specify a valid scheme (http or https)!');
|
||||
}
|
||||
|
||||
// Path cannot be root (../)
|
||||
if(parse_url($this->threadurl, PHP_URL_PATH) === '/') {
|
||||
returnClientError('The URL you provided doesn\'t link to a valid thread (root path)!');
|
||||
}
|
||||
|
||||
// XenForo adds a thread ID to the URL, like "...-thread.454934283". It must be present
|
||||
if(preg_match('/.+\.\d+[\/]{0,1}/', parse_URL($this->threadurl, PHP_URL_PATH)) == false) {
|
||||
returnClientError('The URL you provided doesn\'t link to a valid thread (ID missing)!');
|
||||
}
|
||||
|
||||
// We want to start at the first page in the thread. XenForo uses "../page-n" syntax
|
||||
// to identify pages (except for the first page).
|
||||
// Notice: XenForo uses the concept of "sentinels" to find and replace parts in the
|
||||
// URL. Technically forum hosts can change the syntax!
|
||||
if(preg_match('/.+\/(page-\d+.*)$/', $this->threadurl, $matches) != false) {
|
||||
|
||||
// before: https://xenforo.com/community/threads/guide-to-suggestions.2285/page-5
|
||||
// after : https://xenforo.com/community/threads/guide-to-suggestions.2285/
|
||||
$this->threadurl = str_replace($matches[1], '', $this->threadurl);
|
||||
|
||||
}
|
||||
|
||||
$html = getSimpleHTMLDOMCached($this->threadurl)
|
||||
or returnServerError('Failed loading data from "' . $this->threadurl . '"!');
|
||||
|
||||
$html = defaultLinkTo($html, $this->threadurl);
|
||||
|
||||
// Notice: The DOM structure changes depending on the XenForo version used
|
||||
if($mainContent = $html->find('div.mainContent', 0)) {
|
||||
$this->version = self::XENFORO_VERSION_1;
|
||||
} elseif ($mainContent = $html->find('div[class="p-body"]', 0)) {
|
||||
$this->version = self::XENFORO_VERSION_2;
|
||||
} else {
|
||||
returnServerError('This forum is currently not supported!');
|
||||
}
|
||||
|
||||
switch($this->version) {
|
||||
case self::XENFORO_VERSION_1:
|
||||
|
||||
$titleBar = $mainContent->find('div.titleBar h1', 0)
|
||||
or returnServerError('Error finding title bar!');
|
||||
|
||||
$this->title = $titleBar->plaintext;
|
||||
|
||||
// Store items from current page (we'll use $this->items as LIFO buffer)
|
||||
$this->extractThreadPostsV1($html, $this->threadurl);
|
||||
$this->extractPagesV1($html);
|
||||
|
||||
break;
|
||||
|
||||
case self::XENFORO_VERSION_2:
|
||||
|
||||
$titleBar = $mainContent->find('div[class="p-title"] h1', 0)
|
||||
or returnServerError('Error finding title bar!');
|
||||
|
||||
$this->title = $titleBar->plaintext;
|
||||
$this->extractThreadPostsV2($html, $this->threadurl);
|
||||
$this->extractPagesV2($html);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
while(count($this->items) > $this->getInput('limit')) {
|
||||
array_shift($this->items);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts thread posts
|
||||
* @param $html A simplehtmldom object
|
||||
* @param $url The url from which $html was loaded
|
||||
*/
|
||||
private function extractThreadPostsV1($html, $url) {
|
||||
|
||||
$lang = $html->find('html', 0)->lang;
|
||||
|
||||
// Posts are contained in an "ol"
|
||||
$messageList = $html->find('#messageList li')
|
||||
or returnServerError('Error finding message list!');
|
||||
|
||||
foreach($messageList as $post) {
|
||||
|
||||
if(!isset($post->attr['id'])) { // Skip ads
|
||||
continue;
|
||||
}
|
||||
|
||||
$item = array();
|
||||
|
||||
$item['uri'] = $url . '#' . $post->getAttribute('id');
|
||||
|
||||
$content = $post->find('.messageContent article', 0);
|
||||
|
||||
// Add some style to quotes
|
||||
foreach($content->find('.bbCodeQuote') as $quote) {
|
||||
$quote->style = '
|
||||
color: #495566;
|
||||
background-color: rgb(248,251,253);
|
||||
border: 1px solid rgb(111, 140, 180);
|
||||
border-color: rgb(111, 140, 180);
|
||||
font-style: italic;';
|
||||
}
|
||||
|
||||
// Remove script tags
|
||||
foreach($content->find('script') as $script) {
|
||||
$script->outertext = '';
|
||||
}
|
||||
|
||||
$item['content'] = $content->innertext;
|
||||
|
||||
// Remove quotes (for the title)
|
||||
foreach($content->find('.bbCodeQuote') as $quote) {
|
||||
$quote->innertext = '';
|
||||
}
|
||||
|
||||
$title = trim($content->plaintext);
|
||||
|
||||
if(strlen($title) > 70) {
|
||||
$item['title'] = substr($title, 0, strpos($title, ' ', 70)) . '...';
|
||||
} else {
|
||||
$item['title'] = $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Timestamps are presented in two forms:
|
||||
*
|
||||
* 1) short version (for older posts?)
|
||||
* <span
|
||||
* class="DateTime"
|
||||
* title="22 Oct. 2018 at 23:47"
|
||||
* >22 Oct. 2018</span>
|
||||
*
|
||||
* This form has to be interpreted depending on the current language.
|
||||
*
|
||||
* 2) long version (for newer posts?)
|
||||
* <abbr
|
||||
* class="DateTime"
|
||||
* data-time="1541008785"
|
||||
* data-diff="310694"
|
||||
* data-datestring="31 Oct. 2018"
|
||||
* data-timestring="18:59"
|
||||
* title="31 Oct. 2018 at 18:59"
|
||||
* >Wednesday at 18:59</abbr>
|
||||
*
|
||||
* This form has the timestamp embedded (data-time)
|
||||
*/
|
||||
if($timestamp = $post->find('abbr.DateTime', 0)) { // long version (preffered)
|
||||
$item['timestamp'] = $timestamp->{'data-time'};
|
||||
} elseif($timestamp = $post->find('span.DateTime', 0)) { // short version
|
||||
$item['timestamp'] = $this->fixDate($timestamp->title, $lang);
|
||||
}
|
||||
|
||||
$item['author'] = $post->getAttribute('data-author');
|
||||
|
||||
// Bridge specific properties
|
||||
$item['id'] = $post->getAttribute('id');
|
||||
|
||||
$this->items[] = $item;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function extractThreadPostsV2($html, $url) {
|
||||
|
||||
$lang = $html->find('html', 0)->lang;
|
||||
|
||||
$messageList = $html->find('div[class="block-body"] article')
|
||||
or returnServerError('Error finding message list!');
|
||||
|
||||
foreach($messageList as $post) {
|
||||
|
||||
if(!isset($post->attr['id'])) { // Skip ads
|
||||
continue;
|
||||
}
|
||||
|
||||
$item = array();
|
||||
|
||||
$item['uri'] = $url . '#' . $post->getAttribute('id');
|
||||
|
||||
$title = $post->find('div[class="message-content"] article', 0)->plaintext;
|
||||
$end = strpos($title, ' ', 70);
|
||||
$item['title'] = substr($title, 0, $end);
|
||||
|
||||
$item['timestamp'] = $this->fixDate($post->find('time', 0)->title, $lang);
|
||||
$item['author'] = $post->getAttribute('data-author');
|
||||
$item['content'] = $post->find('div[class="message-content"] article', 0);
|
||||
|
||||
// Bridge specific properties
|
||||
$item['id'] = $post->getAttribute('id');
|
||||
|
||||
$this->items[] = $item;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function extractPagesV1($html) {
|
||||
|
||||
// A navigation bar becomes available if the number of posts grows too
|
||||
// high. When this happens we need to load further pages (from last backwards)
|
||||
if(($pageNav = $html->find('div.PageNav', 0))) {
|
||||
|
||||
$lastpage = $pageNav->{'data-last'};
|
||||
$baseurl = $pageNav->{'data-baseurl'};
|
||||
$sentinel = $pageNav->{'data-sentinel'};
|
||||
|
||||
$hosturl = parse_url($this->threadurl, PHP_URL_SCHEME)
|
||||
. '://'
|
||||
. parse_url($this->threadurl, PHP_URL_HOST)
|
||||
. '/';
|
||||
|
||||
$page = $lastpage;
|
||||
|
||||
// Load at least the last page
|
||||
do {
|
||||
|
||||
$pageurl = $hosturl . str_replace($sentinel, $lastpage, $baseurl);
|
||||
|
||||
// We can optimize performance by caching all but the last page
|
||||
if($page != $lastpage) {
|
||||
$html = getSimpleHTMLDOMCached($pageurl)
|
||||
or returnServerError('Error loading contents from ' . $pageurl . '!');
|
||||
} else {
|
||||
$html = getSimpleHTMLDOM($pageurl)
|
||||
or returnServerError('Error loading contents from ' . $pageurl . '!');
|
||||
}
|
||||
|
||||
$html = defaultLinkTo($html, $hosturl);
|
||||
|
||||
$this->extractThreadPostsV1($html, $pageurl);
|
||||
|
||||
$page--;
|
||||
|
||||
} while (count($this->items) < $this->getInput('limit') && $page != 1);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function extractPagesV2($html) {
|
||||
|
||||
// A navigation bar becomes available if the number of posts grows too
|
||||
// high. When this happens we need to load further pages (from last backwards)
|
||||
if(($pageNav = $html->find('div.pageNav', 0))) {
|
||||
|
||||
foreach($pageNav->find('li') as $nav) {
|
||||
$lastpage = $nav->plaintext;
|
||||
}
|
||||
|
||||
// Manually extract baseurl and inject sentinel
|
||||
$baseurl = $pageNav->find('li a', -1)->href;
|
||||
$baseurl = str_replace('page-' . $lastpage, 'page-{{sentinel}}', $baseurl);
|
||||
|
||||
$sentinel = '{{sentinel}}';
|
||||
|
||||
$hosturl = parse_url($this->threadurl, PHP_URL_SCHEME)
|
||||
. '://'
|
||||
. parse_url($this->threadurl, PHP_URL_HOST);
|
||||
|
||||
$page = $lastpage;
|
||||
|
||||
// Load at least the last page
|
||||
do {
|
||||
|
||||
$pageurl = $hosturl . str_replace($sentinel, $lastpage, $baseurl);
|
||||
|
||||
// We can optimize performance by caching all but the last page
|
||||
if($page != $lastpage) {
|
||||
$html = getSimpleHTMLDOMCached($pageurl)
|
||||
or returnServerError('Error loading contents from ' . $pageurl . '!');
|
||||
} else {
|
||||
$html = getSimpleHTMLDOM($pageurl)
|
||||
or returnServerError('Error loading contents from ' . $pageurl . '!');
|
||||
}
|
||||
|
||||
$html = defaultLinkTo($html, $this->hosturl);
|
||||
|
||||
$this->extractThreadPostsV2($html, $this->pageurl);
|
||||
|
||||
$page--;
|
||||
|
||||
} while (count($this->items) < $this->getInput('limit') && $page != 1);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixes dates depending on the choosen language:
|
||||
*
|
||||
* de : dd.mm.yy
|
||||
* en : dd.mm.yy
|
||||
* it : dd/mm/yy
|
||||
*
|
||||
* Basically strtotime doesn't convert dates correctly due to formats
|
||||
* being hard to interpret. So we use the DateTime object.
|
||||
*
|
||||
* We don't know the timezone, so just assume +00:00 (or whatever
|
||||
* DateTime chooses)
|
||||
*/
|
||||
private function fixDate($date, $lang = 'en-US') {
|
||||
|
||||
$mnamesen = [
|
||||
'January',
|
||||
'Feburary',
|
||||
'March',
|
||||
'April',
|
||||
'May',
|
||||
'June',
|
||||
'July',
|
||||
'August',
|
||||
'September',
|
||||
'October',
|
||||
'November',
|
||||
'December'
|
||||
];
|
||||
|
||||
switch($lang) {
|
||||
case 'en-US': // example: Jun 9, 2018 at 11:46 PM
|
||||
|
||||
$df = date_create_from_format('M d, Y \a\t H:i A', $date);
|
||||
break;
|
||||
|
||||
case 'de-DE': // example: 19 Juli 2018 um 19:27 Uhr
|
||||
|
||||
$mnamesde = [
|
||||
'Januar',
|
||||
'Februar',
|
||||
'März',
|
||||
'April',
|
||||
'Mai',
|
||||
'Juni',
|
||||
'Juli',
|
||||
'August',
|
||||
'September',
|
||||
'Oktober',
|
||||
'November',
|
||||
'Dezember'
|
||||
];
|
||||
|
||||
$mnamesdeshort = [
|
||||
'Jan.',
|
||||
'Feb.',
|
||||
'Mär.',
|
||||
'Apr.',
|
||||
'Mai',
|
||||
'Juni',
|
||||
'Juli',
|
||||
'Aug.',
|
||||
'Sep.',
|
||||
'Okt.',
|
||||
'Nov.',
|
||||
'Dez.'
|
||||
];
|
||||
|
||||
$date = str_ireplace($mnamesde, $mnamesen, $date);
|
||||
$date = str_ireplace($mnamesdeshort, $mnamesen, $date);
|
||||
|
||||
$df = date_create_from_format('d M Y \u\m H:i \U\h\r', $date);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
// debugMessage(date_format($df, 'U'));
|
||||
|
||||
return date_format($df, 'U');
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -147,12 +147,19 @@ class YoutubeBridge extends BridgeAbstract {
|
||||
$time = 0;
|
||||
$vid = str_replace('/watch?v=', '', $element->find('a', 0)->href);
|
||||
$vid = substr($vid, 0, strpos($vid, '&') ?: strlen($vid));
|
||||
$title = $this->ytBridgeFixTitle($element->find($title_selector, 0)->plaintext);
|
||||
$title = trim($this->ytBridgeFixTitle($element->find($title_selector, 0)->plaintext));
|
||||
|
||||
if (strpos($vid, 'googleads') !== false
|
||||
|| $title == '[Private video]'
|
||||
|| $title == '[Deleted video]'
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// The duration comes in one of the formats:
|
||||
// hh:mm:ss / mm:ss / m:ss
|
||||
// 01:03:30 / 15:06 / 1:24
|
||||
$durationText = trim($element->find('span[class="video-time"]', 0)->plaintext);
|
||||
$durationText = trim($element->find('div.timestamp span', 0)->plaintext);
|
||||
$durationText = preg_replace('/([\d]{1,2})\:([\d]{2})/', '00:$1:$2', $durationText);
|
||||
|
||||
sscanf($durationText, '%d:%d:%d', $hours, $minutes, $seconds);
|
||||
@@ -162,13 +169,11 @@ class YoutubeBridge extends BridgeAbstract {
|
||||
continue;
|
||||
}
|
||||
|
||||
if($title != '[Private Video]' && strpos($vid, 'googleads') === false) {
|
||||
if ($add_parsed_items) {
|
||||
$this->ytBridgeQueryVideoInfo($vid, $author, $desc, $time);
|
||||
$this->ytBridgeAddItem($vid, $title, $author, $desc, $time);
|
||||
}
|
||||
$count++;
|
||||
if ($add_parsed_items) {
|
||||
$this->ytBridgeQueryVideoInfo($vid, $author, $desc, $time);
|
||||
$this->ytBridgeAddItem($vid, $title, $author, $desc, $time);
|
||||
}
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
return $count;
|
||||
@@ -181,7 +186,9 @@ class YoutubeBridge extends BridgeAbstract {
|
||||
|
||||
private function ytGetSimpleHTMLDOM($url){
|
||||
return getSimpleHTMLDOM($url,
|
||||
$header = array(),
|
||||
$header = array(
|
||||
'Accept-Language: en-US'
|
||||
),
|
||||
$opts = array(),
|
||||
$lowercase = true,
|
||||
$forceTagsClosed = true,
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
class ZoneTelechargementBridge extends BridgeAbstract {
|
||||
const NAME = 'Zone Telechargement';
|
||||
const URI = 'https://ww4.zone-telechargement1.org/';
|
||||
const URI = 'https://www.zone-telechargement1.org/';
|
||||
const DESCRIPTION = 'Suivi de série sur Zone Telechargement';
|
||||
const MAINTAINER = 'sysadminstory';
|
||||
const PARAMETERS = array(
|
||||
@@ -12,9 +12,13 @@ class ZoneTelechargementBridge extends BridgeAbstract {
|
||||
'required' => true,
|
||||
'title' => 'URL d\'une série sans le https://ww4.zone-telechargement1.org/',
|
||||
'exampleValue' => 'telecharger-series/31079-halt-and-catch-fire-saison-4-french-hd720p.html'
|
||||
)
|
||||
)
|
||||
);
|
||||
)
|
||||
);
|
||||
|
||||
public function getIcon() {
|
||||
return 'https://ww7.zone-telechargement1.org/templates/Default/images/favicon.ico';
|
||||
}
|
||||
|
||||
public function collectData(){
|
||||
$html = getSimpleHTMLDOM(self::URI . $this->getInput('url'))
|
||||
@@ -39,7 +43,7 @@ class ZoneTelechargementBridge extends BridgeAbstract {
|
||||
$hoster = $this->findLinkHoster($element);
|
||||
|
||||
// Format the link and add the link to the corresponding episode table
|
||||
$episodes[$epnumber][] = '<a href="' . $element->href . '">'. $hoster . ' - '
|
||||
$episodes[$epnumber][] = '<a href="' . $element->href . '">' . $hoster . ' - '
|
||||
. $this->showTitle . ' Episode ' . $epnumber . '</a>';
|
||||
|
||||
}
|
||||
@@ -58,7 +62,7 @@ class ZoneTelechargementBridge extends BridgeAbstract {
|
||||
}
|
||||
}
|
||||
|
||||
public function getName(){
|
||||
public function getName() {
|
||||
switch($this->queriedContext) {
|
||||
case 'Suivre la publication des épisodes d\'une série en cours de diffusion':
|
||||
return $this->showTitle . ' - ' . self::NAME;
|
||||
@@ -68,8 +72,7 @@ class ZoneTelechargementBridge extends BridgeAbstract {
|
||||
}
|
||||
}
|
||||
|
||||
private function findLinkHoster($element)
|
||||
{
|
||||
private function findLinkHoster($element) {
|
||||
// The hoster name is one level higher than the link tag : get the parent element
|
||||
$element = $element->parent();
|
||||
//echo "PARENT : $element \n";
|
||||
|
@@ -11,6 +11,12 @@
|
||||
; false = disabled (default)
|
||||
custom_timeout = false
|
||||
|
||||
[admin]
|
||||
; Advertise an email address where people can reach the administrator.
|
||||
; This address is displayed on the main page, visible to everyone!
|
||||
; "" = Disabled (default)
|
||||
email = ""
|
||||
|
||||
[proxy]
|
||||
|
||||
; Sets the proxy url (i.e. "tcp://192.168.0.0:32")
|
||||
|
@@ -21,7 +21,7 @@ class AtomFormat extends FormatAbstract{
|
||||
if(!empty($extraInfos['icon'])) {
|
||||
$icon = $extraInfos['icon'];
|
||||
} else {
|
||||
$icon = $this->xml_encode($uriparts['scheme'] . '://' . $uriparts['host'] .'/favicon.ico');
|
||||
$icon = $this->xml_encode($uriparts['scheme'] . '://' . $uriparts['host'] . '/favicon.ico');
|
||||
}
|
||||
|
||||
$uri = $this->xml_encode($uri);
|
||||
|
@@ -22,7 +22,7 @@ class MrssFormat extends FormatAbstract {
|
||||
}
|
||||
|
||||
$uriparts = parse_url($uri);
|
||||
$icon = $this->xml_encode($uriparts['scheme'] . '://' . $uriparts['host'] .'/favicon.ico');
|
||||
$icon = $this->xml_encode($uriparts['scheme'] . '://' . $uriparts['host'] . '/favicon.ico');
|
||||
|
||||
$items = '';
|
||||
foreach($this->getItems() as $item) {
|
||||
@@ -56,7 +56,7 @@ Some media files might not be shown to you. Consider using the ATOM format inste
|
||||
|
||||
foreach($item['categories'] as $category) {
|
||||
$entryCategories .= '<category>'
|
||||
. $category . '</category>'
|
||||
. $category . '</category>'
|
||||
. PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
12
index.php
12
index.php
@@ -202,6 +202,7 @@ try {
|
||||
'format',
|
||||
'_noproxy',
|
||||
'_cache_timeout',
|
||||
'_error_time'
|
||||
), '')
|
||||
);
|
||||
|
||||
@@ -214,6 +215,7 @@ try {
|
||||
'format',
|
||||
'_noproxy',
|
||||
'_cache_timeout',
|
||||
'_error_time'
|
||||
), '')
|
||||
);
|
||||
|
||||
@@ -262,6 +264,8 @@ try {
|
||||
'icon' => $bridge->getIcon()
|
||||
);
|
||||
} catch(Error $e) {
|
||||
error_log($e);
|
||||
|
||||
$item = array();
|
||||
|
||||
// Create "new" error message every 24 hours
|
||||
@@ -280,6 +284,8 @@ try {
|
||||
|
||||
$items[] = $item;
|
||||
} catch(Exception $e) {
|
||||
error_log($e);
|
||||
|
||||
$item = array();
|
||||
|
||||
// Create "new" error message every 24 hours
|
||||
@@ -306,13 +312,15 @@ try {
|
||||
$format = Format::create($format);
|
||||
$format->setItems($items);
|
||||
$format->setExtraInfos($infos);
|
||||
$format->setLastModified($mtime);
|
||||
$format->setLastModified($cache->getTime());
|
||||
$format->display();
|
||||
} catch(Error $e) {
|
||||
error_log($e);
|
||||
http_response_code($e->getCode());
|
||||
header('Content-Type: text/html');
|
||||
die(buildTransformException($e, $bridge));
|
||||
} catch(Exception $e) {
|
||||
error_log($e);
|
||||
http_response_code($e->getCode());
|
||||
header('Content-Type: text/html');
|
||||
die(buildTransformException($e, $bridge));
|
||||
@@ -321,9 +329,11 @@ try {
|
||||
echo BridgeList::create($whitelist_selection, $showInactive);
|
||||
}
|
||||
} catch(HttpException $e) {
|
||||
error_log($e);
|
||||
http_response_code($e->getCode());
|
||||
header('Content-Type: text/plain');
|
||||
die($e->getMessage());
|
||||
} catch(\Exception $e) {
|
||||
error_log($e);
|
||||
die($e->getMessage());
|
||||
}
|
||||
|
@@ -193,7 +193,7 @@ This bridge is not fetching its content through a secure connection</div>';
|
||||
. '" type="checkbox" name="'
|
||||
. $name
|
||||
. '" '
|
||||
. ($entry['defaultValue'] === 'checked' ?: '')
|
||||
. ($entry['defaultValue'] === 'checked' ? 'checked' : '')
|
||||
. ' />'
|
||||
. PHP_EOL;
|
||||
}
|
||||
|
@@ -96,6 +96,18 @@ EOD;
|
||||
private static function getFooter($totalBridges, $totalActiveBridges, $showInactive) {
|
||||
$version = Configuration::getVersion();
|
||||
|
||||
$email = Configuration::getConfig('admin', 'email');
|
||||
$admininfo = '';
|
||||
if (!empty($email)) {
|
||||
$admininfo = <<<EOD
|
||||
<br />
|
||||
<span>
|
||||
You may email the administrator of this RSS-Bridge instance
|
||||
at <a href="mailto:{$email}">{$email}</a>
|
||||
</span>
|
||||
EOD;
|
||||
}
|
||||
|
||||
$inactive = '';
|
||||
|
||||
if($totalActiveBridges !== $totalBridges) {
|
||||
@@ -114,6 +126,7 @@ EOD;
|
||||
<p class="version">{$version}</p>
|
||||
{$totalActiveBridges}/{$totalBridges} active bridges.<br>
|
||||
{$inactive}
|
||||
{$admininfo}
|
||||
</section>
|
||||
EOD;
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
class Configuration {
|
||||
|
||||
public static $VERSION = '2018-10-15';
|
||||
public static $VERSION = 'dev.2018-10-15';
|
||||
|
||||
public static $config = null;
|
||||
|
||||
@@ -107,7 +107,8 @@ class Configuration {
|
||||
|
||||
$headFile = '.git/HEAD';
|
||||
|
||||
if(file_exists($headFile)) {
|
||||
// '@' is used to mute open_basedir warning
|
||||
if(@is_readable($headFile)) {
|
||||
|
||||
$revisionHashFile = '.git/' . substr(file_get_contents($headFile), 5, -1);
|
||||
$branchName = explode('/', $revisionHashFile)[3];
|
||||
|
@@ -1,24 +1,37 @@
|
||||
<?php
|
||||
function getContents($url, $header = array(), $opts = array()){
|
||||
debugMessage('Reading contents from "' . $url . '"');
|
||||
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
||||
|
||||
if(is_array($header) && count($header) !== 0)
|
||||
if(is_array($header) && count($header) !== 0) {
|
||||
|
||||
debugMessage('Setting headers: ' . json_encode($header));
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
|
||||
|
||||
}
|
||||
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, ini_get('user_agent'));
|
||||
curl_setopt($ch, CURLOPT_ENCODING, '');
|
||||
curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
|
||||
|
||||
if(is_array($opts)) {
|
||||
if(is_array($opts) && count($opts) !== 0) {
|
||||
|
||||
debugMessage('Setting options: ' . json_encode($opts));
|
||||
|
||||
foreach($opts as $key => $value) {
|
||||
curl_setopt($ch, $key, $value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(defined('PROXY_URL') && !defined('NOPROXY')) {
|
||||
|
||||
debugMessage('Setting proxy url: ' . PROXY_URL);
|
||||
curl_setopt($ch, CURLOPT_PROXY, PROXY_URL);
|
||||
|
||||
}
|
||||
|
||||
// We always want the response header as part of the data!
|
||||
@@ -34,6 +47,9 @@ function getContents($url, $header = array(), $opts = array()){
|
||||
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
|
||||
$errorCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$header = substr($data, 0, $headerSize);
|
||||
|
||||
debugMessage('Response header: ' . $header);
|
||||
|
||||
$headers = parseResponseHeader($header);
|
||||
$finalHeader = end($headers);
|
||||
|
||||
@@ -48,7 +64,7 @@ EOD
|
||||
}
|
||||
|
||||
returnError(<<<EOD
|
||||
The requested resouce cannot be found!
|
||||
The requested resource cannot be found!
|
||||
Please make sure your input parameters are correct!
|
||||
EOD
|
||||
, $errorCode);
|
||||
|
@@ -155,8 +155,8 @@ function markdownToHtml($string) {
|
||||
$string = preg_replace('/_(.*)_/U', '<i>$1</i>', $string);
|
||||
$string = preg_replace('/[-]{6,99}/', '<hr />', $string);
|
||||
$string = str_replace(' ', '<br />', $string);
|
||||
$string = preg_replace('/([^"])(https?:\/\/[^ "<]+)([^"])/', '$1<a href="$2">$2</a>$3', $string.' ');
|
||||
$string = preg_replace('/([^"\/])(www\.[^ "<]+)([^"])/', '$1<a href="http://$2">$2</a>$3', $string.' ');
|
||||
$string = preg_replace('/([^"])(https?:\/\/[^ "<]+)([^"])/', '$1<a href="$2">$2</a>$3', $string . ' ');
|
||||
$string = preg_replace('/([^"\/])(www\.[^ "<]+)([^"])/', '$1<a href="http://$2">$2</a>$3', $string . ' ');
|
||||
|
||||
//As the regex are not perfect, we need to fix <i> and </i> that are introduced in URLs
|
||||
// Fixup regex <i>: https://regex101.com/r/NTRPf6/1
|
||||
|
@@ -13,6 +13,13 @@
|
||||
<rule ref="Generic.CodeAnalysis.UnnecessaryFinalModifier"/>
|
||||
<!-- Do not override methods to call their parent -->
|
||||
<rule ref="Generic.CodeAnalysis.UselessOverridingMethod"/>
|
||||
<!-- Make sure the concatenation operator has spaces around it -->
|
||||
<rule ref="Squiz.Strings.ConcatenationSpacing">
|
||||
<properties>
|
||||
<property name="spacing" value="1"/>
|
||||
<property name="ignoreNewlines" value="true"/>
|
||||
</properties>
|
||||
</rule>
|
||||
<!-- One line should not have more than 80 characters -->
|
||||
<!-- One line must never exceed 120 characters -->
|
||||
<rule ref="Generic.Files.LineLength">
|
||||
|
9
vendor/php-urljoin/src/urljoin.php
vendored
9
vendor/php-urljoin/src/urljoin.php
vendored
@@ -26,6 +26,15 @@ function urljoin($base, $rel) {
|
||||
$pbase = parse_url($base);
|
||||
$prel = parse_url($rel);
|
||||
|
||||
if ($prel === false || preg_match('/^[a-z0-9\-.]*[^a-z0-9\-.:][a-z0-9\-.]*:/i', $rel)) {
|
||||
/*
|
||||
Either parse_url couldn't parse this, or the original URL
|
||||
fragment had an invalid scheme character before the first :,
|
||||
which can confuse parse_url
|
||||
*/
|
||||
$prel = array('path' => $rel);
|
||||
}
|
||||
|
||||
if (array_key_exists('path', $pbase) && $pbase['path'] === '/') {
|
||||
unset($pbase['path']);
|
||||
}
|
||||
|
Reference in New Issue
Block a user