~xibo-maintainers/xibo/tempel

« back to all changes in this revision

Viewing changes to modules/ticker.module.php

  • Committer: Dan Garner
  • Date: 2015-06-11 15:10:51 UTC
  • mto: This revision was merged to the branch mainline in revision 429.
  • Revision ID: git-v1:c4db8c301380f37bc63cb37fc0894c4bba0e1249
Namespaced Modules

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
<?php
2
 
/*
3
 
 * Xibo - Digital Signage - http://www.xibo.org.uk
4
 
 * Copyright (C) 2006-2015 Daniel Garner
5
 
 *
6
 
 * This file is part of Xibo.
7
 
 *
8
 
 * Xibo is free software: you can redistribute it and/or modify
9
 
 * it under the terms of the GNU Affero General Public License as published by
10
 
 * the Free Software Foundation, either version 3 of the License, or
11
 
 * any later version. 
12
 
 *
13
 
 * Xibo is distributed in the hope that it will be useful,
14
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 
 * GNU Affero General Public License for more details.
17
 
 *
18
 
 * You should have received a copy of the GNU Affero General Public License
19
 
 * along with Xibo.  If not, see <http://www.gnu.org/licenses/>.
20
 
 */
21
 
use Widget\Module;
22
 
use Xibo\Controller\File;
23
 
use Xibo\Helper\Date;
24
 
use Xibo\Helper\Form;
25
 
use Xibo\Helper\Log;
26
 
use Xibo\Helper\Theme;
27
 
 
28
 
class ticker extends Module
29
 
