mirror of
https://github.com/RSS-Bridge/rss-bridge.git
synced 2024-11-22 01:25:28 +03:00
refactor: extract frontpage to template (#3130)
Also introduce usage of Response object
This commit is contained in:
parent
fe59cbabc9
commit
2ef98b299f
12 changed files with 147 additions and 225 deletions
|
@ -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']);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 .= 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>';
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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']);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -22,7 +22,7 @@ interface ActionInterface
|
|||
*
|
||||
* Note: This function directly outputs data to the user.
|
||||
*
|
||||
* @return void
|
||||
* @return ?string
|
||||
*/
|
||||
public function execute(array $request);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
|
62
templates/frontpage.html.php
Normal file
62
templates/frontpage.html.php
Normal file
|
@ -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>
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue