~xibo-maintainers/xibo/tempel

« back to all changes in this revision

Viewing changes to lib/Entity/DataSet.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:
9
9
namespace Xibo\Entity;
10
10
 
11
11
use Respect\Validation\Validator as v;
12
 
use Stash\Interfaces\PoolInterface;
13
 
use Xibo\Exception\ConfigurationException;
14
 
use Xibo\Exception\DuplicateEntityException;
15
 
use Xibo\Exception\InvalidArgumentException;
16
12
use Xibo\Exception\NotFoundException;
17
 
use Xibo\Exception\XiboException;
18
13
use Xibo\Factory\DataSetColumnFactory;
19
14
use Xibo\Factory\DataSetFactory;
20
15
use Xibo\Factory\DisplayFactory;
21
16
use Xibo\Factory\PermissionFactory;
22
 
use Xibo\Service\ConfigServiceInterface;
23
 
use Xibo\Service\DateServiceInterface;
24
 
use Xibo\Service\LogServiceInterface;
25
 
use Xibo\Service\SanitizerServiceInterface;
26
 
use Xibo\Storage\StorageServiceInterface;
 
17
use Xibo\Helper\Config;
 
18
use Xibo\Helper\Log;
 
19
use Xibo\Helper\Sanitize;
 
20
use Xibo\Storage\PDOConnect;
27
21
 
28
22
/**
29
23
 * Class DataSet
77
71
     */
78
72
    public $groupsWithPermissions;
79
73
 
80
 
    /**
81
 
     * @SWG\Property(description="A code for this Data Set")
82
 
     * @var string
83
 
     */
84
 
    public $code;
85
 
 
86
 
    /**
87
 
     * @SWG\Property(description="Flag to indicate whether this DataSet is a lookup table")
88
 
     * @var int
89
 
     */
90
 
    public $isLookup = 0;
91
 
 
92
 
    /**
93
 
     * @SWG\Property(description="Flag to indicate whether this DataSet is Remote")
94
 
     * @var int
95
 
     */
96
 
    public $isRemote = 0;
97
 
 
98
 
    /**
99
 
     * @SWG\Property(description="Method to fetch the Data, can be GET or POST")
100
 
     * @var string
101
 
     */
102
 
    public $method;
103
 
 
104
 
    /**
105
 
     * @SWG\Property(description="URI to call to fetch Data from. Replacements are {{DATE}}, {{TIME}} and, in case this is a sequencial used DataSet, {{COL.NAME}} where NAME is a ColumnName from the underlying DataSet.")
106
 
     * @var string
107
 
     */
108
 
    public $uri;
109
 
 
110
 
    /**
111
 
     * @SWG\Property(description="Data to send as POST-Data to the remote host with the same Replacements as in the URI.")
112
 
     * @var string
113
 
     */
114
 
    public $postData;
115
 
 
116
 
    /**
117
 
     * @SWG\Property(description="Authentication method, can be none, digest, basic")
118
 
     * @var string
119
 
     */
120
 
    public $authentication;
121
 
 
122
 
    /**
123
 
     * @SWG\Property(description="Username to authenticate with")
124
 
     * @var string
125
 
     */
126
 
    public $username;
127
 
 
128
 
    /**
129
 
     * @SWG\Property(description="Corresponding password")
130
 
     * @var string
131
 
     */
132
 
    public $password;
133
 
 
134
 
    /**
135
 
     * @SWG\Property(description="Time in seconds this DataSet should fetch new Datas from the remote host")
136
 
     * @var int
137
 
     */
138
 
    public $refreshRate;
139
 
 
140
 
    /**
141
 
     * @SWG\Property(description="Time in seconds when this Dataset should be cleared. If here is a lower value than in RefreshRate it will be cleared when the data is refreshed")
142
 
     * @var int
143
 
     */
144
 
    public $clearRate;
145
 
 
146
 
    /**
147
 
     * @SWG\Property(description="DataSetID of the DataSet which should be fetched and present before the Data from this DataSet are fetched")
148
 
     * @var int
149
 
     */
150
 
    public $runsAfter;
151
 
 
152
 
    /**
153
 
     * @SWG\Property(description="Last Synchronisation Timestamp")
154
 
     * @var int
155
 
     */
156
 
    public $lastSync = 0;
157
 
 
158
 
    /**
159
 
     * @SWG\Property(description="Root-Element form JSON where the data are stored in")
160
 
     * @var String
161
 
     */
162
 
    public $dataRoot;
163
 
 
164
 
    /**
165
 
     * @SWG\Property(description="Optional function to use for summarize or count unique fields in a remote request")
166
 
     * @var String
167
 
     */
168
 
    public $summarize;
169
 
 
170
 
    /**
171
 
     * @SWG\Property(description="JSON-Element below the Root-Element on which the consolidation should be applied on")
172
 
     * @var String
173
 
     */
