aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--actions/ConnectivityAction.php8
-rw-r--r--actions/DetectAction.php4
-rw-r--r--actions/DisplayAction.php14
-rw-r--r--actions/FrontpageAction.php142
-rw-r--r--actions/ListAction.php5
-rw-r--r--lib/ActionFactory.php38
-rw-r--r--lib/ActionInterface.php2
-rw-r--r--lib/RssBridge.php22
-rw-r--r--lib/contents.php32
-rw-r--r--static/style.css4
-rw-r--r--templates/frontpage.html.php62
-rw-r--r--tests/Actions/ListActionTest.php39
12 files changed, 147 insertions, 225 deletions
diff --git a/actions/ConnectivityAction.php b/actions/ConnectivityAction.php
index 9ebd640c..19e6b9a6 100644
--- a/actions/ConnectivityAction.php
+++ b/actions/ConnectivityAction.php
@@ -38,8 +38,7 @@ class ConnectivityAction implements ActionInterface
}
if (!isset($request['bridge'])) {
- print render_template('connectivity.html.php');
- return;
+ return render_template('connectivity.html.php');
}
$bridgeClassName = $this->bridgeFactory->sanitizeBridgeName($request['bridge']);
@@ -48,7 +47,7 @@ class ConnectivityAction implements ActionInterface
throw new \InvalidArgumentException('Bridge name invalid!');
}
- $this->reportBridgeConnectivity($bridgeClassName);
+ return $this->reportBridgeConnectivity($bridgeClassName);
}
private function reportBridgeConnectivity($bridgeClassName)
@@ -80,7 +79,6 @@ class ConnectivityAction implements ActionInterface
$retVal['successful'] = false;
}
- header('Content-Type: text/json');
- print Json::encode($retVal);
+ return new Response(Json::encode($retVal), 200, ['Content-Type' => 'text/json']);
}
}
diff --git a/actions/DetectAction.php b/actions/DetectAction.php
index 71060bb8..6524bdfe 100644
--- a/actions/DetectAction.php
+++ b/actions/DetectAction.php
@@ -44,8 +44,8 @@ class DetectAction implements ActionInterface
$bridgeParams['bridge'] = $bridgeClassName;
$bridgeParams['format'] = $format;
- header('Location: ?action=display&' . http_build_query($bridgeParams), true, 301);
- return;
+ $url = '?action=display&' . http_build_query($bridgeParams);
+ return new Response('', 301, ['Location' => $url]);
}
throw new \Exception('No bridge found for given URL: ' . $targetURL);
diff --git a/actions/DisplayAction.php b/actions/DisplayAction.php
index 67c7c741..a0b23fd0 100644
--- a/actions/DisplayAction.php
+++ b/actions/DisplayAction.php
@@ -52,8 +52,7 @@ class DisplayAction implements ActionInterface
if (! Configuration::getConfig('cache', 'custom_timeout')) {
unset($request['_cache_timeout']);
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) . '?' . http_build_query($request);
- header('Location: ' . $uri, true, 301);
- return;
+ return new Response('', 301, ['Location' => $uri]);
}
$cache_timeout = filter_var($request['_cache_timeout'], FILTER_VALIDATE_INT);
@@ -116,8 +115,8 @@ class DisplayAction implements ActionInterface
if ($mtime <= $stime) {
// Cached data is older or same
- header('Last-Modified: ' . gmdate('D, d M Y H:i:s ', $mtime) . 'GMT', true, 304);
- return;
+ $lastModified2 = gmdate('D, d M Y H:i:s ', $mtime) . 'GMT';
+ return new Response('', 304, ['Last-Modified' => $lastModified2]);
}
}
@@ -197,11 +196,12 @@ class DisplayAction implements ActionInterface
$format->setExtraInfos($infos);
$lastModified = $cache->getTime();
$format->setLastModified($lastModified);
+ $headers = [];
if ($lastModified) {
- header('Last-Modified: ' . gmdate('D, d M Y H:i:s ', $lastModified) . 'GMT');
+ $headers['Last-Modified'] = gmdate('D, d M Y H:i:s ', $lastModified) . 'GMT';
}
- header('Content-Type: ' . $format->getMimeType() . '; charset=' . $format->getCharset());
- print $format->stringify();
+ $headers['Content-Type'] = $format->getMimeType() . '; charset=' . $format->getCharset();
+ return new Response($format->stringify(), 200, $headers);
}
private static function createGithubIssueUrl($bridge, $e, string $message): string
diff --git a/actions/FrontpageAction.php b/actions/FrontpageAction.php
index 9980facf..4b9d7cd3 100644
--- a/actions/FrontpageAction.php
+++ b/actions/FrontpageAction.php
@@ -5,91 +5,7 @@ final class FrontpageAction implements ActionInterface
public function execute(array $request)
{
$showInactive = (bool) ($request['show_inactive'] ?? null);
-
- $totalBridges = 0;
- $totalActiveBridges = 0;
-
- $html = self::getHead()
- . self::getHeader()
- . self::getSearchbar()
- . self::getBridges($showInactive, $totalBridges, $totalActiveBridges)
- . self::getFooter($totalBridges, $totalActiveBridges, $showInactive);
-
- print $html;
- }
-
- private static function getHead()
- {
- return <<<EOD
-<!DOCTYPE html><html lang="en">
-<head>
- <meta charset="utf-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
- <meta name="description" content="RSS-Bridge" />
- <title>RSS-Bridge</title>
- <link href="static/style.css" rel="stylesheet">
- <link rel="icon" type="image/png" href="static/favicon.png">
- <script src="static/rss-bridge.js"></script>
- <script>
- document.addEventListener('DOMContentLoaded', rssbridge_toggle_bridge);
- </script>
-</head>
-<body onload="rssbridge_list_search()">
-<div class="container">
-EOD;
- }
-
- private static function getHeader()
- {
- $warning = '';
-
- if (Debug::isEnabled()) {
- if (!Debug::isSecure()) {
- $warning .= <<<EOD
-<section class="critical-warning">Warning : Debug mode is active from any location,
- make sure only you can access RSS-Bridge.</section>
-EOD;
- } else {
- $warning .= <<<EOD
-<section class="warning">Warning : Debug mode is active from your IP address,
- your requests will bypass the cache.</section>
-EOD;
- }
- }
-
- return <<<EOD
-<header>
- <div class="logo"></div>
- {$warning}
-</header>
-EOD;
- }
-
- private static function getSearchbar()
- {
- $query = filter_input(INPUT_GET, 'q', \FILTER_SANITIZE_SPECIAL_CHARS);
-
- return <<<EOD
-<section class="searchbar">
- <h3>Search</h3>
- <input
- type="text"
- name="searchfield"
- id="searchfield"
- placeholder="Insert URL or bridge name"
- onchange="rssbridge_list_search()"
- onkeyup="rssbridge_list_search()"
- value="{$query}"
- >
-</section>
-EOD;
- }
-
- private static function getBridges($showInactive, &$totalBridges, &$totalActiveBridges)
- {
- $body = '';
- $totalActiveBridges = 0;
- $inactiveBridges = '';
+ $activeBridges = 0;
$bridgeFactory = new BridgeFactory();
$bridgeClassNames = $bridgeFactory->getBridgeClassNames();
@@ -97,58 +13,22 @@ EOD;
$formatFactory = new FormatFactory();
$formats = $formatFactory->getFormatNames();
- $totalBridges = count($bridgeClassNames);
-
+ $body = '';
foreach ($bridgeClassNames as $bridgeClassName) {
if ($bridgeFactory->isWhitelisted($bridgeClassName)) {
$body .= BridgeCard::displayBridgeCard($bridgeClassName, $formats);
- $totalActiveBridges++;
+ $activeBridges++;
} elseif ($showInactive) {
- $inactiveBridges .= BridgeCard::displayBridgeCard($bridgeClassName, $formats, false) . PHP_EOL;
- }
- }
-
- $body .= $inactiveBridges;
-
- return $body;
- }
-
- private static function getFooter($totalBridges, $totalActiveBridges, $showInactive)
- {
- $version = Configuration::getVersion();
-
- $email = Configuration::getConfig('admin', 'email');
- $admininfo = '';
- if ($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) {
- if ($showInactive) {
- $inactive = '<a href="?show_inactive=0"><button class="small">Hide inactive bridges</button></a><br>';
- } else {
- $inactive = '<a href="?show_inactive=1"><button class="small">Show inactive bridges</button></a><br>';
+ $body .= BridgeCard::displayBridgeCard($bridgeClassName, $formats, false) . PHP_EOL;
}
}
- return <<<EOD
-<section class="footer">
- <a href="https://github.com/rss-bridge/rss-bridge">RSS-Bridge ~ Public Domain</a><br>
- <p class="version">{$version}</p>
- {$totalActiveBridges}/{$totalBridges} active bridges.<br>
- {$inactive}
- {$admininfo}
-</section>
-</div>
-</body></html>
-EOD;
+ return render(__DIR__ . '/../templates/frontpage.html.php', [
+ 'admin_email' => Configuration::getConfig('admin', 'email'),
+ 'bridges' => $body,
+ 'active_bridges' => $activeBridges,
+ 'total_bridges' => count($bridgeClassNames),
+ 'show_inactive' => $showInactive,
+ ]);
}
}
diff --git a/actions/ListAction.php b/actions/ListAction.php
index e2b0ccb9..3e151690 100644
--- a/actions/ListAction.php
+++ b/actions/ListAction.php
@@ -36,10 +36,7 @@ class ListAction implements ActionInterface
'description' => $bridge->getDescription()
];
}
-
$list->total = count($list->bridges);
-
- header('Content-Type: application/json');
- print Json::encode($list);
+ return new Response(Json::encode($list), 200, ['Content-Type' => 'application/json']);
}
}
diff --git a/lib/ActionFactory.php b/lib/ActionFactory.php
deleted file mode 100644
index c97891b7..00000000
--- a/lib/ActionFactory.php
+++ /dev/null
@@ -1,38 +0,0 @@
-<?php
-
-/**
- * This file is part of RSS-Bridge, a PHP project capable of generating RSS and
- * Atom feeds for websites that don't have one.
- *
- * For the full license information, please view the UNLICENSE file distributed
- * with this source code.
- *
- * @package Core
- * @license http://unlicense.org/ UNLICENSE
- * @link https://github.com/rss-bridge/rss-bridge
- */
-
-class ActionFactory
-{
- private $folder;
-
- public function __construct(string $folder = PATH_LIB_ACTIONS)
- {
- $this->folder = $folder;
- }
-
- /**
- * @param string $name The name of the action e.g. "Display", "List", or "Connectivity"
- */
- public function create(string $name): ActionInterface
- {
- $name = strtolower($name) . 'Action';
- $name = implode(array_map('ucfirst', explode('-', $name)));
- $filePath = $this->folder . $name . '.php';
- if (!file_exists($filePath)) {
- throw new \Exception('Invalid action');
- }
- $className = '\\' . $name;
- return new $className();
- }
-}
diff --git a/lib/ActionInterface.php b/lib/ActionInterface.php
index ea5020a3..4eb9cc65 100644
--- a/lib/ActionInterface.php
+++ b/lib/ActionInterface.php
@@ -22,7 +22,7 @@ interface ActionInterface
*
* Note: This function directly outputs data to the user.
*
- * @return void
+ * @return ?string
*/
public function execute(array $request);
}
diff --git a/lib/RssBridge.php b/lib/RssBridge.php
index 3ff118f7..904a1cd4 100644
--- a/lib/RssBridge.php
+++ b/lib/RssBridge.php
@@ -60,6 +60,7 @@ final class RssBridge
}
});
+ // Consider: ini_set('error_reporting', E_ALL & ~E_DEPRECATED);
date_default_timezone_set(Configuration::getConfig('system', 'timezone'));
$authenticationMiddleware = new AuthenticationMiddleware();
@@ -73,9 +74,22 @@ final class RssBridge
}
}
- $actionFactory = new ActionFactory();
- $action = $request['action'] ?? 'Frontpage';
- $action = $actionFactory->create($action);
- $action->execute($request);
+ $actionName = $request['action'] ?? 'Frontpage';
+ $actionName = strtolower($actionName) . 'Action';
+ $actionName = implode(array_map('ucfirst', explode('-', $actionName)));
+
+ $filePath = __DIR__ . '/../actions/' . $actionName . '.php';
+ if (!file_exists($filePath)) {
+ throw new \Exception(sprintf('Invalid action: %s', $actionName));
+ }
+ $className = '\\' . $actionName;
+ $action = new $className();
+
+ $response = $action->execute($request);
+ if (is_string($response)) {
+ print $response;
+ } elseif ($response instanceof Response) {
+ $response->send();
+ }
}
}
diff --git a/lib/contents.php b/lib/contents.php
index 33f20cc2..c339d3ca 100644
--- a/lib/contents.php
+++ b/lib/contents.php
@@ -44,6 +44,38 @@ final class Response
'504' => 'Gateway Timeout',
'505' => 'HTTP Version Not Supported'
];
+ private string $body;
+ private int $code;
+ private array $headers;
+
+ public function __construct(
+ string $body = '',
+ int $code = 200,
+ array $headers = []
+ ) {
+ $this->body = $body;
+ $this->code = $code;
+ $this->headers = $headers;
+ }
+
+ public function getBody()
+ {
+ return $this->body;
+ }
+
+ public function getHeaders()
+ {
+ return $this->headers;
+ }
+
+ public function send(): void
+ {
+ http_response_code($this->code);
+ foreach ($this->headers as $name => $value) {
+ header(sprintf('%s: %s', $name, $value));
+ }
+ print $this->body;
+ }
}
/**
diff --git a/static/style.css b/static/style.css
index b5f3c00b..786e1526 100644
--- a/static/style.css
+++ b/static/style.css
@@ -78,12 +78,12 @@ header > div.logo {
margin: auto;
}
-header > section.warning {
+section.warning {
background-color: #ffc600;
color: #5f5f5f;
}
-header > section.critical-warning {
+section.critical-warning {
background-color: #cf3e3e;
font-weight: bold;
color: white;
diff --git a/templates/frontpage.html.php b/templates/frontpage.html.php
new file mode 100644
index 00000000..4560dc28
--- /dev/null
+++ b/templates/frontpage.html.php
@@ -0,0 +1,62 @@
+<script src="static/rss-bridge.js"></script>
+<script>
+ document.addEventListener('DOMContentLoaded', rssbridge_toggle_bridge);
+ document.addEventListener('DOMContentLoaded', rssbridge_list_search);
+</script>
+
+<?php if (Debug::isEnabled()): ?>
+ <?php if (!Debug::isSecure()): ?>
+ <section class="critical-warning">
+ Warning : Debug mode is active from any location,
+ make sure only you can access RSS-Bridge.
+ </section>
+ <?php else: ?>
+ <section class="warning">
+ Warning : Debug mode is active from your IP address,
+ your requests will bypass the cache.
+ </section>
+ <?php endif; ?>
+<?php endif; ?>
+
+<section class="searchbar">
+ <h3>Search</h3>
+ <input
+ type="text"
+ name="searchfield"
+ id="searchfield"
+ placeholder="Insert URL or bridge name"
+ onchange="rssbridge_list_search()"
+ onkeyup="rssbridge_list_search()"
+ value=""
+ >
+</section>
+
+<?= raw($bridges) ?>
+
+<section class="footer">
+ <a href="https://github.com/rss-bridge/rss-bridge">RSS-Bridge ~ Public Domain</a><br>
+ <p class="version"><?= e(Configuration::getVersion()) ?></p>
+
+ <?= $active_bridges ?>/<?= $total_bridges ?> active bridges.<br>
+
+ <?php if ($active_bridges !== $total_bridges): ?>
+ <?php if ($show_inactive): ?>
+ <a href="?show_inactive=0">
+ <button class="small">Hide inactive bridges</button>
+ </a>
+ <br>
+ <?php else: ?>
+ <a href="?show_inactive=1">
+ <button class="small">Show inactive bridges</button>
+ </a>
+ <br>
+ <?php endif; ?>
+ <?php endif; ?>
+
+ <?php if ($admin_email): ?>
+ <span>
+ You may email the administrator of this RSS-Bridge instance at
+ <a href="mailto:<?= e($admin_email) ?>"><?= e($admin_email) ?></a>
+ </span>
+ <?php endif; ?>
+</section>
diff --git a/tests/Actions/ListActionTest.php b/tests/Actions/ListActionTest.php
index f3d06db6..4373be76 100644
--- a/tests/Actions/ListActionTest.php
+++ b/tests/Actions/ListActionTest.php
@@ -2,36 +2,26 @@
namespace RssBridge\Tests\Actions;
-use ActionFactory;
use BridgeFactory;
use PHPUnit\Framework\TestCase;
class ListActionTest extends TestCase
{
- private $data;
-
- /**
- * @runInSeparateProcess
- * @requires function xdebug_get_headers
- */
public function testHeaders()
{
- $this->initAction();
-
- $this->assertContains(
- 'Content-Type: application/json',
- xdebug_get_headers()
- );
+ $action = new \ListAction();
+ $response = $action->execute([]);
+ $headers = $response->getHeaders();
+ $this->assertSame($headers['Content-Type'], 'application/json');
}
- /**
- * @runInSeparateProcess
- */
public function testOutput()
{
- $this->initAction();
+ $action = new \ListAction();
+ $response = $action->execute([]);
+ $data = $response->getBody();
- $items = json_decode($this->data, true);
+ $items = json_decode($data, true);
$this->assertNotNull($items, 'invalid JSON output: ' . json_last_error_msg());
@@ -77,17 +67,4 @@ class ListActionTest extends TestCase
$this->assertContains($bridge['status'], $allowedStatus, 'Invalid status value');
}
}
-
- private function initAction()
- {
- $actionFactory = new ActionFactory();
-
- $action = $actionFactory->create('list');
-
- ob_start();
- $action->execute([]);
- $this->data = ob_get_contents();
- ob_clean();
- ob_end_flush();
- }
}