Can I upload feed.xml 
Автор: Raoul R.
Просмотрено 79,
Подписчики 1,
Размещенный 0
Our review platform does not allow embedding via i-frame. And come back to me that I should place the feed.xml on my server for crawling.....
Is there a way that I can upload the feed.xml to server (css)?
https://www.klantenvertellen.nl/v1/review/feed.xml?hash=o8st5h6aoduubpq
Размещено

I don't know if I understood the problem correctly. Files can be uploaded to your own web server or web space using an FTP program or using the FTP window of WebSite X5.
-----
Автор
PHP class for your exact feed
Save as:
/includes/klantenvertellen_feed.php
<?php
declare(strict_types=1);
final class KlantenVertellenFeed
{
private string $feedUrl;
private string $cacheFile;
private int $cacheTtlSeconds;
public function __construct(string $feedUrl, string $cacheFile, int $cacheTtlSeconds = 21600)
{
$this->feedUrl = $feedUrl;
$this->cacheFile = $cacheFile;
$this->cacheTtlSeconds = $cacheTtlSeconds;
}
/**
* @return array{
* locationName:string,
* averageRating:string,
* numberReviews:string,
* viewReviewUrl:string,
* reviews:array<int, array{
* author:string,
* city:string,
* rating:string,
* reviewBody:string,
* datePublished:string,
* language:string
* }>
* }
*/
public function getData(int $maxReviews = 3): array
{
$xml = $this->loadXml();
if (!$xml instanceof SimpleXMLElement) {
return [
'locationName' => 'ClubTaxi',
'averageRating' => '',
'numberReviews' => '',
'viewReviewUrl' => 'https://www.klantenvertellen.nl/reviews/1023381/clubtaxi'
'reviews' => [],
];
}
$data = [
'locationName' => trim((string) $xml->locationName),
'averageRating' => trim((string) $xml->averageRating),
'numberReviews' => trim((string) $xml->numberReviews),
'viewReviewUrl' => trim((string) $xml->viewReviewUrl),
'reviews' => [],
];
if (isset($xml->reviews->reviews)) {
$count = 0;
foreach ($xml->reviews->reviews as $reviewNode) {
$reviewBody = $this->extractReviewText($reviewNode);
$data['reviews'][] = [
'author' => trim((string) $reviewNode->reviewAuthor) ?: 'Klant',
'city' => trim((string) $reviewNode->city),
'rating' => trim((string) $reviewNode->rating),
'reviewBody' => $reviewBody,
'datePublished' => $this->normaliseDate((string) $reviewNode->dateSince),
'language' => trim((string) $reviewNode->reviewLanguage),
];
$count++;
if ($count >= $maxReviews) {
break;
}
}
}
return $data;
}
private function extractReviewText(SimpleXMLElement $reviewNode): string
{
if (!isset($reviewNode->reviewContent->reviewContent)) {
return '';
}
$oneLiner = '';
$opinion = '';
foreach ($reviewNode->reviewContent->reviewContent as $contentNode) {
$group = trim((string) $contentNode->questionGroup);
$value = trim((string) $contentNode->rating);
if ($group === 'DEFAULT_ONELINER' && $value !== '') {
$oneLiner = $value;
}
if ($group === 'DEFAULT_OPINION' && $value !== '') {
$opinion = $value;
}
}
// Prefer fuller opinion text; fall back to one-liner.
if ($opinion !== '') {
return $opinion;
}
return $oneLiner;
}
private function loadXml(): ?SimpleXMLElement
{
$xmlString = $this->getXmlString();
if ($xmlString === null || trim($xmlString) === '') {
return null;
}
libxml_use_internal_errors(true);
$xml = simplexml_load_string($xmlString);
return $xml instanceof SimpleXMLElement ? $xml : null;
}
private function getXmlString(): ?string
{
if ($this->isCacheValid()) {
$cached = @file_get_contents($this->cacheFile);
return $cached !== false ? $cached : null;
}
$fresh = $this->downloadFeed();
if ($fresh !== null) {
$this->writeCache($fresh);
return $fresh;
}
if (is_file($this->cacheFile)) {
$cached = @file_get_contents($this->cacheFile);
return $cached !== false ? $cached : null;
}
return null;
}
private function isCacheValid(): bool
{
return is_file($this->cacheFile)
&& filemtime($this->cacheFile) !== false
&& (time() - (int) filemtime($this->cacheFile) < $this->cacheTtlSeconds);
}
private function downloadFeed(): ?string
{
$context = stream_context_create([
'http' => [
'method' => 'GET',
'timeout' => 10,
'header' => implode("\r\n", [
'User-Agent: ClubTaxi Review Feed Reader/1.0',
'Accept: application/xml,text/xml,*/*;q=0.8',
]),
],
'ssl' => [
'verify_peer' => true,
'verify_peer_name' => true,
],
]);
$result = @file_get_contents($this->feedUrl, false, $context);
return $result !== false ? $result : null;
}
private function writeCache(string $xml): void
{
$dir = dirname($this->cacheFile);
if (!is_dir($dir)) {
@mkdir($dir, 0775, true);
}
@file_put_contents($this->cacheFile, $xml, LOCK_EX);
}
private function normaliseDate(string $date): string
{
$date = trim($date);
if ($date === '') {
return '';
}
try {
$dt = new DateTimeImmutable($date);
return $dt->format('Y-m-d');
} catch (Throwable $e) {
return '';
}
}
}
Homepage usage
Place this where you want the review block to appear:
<?php
require_once __DIR__ . '/includes/klantenvertellen_feed.php';
$kvFeed = new KlantenVertellenFeed(
'https://www.klantenvertellen.nl/v1/review/feed.xml?hash=o8st5h6aoduubpq'
__DIR__ . '/cache/klantenvertellen_feed.xml',
21600 // 6 hours
);
$kv = $kvFeed->getData(3);
?>
<section class="reviews-panel">
<h3>Beoordeeld op KlantenVertellen</h3>
<?php if ($kv['averageRating'] !== '' && $kv['numberReviews'] !== ''): ?>
<div class="reviews-score">
<span class="score"><?php echo htmlspecialchars($kv['averageRating'], ENT_QUOTES, 'UTF-8'); ?></span>
<span class="score-meta">/ 10 op basis van <?php echo htmlspecialchars($kv['numberReviews'], ENT_QUOTES, 'UTF-8'); ?> beoordelingen</span>
</div>
<?php endif; ?>
<p class="reviews-intro">
ClubTaxi wordt door klanten beoordeeld om betrouwbaarheid, stiptheid en comfortabele ritten.
</p>
<?php if (!empty($kv['reviews'])): ?>
<div class="review-quotes">
<?php foreach ($kv['reviews'] as $review): ?>
<blockquote class="review-quote">
<p>“<?php echo htmlspecialchars($review['reviewBody'], ENT_QUOTES, 'UTF-8'); ?>”</p>
<footer>
– <?php echo htmlspecialchars($review['author'], ENT_QUOTES, 'UTF-8'); ?>
<?php if ($review['city'] !== ''): ?>
uit <?php echo htmlspecialchars($review['city'], ENT_QUOTES, 'UTF-8'); ?>
<?php endif; ?>
<?php if ($review['rating'] !== ''): ?>
(<?php echo htmlspecialchars($review['rating'], ENT_QUOTES, 'UTF-8'); ?>/10)
<?php endif; ?>
</footer>
</blockquote>
<?php endforeach; ?>
</div>
<?php endif; ?>
<p class="reviews-link">
<a href="<?php echo htmlspecialchars($kv['viewReviewUrl'], ENT_QUOTES, 'UTF-8'); ?>" target="_blank" rel="noopener noreferrer">
Bekijk alle beoordelingen op KlantenVertellen
</a>
</p>
</section>
CSS
.reviews-panel {
max-width: 900px;
margin: 2rem auto;
padding: 1.5rem;
border: 1px solid #ddd;
border-radius: 12px;
background: #fff;
}
.reviews-panel h3 {
margin-top: 0;
margin-bottom: 0.75rem;
}
.reviews-score {
margin-bottom: 1rem;
line-height: 1.2;
}
.reviews-score .score {
font-size: 2.2rem;
font-weight: 700;
}
.reviews-score .score-meta {
display: block;
font-size: 1rem;
}
.reviews-intro {
margin-bottom: 1.25rem;
}
.review-quotes {
display: grid;
gap: 1rem;
}
.review-quote {
margin: 0;
padding: 1rem;
border-left: 4px solid #ccc;
background: #f8f8f8;
border-radius: 6px;
}
.review-quote p {
margin: 0 0 0.5rem 0;
}
.review-quote footer {
font-size: 0.95rem;
}
.reviews-link {
margin-top: 1.25rem;
}
.reviews-link a {
font-weight: 600;
text-decoration: none;
}
.reviews-link a:hover,
.reviews-link a:focus {
text-decoration: underline;
}
Dynamic JSON-LD from the same feed
This lets you keep your rating data automatically up to date.
<?php if ($kv['averageRating'] !== '' && $kv['numberReviews'] !== ''): ?>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "ClubTaxi",
"url": "https://www.clubtaxi.nl",
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "<?php echo htmlspecialchars($kv['averageRating'], ENT_QUOTES, 'UTF-8'); ?>",
"bestRating": "10",
"reviewCount": "<?php echo htmlspecialchars($kv['numberReviews'], ENT_QUOTES, 'UTF-8'); ?>"
}
}
</script>
<?php endif; ?>
There is also a code suggested by ChatGPT for filtering language. But hey, this is way over my head already : ) Thanks
Автор
Thanks Daniel, I got that. But how to implement the feed.xml into the project, that it gets uploaded, to a proper folder so that bots know to crawl the feed. That is the thing.
If I understood correctly, then the PHP code is probably saved in the file klantenvertellen.php (and not in the file feed.xml) and loaded into the "includes" directory on the web space.
The feed has the file extension .php - for WebSite X5 e.g. x5feed.php (RSS feed).
With WebSite X5, the RSS feed readers need the file x5feed.php and the review feed probably needs the file klantenvertellen.php - if I understood that correctly.
Автор
I have not saved anything yet related to the feed.xml. But I looked at using the RSS feed tool as well. And my conclusion was, that I cannot for this purpose. If that is what you are saying, correct: we are on the same page.
Автор
The fact that platforms do not allow embedding is a common thing, I do not agree but it is what it is. Hosted by Godaddy means to me that I do not want to experiment, cause they are experts in blaming the customer for things that go wrong. I like not to edit files via their dashboard. I think the conclusion is that the team looks at the above as a request to develope a utility widget for this purpose. Await respons from the team, thanks Daniel, sleep well : )
The problem is probably that the PHP script wants to access a third-party website - klantenvertellen.nl - and is therefore blocked by the GoDaddy firewall.
Let's see if the Incomedia staff has a solution or just point you to GoDaddy support.
Maybe GoDaddy users have some tips on how to solve the problem.
I'll say goodnight!
-----
My test page with RSS widget from Elfsight (1 Widget in free plan), see
>> https://findelinks.de/123test-aufklappmenue-2/elfsight-rss-feed-widget.html
If Elfsight's widgets are also blocked by GoDaddy, then certain addresses/IPs would probably have to be activated so that they are not blocked.
Good bye
----- Settings at Elfsight -----
Автор
to be clear, I did not upload anything yet, nothing got refused by godaddy. I cannot create an upload that fits the criteria given and described in the above. Thanks Daniel, but that widget is not creating the files that I think I need on the server as well. Await respons from the team.