174
 
    public $summarizeField;
175
 
 
176
 
    /** @var array Permissions */
177
74
    private $permissions = [];
178
 
 
179
 
    /**
180
 
     * @var DataSetColumn[]
181
 
     */
182
 
    public $columns = [];
 
75
    private $columns = [];
183
76
 
184
77
    private $countLast = 0;
185
78
 
186
 
    /** @var array Blacklist for SQL */
187
 
    private $blackList = array(';', 'INSERT', 'UPDATE', 'SELECT', 'DELETE', 'TRUNCATE', 'TABLE', 'FROM', 'WHERE');
188
 
 
189
 
    /** @var  SanitizerServiceInterface */
190
 
    private $sanitizer;
191
 
 
192
 
    /** @var  ConfigServiceInterface */
193
 
    private $config;
194
 
 
195
 
    /** @var PoolInterface */
196
 
    private $pool;
197
 
 
198
 
    /** @var  DataSetFactory */
199
 
    private $dataSetFactory;
200
 
 
201
 
    /** @var  DataSetColumnFactory */
202
 
    private $dataSetColumnFactory;
203
 
 
204
 
    /** @var  PermissionFactory */
205
 
    private $permissionFactory;
206
 
 
207
 
    /** @var  DisplayFactory */
208
 
    private $displayFactory;
209
 
 
210
 
    /** @var DateServiceInterface */
211
 
    private $date;
212
 
 
213
 
    /**
214
 
     * Entity constructor.
215
 
     * @param StorageServiceInterface $store
216
 
     * @param LogServiceInterface $log
217
 
     * @param SanitizerServiceInterface $sanitizer
218
 
     * @param ConfigServiceInterface $config
219
 
     * @param PoolInterface $pool
220
 
     * @param DataSetFactory $dataSetFactory
221
 
     * @param DataSetColumnFactory $dataSetColumnFactory
222
 
     * @param PermissionFactory $permissionFactory
223
 
     * @param DisplayFactory $displayFactory
224
 
     * @param DateServiceInterface $date
225
 
     */
226
 
    public function __construct($store, $log, $sanitizer, $config, $pool, $dataSetFactory, $dataSetColumnFactory, $permissionFactory, $displayFactory, $date)
227
 
    {
228
 
        $this->setCommonDependencies($store, $log);
229
 
        $this->sanitizer = $sanitizer;
230
 
        $this->config = $config;
231
 
        $this->pool = $pool;
232
 
        $this->dataSetFactory = $dataSetFactory;
233
 
        $this->dataSetColumnFactory = $dataSetColumnFactory;
234
 
        $this->permissionFactory = $permissionFactory;
235
 
        $this->displayFactory = $displayFactory;
236
 
        $this->date = $date;
237
 
    }
238
 
 
239
 
    /**
240
 
     * Clone
241
 
     */
242
 
    public function __clone()
243
 
    {
244
 
        $this->dataSetId = null;
245
 
 
246
 
        $this->columns = array_map(function ($object) { return clone $object; }, $this->columns);
247
 
    }
248
 
 
249
 
    /**
250
 
     * @return int
251
 
     */
252
79
    public function getId()
253
80
    {
254
81
        return $this->dataSetId;
255
82
    }
256
83
 
257
 
    /**
258
 
     * @return int
259
 
     */
260
84
    public function getOwnerId()
261
85
    {
262
86
        return $this->userId;
274
98
    /**
275
99
     * Get Column
276
100
     * @param int[Optional] $dataSetColumnId
277
 
     * @return DataSetColumn[]|DataSetColumn
 
101
     * @return array[DataSetColumn]|DataSetColumn
278
102
     * @throws NotFoundException when the heading is provided and the column cannot be found
279
103
     */
280
104
    public function getColumn($dataSetColumnId = 0)
297
121
    }
298
122
 
299
123
    /**
300
 
     * Get Column
301
 
     * @param string $dataSetColumn
302
 
     * @return DataSetColumn[]|DataSetColumn
303
 
     * @throws NotFoundException when the heading is provided and the column cannot be found
304
 
     */
305
 
    public function getColumnByName($dataSetColumn)
306
 
    {
307
 
        $this->load();
308
 
 
309
 
        foreach ($this->columns as $column) {
310
 
            /* @var DataSetColumn $column */
311
 
            if ($column->heading == $dataSetColumn)
312
 
                return $column;
313
 
        }
314
 
 
315
 
        throw new NotFoundException(sprintf(__('Column %s not found'), $dataSetColumn));
316
 
    }
317
 
 
318
 
    /**
319
 
     * @param string[] $columns Column Names to select
320
 
     * @return array
321
 
     */
322
 
    public function getUniqueColumnValues($columns)
