3
* Xibo - Digital Signage - http://www.xibo.org.uk
4
* Copyright (C) 2009-2013 Daniel Garner
6
* This file is part of Xibo.
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
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.
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/>.
21
defined('XIBO') or die("Sorry, you are not allowed to directly access this page.<br /> Please press the back button in your browser.");
23
class Display extends Data {
31
public $defaultLayoutId;
34
public $currentLicensed;
40
public $clientAddress;
41
public $mediaInventoryStatus;
42
public $mediaInventoryXml;
45
public $numberOfMacAddressChanges;
46
public $lastWakeOnLanCommandSent;
47
public $wakeOnLanEnabled;
48
public $wakeOnLanTime;
49
public $broadCastAddress;
54
public $versionInstructions;
56
public $clientVersion;
58
public $displayProfileId;
59
public $currentLayoutId;
60
public $screenShotRequested;
62
public $displayGroupId;
65
public function Load() {
67
$dbh = PDOConnect::init();
69
$sth = $dbh->prepare('
70
SELECT display.*, displaygroup.displaygroupid, displaygroup.description, X(display.GeoLocation) AS Latitude, Y(display.GeoLocation) AS Longitude
72
INNER JOIN `lkdisplaydg`
73
ON lkdisplaydg.displayid = display.displayId
74
INNER JOIN `displaygroup`
75
ON displaygroup.displaygroupid = lkdisplaydg.displaygroupid
76
AND isdisplayspecific = 1
77
WHERE display.displayid = :display_id');
79
$sth->execute(array('display_id' => $this->displayId));
81
if (!$row = $sth->fetch())
82
$this->ThrowError(25004, __('Cannot find display record'));
84
$this->isAuditing = Kit::ValidateParam($row['isAuditing'], _INT);
85
$this->display = Kit::ValidateParam($row['display'], _STRING);
86
$this->description = Kit::ValidateParam($row['description'], _STRING);
87
$this->defaultLayoutId = Kit::ValidateParam($row['defaultlayoutid'], _INT);
88
$this->license = Kit::ValidateParam($row['license'], _STRING);
89
$this->licensed = Kit::ValidateParam($row['licensed'], _INT);
90
$this->loggedIn = Kit::ValidateParam($row['loggedin'], _INT);
91
$this->lastAccessed = Kit::ValidateParam($row['lastaccessed'], _INT);
92
$this->incSchedule = Kit::ValidateParam($row['inc_schedule'], _INT);
93
$this->emailAlert = Kit::ValidateParam($row['email_alert'], _INT);
94
$this->alertTimeout = Kit::ValidateParam($row['alert_timeout'], _INT);
95
$this->clientAddress = Kit::ValidateParam($row['ClientAddress'], _STRING);
96
$this->mediaInventoryStatus = Kit::ValidateParam($row['MediaInventoryStatus'], _INT);
97
$this->mediaInventoryXml = Kit::ValidateParam($row['MediaInventoryXml'], _HTMLSTRING);
98
$this->macAddress = Kit::ValidateParam($row['MacAddress'], _STRING);
99
$this->lastChanged = Kit::ValidateParam($row['LastChanged'], _INT);
100
$this->numberOfMacAddressChanges = Kit::ValidateParam($row['NumberOfMacAddressChanges'], _INT);
101
$this->lastWakeOnLanCommandSent = Kit::ValidateParam($row['LastWakeOnLanCommandSent'], _INT);
102
$this->wakeOnLanEnabled = Kit::ValidateParam($row['WakeOnLan'], _INT);
103
$this->wakeOnLanTime = Kit::ValidateParam($row['WakeOnLanTime'], _STRING);
104
$this->broadCastAddress = Kit::ValidateParam($row['BroadCastAddress'], _STRING);
105
$this->secureOn = Kit::ValidateParam($row['SecureOn'], _STRING);
106
$this->cidr = Kit::ValidateParam($row['Cidr'], _STRING);
107
$this->latitude = Kit::ValidateParam($row['Latitude'], _DOUBLE);
108
$this->longitude = Kit::ValidateParam($row['Longitude'], _DOUBLE);
109
$this->versionInstructions = Kit::ValidateParam($row['version_instructions'], _STRING);
110
$this->clientType = Kit::ValidateParam($row['client_type'], _STRING);
111
$this->clientVersion = Kit::ValidateParam($row['client_version'], _STRING);
112
$this->clientCode = Kit::ValidateParam($row['client_code'], _INT);
113
$this->displayProfileId = Kit::ValidateParam($row['displayprofileid'], _INT);
114
$this->currentLayoutId = Kit::ValidateParam($row['currentLayoutId'], _INT);
115
$this->screenShotRequested = Kit::ValidateParam($row['screenShotRequested'], _INT);
117
$this->displayGroupId = Kit::ValidateParam($row['displaygroupid'], _INT);
119
// Store the current licensed flag, in case we are changing it and need to check it.
120
$this->currentLicensed = $this->licensed;
122
$this->loaded = true;
126
catch (Exception $e) {
128
Debug::LogEntry('error', $e->getMessage());
130
if (!$this->IsError())
131
$this->SetError(1, __('Unknown Error'));
140
* @param $display Object
141
* @param $isAuditing Object
142
* @param $defaultLayoutID Object
143
* @param $license Object
144
* @param $licensed Object
145
* @param $incSchedule Object
147
public function Add($display, $isAuditing, $defaultLayoutID, $license, $licensed, $incSchedule) {
148
Debug::LogEntry('audit', 'IN', get_class(), __FUNCTION__);
151
$dbh = PDOConnect::init();
155
$SQL .= "INSERT INTO display (display, isAuditing, defaultlayoutid, license, licensed, inc_schedule, email_alert, alert_timeout) ";
156
$SQL .= " VALUES (:display, :isauditing, :defaultlayoutid, :license, :licensed, :inc_schedule, :email_alert, :alert_timeout) ";
158
$sth = $dbh->prepare($SQL);
160
'display' => $display,
162
'defaultlayoutid' => 1,
163
'license' => $license,
170
// Get the ID of the inserted record
171
$displayId = $dbh->lastInsertId();
173
// Also want to add the DisplayGroup associated with this Display.
174
$displayGroupObject = new DisplayGroup($this->db);
176
if (!$displayGroupId = $displayGroupObject->Add($display, 1, ''))
177
$this->ThrowError(25001, __('Could not add a display group for the new display.'));
179
// Link the Two together
180
if (!$displayGroupObject->Link($displayGroupId, $displayId))
181
$this->ThrowError(25001, __('Could not link the new display with its group.'));
183
Debug::LogEntry('audit', 'OUT', 'Display', 'Add');
187
catch (Exception $e) {
189
Debug::LogEntry('error', $e->getMessage());
191
if (!$this->IsError())
192
$this->SetError(25000, __('Could not add display'));
201
* @param $displayID Object
202
* @param $isAuditing Object
203
* @param $defaultLayoutID Object
204
* @param $licensed Object
205
* @param $incSchedule Object
207
public function Edit() {
208
Debug::LogEntry('audit', 'IN', get_class(), __FUNCTION__);
211
if ($this->display == '')
212
return $this->SetError(__("Can not have a display without a name"));
214
if ($this->wakeOnLanEnabled == 1 && $this->wakeOnLanTime == '')
215
return $this->SetError(__('Wake on Lan is enabled, but you have not specified a time to wake the display'));
218
$dbh = PDOConnect::init();
220
// Check the number of licensed displays
221
$maxDisplays = Config::GetSetting('MAX_LICENSED_DISPLAYS');
223
if ($maxDisplays > 0) {
224
// See if this is a license switch
225
// Has the licence flag toggled?
226
if ($this->currentLicensed != $this->licensed && $this->licensed == 1)
228
// License change - test number of licensed displays.
229
$sth = $dbh->prepare('SELECT COUNT(DisplayID) AS CountLicensed FROM display WHERE licensed = 1');
232
if (!$row = $sth->fetch())
233
$this->ThrowError(1, __('Unable to get count of licensed displays.'));
235
$count = Kit::ValidateParam($row['CountLicensed'], _INT);
239
if ($count + 1 > $maxDisplays)
240
$this->ThrowError(25000, sprintf(__('You have exceeded your maximum number of licensed displays. %d'), $maxDisplays));
244
// Validate some parameters
246
// Fill $addr with client's IP address, if $addr is empty
247
if ($this->broadCastAddress != '')
249
// Resolve broadcast address
250
// same as (but easier than): preg_match("/\b(([01]?\d?\d|2[0-4]\d|25[0-5])\.){3}([01]?\d?\d|2[0-4]\d|25[0-5])\b/",$addr)
251
if (!filter_var($this->broadCastAddress, FILTER_VALIDATE_IP))
252
$this->ThrowError(25015, __('BroadCast Address is not a valid IP Address'));
255
// Check whether $cidr is valid
256
if ($this->cidr != '')
258
if ((!is_numeric($this->cidr)) || ($this->cidr < 0) || ($this->cidr > 32))
259
$this->ThrowError(25015, __('CIDR subnet mask is not a number within the range of 0 to 32.'));
262
// Check whether $secureOn is valid
263
if ($this->secureOn != '')
265
$this->secureOn = strtoupper($this->secureOn);
266
$this->secureOn = str_replace(":", "-", $this->secureOn);
268
if ((!preg_match("/([A-F0-9]{2}[-]){5}([0-9A-F]){2}/", $this->secureOn)) || (strlen($this->secureOn) != 17))
269
$this->ThrowError(25015, __('Pattern of secureOn-password is not "xx-xx-xx-xx-xx-xx" (x = digit or CAPITAL letter)'));
272
Debug::LogEntry('audit', 'Validation Complete and Passed', 'Display', 'Edit');
274
// Update the display record
277
SET display = :display,
278
defaultlayoutid = :defaultlayoutid,
279
inc_schedule = :incschedule,
280
licensed = :licensed,
281
isAuditing = :isauditing,
282
email_alert = :emailalert,
283
alert_timeout = :alerttimeout,
284
WakeOnLan = :wakeonlan,
285
WakeOnLanTime = :wakeonlantime,
286
BroadCastAddress = :broadcastaddress,
287
SecureOn = :secureon,
289
GeoLocation = POINT(:latitude, :longitude),
290
displayprofileid = :displayprofileid
291
WHERE displayid = :displayid';
293
$sth = $dbh->prepare($SQL);
295
'display' => $this->display,
296
'defaultlayoutid' => $this->defaultLayoutId,
297
'incschedule' => $this->incSchedule,
298
'licensed' => $this->licensed,
299
'isauditing' => $this->isAuditing,
300
'emailalert' => $this->emailAlert,
301
'alerttimeout' => $this->alertTimeout,
302
'wakeonlan' => $this->wakeOnLanEnabled,
303
'wakeonlantime' => $this->wakeOnLanTime,
304
'broadcastaddress' => $this->broadCastAddress,
305
'secureon' => $this->secureOn,
306
'cidr' => $this->cidr,
307
'latitude' => $this->latitude,
308
'longitude' => $this->longitude,
309
'displayprofileid' => $this->displayProfileId,
310
'displayid' => $this->displayId
313
Debug::LogEntry('audit', 'Display Edited', 'Display', 'Edit');
315
// Use a DisplayGroup to handle the default layout and displaygroup name for this display
316
Kit::ClassLoader('displaygroup');
317
$displayGroupObject = new DisplayGroup();
319
// Do we also want to update the linked Display Groups name (seeing as that is what we will be presenting to everyone)
320
if (!$displayGroupObject->Edit($this->displayGroupId, $this->display, $this->description)) {
321
$this->ThrowError(25002, __('Could not update this display with a new name.'));
324
Debug::LogEntry('audit', 'OUT', 'DisplayGroup', 'Edit');
328
catch (Exception $e) {
330
Debug::LogEntry('error', $e->getMessage(), get_class(), __FUNCTION__);
332
if (!$this->IsError())
333
$this->SetError(25000, __('Could not update display'));
342
* @param $displayID Object
344
public function Delete($displayID)
346
Debug::LogEntry('audit', 'IN', get_class(), __FUNCTION__);
349
$dbh = PDOConnect::init();
351
// Pass over to the DisplayGroup data class so that it can try and delete the
352
// display specific group first (it is that group which is linked to schedules)
353
$displayGroupObject = new DisplayGroup($this->db);
355
// Do we also want to update the linked Display Groups name (seeing as that is what we will be presenting to everyone)
356
if (!$displayGroupObject->DeleteDisplay($displayID))
357
$this->ThrowError($displayGroupObject->GetErrorMessage(), $displayGroupObject->GetErrorMessage());
359
// Delete the blacklist
360
$sth = $dbh->prepare('DELETE FROM blacklist WHERE DisplayID = :displayid');
362
'displayid' => $displayID
365
// Now we know the Display Group is gone - and so are any links
366
// delete the display
367
$sth = $dbh->prepare('DELETE FROM display WHERE DisplayID = :displayid');
369
'displayid' => $displayID
372
Debug::LogEntry('audit', 'OUT', 'Display', 'Delete');
376
catch (Exception $e) {
378
Debug::LogEntry('error', $e->getMessage());
380
if (!$this->IsError())
381
$this->SetError(25015,__('Unable to delete display record.'));
388
* Edits a Displays Name
390
* @param $license Object
391
* @param $display Object
393
public function EditDisplayName($license, $display)
395
Debug::LogEntry('audit', 'IN', get_class(), __FUNCTION__);
398
$dbh = PDOConnect::init();
400
// Update the display with its new name (using the licence as the key)
401
$sth = $dbh->prepare('UPDATE display SET display = :display WHERE license = :license');
403
'display' => $display,
404
'license' => $license
407
// Also need to update the display group name here.
408
$displayGroupObject = new DisplayGroup($this->db);
410
// Do we also want to update the linked Display Groups name (seeing as that is what we will be presenting to everyone)
411
if (!$displayGroupObject->EditDisplayGroup($displayID, $display))
412
$this->ThrowError(25015, __('Could not update this display with a new name.'));
414
Debug::LogEntry('audit', 'OUT', 'DisplayGroup', 'EditDisplayName');
418
catch (Exception $e) {
420
Debug::LogEntry('error', $e->getMessage());
422
if (!$this->IsError())
423
$this->SetError(25015,__('Unable to edit display record.'));
430
* Sets the information required on the display to indicate that it is still logged in
431
* @param int $displayId The Display ID
432
* @param array $status The Display Status
434
public function Touch($displayId, $status = array())
436
Debug::LogEntry('audit', 'IN', get_class(), __FUNCTION__);
439
$dbh = PDOConnect::init();
441
$this->displayId = $displayId;
444
// Update last accessed and set to be logged in
445
$this->lastAccessed = time();
448
// Pull in any of the optional parameters from the status array
449
$this->clientAddress = (Kit::GetParam('clientAddress', $status, _STRING) == '') ? $this->clientAddress : Kit::GetParam('clientAddress', $status, _STRING);
450
$this->mediaInventoryStatus = (Kit::GetParam('mediaInventoryStatus', $status, _INT) == 0) ? $this->mediaInventoryStatus : Kit::GetParam('mediaInventoryStatus', $status, _INT);
451
$this->mediaInventoryXml = (Kit::GetParam('mediaInventoryXml', $status, _HTMLSTRING) == '') ? $this->mediaInventoryXml : Kit::GetParam('mediaInventoryXml', $status, _HTMLSTRING);
452
$this->clientType = (Kit::GetParam('clientType', $status, _STRING) == '') ? $this->clientType : Kit::GetParam('clientType', $status, _STRING);
453
$this->clientVersion = (Kit::GetParam('clientVersion', $status, _STRING) == '') ? $this->clientVersion : Kit::GetParam('clientVersion', $status, _STRING);
454
$this->clientCode = (Kit::GetParam('clientCode', $status, _INT) == 0) ? $this->clientCode : Kit::GetParam('clientCode', $status, _INT);
455
$this->currentLayoutId = (Kit::GetParam('currentLayoutId', $status, _INT) == 0) ? $this->currentLayoutId : Kit::GetParam('currentLayoutId', $status, _INT);
456
$this->screenShotRequested = (Kit::GetParam('screenShotRequested', $status, _INT, -1) == -1) ? $this->screenShotRequested : Kit::GetParam('screenShotRequested', $status, _INT);
458
// Has the mac address changed
459
if (Kit::GetParam('macAddress', $status, _STRING) != '') {
460
if ($this->macAddress != Kit::GetParam('macAddress', $status, _STRING)) {
461
// Mac address change detected
462
$this->macAddress = Kit::GetParam('macAddress', $status, _STRING);
463
$this->numberOfMacAddressChanges++;
464
$this->lastChanged = time();
470
UPDATE display SET lastaccessed = :lastAccessed,
471
loggedin = :loggedIn,
472
ClientAddress = :clientAddress,
473
MediaInventoryStatus = :mediaInventoryStatus,
474
MediaInventoryXml = :mediaInventoryXml,
475
client_type = :clientType,
476
client_version = :clientVersion,
477
client_code = :clientCode,
478
MacAddress = :macAddress,
479
LastChanged = :lastChanged,
480
NumberOfMacAddressChanges = :numberOfMacAddressChanges,
481
currentLayoutId = :currentLayoutId,
482
screenShotRequested = :screenShotRequested
483
WHERE displayId = :displayId
486
// Update the display
487
$sth = $dbh->prepare($SQL);
489
'displayId' => $this->displayId,
490
'lastAccessed' => $this->lastAccessed,
491
'loggedIn' => $this->loggedIn,
492
'clientAddress' => $this->clientAddress,
493
'mediaInventoryStatus' => $this->mediaInventoryStatus,
494
'mediaInventoryXml' => $this->mediaInventoryXml,
495
'clientType' => $this->clientType,
496
'clientVersion' => $this->clientVersion,
497
'clientCode' => $this->clientCode,
498
'macAddress' => $this->macAddress,
499
'lastChanged' => $this->lastChanged,
500
'numberOfMacAddressChanges' => $this->numberOfMacAddressChanges,
501
'currentLayoutId' => $this->currentLayoutId,
502
'screenShotRequested' => $this->screenShotRequested
507
catch (Exception $e) {
508
Debug::LogEntry('error', $e->getMessage());
509
return $this->SetError(25002, __("Error updating this displays last accessed information."));
513
public function RequestScreenShot($displayId) {
514
return $this->Touch($displayId, array('screenShotRequested' => 1));
518
* Flags a display as being incomplete
519
* @param <type> $displayId
521
public function FlagIncomplete($displayId)
523
Debug::LogEntry('audit', sprintf('Flag DisplayID %d incomplete.', $displayId), 'display', 'NotifyDisplays');
526
$dbh = PDOConnect::init();
528
$sth = $dbh->prepare('UPDATE display SET MediaInventoryStatus = 3 WHERE displayID = :displayid');
530
'displayid' => $displayId
535
catch (Exception $e) {
536
Debug::LogEntry('error', $e->getMessage());
537
return $this->SetError(25004, 'Unable to Flag Display as incomplete');
542
* Notify displays of this campaign change
543
* @param <type> $layoutId
545
public function NotifyDisplays($campaignId)
547
Debug::LogEntry('audit', sprintf('Checking for Displays to refresh on Layout %d', $campaignId), 'display', 'NotifyDisplays');
550
$dbh = PDOConnect::init();
552
$currentdate = time();
553
$rfLookahead = Kit::ValidateParam(Config::GetSetting('REQUIRED_FILES_LOOKAHEAD'), _INT);
554
$rfLookahead = $currentdate + $rfLookahead;
556
// Which displays does a change to this layout effect?
557
$SQL = " SELECT DISTINCT display.DisplayID ";
558
$SQL .= " FROM schedule ";
559
$SQL .= " INNER JOIN schedule_detail ";
560
$SQL .= " ON schedule_detail.eventid = schedule.eventid ";
561
$SQL .= " INNER JOIN lkdisplaydg ";
562
$SQL .= " ON lkdisplaydg.DisplayGroupID = schedule_detail.DisplayGroupID ";
563
$SQL .= " INNER JOIN display ";
564
$SQL .= " ON lkdisplaydg.DisplayID = display.displayID ";
565
$SQL .= " WHERE schedule.CampaignID = :campaignid ";
566
$SQL .= " AND schedule_detail.FromDT < :fromdt AND schedule_detail.ToDT > :todt ";
568
$SQL .= " SELECT DISTINCT display.DisplayID ";
569
$SQL .= " FROM display ";
570
$SQL .= " INNER JOIN lkcampaignlayout ";
571
$SQL .= " ON lkcampaignlayout.LayoutID = display.DefaultLayoutID ";
572
$SQL .= " WHERE lkcampaignlayout.CampaignID = :campaignid";
574
$sth = $dbh->prepare($SQL);
576
'campaignid' => $campaignId,
577
'fromdt' => $rfLookahead,
578
'todt' => $currentdate - 3600
581
while ($row = $sth->fetch()) {
582
// Notify each display in turn
583
$displayId = Kit::ValidateParam($row['DisplayID'], _INT);
584
$this->FlagIncomplete($displayId);
587
catch (Exception $e) {
588
Debug::LogEntry('error', $e->getMessage());
590
if (!$this->IsError())
591
$this->SetError(25004, 'Unable to Flag Display as incomplete');
598
* Edits the default layout for a display
599
* @param <type> $displayId
600
* @param <type> $defaultLayoutId
603
public function EditDefaultLayout($displayId, $defaultLayoutId)
605
Debug::LogEntry('audit', 'IN', get_class(), __FUNCTION__);
608
$dbh = PDOConnect::init();
610
$sth = $dbh->prepare('UPDATE display SET defaultLayoutId = :defaultlayoutid WHERE displayID = :displayid');
612
'defaultlayoutid' => $defaultLayoutId,
613
'displayid' => $displayId
616
// Flag this display as not having all the content
617
$this->FlagIncomplete($displayId);
619
Debug::LogEntry('audit', 'OUT', 'Display', 'EditDefaultLayout');
623
catch (Exception $e) {
624
Debug::LogEntry('error', $e->getMessage());
625
return $this->SetError(25012, __('Error updating this displays default layout.'));
629
public function SetVersionInstructions($displayId, $mediaId, $storedAs) {
630
Debug::LogEntry('audit', 'IN', get_class(), __FUNCTION__);
633
$dbh = PDOConnect::init();
635
// Set the instructions
636
$version_instructions = array();
637
$version_instructions['id'] = $mediaId;
638
$version_instructions['file'] = $storedAs;
640
$sth = $dbh->prepare('UPDATE `display` SET version_instructions = :version_instructions WHERE displayid = :displayid');
642
'displayid' => $displayId,
643
'version_instructions' => json_encode($version_instructions)
648
catch (Exception $e) {
650
Debug::LogEntry('error', $e->getMessage(), get_class(), __FUNCTION__);
652
if (!$this->IsError())
653
$this->SetError(1, __('Unknown Error'));
659
public function GetSetting($key, $default) {
661
if (!$this->SetConfig())
666
foreach($this->_config as $row) {
667
if ($row['name'] == $key || $row['name'] == ucfirst($key)) {
668
//Debug::Audit('Found ' . $key . '. value= ' . $row['value']);
669
$return = $row['value'];
677
private function SetConfig() {
678
if ($this->_config == null) {
680
$dbh = PDOConnect::init();
682
$displayProfile = new DisplayProfile();
683
$displayProfile->displayProfileId = $this->displayProfileId;
685
if ($displayProfile->displayProfileId == 0) {
686
// Load the default profile
687
$displayProfile->type = $this->clientType;
688
$displayProfile->LoadDefault();
691
// Load the specified profile
692
$displayProfile->Load();
695
$this->_config = $displayProfile->config;
699
catch (Exception $e) {
701
Debug::LogEntry('error', $e->getMessage(), get_class(), __FUNCTION__);
703
if (!$this->IsError())
704
$this->SetError(1, __('Unknown Error'));
711
* Assess each Display to correctly set the logged in flag based on last accessed time
714
public static function ValidateDisplays() {
715
// Maintain an array of timed out displays
716
$timedOutDisplays = array();
719
$dbh = PDOConnect::init();
720
$statObject = new Stat();
722
// Get a list of all displays and there last accessed / alert time out value
723
$sth = $dbh->prepare('SELECT displayid, display, lastaccessed, alert_timeout, client_type, displayprofileid, email_alert, loggedin FROM display');
724
$sthUpdate = $dbh->prepare('UPDATE display SET loggedin = 0 WHERE displayid = :displayid');
726
$sth->execute(array());
728
// Get the global time out (overrides the alert time out on the display if 0)
729
$globalTimeout = Config::GetSetting('MAINTENANCE_ALERT_TOUT') * 60;
731
$displays = $sth->fetchAll();
733
foreach ($displays as $row) {
734
$displayid = Kit::ValidateParam($row['displayid'], _INT);
735
$lastAccessed = Kit::ValidateParam($row['lastaccessed'], _INT);
736
$alertTimeout = Kit::ValidateParam($row['alert_timeout'], _INT);
737
$clientType = Kit::ValidateParam($row['client_type'], _WORD);
738
$loggedIn = Kit::ValidateParam($row['loggedin'], _INT);
740
// Get the config object
741
if ($alertTimeout == 0) {
742
$displayProfileId = (empty($row['displayprofileid']) ? 0 : Kit::ValidateParam($row['displayprofileid'], _INT));
744
$display = new Display();
745
$display->displayId = $displayid;
746
$display->displayProfileId = $displayProfileId;
747
$display->clientType = $clientType;
748
$timeoutToTestAgainst = $display->GetSetting('collectInterval', $globalTimeout);
751
$timeoutToTestAgainst = $globalTimeout;
754
// Store the time out to test against
755
$row['timeout'] = $timeoutToTestAgainst;
756
$timeOut = $lastAccessed + $timeoutToTestAgainst;
758
// If the last time we accessed is less than now minus the time out
759
if ($timeOut < time()) {
760
Debug::Audit('Timed out display. Last Accessed: ' . date('Y-m-d h:i:s', $lastAccessed) . '. Time out: ' . date('Y-m-d h:i:s', $timeOut));
762
// If this is the first switch (i.e. the row was logged in before)
763
if ($loggedIn == 1) {
765
// Update the display and set it as logged out
766
$sthUpdate->execute(array('displayid' => $displayid));
768
// Log the down event
769
$statObject->displayDown($displayid, $lastAccessed);
773
$timedOutDisplays[] = $row;
777
return $timedOutDisplays;
779
catch (Exception $e) {
781
Debug::LogEntry('error', $e->getMessage(), get_class(), __FUNCTION__);
783
if (!$this->IsError())
784
$this->SetError(1, __('Unknown Error'));
791
* Wake this display using a WOL command
792
* @param <int> $displayId
795
public function WakeOnLan($displayId)
797
Debug::LogEntry('audit', 'IN', get_class(), __FUNCTION__);
800
$dbh = PDOConnect::init();
802
// Get the Client Address and the Mac Address
803
$sth = $dbh->prepare('SELECT MacAddress, BroadCastAddress, SecureOn, Cidr FROM `display` WHERE DisplayID = :displayid');
805
'displayid' => $displayId
808
if (!$row = $sth->fetch())
809
$this->ThrowError(25013, __('Unable to get the Mac or Client Address'));
811
// Check they are populated
812
if ($row['MacAddress'] == '' || $row['BroadCastAddress'] == '')
813
$this->SetError(25014, __('This display has no mac address recorded against it yet. Make sure the display is running.'));
815
Debug::LogEntry('audit', 'About to send WOL packet to ' . $row['BroadCastAddress'] . ' with Mac Address ' . $row['MacAddress'], 'display', 'WakeOnLan');
817
if (!$this->TransmitWakeOnLan($row['MacAddress'], $row['SecureOn'], $row['BroadCastAddress'], $row['Cidr'], "9"))
818
throw new Exception('Error in TransmitWakeOnLan');
820
// If we succeeded then update this display with the last WOL time
821
$sth = $dbh->prepare('UPDATE `display` SET LastWakeOnLanCommandSent = :lastaccessed WHERE DisplayID = :displayid');
823
'displayid' => $displayId,
824
'lastaccessed' => time()
829
catch (Exception $e) {
830
Debug::LogEntry('error', $e->getMessage());
832
if (!$this->IsError())
833
$this->SetError(25012, __('Unknown Error.'));
840
* Get a list of users that have permission for the provided display
841
* @param int $displayId The Display
842
* @param string $authLevel The Auth Level (view|edit|delete)
843
* @return array Users Array
845
public static function getUsers($displayId, $authLevel = 'view')
848
$dbh = PDOConnect::init();
850
$sth = $dbh->prepare('
851
SELECT DISTINCT user.userId, user.userName, user.email
853
INNER JOIN `lkusergroup`
854
ON lkusergroup.userId = user.userId
856
ON group.groupId = lkusergroup.groupId
857
INNER JOIN `lkdisplaygroupgroup`
858
ON lkdisplaygroupgroup.groupId = group.groupId
859
INNER JOIN `displaygroup`
860
ON displaygroup.displayGroupId = lkdisplaygroupgroup.displayGroupId
861
INNER JOIN `lkdisplaydg`
862
ON lkdisplaydg.displayGroupId = lkdisplaygroupgroup.displayGroupId
863
WHERE lkdisplaydg.displayId = :displayId
867
'displayId' => $displayId
870
// Return this list of users
871
return $sth->fetchAll();
873
catch (Exception $e) {
874
Debug::LogEntry('error', $e->getMessage(), get_class(), __FUNCTION__);
882
* // Author of this application:
883
* // DS508_customer (http://www.synology.com/enu/forum/memberlist.php?mode=viewprofile&u=12636)
884
* // Please inform the author of any suggestions on (the functionality, graphical design, ... of) this application.
885
* // More info: http://wolviaphp.sourceforge.net
886
* // License: GPLv2.0
888
* Modified for use with the Xibo project by Dan Garner.
890
function TransmitWakeOnLan($mac_address, $secureon, $addr, $cidr, $port) {
891
Debug::LogEntry('audit', 'IN', get_class(), __FUNCTION__);
893
// Prepare magic packet: part 1/3 (defined constant)
896
// the defined constant as represented in hexadecimal: FF FF FF FF FF FF (i.e., 6 bytes of hexadecimal FF)
897
for ($a=0; $a<6; $a++) $buf .= chr(255);
899
// Check whether $mac_address is valid
900
$mac_address = strtoupper($mac_address);
901
$mac_address = str_replace(":", "-", $mac_address);
903
if ((!preg_match("/([A-F0-9]{2}[-]){5}([0-9A-F]){2}/",$mac_address)) || (strlen($mac_address) != 17))
905
return $this->SetError(25015, __('Pattern of MAC-address is not "xx-xx-xx-xx-xx-xx" (x = digit or letter)'));
909
// Prepare magic packet: part 2/3 (16 times MAC-address)
910
// Split MAC-address into an array of (six) bytes
911
$addr_byte = explode('-', $mac_address);
914
// Convert MAC-address from bytes to hexadecimal to decimal
915
for ($a=0; $a<6; $a++) $hw_addr .= chr(hexdec($addr_byte[$a]));
917
$hw_addr_string = "";
919
for ($a=0; $a<16; $a++) $hw_addr_string .= $hw_addr;
920
$buf .= $hw_addr_string;
925
// Check whether $secureon is valid
926
$secureon = strtoupper($secureon);
927
$secureon = str_replace(":", "-", $secureon);
929
if ((!preg_match("/([A-F0-9]{2}[-]){5}([0-9A-F]){2}/", $secureon)) || (strlen($secureon) != 17))
931
return $this->SetError(25015, __('Pattern of SecureOn-password is not "xx-xx-xx-xx-xx-xx" (x = digit or CAPITAL letter)'));
935
// Prepare magic packet: part 3/3 (Secureon password)
936
// Split MAC-address into an array of (six) bytes
937
$addr_byte = explode('-', $secureon);
940
// Convert MAC address from hexadecimal to decimal
941
for ($a=0; $a<6; $a++) $hw_addr .= chr(hexdec($addr_byte[$a]));
946
// Fill $addr with client's IP address, if $addr is empty
948
return $this->SetError(25000, __('No IP Address Specified'));
950
// Resolve broadcast address
951
if (filter_var ($addr, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) // same as (but easier than): preg_match("/\b(([01]?\d?\d|2[0-4]\d|25[0-5])\.){3}([01]?\d?\d|2[0-4]\d|25[0-5])\b/",$addr)
953
// $addr has an IP-adres format
957
return $this->SetError(25000, __('IP Address Incorrectly Formed'));
960
// If $cidr is set, replace $addr for its broadcast address
963
// Check whether $cidr is valid
964
if ((!ctype_digit($cidr)) || ($cidr < 0) || ($cidr > 32))
966
return $this->SetError(25015, __('CIDR subnet mask is not a number within the range of 0 till 32.'));
969
// Convert $cidr from one decimal to one inverted binary array
970
$inverted_binary_cidr = "";
972
// Build $inverted_binary_cidr by $cidr * zeros (this is the mask)
973
for ($a=0; $a<$cidr; $a++) $inverted_binary_cidr .= "0";
975
// Invert the mask (by postfixing ones to $inverted_binary_cidr untill 32 bits are filled/ complete)
976
$inverted_binary_cidr = $inverted_binary_cidr.substr("11111111111111111111111111111111", 0, 32 - strlen($inverted_binary_cidr));
978
// Convert $inverted_binary_cidr to an array of bits
979
$inverted_binary_cidr_array = str_split($inverted_binary_cidr);
981
// Convert IP address from four decimals to one binary array
982
// Split IP address into an array of (four) decimals
983
$addr_byte = explode('.', $addr);
986
for ($a=0; $a<4; $a++)
989
$pre = substr("00000000",0,8-strlen(decbin($addr_byte[$a])));
991
// Postfix binary decimal
992
$post = decbin($addr_byte[$a]);
993
$binary_addr .= $pre.$post;
996
// Convert $binary_addr to an array of bits
997
$binary_addr_array = str_split($binary_addr);
999
// Perform a bitwise OR operation on arrays ($binary_addr_array & $inverted_binary_cidr_array)
1000
$binary_broadcast_addr_array="";
1002
// binary array of 32 bit variables ('|' = logical operator 'or')
1003
for ($a=0; $a<32; $a++) $binary_broadcast_addr_array[$a] = ($binary_addr_array[$a] | $inverted_binary_cidr_array[$a]);
1005
// build binary address of four bundles of 8 bits (= 1 byte)
1006
$binary_broadcast_addr = chunk_split(implode("", $binary_broadcast_addr_array), 8, ".");
1008
// chop off last dot ('.')
1009
$binary_broadcast_addr = substr($binary_broadcast_addr,0,strlen($binary_broadcast_addr)-1);
1011
// binary array of 4 byte variables
1012
$binary_broadcast_addr_array = explode(".", $binary_broadcast_addr);
1013
$broadcast_addr_array = "";
1015
// decimal array of 4 byte variables
1016
for ($a=0; $a<4; $a++) $broadcast_addr_array[$a] = bindec($binary_broadcast_addr_array[$a]);
1018
// broadcast address
1019
$addr = implode(".", $broadcast_addr_array);
1022
// Check whether $port is valid
1023
if ((!ctype_digit($port)) || ($port < 0) || ($port > 65536))
1024
return $this->SetError(25000, __('Port is not a number within the range of 0 till 65536. Port Provided: ' . $port));
1026
// Check whether UDP is supported
1027
if (!array_search('udp', stream_get_transports()))
1028
return $this->SetError(25000, __('No magic packet can been sent, since UDP is unsupported (not a registered socket transport)'));
1030
// Ready to send the packet
1031
if (function_exists('fsockopen'))
1033
// Try fsockopen function - To do: handle error 'Permission denied'
1034
$socket = fsockopen("udp://" . $addr, $port, $errno, $errstr);
1038
$socket_data = fwrite($socket, $buf);
1042
$function = "fwrite";
1043
$sent_fsockopen = "A magic packet of ".$socket_data." bytes has been sent via UDP to IP address: ".$addr.":".$port.", using the '".$function."()' function.";
1044
$content = bin2hex($buf);
1046
$sent_fsockopen = $sent_fsockopen."Contents of magic packet:".strlen($content)." ".$content;
1051
Debug::LogEntry('audit', $sent_fsockopen, 'display', 'WakeOnLan');
1058
return $this->SetError(25015, __('Using "fwrite()" failed, due to error: ' . $errstr. ' ("' . $errno . '")'));
1065
Debug::LogEntry('audit', __('Using fsockopen() failed, due to denied permission'));
1069
// Try socket_create function
1070
if (function_exists('socket_create'))
1072
// create socket based on IPv4, datagram and UDP
1073
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
1077
// to enable manipulation of options at the socket level (you may have to change this to 1)
1078
$level = SOL_SOCKET;
1080
// to enable permission to transmit broadcast datagrams on the socket (you may have to change this to 6)
1081
$optname = SO_BROADCAST;
1084
$opt_returnvalue = socket_set_option($socket, $level, $optname, $optval);
1086
if ($opt_returnvalue < 0)
1088
return $this->SetError(25015, __('Using "socket_set_option()" failed, due to error: ' . socket_strerror($opt_returnvalue)));
1093
// To do: handle error 'Operation not permitted'
1094
$socket_data = socket_sendto($socket, $buf, strlen($buf), $flags, $addr, $port);
1098
$function = "socket_sendto";
1099
$socket_create = "A magic packet of ". $socket_data . " bytes has been sent via UDP to IP address: ".$addr.":".$port.", using the '".$function."()' function.<br>";
1101
$content = bin2hex($buf);
1102
$socket_create = $socket_create . "Contents of magic packet:" . strlen($content) ." " . $content;
1104
socket_close($socket);
1107
Debug::LogEntry('audit', $socket_create, 'display', 'WakeOnLan');
1112
$error = "Using 'socket_sendto()' failed, due to error: '".socket_strerror(socket_last_error($socket))."' (".socket_last_error($socket).")<br>\n";
1113
socket_close($socket);
1116
return $this->SetError(25015, __('Using "socket_sendto()" failed, due to error: ' . socket_strerror(socket_last_error($socket)) . ' (' . socket_last_error($socket) . ')'));
1121
return $this->SetError(25015, __('Using "socket_sendto()" failed, due to error: ' . socket_strerror(socket_last_error($socket)) . ' (' . socket_last_error($socket) . ')'));
1126
return $this->SetError(25015, __('Wake On Lan Failed as there are no functions available to transmit it'));