3
* Xibo - Digital Signage - http://www.xibo.org.uk
4
* Copyright (C) 2014-2015 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/>.
22
namespace Xibo\Widget;
24
use GuzzleHttp\Exception\RequestException;
25
use Stash\Invalidation;
26
use Xibo\Exception\InvalidArgumentException;
27
use Xibo\Exception\NotFoundException;
28
use Xibo\Factory\ModuleFactory;
32
* @package Xibo\Widget
34
class Currencies extends AlphaVantageBase
36
public $codeSchemaVersion = 1;
39
* Install or Update this module
40
* @param ModuleFactory $moduleFactory
42
public function installOrUpdate($moduleFactory)
44
if ($this->module == null) {
46
$module = $moduleFactory->createEmpty();
47
$module->name = 'Currencies';
48
$module->type = 'currencies';
49
$module->class = 'Xibo\Widget\Currencies';
50
$module->description = 'A module for showing Currency pairs and exchange rates';
51
$module->imageUri = 'forms/library.gif';
53
$module->previewEnabled = 1;
54
$module->assignable = 1;
55
$module->regionSpecific = 1;
56
$module->renderAs = 'html';
57
$module->schemaVersion = $this->codeSchemaVersion;
58
$module->defaultDuration = 30;
59
$module->settings = [];
61
$this->setModule($module);
62
$this->installModule();
65
// Check we are all installed
66
$this->installFiles();
72
public function installFiles()
74
$this->mediaFactory->createModuleSystemFile(PROJECT_ROOT . '/modules/vendor/jquery-1.11.1.min.js')->save();
75
$this->mediaFactory->createModuleSystemFile(PROJECT_ROOT . '/modules/xibo-finance-render.js')->save();
76
$this->mediaFactory->createModuleSystemFile(PROJECT_ROOT . '/modules/xibo-layout-scaler.js')->save();
77
$this->mediaFactory->createModuleSystemFile(PROJECT_ROOT . '/modules/xibo-image-render.js')->save();
78
$this->mediaFactory->createModuleSystemFile(PROJECT_ROOT . '/modules/vendor/bootstrap.min.css')->save();
82
* Form for updating the module settings
84
public function settingsForm()
86
return 'currencies-form-settings';
90
* Process any module settings
92
public function settings()
94
$this->module->settings['apiKey'] = $this->getSanitizer()->getString('apiKey');
95
$this->module->settings['cachePeriod'] = $this->getSanitizer()->getInt('cachePeriod', 300);
97
// Return an array of the processed settings.
98
return $this->module->settings;
103
* @throws InvalidArgumentException
105
public function validate()
107
if($this->getOption('overrideTemplate') == 0 && ( $this->getOption('templateId') == '' || $this->getOption('templateId') == null) )
108
throw new InvalidArgumentException(__('Please choose a template'), 'templateId');
110
if ($this->getUseDuration() == 1 && $this->getDuration() == 0)
111
throw new InvalidArgumentException(__('Please enter a duration'), 'duration');
113
// Validate for the items field
114
if ($this->getOption('items') == '')
115
throw new InvalidArgumentException(__('Please provide a comma separated list of symbols in the items field.'), 'items');
117
if ($this->getOption('base') == '')
118
throw new InvalidArgumentException(__('Please provide a symbols in the base field.'), 'base');
122
* Adds a Currencies Widget
124
* path="/playlist/widget/currencies/{playlistId}",
125
* operationId="WidgetCurrenciesAdd",
127
* summary="Add a Currencies Widget",
128
* description="Add a new Currencies Widget to the specified playlist",
132
* description="The playlist ID to add a Currencies widget",
139
* description="Optional Widget Name",
146
* description="Widget Duration",
151
* name="useDuration",
153
* description="(0, 1) Select 1 only if you will provide duration parameter as well",
160
* description="The base currency",
167
* description="Items wanted",
172
* name="reverseConversion",
174
* description="(0, 1) Select 1 if you'd like your base currency to be used as the comparison currency you've entered",
181
* description="Effect that will be used to transitions between items, available options: fade, fadeout, scrollVert, scollHorz, flipVert, flipHorz, shuffle, tileSlide, tileBlind ",
188
* description="The transition speed of the selected effect in milliseconds (1000 = normal)",
193
* name="backgroundColor",
195
* description="A HEX color to use as the background color of this widget",
200
* name="noRecordsMessage",
202
* description="A message to display when there are no records returned by the search query",
209
* description="The format to apply to all dates returned by he widget",
214
* name="updateInterval",
216
* description="Update interval in minutes, should be kept as high as possible, if data change once per hour, this should be set to 60",
221
* name="durationIsPerPage",
223
* description="A flag (0, 1), The duration specified is per page/item, otherwise the widget duration is divided between the number of pages/items",
230
* description="Use pre-configured templates, available options: currencies1, currencies2",
235
* name="overrideTemplate",
237
* description="flag (0, 1) set to 0 and use templateId or set to 1 and provide whole template in the next parameters",
242
* name="widgetOriginalWidth",
244
* 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",
249
* name="widgetOriginalHeight",
251
* 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",
256
* name="maxItemsPerPage",
258
* description="This is the intended number of items on each page",
263
* name="mainTemplate",
265
* description="Main template, Pass only with overrideTemplate set to 1 ",
270
* name="itemtemplate",
272
* description="Template for each item, replaces [itemsTemplate] in main template, Pass only with overrideTemplate set to 1 ",
279
* description="Optional StyleSheet Pass only with overrideTemplate set to 1 ",
286
* description="Optional JavaScript, Pass only with overrideTemplate set to 1 ",
292
* description="successful operation",
293
* @SWG\Schema(ref="#/definitions/Widget"),
296
* description="Location of the new widget",
302
public function add()
304
$this->setCommonOptions();
314
public function edit()
316
$this->setCommonOptions();
323
public function setCommonOptions()
325
$this->setDuration($this->getSanitizer()->getInt('duration', $this->getDuration()));
326
$this->setUseDuration($this->getSanitizer()->getCheckbox('useDuration'));
327
$this->setOption('name', $this->getSanitizer()->getString('name'));
328
$this->setOption('base', $this->getSanitizer()->getString('base', ''));
329
$this->setOption('items', $this->getSanitizer()->getString('items'));
330
$this->setOption('effect', $this->getSanitizer()->getString('effect'));
331
$this->setOption('speed', $this->getSanitizer()->getInt('speed'));
332
$this->setOption('backgroundColor', $this->getSanitizer()->getString('backgroundColor'));
333
$this->setOption('noRecordsMessage', $this->getSanitizer()->getString('noRecordsMessage'));
334
$this->setOption('dateFormat', $this->getSanitizer()->getString('dateFormat'));
335
$this->setOption('reverseConversion', $this->getSanitizer()->getCheckbox('reverseConversion'));
336
$this->setOption('updateInterval', $this->getSanitizer()->getInt('updateInterval', 60));
337
$this->setOption('templateId', $this->getSanitizer()->getString('templateId'));
338
$this->setOption('durationIsPerPage', $this->getSanitizer()->getCheckbox('durationIsPerPage'));
339
$this->setRawNode('javaScript', $this->getSanitizer()->getParam('javaScript', ''));
340
$this->setOption('overrideTemplate', $this->getSanitizer()->getCheckbox('overrideTemplate'));
342
if( $this->getOption('overrideTemplate') == 1 ){
343
$this->setRawNode('mainTemplate', $this->getSanitizer()->getParam('mainTemplate', $this->getSanitizer()->getParam('mainTemplate', null)));
344
$this->setRawNode('itemTemplate', $this->getSanitizer()->getParam('itemTemplate', $this->getSanitizer()->getParam('itemTemplate', null)));
345
$this->setRawNode('styleSheet', $this->getSanitizer()->getParam('styleSheet', $this->getSanitizer()->getParam('styleSheet', null)));
346
$this->setOption('widgetOriginalWidth', $this->getSanitizer()->getInt('widgetOriginalWidth'));
347
$this->setOption('widgetOriginalHeight', $this->getSanitizer()->getInt('widgetOriginalHeight'));
348
$this->setOption('maxItemsPerPage', $this->getSanitizer()->getInt('maxItemsPerPage', 4));
354
* @return array|bool an array of results. false if an invalid value is returned.
356
protected function getResults()
358
// Does this require a reversed conversion?
359
$reverseConversion = ($this->getOption('reverseConversion', 0) == 1);
361
// What items/base currencies are we interested in?
362
$items = $this->getOption('items');
363
$base = $this->getOption('base');
365
if ($items == '' || $base == '' ) {
366
$this->getLog()->error('Missing Items for Currencies Module with WidgetId ' . $this->getWidgetId());
370
// Parse items out into an array
371
$items = explode(',', $items);
373
// Get current item template
374
$itemTemplate = null;
376
if ($this->getOption('overrideTemplate') == 0) {
377
$template = $this->getTemplateById($this->getOption('templateId'));
379
if (isset($template)) {
380
$itemTemplate = $template['item'];
383
$itemTemplate = $this->getRawNode('itemTemplate');
386
// Does the template require a percentage change calculation.
387
$percentageChangeRequested = stripos($itemTemplate, '[ChangePercentage]') > -1;
389
// Our cache key is based on the base/items/changepercentage
390
/** @var \Stash\Item $cache */
391
$cache = $this->getPool()->getItem($this->makeCacheKey(md5($base . implode(',', $items) . $percentageChangeRequested . $reverseConversion)));
392
$cache->setInvalidationMethod(Invalidation::SLEEP, 5000, 15);
394
$data = $cache->get();
396
if ($cache->isMiss()) {
397
// Lock this cache record
404
// Do we need to get the data for percentage change?
405
if ($percentageChangeRequested && !$reverseConversion) {
408
$priorDay = $this->getPriorDay($base, $items);
410
$this->getLog()->debug('Percentage change requested, prior day is ' . var_export($priorDay, true));
412
} catch (RequestException $requestException) {
413
$this->getLog()->error('Problem getting percentage change currency information. E = ' . $requestException->getMessage());
414
$this->getLog()->debug($requestException->getTraceAsString());
418
// Each item we want is a call to the results API
420
foreach ($items as $currency) {
421
// Remove the multiplier if there's one (this is handled when we substitute the results into the template)
422
$currency = explode('|', $currency)[0];
424
// Do we need to reverse the from/to currency for this comparison?
425
if ($reverseConversion) {
426
$result = $this->getCurrencyExchangeRate($currency, $base);
428
// We need to get the proir day for this pair only (reversed)
429
$priorDay = $this->getPriorDay($currency, $base);
431
$this->getLog()->debug('Percentage change requested, prior day is ' . var_export($priorDay, true));
434
$result = $this->getCurrencyExchangeRate($base, $currency);
437
$this->getLog()->debug('Results are: ' . var_export($result, true));
440
'time' => $result['Realtime Currency Exchange Rate']['6. Last Refreshed'],
441
'ToName' => $result['Realtime Currency Exchange Rate']['3. To_Currency Code'],
442
'ToCurrency' => $result['Realtime Currency Exchange Rate']['4. To_Currency Name'],
443
'FromName' => $result['Realtime Currency Exchange Rate']['1. From_Currency Code'],
444
'FromCurrency' => $result['Realtime Currency Exchange Rate']['2. From_Currency Name'],
445
'Bid' => round($result['Realtime Currency Exchange Rate']['5. Exchange Rate'], 4),
446
'Ask' => round($result['Realtime Currency Exchange Rate']['5. Exchange Rate'], 4),
447
'LastTradePriceOnly' => round($result['Realtime Currency Exchange Rate']['5. Exchange Rate'], 4),
448
'RawLastTradePriceOnly' => $result['Realtime Currency Exchange Rate']['5. Exchange Rate'],
449
'TimeZone' => $result['Realtime Currency Exchange Rate']['7. Time Zone'],
452
// Set the name/currency to be the full name including the base currency
453
$parsedResult['Name'] = $parsedResult['FromName'] . '/' . $parsedResult['ToName'];
454
$parsedResult['Currency'] = $parsedResult['FromCurrency'] . '/' . $parsedResult['ToCurrency'];
456
// work out the change when compared to the previous day
457
if ($percentageChangeRequested && isset($priorDay[$parsedResult['ToName']]) && is_numeric($priorDay[$parsedResult['ToName']])) {
458
$parsedResult['YesterdayTradePriceOnly'] = $priorDay[$parsedResult['ToName']];
459
$parsedResult['Change'] = $parsedResult['RawLastTradePriceOnly'] - $parsedResult['YesterdayTradePriceOnly'];
461
$parsedResult['YesterdayTradePriceOnly'] = 0;
462
$parsedResult['Change'] = 0;
465
// Parse the result and add it to our data array
466
$data[] = $parsedResult;
468
} catch (RequestException $requestException) {
469
$this->getLog()->error('Problem getting currency information. E = ' . $requestException->getMessage());
470
$this->getLog()->debug($requestException->getTraceAsString());
475
$this->getLog()->debug('Parsed Results are: ' . var_export($data, true));
479
$cache->expiresAfter($this->getSetting('cachePeriod', 3600));
480
$this->getPool()->saveDeferred($cache);
487
* Run through the data and substitute into the template
490
* @param $baseCurrency
493
private function makeSubstitutions($data, $source, $baseCurrency)
495
// Replace all matches.
497
preg_match_all('/\[.*?\]/', $source, $matches);
499
// Get the currencies' items
500
$items = $this->getOption('items');
502
if (strstr($items, ','))
503
$items = explode(',', $items);
507
$reverseConversion = ($this->getOption('reverseConversion', 0) == 1);
510
foreach ($matches[0] as $sub) {
511
$replace = str_replace('[', '', str_replace(']', '', $sub));
512
$replacement = 'NULL';
514
$isPreview = ($this->getSanitizer()->getCheckbox('preview') == 1);
516
// Match that in the array
517
if (isset($data[$replace])) {
518
// If the tag exists on the data variables use that var
519
$replacement = $data[$replace];
523
// Replace the time tag
524
if (stripos($replace, 'time|') > -1) {
525
$timeSplit = explode('|', $replace);
527
$time = $this->getDate()->parse($data['time']. 'Y-m-d H:i:s')->format($timeSplit[1]);
529
$replacement = $time;
531
} else if (stripos($replace, 'NameTrimmed|') > -1) {
532
$nameSplit = explode('|', $replace);
533
$name = $data['Name'];
535
// Remove the last word until the string is inside the pretended Serializable
536
while (strlen($name) > $nameSplit[1]) {
537
$name = substr($name, 0, strrpos($name, " "));
540
$replacement = strtoupper($name);
544
// Replace the other tags
548
$replacement = ($reverseConversion) ? $data['FromName'] : $data['ToName'];
554
// Initialize replacement with empty string
557
// Get the current currency name/code
558
$currencyName = ($reverseConversion) ? $data['FromName'] : $data['ToName'];
560
// Search for the item that relates to the actual currency
561
foreach ($items as $item) {
564
$itemName = trim(explode('|', $item)[0]);
566
// Compare the item name with the actual currency and test if the inputed value has a multiplier flag
567
if( sizeof(explode('|', $item)) > 1 && strcmp($itemName, $currencyName) == 0 ){
569
// Get the multiplier
570
$replacement = explode('|', $item)[1];
578
$currencyCode = ($reverseConversion) ? $data['FromName'] : $data['ToName'];
580
if (!file_exists(PROJECT_ROOT . '/modules/currencies/currency-flags/' . $currencyCode . '.svg'))
581
$currencyCode = 'default';
583
$file = $this->mediaFactory->createModuleFile('currency_' . $currencyCode, PROJECT_ROOT . '/modules/currencies/currency-flags/' . $currencyCode . '.svg');
584
$file->alwaysCopy = true;
585
$file->storedAs = 'currency_' . $currencyCode . '.svg';
588
// Tag this layout with this file
589
$this->assignMedia($file->mediaId);
591
$replacement = $this->getFileUrl($file);
595
case 'LastTradePriceOnlyValue':
599
// Get the converted currency name
600
$currencyName = ($reverseConversion) ? $data['FromName'] : $data['ToName'];
602
// Get the field's name and set the replacement as the default value from the API
603
$fieldName = str_replace('Value', '', $replace);
604
$replacement = $data[$fieldName];
606
// Search for the item that relates to the actual currency
607
foreach ($items as $item) {
610
$itemName = trim(explode('|', $item)[0]);
612
// Compare the item name with the actual currency and test if the inputed value has a multiplier flag
613
if( sizeof(explode('|', $item)) > 1 && strcmp($itemName, $currencyName) == 0 ){
614
// Get the multiplier
615
$multiplier = explode('|', $item)[1];
617
// Set the replacement to be the API value times the multiplier
618
$replacement = $data[$fieldName] * (float)$multiplier;
624
case 'ChangePercentage':
625
// Protect against null values
626
if(($data['Change'] == null || $data['LastTradePriceOnly'] == null)){
627
$replacement = "NULL";
629
// Calculate the percentage dividing the change by the ( previous value minus the change )
630
$percentage = $data['Change'] / ( $data['LastTradePriceOnly'] - $data['Change'] );
632
// Convert the value to percentage and round it
633
$replacement = round($percentage*100, 2);
639
// Default value as no change
640
$replacement = 'value-equal';
642
// Protect against null values
643
if (($data['Change'] != null && $data['LastTradePriceOnly'] != null)) {
645
if ($data['Change'] > 0) {
646
$replacement = 'value-up';
647
} else if ( $data['Change'] < 0 ){
648
$replacement = 'value-down';
656
// Default value as no change
657
$replacement = 'right-arrow';
659
// Protect against null values
660
if (($data['Change'] != null && $data['LastTradePriceOnly'] != null)) {
662
if ( $data['Change'] > 0 ) {
663
$replacement = 'up-arrow';
664
} else if ( $data['Change'] < 0 ){
665
$replacement = 'down-arrow';
671
case 'CurrencyUpper':
672
// Currency in uppercase
673
$replacement = strtoupper($data['Currency']);
678
$replacement = 'NULL';
685
// Replace the variable on the source string
686
$source = str_replace($sub, $replacement, $source);
695
public function getTab($tab)
697
if (!$data = $this->getResults())
698
throw new NotFoundException(__('No data returned, please check error log.'));
700
return ['results' => $data[0]];
705
* @param int $displayId
708
public function getResource($displayId = 0)
711
$isPreview = ($this->getSanitizer()->getCheckbox('preview') == 1);
713
// Replace the View Port Width?
714
$data['viewPortWidth'] = ($isPreview) ? $this->region->width : '[[ViewPortWidth]]';
716
// Information from the Module
717
$duration = $this->getCalculatedDurationForGetResource();
718
$durationIsPerItem = $this->getOption('durationIsPerItem', 1);
720
// Generate a JSON string of items.
721
if (!$items = $this->getResults()) {
725
if( $this->getOption('overrideTemplate') == 0 ) {
727
$template = $this->getTemplateById($this->getOption('templateId'));
729
if (isset($template)) {
730
$mainTemplate = $template['main'];
731
$itemTemplate = $template['item'];
732
$styleSheet = $template['css'];
733
$widgetOriginalWidth = $template['widgetOriginalWidth'];
734
$widgetOriginalHeight = $template['widgetOriginalHeight'];
735
$maxItemsPerPage = $template['maxItemsPerPage'];
740
$mainTemplate = $this->getRawNode('mainTemplate');
741
$itemTemplate = $this->getRawNode('itemTemplate');
742
$styleSheet = $this->getRawNode('styleSheet', '');
743
$widgetOriginalWidth = $this->getSanitizer()->int($this->getOption('widgetOriginalWidth'));
744
$widgetOriginalHeight = $this->getSanitizer()->int($this->getOption('widgetOriginalHeight'));
745
$maxItemsPerPage = $this->getSanitizer()->int($this->getOption('maxItemsPerPage'));
748
// Run through each item and substitute with the template
749
$mainTemplate = $this->parseLibraryReferences($isPreview, $mainTemplate);
750
$itemTemplate = $this->parseLibraryReferences($isPreview, $itemTemplate);
754
$base = $this->getOption('base');
756
foreach ($items as $item) {
757
$renderedItems[] = $this->makeSubstitutions($item, $itemTemplate, $base);
761
'type' => $this->getModuleType(),
762
'fx' => $this->getOption('effect', 'none'),
763
'speed' => $this->getOption('speed', 500),
764
'duration' => $duration,
765
'durationIsPerPage' => ($this->getOption('durationIsPerPage', 0) == 1),
766
'numItems' => count($renderedItems),
767
'originalWidth' => $this->region->width,
768
'originalHeight' => $this->region->height,
769
'previewWidth' => $this->getSanitizer()->getDouble('width', 0),
770
'previewHeight' => $this->getSanitizer()->getDouble('height', 0),
771
'widgetDesignWidth' => $widgetOriginalWidth,
772
'widgetDesignHeight'=> $widgetOriginalHeight,
773
'scaleOverride' => $this->getSanitizer()->getDouble('scale_override', 0),
774
'maxItemsPerPage' => $maxItemsPerPage
777
$itemsPerPage = $options['maxItemsPerPage'];
778
$pages = count($renderedItems);
779
$pages = ($itemsPerPage > 0) ? ceil($pages / $itemsPerPage) : $pages;
780
$totalDuration = ($durationIsPerItem == 0) ? $duration : ($duration * $pages);
782
// Replace and Control Meta options
783
$data['controlMeta'] = '<!-- NUMITEMS=' . $pages . ' -->' . PHP_EOL . '<!-- DURATION=' . $totalDuration . ' -->';
785
// Get the JavaScript node
786
$javaScript = $this->parseLibraryReferences($isPreview, $this->getRawNode('javaScript', ''));
788
// Replace the head content
791
// Add our fonts.css file
792
$headContent .= '<link href="' . (($isPreview) ? $this->getApp()->urlFor('library.font.css') : 'fonts.css') . '" rel="stylesheet" media="screen">
793
<link href="' . $this->getResourceUrl('vendor/bootstrap.min.css') . '" rel="stylesheet" media="screen">';
795
$backgroundColor = $this->getOption('backgroundColor');
796
if ($backgroundColor != '') {
797
$headContent .= '<style type="text/css"> body { background-color: ' . $backgroundColor . ' }</style>';
799
$headContent .= '<style type="text/css"> body { background-color: transparent }</style>';
802
// Add the CSS if it isn't empty, and replace the wallpaper
803
$css = $this->makeSubstitutions([], $styleSheet, '');
806
$headContent .= '<style type="text/css">' . $this->parseLibraryReferences($isPreview, $css) . '</style>';
808
$headContent .= '<style type="text/css">' . file_get_contents($this->getConfig()->uri('css/client.css', true)) . '</style>';
810
// Replace the Head Content with our generated javascript
811
$data['head'] = $headContent;
813
// Add some scripts to the JavaScript Content
814
$javaScriptContent = '<script type="text/javascript" src="' . $this->getResourceUrl('vendor/jquery-1.11.1.min.js') . '"></script>';
816
$javaScriptContent .= '<script type="text/javascript" src="' . $this->getResourceUrl('vendor/jquery-cycle-2.1.6.min.js') . '"></script>';
818
$javaScriptContent .= '<script type="text/javascript" src="' . $this->getResourceUrl('xibo-layout-scaler.js') . '"></script>';
819
$javaScriptContent .= '<script type="text/javascript" src="' . $this->getResourceUrl('xibo-finance-render.js') . '"></script>';
820
$javaScriptContent .= '<script type="text/javascript" src="' . $this->getResourceUrl('xibo-image-render.js') . '"></script>';
822
$javaScriptContent .= '<script type="text/javascript">';
823
$javaScriptContent .= ' var options = ' . json_encode($options) . ';';
824
$javaScriptContent .= ' var items = ' . json_encode($renderedItems) . ';';
825
$javaScriptContent .= ' var body = ' . json_encode($mainTemplate) . ';';
826
$javaScriptContent .= ' $(document).ready(function() { ';
827
$javaScriptContent .= ' $("body").xiboLayoutScaler(options); $("#content").xiboFinanceRender(options, items, body); $("#content").find("img").xiboImageRender(options); ';
828
$javaScriptContent .= ' }); ';
829
$javaScriptContent .= $javaScript;
830
$javaScriptContent .= '</script>';
832
// Replace the Head Content with our generated javascript
833
$data['javaScript'] = $javaScriptContent;
835
// Update and save widget if we've changed our assignments.
836
if ($this->hasMediaChanged())
837
$this->widget->save(['saveWidgetOptions' => false, 'notify' => false, 'notifyDisplays' => true, 'audit' => false]);
839
return $this->renderTemplate($data);
842
public function isValid()
844
// Using the information you have in your module calculate whether it is valid or not.