~xibo-maintainers/xibo/tempel

« back to all changes in this revision

Viewing changes to lib/Widget/Ticker.php

  • Committer: Dan Garner
  • Date: 2015-09-29 15:16:59 UTC
  • mto: (454.2.11) (471.2.2)
  • mto: This revision was merged to the branch mainline in revision 468.
  • Revision ID: git-v1:ae24387a7b1397750b6ec86d0f286373da05eb16
Fixed Display Version Information Form (not showing media name)

Show diffs side-by-side

added added

removed removed

Lines of Context:
20
20
 */
21
21
namespace Xibo\Widget;
22
22
 
23
 
use GuzzleHttp\Client;
24
 
use GuzzleHttp\Exception\RequestException;
25
 
use PicoFeed\Config\Config;
26
23
use PicoFeed\Logging\Logger;
27
24
use PicoFeed\Parser\Item;
28
25
use PicoFeed\PicoFeedException;
29
26
use PicoFeed\Reader\Reader;
30
27
use Respect\Validation\Validator as v;
31
 
use Stash\Invalidation;
32
28
use Xibo\Controller\Library;
33
29
use Xibo\Entity\DataSetColumn;
34
30
use Xibo\Exception\NotFoundException;
35
 
use Xibo\Exception\XiboException;
36
 
use Xibo\Service\LogService;
37
 
 
 
31
use Xibo\Factory\DataSetColumnFactory;
 
32
use Xibo\Factory\DataSetFactory;
 
33
use Xibo\Factory\MediaFactory;
 
34
use Xibo\Helper\Cache;
 
35
use Xibo\Helper\Config;
 
36
use Xibo\Helper\Date;
 
37
use Xibo\Helper\Log;
 
38
use Xibo\Helper\Sanitize;
 
39
use Xibo\Helper\Theme;
38
40
 
