mirror of
https://github.com/RSS-Bridge/rss-bridge.git
synced 2024-11-25 10:56:18 +03:00
refactor/fix (#3924)
This commit is contained in:
parent
06b299e627
commit
9574c17ddc
9 changed files with 153 additions and 178 deletions
|
@ -98,7 +98,16 @@ class DisplayAction implements ActionInterface
|
||||||
try {
|
try {
|
||||||
$bridge->loadConfiguration();
|
$bridge->loadConfiguration();
|
||||||
// Remove parameters that don't concern bridges
|
// Remove parameters that don't concern bridges
|
||||||
$input = array_diff_key($request, array_fill_keys(['action', 'bridge', 'format', '_noproxy', '_cache_timeout', '_error_time'], ''));
|
$remove = [
|
||||||
|
'action',
|
||||||
|
'bridge',
|
||||||
|
'format',
|
||||||
|
'_noproxy',
|
||||||
|
'_cache_timeout',
|
||||||
|
'_error_time',
|
||||||
|
'_', // Some RSS readers add a cache-busting parameter (_=<timestamp>) to feed URLs, detect and ignore them.
|
||||||
|
];
|
||||||
|
$input = array_diff_key($request, array_fill_keys($remove, ''));
|
||||||
$bridge->setInput($input);
|
$bridge->setInput($input);
|
||||||
$bridge->collectData();
|
$bridge->collectData();
|
||||||
$items = $bridge->getItems();
|
$items = $bridge->getItems();
|
||||||
|
|
|
@ -24,14 +24,14 @@ final class FrontpageAction implements ActionInterface
|
||||||
$body = '';
|
$body = '';
|
||||||
foreach ($bridgeClassNames as $bridgeClassName) {
|
foreach ($bridgeClassNames as $bridgeClassName) {
|
||||||
if ($bridgeFactory->isEnabled($bridgeClassName)) {
|
if ($bridgeFactory->isEnabled($bridgeClassName)) {
|
||||||
$body .= BridgeCard::displayBridgeCard($bridgeClassName, $formats);
|
$body .= BridgeCard::displayBridgeCard($bridgeClassName);
|
||||||
$activeBridges++;
|
$activeBridges++;
|
||||||
} elseif ($showInactive) {
|
} elseif ($showInactive) {
|
||||||
$body .= BridgeCard::displayBridgeCard($bridgeClassName, $formats, false) . PHP_EOL;
|
$body .= BridgeCard::displayBridgeCard($bridgeClassName, false) . "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: cache this renderered template
|
// todo: cache this renderered template?
|
||||||
return render(__DIR__ . '/../templates/frontpage.html.php', [
|
return render(__DIR__ . '/../templates/frontpage.html.php', [
|
||||||
'messages' => $messages,
|
'messages' => $messages,
|
||||||
'admin_email' => Configuration::getConfig('admin', 'email'),
|
'admin_email' => Configuration::getConfig('admin', 'email'),
|
||||||
|
|
|
@ -52,7 +52,9 @@ class PresidenciaPTBridge extends BridgeAbstract
|
||||||
|
|
||||||
public function collectData()
|
public function collectData()
|
||||||
{
|
{
|
||||||
foreach (array_keys($this->getParameters()['Section']) as $k) {
|
$contexts = $this->getParameters();
|
||||||
|
|
||||||
|
foreach (array_keys($contexts['Section']) as $k) {
|
||||||
Debug::log('Key: ' . var_export($k, true));
|
Debug::log('Key: ' . var_export($k, true));
|
||||||
if ($this->getInput($k)) {
|
if ($this->getInput($k)) {
|
||||||
$html = getSimpleHTMLDOMCached($this->getURI() . $k);
|
$html = getSimpleHTMLDOMCached($this->getURI() . $k);
|
||||||
|
|
|
@ -92,6 +92,9 @@ abstract class BridgeAbstract
|
||||||
return static::MAINTAINER;
|
return static::MAINTAINER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A more correct method name would have been "getContexts"
|
||||||
|
*/
|
||||||
public function getParameters(): array
|
public function getParameters(): array
|
||||||
{
|
{
|
||||||
return static::PARAMETERS;
|
return static::PARAMETERS;
|
||||||
|
@ -128,16 +131,17 @@ abstract class BridgeAbstract
|
||||||
|
|
||||||
public function setInput(array $input)
|
public function setInput(array $input)
|
||||||
{
|
{
|
||||||
$context = $input['context'] ?? null;
|
// This is the submitted context
|
||||||
if ($context) {
|
$contextName = $input['context'] ?? null;
|
||||||
|
if ($contextName) {
|
||||||
// Context hinting (optional)
|
// Context hinting (optional)
|
||||||
$this->queriedContext = $context;
|
$this->queriedContext = $contextName;
|
||||||
unset($input['context']);
|
unset($input['context']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$parameters = $this->getParameters();
|
$contexts = $this->getParameters();
|
||||||
|
|
||||||
if (!$parameters) {
|
if (!$contexts) {
|
||||||
if ($input) {
|
if ($input) {
|
||||||
throw new \Exception('Invalid parameters value(s)');
|
throw new \Exception('Invalid parameters value(s)');
|
||||||
}
|
}
|
||||||
|
@ -146,15 +150,16 @@ abstract class BridgeAbstract
|
||||||
|
|
||||||
$validator = new ParameterValidator();
|
$validator = new ParameterValidator();
|
||||||
|
|
||||||
// $input is passed by reference!
|
// $input IS PASSED BY REFERENCE!
|
||||||
if (!$validator->validateInput($input, $parameters)) {
|
$errors = $validator->validateInput($input, $contexts);
|
||||||
$invalidParameterKeys = array_column($validator->getInvalidParameters(), 'name');
|
if ($errors !== []) {
|
||||||
|
$invalidParameterKeys = array_column($errors, 'name');
|
||||||
throw new \Exception(sprintf('Invalid parameters value(s): %s', implode(', ', $invalidParameterKeys)));
|
throw new \Exception(sprintf('Invalid parameters value(s): %s', implode(', ', $invalidParameterKeys)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Guess the context from input data
|
// Guess the context from input data
|
||||||
if (empty($this->queriedContext)) {
|
if (empty($this->queriedContext)) {
|
||||||
$queriedContext = $validator->getQueriedContext($input, $parameters);
|
$queriedContext = $validator->getQueriedContext($input, $contexts);
|
||||||
$this->queriedContext = $queriedContext;
|
$this->queriedContext = $queriedContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,12 +184,12 @@ abstract class BridgeAbstract
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply default values to missing data
|
// Apply default values to missing data
|
||||||
$contexts = [$queriedContext];
|
$contextNames = [$queriedContext];
|
||||||
if (array_key_exists('global', $this->getParameters())) {
|
if (array_key_exists('global', $this->getParameters())) {
|
||||||
$contexts[] = 'global';
|
$contextNames[] = 'global';
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($contexts as $context) {
|
foreach ($contextNames as $context) {
|
||||||
if (!isset($this->getParameters()[$context])) {
|
if (!isset($this->getParameters()[$context])) {
|
||||||
// unknown context provided by client, throw exception here? or continue?
|
// unknown context provided by client, throw exception here? or continue?
|
||||||
}
|
}
|
||||||
|
@ -240,7 +245,9 @@ abstract class BridgeAbstract
|
||||||
|
|
||||||
// Only keep guessed context parameters values
|
// Only keep guessed context parameters values
|
||||||
if (isset($this->inputs[$queriedContext])) {
|
if (isset($this->inputs[$queriedContext])) {
|
||||||
$this->inputs = [$queriedContext => $this->inputs[$queriedContext]];
|
$this->inputs = [
|
||||||
|
$queriedContext => $this->inputs[$queriedContext],
|
||||||
|
];
|
||||||
} else {
|
} else {
|
||||||
$this->inputs = [];
|
$this->inputs = [];
|
||||||
}
|
}
|
||||||
|
@ -263,17 +270,20 @@ abstract class BridgeAbstract
|
||||||
if (!isset($this->inputs[$this->queriedContext][$input]['value'])) {
|
if (!isset($this->inputs[$this->queriedContext][$input]['value'])) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (array_key_exists('global', $this->getParameters())) {
|
|
||||||
if (array_key_exists($input, $this->getParameters()['global'])) {
|
$contexts = $this->getParameters();
|
||||||
$context = 'global';
|
|
||||||
|
if (array_key_exists('global', $contexts)) {
|
||||||
|
if (array_key_exists($input, $contexts['global'])) {
|
||||||
|
$contextName = 'global';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!isset($context)) {
|
if (!isset($contextName)) {
|
||||||
$context = $this->queriedContext;
|
$contextName = $this->queriedContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
$needle = $this->inputs[$this->queriedContext][$input]['value'];
|
$needle = $this->inputs[$this->queriedContext][$input]['value'];
|
||||||
foreach ($this->getParameters()[$context][$input]['values'] as $first_level_key => $first_level_value) {
|
foreach ($contexts[$contextName][$input]['values'] as $first_level_key => $first_level_value) {
|
||||||
if (!is_array($first_level_value) && $needle === (string)$first_level_value) {
|
if (!is_array($first_level_value) && $needle === (string)$first_level_value) {
|
||||||
return $first_level_key;
|
return $first_level_key;
|
||||||
} elseif (is_array($first_level_value)) {
|
} elseif (is_array($first_level_value)) {
|
||||||
|
@ -289,8 +299,11 @@ abstract class BridgeAbstract
|
||||||
public function detectParameters($url)
|
public function detectParameters($url)
|
||||||
{
|
{
|
||||||
$regex = '/^(https?:\/\/)?(www\.)?(.+?)(\/)?$/';
|
$regex = '/^(https?:\/\/)?(www\.)?(.+?)(\/)?$/';
|
||||||
|
|
||||||
|
$contexts = $this->getParameters();
|
||||||
|
|
||||||
if (
|
if (
|
||||||
empty($this->getParameters())
|
empty($contexts)
|
||||||
&& preg_match($regex, $url, $urlMatches) > 0
|
&& preg_match($regex, $url, $urlMatches) > 0
|
||||||
&& preg_match($regex, static::URI, $bridgeUriMatches) > 0
|
&& preg_match($regex, static::URI, $bridgeUriMatches) > 0
|
||||||
&& $urlMatches[3] === $bridgeUriMatches[3]
|
&& $urlMatches[3] === $bridgeUriMatches[3]
|
||||||
|
|
|
@ -6,33 +6,30 @@ final class BridgeCard
|
||||||
* Gets a single bridge card
|
* Gets a single bridge card
|
||||||
*
|
*
|
||||||
* @param class-string<BridgeAbstract> $bridgeClassName The bridge name
|
* @param class-string<BridgeAbstract> $bridgeClassName The bridge name
|
||||||
* @param array $formats A list of formats
|
|
||||||
* @param bool $isActive Indicates if the bridge is active or not
|
* @param bool $isActive Indicates if the bridge is active or not
|
||||||
* @return string The bridge card
|
* @return string The bridge card
|
||||||
*/
|
*/
|
||||||
public static function displayBridgeCard($bridgeClassName, $formats, $isActive = true)
|
public static function displayBridgeCard($bridgeClassName, $isActive = true)
|
||||||
{
|
{
|
||||||
$bridgeFactory = new BridgeFactory();
|
$bridgeFactory = new BridgeFactory();
|
||||||
|
|
||||||
$bridge = $bridgeFactory->create($bridgeClassName);
|
$bridge = $bridgeFactory->create($bridgeClassName);
|
||||||
|
|
||||||
$isHttps = str_starts_with($bridge->getURI(), 'https');
|
|
||||||
|
|
||||||
$uri = $bridge->getURI();
|
$uri = $bridge->getURI();
|
||||||
$name = $bridge->getName();
|
$name = $bridge->getName();
|
||||||
$icon = $bridge->getIcon();
|
$icon = $bridge->getIcon();
|
||||||
$description = $bridge->getDescription();
|
$description = $bridge->getDescription();
|
||||||
$parameters = $bridge->getParameters();
|
$contexts = $bridge->getParameters();
|
||||||
|
|
||||||
if (Configuration::getConfig('proxy', 'url') && Configuration::getConfig('proxy', 'by_bridge')) {
|
if (Configuration::getConfig('proxy', 'url') && Configuration::getConfig('proxy', 'by_bridge')) {
|
||||||
$parameters['global']['_noproxy'] = [
|
$contexts['global']['_noproxy'] = [
|
||||||
'name' => 'Disable proxy (' . (Configuration::getConfig('proxy', 'name') ?: Configuration::getConfig('proxy', 'url')) . ')',
|
'name' => 'Disable proxy (' . (Configuration::getConfig('proxy', 'name') ?: Configuration::getConfig('proxy', 'url')) . ')',
|
||||||
'type' => 'checkbox'
|
'type' => 'checkbox'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Configuration::getConfig('cache', 'custom_timeout')) {
|
if (Configuration::getConfig('cache', 'custom_timeout')) {
|
||||||
$parameters['global']['_cache_timeout'] = [
|
$contexts['global']['_cache_timeout'] = [
|
||||||
'name' => 'Cache timeout in seconds',
|
'name' => 'Cache timeout in seconds',
|
||||||
'type' => 'number',
|
'type' => 'number',
|
||||||
'defaultValue' => $bridge->getCacheTimeout()
|
'defaultValue' => $bridge->getCacheTimeout()
|
||||||
|
@ -41,45 +38,51 @@ final class BridgeCard
|
||||||
|
|
||||||
$shortName = $bridge->getShortName();
|
$shortName = $bridge->getShortName();
|
||||||
$card = <<<CARD
|
$card = <<<CARD
|
||||||
<section
|
<section
|
||||||
class="bridge-card"
|
class="bridge-card"
|
||||||
id="bridge-{$bridgeClassName}"
|
id="bridge-{$bridgeClassName}"
|
||||||
data-ref="{$name}"
|
data-ref="{$name}"
|
||||||
data-short-name="$shortName"
|
data-short-name="$shortName"
|
||||||
>
|
>
|
||||||
|
|
||||||
<h2><a href="{$uri}">{$name}</a></h2>
|
<h2><a href="{$uri}">{$name}</a></h2>
|
||||||
<p class="description">{$description}</p>
|
<p class="description">{$description}</p>
|
||||||
<input type="checkbox" class="showmore-box" id="showmore-{$bridgeClassName}" />
|
<input type="checkbox" class="showmore-box" id="showmore-{$bridgeClassName}" />
|
||||||
<label class="showmore" for="showmore-{$bridgeClassName}">Show more</label>
|
<label class="showmore" for="showmore-{$bridgeClassName}">Show more</label>
|
||||||
CARD;
|
|
||||||
|
|
||||||
|
CARD;
|
||||||
|
|
||||||
// If we don't have any parameter for the bridge, we print a generic form to load it.
|
// If we don't have any parameter for the bridge, we print a generic form to load it.
|
||||||
if (count($parameters) === 0) {
|
if (count($contexts) === 0) {
|
||||||
$card .= self::getForm($bridgeClassName, $formats, $isActive, $isHttps);
|
// The bridge has zero parameters
|
||||||
|
$card .= self::getForm($bridgeClassName, $isActive);
|
||||||
// Display form with cache timeout and/or noproxy options (if enabled) when bridge has no parameters
|
} elseif (count($contexts) === 1 && array_key_exists('global', $contexts)) {
|
||||||
} elseif (count($parameters) === 1 && array_key_exists('global', $parameters)) {
|
// The bridge has a single context with key 'global'
|
||||||
$card .= self::getForm($bridgeClassName, $formats, $isActive, $isHttps, '', $parameters['global']);
|
$card .= self::getForm($bridgeClassName, $isActive, '', $contexts['global']);
|
||||||
} else {
|
} else {
|
||||||
foreach ($parameters as $parameterName => $parameter) {
|
// The bridge has one or more contexts (named or unnamed)
|
||||||
if (!is_numeric($parameterName) && $parameterName === 'global') {
|
foreach ($contexts as $contextName => $contextParameters) {
|
||||||
|
if ($contextName === 'global') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (array_key_exists('global', $parameters)) {
|
if (array_key_exists('global', $contexts)) {
|
||||||
$parameter = array_merge($parameter, $parameters['global']);
|
// Merge the global parameters into current context
|
||||||
|
$contextParameters = array_merge($contextParameters, $contexts['global']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_numeric($parameterName)) {
|
if (!is_numeric($contextName)) {
|
||||||
$card .= '<h5>' . $parameterName . '</h5>' . PHP_EOL;
|
// This is a named context
|
||||||
|
$card .= '<h5>' . $contextName . '</h5>' . PHP_EOL;
|
||||||
}
|
}
|
||||||
|
|
||||||
$card .= self::getForm($bridgeClassName, $formats, $isActive, $isHttps, $parameterName, $parameter);
|
$card .= self::getForm($bridgeClassName, $isActive, $contextName, $contextParameters);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$card .= sprintf('<label class="showless" for="showmore-%s">Show less</label>', $bridgeClassName);
|
$card .= sprintf('<label class="showless" for="showmore-%s">Show less</label>', $bridgeClassName);
|
||||||
|
|
||||||
if ($bridge->getDonationURI() !== '' && Configuration::getConfig('admin', 'donations')) {
|
if ($bridge->getDonationURI() !== '' && Configuration::getConfig('admin', 'donations')) {
|
||||||
$card .= sprintf(
|
$card .= sprintf(
|
||||||
'<p class="maintainer">%s ~ <a href="%s">Donate</a></p>',
|
'<p class="maintainer">%s ~ <a href="%s">Donate</a></p>',
|
||||||
|
@ -94,31 +97,27 @@ CARD;
|
||||||
return $card;
|
return $card;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the form body for a bridge
|
|
||||||
*
|
|
||||||
* @param class-string<BridgeAbstract> $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(
|
private static function getForm(
|
||||||
$bridgeClassName,
|
string $bridgeClassName,
|
||||||
$formats,
|
bool $isActive = false,
|
||||||
$isActive = false,
|
string $contextName = '',
|
||||||
$isHttps = false,
|
array $contextParameters = []
|
||||||
$parameterName = '',
|
|
||||||
$parameters = []
|
|
||||||
) {
|
) {
|
||||||
$form = self::getFormHeader($bridgeClassName, $isHttps, $parameterName);
|
$form = <<<EOD
|
||||||
|
<form method="GET" action="?">
|
||||||
|
<input type="hidden" name="action" value="display" />
|
||||||
|
<input type="hidden" name="bridge" value="{$bridgeClassName}" />
|
||||||
|
|
||||||
if (count($parameters) > 0) {
|
EOD;
|
||||||
|
|
||||||
|
if (!empty($contextName)) {
|
||||||
|
$form .= sprintf('<input type="hidden" name="context" value="%s" />', $contextName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($contextParameters) > 0) {
|
||||||
$form .= '<div class="parameters">';
|
$form .= '<div class="parameters">';
|
||||||
|
|
||||||
foreach ($parameters as $id => $inputEntry) {
|
foreach ($contextParameters as $id => $inputEntry) {
|
||||||
if (!isset($inputEntry['exampleValue'])) {
|
if (!isset($inputEntry['exampleValue'])) {
|
||||||
$inputEntry['exampleValue'] = '';
|
$inputEntry['exampleValue'] = '';
|
||||||
}
|
}
|
||||||
|
@ -127,19 +126,25 @@ CARD;
|
||||||
$inputEntry['defaultValue'] = '';
|
$inputEntry['defaultValue'] = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
$idArg = 'arg-' . urlencode($bridgeClassName) . '-' . urlencode($parameterName) . '-' . urlencode($id);
|
$idArg = 'arg-' . urlencode($bridgeClassName) . '-' . urlencode($contextName) . '-' . urlencode($id);
|
||||||
|
|
||||||
$inputName = filter_var($inputEntry['name'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
|
$inputName = filter_var($inputEntry['name'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
|
||||||
$form .= '<label for="' . $idArg . '">' . $inputName . '</label>' . PHP_EOL;
|
$form .= '<label for="' . $idArg . '">' . $inputName . '</label>' . PHP_EOL;
|
||||||
|
|
||||||
if (!isset($inputEntry['type']) || $inputEntry['type'] === 'text') {
|
if (
|
||||||
$form .= self::getTextInput($inputEntry, $idArg, $id);
|
!isset($inputEntry['type'])
|
||||||
|
|| $inputEntry['type'] === 'text'
|
||||||
|
) {
|
||||||
|
$form .= self::getTextInput($inputEntry, $idArg, $id) . "\n";
|
||||||
} elseif ($inputEntry['type'] === 'number') {
|
} elseif ($inputEntry['type'] === 'number') {
|
||||||
$form .= self::getNumberInput($inputEntry, $idArg, $id);
|
$form .= self::getNumberInput($inputEntry, $idArg, $id);
|
||||||
} elseif ($inputEntry['type'] === 'list') {
|
} elseif ($inputEntry['type'] === 'list') {
|
||||||
$form .= self::getListInput($inputEntry, $idArg, $id);
|
$form .= self::getListInput($inputEntry, $idArg, $id) . "\n";
|
||||||
} elseif ($inputEntry['type'] === 'checkbox') {
|
} elseif ($inputEntry['type'] === 'checkbox') {
|
||||||
$form .= self::getCheckboxInput($inputEntry, $idArg, $id);
|
$form .= self::getCheckboxInput($inputEntry, $idArg, $id);
|
||||||
|
} else {
|
||||||
|
$foo = 2;
|
||||||
|
// oops?
|
||||||
}
|
}
|
||||||
|
|
||||||
$infoText = [];
|
$infoText = [];
|
||||||
|
@ -171,39 +176,13 @@ CARD;
|
||||||
return $form . '</form>' . PHP_EOL;
|
return $form . '</form>' . PHP_EOL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the form header for a bridge card
|
|
||||||
*
|
|
||||||
* @param class-string<BridgeAbstract> $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;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getTextInput(array $entry, string $id, string $name): string
|
public static function getTextInput(array $entry, string $id, string $name): string
|
||||||
{
|
{
|
||||||
$defaultValue = filter_var($entry['defaultValue'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
|
$defaultValue = filter_var($entry['defaultValue'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
|
||||||
$exampleValue = filter_var($entry['exampleValue'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
|
$exampleValue = filter_var($entry['exampleValue'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
|
||||||
$attributes = self::getInputAttributes($entry);
|
$attributes = self::getInputAttributes($entry);
|
||||||
|
|
||||||
return sprintf('<input %s id="%s" type="text" value="%s" placeholder="%s" name="%s" />' . "\n", $attributes, $id, $defaultValue, $exampleValue, $name);
|
return sprintf('<input %s id="%s" type="text" value="%s" placeholder="%s" name="%s" />', $attributes, $id, $defaultValue, $exampleValue, $name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getNumberInput(array $entry, string $id, string $name): string
|
public static function getNumberInput(array $entry, string $id, string $name): string
|
||||||
|
@ -224,7 +203,7 @@ EOD;
|
||||||
}
|
}
|
||||||
|
|
||||||
$attributes = self::getInputAttributes($entry);
|
$attributes = self::getInputAttributes($entry);
|
||||||
$list = sprintf('<select %s id="%s" name="%s" >', $attributes, $id, $name);
|
$list = sprintf('<select %s id="%s" name="%s" >' . "\n", $attributes, $id, $name);
|
||||||
|
|
||||||
foreach ($entry['values'] as $name => $value) {
|
foreach ($entry['values'] as $name => $value) {
|
||||||
if (is_array($value)) {
|
if (is_array($value)) {
|
||||||
|
@ -245,9 +224,9 @@ EOD;
|
||||||
$entry['defaultValue'] === $name
|
$entry['defaultValue'] === $name
|
||||||
|| $entry['defaultValue'] === $value
|
|| $entry['defaultValue'] === $value
|
||||||
) {
|
) {
|
||||||
$list .= '<option value="' . $value . '" selected>' . $name . '</option>';
|
$list .= '<option value="' . $value . '" selected>' . $name . '</option>' . "\n";
|
||||||
} else {
|
} else {
|
||||||
$list .= '<option value="' . $value . '">' . $name . '</option>';
|
$list .= '<option value="' . $value . '">' . $name . '</option>' . "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,37 +2,26 @@
|
||||||
|
|
||||||
class ParameterValidator
|
class ParameterValidator
|
||||||
{
|
{
|
||||||
private array $invalid = [];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check that inputs are actually present in the bridge parameters.
|
* Validate and sanitize user inputs against configured bridge parameters (contexts)
|
||||||
*
|
|
||||||
* Also check whether input values are allowed.
|
|
||||||
*/
|
*/
|
||||||
public function validateInput(&$input, $parameters): bool
|
public function validateInput(array &$input, $contexts): array
|
||||||
{
|
{
|
||||||
if (!is_array($input)) {
|
$errors = [];
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($input as $name => $value) {
|
foreach ($input as $name => $value) {
|
||||||
// Some RSS readers add a cache-busting parameter (_=<timestamp>) to feed URLs, detect and ignore them.
|
|
||||||
if ($name === '_') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$registered = false;
|
$registered = false;
|
||||||
foreach ($parameters as $context => $set) {
|
foreach ($contexts as $contextName => $contextParameters) {
|
||||||
if (!array_key_exists($name, $set)) {
|
if (!array_key_exists($name, $contextParameters)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$registered = true;
|
$registered = true;
|
||||||
if (!isset($set[$name]['type'])) {
|
if (!isset($contextParameters[$name]['type'])) {
|
||||||
// Default type is text
|
// Default type is text
|
||||||
$set[$name]['type'] = 'text';
|
$contextParameters[$name]['type'] = 'text';
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ($set[$name]['type']) {
|
switch ($contextParameters[$name]['type']) {
|
||||||
case 'number':
|
case 'number':
|
||||||
$input[$name] = $this->validateNumberValue($value);
|
$input[$name] = $this->validateNumberValue($value);
|
||||||
break;
|
break;
|
||||||
|
@ -40,12 +29,12 @@ class ParameterValidator
|
||||||
$input[$name] = $this->validateCheckboxValue($value);
|
$input[$name] = $this->validateCheckboxValue($value);
|
||||||
break;
|
break;
|
||||||
case 'list':
|
case 'list':
|
||||||
$input[$name] = $this->validateListValue($value, $set[$name]['values']);
|
$input[$name] = $this->validateListValue($value, $contextParameters[$name]['values']);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
case 'text':
|
case 'text':
|
||||||
if (isset($set[$name]['pattern'])) {
|
if (isset($contextParameters[$name]['pattern'])) {
|
||||||
$input[$name] = $this->validateTextValue($value, $set[$name]['pattern']);
|
$input[$name] = $this->validateTextValue($value, $contextParameters[$name]['pattern']);
|
||||||
} else {
|
} else {
|
||||||
$input[$name] = $this->validateTextValue($value);
|
$input[$name] = $this->validateTextValue($value);
|
||||||
}
|
}
|
||||||
|
@ -54,56 +43,56 @@ class ParameterValidator
|
||||||
|
|
||||||
if (
|
if (
|
||||||
is_null($input[$name])
|
is_null($input[$name])
|
||||||
&& isset($set[$name]['required'])
|
&& isset($contextParameters[$name]['required'])
|
||||||
&& $set[$name]['required']
|
&& $contextParameters[$name]['required']
|
||||||
) {
|
) {
|
||||||
$this->invalid[] = ['name' => $name, 'reason' => 'Parameter is invalid!'];
|
$errors[] = ['name' => $name, 'reason' => 'Parameter is invalid!'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$registered) {
|
if (!$registered) {
|
||||||
$this->invalid[] = ['name' => $name, 'reason' => 'Parameter is not registered!'];
|
$errors[] = ['name' => $name, 'reason' => 'Parameter is not registered!'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->invalid === [];
|
return $errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the name of the context matching the provided inputs
|
* Get the name of the context matching the provided inputs
|
||||||
*
|
*
|
||||||
* @param array $input Associative array of user data
|
* @param array $input Associative array of user data
|
||||||
* @param array $parameters Array of bridge parameters
|
* @param array $contexts Array of bridge parameters
|
||||||
* @return string|null Returns the context name or null if no match was found
|
* @return string|null Returns the context name or null if no match was found
|
||||||
*/
|
*/
|
||||||
public function getQueriedContext($input, $parameters)
|
public function getQueriedContext(array $input, array $contexts)
|
||||||
{
|
{
|
||||||
$queriedContexts = [];
|
$queriedContexts = [];
|
||||||
|
|
||||||
// Detect matching context
|
// Detect matching context
|
||||||
foreach ($parameters as $context => $set) {
|
foreach ($contexts as $contextName => $contextParameters) {
|
||||||
$queriedContexts[$context] = null;
|
$queriedContexts[$contextName] = null;
|
||||||
|
|
||||||
// Ensure all user data exist in the current context
|
// Ensure all user data exist in the current context
|
||||||
$notInContext = array_diff_key($input, $set);
|
$notInContext = array_diff_key($input, $contextParameters);
|
||||||
if (array_key_exists('global', $parameters)) {
|
if (array_key_exists('global', $contexts)) {
|
||||||
$notInContext = array_diff_key($notInContext, $parameters['global']);
|
$notInContext = array_diff_key($notInContext, $contexts['global']);
|
||||||
}
|
}
|
||||||
if (count($notInContext) > 0) {
|
if (count($notInContext) > 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if all parameters of the context are satisfied
|
// Check if all parameters of the context are satisfied
|
||||||
foreach ($set as $id => $properties) {
|
foreach ($contextParameters as $id => $properties) {
|
||||||
if (isset($input[$id]) && !empty($input[$id])) {
|
if (!empty($input[$id])) {
|
||||||
$queriedContexts[$context] = true;
|
$queriedContexts[$contextName] = true;
|
||||||
} elseif (
|
} elseif (
|
||||||
isset($properties['type'])
|
isset($properties['type'])
|
||||||
&& ($properties['type'] === 'checkbox' || $properties['type'] === 'list')
|
&& ($properties['type'] === 'checkbox' || $properties['type'] === 'list')
|
||||||
) {
|
) {
|
||||||
continue;
|
continue;
|
||||||
} elseif (isset($properties['required']) && $properties['required'] === true) {
|
} elseif (isset($properties['required']) && $properties['required'] === true) {
|
||||||
$queriedContexts[$context] = false;
|
$queriedContexts[$contextName] = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -111,7 +100,7 @@ class ParameterValidator
|
||||||
|
|
||||||
// Abort if one of the globally required parameters is not satisfied
|
// Abort if one of the globally required parameters is not satisfied
|
||||||
if (
|
if (
|
||||||
array_key_exists('global', $parameters)
|
array_key_exists('global', $contexts)
|
||||||
&& $queriedContexts['global'] === false
|
&& $queriedContexts['global'] === false
|
||||||
) {
|
) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -124,9 +113,9 @@ class ParameterValidator
|
||||||
if (isset($input['context'])) {
|
if (isset($input['context'])) {
|
||||||
return $input['context'];
|
return $input['context'];
|
||||||
}
|
}
|
||||||
foreach ($queriedContexts as $context => $queried) {
|
foreach ($queriedContexts as $context2 => $queried) {
|
||||||
if (is_null($queried)) {
|
if (is_null($queried)) {
|
||||||
return $context;
|
return $context2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -138,11 +127,6 @@ class ParameterValidator
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getInvalidParameters(): array
|
|
||||||
{
|
|
||||||
return $this->invalid;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function validateTextValue($value, $pattern = null)
|
private function validateTextValue($value, $pattern = null)
|
||||||
{
|
{
|
||||||
if (is_null($pattern)) {
|
if (is_null($pattern)) {
|
||||||
|
|
|
@ -281,16 +281,6 @@ p.maintainer {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.secure-warning {
|
|
||||||
background-color: #ffc600;
|
|
||||||
color: #5f5f5f;
|
|
||||||
box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.3);
|
|
||||||
border-radius: 2px;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
width: 80%;
|
|
||||||
margin: auto auto 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error strong {
|
.error strong {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 100px;
|
width: 100px;
|
||||||
|
@ -395,11 +385,6 @@ button {
|
||||||
}
|
}
|
||||||
|
|
||||||
} /* @supports (display: grid) */
|
} /* @supports (display: grid) */
|
||||||
|
|
||||||
.secure-warning {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dark theme */
|
/* Dark theme */
|
||||||
|
|
|
@ -30,12 +30,12 @@ class BridgeCardTest extends TestCase
|
||||||
'defaultValue' => 'yo1',
|
'defaultValue' => 'yo1',
|
||||||
'exampleValue' => 'yo2',
|
'exampleValue' => 'yo2',
|
||||||
];
|
];
|
||||||
$this->assertSame('<input id="id" type="text" value="yo1" placeholder="yo2" name="name" />' . "\n", BridgeCard::getTextInput($entry, 'id', 'name'));
|
$this->assertSame('<input id="id" type="text" value="yo1" placeholder="yo2" name="name" />', BridgeCard::getTextInput($entry, 'id', 'name'));
|
||||||
|
|
||||||
$entry = [
|
$entry = [
|
||||||
'values' => [],
|
'values' => [],
|
||||||
];
|
];
|
||||||
$this->assertSame('<select id="id" name="name" ></select>', BridgeCard::getListInput($entry, 'id', 'name'));
|
$this->assertSame('<select id="id" name="name" >' . "\n" . '</select>', BridgeCard::getListInput($entry, 'id', 'name'));
|
||||||
|
|
||||||
$entry = [
|
$entry = [
|
||||||
'defaultValue' => 2,
|
'defaultValue' => 2,
|
||||||
|
@ -43,7 +43,7 @@ class BridgeCardTest extends TestCase
|
||||||
'foo' => 'bar',
|
'foo' => 'bar',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
$this->assertSame('<select id="id" name="name" ><option value="bar">foo</option></select>', BridgeCard::getListInput($entry, 'id', 'name'));
|
$this->assertSame('<select id="id" name="name" >' . "\n" . '<option value="bar">foo</option>' . "\n" . '</select>', BridgeCard::getListInput($entry, 'id', 'name'));
|
||||||
|
|
||||||
// optgroup
|
// optgroup
|
||||||
$entry = [
|
$entry = [
|
||||||
|
@ -52,6 +52,9 @@ class BridgeCardTest extends TestCase
|
||||||
'f' => 'b',
|
'f' => 'b',
|
||||||
]],
|
]],
|
||||||
];
|
];
|
||||||
$this->assertSame('<select id="id" name="name" ><optgroup label="kek"><option value="b">f</option></optgroup></select>', BridgeCard::getListInput($entry, 'id', 'name'));
|
$this->assertSame(
|
||||||
|
'<select id="id" name="name" >' . "\n" . '<optgroup label="kek"><option value="b">f</option></optgroup></select>',
|
||||||
|
BridgeCard::getListInput($entry, 'id', 'name')
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -20,7 +20,7 @@ class ParameterValidatorTest extends TestCase
|
||||||
],
|
],
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
$this->assertTrue($sut->validateInput($input, $parameters));
|
$this->assertSame([], $sut->validateInput($input, $parameters));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test2()
|
public function test2()
|
||||||
|
@ -35,6 +35,6 @@ class ParameterValidatorTest extends TestCase
|
||||||
],
|
],
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
$this->assertFalse($sut->validateInput($input, $parameters));
|
$this->assertNotEmpty($sut->validateInput($input, $parameters));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue