mirror of
https://github.com/RSS-Bridge/rss-bridge.git
synced 2024-11-22 09:35:28 +03:00
[Core] New feature : User Interface to "Detect" Feed from an URL (#3436)
* [Core] New feature : User Interface to "Detect" Feed from an URL Detect Action has been expanded to support returning a Feed in a JSON format instead of a Redirect. Existing usage of the Detect action will keep working as usual. Frontpage template has now a section to display the Feed detection result, and a button to start the Feed Detection. A new JS file contains the necessary JS (Ajax and Event management) to fill the Feed Detection section. * Coding policy fixes * [Core] New feature : User Interface to "Detect" Feed from an URL - Switch from old school XMLHttpRequest to fetch - Enhance UX of search results - Revert to it's original content - Switch to a new Action : FindfeedAction.php - Switch to template literals instead of string concatenation - FindFeed action could retrun multiple feeds - Results are sent with an absolute URL - Switch to Json::encode() helper function * [Core] New feature : User Interface to "Detect" Feed from an URL - Move specific JS code to rss-bridge.js - Change HTML tag for the button to have a consistant style with th rest of the page * [Core] New feature : User Interface to "Detect" Feed from an URL - If no context is sent, assume there is only one unnamed context - Find parameter name in global and currect context * fix * remove typo --------- Co-authored-by: Dag <me@dvikan.no>
This commit is contained in:
parent
54045be951
commit
7591b10219
4 changed files with 212 additions and 0 deletions
89
actions/FindfeedAction.php
Normal file
89
actions/FindfeedAction.php
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This action is used by the frontpage form search.
|
||||||
|
* It finds a bridge based off of a user input url.
|
||||||
|
* It uses bridges' detectParameters implementation.
|
||||||
|
*/
|
||||||
|
class FindfeedAction implements ActionInterface
|
||||||
|
{
|
||||||
|
public function execute(array $request)
|
||||||
|
{
|
||||||
|
$targetURL = $request['url'] ?? null;
|
||||||
|
$format = $request['format'] ?? null;
|
||||||
|
|
||||||
|
if (!$targetURL) {
|
||||||
|
return new Response('You must specify a url', 400);
|
||||||
|
}
|
||||||
|
if (!$format) {
|
||||||
|
return new Response('You must specify a format', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
$bridgeFactory = new BridgeFactory();
|
||||||
|
|
||||||
|
$results = [];
|
||||||
|
foreach ($bridgeFactory->getBridgeClassNames() as $bridgeClassName) {
|
||||||
|
if (!$bridgeFactory->isEnabled($bridgeClassName)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$bridge = $bridgeFactory->create($bridgeClassName);
|
||||||
|
|
||||||
|
$bridgeParams = $bridge->detectParameters($targetURL);
|
||||||
|
|
||||||
|
if ($bridgeParams === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// It's allowed to have no 'context' in a bridge (only a default context without any name)
|
||||||
|
// In this case, the reference to the parameters are found in the first element of the PARAMETERS array
|
||||||
|
|
||||||
|
$context = $bridgeParams['context'] ?? 0;
|
||||||
|
|
||||||
|
$bridgeData = [];
|
||||||
|
// Construct the array of parameters
|
||||||
|
foreach ($bridgeParams as $key => $value) {
|
||||||
|
// 'context' is a special case : it's a bridge parameters, there is no "name" for this parameter
|
||||||
|
if ($key == 'context') {
|
||||||
|
$bridgeData[$key]['name'] = 'Context';
|
||||||
|
$bridgeData[$key]['value'] = $value;
|
||||||
|
} else {
|
||||||
|
$bridgeData[$key]['name'] = $this->getParameterName($bridge, $context, $key);
|
||||||
|
$bridgeData[$key]['value'] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$bridgeParams['bridge'] = $bridgeClassName;
|
||||||
|
$bridgeParams['format'] = $format;
|
||||||
|
$content = [
|
||||||
|
'url' => get_home_page_url() . '?action=display&' . http_build_query($bridgeParams),
|
||||||
|
'bridgeParams' => $bridgeParams,
|
||||||
|
'bridgeData' => $bridgeData,
|
||||||
|
'bridgeMeta' => [
|
||||||
|
'name' => $bridge::NAME,
|
||||||
|
'description' => $bridge::DESCRIPTION,
|
||||||
|
'parameters' => $bridge::PARAMETERS,
|
||||||
|
'icon' => $bridge->getIcon(),
|
||||||
|
],
|
||||||
|
];
|
||||||
|
$results[] = $content;
|
||||||
|
}
|
||||||
|
if ($results === []) {
|
||||||
|
return new Response(Json::encode(['message' => 'No bridge found for given url']), 404, ['content-type' => 'application/json']);
|
||||||
|
}
|
||||||
|
return new Response(Json::encode($results), 200, ['content-type' => 'application/json']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get parameter name in the actual context, or in the global parameter
|
||||||
|
private function getParameterName($bridge, $context, $key)
|
||||||
|
{
|
||||||
|
if (isset($bridge::PARAMETERS[$context][$key]['name'])) {
|
||||||
|
$name = $bridge::PARAMETERS[$context][$key]['name'];
|
||||||
|
} else if (isset($bridge::PARAMETERS['global'][$key]['name'])) {
|
||||||
|
$name = $bridge::PARAMETERS['global'][$key]['name'];
|
||||||
|
} else {
|
||||||
|
$name = 'Variable "' . $key . '" (No name provided)';
|
||||||
|
}
|
||||||
|
return $name;
|
||||||
|
}
|
||||||
|
}
|
|
@ -47,3 +47,82 @@ function rssbridge_toggle_bridge(){
|
||||||
bridge.getElementsByClassName('showmore-box')[0].checked = true;
|
bridge.getElementsByClassName('showmore-box')[0].checked = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var rssbridge_feed_finder = (function() {
|
||||||
|
/*
|
||||||
|
* Code for "Find feed by URL" feature
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Start the Feed search
|
||||||
|
async function rssbridge_feed_search(event) {
|
||||||
|
const input = document.getElementById('searchfield');
|
||||||
|
let content = input.value;
|
||||||
|
if (content) {
|
||||||
|
const findfeedresults = document.getElementById('findfeedresults');
|
||||||
|
findfeedresults.innerHTML = 'Searching for matching feeds ...';
|
||||||
|
let baseurl = window.location.protocol + window.location.pathname;
|
||||||
|
let url = baseurl + '?action=findfeed&format=Html&url=' + content;
|
||||||
|
const response = await fetch(url);
|
||||||
|
if (response.ok) {
|
||||||
|
const data = await response.json();
|
||||||
|
rss_bridge_feed_display_found_feed(data);
|
||||||
|
} else {
|
||||||
|
rss_bridge_feed_display_feed_search_fail();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rss_bridge_feed_display_find_feed_empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display the found feeds
|
||||||
|
function rss_bridge_feed_display_found_feed(obj) {
|
||||||
|
const findfeedresults = document.getElementById('findfeedresults');
|
||||||
|
|
||||||
|
let content = 'Found Feed(s) :';
|
||||||
|
|
||||||
|
// Let's go throug every Feed found
|
||||||
|
for (const element of obj) {
|
||||||
|
content += `<div class="search-result">
|
||||||
|
<div class="icon">
|
||||||
|
<img src="${element.bridgeMeta.icon}" width="60" />
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<h2><a href="${element.url}">${element.bridgeMeta.name}</a></h2>
|
||||||
|
<p>
|
||||||
|
<span class="description"><a href="${element.url}">${element.bridgeMeta.description}</a></span>
|
||||||
|
</p>
|
||||||
|
<div>
|
||||||
|
<ul>`;
|
||||||
|
|
||||||
|
// Now display every Feed parameter
|
||||||
|
for (const param in element.bridgeData) {
|
||||||
|
content += `<li>${element.bridgeData[param].name} : ${element.bridgeData[param].value}</li>`;
|
||||||
|
}
|
||||||
|
content += `</div>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
content += '<p><div class="alert alert-info" role="alert">This feed may be only one of the possible feeds. You may find more feeds using one of the bridges with different parameters, for example.</div></p>';
|
||||||
|
findfeedresults.innerHTML = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display an error if no feed were found
|
||||||
|
function rss_bridge_feed_display_feed_search_fail() {
|
||||||
|
const findfeedresults = document.getElementById('findfeedresults');
|
||||||
|
findfeedresults.innerHTML = 'No Feed found !<div class="alert alert-info" role="alert">Not every bridge supports feed detection. You can check below within the bridge parameters to create a feed.</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty the Found Feed section
|
||||||
|
function rss_bridge_feed_display_find_feed_empty() {
|
||||||
|
const findfeedresults = document.getElementById('findfeedresults');
|
||||||
|
findfeedresults.innerHTML = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add Event to 'Detect Feed" button
|
||||||
|
var rssbridge_feed_finder = function() {
|
||||||
|
const button = document.getElementById('findfeed');
|
||||||
|
button.addEventListener("click", rssbridge_feed_search);
|
||||||
|
button.addEventListener("keyup", rssbridge_feed_search);
|
||||||
|
};
|
||||||
|
return rssbridge_feed_finder;
|
||||||
|
}());
|
||||||
|
|
|
@ -453,3 +453,38 @@ button {
|
||||||
color: #d8d3cb;
|
color: #d8d3cb;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* find-feed */
|
||||||
|
.search-result {
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 15px;
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
.search-result {
|
||||||
|
background-color: #202325;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.search-result h2 {
|
||||||
|
color: #288cfc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-result a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #248afa;
|
||||||
|
}
|
||||||
|
.search-result .icon {
|
||||||
|
margin: 0 15px 0 0;
|
||||||
|
}
|
||||||
|
.search-result span {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.search-result .description {
|
||||||
|
font-size: 110%;
|
||||||
|
margin-right: 0 !important;
|
||||||
|
margin-top: 5px !important;
|
||||||
|
}
|
||||||
|
/* end find-feed */
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', rssbridge_toggle_bridge);
|
document.addEventListener('DOMContentLoaded', rssbridge_toggle_bridge);
|
||||||
document.addEventListener('DOMContentLoaded', rssbridge_list_search);
|
document.addEventListener('DOMContentLoaded', rssbridge_list_search);
|
||||||
|
document.addEventListener('DOMContentLoaded', rssbridge_feed_finder);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<section class="searchbar">
|
<section class="searchbar">
|
||||||
|
@ -15,6 +16,14 @@
|
||||||
onkeyup="rssbridge_list_search()"
|
onkeyup="rssbridge_list_search()"
|
||||||
value=""
|
value=""
|
||||||
>
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
id="findfeed"
|
||||||
|
name="findfeed"
|
||||||
|
/>Find Feed from URL</button>
|
||||||
|
<section id="findfeedresults">
|
||||||
|
</section>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<?= raw($bridges) ?>
|
<?= raw($bridges) ?>
|
||||||
|
|
Loading…
Reference in a new issue