39
41
class Ticker extends ModuleWidget
40
42
{
43
45
     */
44
46
    public function installFiles()
45
47
    {
46
 
        $this->mediaFactory->createModuleSystemFile(PROJECT_ROOT . '/modules/vendor/jquery-1.11.1.min.js')->save();
47
 
        $this->mediaFactory->createModuleSystemFile(PROJECT_ROOT . '/modules/vendor/moment.js')->save();
48
 
        $this->mediaFactory->createModuleSystemFile(PROJECT_ROOT . '/modules/vendor/jquery.marquee.min.js')->save();
49
 
        $this->mediaFactory->createModuleSystemFile(PROJECT_ROOT . '/modules/vendor/jquery-cycle-2.1.6.min.js')->save();
50
 
        $this->mediaFactory->createModuleSystemFile(PROJECT_ROOT . '/modules/xibo-layout-scaler.js')->save();
51
 
        $this->mediaFactory->createModuleSystemFile(PROJECT_ROOT . '/modules/xibo-text-render.js')->save();
52
 
        $this->mediaFactory->createModuleSystemFile(PROJECT_ROOT . '/modules/xibo-image-render.js')->save();
53
 
    }
54
 
 
55
 
    /**
56
 
     * @return string
57
 
     */
58
 
    public function layoutDesignerJavaScript()
59
 
    {
60
 
        // We use the same javascript as the data set view designer
61
 
        return 'datasetview-designer-javascript';
 
48
        MediaFactory::createModuleFile('modules/vendor/jquery-1.11.1.min.js')->save();
 
49
        MediaFactory::createModuleFile('modules/vendor/moment.js')->save();
 
50
        MediaFactory::createModuleFile('modules/vendor/jquery.marquee.min.js')->save();
 
51
        MediaFactory::createModuleFile('modules/vendor/jquery-cycle-2.1.6.min.js')->save();
 
52
        MediaFactory::createModuleFile('modules/xibo-layout-scaler.js')->save();
 
53
        MediaFactory::createModuleFile('modules/xibo-text-render.js')->save();
62
54
    }
63
55
 
64
56
    /**
67
59
     */
68
60
    public function dataSets()
69
61
    {
70
 
        return $this->dataSetFactory->query();
 
62
        return DataSetFactory::query();
71
63
    }
72
64
 
73
65
    /**
79
71
        if ($this->getOption('dataSetId') == 0)
80
72
            throw new \InvalidArgumentException(__('DataSet not selected'));
81
73
 
82
 
       return $this->dataSetColumnFactory->getByDataSetId($this->getOption('dataSetId'));
83
 
    }
84
 
 
85
 
    /**
86
 
     * Get the Order Clause
87
 
     * @return mixed
88
 
     */
89
 
    public function getOrderClause()
90
 
    {
91
 
        return json_decode($this->getOption('orderClauses', "[]"), true);
92
 
    }
93
 
 
94
 
    /**
95
 
     * Get the Filter Clause
96
 
     * @return mixed
97
 
     */
98
 
    public function getFilterClause()
99
 
    {
100
 
        return json_decode($this->getOption('filterClauses', "[]"), true);
101
 
    }
102
 
 
103
 
    /**
104
 
     * Get Extra content for the form
 
74
       return DataSetColumnFactory::getByDataSetId($this->getOption('dataSetId'));
 
75
    }
 
76
 
 
77
    /**
 
78
     * Loads templates for this module
 
79
     */
 
80
    private function loadTemplates()
 
81
    {
 
82
        // Scan the folder for template files
 
83
        foreach (glob(PROJECT_ROOT . '/modules/ticker/*.template.json') as $template) {
 
84
            // Read the contents, json_decode and add to the array
 
85
            $this->module->settings['templates'][] = json_decode(file_get_contents($template), true);
 
86
        }
 
87
 
 
88
        Log::debug(count($this->module->settings['templates']));
 
89
    }
 
90
 
 
91
    /**
 
92
     * Templates available
105
93
     * @return array
106
94
     */
107
 
    public function getExtra()
 
95
    public function templatesAvailable()
108
96
    {
109
 
        if ($this->getOption('sourceId') == 2) {
110
 
            return [
111
 
                'templates' => $this->templatesAvailable(),
112
 
                'orderClause' => $this->getOrderClause(),
113
 
                'filterClause' => $this->getFilterClause(),
114
 
                'columns' => $this->dataSetColumns(),
115
 
                'dataSet' => ($this->getOption('dataSetId', 0) != 0) ? $this->dataSetFactory->getById($this->getOption('dataSetId')) : null
116
 
            ];
117
 
        } else {
118
 
            return [
119
 
                'templates' => $this->templatesAvailable(),
120
 
            ];
121
 
        }
 
97
        if (!isset($this->module->settings['templates']))
 
98
            $this->loadTemplates();
 
99
 
 
100
        return $this->module->settings['templates'];
122
101
    }
123
102
 
124
103
    public function validate()
125
104
    {
126
105
        // Must have a duration
127
 
        if ($this->getUseDuration() == 1 && $this->getDuration() == 0)
 
106
        if ($this->getDuration() == 0)
128
107
            throw new \InvalidArgumentException(__('Please enter a duration'));
129
108
 
130
109
        $sourceId = $this->getOption('sourceId');
142
121
                throw new \InvalidArgumentException(__('Please select a DataSet'));
143
122
 
144
123
            // Check we have permission to use this DataSetId
145
 
            if (!$this->getUser()->checkViewable($this->dataSetFactory->getById($this->getOption('dataSetId'))))
 
124
            if (!$this->getUser()->checkViewable(DataSetFactory::getById($this->getOption('dataSetId'))))
146
125
                throw new \InvalidArgumentException(__('You do not have permission to use that dataset'));
147
126
 
148
127
            if ($this->widget->widgetId != 0) {
172
151
            if (!v::numeric()->validate($this->getOption('numItems', 0)))
173
152
                throw new \InvalidArgumentException(__('The value in Number of Items must be numeric.'));
174
153
 
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'));
177
156
        }
178
157
    }
179
158
 
180
159
    /**
181
 
     * Adds a Ticker Widget
182
 
     * @SWG\Post(
183
 
     *  path="/playlist/widget/ticker/{playlistId}",
184
 
     *  operationId="WidgetTickerAdd",
185
 
     *  tags={"widget"},
186
 
     *  summary="Add a ticker Widget",
187
 
     *  description="Add a new ticker Widget to the specified playlist",
188
 
     *  @SWG\Parameter(
189
 
     *      name="playlistId",
190
 
     *      in="path",
191
 
     *      description="The playlist ID to add a Widget to",
192
 
     *      type="integer",
193
 
     *      required=true
194
 
     *   ),
195
 
     *  @SWG\Parameter(
196
 
     *      name="name",
197
 
     *      in="formData",
198
 
     *      description="Optional Widget Name",
199
 
     *      type="string",
200
 
     *      required=false
201
 
     *  ),
202
 
     *  @SWG\Parameter(
203
 
     *      name="duration",
204
 
     *      in="formData",
205
 
     *      description="The Widget Duration",
206
 
     *      type="integer",
207
 
     *      required=false
208
 
     *  ),
209
 
     *  @SWG\Parameter(
210
 
     *      name="useDuration",
211
 
     *      in="formData",
212
 
     *      description="(0, 1) Select 1 only if you will provide duration parameter as well",
213
 
     *      type="integer",
214
 
     *      required=false
215
 
     *  ),
216
 
     *  @SWG\Parameter(
217
 
     *      name="sourceId",
218
 
     *      in="formData",
219
 
     *      description="Add only - 1 for rss feed, 2 for dataset",
220
 
     *      type="integer",
221
 
     *      required=true
222
 
     *  ),
223
 
     *  @SWG\Parameter(
224
 
     *      name="uri",
225
 
     *      in="formData",
226
 
     *      description="For sourceId=1, the link for the rss feed",
227
 
     *      type="string",
228
 
     *      required=true
229
 
     *  ),
230
 
     *  @SWG\Parameter(
231
 
     *      name="dataSetId",
232
 
     *      in="formData",
233
 
     *      description="For sourceId=2, Create ticker Widget using provided dataSetId of an existing dataSet",
234
 
     *      type="integer",
235
 
     *      required=true
236
 
     *  ),
237
 
     *  @SWG\Parameter(
238
 
     *      name="updateInterval",
239
 
     *      in="formData",
240
 
     *      description="EDIT Only - Update interval in minutes",
241
 
     *      type="integer",
242
 
     *      required=false
243
 
     *   ),
244
 
     *  @SWG\Parameter(
245
 
     *      name="effect",
246
 
     *      in="formData",
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",
248
 
     *      type="string",
249
 
     *      required=false
250
 
     *   ),
251
 
     *  @SWG\Parameter(
252
 
     *      name="speed",
253
 
     *      in="formData",
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)",
255
 
     *      type="integer",
256
 
     *      required=false
257
 
     *   ),
258
 
     *  @SWG\Parameter(
259
 
     *      name="copyright",
260
 
     *      in="formData",
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",
262
 
     *      type="string",
263
 
     *      required=false
264
 
     *   ),
265
 
     *  @SWG\Parameter(
266
 
     *      name="numItems",
267
 
     *      in="formData",
268
 
     *      description="EDIT Only and SourceId=1 - The number of RSS items you want to display",
269
 
     *      type="integer",
270
 
     *      required=false
271
 
     *   ),
272
 
     *  @SWG\Parameter(
273
 
     *      name="takeItemsFrom",
274
 
     *      in="formData",
275
 
     *      description="EDIT Only and SourceId=1 - Take the items form the beginning or the end of the list, available options: start, end",
276
 
     *      type="string",
277
 
     *      required=false
278
 
     *   ),
279
 
     *  @SWG\Parameter(
280
 
     *      name="durationIsPerItem",
281
 
     *      in="formData",
282
 
     *      description="A flag (0, 1), The duration specified is per item, otherwise it is per feed",
283
 
     *      type="integer",
284
 
     *      required=false
285
 
     *   ),
286
 
     *  @SWG\Parameter(
287
 
     *      name="itemsSideBySide",
288
 
     *      in="formData",
289
 
     *      description="A flag (0, 1), Should items be shown side by side",
290
 
     *      type="integer",
291
 
     *      required=false
292
 
     *   ),
293
 
     *  @SWG\Parameter(
294
 
     *      name="upperLimit",
295
 
     *      in="formData",
296
 
     *      description="EDIT Only, SourceId=2 - Upper low limit for this dataSet, 0 for nor limit",
297
 
     *      type="integer",
298
 
     *      required=false
299
 
     *   ),
300
 
     *  @SWG\Parameter(
301
 
     *      name="lowerLimit",
302
 
     *      in="formData",
303
 
     *      description="EDIT Only, SourceId=2 - Lower low limit for this dataSet, 0 for nor limit",
304
 
     *      type="integer",
305
 
     *      required=false
306
 
     *   ),
307
 
     *  @SWG\Parameter(
308
 
     *      name="itemsPerPage",
309
 
     *      in="formData",
310
 
     *      description="EDIT Only - When in single mode, how many items per page should be shown",
311
 
     *      type="integer",
312
 
     *      required=false
313
 
     *   ),
314
 
     *  @SWG\Parameter(
315
 
     *      name="dateFormat",
316
 
     *      in="formData",
317
 
     *      description="EDIT Only - The date format to apply to all dates returned by the ticker",
318
 
     *      type="string",
319
 
     *      required=false
320
 
     *   ),
321
 
     *  @SWG\Parameter(
322
 
     *      name="allowedAttributes",
323
 
     *      in="formData",
324
 
     *      description="EDIT Only and SourceId=1 - A comma separated list of attributes that should not be stripped from the feed",
325
 
     *      type="string",
326
 
     *      required=false
327
 
     *   ),
328
 
     *  @SWG\Parameter(
329
 
     *      name="stripTags",
330
 
     *      in="formData",
331
 
     *      description="EDIT Only and SourceId=1 - A comma separated list of attributes that should be stripped from the feed",
332
 
     *      type="string",
333
 
     *      required=false
334
 
     *   ),
335
 
     *  @SWG\Parameter(
336
 
     *      name="backgroundColor",
337
 
     *      in="formData",
338
 
     *      description="Edit only - A HEX color to use as the background color of this widget",
339
 
     *      type="string",
340
 
     *      required=false
341
 
     *   ),
342
 
     *  @SWG\Parameter(
343
 
     *      name="disableDateSort",
344
 
     *      in="formData",
345
 
     *      description="EDIT Only, SourceId=1 - Should the date sort applied to the feed be disabled?",
346
 
     *      type="integer",
347
 
     *      required=false
348
 
     *   ),
349
 
     *  @SWG\Parameter(
350
 
     *      name="textDirection",
351
 
     *      in="formData",
352
 
     *      description="EDIT Only, SourceId=1 - Which direction does the text in the feed use? Available options: ltr, rtl",
353
 
     *      type="string",
354
 
     *      required=false
355
 
     *   ),
356
 
     *  @SWG\Parameter(
357
 
     *      name="noDataMessage",
358
 
     *      in="formData",
359
 
     *      description="EDIT Only - A message to display when no data is returned from the source",
360
 
     *      type="string",
361
 
     *      required=false
362
 
     *   ),
363
 
     *  @SWG\Parameter(
364
 
     *      name="templateId",
365
 
     *      in="formData",
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",
367
 
     *      type="string",
368
 
     *      required=false
369
 
     *   ),
370
 
     *  @SWG\Parameter(
371
 
     *      name="overrideTemplate",
372
 
     *      in="formData",
373
 
     *      description="EDIT Only, SourceId=1 - flag (0, 1) override template checkbox",
374
 
     *      type="integer",
375
 
     *      required=false
376
 
     *   ),
377
 
     *  @SWG\Parameter(
378
 
     *      name="template",
379
 
     *      in="formData",
380
 
     *      description="Template for each item, replaces [itemsTemplate] in main template, Pass only with overrideTemplate set to 1 or with sourceId=2 ",
381
 
     *      type="string",
382
 
     *      required=false
383
 
     *   ),
384
 
     *  @SWG\Parameter(
385
 
     *      name="css",
386
 
     *      in="formData",
387
 
     *      description="Optional StyleSheet Pass only with overrideTemplate set to 1 or with sourceId=2 ",
388
 
     *      type="string",
389
 
     *      required=false
390
 
     *   ),
391
 
     *  @SWG\Parameter(
392
 
     *      name="javaScript",
393
 
     *      in="formData",
394
 
     *      description="Optional JavaScript, Pass only with overrideTemplate set to 1 ",
395
 
     *      type="string",
396
 
     *      required=false
397
 
     *   ),
398
 
     *  @SWG\Parameter(
399
 
     *      name="filter",
400
 
     *      in="formData",
401
 
     *      description="EDIT Only, SourceId=2 - SQL clause for filter this dataSet",
402
 
     *      type="string",
403
 
     *      required=false
404
 
     *   ),
405
 
     *  @SWG\Parameter(
406
 
     *      name="ordering",
407
 
     *      in="formData",
408
 
     *      description="EDIT Only, SourceId=2- SQL clause for how this dataSet should be ordered",
409
 
     *      type="string",
410
 
     *      required=false
411
 
     *   ),
412
 
     *  @SWG\Parameter(
413
 
     *      name="useOrderingClause",
414
 
     *      in="formData",
415
 
     *      description="EDIT Only, SourceId=2 - flag (0,1) Use advanced order clause - set to 1 if ordering is provided",
416
 
     *      type="integer",
417
 
     *      required=false
418
 
     *   ),
419
 
     *  @SWG\Parameter(
420
 
     *      name="useFilteringClause",
421
 
     *      in="formData",
422
 
     *      description="EDIT Only, SourceId=2 - flag (0,1) Use advanced filter clause - set to 1 if filter is provided",
423
 
     *      type="integer",
424
 
     *      required=false
425
 
     *   ),
426
 
     *  @SWG\Parameter(
427
 
     *      name="randomiseItems",
428
 
     *      in="formData",
429
 
     *      description="A flag (0, 1), whether to randomise the feed",
430
 
     *      type="integer",
431
 
     *      required=false
432
 
     *   ),
433
 
     *  @SWG\Response(
434
 
     *      response=201,
435
 
     *      description="successful operation",
436
 
     *      @SWG\Schema(ref="#/definitions/Widget"),
437
 
     *      @SWG\Header(
438
 
     *          header="Location",
439
 
     *          description="Location of the new widget",
440
 
     *          type="string"
441
 
     *      )
442
 
     *  )
443
 
     * )
 
160
     * Add Media
444
161
     */
445
162
    public function add()
446
163
    {
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);
455
171
 
456
 
        if ($this->getOption('sourceId') == 2)
457
 
            $this->setOption('dataSetId', $this->getSanitizer()->getInt('dataSetId', 0));
458
 
 
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);
472
185
    {
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));
491
 
 
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', ''));
503
 
 
504
 
        // DataSet
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'));
511
 
 
512
 
            // Order and Filter criteria