323
 
    {
324
 
        $this->load();
325
 
 
326
 
        $select = '';
327
 
        foreach ($columns as $heading) {
328
 
            // Check this exists
329
 
            $found = false;
330
 
            foreach ($this->columns as $column) {
331
 
                if ($column->heading == $heading) {
332
 
                    // Formula column?
333
 
                    if ($column->dataSetColumnTypeId == 2) {
334
 
                        $select .= str_replace($this->blackList, '', htmlspecialchars_decode($column->formula, ENT_QUOTES)) . ' AS `' . $column->heading . '`,';
335
 
                    }
336
 
                    else {
337
 
                        $select .= '`' . $column->heading . '`,';
338
 
                    }
339
 
                    $found = true;
340
 
                    break;
341
 
                }
342
 
            }
343
 
 
344
 
            if (!$found)
345
 
                throw new \InvalidArgumentException(__('Unknown Column ' . $heading));
346
 
        }
347
 
        $select = rtrim($select, ',');
348
 
        // $select is safe
349
 
 
350
 
        return $this->getStore()->select('SELECT DISTINCT ' . $select . ' FROM `dataset_' . $this->dataSetId . '`', []);
351
 
    }
352
 
 
353
 
    /**
354
124
     * Get DataSet Data
355
125
     * @param array $filterBy
356
 
     * @param array $options
357
126
     * @return array
358
 
     * @throws NotFoundException
359
127
     */
360
 
    public function getData($filterBy = [], $options = [])
 
128
    public function getData($filterBy = [])
