24
24
use GuzzleHttp\Client;
25
25
use GuzzleHttp\Exception\RequestException;
26
use Respect\Validation\Validator as v;
27
26
use Xibo\Entity\Media;
28
27
use Xibo\Exception\NotFoundException;
29
use Xibo\Exception\XiboException;
30
use Xibo\Factory\ModuleFactory;
28
use Xibo\Factory\DisplayFactory;
29
use Xibo\Factory\MediaFactory;
30
use Xibo\Helper\Cache;
31
use Xibo\Helper\Config;
34
use Xibo\Helper\Sanitize;
35
use Xibo\Helper\Theme;
34
* Weather module powered by the DarkSky API
35
* @package Xibo\Widget
37
37
class ForecastIo extends ModuleWidget
39
const API_ENDPOINT = 'https://api.darksky.net/forecast/';
39
const API_ENDPOINT = 'https://api.forecast.io/forecast/';
41
41
private $resourceFolder;
42
42
protected $codeSchemaVersion = 1;
45
* ForecastIo constructor.
47
public function init()
44
public function __construct()
49
$this->resourceFolder = PROJECT_ROOT . '/modules/forecastio/player';
46
$this->resourceFolder = PROJECT_ROOT . '/web/modules/forecastio';
51
// Initialise extra validation rules
52
v::with('Xibo\\Validation\\Rules\\');
48
parent::__construct();
56
52
* Install or Update this module
57
* @param ModuleFactory $moduleFactory
59
public function installOrUpdate($moduleFactory)
54
public function installOrUpdate()
61
56
if ($this->module == null) {
63
$module = $moduleFactory->createEmpty();
64
$module->name = 'Weather';
58
$module = new \Xibo\Entity\Module();
59
$module->name = 'Forecast IO';
65
60
$module->type = 'forecastio';
66
61
$module->class = 'Xibo\Widget\ForecastIo';
67
$module->description = 'Weather Powered by DarkSky';
62
$module->description = 'Weather forecasting from Forecast IO';
68
63
$module->imageUri = 'forms/library.gif';
69
64
$module->enabled = 1;
70
65
$module->previewEnabled = 1;
110
103
public function settings()
112
105
// Process any module settings you asked for.
113
$apiKey = $this->getSanitizer()->getString('apiKey');
106
$apiKey = Sanitize::getString('apiKey');
115
108
if ($apiKey == '')
116
109
throw new \InvalidArgumentException(__('Missing API Key'));
118
111
$this->module->settings['apiKey'] = $apiKey;
119
$this->module->settings['cachePeriod'] = $this->getSanitizer()->getInt('cachePeriod', 300);
112
$this->module->settings['cachePeriod'] = Sanitize::getInt('cachePeriod', 300);
116
* Loads templates for this module
118
private function loadTemplates()
120
// Scan the folder for template files
121
foreach (glob(PROJECT_ROOT . '/modules/forecastio/*.template.json') as $template) {
122
// Read the contents, json_decode and add to the array
123
$this->module->settings['templates'][] = json_decode(file_get_contents($template), true);
126
Log::debug(count($this->module->settings['templates']));
130
* Templates available
133
public function templatesAvailable()
135
if (!isset($this->module->settings['templates']))
136
$this->loadTemplates();
138
return $this->module->settings['templates'];
122
141
public function validate()
125
if($this->getOption('overrideTemplate') == 0 && ( $this->getOption('templateId') == '' || $this->getOption('templateId') == null) )
126
throw new \InvalidArgumentException(__('Please choose a template'));
128
143
if ($this->getUseDuration() == 1 && $this->getDuration() == 0)
129
144
throw new \InvalidArgumentException(__('Please enter a duration'));
131
if ($this->getOption('useDisplayLocation') == 0) {
133
if (!v::latitude()->validate($this->getOption('latitude')))
134
throw new \InvalidArgumentException(__('The latitude entered is not valid.'));
136
if (!v::longitude()->validate($this->getOption('longitude')))
137
throw new \InvalidArgumentException(__('The longitude entered is not valid.'));
142
* Adds a Weather Widget
144
* path="/playlist/widget/forecastIo/{playlistId}",
145
* operationId="WidgetWeatherAdd",
147
* summary="Add a Weather Widget",
148
* description="Add a new Weather Widget to the specified playlist",
152
* description="The playlist ID to add a Weather widget",
159
* description="Optional Widget Name",
166
* description="Widget Duration",
171
* name="useDuration",
173
* description="(0, 1) Select 1 only if you will provide duration parameter as well",
178
* name="useDisplayLocation",
180
* description="Flag (0, 1) Use the location configured on display",
187
* description="The longitude for this weather widget, only pass if useDisplayLocation set to 0",
194
* description="The latitude for this weather widget, only pass if useDisplayLocation set to 0",
201
* description="Use pre-configured templates, available options: weather-module0-5day, weather-module0-singleday, weather-module0-singleday2, weather-module1l, weather-module1p, weather-module2l, weather-module2p, weather-module3l, weather-module3p, weather-module4l, weather-module4p, weather-module5l, weather-module6v, weather-module6h",
208
* description="Units you would like to use, available options: auto, ca, si, uk2, us",
213
* name="updateInterval",
215
* description="Update interval in minutes, should be kept as high as possible, if data change once per hour, this should be set to 60",
222
* description="Language you'd like to use, supported languages ar, az, be, bs, cs, de, en, el, es, fr, hr, hu, id, it, is, kw, nb, nl, pl, pt, ru, sk, sr, sv, tet, tr, uk, x-pig-latin, zh, zh-tw",
227
* name="dayConditionsOnly",
229
* description="Flag (0, 1) Would you like to only show the Daytime weather conditions",
234
* name="overrideTemplate",
236
* description="flag (0, 1) set to 0 and use templateId or set to 1 and provide whole template in the next parameters",
241
* name="widgetOriginalWidth",
243
* description="This is the intended Width of the template and is used to scale the Widget within it's region when the template is applied, Pass only with overrideTemplate set to 1",
248
* name="widgetOriginalHeight",
250
* description="This is the intended Height of the template and is used to scale the Widget within it's region when the template is applied, Pass only with overrideTemplate set to 1",
255
* name="currentTemplate",
257
* description="Current template, Pass only with overrideTemplate set to 1 ",
262
* name="dailyTemplate",
264
* description="Replaces [dailyForecast] in main template, Pass only with overrideTemplate set to 1 ",
271
* description="Optional StyleSheet, Pass only with overrideTemplate set to 1 ",
278
* description="Optional JavaScript, Pass only with overrideTemplate set to 1 ",
284
* description="successful operation",
285
* @SWG\Schema(ref="#/definitions/Widget"),
288
* description="Location of the new widget",
148
* Add Media to the Database
294
150
public function add()
296
$this->setDuration($this->getSanitizer()->getInt('duration', $this->getDuration()));
297
$this->setUseDuration($this->getSanitizer()->getCheckbox('useDuration'));
298
$this->setOption('name', $this->getSanitizer()->getString('name'));
299
$this->setOption('useDisplayLocation', $this->getSanitizer()->getCheckbox('useDisplayLocation'));
300
$this->setOption('longitude', $this->getSanitizer()->getDouble('longitude'));
301
$this->setOption('latitude', $this->getSanitizer()->getDouble('latitude'));
302
$this->setOption('templateId', $this->getSanitizer()->getString('templateId'));
303
$this->setOption('overrideTemplate', $this->getSanitizer()->getCheckbox('overrideTemplate'));
304
$this->setOption('units', $this->getSanitizer()->getString('units'));
305
$this->setOption('updateInterval', $this->getSanitizer()->getInt('updateInterval', 60));
306
$this->setOption('lang', $this->getSanitizer()->getString('lang'));
307
$this->setOption('dayConditionsOnly', $this->getSanitizer()->getCheckbox('dayConditionsOnly'));
309
if( $this->getOption('overrideTemplate') == 1 ){
310
$this->setRawNode('styleSheet', $this->getSanitizer()->getParam('styleSheet', null));
311
$this->setRawNode('currentTemplate', $this->getSanitizer()->getParam('currentTemplate', null));
312
$this->setRawNode('dailyTemplate', $this->getSanitizer()->getParam('dailyTemplate', null));
313
$this->setOption('widgetOriginalWidth', $this->getSanitizer()->getInt('widgetOriginalWidth'));
314
$this->setOption('widgetOriginalHeight', $this->getSanitizer()->getInt('widgetOriginalHeight'));
317
$this->setRawNode('javaScript', $this->getSanitizer()->getParam('javaScript', ''));
152
$this->setDuration(Sanitize::getInt('duration', $this->getDuration()));
153
$this->setUseDuration(Sanitize::getCheckbox('useDuration'));
154
$this->setOption('name', Sanitize::getString('name'));
155
$this->setOption('useDisplayLocation', Sanitize::getCheckbox('useDisplayLocation'));
156
$this->setOption('color', Sanitize::getString('color'));
157
$this->setOption('longitude', Sanitize::getDouble('longitude'));
158
$this->setOption('latitude', Sanitize::getDouble('latitude'));
159
$this->setOption('templateId', Sanitize::getString('templateId'));
160
$this->setOption('icons', Sanitize::getString('icons'));
161
$this->setOption('overrideTemplate', Sanitize::getCheckbox('overrideTemplate'));
162
$this->setOption('size', Sanitize::getInt('size'));
163
$this->setOption('units', Sanitize::getString('units'));
164
$this->setOption('updateInterval', Sanitize::getInt('updateInterval', 60));
165
$this->setOption('lang', Sanitize::getString('lang'));
166
$this->setOption('dayConditionsOnly', Sanitize::getCheckbox('dayConditionsOnly'));
168
$this->setRawNode('styleSheet', Sanitize::getParam('styleSheet', null));
169
$this->setRawNode('currentTemplate', Sanitize::getParam('currentTemplate', null));
170
$this->setRawNode('dailyTemplate', Sanitize::getParam('dailyTemplate', null));
319
172
// Save the widget
320
173
$this->validate();
321
174
$this->saveWidget();
327
180
public function edit()
329
$this->setDuration($this->getSanitizer()->getInt('duration', $this->getDuration()));
330
$this->setUseDuration($this->getSanitizer()->getCheckbox('useDuration'));
331
$this->setOption('name', $this->getSanitizer()->getString('name'));
332
$this->setOption('useDisplayLocation', $this->getSanitizer()->getCheckbox('useDisplayLocation'));
333
$this->setOption('longitude', $this->getSanitizer()->getDouble('longitude'));
334
$this->setOption('latitude', $this->getSanitizer()->getDouble('latitude'));
335
$this->setOption('templateId', $this->getSanitizer()->getString('templateId'));
336
$this->setOption('overrideTemplate', $this->getSanitizer()->getCheckbox('overrideTemplate'));
337
$this->setOption('units', $this->getSanitizer()->getString('units'));
338
$this->setOption('updateInterval', $this->getSanitizer()->getInt('updateInterval', 60));
339
$this->setOption('lang', $this->getSanitizer()->getString('lang'));
340
$this->setOption('dayConditionsOnly', $this->getSanitizer()->getCheckbox('dayConditionsOnly'));
342
if( $this->getOption('overrideTemplate') == 1 ){
343
$this->setRawNode('styleSheet', $this->getSanitizer()->getParam('styleSheet', null));
344
$this->setRawNode('currentTemplate', $this->getSanitizer()->getParam('currentTemplate', null));
345
$this->setRawNode('dailyTemplate', $this->getSanitizer()->getParam('dailyTemplate', null));
346
$this->setOption('widgetOriginalWidth', $this->getSanitizer()->getInt('widgetOriginalWidth'));
347
$this->setOption('widgetOriginalHeight', $this->getSanitizer()->getInt('widgetOriginalHeight'));
182
$this->setDuration(Sanitize::getInt('duration', $this->getDuration()));
183
$this->setUseDuration(Sanitize::getCheckbox('useDuration'));
184
$this->setOption('name', Sanitize::getString('name'));
185
$this->setOption('useDisplayLocation', Sanitize::getCheckbox('useDisplayLocation'));
186
$this->setOption('color', Sanitize::getString('color'));
187
$this->setOption('longitude', Sanitize::getDouble('longitude'));
188
$this->setOption('latitude', Sanitize::getDouble('latitude'));
189
$this->setOption('templateId', Sanitize::getString('templateId'));
190
$this->setOption('icons', Sanitize::getString('icons'));
191
$this->setOption('overrideTemplate', Sanitize::getCheckbox('overrideTemplate'));
192
$this->setOption('size', Sanitize::getInt('size'));
193
$this->setOption('units', Sanitize::getString('units'));
194
$this->setOption('updateInterval', Sanitize::getInt('updateInterval', 60));
195
$this->setOption('lang', Sanitize::getString('lang'));
196
$this->setOption('dayConditionsOnly', Sanitize::getCheckbox('dayConditionsOnly'));
350
$this->setRawNode('javaScript', $this->getSanitizer()->getParam('javaScript', ''));
198
$this->setRawNode('styleSheet', Sanitize::getParam('styleSheet', null));
199
$this->setRawNode('currentTemplate', Sanitize::getParam('currentTemplate', null));
200
$this->setRawNode('dailyTemplate', Sanitize::getParam('dailyTemplate', null));
352
202
// Save the widget
353
203
$this->validate();
354
204
$this->saveWidget();
207
public function iconsAvailable()
209
// Scan the forecast io folder for icons
212
foreach (array_diff(scandir($this->resourceFolder), array('..', '.')) as $file) {
213
if (stripos($file, '.png'))
214
$icons[] = array('id' => $file, 'value' => ucfirst(str_replace('-', ' ', str_replace('.png', '', $file))));
358
221
* Units supported by Forecast.IO API
359
* @return array The Units Available (temperature, wind speed and visible distance)
222
* @return array The Units Available
361
224
public function unitsAvailable()
364
array('id' => 'auto', 'value' => 'Automatically select based on geographic location', 'tempUnit' => '', 'windUnit' => '', 'visibilityUnit' => ''),
365
array('id' => 'ca', 'value' => 'Canada', 'tempUnit' => 'C', 'windUnit' => 'KPH', 'visibilityUnit' => 'km'),
366
array('id' => 'si', 'value' => 'Standard International Units', 'tempUnit' => 'C', 'windUnit' => 'MPS', 'visibilityUnit' => 'km'),
367
array('id' => 'uk2', 'value' => 'United Kingdom', 'tempUnit' => 'C', 'windUnit' => 'MPH', 'visibilityUnit' => 'mi'),
368
array('id' => 'us', 'value' => 'United States', 'tempUnit' => 'F', 'windUnit' => 'MPH', 'visibilityUnit' => 'km'),
227
array('id' => 'auto', 'value' => 'Automatically select based on geographic location', 'tempUnit' => ''),
228
array('id' => 'ca', 'value' => 'Canada', 'tempUnit' => 'F'),
229
array('id' => 'si', 'value' => 'Standard International Units', 'tempUnit' => 'C'),
230
array('id' => 'uk', 'value' => 'United Kingdom', 'tempUnit' => 'C'),
231
array('id' => 'us', 'value' => 'United States', 'tempUnit' => 'F'),
376
239
public function supportedLanguages()
379
array('id' => 'ar', 'value' => __('Arabic')),
380
array('id' => 'az', 'value' => __('Azerbaijani')),
381
array('id' => 'be', 'value' => __('Belarusian')),
242
array('id' => 'en', 'value' => __('English')),
382
243
array('id' => 'bs', 'value' => __('Bosnian')),
383
array('id' => 'bg', 'value' => __('Bulgarian')),
384
array('id' => 'ca', 'value' => __('Catalan')),
385
array('id' => 'kw', 'value' => __('Cornish')),
386
array('id' => 'zh', 'value' => __('Simplified Chinese')),
387
array('id' => 'zh-tw', 'value' => __('Traditional Chinese')),
388
array('id' => 'hr', 'value' => __('Croatian')),
389
array('id' => 'cs', 'value' => __('Czech')),
390
array('id' => 'da', 'value' => __('Danish')),
391
array('id' => 'nl', 'value' => __('Dutch')),
392
array('id' => 'ka', 'value' => __('Georgian')),
393
244
array('id' => 'de', 'value' => __('German')),
394
array('id' => 'el', 'value' => __('Greek')),
395
array('id' => 'en', 'value' => __('English')),
396
array('id' => 'et', 'value' => __('Estonian')),
397
array('id' => 'fi', 'value' => __('Finnish')),
245
array('id' => 'es', 'value' => __('Spanish')),
398
246
array('id' => 'fr', 'value' => __('French')),
399
array('id' => 'hu', 'value' => __('Hungarian')),
400
array('id' => 'is', 'value' => __('Icelandic')),
401
array('id' => 'id', 'value' => __('Indonesian')),
402
247
array('id' => 'it', 'value' => __('Italian')),
403
array('id' => 'ja', 'value' => __('Japanese')),
404
array('id' => 'nb', 'value' => __('Norwegian Bokmål')),
248
array('id' => 'nl', 'value' => __('Dutch')),
405
249
array('id' => 'pl', 'value' => __('Polish')),
406
250
array('id' => 'pt', 'value' => __('Portuguese')),
407
251
array('id' => 'ru', 'value' => __('Russian')),
408
array('id' => 'sr', 'value' => __('Serbian')),
409
array('id' => 'sk', 'value' => __('Slovak')),
410
array('id' => 'sl', 'value' => __('Slovenian')),
411
array('id' => 'es', 'value' => __('Spanish')),
412
array('id' => 'sv', 'value' => __('Swedish')),
413
252
array('id' => 'tet', 'value' => __('Tetum')),
414
253
array('id' => 'tr', 'value' => __('Turkish')),
415
array('id' => 'uk', 'value' => __('Ukrainian')),
416
254
array('id' => 'x-pig-latin', 'value' => __('lgpay Atinlay'))
423
public function getTab($tab)
425
if ($tab == 'forecast') {
426
if (!$data = $this->getForecastData(0))
427
throw new NotFoundException(__('No data returned, please check error log.'));
429
foreach ($data['currently'] as $key => $value) {
430
if (stripos($key, 'time')) {
431
$value = $this->getDate()->getLocalDate($value);
433
$rows[] = array('forecast' => __('Current'), 'key' => $key, 'value' => $value);
435
foreach ($data['daily']['data'][0] as $key => $value) {
436
if (stripos($key, 'time')) {
437
$value = $this->getDate()->getLocalDate($value);
439
$rows[] = array('forecast' => __('Daily'), 'key' => $key, 'value' => $value);
441
return ['forecast' => $rows];
442
} else if ($tab == 'exporttemplate') {
444
'template' => json_encode([
447
'designWidth' => $this->getOption('designWidth'),
448
'designHeight' => $this->getOption('designHeight'),
449
'main' => $this->getRawNode('currentTemplate'),
450
'daily' => $this->getRawNode('dailyTemplate'),
451
'css' => $this->getRawNode('styleSheet'),
452
'widgetOriginalWidth' => intval($this->getOption('widgetOriginalWidth')),
453
'widgetOriginalHeight' => intval($this->getOption('widgetOriginalHeight')),
454
'image' => 'preview-image'
261
public function getTab($tab)
263
if (!$data = $this->getForecastData(0))
264
throw new NotFoundException(__('No data returned, please check error log.'));
267
foreach ($data['currently'] as $key => $value) {
268
if (stripos($key, 'time')) {
269
$value = Date::getLocalDate($value);
272
$rows[] = array('forecast' => __('Current'), 'key' => $key, 'value' => $value);
275
foreach ($data['daily']['data'][0] as $key => $value) {
276
if (stripos($key, 'time')) {
277
$value = Date::getLocalDate($value);
280
$rows[] = array('forecast' => __('Daily'), 'key' => $key, 'value' => $value);
283
return ['forecast' => $rows];
463
287
* Get the forecast data for the provided display id
464
288
* @param int $displayId
465
* @return array|boolean
466
* @throws XiboException
468
291
private function getForecastData($displayId)
470
$defaultLat = $this->getConfig()->GetSetting('DEFAULT_LAT');
471
$defaultLong = $this->getConfig()->GetSetting('DEFAULT_LONG');
293
$defaultLat = Config::getSetting('DEFAULT_LAT');
294
$defaultLong = Config::getSetting('DEFAULT_LONG');
473
296
if ($this->getOption('useDisplayLocation') == 1) {
474
297
// Use the display ID or the default.
475
298
if ($displayId != 0) {
477
$display = $this->displayFactory->getById($displayId);
479
if ($display->latitude != '' && $display->longitude != '' && v::latitude()->validate($display->latitude) && v::longitude()->validate($display->longitude)) {
480
$defaultLat = $display->latitude;
481
$defaultLong = $display->longitude;
483
$this->getLog()->info('Warning, display %s does not have a lat/long or they are invalid, and yet a forecast widget is set to use display location.', $display->display);
300
$display = DisplayFactory::getById($displayId);
301
$defaultLat = $display->latitude;
302
$defaultLong = $display->longitude;
487
305
$defaultLat = $this->getOption('latitude', $defaultLat);
488
306
$defaultLong = $this->getOption('longitude', $defaultLong);
491
if (!v::longitude()->validate($defaultLong) || !v::latitude()->validate($defaultLat)) {
492
$this->getLog()->error('Weather widget configured with incorrect lat/long. WidgetId is ' . $this->getWidgetId() . ', Lat is ' . $defaultLat . ', Lng is ' . $defaultLong);
496
309
$apiKey = $this->getSetting('apiKey');
497
310
if ($apiKey == '')
498
311
die(__('Incorrectly configured module'));
500
313
// Query the API and Dump the Results.
501
$apiOptions = array('units' => $this->getOption('units', 'auto'), 'lang' => $this->getOption('lang', 'en'), 'exclude' => 'minutely,hourly');
503
$cache = $this->getPool()->getItem($this->makeCacheKey(md5($defaultLat . $defaultLong . implode('.', $apiOptions))));
504
$data = $cache->get();
506
if ($cache->isMiss()) {
509
$this->getLog()->notice('Getting Forecast from the API');
314
$apiOptions = array('units' => $this->getOption('units', 'auto'), 'lang' => $this->getOption('lang', 'en'), 'exclude' => 'flags,minutely,hourly');
315
$key = md5($defaultLat . $defaultLong . 'null' . implode('.', $apiOptions));
317
if (!Cache::has($key)) {
318
Log::notice('Getting Forecast from the API', $this->getModuleType(), __FUNCTION__);
510
319
if (!$data = $this->get($defaultLat, $defaultLong, null, $apiOptions)) {
323
// If the response is empty, cache it for less time
324
$cacheDuration = $this->getSetting('cachePeriod');
516
$cache->expiresAfter($this->getSetting('cachePeriod', 14400));
517
$this->getPool()->saveDeferred($cache);
327
Cache::put($key, $data, $cacheDuration);
519
$this->getLog()->debug('Getting Forecast from cache');
329
Log::notice('Getting Forecast from the Cache with key: ' . $key, $this->getModuleType(), __FUNCTION__);
330
$data = Cache::get($key);
333
//Debug::Audit('Data: ' . var_export($data, true));
524
337
'unmapped' => 'wi-alien',
552
361
if ($data->currently->icon == 'partly-cloudy-night')
553
362
$data->currently->icon = 'clear-day';
556
// Wind Direction Mappings
557
$cardinalDirections = array(
558
'N' => array(337.5, 22.5),
559
'NE' => array(22.5, 67.5),
560
'E' => array(67.5, 112.5),
561
'SE' => array(112.5, 157.5),
562
'S' => array(157.5, 202.5),
563
'SW' => array(202.5, 247.5),
564
'W' => array(247.5, 292.5),
565
'NW' => array(292.5, 337.5)
569
foreach ($cardinalDirections as $dir => $angles) {
570
if ($data->currently->windBearing >= $angles[0] && $data->currently->windBearing < $angles[1]) {
571
$windDirection = $dir;
576
365
$data->currently->wicon = (isset($icons[$data->currently->icon]) ? $icons[$data->currently->icon] : $icons['unmapped']);
577
366
$data->currently->temperatureFloor = (isset($data->currently->temperature) ? floor($data->currently->temperature) : '--');
578
$data->currently->apparentTemperatureFloor = (isset($data->currently->apparentTemperature) ? floor($data->currently->apparentTemperature) : '--');
579
$data->currently->temperatureRound = (isset($data->currently->temperature) ? round($data->currently->temperature, 0) : '--');
580
$data->currently->apparentTemperatureRound = (isset($data->currently->apparentTemperature) ? round($data->currently->apparentTemperature, 0) : '--');
581
367
$data->currently->summary = (isset($data->currently->summary) ? $data->currently->summary : '--');
582
368
$data->currently->weekSummary = (isset($data->daily->summary) ? $data->daily->summary : '--');
583
369
$data->currently->temperatureUnit = $temperatureUnit;
584
$data->currently->windSpeedUnit = $windSpeedUnit;
585
$data->currently->windDirection = $windDirection;
586
$data->currently->visibilityDistanceUnit = $visibilityDistanceUnit;
587
$data->currently->humidityPercent = (isset($data->currently->humidity)) ? ($data->currently->humidity * 100) : '--';
589
371
// Convert a stdObject to an array
590
372
$data = json_decode(json_encode($data), true);
593
$data['currently']['temperatureMaxFloor'] = (isset($data['daily']['data'][0]['temperatureMax'])) ? floor($data['daily']['data'][0]['temperatureMax']) : '--';
594
$data['currently']['temperatureMinFloor'] = (isset($data['daily']['data'][0]['temperatureMin'])) ? floor($data['daily']['data'][0]['temperatureMin']) : '--';
595
$data['currently']['temperatureMeanFloor'] = ($data['currently']['temperatureMaxFloor'] != '--' && $data['currently']['temperatureMinFloor'] != '--') ? floor((($data['currently']['temperatureMinFloor'] + $data['currently']['temperatureMaxFloor']) / 2)) : '--';
597
$data['currently']['temperatureMaxRound'] = (isset($data['daily']['data'][0]['temperatureMax'])) ? round($data['daily']['data'][0]['temperatureMax'], 0) : '--';
598
$data['currently']['temperatureMinRound'] = (isset($data['daily']['data'][0]['temperatureMin'])) ? round($data['daily']['data'][0]['temperatureMin'], 0) : '--';
599
$data['currently']['temperatureMeanRound'] = ($data['currently']['temperatureMaxRound'] != '--' && $data['currently']['temperatureMinRound'] != '--') ? round((($data['currently']['temperatureMinRound'] + $data['currently']['temperatureMaxRound']) / 2), 0) : '--';
601
374
// Process the icon for each day
602
375
for ($i = 0; $i < 7; $i++) {
603
376
// Are we set to only show daytime weather conditions?
679
433
// Do we need to override the language?
680
434
// TODO: I don't like this date fix, the library should really check the file exists?
681
$lang = $this->getOption('lang', 'en');
682
if ($lang != 'en' && file_exists(PROJECT_ROOT . '/vendor/jenssegers/date/src/Lang/' . $lang . '.php')) {
683
mb_internal_encoding('UTF-8');
684
$this->getLog()->debug('Setting language to: ' . $lang);
685
$this->getDate()->setLocale($lang);
435
if ($this->getOption('lang', 'en') != 'en' && file_exists(PROJECT_ROOT . '/vendor/jenssegers/date/src/Lang/' . $this->getOption('lang') . '.php')) {
436
\Jenssegers\Date\Date::setLocale($this->getOption('lang'));
689
$isPreview = ($this->getSanitizer()->getCheckbox('preview') == 1);
440
$isPreview = (Sanitize::getCheckbox('preview') == 1);
691
442
// Replace the View Port Width?
692
443
$data['viewPortWidth'] = ($isPreview) ? $this->region->width : '[[ViewPortWidth]]';
694
if( $this->getOption('overrideTemplate') == 0 ) {
696
// Get CSS and HTML from the default templates
698
$template = $this->getTemplateById($this->getOption('templateId'));
700
if (isset($template)) {
701
$body = $template['main'];
702
$dailyTemplate = $template['daily'];
703
$styleSheet = $template['css'];
704
$widgetOriginalWidth = $template['widgetOriginalWidth'];
705
$widgetOriginalHeight = $template['widgetOriginalHeight'];
709
// Get CSS and HTML from the override input fields
711
$body = $this->parseLibraryReferences($isPreview, $this->getRawNode('currentTemplate', ''));
712
$dailyTemplate = $this->parseLibraryReferences($isPreview, $this->getRawNode('dailyTemplate', ''));
713
$styleSheet = $this->getRawNode('styleSheet', '');
714
$widgetOriginalWidth = $this->getSanitizer()->int($this->getOption('widgetOriginalWidth'));
715
$widgetOriginalHeight = $this->getSanitizer()->int($this->getOption('widgetOriginalHeight'));
718
// Parse library references
719
$body = $this->parseLibraryReferences($isPreview, $body);
720
$dailyTemplate = $this->parseLibraryReferences($isPreview, $dailyTemplate);
722
// Provide the background images to the templates styleSheet
723
$styleSheet = $this->makeSubstitutions([
724
'cloudy-image' => $this->getResourceUrl('forecastio/wi-cloudy.jpg'),
725
'day-cloudy-image' => $this->getResourceUrl('forecastio/wi-day-cloudy.jpg'),
726
'day-sunny-image' => $this->getResourceUrl('forecastio/wi-day-sunny.jpg'),
727
'fog-image' => $this->getResourceUrl('forecastio/wi-fog.jpg'),
728
'hail-image' => $this->getResourceUrl('forecastio/wi-hail.jpg'),
729
'night-clear-image' => $this->getResourceUrl('forecastio/wi-night-clear.jpg'),
730
'night-partly-cloudy-image' => $this->getResourceUrl('forecastio/wi-night-partly-cloudy.jpg'),
731
'rain-image' => $this->getResourceUrl('forecastio/wi-rain.jpg'),
732
'snow-image' => $this->getResourceUrl('forecastio/wi-snow.jpg'),
733
'windy-image' => $this->getResourceUrl('forecastio/wi-windy.jpg'),
738
<link href="' . $this->getResourceUrl('vendor/bootstrap.min.css') . '" rel="stylesheet" media="screen">
739
446
<link href="' . $this->getResourceUrl('forecastio/weather-icons.min.css') . '" rel="stylesheet" media="screen">
740
<link href="' . $this->getResourceUrl('forecastio/font-awesome.min.css') . '" rel="stylesheet" media="screen">
741
<link href="' . $this->getResourceUrl('forecastio/animate.css') . '" rel="stylesheet" media="screen">
742
<style type="text/css"> body { background-color: transparent }</style>
743
447
<style type="text/css">
744
' . $this->parseLibraryReferences($isPreview, $styleSheet) . '
448
.container { color: ' . $this->getOption('color', '000') . '; }
449
#content { zoom: ' . $this->getOption('size', 1) . '; }
450
' . $this->parseLibraryReferences($isPreview, $this->getRawNode('styleSheet', null)) . '
748
454
// Add our fonts.css file
749
$headContent .= '<link href="' . (($isPreview) ? $this->getApp()->urlFor('library.font.css') : 'fonts.css') . '" rel="stylesheet" media="screen">';
750
$headContent .= '<style type="text/css">' . file_get_contents($this->getConfig()->uri('css/client.css', true)) . '</style>';
455
$headContent .= '<link href="' . $this->getResourceUrl('fonts.css') . '" rel="stylesheet" media="screen">';
456
$headContent .= '<style type="text/css">' . file_get_contents(Theme::uri('css/client.css', true)) . '</style>';
752
458
// Replace any icon sets
753
459
$data['head'] = str_replace('[[ICONS]]', $this->getResourceUrl('forecastio/' . $this->getOption('icons')), $headContent);
755
// Get the JavaScript node
756
$javaScript = $this->parseLibraryReferences($isPreview, $this->getRawNode('javaScript', ''));
461
// Make some body content
462
$body = $this->parseLibraryReferences($isPreview, $this->getRawNode('currentTemplate', null));
463
$dailyTemplate = $this->parseLibraryReferences($isPreview, $this->getRawNode('dailyTemplate', null));
758
465
// Handle the daily template (if its here)
761
preg_match_all('/\[dailyForecast.*?\]/', $body, $matches);
763
foreach ($matches[0] as $sub) {
764
$replace = str_replace('[', '', str_replace(']', '', $sub));
765
// Handling for date/time
767
$stopPosition = $itterations;
769
if (stripos($replace, '|') > -1) {
770
$quantity = explode('|', $replace);
771
$itterations = $quantity[1];
773
if (count($quantity) > 1)
774
$offset = $quantity[2];
776
$stopPosition = (($itterations+$offset) > 7) ? 7 : $itterations+$offset;
466
if (stripos($body, '[dailyForecast]')) {
780
467
// Pull it out, and run substitute over it for each day
781
469
// Substitute for every day (i.e. 7 times).
782
for ($i = $offset; $i < $stopPosition; $i++) {
783
$this->getLog()->debug('Substitiution for Daily, day ' . $i);
784
$dailySubs .= $this->makeSubstitutions($foreCast['daily']['data'][$i], $dailyTemplate, $foreCast['timezone']);
470
for ($i = 0; $i < 7; $i++) {
471
$dailySubs .= $this->makeSubstitutions($foreCast['daily']['data'][$i], $dailyTemplate);
786
474
// Substitute the completed template
787
$body = str_replace($sub, $dailySubs, $body);
475
$body = str_replace('[dailyForecast]', $dailySubs, $body);
791
478
// Run replace over the main template
792
$data['body'] = $this->makeSubstitutions($foreCast['currently'], $body, $foreCast['timezone']);
479
$data['body'] = $this->makeSubstitutions($foreCast['currently'], $body);
794
482
// JavaScript to control the size (override the original width and height so that the widget gets blown up )
795
483
$options = array(
796
'previewWidth' => $this->getSanitizer()->getDouble('width', 0),
797
'previewHeight' => $this->getSanitizer()->getDouble('height', 0),
484
'previewWidth' => Sanitize::getDouble('width', 0),
485
'previewHeight' => Sanitize::getDouble('height', 0),
798
486
'originalWidth' => $this->region->width,
799
487
'originalHeight' => $this->region->height,
800
'scaleOverride' => $this->getSanitizer()->getDouble('scale_override', 0),
801
'widgetDesignWidth' => $widgetOriginalWidth,
802
'widgetDesignHeight'=> $widgetOriginalHeight
488
'scaleOverride' => Sanitize::getDouble('scale_override', 0)
805
491
$javaScriptContent = '<script type="text/javascript" src="' . $this->getResourceUrl('vendor/jquery-1.11.1.min.js') . '"></script>';
806
492
$javaScriptContent .= '<script type="text/javascript" src="' . $this->getResourceUrl('xibo-layout-scaler.js') . '"></script>';
807
$javaScriptContent .= '<script type="text/javascript" src="' . $this->getResourceUrl('xibo-image-render.js') . '"></script>';
808
493
$javaScriptContent .= '<script>
810
495
var options = ' . json_encode($options) . '
812
497
$(document).ready(function() {
813
498
$("body").xiboLayoutScaler(options);
814
$("#content").find("img").xiboImageRender(options);
817
$javaScriptContent .= $javaScript;
819
502
// Replace the After body Content
820
503
$data['javaScript'] = $javaScriptContent;
822
505
// Update and save widget if we've changed our assignments.
823
506
if ($this->hasMediaChanged())
824
$this->widget->save(['saveWidgetOptions' => false, 'notify' => false, 'notifyDisplays' => true, 'audit' => false]);
507
$this->widget->save(['saveWidgetOptions' => false]);
826
509
// Return that content.
827
510
return $this->renderTemplate($data);