513
 
            $orderClauses = $this->getSanitizer()->getStringArray('orderClause');
514
 
            $orderClauseDirections = $this->getSanitizer()->getStringArray('orderClauseDirection');
515
 
            $orderClauseMapping = [];
516
 
 
517
 
            $i = -1;
518
 
            foreach ($orderClauses as $orderClause) {
519
 
                $i++;
520
 
 
521
 
                if ($orderClause == '')
522
 
                    continue;
523
 
 
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] : '',
528
 
                ];
529
 
            }
530
 
 
531
 
            $this->setOption('orderClauses', json_encode($orderClauseMapping));
532
 
 
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 = [];
538
 
 
539
 
            $i = -1;
540
 
            foreach ($filterClauses as $filterClause) {
541
 
                $i++;
542
 
 
543
 
                if ($filterClause == '')
544
 
                    continue;
545
 
 
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] : '',
552
 
                ];
553
 
            }
554
 
 
555
 
            $this->setOption('filterClauses', json_encode($filterClauseMapping));
556
 
 
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)));
560
 
 
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)));
565
 
        }
566
 
        
 
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'));
 
213
 
 
214
        // Text Template
 
215
        $this->setRawNode('template', Sanitize::getParam('ta_text', null));
 
216
        $this->setRawNode('css', Sanitize::getParam('ta_css', null));
 
217
 
567
218
        // Save the widget
568
219
        $this->validate();
569
220
        $this->saveWidget();
570
221
    }
571
222
 
572
 
    /**
573
 
     * @inheritdoc
574
 
     */
575
223
    public function hoverPreview()