361
129
    {
362
 
        $this->touchLastAccessed();
363
 
 
364
 
        $start = $this->sanitizer->getInt('start', 0, $filterBy);
365
 
        $size = $this->sanitizer->getInt('size', 0, $filterBy);
366
 
        $filter = $this->sanitizer->getParam('filter', $filterBy);
367
 
        $ordering = $this->sanitizer->getString('order', $filterBy);
368
 
        $displayId = $this->sanitizer->getInt('displayId', 0, $filterBy);
369
 
 
370
 
        $options = array_merge([
371
 
            'includeFormulaColumns' => true,
372
 
            'requireTotal' => true
373
 
        ], $options);
 
130
        $start = Sanitize::getInt('start', 0, $filterBy);
 
131
        $size = Sanitize::getInt('size', 0, $filterBy);
 
132
        $filter = Sanitize::getString('filter', $filterBy);
 
133
        $ordering = Sanitize::getString('order', $filterBy);
 
134
        $displayId = Sanitize::getInt('displayId', 0, $filterBy);
374
135
 
375
136
        // Params
376
137
        $params = [];
377
138
 
378
139
        // Sanitize the filter options provided
 
140
        $blackList = array(';', 'INSERT', 'UPDATE', 'SELECT', 'DELETE', 'TRUNCATE', 'TABLE', 'FROM', 'WHERE');
 
141
 
379
142
        // Get the Latitude and Longitude ( might be used in a formula )
380
143
        if ($displayId == 0) {
381
 
            $displayGeoLocation = "GEOMFROMTEXT('POINT(" . $this->config->GetSetting('DEFAULT_LAT') . " " . $this->config->GetSetting('DEFAULT_LONG') . ")')";
 
144
            $displayGeoLocation = "GEOMFROMTEXT('POINT(" . Config::GetSetting('DEFAULT_LAT') . " " . Config::GetSetting('DEFAULT_LONG') . ")')";
382
145
        }
383
146
        else {
384
147
            $displayGeoLocation = '(SELECT GeoLocation FROM `display` WHERE DisplayID = :displayId)';
388
151
        // Build a SQL statement, based on the columns for this dataset
389
152
        $this->load();
390
153
 
391
 
        $select  = 'SELECT * FROM ( ';
392
 
        $body = 'SELECT id';
 
154
        $select = 'SELECT id';
393
155
 
394
156
        // Keep track of the columns we are allowed to order by
395
157
        $allowedOrderCols = ['id'];
396
158
 
397
 
        // Are there any client side formulas
398
 
        $clientSideFormula = [];
399
 
 
400
159
        // Select (columns)
401
160
        foreach ($this->getColumn() as $column) {
402
161
            /* @var DataSetColumn $column */
403
162
            $allowedOrderCols[] = $column->heading;
404
 
            
405
 
            if ($column->dataSetColumnTypeId == 2 && !$options['includeFormulaColumns'])
406
 
                continue;
407
163
 
408
164
            // Formula column?
409
165
            if ($column->dataSetColumnTypeId == 2) {
410
 
 
411
 
                // Is this a client side column?
412
 
                if (substr($column->formula, 0, 1) === '$') {
413
 
                    $clientSideFormula[] = $column;
414
 
                    continue;
415
 
                }
416
 
 
417
 
                $formula = str_replace($this->blackList, '', htmlspecialchars_decode($column->formula, ENT_QUOTES));
418
 
                $formula = str_replace('[DisplayId]', $displayId, $formula);
419
 
 
420
 
                $heading = str_replace('[DisplayGeoLocation]', $displayGeoLocation, $formula) . ' AS `' . $column->heading . '`';
 
166
                $formula = str_replace($blackList, '', htmlspecialchars_decode($column->formula, ENT_QUOTES));
 
167
 
 
168
                $heading = str_replace('[DisplayGeoLocation]', $displayGeoLocation, $formula) . ' AS \'' . $column->heading . '\'';
421
169
            }
422
170
            else {
423
171
                $heading = '`' . $column->heading . '`';
424
172
            }
425
173
 
426
 
            $body .= ', ' . $heading;
 
174
            $select .= ', ' . $heading;
427
175
        }
428
176
 
429
 
        $body .= ' FROM `dataset_' . $this->dataSetId . '`) dataset WHERE 1 = 1 ';
 
177
        $body = ' FROM `dataset_' . $this->dataSetId . '` WHERE 1 = 1 ';
430
178
 
431
179
        // Filtering
432
180
        if ($filter != '') {
433
 
            // Support display filtering.
434
 
            $filter = str_replace('[DisplayId]', $displayId, $filter);
435
 
            $filter = str_replace($this->blackList, '', $filter);
436
 
 
437
 
            $body .= ' AND ' . $filter;
 
181
            $body .= ' AND ' . str_replace($blackList, '', $filter);
438
182
        }
439
183
 
440
184
        // Filter by ID
441
185
        if (
442
 
            $this->sanitizer->getInt('id', $filterBy) !== null) {
 
186
            Sanitize::getInt('id', $filterBy) !== null) {
443
187
            $body .= ' AND id = :id ';
444
 
            $params['id'] = $this->sanitizer->getInt('id', $filterBy);
 
188
            $params['id'] = Sanitize::getInt('id', $filterBy);
445
189
        }
446
190
 
447
191
        // Ordering
453
197
 
454
198
            foreach ($ordering as $orderPair) {
455
199
                // Sanitize the clause
456
 
                $sanitized = str_replace('`', '', str_replace(' ASC', '', str_replace(' DESC', '', $orderPair)));
 
200
                $sanitized = str_replace('`', '', str_replace(' DESC', '', $orderPair));
457
201
 
458
202
                // Check allowable
459
203
                if (!in_array($sanitized, $allowedOrderCols)) {
460
 
                    $this->getLog()->info('Disallowed column: ' . $sanitized);
 
204
                    Log::Info('Disallowed column: ' . $sanitized);
461
205
                    continue;
462
206
                }
463
207
 
465
209
                if (strripos($orderPair, ' DESC')) {
466
210
                    $order .= sprintf(' `%s`  DESC,', $sanitized);
467
211
                }
468
 
                else if (strripos($orderPair, ' ASC')) {
469
 
                    $order .= sprintf(' `%s`  ASC,', $sanitized);
470
 
                }
471
212
                else {
472
213
                    $order .= sprintf(' `%s`,', $sanitized);
473
214
                }
488
229
 
489
230
        $sql = $select . $body . $order . $limit;
490
231
 
491
 
        $data = $this->getStore()->select($sql, $params);
 
232
        Log::sql($sql, $params);
 
233
 
 
234
        $data = PDOConnect::select($sql, $params);
492
235
 
493
236
        // If there are limits run some SQL to work out the full payload of rows
494
 
        if ($options['requireTotal']) {
495
 
            $results = $this->getStore()->select('SELECT COUNT(*) AS total FROM (' . $body, $params);
496
 
            $this->countLast = intval($results[0]['total']);
497
 
        }
498
 
 
499
 
        // Are there any client side formulas?
500
 
        if (count($clientSideFormula) > 0) {
501
 
            $renderedData = [];
502
 
            foreach ($data as $item) {
503
 
                foreach ($clientSideFormula as $column) {
504
 
                    // Run the formula and add the resulting value to the list
505
 
                    $value = null;
506
 
                    try {
507
 
                        if (substr($column->formula, 0, strlen('$dateFormat(')) === '$dateFormat(') {
508
 
                            // Pull out the column name and date format
509
 
                            $details = explode(',', str_replace(')', '', str_replace('$dateFormat(', '', $column->formula)));
510
 
 
511
 
                            $value = $this->date->parse($item[$details[0]])->format($details[1]);
512
 
                        }
513
 
                    } catch (\Exception $e) {
514
 
                        $this->getLog()->error('DataSet client side formula error in dataSetId ' . $this->dataSetId . ' with column formula ' . $column->formula);
515
 
                    }
516
 
 
517
 
                    $item[$column->heading] = $value;
518
 
                }
519
 
 
520
 
                $renderedData[] = $item;
521
 
            }
522
 
        } else {
523
 
            $renderedData = $data;
524
 
        }
525
 
 
526
 
        return $renderedData;
 
237
        $results = PDOConnect::select('SELECT COUNT(*) AS total ' . $body, $params);
 
238
        $this->countLast = intval($results[0]['total']);
 
239
 
 
240
        return $data;
527
241
    }
528
242
 
529
243
    /**
534
248
    {
535
249
        $this->load();
536
250
 
537
 
        // Set the dataSetId
538
 
        $column->dataSetId = $this->dataSetId;
539
 
 
540
251
        // Set the column order if we need to
541
252
        if ($column->columnOrder == 0)
542
253
            $column->columnOrder = count($this->columns) + 1;
550
261
     */
551
262
    public function hasData()
552
263
    {
553
 
        return $this->getStore()->exists('SELECT id FROM `dataset_' . $this->dataSetId . '` LIMIT 1', []);
554
 
    }
555
 
 
556
 
    /**
557
 
     * Returns a Timestamp for the next Synchronisation process.
558
 
     * @return int Seconds
559
 
     */
560
 
    public function getNextSyncTime()
561
 
    {
562
 
        return $this->lastSync + $this->refreshRate;
563
 
    }
564
 
 
565
 
    /**
566
 
     * @return bool
567
 
     */
568
 
    public function isTruncateEnabled()
569
 
    {
570
 
        return $this->clearRate === 0;
571
 
    }
572
 
 
573
 
    /**
574
 
     * Returns a Timestamp for the next Clearing process.
575
 
     * @return int Seconds
576
 
     */
577
 
    public function getNextClearTime()
578
 
    {
579
 
        return $this->lastSync + $this->clearRate;
580
 
    }
581
 
 
582
 
    /**
583
 
     * Returns if there is a consolidation field and method present or not.
584
 
     * @return boolean
585
 
     */
586
 
    public function doConsolidate()
587
 
    {
588
 
        return ($this->summarizeField != null) && ($this->summarizeField != '')
589
 
            && ($this->summarize != null) && ($this->summarize != '');
590
 
    }
591
 
 
592
 
    /**
593
 
     * Returns the last Part of the Fieldname on which the consolidation should be applied on
594
 
     * @return String
595
 
     */
596
 
    public function getConsolidationField()
597
 
    {
598
 
        $pos = strrpos($this->summarizeField, '.');
599
 
        if ($pos !== false) {
600
 
            return substr($this->summarizeField, $pos + 1);
601
 
        }
602
 
        return $this->summarizeField;
603
 
    }
604
 
 
605
 
    /**
606
 
     * Tests if this DataSet contains parameters for getting values on the dependant DataSet
607
 
     * @return boolean
608
 
     */
609
 
    public function containsDependantFieldsInRequest()
610
 
    {
611
 
        return strpos($this->postData, '{{COL.') !== false || strpos($this->uri, '{{COL.') !== false;
 
264
        return PDOConnect::exists('SELECT id FROM `dataset_' . $this->dataSetId . '` LIMIT 1', []);
612
265
    }
613
266
 
614
267
    /**
615
268
     * Validate
616
 
     * @throws InvalidArgumentException
617
 
     * @throws DuplicateEntityException
618
269
     */
619
270
    public function validate()
620
271
    {
621
 
        if (!v::stringType()->notEmpty()->length(null, 50)->validate($this->dataSet))
622
 
            throw new InvalidArgumentException(__('Name must be between 1 and 50 characters'), 'dataSet');
623
 
 
624
 
        if ($this->description != null && !v::stringType()->length(null, 254)->validate($this->description))
625
 
            throw new InvalidArgumentException(__('Description can not be longer than 254 characters'), 'description');
626
 
 
627
 
        // If we are a remote dataset do some additional checks
628
 
        if ($this->isRemote === 1) {
629
 
            if (!v::stringType()->notEmpty()->validate($this->uri))
630
 
                throw new InvalidArgumentException(__('A remote DataSet must have a URI.'), 'uri');
631
 
        }
 
272
        if (!v::string()->notEmpty()->length(null, 50)->validate($this->dataSet))
 
273
            throw new \InvalidArgumentException(__('Name must be between 1 and 50 characters'));
 
274
 
 
275
        if ($this->description != null && !v::string()->length(null, 254)->validate($this->description))
 
276
            throw new \InvalidArgumentException(__('Description can not be longer than 254 characters'));
632
277
 
633
278
        try {
634
 
            $existing = $this->dataSetFactory->getByName($this->dataSet, $this->userId);
 
279
            $existing = DataSetFactory::getByName($this->dataSet);
635
280
 
636
281
            if ($this->dataSetId == 0 || $this->dataSetId != $existing->dataSetId)
637
 
                throw new DuplicateEntityException(sprintf(__('There is already dataSet called %s. Please choose another name.'), $this->dataSet));
 
282
                throw new \InvalidArgumentException(sprintf(__('There is already dataSet called %s. Please choose another name.'), $this->dataSet));
638
283
        }
639
284
        catch (NotFoundException $e) {
640
285
            // This is good
650
295
            return;
651
296
 
652
297
        // Load Columns
653
 
        $this->columns = $this->dataSetColumnFactory->getByDataSetId($this->dataSetId);
 
298
        $this->columns = DataSetColumnFactory::getByDataSetId($this->dataSetId);
654
299
 
655
300
        // Load Permissions
656
 
        $this->permissions = $this->permissionFactory->getByObjectId(get_class($this), $this->getId());
 
301
        $this->permissions = PermissionFactory::getByObjectId(get_class($this), $this->getId());
657
302
 
658
303
        $this->loaded = true;
659
304
    }
661
306
    /**
662
307
     * Save this DataSet
663
308
     * @param array $options
664
 
     * @throws InvalidArgumentException
665
 
     * @throws DuplicateEntityException
666
309
     */
667
310
    public function save($options = [])
668
311
    {
685
328
            }
686
329
        }
687
330
 
688
 
        // We've been touched
689
 
        $this->touchLastAccessed();
690
 
 
691
331
        // Notify Displays?
692
332
        $this->notify();
693
333
    }
694
334
 
695
335
    /**
696
 
     * @param int $time
697
 
     * @return $this
698
 
     */
699
 
    public function saveLastSync($time)
700
 
    {
701
 
        $this->lastSync = $time;
702
 
 
703
 
        $this->getStore()->update('UPDATE `dataset` SET lastSync = :lastSync WHERE dataSetId = :dataSetId', [
704
 
            'dataSetId' => $this->dataSetId,
705
 
            'lastSync' => $this->lastSync
706
 
        ]);
707
 
 
708
 
        return $this;
709
 
    }
710
 
 
711
 
    private function touchLastAccessed()
712
 
    {
713
 
        // Touch this dataSet
714
 
        $dataSetCache = $this->pool->getItem('/dataset/accessed/' . $this->dataSetId);
715
 
        $dataSetCache->set('true');
716
 
        $dataSetCache->expiresAfter(intval($this->config->GetSetting('REQUIRED_FILES_LOOKAHEAD')) * 1.5);
717
 
        $this->pool->saveDeferred($dataSetCache);
718
 
    }
719
 
 
720
 
    /**
721
336
     * Delete DataSet
722
 
     * @throws ConfigurationException
723
 
     * @throws InvalidArgumentException
724
337
     */
725
338
    public function delete()
726
339
    {
727
340
        $this->load();
728
341
 
729
 
        if ($this->isLookup)
730
 
            throw new ConfigurationException(__('Lookup Tables cannot be deleted'));
731
 
 
732
 
        // TODO: Make sure we're not used as a dependent DataSet
733
 
 
734
 
        // Make sure we're able to delete
735
 
        if ($this->getStore()->exists('
736
 
            SELECT widgetId 
737
 
              FROM `widgetoption`
738
 
              WHERE `widgetoption`.type = \'attrib\'
739
 
                AND `widgetoption`.option = \'dataSetId\'
740
 
                AND `widgetoption`.value = :dataSetId
741
 
        ', ['dataSetId' => $this->dataSetId])) {
742
 
            throw new InvalidArgumentException('Cannot delete because DataSet is in use on one or more Layouts.', 'dataSetId');
743
 
        }
 
342
        // TODO check we aren't being used
744
343
 
745
344
        // Delete Permissions
746
345
        foreach ($this->permissions as $permission) {
755
354
        }
756
355
 
757
356
        // Delete the data set
758
 
        $this->getStore()->update('DELETE FROM `dataset` WHERE dataSetId = :dataSetId', ['dataSetId' => $this->dataSetId]);
 
357
        PDOConnect::update('DELETE FROM `dataset` WHERE dataSetId = :dataSetId', ['dataSetId' => $this->dataSetId]);
759
358
 
760
359
        // The last thing we do is drop the dataSet table
761
 
        $this->dropTable();
 
360
        PDOConnect::update('DROP TABLE dataset_' . $this->dataSetId, []);
762
361
    }
763
362
 
764
 
    /**
765
 
     * Delete all data
766
 
     */
767
363
    public function deleteData()
768
364
    {
769
365
        // The last thing we do is drop the dataSet table
770
 
        $this->getStore()->isolated('TRUNCATE TABLE `dataset_' . $this->dataSetId . '`', []);
771
 
        $this->getStore()->isolated('ALTER TABLE `dataset_' . $this->dataSetId . '` AUTO_INCREMENT = 1', []);
 
366
        PDOConnect::update('TRUNCATE TABLE `dataset_' . $this->dataSetId . '`', []);
 
367
        PDOConnect::update('ALTER TABLE `dataset_' . $this->dataSetId . '` AUTO_INCREMENT = 1', []);
772
368
    }
773
369
 
774
370
    /**
776
372
     */
777
373
    private function add()
778
374
    {
779
 
        $columns = 'DataSet, Description, UserID, `code`, `isLookup`, `isRemote`';
780
 
        $values = ':dataSet, :description, :userId, :code, :isLookup, :isRemote';
781
 
 
782
 
        $params = [
783
 
            'dataSet' => $this->dataSet,
784
 
            'description' => $this->description,
785
 
            'userId' => $this->userId,
786
 
            'code' => ($this->code == '') ? null : $this->code,
787
 
            'isLookup' => $this->isLookup,
788
 
            'isRemote' => $this->isRemote
789
 
        ];
790
 
 
791
 
        // Insert the extra columns we expect for a remote DataSet
792
 
        if ($this->isRemote === 1) {
793
 
            $columns .= ', `method`, `uri`, `postData`, `authentication`, `username`, `password`, `refreshRate`, `clearRate`, `runsAfter`, `dataRoot`, `lastSync`, `summarize`, `summarizeField`';
794
 
            $values .= ', :method, :uri, :postData, :authentication, :username, :password, :refreshRate, :clearRate, :runsAfter, :dataRoot, :lastSync, :summarize, :summarizeField';
795
 
 
796
 
            $params['method'] = $this->method;
797
 
            $params['uri'] = $this->uri;
798
 
            $params['postData'] = $this->postData;
799
 
            $params['authentication'] = $this->authentication;
800
 
            $params['username'] = $this->username;
801
 
            $params['password'] = $this->password;
802
 
            $params['refreshRate'] = $this->refreshRate;
803
 
            $params['clearRate'] = $this->clearRate;
804
 
            $params['runsAfter'] = $this->runsAfter;
805
 
            $params['dataRoot'] = $this->dataRoot;
806
 
            $params['summarize'] = $this->summarize;
807
 
            $params['summarizeField'] = $this->summarizeField;
808
 
            $params['lastSync'] = 0;
809
 
        }
810
 
 
811
 
        // Do the insert
812
 
        $this->dataSetId = $this->getStore()->insert('INSERT INTO `dataset` (' . $columns . ') VALUES (' . $values . ')', $params);
813
 
 
814
 
        // Create the data table for this dataSet
815
 
        $this->createTable();
816
 
    }
817
 
 
818
 
    /**
819
 
     * Edit
820
 
     */
821
 
    private function edit()
822
 
    {
823
 
        $sql = 'DataSet = :dataSet, Description = :description, lastDataEdit = :lastDataEdit, `code` = :code, `isLookup` = :isLookup, `isRemote` = :isRemote ';
824
 
        $params = [
825
 
            'dataSetId' => $this->dataSetId,
826
 
            'dataSet' => $this->dataSet,
827
 
            'description' => $this->description,
828
 
            'lastDataEdit' => $this->lastDataEdit,
829
 
            'code' => $this->code,
830
 
            'isLookup' => $this->isLookup,
831
 
            'isRemote' => $this->isRemote,
832
 
        ];
833
 
 
834
 
        if ($this->isRemote) {
835
 
            $sql .= ', method = :method, uri = :uri, postData = :postData, authentication = :authentication, `username` = :username, `password` = :password, refreshRate = :refreshRate, clearRate = :clearRate, runsAfter = :runsAfter, `dataRoot` = :dataRoot, `summarize` = :summarize, `summarizeField` = :summarizeField ';
836
 
 
837
 
            $params['method'] = $this->method;
838
 
            $params['uri'] = $this->uri;
839
 
            $params['postData'] = $this->postData;
840
 
            $params['authentication'] = $this->authentication;
841
 
            $params['username'] = $this->username;
842
 
            $params['password'] = $this->password;
843
 
            $params['refreshRate'] = $this->refreshRate;
844
 
            $params['clearRate'] = $this->clearRate;
845
 
            $params['runsAfter'] = $this->runsAfter;
846
 
            $params['dataRoot'] = $this->dataRoot;
847
 
            $params['summarize'] = $this->summarize;
848
 
            $params['summarizeField'] = $this->summarizeField;
849
 
        }
850
 
 
851
 
        $this->getStore()->update('UPDATE dataset SET ' . $sql . '  WHERE DataSetID = :dataSetId', $params);
852
 
    }
853
 
 
854
 
    /**
855
 
     * Create the realised table structure for this DataSet
856
 
     */
857
 
    private function createTable()
858
 
    {
 
375
        $this->dataSetId = PDOConnect::insert('
 
376
          INSERT INTO `dataset` (DataSet, Description, UserID)
 
377
            VALUES (:dataSet, :description, :userId)
 
378
        ', [
 
379
            'dataSet' => $this->dataSet,
 
380
            'description' => $this->description,
 
381
            'userId' => $this->userId
 
382
        ]);
 
383
 
859
384
        // Create the data table for this dataset
860
 
        $this->getStore()->update('
 
385
        PDOConnect::update('
861
386
          CREATE TABLE `dataset_' . $this->dataSetId . '` (
862
387
            `id` int(11) NOT NULL AUTO_INCREMENT,
863
388
            PRIMARY KEY (`id`)
865
390
        ', []);
866
391
    }
867
392
 
868
 
    private function dropTable()
869
 
    {
870
 
        $this->getStore()->isolated('DROP TABLE IF EXISTS dataset_' . $this->dataSetId, []);
871
 
    }
872
 
 
873
393
    /**
874
 
     * Rebuild the dataSet table
875
 
     * @throws XiboException
 
394
     * Edit
876
395
     */
877
 
    public function rebuild()
 
396
    private function edit()
878
397
    {
879
 
        $this->load();
880
 
 
881
 
        // Drop the data table
882
 
        $this->dropTable();
883
 
 
884
 
        // Add the data table
885
 
        $this->createTable();
886
 
 
887
 
        foreach ($this->columns as $column) {
888
 
            /* @var \Xibo\Entity\DataSetColumn $column */
889
 
            $column->dataSetId = $this->dataSetId;
890
 
            $column->save(['rebuilding' => true]);
891
 
        }
 
398
        PDOConnect::update('
 
399
          UPDATE dataset SET DataSet = :dataSet, Description = :description, lastDataEdit = :lastDataEdit WHERE DataSetID = :dataSetId
 
400
        ', [
 
401
            'dataSetId' => $this->dataSetId,
 
402
            'dataSet' => $this->dataSet,
 
403
            'description' => $this->description,
 
404
            'lastDataEdit' => $this->lastDataEdit
 
405
        ]);
892
406
    }
893
407
 
894
408
    /**
896
410
     */
897
411
    public function notify()
898
412
    {
899
 
        $this->getLog()->debug('DataSet ' . $this->dataSetId . ' wants to notify');
 
413
        Log::debug('Checking for Displays to refresh for DataSet %d', $this->dataSetId);
900
414
 
901
 
        $this->displayFactory->getDisplayNotifyService()->collectNow()->notifyByDataSetId($this->dataSetId);
 
415
        foreach (DisplayFactory::getByDataSetId($this->dataSetId) as $display) {
 
416
            /* @var \Xibo\Entity\Display $display */
 
417
            $display->setMediaIncomplete();
 
418
            $display->save(false, false);
 
419
        }
902
420
    }
903
421
 
904
422
    /**
908
426
     */
909
427
    public function addRow($row)
910
428
    {
911
 
        $this->getLog()->debug('Adding row ' . var_export($row, true));
 
429
        Log::debug('Adding row %s', var_export($row, true));
912
430
 
913
431
        // Update the last edit date on this dataSet
914
432
        $this->lastDataEdit = time();
918
436
        $keys[] = 'id';
919
437
 
920
438
        $values = array_values($row);
921
 
        $values[] = NULL;
922
 
 
923
 
        $sql = 'INSERT INTO `dataset_' . $this->dataSetId . '` (`' . implode('`, `', $keys) . '`) VALUES (' . implode(',', array_fill(0, count($values), '?')) . ')';
924
 
 
925
 
        return $this->getStore()->insert($sql, $values);
 
439
        $values[] = 'NULL';
 
440
 
 
441
        $sql = 'INSERT INTO `dataset_' . $this->dataSetId . '` (' . implode(',', $keys) . ') VALUES (' . implode(',', array_fill(0, count($values), '?')) . ')';
 
442
 
 
443
        Log::sql($sql, $values);
 
444
 
 
445
        return PDOConnect::insert($sql, $values);
926
446
    }
927
447
 
928
448
    /**
932
452
     */
933
453
    public function editRow($rowId, $row)
934
454
    {
935
 
        $this->getLog()->debug('Editing row %s', var_export($row, true));
 
455
        Log::debug('Editing row %s', var_export($row, true));
936
456
 
937
457
        // Update the last edit date on this dataSet
938
458
        $this->lastDataEdit = time();
954
474
 
955
475
        $sql .= ' WHERE `id` = :id ';
956
476
 
957
 
 
958
 
 
959
 
        $this->getStore()->update($sql, $params);
 
477
        Log::sql($sql, $params);
 
478
 
 
479
        PDOConnect::update($sql, $params);
960
480
    }
961
481
 
962
482
    /**
967
487
    {
968
488
        $this->lastDataEdit = time();
969
489
 
970
 
        $this->getStore()->update('DELETE FROM `dataset_' . $this->dataSetId . '` WHERE id = :id', [
 
490
        PDOConnect::update('DELETE FROM `dataset_' . $this->dataSetId . '` WHERE id = :id', [
971
491
            'id' => $rowId
972
492
        ]);
973
493
    }