diff --git a/lib/Connector/XiboDashboardConnector.php b/lib/Connector/XiboDashboardConnector.php index ac0245af05..4dac845dab 100644 --- a/lib/Connector/XiboDashboardConnector.php +++ b/lib/Connector/XiboDashboardConnector.php @@ -42,6 +42,9 @@ class XiboDashboardConnector implements ConnectorInterface { use ConnectorTrait; + /** @var float|int The token TTL */ + const TOKEN_TTL_SECONDS = 3600 * 24 * 2; + /** @var string Used when rendering the form */ private $errorMessage; @@ -517,7 +520,7 @@ public function onDataRequest(DashboardDataRequestEvent $event, $eventName, Even try { $tokenEvent = new XmdsConnectorTokenEvent(); $tokenEvent->setTargets($event->getDataProvider()->getDisplayId(), $event->getDataProvider()->getWidgetId()); - $tokenEvent->setTtl(3600 * 24 * 2); + $tokenEvent->setTtl(self::TOKEN_TTL_SECONDS); $dispatcher->dispatch($tokenEvent, XmdsConnectorTokenEvent::$NAME); $token = $tokenEvent->getToken(); @@ -537,6 +540,11 @@ public function onDataRequest(DashboardDataRequestEvent $event, $eventName, Even $item['token'] = $token; $item['isPreview'] = $event->getDataProvider()->isPreview(); + // We make sure our data cache expires shortly before the token itself expires (so that we have a new token + // generated for it). + $event->getDataProvider()->setCacheTtl(self::TOKEN_TTL_SECONDS - 3600); + + // Add our item and set handled $event->getDataProvider()->addItem($item); $event->getDataProvider()->setIsHandled(); } diff --git a/lib/Entity/Layout.php b/lib/Entity/Layout.php index 72e0743a33..ec6415bd90 100644 --- a/lib/Entity/Layout.php +++ b/lib/Entity/Layout.php @@ -1853,6 +1853,9 @@ public function assessWidgetStatus(Module $module, Widget $widget, int &$status) } } } catch (GeneralException $xiboException) { + $this->getLog()->debug('assessWidgetStatus: ' . $module->moduleId . ' invalid, e: ' + . $xiboException->getMessage()); + $moduleStatus = Status::$STATUS_INVALID; // Include the exception on diff --git a/lib/Widget/Compatibility/RssWidgetCompatibility.php b/lib/Widget/Compatibility/RssWidgetCompatibility.php index fd99bcde70..1bc35479ec 100644 --- a/lib/Widget/Compatibility/RssWidgetCompatibility.php +++ b/lib/Widget/Compatibility/RssWidgetCompatibility.php @@ -58,6 +58,45 @@ public function upgradeWidget(Widget $widget, int $fromSchema, int $toSchema): b } $widget->setOptionValue('templateId', 'attrib', $newTemplateId); + // If the new templateId is custom, we need to parse the old template for image enclosures + if ($newTemplateId === 'article_custom_html') { + $template = $widget->getOptionValue('template', null); + if (!empty($template)) { + $modified = false; + $matches = []; + preg_match_all('/\[(.*?)\]/', $template, $matches); + + for ($i = 0; $i < count($matches[1]); $i++) { + // We have a [Link] or a [xxx|image] tag + $match = $matches[1][$i]; + if ($match === 'Link' || $match === 'Link|image') { + // This is a straight-up enclosure (which is the default). + $template = str_replace($matches[0][$i], 'Image', $template); + $modified = true; + } else if (str_contains($match, '|image')) { + // [tag|image|attribute] + // Set the necessary options depending on how our tag is made up + $parts = explode('|', $match); + $tag = $parts[0]; + $attribute = $parts[2] ?? null; + + $widget->setOptionValue('imageSource', 'attrib', 'custom'); + $widget->setOptionValue('imageSourceTag', 'attrib', $tag); + if (!empty($attribute)) { + $widget->setOptionValue('imageSourceAttribute', 'attrib', $attribute); + } + + $template = str_replace($matches[0][$i], 'Image', $template); + $modified = true; + } + } + + if ($modified) { + $widget->setOptionValue('template', 'attrib', $template); + } + } + } + // Change some other options if they have been set. foreach ($widget->widgetOptions as $option) { $widgetChangeOption = null; diff --git a/lib/Widget/RssProvider.php b/lib/Widget/RssProvider.php index ae63ff3bfc..760c879103 100644 --- a/lib/Widget/RssProvider.php +++ b/lib/Widget/RssProvider.php @@ -137,6 +137,21 @@ public function fetchData(DataProviderInterface $dataProvider): WidgetProviderIn } } + // Where should we get images? + $imageSource = $dataProvider->getProperty('imageSource', 'enclosure'); + $imageTag = match ($imageSource) { + 'mediaContent' => 'media:content', + 'image' => 'image', + 'custom' => $dataProvider->getProperty('imageSourceTag', 'image'), + default => 'enclosure' + }; + $imageSourceAttribute = null; + if ($imageSource === 'mediaContent') { + $imageSourceAttribute = 'url'; + } else if ($imageSource === 'custom') { + $imageSourceAttribute = $dataProvider->getProperty('imageSourceAttribute', null); + } + $countItems = 0; // Parse each item into an article foreach ($feedItems as $item) { @@ -159,14 +174,19 @@ public function fetchData(DataProviderInterface $dataProvider): WidgetProviderIn $article->summary = trim($descriptionTag ? strip_tags($descriptionTag[0]) : $article->content); // Do we have an image included? - if (stripos($item->getEnclosureType(), 'image') > -1) { - $link = $item->getEnclosureUrl(); - - if (!(empty($link))) { - $article->image = $dataProvider->addImage('ticker_' . md5($link), $link, $expiresImage); - } else { - $this->getLog()->debug('No image found for image tag using getEnclosureUrl'); + $link = null; + if ($imageTag === 'enclosure') { + if (stripos($item->getEnclosureType(), 'image') > -1) { + $link = $item->getEnclosureUrl(); } + } else { + $link = $item->getTag($imageTag, $imageSourceAttribute)[0] ?? null; + } + + if (!(empty($link))) { + $article->image = $dataProvider->addImage('ticker_' . md5($link), $link, $expiresImage); + } else { + $this->getLog()->debug('fetchData: no image found for image tag using ' . $imageTag); } if ($dataProvider->getProperty('decodeHtml') == 1) { diff --git a/modules/countdown-clock.xml b/modules/countdown-clock.xml index ee98668b40..bc33c84c6a 100755 --- a/modules/countdown-clock.xml +++ b/modules/countdown-clock.xml @@ -86,7 +86,7 @@ 2 - + 2 diff --git a/modules/countdown-custom.xml b/modules/countdown-custom.xml index f739d58975..d5e5d12e2a 100755 --- a/modules/countdown-custom.xml +++ b/modules/countdown-custom.xml @@ -90,7 +90,7 @@ 2 - + 2 diff --git a/modules/countdown-days.xml b/modules/countdown-days.xml index d4a454ca3a..84336206bb 100755 --- a/modules/countdown-days.xml +++ b/modules/countdown-days.xml @@ -91,7 +91,7 @@ 2 - + 2 diff --git a/modules/countdown-table.xml b/modules/countdown-table.xml index bdf4e4e431..2a5f35612b 100755 --- a/modules/countdown-table.xml +++ b/modules/countdown-table.xml @@ -91,7 +91,7 @@ 2 - + 2 diff --git a/modules/countdown-text.xml b/modules/countdown-text.xml index 713b5649d7..6ce5315ed3 100755 --- a/modules/countdown-text.xml +++ b/modules/countdown-text.xml @@ -91,7 +91,7 @@ 2 - + 2 diff --git a/modules/rss-ticker.xml b/modules/rss-ticker.xml index 9b301e80cd..d9a02efd03 100644 --- a/modules/rss-ticker.xml +++ b/modules/rss-ticker.xml @@ -29,7 +29,7 @@ rss-ticker ticker article - %uri%_%numItems%_%stripTags% + %uri%_%numItems%_%stripTags%_%imageSource%_%imageSourceTag% 2 1 1 @@ -99,6 +99,37 @@ Should the order of the feed be randomised? When enabled each time the Widget is shown the items will be randomly shuffled and displayed in a random order. 0 + + Image Tag + Choose the tag in the feed to get an image URL + enclosure + + + + + + + + + Custom Tag + A valid tag name which appears in this feed and will be used to get an image URL. + + + + custom + + + + + Custom Tag Attribute + If the image URL is on an attribute of the custom tag, provide the attribute name. + + + + custom + + + Allowable Attributes A comma separated list of attributes that should not be stripped from the incoming feed. diff --git a/modules/templates/article-static.xml b/modules/templates/article-static.xml index c3f5021eac..645ea3fad0 100644 --- a/modules/templates/article-static.xml +++ b/modules/templates/article-static.xml @@ -192,7 +192,7 @@ $(target).xiboTextRender(properties, $(target).find('#content > *')); - + {{#if image}}{{/if}} ]]> .image, #content - + {{#if image}}{{/if}}
{{title}}
{{{content}}}
@@ -516,7 +516,7 @@ $(target).xiboTextRender(properties, $(target).find('#content > .image, #content - + {{#if image}}{{/if}}
{{title}}
diff --git a/views/welcome-page.twig b/views/welcome-page.twig index 895066dac6..e2fc5eab7c 100644 --- a/views/welcome-page.twig +++ b/views/welcome-page.twig @@ -68,7 +68,6 @@ Upgrade {% else %}

{{ "Your service provider can help you with installation and upgrade."|trans }}

- Get Help {% endif %}
@@ -159,7 +158,6 @@ Community {% else %}

{{ "Contact your service provider for assistance."|trans }}

- {{ "Get Help"|trans }} {% endif %} diff --git a/web/xmds.php b/web/xmds.php index face94f719..f3b9a75d83 100755 --- a/web/xmds.php +++ b/web/xmds.php @@ -162,8 +162,16 @@ throw new \Xibo\Support\Exception\InstanceSuspendedException('Bandwidth Exceeded'); } - // Check the display specific limit next. + // Get the display + /** @var \Xibo\Entity\Display $display */ $display = $container->get('displayFactory')->getById($displayId); + + // Check it is still authorised. + if ($display->licensed == 0) { + throw new NotFoundException(__('Display unauthorised')); + } + + // Check the display specific limit next. $usage = 0; if ($container->get('bandwidthFactory')->isBandwidthExceeded( $display->bandwidthLimit, @@ -283,6 +291,15 @@ exit; } + // Get the display + /** @var \Xibo\Entity\Display $display */ + $display = $container->get('displayFactory')->getById($tokenEvent->getDisplayId()); + + // Check it is still authorised. + if ($display->licensed == 0) { + throw new NotFoundException(__('Display unauthorised')); + } + // Check the widgetId is permissible, and in required files for the display. /** @var \Xibo\Entity\RequiredFile $file */ $file = $container->get('requiredFileFactory')->getByDisplayAndWidget(