576
224
    {
577
225
        $name = $this->getOption('name');
579
227
        $sourceId = $this->getOption('sourceId', 1);
580
228
 
581
229
        // Default Hover window contains a thumbnail, media type and duration
582
 
        $output = '<div class="thumbnail"><img alt="' . $this->module->name . ' thumbnail" src="' . $this->getConfig()->uri('img/forms/' . $this->getModuleType() . '.gif') . '"></div>';
 
230
        $output = '<div class="thumbnail"><img alt="' . $this->module->name . ' thumbnail" src="' . Theme::uri('img/forms/' . $this->getModuleType() . '.gif') . '"></div>';
583
231
        $output .= '<div class="info">';
584
232
        $output .= '    <ul>';
585
233
        $output .= '    <li>' . __('Type') . ': ' . $this->module->name . '</li>';
586
234
        $output .= '    <li>' . __('Name') . ': ' . $name . '</li>';
587
235
 
588
 
        if ($sourceId == 2) {
589
 
            // Get the DataSet name
590
 
            try {
591
 
                $dataSet = $this->dataSetFactory->getById($this->getOption('dataSetId'));
592
 
 
593
 
                $output .= '    <li>' . __('Source: DataSet named "%s".', $dataSet->dataSet) . '</li>';
594
 
            } catch (NotFoundException $notFoundException) {
595
 
                $this->getLog()->error('Layout Widget without a DataSet. widgetId: ' . $this->getWidgetId());
596
 
                $output .= '    <li>' . __('Warning: No DataSet found.') . '</li>';
597
 
            }
598
 
        }
 
236
        if ($sourceId == 2)
 
237
            $output .= '    <li>' . __('Source') . ': DataSet</li>';
599
238
        else
600
239
            $output .= '    <li>' . __('Source') . ': <a href="' . $url . '" target="_blank" title="' . __('Source') . '">' . $url . '</a></li>';
601
240
 
611
250
     * Get Resource
612
251
     * @param int $displayId
613
252
     * @return mixed
614
 
     * @throws XiboException
615
253
     */
616
254
    public function getResource($displayId = 0)
617
255
    {
618
256
        // Load in the template
619
257
        $data = [];
620
 
        $isPreview = ($this->getSanitizer()->getCheckbox('preview') == 1);
 
258
        $isPreview = (Sanitize::getCheckbox('preview') == 1);
 
259
 
 
260
        // Clear all linked media.
 
261
        $this->clearMedia();
621
262
 
622
263
        // Replace the View Port Width?
623
264
        $data['viewPortWidth'] = ($isPreview) ? $this->region->width : '[[ViewPortWidth]]';
627
268
 
628
269
        // Information from the Module
629
270
        $itemsSideBySide = $this->getOption('itemsSideBySide', 0);
630
 
        $duration = $this->getCalculatedDurationForGetResource();
631
 
        $durationIsPerItem = $this->getOption('durationIsPerItem', 1);
 
271
        $duration = $this->getDuration();
 
272
        $durationIsPerItem = $this->getOption('durationIsPerItem', 0);
632
273
        $numItems = $this->getOption('numItems', 0);
633
274
        $takeItemsFrom = $this->getOption('takeItemsFrom', 'start');
634
275
        $itemsPerPage = $this->getOption('itemsPerPage', 0);
635
276
 
636
 
        // Text/CSS subsitution variables.
637
 
        $text = null;
638
 
        $css = null;
639
 
 
640
 
        // Get CSS and HTML template from the original template or from the input field
641
 
        if ($sourceId != 2 && $this->getOption('overrideTemplate') == 0) {
642
 
            // Feed tickers without override set.
643
 
            $template = $this->getTemplateById($this->getOption('templateId', 'title-only'));
644
 
            
645
 
            if (isset($template)) {
646
 
                $text = $template['template'];
647
 
                $css = $template['css'];
648
 
            } else {
649
 
                $text = $this->getRawNode('template', '');
650
 
                $css = $this->getRawNode('css', '');
651
 
            }
652
 
        } else {
653
 
            // DataSet tickers or feed tickers without overrides.
654
 
            $text = $this->getRawNode('template', '');
655
 
            $css = $this->getRawNode('css', '');
656
 
        }
657
 
        
658
 
        // Parse library references on the template
659
 
        $text = $this->parseLibraryReferences($isPreview, $text);
660
 
 
661
 
        // Parse library references on the CSS Node
662
 
        $css = $this->parseLibraryReferences($isPreview, $css);
663
 
 
664
 
        // Get the JavaScript node
665
 
        $javaScript = $this->parseLibraryReferences($isPreview, $this->getRawNode('javaScript', ''));
 
277
        // Get the text out of RAW
 
278
        $text = $this->parseLibraryReferences($isPreview, $this->getRawNode('template', null));
 
279
 
 
280
        // Get the CSS Node
 
281
        $css = $this->parseLibraryReferences($isPreview, $this->getRawNode('css', ''));
666
282
 
667
283
        // Handle older layouts that have a direction node but no effect node
668
284
        $oldDirection = $this->getOption('direction', 'none');
669
285
 
670
286
        if ($oldDirection == 'single')
671
 
            $oldDirection = 'noTransition';
 
287
            $oldDirection = 'fade';
672
288
        else if ($oldDirection != 'none')
673
289
            $oldDirection = 'marquee' . ucfirst($oldDirection);
674
290
 
682
298
            'numItems' => $numItems,
683
299
            'takeItemsFrom' => $takeItemsFrom,
684
300
            'itemsPerPage' => $itemsPerPage,
685
 
            'randomiseItems' => $this->getOption('randomiseItems', 0),
686
 
            'speed' => $this->getOption('speed', 1000),
 
301
            'speed' => $this->getOption('speed'),
687
302
            'originalWidth' => $this->region->width,
688
303
            'originalHeight' => $this->region->height,
689
 
            'previewWidth' => $this->getSanitizer()->getDouble('width', 0),
690
 
            'previewHeight' => $this->getSanitizer()->getDouble('height', 0),
691
 
            'scaleOverride' => $this->getSanitizer()->getDouble('scale_override', 0)
 
304
            'previewWidth' => Sanitize::getDouble('width', 0),
 
305
            'previewHeight' => Sanitize::getDouble('height', 0),
 
306
            'scaleOverride' => Sanitize::getDouble('scale_override', 0)
692
307
        );
693
308
 
694
309
        // Generate a JSON string of substituted items.
699
314
        }
700
315
 
701
316
        // Return empty string if there are no items to show.
702
 
        if (count($items) == 0) {
703
 
            // Do we have a no-data message to display?
704
 
            $noDataMessage = $this->getRawNode('noDataMessage');
705
 
 
706
 
            if ($noDataMessage != '') {
707
 
                $items[] = $noDataMessage;
708
 
            } else {
709
 
                $this->getLog()->error('Request failed for dataSet id=%d. Widget=%d. Due to No Records Found', $this->getOption('dataSetId'), $this->getWidgetId());
710
 
                return '';
711
 
            }
712
 
        }
 
317
        if (count($items) == 0)
 
318
            return '';
713
319
 
714
320
        // Work out how many pages we will be showing.
715
321
        $pages = $numItems;
 
322
 
716
323
        if ($numItems > count($items) || $numItems == 0)
717
324
            $pages = count($items);
718
325
 
720
327
        $totalDuration = ($durationIsPerItem == 0) ? $duration : ($duration * $pages);
721
328
 
722
329
        // Replace and Control Meta options
723
 
        $data['controlMeta'] = '<!-- NUMITEMS=' . $pages . ' -->' . PHP_EOL . '<!-- DURATION=' . $totalDuration . ' -->';   
 
330
        $data['controlMeta'] = '<!-- NUMITEMS=' . $pages . ' -->' . PHP_EOL . '<!-- DURATION=' . $totalDuration . ' -->';
 
331
 
724
332
        // Replace the head content
725
333
        $headContent = '';
726
 
        
 
334
 
727
335
        if ($itemsSideBySide == 1) {
728
336
            $headContent .= '<style type="text/css">';
729
337
            $headContent .= ' .item, .page { float: left; }';
748
356
        }
749
357
 
750
358
        // Add our fonts.css file
751
 
        $headContent .= '<link href="' . (($isPreview) ? $this->getApp()->urlFor('library.font.css') : 'fonts.css') . '" rel="stylesheet" media="screen">';
752
 
        $headContent .= '<style type="text/css">' . file_get_contents($this->getConfig()->uri('css/client.css', true)) . '</style>';
 
359
        $headContent .= '<link href="' . $this->getResourceUrl('fonts.css') . ' rel="stylesheet" media="screen">';
 
360
        $headContent .= '<style type="text/css">' . file_get_contents(Theme::uri('css/client.css', true)) . '</style>';
753
361
 
754
362
        // Replace the Head Content with our generated javascript
755
363
        $data['head'] = $headContent;
767
375
 
768
376
        $javaScriptContent .= '<script type="text/javascript" src="' . $this->getResourceUrl('xibo-layout-scaler.js') . '"></script>';
769
377
        $javaScriptContent .= '<script type="text/javascript" src="' . $this->getResourceUrl('xibo-text-render.js') . '"></script>';
770
 
        $javaScriptContent .= '<script type="text/javascript" src="' . $this->getResourceUrl('xibo-image-render.js') . '"></script>';
771
378
 
772
379
        $javaScriptContent .= '<script type="text/javascript">';
773
380
        $javaScriptContent .= '   var options = ' . json_encode($options) . ';';
774
381
        $javaScriptContent .= '   var items = ' . json_encode($items) . ';';
775
382
        $javaScriptContent .= '   $(document).ready(function() { ';
