~xibo-maintainers/xibo/tempel

375 by Dan Garner
Status Dashboard implemented.
1
<?php
2
/*
3
 * Xibo - Digital Signage - http://www.xibo.org.uk
4
 * Copyright (C) 2015 Spring Signage Ltd
5
 *
6
 * This file (Display.php) 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
22
23
namespace Xibo\Entity;
24
25
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
26
use Respect\Validation\Validator as v;
560.2.19 by Dan Garner
Store the current layout id in cache.
27
use Stash\Interfaces\PoolInterface;
547 by Dan Garner
Bugfix/1.8.0 pack1 (#227)
28
use Xibo\Exception\DeadlockException;
540.1.5 by Dan Garner
Update InvalidArgumentExceptions to use XiboException and switch to DuplicateEntity exception where appropriate
29
use Xibo\Exception\InvalidArgumentException;
529.1.9 by Dan Garner
Fix case where a display has a deleted display profile assigned
30
use Xibo\Exception\NotFoundException;
484.3.1 by Dan Garner
CDN implementation for XMDS.
31
use Xibo\Factory\DisplayFactory;
484.2.23 by Dan Garner
DI down the stack for other Controllers
32
use Xibo\Factory\DisplayGroupFactory;
33
use Xibo\Factory\DisplayProfileFactory;
484.3.1 by Dan Garner
CDN implementation for XMDS.
34
use Xibo\Factory\LayoutFactory;
35
use Xibo\Factory\MediaFactory;
36
use Xibo\Factory\ScheduleFactory;
484.2.23 by Dan Garner
DI down the stack for other Controllers
37
use Xibo\Service\ConfigServiceInterface;
484.2.18 by Dan Garner
Start work building class constructors for all dependencies.
38
use Xibo\Service\LogServiceInterface;
39
use Xibo\Storage\StorageServiceInterface;
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
40
441.1.40 by Dan Garner
Document Models with Swagger
41
/**
42
 * Class Display
43
 * @package Xibo\Entity
44
 *
45
 * @SWG\Definition()
46
 */
