19
19
* along with Foobar. If not, see <http://www.gnu.org/licenses/>.
21
21
namespace Xibo\Controller;
23
use GuzzleHttp\Client;
24
use PicoFeed\PicoFeedException;
25
use PicoFeed\Reader\Reader;
26
use Stash\Interfaces\PoolInterface;
27
use Xibo\Factory\DisplayFactory;
28
use Xibo\Factory\DisplayGroupFactory;
29
use Xibo\Factory\MediaFactory;
30
use Xibo\Factory\UserFactory;
31
use Xibo\Helper\ByteFormatter;
32
use Xibo\Service\ConfigServiceInterface;
33
use Xibo\Service\DateServiceInterface;
34
use Xibo\Service\LogServiceInterface;
35
use Xibo\Service\SanitizerServiceInterface;
36
use Xibo\Storage\StorageServiceInterface;
27
use Xibo\Helper\Theme;
39
* Class StatusDashboard
40
* @package Xibo\Controller
42
29
class StatusDashboard extends Base
45
* @var StorageServiceInterface
62
private $displayFactory;
65
* @var DisplayGroupFactory
67
private $displayGroupFactory;
72
private $mediaFactory;
75
* Set common dependencies.
76
* @param LogServiceInterface $log
77
* @param SanitizerServiceInterface $sanitizerService
78
* @param \Xibo\Helper\ApplicationState $state
79
* @param \Xibo\Entity\User $user
80
* @param \Xibo\Service\HelpServiceInterface $help
81
* @param DateServiceInterface $date
82
* @param ConfigServiceInterface $config
83
* @param StorageServiceInterface $store
84
* @param PoolInterface $pool
85
* @param UserFactory $userFactory
86
* @param DisplayFactory $displayFactory
87
* @param DisplayGroupFactory $displayGroupFactory
88
* @param MediaFactory $mediaFactory
90
public function __construct($log, $sanitizerService, $state, $user, $help, $date, $config, $store, $pool, $userFactory, $displayFactory, $displayGroupFactory, $mediaFactory)
92
$this->setCommonDependencies($log, $sanitizerService, $state, $user, $help, $date, $config);
94
$this->store = $store;
96
$this->userFactory = $userFactory;
97
$this->displayFactory = $displayFactory;
98
$this->displayGroupFactory = $displayGroupFactory;
99
$this->mediaFactory = $mediaFactory;
105
31
function displayPage()
109
33
// Set up some suffixes
110
$suffixes = array('B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB');
34
$suffixes = array('bytes', 'k', 'M', 'G', 'T');
36
// Get some data for a bandwidth chart
113
// Displays this user has access to
114
$displays = $this->displayFactory->query(['display']);
115
$displayIds = array_map(function($element) {
116
return $element->displayId;
120
// Get some data for a bandwidth chart
121
$dbh = $this->store->getConnection();
124
SELECT MAX(FROM_UNIXTIME(month)) AS month,
125
IFNULL(SUM(Size), 0) AS size
127
WHERE month > :month AND displayId IN (' . implode(',', $displayIds) . ')
128
GROUP BY MONTH(FROM_UNIXTIME(month)) ORDER BY MIN(month);
130
$params = array('month' => time() - (86400 * 365));
133
$results = $this->store->select($sql, $params);
38
$dbh = \Xibo\Storage\PDOConnect::init();
40
$sth = $dbh->prepare('SELECT FROM_UNIXTIME(month) AS month, IFNULL(SUM(Size), 0) AS size FROM `bandwidth` WHERE month > :month GROUP BY FROM_UNIXTIME(month) ORDER BY MIN(month);');
41
$sth->execute(array('month' => time() - (86400 * 365)));
43
$results = $sth->fetchAll();
135
45
// Monthly bandwidth - optionally tested against limits
136
$xmdsLimit = $this->getConfig()->GetSetting('MONTHLY_XMDS_TRANSFER_LIMIT_KB');
46
$xmdsLimit = Config::GetSetting('MONTHLY_XMDS_TRANSFER_LIMIT_KB');
139
49
foreach ($results as $row) {
146
56
if ($xmdsLimit > 0) {
147
57
// Convert to appropriate size (xmds limit is in KB)
148
58
$xmdsLimit = ($xmdsLimit * 1024) / (pow(1024, $base));
149
$data['xmdsLimit'] = round($xmdsLimit, 2) . ' ' . $suffixes[$base];
59
Theme::Set('xmdsLimit', $xmdsLimit . ' ' . $suffixes[$base]);
156
64
foreach ($results as $row) {
157
$labels[] = $this->getDate()->getLocalDate($this->getSanitizer()->getDate('month', $row), 'F');
159
65
$size = ((double)$row['size']) / (pow(1024, $base));
160
$usage[] = round($size, 2);
162
$limit[] = round($xmdsLimit - $size, 2);
66
$remaining = $xmdsLimit - $size;
68
'label' => Date::getLocalDate(Date::getDateFromGregorianString($row['month']), 'F'),
69
'value' => round($size, 2),
70
'limit' => round($remaining, 2)
165
74
// What if we are empty?
166
if (count($results) == 0) {
167
$labels[] = $this->getDate()->getLocalDate(null, 'F');
172
// Organise our datasets
175
'label' => __('Used'),
176
'backgroundColor' => ($xmdsLimit > 0) ? 'rgb(255, 0, 0)' : 'rgb(11, 98, 164)',
181
if ($xmdsLimit > 0) {
183
'label' => __('Available'),
184
'backgroundColor' => 'rgb(0, 204, 0)',
75
if (count($output) == 0) {
77
'label' => Date::getLocalDate(null, 'F'),
190
$data['xmdsLimitSet'] = ($xmdsLimit > 0);
191
$data['bandwidthSuffix'] = $suffixes[$base];
192
$data['bandwidthWidget'] = json_encode([
194
'datasets' => $dataSets
84
Theme::Set('xmdsLimitSet', ($xmdsLimit > 0));
85
Theme::Set('bandwidthSuffix', $suffixes[$base]);
86
Theme::Set('bandwidthWidget', json_encode($output));
197
88
// We would also like a library usage pie chart!
198
if ($this->getUser()->libraryQuota != 0) {
199
$libraryLimit = $this->getUser()->libraryQuota * 1024;
202
$libraryLimit = $this->getConfig()->GetSetting('LIBRARY_SIZE_LIMIT_KB') * 1024;
89
$libraryLimit = Config::GetSetting('LIBRARY_SIZE_LIMIT_KB');
90
$libraryLimit = $libraryLimit * 1024;
205
92
// Library Size in Bytes
207
$sql = 'SELECT IFNULL(SUM(FileSize), 0) AS SumSize, type FROM `media` WHERE 1 = 1 ';
208
$this->mediaFactory->viewPermissionSql('Xibo\Entity\Media', $sql, $params, '`media`.mediaId', '`media`.userId');
209
$sql .= ' GROUP BY type ';
211
$sth = $dbh->prepare($sql);
212
$sth->execute($params);
93
$sth = $dbh->prepare('SELECT IFNULL(SUM(FileSize), 0) AS SumSize, type FROM media GROUP BY type;');
214
96
$results = $sth->fetchAll();
227
109
// Decide what our units are going to be, based on the size
228
110
$base = ($maxSize == 0) ? 0 : floor(log($maxSize) / log(1024));
233
114
foreach ($results as $library) {
234
$libraryUsage[] = round((double)$library['SumSize'] / (pow(1024, $base)), 2);
235
$libraryLabels[] = ucfirst($library['type']) . ' ' . $suffixes[$base];
116
'value' => round((double)$library['SumSize'] / (pow(1024, $base)), 2),
117
'label' => ucfirst($library['type'])
237
119
$totalSize = $totalSize + $library['SumSize'];
240
122
// Do we need to add the library remaining?
241
123
if ($libraryLimit > 0) {
242
124
$remaining = round(($libraryLimit - $totalSize) / (pow(1024, $base)), 2);
244
$libraryUsage[] = $remaining;
245
$libraryLabels[] = __('Free') . ' ' . $suffixes[$base];
126
'value' => $remaining,
127
'label' => __('Free')
248
131
// What if we are empty?
249
if (count($results) == 0 && $libraryLimit <= 0) {
251
$libraryLabels[] = __('Empty');
132
if (count($output) == 0) {
134
'label' => __('Empty'),
254
$data['libraryLimitSet'] = ($libraryLimit > 0);
255
$data['libraryLimit'] = (round((double)$libraryLimit / (pow(1024, $base)), 2)) . ' ' . $suffixes[$base];
256
$data['librarySize'] = ByteFormatter::format($totalSize, 1);
257
$data['librarySuffix'] = $suffixes[$base];
258
$data['libraryWidgetLabels'] = json_encode($libraryLabels);
259
$data['libraryWidgetData'] = json_encode($libraryUsage);
139
Theme::Set('libraryLimitSet', $libraryLimit);
140
Theme::Set('libraryLimit', (round((double)$libraryLimit / (pow(1024, $base)), 2)) . ' ' . $suffixes[$base]);
141
Theme::Set('librarySize', \Kit::formatBytes($totalSize, 1));
142
Theme::Set('librarySuffix', $suffixes[$base]);
143
Theme::Set('libraryWidget', json_encode($output));
261
145
// Also a display widget
262
$data['displays'] = $displays;
146
$sort_order = array('display');
147
$displays = $this->getUser()->DisplayList($sort_order);
151
if (is_array($displays) && count($displays) > 0) {
152
// Output a table showing the displays
153
foreach ($displays as $display) {
154
/* @var \Xibo\Entity\Display $display */
155
$row['mediainventorystatus'] = ($display->mediaInventoryStatus == 1) ? 'success' : (($display->mediaInventoryStatus == 2) ? 'danger' : 'warning');
156
$row['display'] = $display->display;
157
$row['loggedin'] = $display->loggedIn;
158
$row['licensed'] = $display->licensed;
159
// Assign this to the table row
164
Theme::Set('display-widget-rows', $rows);
264
166
// Get a count of users
265
$data['countUsers'] = count($this->userFactory->query());
267
// Get a count of active layouts, only for display groups we have permission for
268
$displayGroups = $this->displayGroupFactory->query(null, ['isDisplaySpecific' => -1]);
269
$displayGroupIds = array_map(function($element) {
270
return $element->displayGroupId;
273
$displayGroupIds[] = -1;
276
SELECT IFNULL(COUNT(*), 0) AS count_scheduled
279
:now BETWEEN FromDT AND ToDT
280
OR `schedule`.recurrence_range >= :now
282
IFNULL(`schedule`.recurrence_range, 0) = 0 AND IFNULL(`schedule`.recurrence_type, \'\') <> \'\'
287
FROM `lkscheduledisplaygroup`
288
WHERE displayGroupId IN (' . implode(',', $displayGroupIds) . ')
291
$params = array('now' => time());
293
$sth = $dbh->prepare($sql);
294
$sth->execute($params);
296
$data['nowShowing'] = $sth->fetchColumn(0);
167
$sth = $dbh->prepare('SELECT IFNULL(COUNT(*), 0) AS count_users FROM `user`');
170
Theme::Set('countUsers', $sth->fetchColumn(0));
172
// Get a count of active layouts
173
$sth = $dbh->prepare('SELECT IFNULL(COUNT(*), 0) AS count_scheduled FROM `schedule_detail` WHERE :now BETWEEN FromDT AND ToDT');
174
$sth->execute(array('now' => time()));
176
Theme::Set('nowShowing', $sth->fetchColumn(0));
299
if ($this->getConfig()->GetSetting('DASHBOARD_LATEST_NEWS_ENABLED') == 1 && !empty($this->getConfig()->GetSetting('LATEST_NEWS_URL'))) {
179
if (Config::GetSetting('DASHBOARD_LATEST_NEWS_ENABLED') == 1) {
300
180
// Make sure we have the cache location configured
301
Library::ensureLibraryExists($this->getConfig()->GetSetting('LIBRARY_LOCATION'));
304
$feedUrl = $this->getConfig()->GetSetting('LATEST_NEWS_URL');
305
$cache = $this->pool->getItem('rss/' . md5($feedUrl));
307
$latestNews = $cache->get();
310
if ($cache->isMiss()) {
312
// Create a Guzzle Client to get the Feed XML
313
$client = new Client();
314
$response = $client->get($feedUrl, $this->getConfig()->getGuzzleProxy());
316
// Pull out the content type and body
317
$result = explode('charset=', $response->getHeaderLine('Content-Type'));
318
$document['encoding'] = isset($result[1]) ? $result[1] : '';
319
$document['xml'] = $response->getBody();
321
// Get the feed parser
322
$reader = new Reader();
323
$parser = $reader->getParser($feedUrl, $document['xml'], $document['encoding']);
326
$feed = $parser->execute();
328
// Parse the items in the feed
331
foreach ($feed->getItems() as $item) {
332
/* @var \PicoFeed\Parser\Item $item */
334
// Try to get the description tag
335
if (!$desc = $item->getTag('description')) {
336
// use content with tags stripped
337
$content = strip_tags($item->getContent());
340
$content = (isset($desc[0]) ? $desc[0] : strip_tags($item->getContent()));
343
$latestNews[] = array(
344
'title' => $item->getTitle(),
345
'description' => $content,
346
'link' => $item->getUrl()
350
// Store in the cache for 1 day
351
$cache->set($latestNews);
352
$cache->expiresAfter(86400);
354
$this->pool->saveDeferred($cache);
181
File::EnsureLibraryExists();
183
// Use SimplePie to get the feed
184
include_once('3rdparty/simplepie/autoloader.php');
186
$feed = new SimplePie();
187
$feed->set_cache_location(File::GetLibraryCacheUri());
188
$feed->set_feed_url(Theme::GetConfig('latest_news_url'));
189
$feed->set_cache_duration(86400);
190
$feed->handle_content_type();
193
$latestNews = array();
195
if ($feed->error()) {
196
Log::notice('Feed Error: ' . $feed->error(), get_class(), __FUNCTION__);
198
// Store our formatted items
199
foreach ($feed->get_items() as $item) {
200
$latestNews[] = array(
201
'title' => $item->get_title(),
202
'description' => $item->get_description(),
203
'link' => $item->get_link()
357
$data['latestNews'] = $latestNews;
359
catch (PicoFeedException $e) {
360
$this->getLog()->error('Unable to get feed: %s', $e->getMessage());
361
$this->getLog()->debug($e->getTraceAsString());
363
$data['latestNews'] = array(array('title' => __('Latest news not available.'), 'description' => '', 'link' => ''));
208
Theme::Set('latestNews', $latestNews);
367
$data['latestNews'] = array(array('title' => __('Latest news not enabled.'), 'description' => '', 'link' => ''));
211
Theme::Set('latestNews', array(array('title' => __('Latest news not enabled.'), 'description' => '', 'link' => '')));
370
214
catch (Exception $e) {
372
$this->getLog()->error($e->getMessage());
373
$this->getLog()->debug($e->getTraceAsString());
216
Log::error($e->getMessage());
375
218
// Show the error in place of the bandwidth chart
376
$data['widget-error'] = 'Unable to get widget details';
219
Theme::Set('widget-error', 'Unable to get widget details');
379
222
// Do we have an embedded widget?
380
$data['embeddedWidget'] = html_entity_decode($this->getConfig()->GetSetting('EMBEDDED_STATUS_WIDGET'));
223
Theme::Set('embedded-widget', html_entity_decode(Config::GetSetting('EMBEDDED_STATUS_WIDGET')));
382
225
// Render the Theme and output
383
$this->getState()->template = 'dashboard-status-page';
384
$this->getState()->setData($data);
226
$this->getState()->html .= Theme::RenderReturn('status_dashboard');