776
 
        $javaScriptContent .= '       $("body").xiboLayoutScaler(options); $("#content").xiboTextRender(options, items); $("#content").find("img").xiboImageRender(options); ';
 
383
        $javaScriptContent .= '       $("body").xiboLayoutScaler(options); $("#content").xiboTextRender(options, items);';
777
384
        $javaScriptContent .= '   }); ';
778
 
        $javaScriptContent .= $javaScript;
779
385
        $javaScriptContent .= '</script>';
780
386
 
781
387
        // Replace the Head Content with our generated javascript
783
389
 
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]);
787
 
 
788
 
        $this->concurrentRequestRelease();
 
392
            $this->widget->save(['saveWidgetOptions' => false]);
789
393
 
790
394
        return $this->renderTemplate($data);
791
395
    }
792
396
 
793
 
    /**
794
 
     * @param $isPreview
795
 
     * @param $text
796
 
     * @return array|mixed|null
797
 
     * @throws \Xibo\Exception\ConfigurationException
798
 
     */
799
397
    private function getRssItems($isPreview, $text)
800
398
    {
801
399
        // Make sure we have the cache location configured
802
 
        Library::ensureLibraryExists($this->getConfig()->GetSetting('LIBRARY_LOCATION'));
 
400
        Library::ensureLibraryExists();
803
401
 
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'));
807
406
 
808
 
        // Lock this entire request
809
 
        $this->concurrentRequestLock(md5($feedUrl));
810
 
 
811
 
        /** @var \Stash\Item $cache */
812
 
        $cache = $this->getPool()->getItem($this->makeCacheKey(md5($feedUrl)));
813
 
        $cache->setInvalidationMethod(Invalidation::SLEEP, 5000, 15);
814
 
 
815
 
        $this->getLog()->debug('Ticker with RSS source ' . $feedUrl . '. Cache key: ' . $cache->getKey());
816
 
 
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);
819
408
 
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');
824
 
 
825
 
            // Lock this cache item (120 seconds)
826
 
            $cache->lock(120);
827
 
 
828
 
            try {
829
 
                // Create a Guzzle Client to get the Feed XML
830
 
                $client = new Client();
831
 
                $response = $client->get($feedUrl, $this->getConfig()->getGuzzleProxy([
832
 
                    'headers' => [
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'
834
 
                    ],
835
 
                    'stream' => true,
836
 
                    'timeout' => 20 // wait no more than 20 seconds: https://github.com/xibosignage/xibo/issues/1401
837
 
                ]));
838
 
 
839
 
                // Pull out the content type
840
 
                $contentType = $response->getHeaderLine('Content-Type');
841
 
 
842
 
                $this->getLog()->debug('Feed returned content-type ' . $contentType);
843
 
 
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);
848
 
                    return false;
849
 
                }
850
 
 
851
 
                // Get the body, etc
852
 
                $result = explode('charset=', $contentType);
853
 
                $document['encoding'] = isset($result[1]) ? $result[1] : '';
854
 
                $document['xml'] = $response->getBody()->getContents();
855
 
 
856
 
                // Add this to the cache.
857
 
                $cache->set($document);
858
 
                $cache->expiresAfter($this->getOption('updateInterval', 360) * 60);
859
 
 
860
 
                // Save
861
 
                $this->getPool()->saveDeferred($cache);
862
 
 
863
 
                $this->getLog()->debug('Processed feed and added to the cache for ' . $this->getOption('updateInterval', 360) . ' minutes');
864
 
 
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());
869
 
 
870
 
                return false;
871
 
            }
 
410
        if (Cache::has($key)) {
 
411
            // Our local cache is valid
 
412
            return Cache::get($key);
872
413
        }
873
414
 
874
 
        //$this->getLog()->debug(var_export($document, true));
 
415
        // Our local cache is not valid
 
416
        // Store our formatted items
 
417
        $items = [];
875
418
 
876
 
        // Cache HIT or we've requested
877
 
        // Load the feed XML document into a feed parser
878
419
        try {
879
 
            // Enable logging if we need to
880
 
            if (LogService::resolveLogLevel($this->getConfig()->GetSetting('audit', 'error')) == \Slim\Log::DEBUG) {
881
 
                Logger::enable();
882
 
            }
 
420
            $clientConfig = Config::getPicoFeedProxy($feedUrl);
883
421
 
884
422
            // Allowable attributes
885
 
            $clientConfig = new Config();
886
 
 
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')));
891
427
            }
892
428
 
 
429
            // Enable logging if we need to
 
430
            if (\Xibo\Helper\Log::resolveLogLevel(Config::GetSetting('audit', 'error')) == \Slim\Log::DEBUG) {
 
431
                Logger::enable();
 
432
            }
 
433
 
 
434
            $reader = new Reader($clientConfig);
 
435
            $resource = $reader->download($feedUrl);
 
436
 
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());
896
439
 
897
440
            // Get a feed object
898
441
            $feed = $parser->execute();
899
442
 
 
443
            // Parse the text template
 
444
            $matches = '';
 
445
            preg_match_all('/\[.*?\]/', $text, $matches);
 
446
 
900
447
            // Get all items
901
448
            $feedItems = $feed->getItems();
902
449
 
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());
907
 
            return false;
908
 
        }
909
 
 
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));
913
 
        }
914
 
 
915
 
        // Parse the text template
916
 
        $matches = '';
917
 
        preg_match_all('/\[.*?\]/', $text, $matches);
918
 
 
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) {
923
 
                /* @var Item $a */
924
 
                /* @var Item $b */
925
 
 
926
 
                return ($a->getDate() < $b->getDate());
927
 
            });
928
 
        }
929
 
 
930
 
        // Date format for the feed items
931
 
        $dateFormat = $this->getOption('dateFormat', $this->getConfig()->GetSetting('DATE_FORMAT'));
932
 
 
933
 
        // Set an expiry time for the media
934
 
        $expires = $this->getDate()->parse()->addMinutes($this->getOption('updateInterval', 3600))->format('U');
935
 
 
936
 
        // Render the content now
937
 
        foreach ($feedItems as $item) {
938
 
            /* @var Item $item */
939
 
 
940
 
            // Substitute for all matches in the template
941
 
            $rowString = $text;
942
 
 
943
 
            // Run through all [] substitutes in $matches
944
 
            foreach ($matches[0] as $sub) {
945
 
                $replace = '';
946
 
 
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
950
 
                    $attribute = NULL;
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);
954
 
                    else
955
 
                        list($tag, $namespace) = explode('|', $sub);
956
 
 
957
 
                    // Replace some things so that we know what we are looking at
958
 
                    $tag = str_replace('[', '', $tag);
959
 
                    $namespace = str_replace(']', '', $namespace);
960
 
 
961
 
                    if ($attribute !== null)
962
 
                        $attribute = str_replace(']', '', $attribute);
963
 
 
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));
967
 
 
968
 
                    // Are we an image place holder? [tag|image]
969
 
                    if ($namespace == 'image') {
970
 
                        // Try to get a link for the image
971
 
                        $link = null;
972
 
 
973
 
                        switch ($tag) {
974
 
                            case 'Link':
975
 
                                if (stripos($item->getEnclosureType(), 'image') > -1) {
976
 
                                    // Use the link to get the image
977
 
                                    $link = $item->getEnclosureUrl();
978
 
 
979
 
                                    if (empty($link)) {
980
 
                                        $this->getLog()->debug('No image found for Link|image tag using getEnclosureUrl');
981
 
                                    }
982
 
                                } else {
983
 
                                    $this->getLog()->debug('No image found for Link|image tag using getEnclosureType');
984
 
                                }
985
 
                                break;
986
 
 
987
 
                            default:
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);
992
 
                                else
993
 
                                    $tags = $item->getTag($tag);
994
 
 
995
 
                                if (count($tags) > 0 && !empty($tags[0]))
996
 
                                    $link = $tags[0];
997
 
                                else
998
 
                                    $this->getLog()->debug('Tag not found for [' . $tag . '] attribute [' . $attribute . ']');
999
 
                        }
