172
151
if (!v::numeric()->validate($this->getOption('numItems', 0)))
173
152
throw new \InvalidArgumentException(__('The value in Number of Items must be numeric.'));
175
if (!v::intType()->min(0)->validate($this->getOption('updateInterval')))
154
if (!v::numeric()->notEmpty()->min(0)->validate($this->getOption('updateInterval')))
176
155
throw new \InvalidArgumentException(__('Update Interval must be greater than or equal to 0'));
181
* Adds a Ticker Widget
183
* path="/playlist/widget/ticker/{playlistId}",
184
* operationId="WidgetTickerAdd",
186
* summary="Add a ticker Widget",
187
* description="Add a new ticker Widget to the specified playlist",
191
* description="The playlist ID to add a Widget to",
198
* description="Optional Widget Name",
205
* description="The Widget Duration",
210
* name="useDuration",
212
* description="(0, 1) Select 1 only if you will provide duration parameter as well",
219
* description="Add only - 1 for rss feed, 2 for dataset",
226
* description="For sourceId=1, the link for the rss feed",
233
* description="For sourceId=2, Create ticker Widget using provided dataSetId of an existing dataSet",
238
* name="updateInterval",
240
* description="EDIT Only - Update interval in minutes",
247
* description="Edit only - Effect that will be used to transitions between items, available options: fade, fadeout, scrollVert, scollHorz, flipVert, flipHorz, shuffle, tileSlide, tileBlind, marqueeUp, marqueeDown, marqueeRight, marqueeLeft",
254
* description="Edit only - The transition speed of the selected effect in milliseconds (1000 = normal) or the Marquee speed in a low to high scale (normal = 1)",
261
* description="EDIT Only and SourceId=1 - Copyright information to display as the last item in this feed. can be styled with the #copyright CSS selector",
268
* description="EDIT Only and SourceId=1 - The number of RSS items you want to display",
273
* name="takeItemsFrom",
275
* description="EDIT Only and SourceId=1 - Take the items form the beginning or the end of the list, available options: start, end",
280
* name="durationIsPerItem",
282
* description="A flag (0, 1), The duration specified is per item, otherwise it is per feed",
287
* name="itemsSideBySide",
289
* description="A flag (0, 1), Should items be shown side by side",
296
* description="EDIT Only, SourceId=2 - Upper low limit for this dataSet, 0 for nor limit",
303
* description="EDIT Only, SourceId=2 - Lower low limit for this dataSet, 0 for nor limit",
308
* name="itemsPerPage",
310
* description="EDIT Only - When in single mode, how many items per page should be shown",
317
* description="EDIT Only - The date format to apply to all dates returned by the ticker",
322
* name="allowedAttributes",
324
* description="EDIT Only and SourceId=1 - A comma separated list of attributes that should not be stripped from the feed",
331
* description="EDIT Only and SourceId=1 - A comma separated list of attributes that should be stripped from the feed",
336
* name="backgroundColor",
338
* description="Edit only - A HEX color to use as the background color of this widget",
343
* name="disableDateSort",
345
* description="EDIT Only, SourceId=1 - Should the date sort applied to the feed be disabled?",
350
* name="textDirection",
352
* description="EDIT Only, SourceId=1 - Which direction does the text in the feed use? Available options: ltr, rtl",
357
* name="noDataMessage",
359
* description="EDIT Only - A message to display when no data is returned from the source",
366
* description="EDIT Only, SourceId=1 - Template you'd like to apply, options available: title-only, prominent-title-with-desc-and-name-separator, media-rss-with-title, media-rss-wth-left-hand-text",
371
* name="overrideTemplate",
373
* description="EDIT Only, SourceId=1 - flag (0, 1) override template checkbox",
380
* description="Template for each item, replaces [itemsTemplate] in main template, Pass only with overrideTemplate set to 1 or with sourceId=2 ",
387
* description="Optional StyleSheet Pass only with overrideTemplate set to 1 or with sourceId=2 ",
394
* description="Optional JavaScript, Pass only with overrideTemplate set to 1 ",
401
* description="EDIT Only, SourceId=2 - SQL clause for filter this dataSet",
408
* description="EDIT Only, SourceId=2- SQL clause for how this dataSet should be ordered",
413
* name="useOrderingClause",
415
* description="EDIT Only, SourceId=2 - flag (0,1) Use advanced order clause - set to 1 if ordering is provided",
420
* name="useFilteringClause",
422
* description="EDIT Only, SourceId=2 - flag (0,1) Use advanced filter clause - set to 1 if filter is provided",
427
* name="randomiseItems",
429
* description="A flag (0, 1), whether to randomise the feed",
435
* description="successful operation",
436
* @SWG\Schema(ref="#/definitions/Widget"),
439
* description="Location of the new widget",
445
162
public function add()
447
$this->setDuration($this->getSanitizer()->getInt('duration', $this->getDuration()));
448
$this->setUseDuration($this->getSanitizer()->getCheckbox('useDuration'));
164
$this->setDuration(Sanitize::getInt('duration', $this->getDuration()));
449
165
$this->setOption('xmds', true);
450
$this->setOption('sourceId', $this->getSanitizer()->getInt('sourceId'));
451
$this->setOption('uri', urlencode($this->getSanitizer()->getString('uri')));
452
$this->setOption('durationIsPerItem', 1);
166
$this->setOption('sourceId', Sanitize::getInt('sourceId'));
167
$this->setOption('uri', urlencode(Sanitize::getString('uri')));
168
$this->setOption('dataSetId', Sanitize::getInt('dataSetId', 0));
453
169
$this->setOption('updateInterval', 120);
454
170
$this->setOption('speed', 2);
456
if ($this->getOption('sourceId') == 2)
457
$this->setOption('dataSetId', $this->getSanitizer()->getInt('dataSetId', 0));
459
172
// New tickers have template override set to 0 by add.
460
173
// the edit form can then default to 1 when the element doesn't exist (for legacy)
461
174
$this->setOption('overrideTemplate', 0);
473
186
// Source is selected during add() and cannot be edited.
474
187
// Other properties
475
$this->setDuration($this->getSanitizer()->getInt('duration', $this->getDuration()));
476
$this->setUseDuration($this->getSanitizer()->getCheckbox('useDuration'));
188
$this->setDuration(Sanitize::getInt('duration', $this->getDuration()));
477
189
$this->setOption('xmds', true);
478
$this->setOption('uri', urlencode($this->getSanitizer()->getString('uri')));
479
$this->setOption('updateInterval', $this->getSanitizer()->getInt('updateInterval', 120));
480
$this->setOption('speed', $this->getSanitizer()->getInt('speed', 2));
481
$this->setOption('name', $this->getSanitizer()->getString('name'));
482
$this->setOption('effect', $this->getSanitizer()->getString('effect'));
483
$this->setOption('copyright', $this->getSanitizer()->getString('copyright'));
484
$this->setOption('numItems', $this->getSanitizer()->getInt('numItems'));
485
$this->setOption('takeItemsFrom', $this->getSanitizer()->getString('takeItemsFrom'));
486
$this->setOption('durationIsPerItem', $this->getSanitizer()->getCheckbox('durationIsPerItem'));
487
$this->setOption('randomiseItems', $this->getSanitizer()->getCheckbox('randomiseItems'));
488
$this->setOption('itemsSideBySide', $this->getSanitizer()->getCheckbox('itemsSideBySide'));
489
$this->setOption('upperLimit', $this->getSanitizer()->getInt('upperLimit', 0));
490
$this->setOption('lowerLimit', $this->getSanitizer()->getInt('lowerLimit', 0));
492
$this->setOption('itemsPerPage', $this->getSanitizer()->getInt('itemsPerPage'));
493
$this->setOption('dateFormat', $this->getSanitizer()->getString('dateFormat'));
494
$this->setOption('allowedAttributes', $this->getSanitizer()->getString('allowedAttributes'));
495
$this->setOption('stripTags', $this->getSanitizer()->getString('stripTags'));
496
$this->setOption('backgroundColor', $this->getSanitizer()->getString('backgroundColor'));
497
$this->setOption('disableDateSort', $this->getSanitizer()->getCheckbox('disableDateSort'));
498
$this->setOption('textDirection', $this->getSanitizer()->getString('textDirection'));
499
$this->setOption('overrideTemplate', $this->getSanitizer()->getCheckbox('overrideTemplate'));
500
$this->setOption('templateId', $this->getSanitizer()->getString('templateId'));
501
$this->setRawNode('noDataMessage', $this->getSanitizer()->getParam('noDataMessage', ''));
502
$this->setRawNode('javaScript', $this->getSanitizer()->getParam('javaScript', ''));
505
if ($this->getOption('sourceId') == 2) {
506
// We are a data set, so get the custom filter controls
507
$this->setOption('filter', $this->getSanitizer()->getParam('filter', null));
508
$this->setOption('ordering', $this->getSanitizer()->getString('ordering'));
509
$this->setOption('useOrderingClause', $this->getSanitizer()->getCheckbox('useOrderingClause'));
510
$this->setOption('useFilteringClause', $this->getSanitizer()->getCheckbox('useFilteringClause'));
512
// Order and Filter criteria
513
$orderClauses = $this->getSanitizer()->getStringArray('orderClause');
514
$orderClauseDirections = $this->getSanitizer()->getStringArray('orderClauseDirection');
515
$orderClauseMapping = [];
518
foreach ($orderClauses as $orderClause) {
521
if ($orderClause == '')
524
// Map the stop code received to the stop ref (if there is one)
525
$orderClauseMapping[] = [
526
'orderClause' => $orderClause,
527
'orderClauseDirection' => isset($orderClauseDirections[$i]) ? $orderClauseDirections[$i] : '',
531
$this->setOption('orderClauses', json_encode($orderClauseMapping));
533
$filterClauses = $this->getSanitizer()->getStringArray('filterClause');
534
$filterClauseOperator = $this->getSanitizer()->getStringArray('filterClauseOperator');
535
$filterClauseCriteria = $this->getSanitizer()->getStringArray('filterClauseCriteria');
536
$filterClauseValue = $this->getSanitizer()->getStringArray('filterClauseValue');
537
$filterClauseMapping = [];
540
foreach ($filterClauses as $filterClause) {
543
if ($filterClause == '')
546
// Map the stop code received to the stop ref (if there is one)
547
$filterClauseMapping[] = [
548
'filterClause' => $filterClause,
549
'filterClauseOperator' => isset($filterClauseOperator[$i]) ? $filterClauseOperator[$i] : '',
550
'filterClauseCriteria' => isset($filterClauseCriteria[$i]) ? $filterClauseCriteria[$i] : '',
551
'filterClauseValue' => isset($filterClauseValue[$i]) ? $filterClauseValue[$i] : '',
555
$this->setOption('filterClauses', json_encode($filterClauseMapping));
557
// DataSet Tickers always have Templates provided.
558
$this->setRawNode('template', $this->getSanitizer()->getParam('ta_text', $this->getSanitizer()->getParam('template', null)));
559
$this->setRawNode('css', $this->getSanitizer()->getParam('ta_css', $this->getSanitizer()->getParam('css', null)));
561
} else if ($this->getOption('overrideTemplate') == 1) {
562
// Feed tickers should only use the template if they have override set.
563
$this->setRawNode('template', $this->getSanitizer()->getParam('ta_text', $this->getSanitizer()->getParam('template', null)));
564
$this->setRawNode('css', $this->getSanitizer()->getParam('ta_css', $this->getSanitizer()->getParam('css', null)));
190
$this->setOption('uri', Sanitize::getString('uri'));
191
$this->setOption('updateInterval', urlencode(Sanitize::getInt('updateInterval', 120)));
192
$this->setOption('speed', Sanitize::getInt('speed', 2));
193
$this->setOption('name', Sanitize::getString('name'));
194
$this->setOption('effect', Sanitize::getString('effect'));
195
$this->setOption('copyright', Sanitize::getString('copyright'));
196
$this->setOption('numItems', Sanitize::getInt('numItems'));
197
$this->setOption('takeItemsFrom', Sanitize::getString('takeItemsFrom'));
198
$this->setOption('durationIsPerItem', Sanitize::getCheckbox('durationIsPerItem'));
199
$this->setOption('itemsSideBySide', Sanitize::getCheckbox('itemsSideBySide'));
200
$this->setOption('upperLimit', Sanitize::getInt('upperLimit', 0));
201
$this->setOption('lowerLimit', Sanitize::getInt('lowerLimit', 0));
202
$this->setOption('filter', Sanitize::getString('filter'));
203
$this->setOption('ordering', Sanitize::getString('ordering'));
204
$this->setOption('itemsPerPage', Sanitize::getInt('itemsPerPage'));
205
$this->setOption('dateFormat', Sanitize::getString('dateFormat'));
206
$this->setOption('allowedAttributes', Sanitize::getString('allowedAttributes'));
207
$this->setOption('stripTags', Sanitize::getString('stripTags'));
208
$this->setOption('backgroundColor', Sanitize::getString('backgroundColor'));
209
$this->setOption('disableDateSort', Sanitize::getCheckbox('disableDateSort'));
210
$this->setOption('textDirection', Sanitize::getString('textDirection'));
211
$this->setOption('overrideTemplate', Sanitize::getCheckbox('overrideTemplate'));
212
$this->setOption('templateId', Sanitize::getString('templateId'));
215
$this->setRawNode('template', Sanitize::getParam('ta_text', null));
216
$this->setRawNode('css', Sanitize::getParam('ta_css', null));
567
218
// Save the widget
568
219
$this->validate();
569
220
$this->saveWidget();
575
223
public function hoverPreview()
577
225
$name = $this->getOption('name');
784
390
// Update and save widget if we've changed our assignments.
785
391
if ($this->hasMediaChanged())
786
$this->widget->save(['saveWidgetOptions' => false, 'notify' => false, 'notifyDisplays' => true, 'audit' => false]);
788
$this->concurrentRequestRelease();
392
$this->widget->save(['saveWidgetOptions' => false]);
790
394
return $this->renderTemplate($data);
796
* @return array|mixed|null
797
* @throws \Xibo\Exception\ConfigurationException
799
397
private function getRssItems($isPreview, $text)
801
399
// Make sure we have the cache location configured
802
Library::ensureLibraryExists($this->getConfig()->GetSetting('LIBRARY_LOCATION'));
400
Library::ensureLibraryExists();
804
402
// Create a key to use as a caching key for this item.
805
403
// the rendered feed will be cached, so it is important the key covers all options.
404
$key = md5(json_encode($this->widget->widgetOptions));
806
405
$feedUrl = urldecode($this->getOption('uri'));
808
// Lock this entire request
809
$this->concurrentRequestLock(md5($feedUrl));
811
/** @var \Stash\Item $cache */
812
$cache = $this->getPool()->getItem($this->makeCacheKey(md5($feedUrl)));
813
$cache->setInvalidationMethod(Invalidation::SLEEP, 5000, 15);
815
$this->getLog()->debug('Ticker with RSS source ' . $feedUrl . '. Cache key: ' . $cache->getKey());
817
// Get the document out of the cache
818
$document = $cache->get();
407
Log::debug('Ticker with RSS source %s. Cache key: %s.', $feedUrl, $key);
820
409
// Check our cache to see if the key exists
821
if ($cache->isMiss()) {
822
// Invalid local cache, requery using picofeed.
823
$this->getLog()->debug('Cache Miss, getting RSS items');
825
// Lock this cache item (120 seconds)
829
// Create a Guzzle Client to get the Feed XML
830
$client = new Client();
831
$response = $client->get($feedUrl, $this->getConfig()->getGuzzleProxy([
833
'Accept' => 'application/rss+xml, application/rdf+xml;q=0.8, application/atom+xml;q=0.6, application/xml;q=0.4, text/xml;q=0.4'
836
'timeout' => 20 // wait no more than 20 seconds: https://github.com/xibosignage/xibo/issues/1401
839
// Pull out the content type
840
$contentType = $response->getHeaderLine('Content-Type');
842
$this->getLog()->debug('Feed returned content-type ' . $contentType);
844
// https://github.com/xibosignage/xibo/issues/1401
845
if (stripos($contentType, 'rss') === false && stripos($contentType, 'xml') === false) {
846
// The content type isn't compatible
847
$this->getLog()->error('Incompatible content type: ' . $contentType);
852
$result = explode('charset=', $contentType);
853
$document['encoding'] = isset($result[1]) ? $result[1] : '';
854
$document['xml'] = $response->getBody()->getContents();
856
// Add this to the cache.
857
$cache->set($document);
858
$cache->expiresAfter($this->getOption('updateInterval', 360) * 60);
861
$this->getPool()->saveDeferred($cache);
863
$this->getLog()->debug('Processed feed and added to the cache for ' . $this->getOption('updateInterval', 360) . ' minutes');
865
} catch (RequestException $requestException) {
866
// Log and return empty?
867
$this->getLog()->error('Unable to get feed: ' . $requestException->getMessage());
868
$this->getLog()->debug($requestException->getTraceAsString());
410
if (Cache::has($key)) {
411
// Our local cache is valid
412
return Cache::get($key);
874
//$this->getLog()->debug(var_export($document, true));
415
// Our local cache is not valid
416
// Store our formatted items
876
// Cache HIT or we've requested
877
// Load the feed XML document into a feed parser
879
// Enable logging if we need to
880
if (LogService::resolveLogLevel($this->getConfig()->GetSetting('audit', 'error')) == \Slim\Log::DEBUG) {
420
$clientConfig = Config::getPicoFeedProxy($feedUrl);
884
422
// Allowable attributes
885
$clientConfig = new Config();
887
423
if ($this->getOption('allowedAttributes') != null) {
888
424
// need a sensible way to set this
889
425
// https://github.com/fguillot/picoFeed/issues/196
890
426
//$clientConfig->setFilterWhitelistedTags(explode(',', $this->getOption('allowedAttributes')));
429
// Enable logging if we need to
430
if (\Xibo\Helper\Log::resolveLogLevel(Config::GetSetting('audit', 'error')) == \Slim\Log::DEBUG) {
434
$reader = new Reader($clientConfig);
435
$resource = $reader->download($feedUrl);
893
437
// Get the feed parser
894
$reader = new Reader($clientConfig);
895
$parser = $reader->getParser($feedUrl, $document['xml'], $document['encoding']);
438
$parser = $reader->getParser($resource->getUrl(), $resource->getContent(), $resource->getEncoding());
897
440
// Get a feed object
898
441
$feed = $parser->execute();
443
// Parse the text template
445
preg_match_all('/\[.*?\]/', $text, $matches);
901
448
$feedItems = $feed->getItems();
903
} catch (PicoFeedException $picoFeedException) {
904
// Log and return empty?
905
$this->getLog()->error('Unable to get feed: ' . $picoFeedException->getMessage());
906
$this->getLog()->debug($picoFeedException->getTraceAsString());
910
// Output any PicoFeed logs
911
if (LogService::resolveLogLevel($this->getConfig()->GetSetting('audit', 'error')) == \Slim\Log::DEBUG) {
912
$this->getLog()->debug(var_export(Logger::getMessages(), true));
915
// Parse the text template
917
preg_match_all('/\[.*?\]/', $text, $matches);
919
// Disable date sorting?
920
if ($this->getOption('disableDateSort') == 0 && $this->getOption('randomiseItems', 0) == 0) {
921
// Sort the items array by date
922
usort($feedItems, function($a, $b) {
926
return ($a->getDate() < $b->getDate());
930
// Date format for the feed items
931
$dateFormat = $this->getOption('dateFormat', $this->getConfig()->GetSetting('DATE_FORMAT'));
933
// Set an expiry time for the media
934
$expires = $this->getDate()->parse()->addMinutes($this->getOption('updateInterval', 3600))->format('U');
936
// Render the content now
937
foreach ($feedItems as $item) {
938
/* @var Item $item */
940
// Substitute for all matches in the template
943
// Run through all [] substitutes in $matches
944
foreach ($matches[0] as $sub) {
947
// Does our [] have a | - if so we need to do some special parsing
948
if (strstr($sub, '|') !== false) {
949
// Use the provided name space to extract a tag
951
// Do we have more than 1 | - if we do then we are also interested in getting an attribute
952
if (substr_count($sub, '|') > 1)
953
list($tag, $namespace, $attribute) = explode('|', $sub);
955
list($tag, $namespace) = explode('|', $sub);
957
// Replace some things so that we know what we are looking at
958
$tag = str_replace('[', '', $tag);
959
$namespace = str_replace(']', '', $namespace);
961
if ($attribute !== null)
962
$attribute = str_replace(']', '', $attribute);
964
// What are we looking at
965
$this->getLog()->debug('Namespace: ' . $namespace . ', Tag: ' . $tag . ', Attribute: ' . $attribute);
966
//$this->getLog()->debug('Item content: %s', var_export($item, true));
968
// Are we an image place holder? [tag|image]
969
if ($namespace == 'image') {
970
// Try to get a link for the image
975
if (stripos($item->getEnclosureType(), 'image') > -1) {
976
// Use the link to get the image
977
$link = $item->getEnclosureUrl();
980
$this->getLog()->debug('No image found for Link|image tag using getEnclosureUrl');
983
$this->getLog()->debug('No image found for Link|image tag using getEnclosureType');
988
// Default behaviour just tries to get the content from the tag provided.
989
// it uses the attribute as a namespace if one has been provided
990
if ($attribute != null)
991
$tags = $item->getTag($tag, $attribute);
993
$tags = $item->getTag($tag);
995
if (count($tags) > 0 && !empty($tags[0]))
998
$this->getLog()->debug('Tag not found for [' . $tag . '] attribute [' . $attribute . ']');
1001
$this->getLog()->debug('Resolved link: ' . $link);
1003
// If we have managed to resolve a link, download it and replace the tag with the downloaded
1005
if ($link != NULL) {
1006
// Grab the profile image
1007
$file = $this->mediaFactory->queueDownload('ticker_' . md5($this->getOption('url') . $link), $link, $expires);
1009
$replace = '<img src="' . $this->getFileUrl($file, 'image') . '" ' . $attribute . ' />';
1012
// Our namespace is not "image". Which means we are a normal text substitution using a namespace/attribute
1013
if ($attribute != null)
1014
$tags = $item->getTag($tag, $attribute);
450
// Disable date sorting?
451
if ($this->getOption('disableDateSort') == 0) {
452
// Sort the items array by date
453
usort($feedItems, function($a, $b) {
457
return ($a->getDate() < $b->getDate());
461
// Date format for the feed items
462
$dateFormat = $this->getOption('dateFormat', Config::GetSetting('DATE_FORMAT'));
464
// Set an expiry time for the media
465
$expires = time() + ($this->getOption('updateInterval', 3600) * 60);
467
// Render the content now
468
foreach ($feedItems as $item) {
469
/* @var Item $item */
471
// Substitute for all matches in the template
474
// Run through all [] substitutes in $matches
475
foreach ($matches[0] as $sub) {
478
// Does our [] have a | - if so we need to do some special parsing
479
if (strstr($sub, '|') !== false) {
480
// Use the provided name space to extract a tag
482
// Do we have more than 1 | - if we do then we are also interested in getting an attribute
483
if (substr_count($sub, '|') > 1)
484
list($tag, $namespace, $attribute) = explode('|', $sub);
1016
$tags = $item->getTag($tag);
1018
// If we find some tags then do the business with them
1019
if ($tags != NULL && count($tags) > 0) {
1020
$replace = $tags[0];
486
list($tag, $namespace) = explode('|', $sub);
488
// Replace some things so that we know what we are looking at
489
$tag = str_replace('[', '', $tag);
490
$namespace = str_replace(']', '', $namespace);
492
// What are we looking at
493
Log::debug('Namespace: %s, Tag: %s, Attribute: %s', $namespace, $tag, $attribute);
494
Log::debug('Item content: %s', var_export($item, true));
496
// Are we an image place holder? [tag|image]
497
if ($namespace == 'image') {
498
// Try to get a link for the image
503
if ($item->getEnclosureType() == 'image') {
504
// Use the link to get the image
505
$link = $item->getEnclosureUrl();
510
// Default behaviour just tries to get the content from the tag provided.
511
// it uses the attribute as a namespace if one has been provided
512
if ($attribute != null) {
514
if (array_key_exists($attribute, $item->namespaces)) {
515
$tags = $item->getTag($tag);
518
Log::info('Looking for image with namespace %s, but that namespace does not exist.', $attribute);
521
$tags = $item->getTag($tag);
526
// If we have managed to resolve a link, download it and replace the tag with the downloaded
529
// Grab the profile image
530
$file = MediaFactory::createModuleFile('ticker_' . md5($this->getOption('url') . $link), $link);
531
$file->isRemote = true;
532
$file->expires = $expires;
535
// Tag this layout with this file
536
$this->assignMedia($file->mediaId);
538
$url = $this->getApp()->urlFor('library.download', ['id' => $file->mediaId, 'type' => 'image']);
539
$replace = ($isPreview) ? '<img src="' . $url . '?preview=1&width=' . $this->region->width . '&height=' . $this->region->height . '" ' . $attribute . '/>' : '<img src="' . $file->storedAs . '" ' . $attribute . ' />';
1022
$this->getLog()->debug('Tag not found for ' . $tag . ' attribute ' . $attribute);
1026
// Use the pool of standard tags
1029
$replace = $this->getOption('name');
1033
$replace = $item->getTitle();
1036
case '[Description]':
1037
// Try to get the description tag
1038
if (!$desc = $item->getTag('description')) {
1039
// use content with tags stripped
1040
$replace = strip_tags($item->getContent());
1043
$replace = $desc[0];
1048
$replace = $item->getContent();
1052
$replace = $item->getAuthor();
1056
$replace = $this->getDate()->getLocalDate($item->getDate()->format('U'), $dateFormat);
1060
$replace = $item->getTag('permalink');
1064
$replace = $item->getUrl();
1068
if (stripos($item->getEnclosureType(), 'image') > -1) {
1069
// Use the link to get the image
1070
$link = $item->getEnclosureUrl();
1072
if (!(empty($link))) {
1074
$file = $this->mediaFactory->queueDownload('ticker_' . md5($this->getOption('url') . $link), $link, $expires);
1076
$replace = '<img src="' . $this->getFileUrl($file, 'image') . '"/>';
1078
$this->getLog()->debug('No image found for image tag using getEnclosureUrl');
1081
$this->getLog()->debug('No image found for image tag using getEnclosureType');
1087
if ($this->getOption('stripTags') != '') {
1088
// Handle cache path for HTML serializer
1089
$cachePath = $this->getConfig()->GetSetting('LIBRARY_LOCATION') . 'cache/HTMLPurifier';
1090
if (!is_dir($cachePath))
1093
$config = \HTMLPurifier_Config::createDefault();
1094
$config->set('Cache.SerializerPath', $cachePath);
1095
$config->set('HTML.ForbiddenElements', explode(',', $this->getOption('stripTags')));
1096
$purifier = new \HTMLPurifier($config);
1097
$replace = $purifier->purify($replace);
1100
// Substitute the replacement we have found (it might be '')
1101
$rowString = str_replace($sub, $replace, $rowString);
1104
$items[] = $rowString;
1107
// Process queued downloads
1108
$this->mediaFactory->processDownloads(function($media) {
1110
$this->getLog()->debug((($media->isSaveRequired) ? 'Successfully downloaded ' : 'Download not required for ') . $media->mediaId);
1112
// Tag this layout with this file
1113
$this->assignMedia($media->mediaId);
1116
// Copyright information?
1117
if ($this->getOption('copyright', '') != '') {
1118
$items[] = '<span id="copyright">' . $this->getOption('copyright') . '</span>';
542
// Our namespace is not "image". Which means we are a normal text substitution using a namespace/attribute
543
if ($attribute != null)
544
$tags = $item->getTag($tag, $attribute);
546
$tags = $item->getTag($tag);
548
Log::debug('Tags:' . var_export($tags, true));
550
// If we find some tags then do the business with them
557
// Use the pool of standard tags
560
$replace = $this->getOption('name');
564
$replace = $item->getTitle();
567
case '[Description]':
568
$replace = $item->getContent();
572
$replace = $item->getContent();
576
$replace = $item->getAuthor();
580
$replace = Date::getLocalDate($item->getDate()->format('U'), $dateFormat);
584
$replace = $item->getTag('permalink');
588
$replace = $item->getUrl();
593
if ($this->getOption('stripTags') != '') {
594
$config = \HTMLPurifier_Config::createDefault();
595
$config->set('HTML.ForbiddenElements', explode(',', $this->getOption('stripTags')));
596
$purifier = new \HTMLPurifier($config);
597
$replace = $purifier->purify($replace);
600
// Substitute the replacement we have found (it might be '')
601
$rowString = str_replace($sub, $replace, $rowString);
604
$items[] = $rowString;
607
// Copyright information?
608
if ($this->getOption('copyright', '') != '') {
609
$items[] = '<span id="copyright">' . $this->getOption('copyright') . '</span>';
612
// Add this to the cache.
613
Cache::put($key, $items, $this->getOption('updateInterval', 360) * 60);
615
catch (PicoFeedException $e) {
616
Log::error('Unable to get feed: %s', $e->getMessage());
617
Log::debug($e->getTraceAsString());
620
if (\Xibo\Helper\Log::resolveLogLevel(Config::GetSetting('audit', 'error')) == \Slim\Log::DEBUG) {
621
Log::debug(json_encode(Logger::getMessages()));
624
// Return the formatted items
1130
628
private function getDataSetItems($displayId, $isPreview, $text)
1132
630
// Extra fields for data sets
1133
631
$dataSetId = $this->getOption('dataSetId');
1134
632
$upperLimit = $this->getOption('upperLimit');
1135
633
$lowerLimit = $this->getOption('lowerLimit');
1137
// Lock this request
1138
$this->concurrentRequestLock($dataSetId);
1143
if ($this->getOption('useOrderingClause', 1) == 1) {
1144
$ordering = $this->GetOption('ordering');
1146
// Build an order string
1147
foreach (json_decode($this->getOption('orderClauses', '[]'), true) as $clause) {
1148
$ordering .= $clause['orderClause'] . ' ' . $clause['orderClauseDirection'] . ',';
1151
$ordering = rtrim($ordering, ',');
1157
if ($this->getOption('useFilteringClause', 1) == 1) {
1158
$filter = $this->GetOption('filter');
1162
foreach (json_decode($this->getOption('filterClauses', '[]'), true) as $clause) {
1166
switch ($clause['filterClauseCriteria']) {
1169
$criteria = 'LIKE \'' . $clause['filterClauseValue'] . '%\'';
1173
$criteria = 'LIKE \'%' . $clause['filterClauseValue'] . '\'';
1177
$criteria = 'LIKE \'%' . $clause['filterClauseValue'] . '%\'';
1181
$criteria = '= \'' . $clause['filterClauseValue'] . '\'';
1184
case 'not-contains':
1185
$criteria = 'NOT LIKE \'%' . $clause['filterClauseValue'] . '%\'';
1188
case 'not-starts-with':
1189
$criteria = 'NOT LIKE \'' . $clause['filterClauseValue'] . '%\'';
1192
case 'not-ends-with':
1193
$criteria = 'NOT LIKE \'%' . $clause['filterClauseValue'] . '\'';
1197
$criteria = '<> \'' . $clause['filterClauseValue'] . '\'';
1200
case 'greater-than':
1201
$criteria = '> \'' . $clause['filterClauseValue'] . '\'';
1205
$criteria = '< \'' . $clause['filterClauseValue'] . '\'';
1213
$filter .= ' ' . $clause['filterClauseOperator'] . ' ';
1215
$filter .= $clause['filterClause'] . ' ' . $criteria;
1219
$this->getLog()->notice('Then template for each row is: ' . $text);
634
$filter = $this->getOption('filter');
635
$ordering = $this->getOption('ordering');
637
Log::notice('Then template for each row is: ' . $text);
1221
639
// Set an expiry time for the media
1222
640
$expires = time() + ($this->getOption('updateInterval', 3600) * 60);