rss-bridge/lib/BridgeCard.php

379 lines
13 KiB
PHP

<?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
*/
/**
* A generator class for a single bridge card on the home page of RSS-Bridge.
*
* This class generates the HTML content for a single bridge card for the home
* page of RSS-Bridge.
*
* @todo Return error if a caller creates an object of this class.
*/
final class BridgeCard
{
/**
* Gets a single bridge card
*
* @param class-string<BridgeInterface> $bridgeClassName The bridge name
* @param array $formats A list of formats
* @param bool $isActive Indicates if the bridge is active or not
* @return string The bridge card
*/
public static function displayBridgeCard($bridgeClassName, $formats, $isActive = true)
{
$bridgeFactory = new BridgeFactory();
$bridge = $bridgeFactory->create($bridgeClassName);
$isHttps = strpos($bridge->getURI(), 'https') === 0;
$uri = $bridge->getURI();
$name = $bridge->getName();
$icon = $bridge->getIcon();
$description = $bridge->getDescription();
$parameters = $bridge->getParameters();
if (Configuration::getConfig('proxy', 'url') && Configuration::getConfig('proxy', 'by_bridge')) {
$parameters['global']['_noproxy'] = [
'name' => 'Disable proxy (' . (Configuration::getConfig('proxy', 'name') ?: Configuration::getConfig('proxy', 'url')) . ')',
'type' => 'checkbox'
];
}
if (CUSTOM_CACHE_TIMEOUT) {
$parameters['global']['_cache_timeout'] = [
'name' => 'Cache timeout in seconds',
'type' => 'number',
'defaultValue' => $bridge->getCacheTimeout()
];
}
$card = <<<CARD
<section id="bridge-{$bridgeClassName}" data-ref="{$name}">
<h2><a href="{$uri}">{$name}</a></h2>
<p class="description">{$description}</p>
<input type="checkbox" class="showmore-box" id="showmore-{$bridgeClassName}" />
<label class="showmore" for="showmore-{$bridgeClassName}">Show more</label>
CARD;
// If we don't have any parameter for the bridge, we print a generic form to load it.
if (count($parameters) === 0) {
$card .= self::getForm($bridgeClassName, $formats, $isActive, $isHttps);
// Display form with cache timeout and/or noproxy options (if enabled) when bridge has no parameters
} elseif (count($parameters) === 1 && array_key_exists('global', $parameters)) {
$card .= self::getForm($bridgeClassName, $formats, $isActive, $isHttps, '', $parameters['global']);
} else {
foreach ($parameters as $parameterName => $parameter) {
if (!is_numeric($parameterName) && $parameterName === 'global') {
continue;
}
if (array_key_exists('global', $parameters)) {
$parameter = array_merge($parameter, $parameters['global']);
}
if (!is_numeric($parameterName)) {
$card .= '<h5>' . $parameterName . '</h5>' . PHP_EOL;
}
$card .= self::getForm($bridgeClassName, $formats, $isActive, $isHttps, $parameterName, $parameter);
}
}
$card .= sprintf('<label class="showless" for="showmore-%s">Show less</label>', $bridgeClassName);
if ($bridge->getDonationURI() !== '' && Configuration::getConfig('admin', 'donations')) {
$card .= sprintf(
'<p class="maintainer">%s ~ <a href="%s">Donate</a></p>',
$bridge->getMaintainer(),
$bridge->getDonationURI()
);
} else {
$card .= sprintf('<p class="maintainer">%s</p>', $bridge->getMaintainer());
}
$card .= '</section>';
return $card;
}
/**
* Get the form header for a bridge card
*
* @param class-string<BridgeInterface> $bridgeClassName The bridge name
* @param bool $isHttps If disabled, adds a warning to the form
* @return string The form header
*/
private static function getFormHeader($bridgeClassName, $isHttps = false, $parameterName = '')
{
$form = <<<EOD
<form method="GET" action="?">
<input type="hidden" name="action" value="display" />
<input type="hidden" name="bridge" value="{$bridgeClassName}" />
EOD;
if (!empty($parameterName)) {
$form .= sprintf('<input type="hidden" name="context" value="%s" />', $parameterName);
}
if (!$isHttps) {
$form .= '<div class="secure-warning">Warning :
This bridge is not fetching its content through a secure connection</div>';
}
return $form;
}
/**
* Get the form body for a bridge
*
* @param class-string<BridgeInterface> $bridgeClassName The bridge name
* @param array $formats A list of supported formats
* @param bool $isActive Indicates if a bridge is enabled or not
* @param bool $isHttps Indicates if a bridge uses HTTPS or not
* @param string $parameterName Sets the bridge context for the current form
* @param array $parameters The bridge parameters
* @return string The form body
*/
private static function getForm(
$bridgeClassName,
$formats,
$isActive = false,
$isHttps = false,
$parameterName = '',
$parameters = []
) {
$form = self::getFormHeader($bridgeClassName, $isHttps, $parameterName);
if (count($parameters) > 0) {
$form .= '<div class="parameters">';
foreach ($parameters as $id => $inputEntry) {
if (!isset($inputEntry['exampleValue'])) {
$inputEntry['exampleValue'] = '';
}
if (!isset($inputEntry['defaultValue'])) {
$inputEntry['defaultValue'] = '';
}
$idArg = 'arg-'
. urlencode($bridgeClassName)
. '-'
. urlencode($parameterName)
. '-'
. urlencode($id);
$form .= '<label for="'
. $idArg
. '">'
. filter_var($inputEntry['name'], FILTER_SANITIZE_FULL_SPECIAL_CHARS)
. '</label>'
. PHP_EOL;
if (!isset($inputEntry['type']) || $inputEntry['type'] === 'text') {
$form .= self::getTextInput($inputEntry, $idArg, $id);
} elseif ($inputEntry['type'] === 'number') {
$form .= self::getNumberInput($inputEntry, $idArg, $id);
} elseif ($inputEntry['type'] === 'list') {
$form .= self::getListInput($inputEntry, $idArg, $id);
} elseif ($inputEntry['type'] === 'checkbox') {
$form .= self::getCheckboxInput($inputEntry, $idArg, $id);
}
if (isset($inputEntry['title'])) {
$title_filtered = filter_var($inputEntry['title'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
$form .= '<i class="info" title="' . $title_filtered . '">i</i>';
} else {
$form .= '<i class="no-info"></i>';
}
}
$form .= '</div>';
}
if ($isActive) {
$form .= '<button type="submit" name="format" formtarget="_blank" value="Html">Generate feed</button>';
} else {
$form .= '<span style="font-weight: bold;">Inactive</span>';
}
return $form . '</form>' . PHP_EOL;
}
/**
* Get input field attributes
*
* @param array $entry The current entry
* @return string The input field attributes
*/
private static function getInputAttributes($entry)
{
$retVal = '';
if (isset($entry['required']) && $entry['required'] === true) {
$retVal .= ' required';
}
if (isset($entry['pattern'])) {
$retVal .= ' pattern="' . $entry['pattern'] . '"';
}
return $retVal;
}
/**
* Get text input
*
* @param array $entry The current entry
* @param string $id The field ID
* @param string $name The field name
* @return string The text input field
*/
private static function getTextInput($entry, $id, $name)
{
return '<input '
. self::getInputAttributes($entry)
. ' id="'
. $id
. '" type="text" value="'
. filter_var($entry['defaultValue'], FILTER_SANITIZE_FULL_SPECIAL_CHARS)
. '" placeholder="'
. filter_var($entry['exampleValue'], FILTER_SANITIZE_FULL_SPECIAL_CHARS)
. '" name="'
. $name
. '" />'
. PHP_EOL;
}
/**
* Get number input
*
* @param array $entry The current entry
* @param string $id The field ID
* @param string $name The field name
* @return string The number input field
*/
private static function getNumberInput($entry, $id, $name)
{
return '<input '
. self::getInputAttributes($entry)
. ' id="'
. $id
. '" type="number" value="'
. filter_var($entry['defaultValue'], FILTER_SANITIZE_NUMBER_INT)
. '" placeholder="'
. filter_var($entry['exampleValue'], FILTER_SANITIZE_NUMBER_INT)
. '" name="'
. $name
. '" />'
. PHP_EOL;
}
/**
* Get list input
*
* @param array $entry The current entry
* @param string $id The field ID
* @param string $name The field name
* @return string The list input field
*/
private static function getListInput($entry, $id, $name)
{
if (isset($entry['required']) && $entry['required'] === true) {
Debug::log('The "required" attribute is not supported for lists.');
unset($entry['required']);
}
$list = '<select '
. self::getInputAttributes($entry)
. ' id="'
. $id
. '" name="'
. $name
. '" >';
foreach ($entry['values'] as $name => $value) {
if (is_array($value)) {
$list .= '<optgroup label="' . htmlentities($name) . '">';
foreach ($value as $subname => $subvalue) {
if (
$entry['defaultValue'] === $subname
|| $entry['defaultValue'] === $subvalue
) {
$list .= '<option value="'
. $subvalue
. '" selected>'
. $subname
. '</option>';
} else {
$list .= '<option value="'
. $subvalue
. '">'
. $subname
. '</option>';
}
}
$list .= '</optgroup>';
} else {
if (
$entry['defaultValue'] === $name
|| $entry['defaultValue'] === $value
) {
$list .= '<option value="'
. $value
. '" selected>'
. $name
. '</option>';
} else {
$list .= '<option value="'
. $value
. '">'
. $name
. '</option>';
}
}
}
$list .= '</select>';
return $list;
}
/**
* Get checkbox input
*
* @param array $entry The current entry
* @param string $id The field ID
* @param string $name The field name
* @return string The checkbox input field
*/
private static function getCheckboxInput($entry, $id, $name)
{
if (isset($entry['required']) && $entry['required'] === true) {
Debug::log('The "required" attribute is not supported for checkboxes.');
unset($entry['required']);
}
return '<input '
. self::getInputAttributes($entry)
. ' id="'
. $id
. '" type="checkbox" name="'
. $name
. '" '
. ($entry['defaultValue'] === 'checked' ? 'checked' : '')
. ' />'
. PHP_EOL;
}
}