1000
 
 
1001
 
                        $this->getLog()->debug('Resolved link: ' . $link);
1002
 
 
1003
 
                        // If we have managed to resolve a link, download it and replace the tag with the downloaded
1004
 
                        // image url
1005
 
                        if ($link != NULL) {
1006
 
                            // Grab the profile image
1007
 
                            $file = $this->mediaFactory->queueDownload('ticker_' . md5($this->getOption('url') . $link), $link, $expires);
1008
 
 
1009
 
                            $replace = '<img src="' . $this->getFileUrl($file, 'image') . '" ' . $attribute . ' />';
1010
 
                        }
1011
 
                    } else {
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) {
 
454
                    /* @var Item $a */
 
455
                    /* @var Item $b */
 
456
 
 
457
                    return ($a->getDate() < $b->getDate());
 
458
                });
 
459
            }
 
460
 
 
461
            // Date format for the feed items
 
462
            $dateFormat = $this->getOption('dateFormat', Config::GetSetting('DATE_FORMAT'));
 
463
 
 
464
            // Set an expiry time for the media
 
465
            $expires = time() + ($this->getOption('updateInterval', 3600) * 60);
 
466
 
 
467
            // Render the content now
 
468
            foreach ($feedItems as $item) {
 
469
                /* @var Item $item */
 
470
 
 
471
                // Substitute for all matches in the template
 
472
                $rowString = $text;
 
473
 
 
474
                // Run through all [] substitutes in $matches
 
475
                foreach ($matches[0] as $sub) {
 
476
                    $replace = '';
 
477
 
 
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
 
481
                        $attribute = NULL;
 
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);
1015
485
                        else
1016
 
                            $tags = $item->getTag($tag);
1017
 
 
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);
 
487
 
 
488
                        // Replace some things so that we know what we are looking at
 
489
                        $tag = str_replace('[', '', $tag);
 
490
                        $namespace = str_replace(']', '', $namespace);
 
491
 
 
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));
 
495
 
 
496
                        // Are we an image place holder? [tag|image]
 
497
                        if ($namespace == 'image') {
 
498
                            // Try to get a link for the image
 
499
                            $link = null;
 
500
 
 
501
                            switch ($tag) {
 
502
                                case 'Link':
 
503
                                    if ($item->getEnclosureType() == 'image') {
 
504
                                        // Use the link to get the image
 
505
                                        $link = $item->getEnclosureUrl();
 
506
                                    }
 
507
                                    break;
 
508
 
 
509
                                default:
 
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) {
 
513
                                        // Use a namespace
 
514
                                        if (array_key_exists($attribute, $item->namespaces)) {
 
515
                                            $tags = $item->getTag($tag);
 
516
                                            $link = $tags[0];
 
517
                                        } else {
 
518
                                            Log::info('Looking for image with namespace %s, but that namespace does not exist.', $attribute);
 
519
                                        }
 
520
                                    } else {
 
521
                                        $tags = $item->getTag($tag);
 
522
                                        $link = $tags[0];
 
523
                                    }
 
524
                            }
 
525
 
 
526
                            // If we have managed to resolve a link, download it and replace the tag with the downloaded
 
527
                            // image url
 
528
                            if ($link != NULL) {
 
529
                                // Grab the profile image
 
530
                                $file = MediaFactory::createModuleFile('ticker_' . md5($this->getOption('url') . $link), $link);
 
531
                                $file->isRemote = true;
 
532
                                $file->expires = $expires;
 
533
                                $file->save();
 
534
 
 
535
                                // Tag this layout with this file
 
536
                                $this->assignMedia($file->mediaId);
 
537
 
 
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 . ' />';
 
540
                            }
1021
541
                        } else {
1022
 
                            $this->getLog()->debug('Tag not found for ' . $tag . ' attribute ' . $attribute);
1023
 
                        }
1024
 
                    }
1025
 
                } else {
1026
 
                    // Use the pool of standard tags
1027
 
                    switch ($sub) {
1028
 
                        case '[Name]':
1029
 
                            $replace = $this->getOption('name');
1030
 
                            break;
1031
 
 
1032
 
                        case '[Title]':
1033
 
                            $replace = $item->getTitle();
1034
 
                            break;
1035
 
 
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());
1041
 
                            } else {
1042
 
                                // use description
1043
 
                                $replace = $desc[0];
1044
 
                            }
1045
 
                            break;
1046
 
 
1047
 
                        case '[Content]':
1048
 
                            $replace = $item->getContent();
1049
 
                            break;
1050
 
 
1051
 
                        case '[Copyright]':
1052
 
                            $replace = $item->getAuthor();
1053
 
                            break;
1054
 
 
1055
 
                        case '[Date]':
1056
 
                            $replace = $this->getDate()->getLocalDate($item->getDate()->format('U'), $dateFormat);
1057
 
                            break;
1058
 
 
1059
 
                        case '[PermaLink]':
1060
 
                            $replace = $item->getTag('permalink');
1061
 
                            break;
1062
 
 
1063
 
                        case '[Link]':
1064
 
                            $replace = $item->getUrl();
1065
 
                            break;
1066
 
 
1067
 
                        case '[Image]':
1068
 
                            if (stripos($item->getEnclosureType(), 'image') > -1) {
1069
 
                                // Use the link to get the image
1070
 
                                $link = $item->getEnclosureUrl();
1071
 
 
1072
 
                                if (!(empty($link))) {
1073
 
                                    // Grab the image
1074
 
                                    $file = $this->mediaFactory->queueDownload('ticker_' . md5($this->getOption('url') . $link), $link, $expires);
1075
 
 
1076
 
                                    $replace = '<img src="' . $this->getFileUrl($file, 'image') . '"/>';
1077
 
                                } else {
1078
 
                                    $this->getLog()->debug('No image found for image tag using getEnclosureUrl');
1079
 
                                }
1080
 
                            } else {
1081
 
                                $this->getLog()->debug('No image found for image tag using getEnclosureType');
1082
 
                            }
1083
 
                            break;
1084
 
                    }
1085
 
                }
1086
 
 
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))
1091
 
                        mkdir($cachePath);
1092
 
 
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);
1098
 
                }
1099
 
 
1100
 
                // Substitute the replacement we have found (it might be '')
1101
 
                $rowString = str_replace($sub, $replace, $rowString);
1102
 
            }
1103
 
 
1104
 
            $items[] = $rowString;
1105
 
        }
1106
 
 
1107
 
        // Process queued downloads
1108
 
        $this->mediaFactory->processDownloads(function($media) {
1109
 
            // Success
1110
 
            $this->getLog()->debug((($media->isSaveRequired) ? 'Successfully downloaded ' : 'Download not required for ') . $media->mediaId);
1111
 
 
1112
 
            // Tag this layout with this file
1113
 
            $this->assignMedia($media->mediaId);
1114
 
        });
1115
 
 
1116
 
        // Copyright information?
1117
 
        if ($this->getOption('copyright', '') != '') {
1118
 
            $items[] = '<span id="copyright">' . $this->getOption('copyright') . '</span>';
1119
 
        }