{
30
 
    /**
31
 
     * Install Files
32
 
     */
33
 
    public function InstallFiles()
34
 
    {
35
 
        $media = new Media();
36
 
        $media->addModuleFile('modules/preview/vendor/jquery-1.11.1.min.js');
37
 
        $media->addModuleFile('modules/preview/vendor/moment.js');
38
 
        $media->addModuleFile('modules/preview/vendor/jquery.marquee.min.js');
39
 
        $media->addModuleFile('modules/preview/vendor/jquery-cycle-2.1.6.min.js');
40
 
        $media->addModuleFile('modules/preview/xibo-layout-scaler.js');
41
 
        $media->addModuleFile('modules/preview/xibo-text-render.js');
42
 
    }
43
 
 
44
 
    /** 
45
 
     * Loads templates for this module
46
 
     */
47
 
    public function loadTemplates()
48
 
    {
49
 
        // Scan the folder for template files
50
 
        foreach (glob('modules/theme/ticker/*.template.json') as $template) {
51
 
            // Read the contents, json_decode and add to the array
52
 
            $this->module->settings['templates'][] = json_decode(file_get_contents($template), true);
53
 
        }
54
 
 
55
 
        Log::debug(count($this->module->settings['templates']));
56
 
    }
57
 
    
58
 
    /**
59
 
     * Return the Add Form
60
 
     */
61
 
    public function AddForm()
62
 
    {
63
 
        $response = $this->getState();
64
 
 
65
 
        // Configure form
66
 
        $this->configureForm('AddMedia');
67
 
 
68
 
        // Augment settings with templates
69
 
        $this->loadTemplates();
70
 
 
71
 
        $formFields = array();
72
 
        $formFields[] = Form::AddCombo(
73
 
                    'sourceid', 
74
 
                    __('Source Type'), 
75
 
                    NULL,
76
 
                    array(array('sourceid' => '1', 'source' => __('Feed')), array('sourceid' => '2', 'source' => __('DataSet'))),
77
 
                    'sourceid',
78
 
                    'source',
79
 
                    __('The source for this Ticker'), 
80
 
                    's');
81
 
 
82
 
        $formFields[] = Form::AddText('uri', __('Feed URL'), NULL,
83
 
            __('The Link for the RSS feed'), 'f', '', 'feed-fields');
84
 
 
85
 
        $datasets = $this->getUser()->DataSetList();
86
 
        array_unshift($datasets, array('datasetid' => '0', 'dataset' => 'None'));
87
 
        Theme::Set('dataset_field_list', $datasets);
88
 
 
89
 
        $formFields[] = Form::AddCombo(
90
 
                    'datasetid', 
91
 
                    __('DataSet'), 
92
 
                    NULL,
93
 
                    $datasets,
94
 
                    'datasetid',
95
 
                    'dataset',
96
 
                    __('Please select the DataSet to use as a source of data for this ticker.'), 
97
 
                    'd', 'dataset-fields');
98
 
 
99
 
        $formFields[] = Form::AddNumber('duration', __('Duration'), NULL,
100
 
            __('The duration in seconds this should be displayed'), 'd', 'required');
101
 
 
102
 
        Theme::Set('form_fields', $formFields);
103
 
 
104
 
        // Field dependencies
105
 
        $sourceFieldDepencies_1 = array(
106
 
                '.feed-fields' => array('display' => 'block'),
107
 
                '.dataset-fields' => array('display' => 'none'),
108
 
            );
109
 
 
110
 
        $sourceFieldDepencies_2 = array(
111
 
                '.feed-fields' => array('display' => 'none'),
112
 
                '.dataset-fields' => array('display' => 'block'),
113
 
            );
114
 
 
115
 
        $response->AddFieldAction('sourceid', 'init', 1, $sourceFieldDepencies_1);
116
 
        $response->AddFieldAction('sourceid', 'change', 1, $sourceFieldDepencies_1);
117
 
        $response->AddFieldAction('sourceid', 'init', 2, $sourceFieldDepencies_2);
118
 
        $response->AddFieldAction('sourceid', 'change', 2, $sourceFieldDepencies_2);
119
 
                
120
 
        // Return
121
 
        $response->html = Theme::RenderReturn('form_render');
122
 
        $this->configureFormButtons($response);
123
 
        $response->dialogTitle = __('Add New Ticker');
124
 
 
125
 
        return $response;
126
 
    }
127
 
    
128
 
    /**
129
 
     * Return the Edit Form as HTML
130
 
     */
131
 
    public function EditForm()
132
 
    {
133
 
        $response = $this->getState();
134
 
 
135
 
        // Edit calls are the same as add calls, except you will to check the user has permissions to do the edit
136
 
        if (!$this->auth->edit)
137
 
            throw new Exception(__('You do not have permission to edit this widget.'));
138
 
 
139
 
        // Configure the form
140
 
        $this->configureForm('EditMedia');
141
 
 
142
 
        // Augment settings with templates
143
 
        $this->loadTemplates();
144
 
 
145
 
        $formFields = array();
146
 
 
147
 
        // What is the source for this ticker?
148
 
        $sourceId = $this->GetOption('sourceId');
149
 
        $dataSetId = $this->GetOption('datasetid');
150
 
 
151
 
        $tabs = array();
152
 
        $tabs[] = Form::AddTab('general', __('General'));
153
 
        $tabs[] = Form::AddTab('template', __('Appearance'), array(array('name' => 'enlarge', 'value' => true)));
154
 
        $tabs[] = Form::AddTab('format', __('Format'));
155
 
        $tabs[] = Form::AddTab('advanced', __('Advanced'));
156
 
        Theme::Set('form_tabs', $tabs);
157
 
 
158
 
        $field_name = Form::AddText('name', __('Name'), $this->GetOption('name'),
159
 
            __('An optional name for this media'), 'n');
160
 
 
161
 
        $field_duration = Form::AddNumber('duration', __('Duration'), $this->getDuration(),
162
 
            __('The duration in seconds this item should be displayed'), 'd', 'required', '', ($this->auth->modifyPermissions));
163
 
 
164
 
        // Common fields
165
 
        $oldDirection = $this->GetOption('direction');
166
 
        
167
 
        if ($oldDirection == 'single')
168
 
            $oldDirection = 'fade';
169
 
        else if ($oldDirection != 'none')
170
 
            $oldDirection = 'marquee' . ucfirst($oldDirection);
171
 
 
172
 
        $fieldFx = Form::AddCombo(
173
 
                'effect', 
174
 
                __('Effect'), 
175
 
                $this->GetOption('effect', $oldDirection),
176
 
                array(
177
 
                    array('effectid' => 'none', 'effect' => __('None')), 
178
 
                    array('effectid' => 'fade', 'effect' => __('Fade')),
179
 
                    array('effectid' => 'fadeout', 'effect' => __('Fade Out')),
180
 
                    array('effectid' => 'scrollHorz', 'effect' => __('Scroll Horizontal')),
181
 
                    array('effectid' => 'scrollVert', 'effect' => __('Scroll Vertical')),
182
 
                    array('effectid' => 'flipHorz', 'effect' => __('Flip Horizontal')),
183
 
                    array('effectid' => 'flipVert', 'effect' => __('Flip Vertical')),
184
 
                    array('effectid' => 'shuffle', 'effect' => __('Shuffle')),
185
 
                    array('effectid' => 'tileSlide', 'effect' => __('Tile Slide')),
186
 
                    array('effectid' => 'tileBlind', 'effect' => __('Tile Blinds')),
187
 
                    array('effectid' => 'marqueeLeft', 'effect' => __('Marquee Left')),
188
 
                    array('effectid' => 'marqueeRight', 'effect' => __('Marquee Right')),
189
 
                    array('effectid' => 'marqueeUp', 'effect' => __('Marquee Up')),
190
 
                    array('effectid' => 'marqueeDown', 'effect' => __('Marquee Down')),
191
 
                ),
192
 
                'effectid',
193
 
                'effect',
194
 
                __('Please select the effect that will be used to transition between items. If all items should be output, select None. Marquee effects are CPU intensive and may not be suitable for lower power displays.'), 
195
 
                'e');
196
 
 
197
 
        $fieldScrollSpeed = Form::AddNumber('speed', __('Speed'), $this->GetOption('speed', $this->GetOption('scrollSpeed')),
198
 
            __('The transition speed of the selected effect in milliseconds (normal = 1000) or the Marquee Speed in a low to high scale (normal = 1).'), 's', NULL, 'effect-controls');
199
 
 
200
 
        $fieldBackgroundColor = Form::AddText('backgroundColor', __('Background Colour'), $this->GetOption('backgroundColor'),
201
 
            __('The selected effect works best with a background colour. Optionally add one here.'), 'c', NULL, 'background-color-group');
202
 
 
203
 
        $field_itemsPerPage = Form::AddNumber('itemsPerPage', __('Items per page'), $this->GetOption('itemsPerPage'),
204
 
            __('When in single mode how many items per page should be shown.'), 'p');
205
 
 
206
 
        $field_updateInterval = Form::AddNumber('updateInterval', __('Update Interval (mins)'), $this->GetOption('updateInterval', 5),
207
 
            __('Please enter the update interval in minutes. This should be kept as high as possible. For example, if the data will only change once per hour this could be set to 60.'),
208
 
            'n', 'required');
209
 
 
210
 
        $field_durationIsPerItem = Form::AddCheckbox('durationIsPerItem', __('Duration is per item'),
211
 
            $this->GetOption('durationIsPerItem'), __('The duration specified is per item otherwise it is per feed.'), 
212
 
            'i');
213
 
 
214
 
        $field_itemsSideBySide = Form::AddCheckbox('itemsSideBySide', __('Show items side by side?'),
215
 
            $this->GetOption('itemsSideBySide'), __('Should items be shown side by side?'), 
216
 
            's');
217
 
 
218
 
        // Data Set Source
219
 
        if ($sourceId == 2) {
220
 
 
221
 
            $formFields['general'][] = $field_name;
222
 
            $formFields['general'][] = $field_duration;
223
 
            $formFields['general'][] = $fieldFx;
224
 
            $formFields['general'][] = $fieldScrollSpeed;
225
 
            $formFields['advanced'][] = $fieldBackgroundColor;
226
 
            $formFields['advanced'][] = $field_durationIsPerItem;
227
 
            $formFields['advanced'][] = $field_updateInterval;
228
 
 
229
 
            // Extra Fields for the DataSet
230
 
            $formFields['general'][] = Form::AddText('ordering', __('Order'), $this->GetOption('ordering'),
231
 
                __('Please enter a SQL clause for how this dataset should be ordered'), 'o');
232
 
 
233
 
            $formFields['general'][] = Form::AddText('filter', __('Filter'), $this->GetOption('filter'),
234
 
                __('Please enter a SQL clause to filter this DataSet.'), 'f');
235
 
 
236
 
            $formFields['advanced'][] = Form::AddNumber('lowerLimit', __('Lower Row Limit'), $this->GetOption('lowerLimit'),
237
 
                __('Please enter the Lower Row Limit for this DataSet (enter 0 for no limit)'), 'l');
238
 
 
239
 
            $formFields['advanced'][] = Form::AddNumber('upperLimit', __('Upper Row Limit'), $this->GetOption('upperLimit'),
240
 
                __('Please enter the Upper Row Limit for this DataSet (enter 0 for no limit)'), 'u');
241
 
 
242
 
            $formFields['format'][] = $field_itemsPerPage;
243
 
            $formFields['format'][] = $field_itemsSideBySide;
244
 
 
245
 
            Theme::Set('columns', \Xibo\Storage\PDOConnect::select(sprintf("SELECT DataSetColumnID, Heading FROM datasetcolumn WHERE DataSetID = %d ", $dataSetId), array()));
246
 
 
247
 
            $formFields['template'][] = Form::AddRaw(Theme::RenderReturn('media_form_ticker_dataset_edit'));
248
 
        }
249
 
        else {
250
 
            // Extra Fields for the Ticker
251
 
            $formFields['general'][] = Form::AddText('uri', __('Feed URL'), urldecode($this->GetOption('uri')),
252
 
                __('The Link for the RSS feed'), 'f');
253
 
 
254
 
            $formFields['general'][] = $field_name;
255
 
            $formFields['general'][] = $field_duration;
256
 
            $formFields['general'][] = $fieldFx;
257
 
            $formFields['format'][] = $fieldScrollSpeed;
258
 
 
259
 
            // Add a field for RTL tickers
260
 
            $formFields['format'][] = Form::AddCombo(
261
 
                    'textDirection', 
262
 
                    __('Text direction'), 
263
 
                    $this->GetOption('textDirection'),
264
 
                    array(
265
 
                        array('textdirectionid' => 'ltr', 'textdirection' => __('Left to Right (LTR)')),
266
 
                        array('textdirectionid' => 'rtl', 'textdirection' => __('Right to Left (RTL)'))
267
 
                    ),
268
 
                    'textdirectionid',
269
 
                    'textdirection',
270
 
                    __('Which direction does the text in the feed use? (left to right or right to left)'), 
271
 
                    'd');
272
 
 
273
 
            $formFields['advanced'][] = $fieldBackgroundColor;
274
 
            
275
 
            $formFields['format'][] = Form::AddNumber('numItems', __('Number of Items'), $this->GetOption('numItems'),
276
 
                __('The Number of RSS items you want to display'), 'o');
277
 
 
278
 
            $formFields['format'][] = $field_itemsPerPage;
279
 
 
280
 
            $formFields['advanced'][] = Form::AddText('copyright', __('Copyright'), $this->GetOption('copyright'),
281
 
                __('Copyright information to display as the last item in this feed. This can be styled with the #copyright CSS selector.'), 'f');
282
 
 
283
 
            $formFields['advanced'][] = $field_updateInterval;
284
 
 
285
 
            $formFields['format'][] = Form::AddCombo(
286
 
                    'takeItemsFrom', 
287
 
                    __('Take items from the '), 
288
 
                    $this->GetOption('takeItemsFrom'),
289
 
                    array(
290
 
                        array('takeitemsfromid' => 'start', 'takeitemsfrom' => __('Start of the Feed')),
291
 
                        array('takeitemsfromid' => 'end', 'takeitemsfrom' => __('End of the Feed'))
292
 
                    ),
293
 
                    'takeitemsfromid',
294
 
                    'takeitemsfrom',
295
 
                    __('Take the items from the beginning or the end of the list'), 
296
 
                    't');
297
 
 
298
 
            $formFields['format'][] = $field_durationIsPerItem;
299
 
            $formFields['advanced'][] = $field_itemsSideBySide;
300
 
 
301
 
            $formFields['advanced'][] = Form::AddText('dateFormat', __('Date Format'), $this->GetOption('dateFormat'),
302
 
                __('The format to apply to all dates returned by the ticker. In PHP date format: http://uk3.php.net/manual/en/function.date.php'), 'f');
303
 
 
304
 
            $subs = array(
305
 
                    array('Substitute' => 'Name'),
306
 
                    array('Substitute' => 'Title'),
307
 
                    array('Substitute' => 'Description'),
308
 
                    array('Substitute' => 'Date'),
309
 
                    array('Substitute' => 'Content'),
310
 
                    array('Substitute' => 'Copyright'),
311
 
                    array('Substitute' => 'Link'),
312
 
                    array('Substitute' => 'PermaLink'),
313
 
                    array('Substitute' => 'Tag|Namespace')
314
 
                );
315
 
            Theme::Set('substitutions', $subs);
316
 
 
317
 
            $formFieldSubs = Form::AddRaw(Theme::RenderReturn('media_form_ticker_edit'));
318
 
 
319
 
            $formFields['advanced'][] = Form::AddText('allowedAttributes', __('Allowable Attributes'), $this->GetOption('allowedAttributes'),
320
 
                __('A comma separated list of attributes that should not be stripped from the incoming feed.'), '');
321
 
 
322
 
            $formFields['advanced'][] = Form::AddText('stripTags', __('Strip Tags'), $this->GetOption('stripTags'),
323
 
                __('A comma separated list of HTML tags that should be stripped from the feed in addition to the default ones.'), '');
324
 
 
325
 
            $formFields['advanced'][] = Form::AddCheckbox('disableDateSort', __('Disable Date Sort'), $this->GetOption('disableDateSort'),
326
 
                __('Should the date sort applied to the feed be disabled?'), '');
327
 
 
328
 
            // Encode up the template
329
 
            //$formFields['advanced'][] = Form::AddMessage('<pre>' . htmlentities(json_encode(array('id' => 'media-rss-with-title', 'value' => 'Image overlaid with the Title', 'template' => '<div class="image">[Link|image]<div class="cycle-overlay"><p style="font-family: Arial, Verdana, sans-serif; font-size:48px;">[Title]</p></div></div>', 'css' => '.image img { width:100%;}.cycle-overlay {color: white;background: black;opacity: .6;filter: alpha(opacity=60);position: absolute;bottom: 0;width: 100%;padding: 15px;text-align:center;}'))) . '</pre>');
330
 
        }
331
 
 
332
 
        // Get the CSS node
333
 
        $formFields['template'][] = Form::AddMultiText('ta_css', NULL, $this->getRawNode('css', null),
334
 
            __('Optional Style sheet'), 's', 10, NULL, 'template-override-controls');
335
 
 
336
 
        // Get the Text Node out of this
337
 
        $formFields['template'][] = Form::AddMultiText('ta_text', NULL, $this->getRawNode('template', null),
338
 
            __('Enter the template. Please note that the background colour has automatically coloured to your layout background colour.'), 't', 10, NULL, 'template-override-controls');
339
 
 
340
 
        // RSS
341
 
        if ($this->GetOption('sourceId') == 1) {
342
 
 
343
 
            // Append the templates to the response
344
 
            $response->extra = $this->module->settings['templates'];
345
 
            
346
 
            $formFields['template'][] = $formFieldSubs;
347
 
 
348
 
            // Add a field for whether to override the template or not.
349
 
            // Default to 1 so that it will work correctly with old items (that didn't have a template selected at all)
350
 
            $formFields['template'][] = Form::AddCheckbox('overrideTemplate', __('Override the template?'), $this->GetOption('overrideTemplate', 1),
351
 
            __('Tick if you would like to override the template.'), 'o');
352
 
 
353
 
            // Template - for standard stuff
354
 
            $formFields['template'][] = Form::AddCombo('templateId', __('Template'), $this->GetOption('templateId', 'title-only'),
355
 
                $this->module->settings['templates'],
356
 
                'id', 
357
 
                'value', 
358
 
                __('Select the template you would like to apply. This can be overridden using the check box below.'), 't', 'template-selector-control');
359
 
 
360
 
            // Add some field dependencies
361
 
            // When the override template check box is ticked, we want to expose the advanced controls and we want to hide the template selector
362
 
            $response->AddFieldAction('overrideTemplate', 'init', false, 
363
 
                array(
364
 
                    '.template-override-controls' => array('display' => 'none'),
365
 
                    '.template-selector-control' => array('display' => 'block')
366
 
                ), 'is:checked');
367
 
            $response->AddFieldAction('overrideTemplate', 'change', false, 
368
 
                array(
369
 
                    '.template-override-controls' => array('display' => 'none'),
370
 
                    '.template-selector-control' => array('display' => 'block')
371
 
                ), 'is:checked');
372
 
            $response->AddFieldAction('overrideTemplate', 'init', true, 
373
 
                array(
374
 
                    '.template-override-controls' => array('display' => 'block'),
375
 
                    '.template-selector-control' => array('display' => 'none')
376
 
                ), 'is:checked');
377
 
            $response->AddFieldAction('overrideTemplate', 'change', true, 
378
 
                array(
379
 
                    '.template-override-controls' => array('display' => 'block'),
380
 
                    '.template-selector-control' => array('display' => 'none')
381
 
                ), 'is:checked');
382
 
        }
383
 
 
384
 
        Theme::Set('form_fields_general', $formFields['general']);
385
 
        Theme::Set('form_fields_template', array_reverse($formFields['template']));
386
 
        Theme::Set('form_fields_format', $formFields['format']);
387
 
        Theme::Set('form_fields_advanced', $formFields['advanced']);
388
 
 
389
 
        // Generate the Response
390
 
        $response->html = Theme::RenderReturn('form_render');
391
 
        $response->callBack   = 'text_callback';
392
 
        $this->configureFormButtons($response);
393
 
        $response->dialogTitle = __('Edit Ticker');
394
 
        $this->response->AddButton(__('Apply'), 'XiboDialogApply("#ModuleForm")');
395
 
 
396
 
        return $response;
397
 
    }
398
 
    
399
 
    /**
400
 
     * Add Media to the Database
401
 
     */
402
 
    public function AddMedia()
403
 
    {
404
 
        $response = $this->getState();
405
 
 
406
 
        // Other properties
407
 
        $sourceId = \Xibo\Helper\Sanitize::getInt('sourceid');
408
 
        $uri = \Kit::GetParam('uri', _POST, _URI);
409
 
        $dataSetId = \Kit::GetParam('datasetid', _POST, _INT, 0);
410
 
        $duration = \Kit::GetParam('duration', _POST, _INT, 0, false);
411
 
        
412
 
        // Must have a duration
413
 
        if ($duration == 0)
414
 
            trigger_error(__('Please enter a duration'), E_USER_ERROR);
415
 
 
416
 
        if ($sourceId == 1) {
417
 
            // Feed
418
 
            
419
 
            // Validate the URL
420
 
            if ($uri == "" || $uri == "http://")
421
 
                trigger_error(__('Please enter a Link for this Ticker'), E_USER_ERROR);
422
 
        }
423
 
        else if ($sourceId == 2) {
424
 
            // DataSet
425
 
            
426
 
            // Validate Data Set Selected
427
 
            if ($dataSetId == 0)
428
 
                trigger_error(__('Please select a DataSet'), E_USER_ERROR);
429
 
 
430
 
            // Check we have permission to use this DataSetId
431
 
            if (!$this->getUser()->DataSetAuth($dataSetId))
432
 
                trigger_error(__('You do not have permission to use that dataset'), E_USER_ERROR);
433
 
        }
434
 
        else {
435
 
            // Only supported two source types at the moment
436
 
            trigger_error(__('Unknown Source Type'));
437
 
        }
438
 
        
439
 
        // Any Options
440
 
        $this->setDuration(Kit::GetParam('duration', _POST, _INT, $this->getDuration()));
441
 
        $this->SetOption('xmds', true);
442
 
        $this->SetOption('sourceId', $sourceId);
443
 
        $this->SetOption('uri', $uri);
444
 
        $this->SetOption('datasetid', $dataSetId);
445
 
        $this->SetOption('updateInterval', 120);
446
 
        $this->SetOption('speed', 2);
447
 
 
448
 
        // New tickers have template override set to 0 by add.
449
 
        // the edit form can then default to 1 when the element doesn't exist (for legacy)
450
 
        $this->SetOption('overrideTemplate', 0);
451
 
 
452
 
        $this->setRawNode('template', null);
453
 
        $this->setRawNode('css', null);
454
 
 
455
 
        // Save the widget
456
 
        $this->saveWidget();
457
 
 
458
 
        // Load form
459
 
        $response->loadForm = true;
460
 
        $response->loadFormUri = 'index.php?p=module&q=Exec&mod=' . $this->getModuleType() . '&method=EditForm&regionId=' . $this->region->regionId . '&widgetId=' . $this->getWidgetId();
461
 
        
462
 
        return $response;
463
 
    }
464
 
    
465
 
    /**
466
 
     * Edit Media in the Database
467
 
     */
468
 
    public function EditMedia()
469
 
    {
470
 
        $response = $this->getState();
471
 
 
472
 
        if (!$this->auth->edit)
473
 
            throw new Exception(__('You do not have permission to edit this widget.'));
474
 
 
475
 
        $sourceId = $this->GetOption('sourceId', 1);
476
 
        
477
 
        // Other properties
478
 
        $uri          = \Kit::GetParam('uri', _POST, _URI);
479
 
                $name = \Xibo\Helper\Sanitize::getString('name');
480
 
        $text         = \Kit::GetParam('ta_text', _POST, _HTMLSTRING);
481
 
        $css = \Kit::GetParam('ta_css', _POST, _HTMLSTRING);
482
 
        $updateInterval = \Kit::GetParam('updateInterval', _POST, _INT, 360);
483
 
        $copyright    = \Xibo\Helper\Sanitize::getString('copyright');
484
 
        $numItems = \Xibo\Helper\Sanitize::getString('numItems');
485
 
        $takeItemsFrom = \Xibo\Helper\Sanitize::getString('takeItemsFrom');
486
 
        $durationIsPerItem = \Xibo\Helper\Sanitize::getCheckbox('durationIsPerItem');
487
 
        $itemsSideBySide = \Xibo\Helper\Sanitize::getCheckbox('itemsSideBySide');
488
 
        
489
 
        // DataSet Specific Options
490
 
        $itemsPerPage = \Xibo\Helper\Sanitize::getInt('itemsPerPage');
491
 
        $upperLimit = \Xibo\Helper\Sanitize::getInt('upperLimit');
492
 
        $lowerLimit = \Xibo\Helper\Sanitize::getInt('lowerLimit');
493
 
        $filter = \Kit::GetParam('filter', _POST, _STRINGSPECIAL);
494
 
        $ordering = \Xibo\Helper\Sanitize::getString('ordering');
495
 
        
496
 
        // Validation
497
 
        if ($text == '')
498
 
            throw new InvalidArgumentException(__('Please enter some text'));
499
 
 
500
 
        if ($sourceId == 1) {
501
 
            // Feed
502
 
            
503
 
            // Validate the URL
504
 
            if ($uri == "" || $uri == "http://")
505
 
                trigger_error(__('Please enter a Link for this Ticker'), E_USER_ERROR);
506
 
        }
507
 
        else if ($sourceId == 2) {
508
 
            // Make sure we havent entered a silly value in the filter
509
 
            if (strstr($filter, 'DESC'))
510
 
                trigger_error(__('Cannot user ordering criteria in the Filter Clause'), E_USER_ERROR);
511
 
 
512
 
            if (!is_numeric($upperLimit) || !is_numeric($lowerLimit))
513
 
                trigger_error(__('Limits must be numbers'), E_USER_ERROR);
514
 
 
515
 
            if ($upperLimit < 0 || $lowerLimit < 0)
516
 
                trigger_error(__('Limits cannot be lower than 0'), E_USER_ERROR);
517
 
 
518
 
            // Check the bounds of the limits
519
 
            if ($upperLimit < $lowerLimit)
520
 
                trigger_error(__('Upper limit must be higher than lower limit'), E_USER_ERROR);
521
 
        }
522
 
        
523
 
        if ($numItems != '') {
524
 
            // Make sure we have a number in here
525
 
            if (!is_numeric($numItems))
526
 
                throw new InvalidArgumentException(__('The value in Number of Items must be numeric.'));
527
 
        }
528
 
 
529
 
        if ($updateInterval < 0)
530
 
            trigger_error(__('Update Interval must be greater than or equal to 0'), E_USER_ERROR);
531
 
        
532
 
        // Any Options
533
 
        $this->setDuration(Kit::GetParam('duration', _POST, _INT, $this->getDuration(), false));
534
 
        $this->SetOption('xmds', true);
535
 
                $this->SetOption('name', $name);
536
 
        $this->SetOption('effect', \Kit::GetParam('effect', _POST, _STRING));
537
 
        $this->SetOption('copyright', $copyright);
538
 
        $this->SetOption('speed', \Kit::GetParam('speed', _POST, _INT));
539
 
        $this->SetOption('updateInterval', $updateInterval);
540
 
        $this->SetOption('uri', $uri);
541
 
        $this->SetOption('numItems', $numItems);
542
 
        $this->SetOption('takeItemsFrom', $takeItemsFrom);
543
 
        $this->SetOption('durationIsPerItem', $durationIsPerItem);
544
 
        $this->SetOption('itemsSideBySide', $itemsSideBySide);
545
 
        $this->SetOption('upperLimit', $upperLimit);
546
 
        $this->SetOption('lowerLimit', $lowerLimit);
547
 
        $this->SetOption('filter', $filter);
548
 
        $this->SetOption('ordering', $ordering);
549
 
        $this->SetOption('itemsPerPage', $itemsPerPage);
550
 
        $this->SetOption('dateFormat', \Kit::GetParam('dateFormat', _POST, _STRING));
551
 
        $this->SetOption('allowedAttributes', \Kit::GetParam('allowedAttributes', _POST, _STRING));
552
 
        $this->SetOption('stripTags', \Kit::GetParam('stripTags', _POST, _STRING));
553
 
        $this->SetOption('backgroundColor', \Kit::GetParam('backgroundColor', _POST, _STRING));
554
 
        $this->SetOption('disableDateSort', \Kit::GetParam('disableDateSort', _POST, _CHECKBOX));
555
 
        $this->SetOption('textDirection', \Kit::GetParam('textDirection', _POST, _WORD));
556
 
        $this->SetOption('overrideTemplate', \Kit::GetParam('overrideTemplate', _POST, _CHECKBOX));
557
 
        $this->SetOption('templateId', \Kit::GetParam('templateId', _POST, _WORD));
558
 
        
559
 
        // Text Template
560
 
        $this->setRawNode('template', $text);
561
 
        $this->setRawNode('css', $css);
562
 
 
563
 
        // Save the widget
564
 
        $this->saveWidget();
565
 
 
566
 
        // Load form
567
 
        $response->loadForm = true;
568
 
        $response->loadFormUri = $this->getTimelineLink();
569
 
            $this->response->callBack = 'refreshPreview("' . $this->regionid . '")';
570
 
 
571
 
        return $response;
572
 
    }
573
 
 
574
 
        public function DeleteMedia()
575
 
    {
576
 
                // TODO: Links for datasets
577
 
 
578
 
        //$dataSet = new DataSet($this->db);
579
 
        //$dataSet->UnlinkLayout($this->GetOption('datasetid'), $this->layoutid, $this->regionid, $this->mediaid);
580
 
 
581
 
        parent::DeleteMedia();
582
 
    }
583
 
 
584
 
    public function HoverPreview()
585
 
    {
586
 
        $name = $this->GetOption('name');
587
 
        $url = urldecode($this->GetOption('uri'));
588
 
        $sourceId = $this->GetOption('sourceId', 1);
589
 
 
590
 
        // Default Hover window contains a thumbnail, media type and duration
591
 
        $output = '<div class="thumbnail"><img alt="' . $this->module->name . ' thumbnail" src="theme/default/img/forms/' . $this->getModuleType() . '.gif"></div>';
592
 
        $output .= '<div class="info">';
593
 
        $output .= '    <ul>';
594
 
        $output .= '    <li>' . __('Type') . ': ' . $this->module->name . '</li>';
595
 
        $output .= '    <li>' . __('Name') . ': ' . $name . '</li>';
596
 
 
597
 
        if ($sourceId == 2)
598
 
            $output .= '    <li>' . __('Source') . ': DataSet</li>';
599
 
        else
600
 
            $output .= '    <li>' . __('Source') . ': <a href="' . $url . '" target="_blank" title="' . __('Source') . '">' . $url . '</a></li>';
601
 
 
602
 
 
603
 
        $output .= '    <li>' . __('Duration') . ': ' . $this->getDuration() . ' ' . __('seconds') . '</li>';
604
 
        $output .= '    </ul>';
605
 
        $output .= '</div>';
606
 
 
607
 
        return $output;
608
 
    }
609
 
 
610
 
    /**
611
 
     * Get Resource
612
 
     * @param int $displayId
613
 
     * @return mixed
614
 
     */
615
 
    public function GetResource($displayId = 0)
616
 
    {
617
 
        // Load in the template
618
 
        $template = file_get_contents('modules/preview/HtmlTemplate.html');
619
 
 
620
 
        $isPreview = (\Kit::GetParam('preview', _REQUEST, _WORD, 'false') == 'true');
621
 
 
622
 
        // Replace the View Port Width?
623
 
        if ($isPreview)
624
 
            $template = str_replace('[[ViewPortWidth]]', $this->region->width, $template);
625
 
 
626
 
        // What is the data source for this ticker?
627
 
        $sourceId = $this->GetOption('sourceId', 1);
628
 
 
629
 
        // Information from the Module
630
 
        $itemsSideBySide = $this->GetOption('itemsSideBySide', 0);
631
 
        $duration = $this->getDuration();
632
 
        $durationIsPerItem = $this->GetOption('durationIsPerItem', 0);
633
 
        $numItems = $this->GetOption('numItems', 0);
634
 
        $takeItemsFrom = $this->GetOption('takeItemsFrom', 'start');
635
 
        $itemsPerPage = $this->GetOption('itemsPerPage', 0);
636
 
 
637
 
        // Get the text out of RAW
638
 
        $text = $this->getRawNode('template', null);
639
 
 
640
 
        // Get the CSS Node
641
 
        $css = $this->getRawNode('css', '');
642
 
 
643
 
        // Handle older layouts that have a direction node but no effect node
644
 
        $oldDirection = $this->GetOption('direction', 'none');
645
 
        
646
 
        if ($oldDirection == 'single')
647
 
            $oldDirection = 'fade';
648
 
        else if ($oldDirection != 'none')
649
 
            $oldDirection = 'marquee' . ucfirst($oldDirection);
650
 
 
651
 
        $effect = $this->GetOption('effect', $oldDirection);
652
 
 
653
 
        $options = array(
654
 
            'type' => $this->getModuleType(),
655
 
            'fx' => $effect,
656
 
            'duration' => $duration,
657
 
            'durationIsPerItem' => (($durationIsPerItem == 0) ? false : true),
658
 
            'numItems' => $numItems,
659
 
            'takeItemsFrom' => $takeItemsFrom,
660
 
            'itemsPerPage' => $itemsPerPage,
661
 
            'speed' => $this->GetOption('speed'),
662
 
            'originalWidth' => $this->region->width,
663
 
            'originalHeight' => $this->region->height,
664
 
            'previewWidth' => \Kit::GetParam('width', _GET, _DOUBLE, 0),
665
 
            'previewHeight' => \Kit::GetParam('height', _GET, _DOUBLE, 0),
666
 
            'scaleOverride' => \Kit::GetParam('scale_override', _GET, _DOUBLE, 0)
667
 
        );
668
 
 
669
 
        // Generate a JSON string of substituted items.
670
 
        if ($sourceId == 2) {
671
 
            $items = $this->GetDataSetItems($displayId, $isPreview, $text);
672
 
        }
673
 
        else {
674
 
            $items = $this->GetRssItems($isPreview, $text);
675
 
        }
676
 
 
677
 
        // Return empty string if there are no items to show.
678
 
        if (count($items) == 0)
679
 
            return '';
680
 
 
681
 
        // Work out how many pages we will be showing.
682
 
        $pages = $numItems;
683
 
 
684
 
        if ($numItems > count($items) || $numItems == 0)
685
 
            $pages = count($items);
686
 
 
687
 
        $pages = ($itemsPerPage > 0) ? ceil($pages / $itemsPerPage) : $pages;
688
 
        $totalDuration = ($durationIsPerItem == 0) ? $duration : ($duration * $pages);
689
 
 
690
 
        // Replace and Control Meta options
691
 
        $template = str_replace('<!--[[[CONTROLMETA]]]-->', '<!-- NUMITEMS=' . $pages . ' -->' . PHP_EOL . '<!-- DURATION=' . $totalDuration . ' -->', $template);
692
 
 
693
 
        // Replace the head content
694
 
        $headContent  = '';
695
 
 
696
 
        if ($itemsSideBySide == 1) {
697
 
            $headContent .= '<style type="text/css">';
698
 
            $headContent .= ' .item, .page { float: left; }';
699
 
            $headContent .= '</style>';
700
 
        }
701
 
 
702
 
        if ($this->GetOption('textDirection') == 'rtl') {
703
 
            $headContent .= '<style type="text/css">';
704
 
            $headContent .= ' #content { direction: rtl; }';
705
 
            $headContent .= '</style>';   
706
 
        }
707
 
 
708
 
        if ($this->GetOption('backgroundColor') != '') {
709
 
            $headContent .= '<style type="text/css">';
710
 
            $headContent .= ' body { background-color: ' . $this->GetOption('backgroundColor') . '; }';
711
 
            $headContent .= '</style>';
712
 
        }
713
 
 
714
 
        // Add the CSS if it isn't empty
715
 
        if ($css != '') {
716
 
            $headContent .= '<style type="text/css">' . $css . '</style>';
717
 
        }
718
 
 
719
 
        // Add our fonts.css file
720
 
        $headContent .= '<link href="' . (($isPreview) ? 'modules/preview/' : '') . 'fonts.css" rel="stylesheet" media="screen">';
721
 
        $headContent .= '<style type="text/css">' . file_get_contents(Theme::ItemPath('css/client.css')) . '</style>';
722
 
 
723
 
        // Replace the Head Content with our generated javascript
724
 
        $template = str_replace('<!--[[[HEADCONTENT]]]-->', $headContent, $template);
725
 
 
726
 
        // Add some scripts to the JavaScript Content
727
 
        $javaScriptContent  = '<script type="text/javascript" src="' . (($isPreview) ? 'modules/preview/vendor/' : '') . 'jquery-1.11.1.min.js"></script>';
728
 
 
729
 
        // Need the marquee plugin?
730
 
        if (stripos($effect, 'marquee') !== false)
731
 
            $javaScriptContent .= '<script type="text/javascript" src="' . (($isPreview) ? 'modules/preview/vendor/' : '') . 'jquery.marquee.min.js"></script>';
732
 
        
733
 
        // Need the cycle plugin?
734
 
        if ($effect != 'none')
735
 
            $javaScriptContent .= '<script type="text/javascript" src="' . (($isPreview) ? 'modules/preview/vendor/' : '') . 'jquery-cycle-2.1.6.min.js"></script>';
736
 
        
737
 
        $javaScriptContent .= '<script type="text/javascript" src="' . (($isPreview) ? 'modules/preview/' : '') . 'xibo-layout-scaler.js"></script>';
738
 
        $javaScriptContent .= '<script type="text/javascript" src="' . (($isPreview) ? 'modules/preview/' : '') . 'xibo-text-render.js"></script>';
739
 
 
740
 
        $javaScriptContent .= '<script type="text/javascript">';
741
 
        $javaScriptContent .= '   var options = ' . json_encode($options) . ';';
742
 
        $javaScriptContent .= '   var items = ' . json_encode($items) . ';';
743
 
        $javaScriptContent .= '   $(document).ready(function() { ';
744
 
        $javaScriptContent .= '       $("body").xiboLayoutScaler(options); $("#content").xiboTextRender(options, items);';
745
 
        $javaScriptContent .= '   }); ';
746
 
        $javaScriptContent .= '</script>';
747
 
 
748
 
        // Replace the Head Content with our generated javascript
749
 
        $template = str_replace('<!--[[[JAVASCRIPTCONTENT]]]-->', $javaScriptContent, $template);
750
 
 
751
 
        // Replace the Body Content with our generated text
752
 
        $template = str_replace('<!--[[[BODYCONTENT]]]-->', '', $template);
753
 
 
754
 
        return $template;
755
 
    }
756
 
 
757
 
    private function GetRssItems($isPreview, $text)
758
 
    {
759
 
        // Make sure we have the cache location configured
760
 
        $file = new File();
761
 
        File::EnsureLibraryExists();
762
 
 
763
 
        // Make sure we have a $media/$layout object to use
764
 
        $media = new Media();
765
 
 
766
 
        // Parse the text template
767
 
        $matches = '';
768
 
        preg_match_all('/\[.*?\]/', $text, $matches);
769
 
 
770
 
        Log::debug('Loading SimplePie to handle RSS parsing.' . urldecode($this->GetOption('uri')) . '. Will substitute items with ' . $text);
771
 
        
772
 
        // Use SimplePie to get the feed
773
 
        include_once('3rdparty/simplepie/autoloader.php');
774
 
 
775
 
        $feed = new SimplePie();
776
 
        $feed->set_cache_location($file->GetLibraryCacheUri());
777
 
        $feed->set_feed_url(urldecode($this->GetOption('uri')));
778
 
        $feed->force_feed(true);
779
 
        $feed->set_cache_duration(($this->GetOption('updateInterval', 3600) * 60));
780
 
        $feed->handle_content_type();
781
 
 
782
 
        // Get a list of allowed attributes
783
 
        if ($this->GetOption('allowedAttributes') != '') {
784
 
            $attrsStrip = array_diff($feed->strip_attributes, explode(',', $this->GetOption('allowedAttributes')));
785
 
            //Debug::Audit(var_export($attrsStrip, true));
786
 
            $feed->strip_attributes($attrsStrip);
787
 
        }
788
 
 
789
 
        // Disable date sorting?
790
 
        if ($this->GetOption('disableDateSort') == 1) {
791
 
            $feed->enable_order_by_date(false);
792
 
        }
793
 
 
794
 
        // Init
795
 
        $feed->init();
796
 
 
797
 
        $dateFormat = $this->GetOption('dateFormat');
798
 
 
799
 
        if ($feed->error()) {
800
 
            Log::notice('Feed Error: ' . $feed->error());
801
 
            return array();
802
 
        }
803
 
 
804
 
        // Set an expiry time for the media
805
 
        $expires = time() + ($this->GetOption('updateInterval', 3600) * 60);
806
 
 
807
 
        // Store our formatted items
808
 
        $items = array();
809
 
 
810
 
        foreach ($feed->get_items() as $item) {
811
 
            /* @var SimplePie_Item $item */
812
 
 
813
 
            // Substitute for all matches in the template
814
 
            $rowString = $text;
815
 
            
816
 
            // Substitute
817
 
            foreach ($matches[0] as $sub) {
818
 
                $replace = '';
819
 
 
820
 
                // Pick the appropriate column out
821
 
                if (strstr($sub, '|') !== false) {
822
 
                    // Use the provided name space to extract a tag
823
 
                    $attributes = NULL;
824
 
                    if (substr_count($sub, '|') > 1)
825
 
                        list($tag, $namespace, $attributes) = explode('|', $sub);
826
 
                    else
827
 
                        list($tag, $namespace) = explode('|', $sub);
828
 
 
829
 
                    // What are we looking at
830
 
                    Log::debug('Namespace: ' . str_replace(']', '', $namespace) . '. Tag: ' . str_replace('[', '', $tag) . '. ');
831
 
 
832
 
                    // Are we an image place holder?
833
 
                    if (strstr($namespace, 'image') != false) {
834
 
                        // Try to get a link for the image
835
 
                        $link = null;
836
 
 
837
 
                        switch (str_replace('[', '', $tag)) {
838
 
                            case 'Link':
839
 
                                if ($enclosure = $item->get_enclosure()) {
840
 
                                    // Use the link to get the image
841
 
                                    $link = $enclosure->get_link();
842
 
                                }
843
 
                                break;
844
 
 
845
 
                            default:
846
 
                                // Default behaviour just tries to get the content from the tag provided (without a name space).
847
 
                                $tags = $item->get_item_tags('', str_replace('[', '', $tag));
848
 
 
849
 
                                if ($tags != null) {
850
 
                                    $link = (is_array($tags)) ? $tags[0]['data'] : '';
851
 
                                }
852
 
                        }
853
 
 
854
 
                        // If we have managed to resolve a link, download it and replace the tag with the downloaded
855
 
                        // image url
856
 
                        if ($link != NULL) {
857
 
                            // Grab the profile image
858
 
                            $file = $media->addModuleFileFromUrl($link, 'ticker_' . md5($this->GetOption('url') . $link), $expires);
859
 
 
860
 
                            // Tag this layout with this file
861
 
                            $this->assignMedia($file['mediaId']);
862
 
 
863
 
                            $replace = ($isPreview) ? '<img src="index.php?p=index.php?p=content&q=getFile&mediaid=' . $file['mediaId'] . '" ' . $attributes . '/>' : '<img src="' . $file['storedAs'] . '" ' . $attributes . ' />';
864
 
                        }
865
 
                    }
866
 
                    else {
867
 
                        $tags = $item->get_item_tags(str_replace(']', '', $namespace), str_replace('[', '', $tag));
868
 
                        
869
 
                        Log::notice('Tags:' . var_export($tags, true));
870
 
 
871
 
                        // If we find some tags then do the business with them
872
 
                        if ($tags != NULL) {
873
 
                            if ($attributes != NULL)
874
 
                                $replace = (is_array($tags)) ? $tags[0]['attribs'][''][str_replace(']', '', $attributes)] : '';
875
 
                            else
876
 
                                $replace = (is_array($tags)) ? $tags[0]['data'] : '';
877
 
                        }
878
 
                    }
879
 
                }
880
 
                else {
881
 
                    
882
 
                    // Use the pool of standard tags
883
 
                    switch ($sub) {
884
 
                        case '[Name]':
885
 
                            $replace = $this->GetOption('name');
886
 
                            break;
887
 
 
888
 
                        case '[Title]':
889
 
                            $replace = $item->get_title();
890
 
                            break;
891
 
 
892
 
                        case '[Description]':
893
 
                            $replace = $item->get_description();
894
 
                            break;
895
 
 
896
 
                        case '[Content]':
897
 
                            $replace = $item->get_content();
898
 
                            break;
899
 
 
900
 
                        case '[Copyright]':
901
 
                            $replace = $item->get_copyright();
902
 
                            break;
903
 
 
904
 
                        case '[Date]':
905
 
                            $replace = Date::getLocalDate($item->get_date('U'), $dateFormat);
906
 
                            break;
907
 
 
908
 
                        case '[PermaLink]':
909
 
                            $replace = $item->get_permalink();
910
 
                            break;
911
 
 
912
 
                        case '[Link]':
913
 
                            $replace = $item->get_link();
914
 
                            break;
915
 
                    }
916
 
                }
917
 
 
918
 
                if ($this->GetOption('stripTags') != '') {
919
 
                        require_once '3rdparty/htmlpurifier/library/HTMLPurifier.auto.php';
920
 
 
921
 
                        $config = HTMLPurifier_Config::createDefault();
922
 
                        $config->set('HTML.ForbiddenElements', array_merge($feed->strip_htmltags, explode(',', $this->GetOption('stripTags'))));
923
 
                        $purifier = new HTMLPurifier($config);
924
 
                        $replace = $purifier->purify($replace);
925
 
                }
926
 
 
927
 
                // Substitute the replacement we have found (it might be '')
928
 
                $rowString = str_replace($sub, $replace, $rowString);
929
 
            }
930
 
 
931
 
            $items[] = $rowString;
932
 
        }
933
 
 
934
 
        // Copyright information?
935
 
        if ($this->GetOption('copyright', '') != '') {
936
 
            $items[] = '<span id="copyright">' . $this->GetOption('copyright') . '</span>';
937
 
        }
938
 
 
939
 
        // Return the formatted items
940
 
        return $items;
941
 
    }
942
 
 
943
 
    private function GetDataSetItems($displayId, $isPreview, $text)
944
 
    {
945
 
        // Extra fields for data sets
946
 
        $dataSetId = $this->GetOption('datasetid');
947
 
        $upperLimit = $this->GetOption('upperLimit');
948
 
        $lowerLimit = $this->GetOption('lowerLimit');
949
 
        $filter = $this->GetOption('filter');
950
 
        $ordering = $this->GetOption('ordering');
951
 
 
952
 
        Log::notice('Then template for each row is: ' . $text);
953
 
 
954
 
        // Set an expiry time for the media
955
 
        $media = new Media();
956
 
        $layout = new Layout();
957
 
        $expires = time() + ($this->GetOption('updateInterval', 3600) * 60);
958
 
 
959
 
        // Combine the column id's with the dataset data
960
 
        $matches = '';
961
 
        preg_match_all('/\[(.*?)\]/', $text, $matches);
962
 
 
963
 
        $columnIds = array();
964
 
        
965
 
        foreach ($matches[1] as $match) {
966
 
            // Get the column id's we are interested in
967
 
            Log::notice('Matched column: ' . $match);
968
 
 
969
 
            $col = explode('|', $match);
970
 
            $columnIds[] = $col[1];
971
 
        }
972
 
 
973
 
        // Get the dataset results
974
 
        $dataSet = new DataSet();
975
 
        if (!$dataSetResults = $dataSet->DataSetResults($dataSetId, implode(',', $columnIds), $filter, $ordering, $lowerLimit, $upperLimit, $displayId)) {
976
 
            return '';
977
 
        }
978
 
 
979
 
        // Create an array of header|datatypeid pairs
980
 
        $columnMap = array();
981
 
        foreach ($dataSetResults['Columns'] as $col) {
982
 
            $columnMap[$col['Text']] = $col;
983
 
        }
984
 
 
985
 
        Log::debug(var_export($columnMap, true));
986
 
 
987
 
        $items = array();
988
 
 
989
 
        foreach ($dataSetResults['Rows'] as $row) {
990
 
            // For each row, substitute into our template
991
 
            $rowString = $text;
992
 
 
993
 
            foreach ($matches[1] as $sub) {
994
 
                // Pick the appropriate column out
995
 
                $subs = explode('|', $sub);
996
 
 
997
 
                // The column header
998
 
                $header = $subs[0];
999
 
                $replace = $row[$header];
1000
 
 
1001
 
                // Check in the columns array to see if this is a special one
1002
 
                if ($columnMap[$header]['DataTypeID'] == 4) {
1003
 
                    // Download the image, alter the replace to wrap in an image tag
1004
 
                    $file = $media->addModuleFileFromUrl(str_replace(' ', '%20', htmlspecialchars_decode($replace)), 'ticker_dataset_' . md5($dataSetId . $columnMap[$header]['DataSetColumnID'] . $replace), $expires);
1005
 
 
1006
 
                    // Tag this layout with this file
1007
 
                    $this->assignMedia($file['mediaId']);
1008
 
 
1009
 
                    $replace = ($isPreview) ? '<img src="index.php?index.php?p=content&q=getFile&mediaid=' . $file['mediaId'] . '" />' : '<img src="' . $file['storedAs'] . '" />';
1010
 
                }
1011
 
                
1012
 
                $rowString = str_replace('[' . $sub . ']', $replace, $rowString);
1013
 
            }
1014
 
 
1015
 
            $items[] = $rowString;
1016
 
        }
1017
 
 
1018
 
        return $items;
1019
 
    }
1020
 
    
1021
 
    public function IsValid()
1022
 
    {
1023
 
        // Can't be sure because the client does the rendering
1024
 
        return 1;
1025
 
    }
1026
 
}