529.1.70 by Dan Garner
Added an embed parameter for GET displays - formatted output types better.
47
class Display implements \JsonSerializable
375 by Dan Garner
Status Dashboard implemented.
48
{
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
49
    private $_config;
50
    use EntityTrait;
51
441.1.40 by Dan Garner
Document Models with Swagger
52
    /**
53
     * @SWG\Property(description="The ID of this Display")
54
     * @var int
55
     */
375 by Dan Garner
Status Dashboard implemented.
56
    public $displayId;
441.1.40 by Dan Garner
Document Models with Swagger
57
58
    /**
59
     * @SWG\Property(description="Flag indicating whether this Display is recording Auditing Information from XMDS")
60
     * @var int
61
     */
507.1.4 by Dan Garner
Display auditing based on date rather than flag
62
    public $auditingUntil;
441.1.40 by Dan Garner
Document Models with Swagger
63
64
    /**
65
     * @SWG\Property(description="The Name of this Display")
66
     * @var string
67
     */
375 by Dan Garner
Status Dashboard implemented.
68
    public $display;
441.1.40 by Dan Garner
Document Models with Swagger
69
70
    /**
71
     * @SWG\Property(description="The Description of this Display")
72
     * @var string
73
     */
375 by Dan Garner
Status Dashboard implemented.
74
    public $description;
441.1.40 by Dan Garner
Document Models with Swagger
75
76
    /**
77
     * @SWG\Property(description="The ID of the Default Layout")
78
     * @var int
79
     */
437.1.7 by Dan Garner
1st pass over Soap4
80
    public $defaultLayoutId = 4;
441.1.40 by Dan Garner
Document Models with Swagger
81
82
    /**
83
     * @SWG\Property(description="The Display Unique Identifier also called hardware key")
84
     * @var string
85
     */
375 by Dan Garner
Status Dashboard implemented.
86
    public $license;
441.1.40 by Dan Garner
Document Models with Swagger
87
88
    /**
89
     * @SWG\Property(description="A flag indicating whether this Display is licensed or not")
90
     * @var int
91
     */
375 by Dan Garner
Status Dashboard implemented.
92
    public $licensed;
441.1.40 by Dan Garner
Document Models with Swagger
93
    private $currentlyLicensed;
94
95
    /**
96
     * @SWG\Property(description="A flag indicating whether this Display is currently logged in")
97
     * @var int
98
     */
375 by Dan Garner
Status Dashboard implemented.
99
    public $loggedIn;
441.1.40 by Dan Garner
Document Models with Swagger
100
101
    /**
102
     * @SWG\Property(description="A timestamp in CMS time for the last time the Display accessed XMDS")
103
     * @var int
104
     */
375 by Dan Garner
Status Dashboard implemented.
105
    public $lastAccessed;
441.1.40 by Dan Garner
Document Models with Swagger
106
107
    /**
108
     * @SWG\Property(description="A flag indicating whether the default layout is interleaved with the Schedule")
109
     * @var int
110
     */
375 by Dan Garner
Status Dashboard implemented.
111
    public $incSchedule;
441.1.40 by Dan Garner
Document Models with Swagger
112
113
    /**
114
     * @SWG\Property(description="A flag indicating whether the Display will send email alerts.")
115
     * @var int
116
     */
375 by Dan Garner
Status Dashboard implemented.
117
    public $emailAlert;
441.1.40 by Dan Garner
Document Models with Swagger
118
119
    /**
120
     * @SWG\Property(description="A timeout in seconds for the Display to send email alerts.")
121
     * @var int
122
     */
375 by Dan Garner
Status Dashboard implemented.
123
    public $alertTimeout;
441.1.40 by Dan Garner
Document Models with Swagger
124
125
    /**
126
     * @SWG\Property(description="The MAC Address of the Display")
127
     * @var string
128
     */
375 by Dan Garner
Status Dashboard implemented.
129
    public $clientAddress;
441.1.40 by Dan Garner
Document Models with Swagger
130
131
    /**
132
     * @SWG\Property(description="The media inventory status of the Display")
133
     * @var int
134
     */
375 by Dan Garner
Status Dashboard implemented.
135
    public $mediaInventoryStatus;
441.1.40 by Dan Garner
Document Models with Swagger
136
137
    /**
138
     * @SWG\Property(description="The current Mac Address of the Player")
139
     * @var string
140
     */
375 by Dan Garner
Status Dashboard implemented.
141
    public $macAddress;
441.1.40 by Dan Garner
Document Models with Swagger
142
143
    /**
144
     * @SWG\Property(description="A timestamp indicating the last time the Mac Address changed")
145
     * @var int
146
     */
375 by Dan Garner
Status Dashboard implemented.
147
    public $lastChanged;
441.1.40 by Dan Garner
Document Models with Swagger
148
149
    /**
150
     * @SWG\Property(description="A count of Mac Address changes")
151
     * @var int
152
     */
375 by Dan Garner
Status Dashboard implemented.
153
    public $numberOfMacAddressChanges;
441.1.40 by Dan Garner
Document Models with Swagger
154
155
    /**
156
     * @SWG\Property(description="A timestamp indicating the last time a WOL command was sent")
157
     * @var int
158
     */
375 by Dan Garner
Status Dashboard implemented.
159
    public $lastWakeOnLanCommandSent;
441.1.40 by Dan Garner
Document Models with Swagger
160
161
    /**
162
     * @SWG\Property(description="A flag indicating whether Wake On Lan is enabled")
163
     * @var int
164
     */
375 by Dan Garner
Status Dashboard implemented.
165
    public $wakeOnLanEnabled;
441.1.40 by Dan Garner
Document Models with Swagger
166
167
    /**
168
     * @SWG\Property(description="A h:i string indicating the time to send a WOL command")
169
     * @var string
170
     */
375 by Dan Garner
Status Dashboard implemented.
171
    public $wakeOnLanTime;
441.1.40 by Dan Garner
Document Models with Swagger
172
173
    /**
174
     * @SWG\Property(description="The broad cast address for this Display")
175
     * @var string
176
     */
375 by Dan Garner
Status Dashboard implemented.
177
    public $broadCastAddress;
441.1.40 by Dan Garner
Document Models with Swagger
178
179
    /**
180
     * @SWG\Property(description="The secureOn WOL settings for this display.")
181
     * @var string
182
     */
375 by Dan Garner
Status Dashboard implemented.
183
    public $secureOn;
441.1.40 by Dan Garner
Document Models with Swagger
184
185
    /**
186
     * @SWG\Property(description="The CIDR WOL settings for this display")
187
     * @var string
188
     */
375 by Dan Garner
Status Dashboard implemented.
189
    public $cidr;
441.1.40 by Dan Garner
Document Models with Swagger
190
191
    /**
192
     * @SWG\Property(description="The display Latitude")
193
     * @var double
194
     */
375 by Dan Garner
Status Dashboard implemented.
195
    public $latitude;
441.1.40 by Dan Garner
Document Models with Swagger
196
197
    /**
198
     * @SWG\Property(description="The display longitude")
199
     * @var double
200
     */
375 by Dan Garner
Status Dashboard implemented.
201
    public $longitude;
441.1.40 by Dan Garner
Document Models with Swagger
202
203
    /**
204
     * @SWG\Property(description="A JSON string representing the player installer that should be installed")
205
     * @var string
206
     */
375 by Dan Garner
Status Dashboard implemented.
207
    public $versionInstructions;
441.1.40 by Dan Garner
Document Models with Swagger
208
209
    /**
210
     * @SWG\Property(description="A string representing the player type")
211
     * @var string
212
     */
375 by Dan Garner
Status Dashboard implemented.
213
    public $clientType;
441.1.40 by Dan Garner
Document Models with Swagger
214
215
    /**
216
     * @SWG\Property(description="A string representing the player version")
217
     * @var string
218
     */
375 by Dan Garner
Status Dashboard implemented.
219
    public $clientVersion;
441.1.40 by Dan Garner
Document Models with Swagger
220
221
    /**
222
     * @SWG\Property(description="A number representing the Player version code")
223
     * @var int
224
     */
375 by Dan Garner
Status Dashboard implemented.
225
    public $clientCode;
441.1.40 by Dan Garner
Document Models with Swagger
226
227
    /**
228
     * @SWG\Property(description="The display settings profile ID for this Display")
229
     * @var int
230
     */
375 by Dan Garner
Status Dashboard implemented.
231
    public $displayProfileId;
441.1.40 by Dan Garner
Document Models with Swagger
232
233
    /**
234
     * @SWG\Property(description="The current layout ID reported via XMDS")
235
     * @var int
236
     */
375 by Dan Garner
Status Dashboard implemented.
237
    public $currentLayoutId;
441.1.40 by Dan Garner
Document Models with Swagger
238
239
    /**
240
     * @SWG\Property(description="A flag indicating that a screen shot should be taken by the Player")
241
     * @var int
242
     */
375 by Dan Garner
Status Dashboard implemented.
243
    public $screenShotRequested;
441.1.40 by Dan Garner
Document Models with Swagger
244
245
    /**
246
     * @SWG\Property(description="The number of bytes of storage available on the device.")
247
     * @var int
248
     */
375 by Dan Garner
Status Dashboard implemented.
249
    public $storageAvailableSpace;
441.1.40 by Dan Garner
Document Models with Swagger
250
251
    /**
252
     * @SWG\Property(description="The number of bytes of storage in total on the device")
253
     * @var int
254
     */
375 by Dan Garner
Status Dashboard implemented.
255
    public $storageTotalSpace;
256
441.1.40 by Dan Garner
Document Models with Swagger
257
    /**
258
     * @SWG\Property(description="The ID of the Display Group for this Device")
259
     * @var int
260
     */
375 by Dan Garner
Status Dashboard implemented.
261
    public $displayGroupId;
441.1.40 by Dan Garner
Document Models with Swagger
262
263
    /**
264
     * @SWG\Property(description="The current layout")
265
     * @var string
266
     */
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
267
    public $currentLayout;
441.1.40 by Dan Garner
Document Models with Swagger
268
269
    /**
270
     * @SWG\Property(description="The default layout")
271
     * @var string
272
     */
428.1.1 by Dan Garner
Display Group Assign files.
273
    public $defaultLayout;
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
274
441.1.40 by Dan Garner
Document Models with Swagger
275
    /**
276
     * @SWG\Property(description="The Display Groups this Display belongs to")
277
     * @var DisplayGroup[]
278
     */
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
279
    public $displayGroups = [];
280
454.2.1 by Dan Garner
Require XMR, XMR network address setting, display pubKey and channel setting, collect now route
281
    /**
282
     * @SWG\Property(description="The Player Subscription Channel")
283
     * @var string
284
     */
285
    public $xmrChannel;
286
287
    /**
288
     * @SWG\Property(description="The Player Public Key")
289
     * @var string
290
     */
291
    public $xmrPubKey;
292
454.2.24 by Dan Garner
Hook collectNowAction into setting mediaInventoryStatus to incomplete.
293
    /**
454.1.188 by Dan Garner
Last command success should be true/false with 2 as unknown.
294
     * @SWG\Property(description="The last command success, 0 = failure, 1 = success, 2 = unknown")
454.1.181 by Dan Garner
Add last command success
295
     * @var int
296
     */
494.1.18 by Dan Garner
Fixed audio module insert in update step (needs double escaping)
297
    public $lastCommandSuccess = 0;
454.1.181 by Dan Garner
Add last command success
298
299
    /**
540.1.21 by Dan Garner
Add device name to the display table
300
     * @SWG\Property(description="The Device Name for the device hardware associated with this Display")
301
     * @var string
302
     */
303
    public $deviceName;
304
305
    /**
549 by Dan Garner
1.8.0 rc1 pack2 (#229)
306
     * @SWG\Property(description="The Display Timezone, or empty to use the CMS timezone")
307
     * @var string
308
     */
309
    public $timeZone;
310
311
    /**
454.1.179 by Dan Garner
Output commands in RegisterDisplay return.
312
     * Commands
313
     * @var array[Command]
314
     */
315
    private $commands = null;
316
542.1.10 by Dan Garner
Separate display notifications into their own service, streamline that process to limit IO churn. xibosignage/xibo#918
317
    public static $saveOptionsMinimum = ['validate' => false, 'audit' => false, 'triggerDynamicDisplayGroupAssessment' => false];
484.1.33 by Dan Garner
Fix cache expiry trigger on events in the always day part
318
484.2.18 by Dan Garner
Start work building class constructors for all dependencies.
319
    /**
484.2.23 by Dan Garner
DI down the stack for other Controllers
320
     * @var ConfigServiceInterface
321
     */
322
    private $config;
323
324
    /**
325
     * @var DisplayGroupFactory
326
     */
327
    private $displayGroupFactory;
328
329
    /**
330
     * @var DisplayProfileFactory
331
     */
332
    private $displayProfileFactory;
333
334
    /**
484.3.1 by Dan Garner
CDN implementation for XMDS.
335
     * @var DisplayFactory
336
     */
337
    private $displayFactory;
338
339
    /**
340
     * @var LayoutFactory
341
     */
342
    private $layoutFactory;
343
344
    /**
345
     * @var MediaFactory
346
     */
347
    private $mediaFactory;
348
349
    /**
350
     * @var ScheduleFactory
351
     */
352
    private $scheduleFactory;
353
354
    /**
484.2.18 by Dan Garner
Start work building class constructors for all dependencies.
355
     * Entity constructor.
356
     * @param StorageServiceInterface $store
357
     * @param LogServiceInterface $log
484.2.23 by Dan Garner
DI down the stack for other Controllers
358
     * @param ConfigServiceInterface $config
359
     * @param DisplayGroupFactory $displayGroupFactory
360
     * @param DisplayProfileFactory $displayProfileFactory
542.1.10 by Dan Garner
Separate display notifications into their own service, streamline that process to limit IO churn. xibosignage/xibo#918
361
     * @param DisplayFactory $displayFactory
484.2.18 by Dan Garner
Start work building class constructors for all dependencies.
362
     */
542.1.10 by Dan Garner
Separate display notifications into their own service, streamline that process to limit IO churn. xibosignage/xibo#918
363
    public function __construct($store, $log, $config, $displayGroupFactory, $displayProfileFactory, $displayFactory)
428.1.18 by Dan Garner
PHPUnit complete for Campaign (simple test). Also improved logging during debug effort.
364
    {
484.2.18 by Dan Garner
Start work building class constructors for all dependencies.
365
        $this->setCommonDependencies($store, $log);
428.1.18 by Dan Garner
PHPUnit complete for Campaign (simple test). Also improved logging during debug effort.
366
        $this->excludeProperty('mediaInventoryXml');
565.2.26 by Dan Garner
Permissions issues with a recent change - revert Display/DisplayGroup permission entity rename and correctly fix original problem of owner field being presented on Display permissions edit. Also fixed a general problem with permission inheritance when more than one permission for the same object exists.
367
        $this->setPermissionsClass('Xibo\Entity\DisplayGroup');
368
        $this->setCanChangeOwner(false);
484.2.23 by Dan Garner
DI down the stack for other Controllers
369
370
        $this->config = $config;
371
        $this->displayGroupFactory = $displayGroupFactory;
372
        $this->displayProfileFactory = $displayProfileFactory;
542.1.10 by Dan Garner
Separate display notifications into their own service, streamline that process to limit IO churn. xibosignage/xibo#918
373
        $this->displayFactory = $displayFactory;
535.1.1 by Dan Garner
Validate against incorrect lat/long
374
375
        // Initialise extra validation rules
376
        v::with('Xibo\\Validation\\Rules\\');
428.1.18 by Dan Garner
PHPUnit complete for Campaign (simple test). Also improved logging during debug effort.
377
    }
375 by Dan Garner
Status Dashboard implemented.
378
484.2.23 by Dan Garner
DI down the stack for other Controllers
379
    /**
484.3.1 by Dan Garner
CDN implementation for XMDS.
380
     * Set child object dependencies
381
     * @param LayoutFactory $layoutFactory
382
     * @param MediaFactory $mediaFactory
383
     * @param ScheduleFactory $scheduleFactory
384
     * @return $this
385
     */
542.1.10 by Dan Garner
Separate display notifications into their own service, streamline that process to limit IO churn. xibosignage/xibo#918
386
    public function setChildObjectDependencies($layoutFactory, $mediaFactory, $scheduleFactory)
484.3.1 by Dan Garner
CDN implementation for XMDS.
387
    {
388
        $this->layoutFactory = $layoutFactory;
389
        $this->mediaFactory = $mediaFactory;
390
        $this->scheduleFactory = $scheduleFactory;
391
        return $this;
392
    }
393
394
    /**
484.2.23 by Dan Garner
DI down the stack for other Controllers
395
     * @return int
396
     */
375 by Dan Garner
Status Dashboard implemented.
397
    public function getId()
398
    {
549 by Dan Garner
1.8.0 rc1 pack2 (#229)
399
        return $this->displayGroupId;
375 by Dan Garner
Status Dashboard implemented.
400
    }
401
484.2.23 by Dan Garner
DI down the stack for other Controllers
402
    /**
403
     * @return int
404
     */
375 by Dan Garner
Status Dashboard implemented.
405
    public function getOwnerId()
406
    {
549 by Dan Garner
1.8.0 rc1 pack2 (#229)
407
        // No owner
408
        return 0;
375 by Dan Garner
Status Dashboard implemented.
409
    }
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
410
427.1.13 by Dan Garner
Part way through building Display/DisplayGroup entities.
411
    /**
484.1.30 by Dan Garner
XMDS Schedule Caching
412
     * Get the cache key
413
     * @return string
414
     */
542.1.10 by Dan Garner
Separate display notifications into their own service, streamline that process to limit IO churn. xibosignage/xibo#918
415
    public static function getCachePrefix()
416
    {
417
        return 'display/';
418
    }
419
420
    /**
421
     * Get the cache key
422
     * @return string
423
     */
484.1.30 by Dan Garner
XMDS Schedule Caching
424
    public function getCacheKey()
425
    {
549 by Dan Garner
1.8.0 rc1 pack2 (#229)
426
        return self::getCachePrefix() . $this->displayId;
484.1.30 by Dan Garner
XMDS Schedule Caching
427
    }
428
429
    /**
507.1.4 by Dan Garner
Display auditing based on date rather than flag
430
     * Is this display auditing?
431
     * return bool
432
     */
433
    public function isAuditing()
434
    {
435
        $this->getLog()->debug('Testing whether this display is auditing. %d vs %d.', $this->auditingUntil, time());
436
        // Test $this->auditingUntil against the current date.
437
        return ($this->auditingUntil >= time());
438
    }
439
440
    /**
427.1.13 by Dan Garner
Part way through building Display/DisplayGroup entities.
441
     * Set the Media Status to Incomplete
442
     */
542.1.10 by Dan Garner
Separate display notifications into their own service, streamline that process to limit IO churn. xibosignage/xibo#918
443
    public function notify()
444
    {
445
        $this->getLog()->debug($this->display . ' requests notify');
446
447
        $this->displayFactory->getDisplayNotifyService()->collectNow()->notifyByDisplayId($this->displayId);
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
448
    }
449
427.1.13 by Dan Garner
Part way through building Display/DisplayGroup entities.
450
    /**
451
     * Validate the Object as it stands
452
     */
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
453
    public function validate()
454
    {
455
        if (!v::string()->notEmpty()->validate($this->display))
540.1.5 by Dan Garner
Update InvalidArgumentExceptions to use XiboException and switch to DuplicateEntity exception where appropriate
456
            throw new InvalidArgumentException(__('Can not have a display without a name'), 'name');
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
457
572.2.37 by Dan Garner
Validate hardwarekey (license) isn't empty
458
        if (!v::string()->notEmpty()->validate($this->license))
459
            throw new InvalidArgumentException(__('Can not have a display without a hardware key'), 'license');
460
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
461
        if ($this->wakeOnLanEnabled == 1 && $this->wakeOnLanTime == '')
540.1.5 by Dan Garner
Update InvalidArgumentExceptions to use XiboException and switch to DuplicateEntity exception where appropriate
462
            throw new InvalidArgumentException(__('Wake on Lan is enabled, but you have not specified a time to wake the display'), 'wakeonlan');
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
463
464
        // Check the number of licensed displays
484.2.23 by Dan Garner
DI down the stack for other Controllers
465
        $maxDisplays = $this->config->GetSetting('MAX_LICENSED_DISPLAYS');
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
466
487.1.50 by Dan Garner
Fix display licence slot validation.
467
        if ($maxDisplays > 0) {
468
            $this->getLog()->debug('Testing licensed displays against %d maximum. Currently Licenced = %d, Licenced = %d.', $maxDisplays, $this->currentlyLicensed, $this->licensed);
469
470
            if ($this->currentlyLicensed != $this->licensed && $this->licensed == 1) {
471
                $countLicensed = $this->getStore()->select('SELECT COUNT(DisplayID) AS CountLicensed FROM display WHERE licensed = 1', []);
472
473
                $this->getLog()->debug('There are %d licenced displays and we the maximum is %d', $countLicensed[0]['CountLicensed'], $maxDisplays);
474
475
                if (intval($countLicensed[0]['CountLicensed']) + 1 > $maxDisplays)
540.1.5 by Dan Garner
Update InvalidArgumentExceptions to use XiboException and switch to DuplicateEntity exception where appropriate
476
                    throw new InvalidArgumentException(sprintf(__('You have exceeded your maximum number of licensed displays. %d'), $maxDisplays), 'maxDisplays');
487.1.50 by Dan Garner
Fix display licence slot validation.
477
            }
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
478
        }
479
480
        // Broadcast Address
437.1.6 by Dan Garner
Start Soap4, hook in logging.
481
        if ($this->broadCastAddress != '' && !v::ip()->validate($this->broadCastAddress))
540.1.5 by Dan Garner
Update InvalidArgumentExceptions to use XiboException and switch to DuplicateEntity exception where appropriate
482
            throw new InvalidArgumentException(__('BroadCast Address is not a valid IP Address'), 'broadCastAddress');
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
483
484
        // CIDR
437.1.6 by Dan Garner
Start Soap4, hook in logging.
485
        if (!empty($this->cidr) && !v::numeric()->between(0, 32)->validate($this->cidr))
540.1.5 by Dan Garner
Update InvalidArgumentExceptions to use XiboException and switch to DuplicateEntity exception where appropriate
486
            throw new InvalidArgumentException(__('CIDR subnet mask is not a number within the range of 0 to 32.'), 'cidr');
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
487
488
        // secureOn
489
        if ($this->secureOn != '') {
490
            $this->secureOn = strtoupper($this->secureOn);
491
            $this->secureOn = str_replace(":", "-", $this->secureOn);
492
493
            if ((!preg_match("/([A-F0-9]{2}[-]){5}([0-9A-F]){2}/", $this->secureOn)) || (strlen($this->secureOn) != 17))
540.1.5 by Dan Garner
Update InvalidArgumentExceptions to use XiboException and switch to DuplicateEntity exception where appropriate
494
                throw new InvalidArgumentException(__('Pattern of secureOn-password is not "xx-xx-xx-xx-xx-xx" (x = digit or CAPITAL letter)'), 'secureOn');
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
495
        }
496
497
        // Mac Address Changes
547 by Dan Garner
Bugfix/1.8.0 pack1 (#227)
498
        if ($this->hasPropertyChanged('macAddress')) {
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
499
            // Mac address change detected
500
            $this->numberOfMacAddressChanges++;
501
            $this->lastChanged = time();
502
        }
535.1.1 by Dan Garner
Validate against incorrect lat/long
503
504
        // Lat/Long
505
        if (!empty($this->longitude) && !v::longitude()->validate($this->longitude))
540.1.5 by Dan Garner
Update InvalidArgumentExceptions to use XiboException and switch to DuplicateEntity exception where appropriate
506
            throw new InvalidArgumentException(__('The longitude entered is not valid.'), 'longitude');
535.1.1 by Dan Garner
Validate against incorrect lat/long
507
508
        if (!empty($this->latitude) && !v::latitude()->validate($this->latitude))
540.1.5 by Dan Garner
Update InvalidArgumentExceptions to use XiboException and switch to DuplicateEntity exception where appropriate
509
            throw new InvalidArgumentException(__('The latitude entered is not valid.'), 'latitude');
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
510
    }
511
427.1.13 by Dan Garner
Part way through building Display/DisplayGroup entities.
512
    /**
513
     * Load
514
     */
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
515
    public function load()
516
    {
427.1.13 by Dan Garner
Part way through building Display/DisplayGroup entities.
517
        // Load this displays group membership
484.2.23 by Dan Garner
DI down the stack for other Controllers
518
        $this->displayGroups = $this->displayGroupFactory->getByDisplayId($this->displayId);
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
519
    }
520
427.1.13 by Dan Garner
Part way through building Display/DisplayGroup entities.
521
    /**
547 by Dan Garner
Bugfix/1.8.0 pack1 (#227)
522
     * Save the media inventory status
523
     */
524
    public function saveMediaInventoryStatus()
525
    {
526
        try {
527
            $this->getStore()->updateWithDeadlockLoop('UPDATE `display` SET mediaInventoryStatus = :mediaInventoryStatus WHERE displayId = :displayId', [
528
                'mediaInventoryStatus' => $this->mediaInventoryStatus,
529
                'displayId' => $this->displayId
530
            ]);
531
        } catch (DeadlockException $deadlockException) {
532
            $this->getLog()->error('Media Inventory Status save failed due to deadlock');
533
        }
534
    }
535
536
    /**
427.1.13 by Dan Garner
Part way through building Display/DisplayGroup entities.
537
     * Save
454.1.142 by Dan Garner
Auditing for displays in more sensible places (prevent build up of auditing)
538
     * @param array $options
427.1.13 by Dan Garner
Part way through building Display/DisplayGroup entities.
539
     */
454.1.142 by Dan Garner
Auditing for displays in more sensible places (prevent build up of auditing)
540
    public function save($options = [])
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
541
    {
454.1.142 by Dan Garner
Auditing for displays in more sensible places (prevent build up of auditing)
542
        $options = array_merge([
543
            'validate' => true,
454.4.129 by Dan Garner
Notification on Display Add/Edit and on Membership change for Display Group
544
            'audit' => true,
542.1.10 by Dan Garner
Separate display notifications into their own service, streamline that process to limit IO churn. xibosignage/xibo#918
545
            'triggerDynamicDisplayGroupAssessment' => false
454.1.142 by Dan Garner
Auditing for displays in more sensible places (prevent build up of auditing)
546
        ], $options);
547
548
        if ($options['validate'])
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
549
            $this->validate();
550
551
        if ($this->displayId == null || $this->displayId == 0)
552
            $this->add();
553
        else
554
            $this->edit();
555
454.1.142 by Dan Garner
Auditing for displays in more sensible places (prevent build up of auditing)
556
        if ($options['audit'])
551.1.9 by Dan Garner
Improvements to audit log recording
557
            $this->getLog()->audit('Display', $this->displayId, 'Display Saved', $this->getChangedProperties());
454.2.24 by Dan Garner
Hook collectNowAction into setting mediaInventoryStatus to incomplete.
558
454.4.129 by Dan Garner
Notification on Display Add/Edit and on Membership change for Display Group
559
        // Trigger an update of all dynamic DisplayGroups
560
        if ($options['triggerDynamicDisplayGroupAssessment']) {
484.2.23 by Dan Garner
DI down the stack for other Controllers
561
            foreach ($this->displayGroupFactory->getByIsDynamic(1) as $group) {
454.4.129 by Dan Garner
Notification on Display Add/Edit and on Membership change for Display Group
562
                /* @var DisplayGroup $group */
484.3.1 by Dan Garner
CDN implementation for XMDS.
563
                $group->setChildObjectDependencies($this->displayFactory, $this->layoutFactory, $this->mediaFactory, $this->scheduleFactory);
454.4.129 by Dan Garner
Notification on Display Add/Edit and on Membership change for Display Group
564
                $group->save(['validate' => false, 'saveGroup' => false, 'manageDisplayLinks' => true]);
565
            }
566
        }
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
567
    }
568
427.1.13 by Dan Garner
Part way through building Display/DisplayGroup entities.
569
    /**
570
     * Delete
571
     * @throws \Xibo\Exception\NotFoundException
572
     */
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
573
    public function delete()
574
    {
575
        $this->load();
576
427.1.13 by Dan Garner
Part way through building Display/DisplayGroup entities.
577
        // Remove our display from any groups it is assigned to
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
578
        foreach ($this->displayGroups as $displayGroup) {
579
            /* @var DisplayGroup $displayGroup */
487.1.65 by Dan Garner
Fixes for scheduling view in non-local timezone (where client is in a different zone to CMS). Fixes for scheduling commands. And for deleting a display.
580
            $displayGroup->setChildObjectDependencies($this->displayFactory, $this->layoutFactory, $this->mediaFactory, $this->scheduleFactory);
437.1.6 by Dan Garner
Start Soap4, hook in logging.
581
            $displayGroup->unassignDisplay($this);
454.4.129 by Dan Garner
Notification on Display Add/Edit and on Membership change for Display Group
582
            $displayGroup->save(['validate' => false]);
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
583
        }
584
585
        // Delete our display specific group
484.2.23 by Dan Garner
DI down the stack for other Controllers
586
        $displayGroup = $this->displayGroupFactory->getById($this->displayGroupId);
487.1.65 by Dan Garner
Fixes for scheduling view in non-local timezone (where client is in a different zone to CMS). Fixes for scheduling commands. And for deleting a display.
587
        $displayGroup->setChildObjectDependencies($this->displayFactory, $this->layoutFactory, $this->mediaFactory, $this->scheduleFactory);
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
588
        $displayGroup->delete();
589
590
        // Delete the display
484.2.4 by Dan Garner
Help service. Log service. Store service.
591
        $this->getStore()->update('DELETE FROM `blacklist` WHERE displayId = :displayId', ['displayId' => $this->displayId]);
592
        $this->getStore()->update('DELETE FROM `display` WHERE displayId = :displayId', ['displayId' => $this->displayId]);
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
593
484.2.4 by Dan Garner
Help service. Log service. Store service.
594
        $this->getLog()->audit('Display', $this->displayId, 'Display Deleted', ['displayId' => $this->displayId]);
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
595
    }
596
597
    private function add()
598
    {
484.2.4 by Dan Garner
Help service. Log service. Store service.
599
        $this->displayId = $this->getStore()->insert('
520.1.1 by Dan Garner
DisplayTest Structure and fixes for WOL route. Filter by hardwareKey in GET /display
600
            INSERT INTO display (display, auditingUntil, defaultlayoutid, license, licensed, inc_schedule, email_alert, alert_timeout, xmrChannel, xmrPubKey, lastCommandSuccess, macAddress)
601
              VALUES (:display, :auditingUntil, :defaultlayoutid, :license, :licensed, :inc_schedule, :email_alert, :alert_timeout, :xmrChannel, :xmrPubKey, :lastCommandSuccess, :macAddress)
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
602
        ', [
603
            'display' => $this->display,
507.1.4 by Dan Garner
Display auditing based on date rather than flag
604
            'auditingUntil' => 0,
437.1.7 by Dan Garner
1st pass over Soap4
605
            'defaultlayoutid' => $this->defaultLayoutId,
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
606
            'license' => $this->license,
607
            'licensed' => 0,
608
            'inc_schedule' => 0,
609
            'email_alert' => 0,
454.2.2 by Dan Garner
XMDS version to V5, XMR public and private addresses. xmr channel and pubkey on display.
610
            'alert_timeout' => 0,
611
            'xmrChannel' => $this->xmrChannel,
494.1.18 by Dan Garner
Fixed audio module insert in update step (needs double escaping)
612
            'xmrPubKey' => $this->xmrPubKey,
520.1.1 by Dan Garner
DisplayTest Structure and fixes for WOL route. Filter by hardwareKey in GET /display
613
            'lastCommandSuccess' => $this->lastCommandSuccess,
614
            'macAddress' => $this->macAddress
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
615
        ]);
616
484.2.23 by Dan Garner
DI down the stack for other Controllers
617
        $displayGroup = $this->displayGroupFactory->createEmpty();
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
618
        $displayGroup->displayGroup = $this->display;
565 by Dan Garner
1.8.0-bugfix-pack2 (#249)
619
        $displayGroup->setDisplaySpecificDisplay($this);
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
620
        $displayGroup->save();
621
    }
622
623
    private function edit()
624
    {
484.2.4 by Dan Garner
Help service. Log service. Store service.
625
        $this->getStore()->update('
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
626
            UPDATE display
627
                SET display = :display,
628
                    defaultlayoutid = :defaultLayoutId,
629
                    inc_schedule = :incSchedule,
447.1.33 by Dan Garner
Fixed problem with Display Edit form
630
                    license = :license,
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
631
                    licensed = :licensed,
507.1.4 by Dan Garner
Display auditing based on date rather than flag
632
                    auditingUntil = :auditingUntil,
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
633
                    email_alert = :emailAlert,
634
                    alert_timeout = :alertTimeout,
635
                    WakeOnLan = :wakeOnLanEnabled,
636
                    WakeOnLanTime = :wakeOnLanTime,
637
                    BroadCastAddress = :broadCastAddress,
638
                    SecureOn = :secureOn,
639
                    Cidr = :cidr,
640
                    GeoLocation = POINT(:latitude, :longitude),
641
                    displayprofileid = :displayProfileId,
642
                    lastaccessed = :lastAccessed,
643
                    loggedin = :loggedIn,
644
                    ClientAddress = :clientAddress,
645
                    MediaInventoryStatus = :mediaInventoryStatus,
646
                    client_type = :clientType,
647
                    client_version = :clientVersion,
648
                    client_code = :clientCode,
649
                    MacAddress = :macAddress,
650
                    LastChanged = :lastChanged,
651
                    NumberOfMacAddressChanges = :numberOfMacAddressChanges,
652
                    screenShotRequested = :screenShotRequested,
653
                    storageAvailableSpace = :storageAvailableSpace,
454.2.1 by Dan Garner
Require XMR, XMR network address setting, display pubKey and channel setting, collect now route
654
                    storageTotalSpace = :storageTotalSpace,
655
                    xmrChannel = :xmrChannel,
454.1.181 by Dan Garner
Add last command success
656
                    xmrPubKey = :xmrPubKey,
502.1.6 by Dan Garner
Fix for setting version information.
657
                    `lastCommandSuccess` = :lastCommandSuccess,
540.1.21 by Dan Garner
Add device name to the display table
658
                    `version_instructions` = :versionInstructions,
549 by Dan Garner
1.8.0 rc1 pack2 (#229)
659
                    `deviceName` = :deviceName,
660
                    `timeZone` = :timeZone
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
661
             WHERE displayid = :displayId
662
        ', [
663
            'display' => $this->display,
664
            'defaultLayoutId' => $this->defaultLayoutId,
665
            'incSchedule' => $this->incSchedule,
447.1.33 by Dan Garner
Fixed problem with Display Edit form
666
            'license' => $this->license,
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
667
            'licensed' => $this->licensed,
507.1.4 by Dan Garner
Display auditing based on date rather than flag
668
            'auditingUntil' => ($this->auditingUntil == null) ? 0 : $this->auditingUntil,
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
669
            'emailAlert' => $this->emailAlert,
670
            'alertTimeout' => $this->alertTimeout,
671
            'wakeOnLanEnabled' => $this->wakeOnLanEnabled,
672
            'wakeOnLanTime' => $this->wakeOnLanTime,
673
            'broadCastAddress' => $this->broadCastAddress,
674
            'secureOn' => $this->secureOn,
675
            'cidr' => $this->cidr,
676
            'latitude' => $this->latitude,
677
            'longitude' => $this->longitude,
678
            'displayProfileId' => $this->displayProfileId,
679
            'lastAccessed' => $this->lastAccessed,
680
            'loggedIn' => $this->loggedIn,
681
            'clientAddress' => $this->clientAddress,
682
            'mediaInventoryStatus' => $this->mediaInventoryStatus,
683
            'clientType' => $this->clientType,
684
            'clientVersion' => $this->clientVersion,
685
            'clientCode' => $this->clientCode,
686
            'macAddress' => $this->macAddress,
687
            'lastChanged' => $this->lastChanged,
688
            'numberOfMacAddressChanges' => $this->numberOfMacAddressChanges,
689
            'screenShotRequested' => $this->screenShotRequested,
690
            'storageAvailableSpace' => $this->storageAvailableSpace,
691
            'storageTotalSpace' => $this->storageTotalSpace,
454.2.1 by Dan Garner
Require XMR, XMR network address setting, display pubKey and channel setting, collect now route
692
            'xmrChannel' => $this->xmrChannel,
693
            'xmrPubKey' => $this->xmrPubKey,
454.1.181 by Dan Garner
Add last command success
694
            'lastCommandSuccess' => $this->lastCommandSuccess,
502.1.6 by Dan Garner
Fix for setting version information.
695
            'versionInstructions' => $this->versionInstructions,
540.1.21 by Dan Garner
Add device name to the display table
696
            'deviceName' => $this->deviceName,
549 by Dan Garner
1.8.0 rc1 pack2 (#229)
697
            'timeZone' => $this->timeZone,
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
698
            'displayId' => $this->displayId
699
        ]);
700
701
        // Maintain the Display Group
540.1.16 by Dan Garner
Prevented display specific displaygroup from being updated unnecessarily.
702
        if ($this->hasPropertyChanged('display') || $this->hasPropertyChanged('description')) {
703
            $this->getLog()->debug('Display specific DisplayGroup properties need updating');
704
705
            $displayGroup = $this->displayGroupFactory->getById($this->displayGroupId);
706
            $displayGroup->displayGroup = $this->display;
707
            $displayGroup->description = $this->description;
708
            $displayGroup->save(DisplayGroup::$saveOptionsMinimum);
709
        }
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
710
    }
711
712
    /**
713
     * Get the Settings Profile for this Display
714
     * @return array
715
     */
716
    public function getSettings()
717
    {
718
        return $this->setConfig();
719
    }
720
484.2.23 by Dan Garner
DI down the stack for other Controllers
721
    /**
722
     * @return array
723
     */
454.1.179 by Dan Garner
Output commands in RegisterDisplay return.
724
    public function getCommands()
725
    {
726
        if ($this->commands == null) {
727
            $this->setConfig();
728
        }
729
730
        return $this->commands;
731
    }
732
427.1.13 by Dan Garner
Part way through building Display/DisplayGroup entities.
733
    /**
734
     * Get a particular setting
735
     * @param string $key
736
     * @param mixed $default
737
     * @return mixed
738
     */
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
739
    public function getSetting($key, $default)
740
    {
741
        $this->setConfig();
742
743
        // Find
744
        $return = $default;
745
        foreach($this->_config as $row) {
746
            if ($row['name'] == $key || $row['name'] == ucfirst($key)) {
747
                $return = $row['value'];
748
                break;
749
            }
750
        }
751
752
        return $return;
753
    }
754
755
    /**
427.1.13 by Dan Garner
Part way through building Display/DisplayGroup entities.
756
     * Set the config array
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
757
     * @return array
758
     */
759
    private function setConfig()
760
    {
761
        if ($this->_config == null) {
762
529.1.9 by Dan Garner
Fix case where a display has a deleted display profile assigned
763
            try {
764
                if ($this->displayProfileId == 0) {
552.3.4 by Dan Garner
Handle case for unknown client type (0 settings).
765
                    // Load the default profile
766
                    $displayProfile = $this->displayProfileFactory->getDefaultByType($this->clientType);
529.1.9 by Dan Garner
Fix case where a display has a deleted display profile assigned
767
                } else {
768
                    // Load the specified profile
769
                    $displayProfile = $this->displayProfileFactory->getById($this->displayProfileId);
770
                }
771
            } catch (NotFoundException $e) {
552.3.4 by Dan Garner
Handle case for unknown client type (0 settings).
772
                $this->getLog()->error('Cannot get display profile');
773
                $this->getLog()->debug($e->getTraceAsString());
774
553.1.11 by Dan Garner
DisplayProfileLoaded event to modify settings from middleware - or provide settings for a custom client type.
775
                $displayProfile = $this->displayProfileFactory->getUnknownProfile($this->clientType);
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
776
            }
777
484.2.15 by Dan Garner
Factory and Module services.
778
            $this->_config = $displayProfile->getProfileConfig();
454.1.179 by Dan Garner
Output commands in RegisterDisplay return.
779
            $this->commands = $displayProfile->commands;
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
780
        }
781
782
        return $this->_config;
783
    }
560.2.19 by Dan Garner
Store the current layout id in cache.
784
785
    /**
786
     * @param PoolInterface $pool
787
     */
788
    public function setCurrentLayoutId($pool)
789
    {
790
        $item = $pool->getItem($this->getCacheKey() . '/currentLayoutId');
791
792
        $data = $item->get();
793
794
        if ($item->isHit()) {
795
            $this->currentLayoutId = $data;
796
797
            try {
798
                $this->currentLayout = $this->layoutFactory->getById($this->currentLayoutId)->layout;
799
            }
800
            catch (NotFoundException $notFoundException) {
801
                // This is ok
802
            }
803
        } else {
804
            $this->getLog()->debug('Cache miss for setCurrentLayoutId on display ' . $this->display);
805
        }
806
    }
427.1.12 by Dan Garner
Removed Display Data Class and started to build Entities
807
}