1120
 
 
 
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);
 
545
                            else
 
546
                                $tags = $item->getTag($tag);
 
547
 
 
548
                            Log::debug('Tags:' . var_export($tags, true));
 
549
 
 
550
                            // If we find some tags then do the business with them
 
551
                            if ($tags != NULL) {
 
552
                                $replace = $tags[0];
 
553
                            }
 
554
                        }
 
555
                    } else {
 
556
 
 
557
                        // Use the pool of standard tags
 
558
                        switch ($sub) {
 
559
                            case '[Name]':
 
560
                                $replace = $this->getOption('name');
 
561
                                break;
 
562
 
 
563
                            case '[Title]':
 
564
                                $replace = $item->getTitle();
 
565
                                break;
 
566
 
 
567
                            case '[Description]':
 
568
                                $replace = $item->getContent();
 
569
                                break;
 
570
 
 
571
                            case '[Content]':
 
572
                                $replace = $item->getContent();
 
573
                                break;
 
574
 
 
575
                            case '[Copyright]':
 
576
                                $replace = $item->getAuthor();
 
577
                                break;
 
578
 
 
579
                            case '[Date]':
 
580
                                $replace = Date::getLocalDate($item->getDate()->format('U'), $dateFormat);
 
581
                                break;
 
582
 
 
583
                            case '[PermaLink]':
 
584
                                $replace = $item->getTag('permalink');
 
585
                                break;
 
586
 
 
587
                            case '[Link]':
 
588
                                $replace = $item->getUrl();
 
589
                                break;
 
590
                        }
 
591
                    }
 
592
 
 
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);
 
598
                    }
 
599
 
 
600
                    // Substitute the replacement we have found (it might be '')
 
601
                    $rowString = str_replace($sub, $replace, $rowString);
 
602
                }
 
603
 
 
604
                $items[] = $rowString;
 
605
            }
 
606
 
 
607
            // Copyright information?
 
608
            if ($this->getOption('copyright', '') != '') {
 
609
                $items[] = '<span id="copyright">' . $this->getOption('copyright') . '</span>';
 
610
            }
 
611
 
 
612
            // Add this to the cache.
 
613
            Cache::put($key, $items, $this->getOption('updateInterval', 360) * 60);
 
614
        }
 
615
        catch (PicoFeedException $e) {
 
616
            Log::error('Unable to get feed: %s', $e->getMessage());
 
617
            Log::debug($e->getTraceAsString());
 
618
        }
 
619
 
 
620
        if (\Xibo\Helper\Log::resolveLogLevel(Config::GetSetting('audit', 'error')) == \Slim\Log::DEBUG) {
 
621
            Log::debug(json_encode(Logger::getMessages()));
 
622
        }
 
623
 
 
624
        // Return the formatted items
1121
625
        return $items;
1122
626
    }
1123
627
 
1124
 
    /**
1125
 
     * @param $displayId
1126
 
     * @param $isPreview
1127
 
     * @param $text
1128
 
     * @return array
1129
 
     */
1130
628
    private function getDataSetItems($displayId, $isPreview, $text)
1131
629
    {
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');
1136
 
 
1137
 
        // Lock this request
1138
 
        $this->concurrentRequestLock($dataSetId);
1139
 
 
1140
 
        // Ordering
1141
 
        $ordering = '';
1142
 
 
1143
 
        if ($this->getOption('useOrderingClause', 1) == 1) {
1144
 
            $ordering = $this->GetOption('ordering');
1145
 
        } else {
1146
 
            // Build an order string
1147
 
            foreach (json_decode($this->getOption('orderClauses', '[]'), true) as $clause) {
1148
 
                $ordering .= $clause['orderClause'] . ' ' . $clause['orderClauseDirection'] . ',';
1149
 
            }
1150
 
 
1151
 
            $ordering = rtrim($ordering, ',');
1152
 
        }
1153
 
 
1154
 
        // Filtering
1155
 
        $filter = '';
1156
 
 
1157
 
        if ($this->getOption('useFilteringClause', 1) == 1) {
1158
 
            $filter = $this->GetOption('filter');
1159
 
        } else {
1160
 
            // Build
1161
 
            $i = 0;
1162
 
            foreach (json_decode($this->getOption('filterClauses', '[]'), true) as $clause) {
1163
 
                $i++;
1164
 
                $criteria = '';
1165
 
 
1166
 
                switch ($clause['filterClauseCriteria']) {
1167
 
 
1168
 
                    case 'starts-with':
1169
 
                        $criteria = 'LIKE \'' . $clause['filterClauseValue'] . '%\'';
1170
 
                        break;
1171
 
 
1172
 
                    case 'ends-with':
1173
 
                        $criteria = 'LIKE \'%' . $clause['filterClauseValue'] . '\'';
1174
 
                        break;
1175
 
 
1176
 
                    case 'contains':
1177
 
                        $criteria = 'LIKE \'%' . $clause['filterClauseValue'] . '%\'';
1178
 
                        break;
1179
 
 
1180
 
                    case 'equals':
1181
 
                        $criteria = '= \'' . $clause['filterClauseValue'] . '\'';
1182
 
                        break;
1183
 
 
1184
 
                    case 'not-contains':
1185
 
                        $criteria = 'NOT LIKE \'%' . $clause['filterClauseValue'] . '%\'';
1186
 
                        break;
1187
 
 
1188
 
                    case 'not-starts-with':
1189
 
                        $criteria = 'NOT LIKE \'' . $clause['filterClauseValue'] . '%\'';
1190
 
                        break;
1191
 
 
1192
 
                    case 'not-ends-with':
1193
 
                        $criteria = 'NOT LIKE \'%' . $clause['filterClauseValue'] . '\'';
1194
 
                        break;
1195
 
 
1196
 
                    case 'not-equals':
1197
 
                        $criteria = '<> \'' . $clause['filterClauseValue'] . '\'';
1198
 
                        break;
1199
 
 
1200
 
                    case 'greater-than':
1201
 
                        $criteria = '> \'' . $clause['filterClauseValue'] . '\'';
1202
 
                        break;
1203
 
 
1204
 
                    case 'less-than':
1205
 
                        $criteria = '< \'' . $clause['filterClauseValue'] . '\'';
1206
 
                        break;
1207
 
 
1208
 
                    default:
1209
 
                        continue;
1210
 
                }
1211
 
 
1212
 
                if ($i > 1)
1213
 
                    $filter .= ' ' . $clause['filterClauseOperator'] . ' ';
1214
 
 
1215
 
                $filter .= $clause['filterClause'] . ' ' . $criteria;
1216
 
            }
1217
 
        }
1218
 
 
1219
 
        $this->getLog()->notice('Then template for each row is: ' . $text);
 
634
        $filter = $this->getOption('filter');
 
635
        $ordering = $this->getOption('ordering');
 
636
 
 
637
        Log::notice('Then template for each row is: ' . $text);
1220
638
 
1221
639
        // Set an expiry time for the media
1222
640
        $expires = time() + ($this->getOption('updateInterval', 3600) * 60);
1229
647
 
1230
648
        foreach ($matches[1] as $match) {
1231
649
            // Get the column id's we are interested in
1232
 
            $this->getLog()->notice('Matched column: ' . $match);
 
650
            Log::notice('Matched column: ' . $match);
1233
651
 
1234
652
            $col = explode('|', $match);
1235
653
            $columnIds[] = $col[1];
1237
655
 
1238
656
        // Create a data set object, to get the results.
1239
657
        try {
1240
 
            $dataSet = $this->dataSetFactory->getById($dataSetId);
 
658
            $dataSet = DataSetFactory::getById($dataSetId);
1241
659
 
1242
660
            // Get an array representing the id->heading mappings
1243
661
            $mappings = [];
1253
671
                ];
1254
672
            }
1255
673
 
1256
 
            $this->getLog()->debug('Resolved column mappings: %s', json_encode($columnIds));
 
674
            Log::debug('Resolved column mappings: %s', json_encode($columnIds));
1257
675
 
1258
676
            $filter = [
1259
677
                'filter' => $filter,
1269
687
                $filter['size'] = $upperLimit - $lowerLimit;
1270
688
            }
1271
689
 
1272
 
            // Set the timezone for SQL
1273
 
            $dateNow = $this->getDate()->parse();
1274
 
            if ($displayId != 0) {
1275
 
                $display = $this->displayFactory->getById($displayId);
1276
 
                $timeZone = $display->getSetting('displayTimeZone', '');
1277
 
                $timeZone = ($timeZone == '') ? $this->getConfig()->GetSetting('defaultTimezone') : $timeZone;
1278
 
                $dateNow->timezone($timeZone);
1279
 
                $this->getLog()->debug('Display Timezone Resolved: %s. Time: %s.', $timeZone, $dateNow->toDateTimeString());
1280
 
            }
1281
 
 
1282
 
            $this->getStore()->setTimeZone($this->getDate()->getLocalDate($dateNow, 'P'));
1283
 
 
1284
690
            // Get the data (complete table, filtered)
1285
691
            $dataSetResults = $dataSet->getData($filter);
1286
692
 
1301
707
                    $header = $subs[0];
1302
708
                    $replace = $row[$header];
1303
709
 
1304
 
                    // If the value is empty, then move on
1305
 
                    if ($replace != '') {
1306
 
                        // Check in the columns array to see if this is a special one
1307
 
                        if ($mappings[$header]['dataTypeId'] == 4) {
1308
 
                            // External Image
1309
 
                            // Download the image, alter the replace to wrap in an image tag
1310
 
                            $file = $this->mediaFactory->queueDownload('ticker_dataset_' . md5($dataSetId . $mappings[$header]['dataSetColumnId'] . $replace), str_replace(' ', '%20', htmlspecialchars_decode($replace)), $expires);
1311
 
 
1312
 
                            $replace = '<img src="' . $this->getFileUrl($file, 'image') . '"/>';
1313
 
 
1314
 
                        } else if ($mappings[$header]['dataTypeId'] == 5) {
1315
 
                            // Library Image
1316
 
                            // The content is the ID of the image
1317
 
                            try {
1318
 
                                if ($replace !== 0) {
1319
 
                                    $file = $this->mediaFactory->getById($replace);
1320
 
 
1321
 
                                    // Tag this layout with this file
1322
 
                                    $this->assignMedia($file->mediaId);
1323
 
 
1324
 
                                    $replace = '<img src="' . $this->getFileUrl($file, 'image') . '" />';
1325
 
                                } else {
1326
 
                                    $replace = '';
1327
 
                                }
1328
 
                            }
1329
 
                            catch (NotFoundException $e) {
1330
 
                                $this->getLog()->error('Library Image [%s] not found in DataSetId %d.', $replace, $dataSetId);
1331
 
                                $replace = '';
1332
 
                            }
1333
 
                        }
 
710
                    // Check in the columns array to see if this is a special one
 
711
                    if ($mappings[$header]['dataTypeId'] == 4) {
 
712
                        // External Image
 
713
                        // Download the image, alter the replace to wrap in an image tag
 
714
                        $file = MediaFactory::createModuleFile('ticker_dataset_' . md5($dataSetId . $mappings[$header]['dataSetColumnId'] . $replace), str_replace(' ', '%20', htmlspecialchars_decode($replace)));
 
715
                        $file->isRemote = true;
 
716
                        $file->expires = $expires;
 
717
                        $file->save();
 
718
 
 
719
                        // Tag this layout with this file
 
720
                        $this->assignMedia($file->mediaId);
 
721
 
 
722
                        $url = $this->getApp()->urlFor('library.download', ['id' => $file->mediaId, 'type' => 'image']);
 
723
                        $replace = ($isPreview) ? '<img src="' . $url . '?preview=1&width=' . $this->region->width . '&height=' . $this->region->height . '" />' : '<img src="' . $file->storedAs . '" />';
 
724
 
 
725
                    } else if ($mappings[$header]['dataTypeId'] == 5) {
 
726
                        // Library Image
 
727
                        // The content is the ID of the image
 
728
                        $file = MediaFactory::getById($replace);
 
729
 
 
730
                        // Tag this layout with this file
 
731
                        $this->assignMedia($file->mediaId);
 
732
 
 
733
                        $url = $this->getApp()->urlFor('library.download', ['id' => $file->mediaId, 'type' => 'image']);
 
734
                        $replace = ($isPreview) ? '<img src="' . $url . '?preview=1&width=' . $this->region->width . '&height=' . $this->region->height . '" />' : '<img src="' . $file->storedAs . '" />';
1334
735
                    }
1335
736
 
1336
737
                    $rowString = str_replace('[' . $sub . ']', $replace, $rowString);
1339
740
                $items[] = $rowString;
1340
741
            }
1341
742
 
1342
 
            // Process queued downloads
1343
 
            $this->mediaFactory->processDownloads(function($media) {
1344
 
                // Success
1345
 
                $this->getLog()->debug('Successfully downloaded ' . $media->mediaId);
1346
 
 
1347
 
                // Tag this layout with this file
1348
 
                $this->assignMedia($media->mediaId);
1349
 
            });
1350
 
 
1351
743
            return $items;
1352
744
        }
1353
745
        catch (NotFoundException $e) {
1354
 
            $this->getLog()->debug('getDataSetItems failed for id=%d. Widget=%d. Due to %s - this might be OK if we have a no-data message', $dataSetId, $this->getWidgetId(), $e->getMessage());
1355
 
            $this->getLog()->debug($e->getTraceAsString());
 
746
            Log::error('Request failed for dataSet id=%d. Widget=%d. Due to %s', $dataSetId, $this->getWidgetId(), $e->getMessage());
1356
747
            return [];
1357
748
        }
1358
749
    }
1362
753
        // Can't be sure because the client does the rendering
1363
754
        return 1;
1364
755
    }
1365
 
 
1366
 
    /** @inheritdoc */
1367
 
    public function getModifiedTimestamp($displayId)
1368
 
    {
1369
 
        $widgetModifiedDt = null;
1370
 
 
1371
 
        if ($this->getOption('sourceId', 1) == 2) {
1372
 
 
1373
 
            $dataSetId = $this->getOption('dataSetId');
1374
 
            $dataSet = $this->dataSetFactory->getById($dataSetId);
1375
 
 
1376
 
            // Set the timestamp
1377
 
            $widgetModifiedDt = $dataSet->lastDataEdit;
1378
 
 
1379
 
            // Remote dataSets are kept "active" by required files
1380
 
            if ($dataSet->isRemote) {
1381
 
                // Touch this dataSet
1382
 
                $dataSetCache = $this->getPool()->getItem('/dataset/accessed/' . $dataSet->dataSetId);
1383
 
                $dataSetCache->set('true');
1384
 
                $dataSetCache->expiresAfter($this->getSetting('REQUIRED_FILES_LOOKAHEAD') * 1.5);
1385
 
                $this->getPool()->saveDeferred($dataSetCache);
1386
 
            }
1387
 
        }
1388
 
 
1389
 
        return $widgetModifiedDt;
1390
 
    }
1391
756
}