Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show error message on Elements with bad configured widget #2116

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/Controller/Layout.php
Original file line number Diff line number Diff line change
Expand Up @@ -1473,7 +1473,7 @@ public function grid(Request $request, Response $response)
$module = $this->moduleFactory->getByType($widget->type);
} catch (NotFoundException $notFoundException) {
// This module isn't available, mark it as invalid.
$widget->isValid = 0;
$widget->isValid = false;
$widget->setUnmatchedProperty('moduleName', __('Invalid Module'));
$widget->setUnmatchedProperty('name', __('Invalid Module'));
$widget->setUnmatchedProperty('tags', []);
Expand Down Expand Up @@ -1504,7 +1504,7 @@ public function grid(Request $request, Response $response)
if (in_array('widget_validity', $embed)) {
$status = 0;
$layout->assessWidgetStatus($module, $widget, $status);
$widget->isValid = $status;
$widget->isValid = $status === 1;
}

// apply default transitions to a dynamic parameters on widget object.
Expand Down
2 changes: 1 addition & 1 deletion lib/Widget/IcsProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public function fetchData(DataProviderInterface $dataProvider): WidgetProviderIn
// Do we have a feed configured?
$uri = $dataProvider->getProperty('uri');
if (empty($uri)) {
throw new InvalidArgumentException('Please enter a the URI to a valid ICS feed.', 'uri');
throw new InvalidArgumentException(__('Please enter the URI to a valid ICS feed.'), 'uri');
}

// Create an ICal helper and pass it the contents of the file.
Expand Down
2 changes: 1 addition & 1 deletion lib/Widget/RssProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public function fetchData(DataProviderInterface $dataProvider): WidgetProviderIn
{
$uri = $dataProvider->getProperty('uri');
if (empty($uri)) {
throw new InvalidArgumentException(__('Please enter a the URI to a valid RSS feed.'), 'uri');
throw new InvalidArgumentException(__('Please enter the URI to a valid RSS feed.'), 'uri');
}

$picoFeedLoggingEnabled = Environment::isDevMode();
Expand Down
17 changes: 4 additions & 13 deletions ui/src/editor-core/properties-panel.js
Original file line number Diff line number Diff line change
Expand Up @@ -528,21 +528,12 @@ PropertiesPanel.prototype.render = function(
// Check if we can use is repeat data
dataToRender.repeatDataActive = hasData;

// Check if we need to show the required elements error message
if (target.requiredElements && target.requiredElements.valid == false) {
const dataType = lD.common.getModuleByType(target.subType).dataType;

// Get element names for the missing elements
const requiredMissingElements =
target.requiredElements.missing.map((el) => {
const elTitle = lD.templateManager.templates[dataType][el].title;
return (elTitle != undefined) ? elTitle : el;
});
// Check required elements
const errorMessage = target.checkRequiredElements();

if (errorMessage != '') {
dataToRender.showErrorMessage = true;
dataToRender.errorMessage =
propertiesPanelTrans.requiredElementsMessage
.replace('%elements%', requiredMissingElements.join(', '));
dataToRender.errorMessage = errorMessage;
}
}

Expand Down
43 changes: 43 additions & 0 deletions ui/src/editor-core/widget.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ const Widget = function(id, data, regionId = null, layoutObject = null) {
this.cachedData = {};
this.forceRecalculateData = false;

this.validateData = {};

this.validateRequiredElements = function() {
const moduleType = this.subType;
// Check if element is required
Expand Down Expand Up @@ -1135,10 +1137,24 @@ Widget.prototype.getData = function() {
data: sampleData,
meta: data?.meta || {},
};

// Save error to widget
self.validateData = {
sampleDataMessage: layoutEditorTrans.showingSampleData,
};

// If we have an error, add it to the validate data
if (data.success === false) {
self.validateData.errorMessage = data.message;
}

resolve(self.cachedData);
}
}
} else {
// Valid, so reset messages
self.validateData = {};

// Run onDataLoad/onParseData
Object.keys(modulesList).forEach(function(item) {
if (modulesList[item].type === self.subType
Expand Down Expand Up @@ -1194,6 +1210,33 @@ Widget.prototype.getData = function() {
return self.cachedDataPromise;
};

/**
* Update element map for this widget
* @return {string} error message
*/
Widget.prototype.checkRequiredElements = function() {
let errorMessage = '';
const self = this;

// Check if we need to show the required elements error message
if (self.requiredElements && self.requiredElements.valid == false) {
const dataType = lD.common.getModuleByType(self.subType).dataType;

// Get element names for the missing elements
const requiredMissingElements =
self.requiredElements.missing.map((el) => {
const elTitle = lD.templateManager.templates[dataType][el].title;
return (elTitle != undefined) ? elTitle : el;
});

errorMessage =
propertiesPanelTrans.requiredElementsMessage
.replace('%elements%', requiredMissingElements.join(', '));
}

return errorMessage;
};

/**
* Update element map for this widget
* @param {object} [element]
Expand Down
35 changes: 35 additions & 0 deletions ui/src/layout-editor/viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -1600,6 +1600,41 @@ Viewer.prototype.renderElementContent = function(
const elData = elementData?.data;
const meta = elementData?.meta;

// If parent widget isn't valid, replace error message
if (!$.isEmptyObject(parentWidget.validateData)) {
const $messageContainer = $elementContainer.find('.invalid-parent');
const errorArray = [$messageContainer.prop('title')];

// Required elements message
const requiredElementsErrorMessage =
parentWidget.checkRequiredElements();

(requiredElementsErrorMessage) &&
errorArray.push(
'<p>' +
requiredElementsErrorMessage +
'</p>');

// Default error message
(parentWidget.validateData.errorMessage) &&
errorArray.push(
'<p>' +
parentWidget.validateData.errorMessage +
'</p>');

(parentWidget.validateData.sampleDataMessage) &&
errorArray.push(
'<p class="sample-data">( ' +
parentWidget.validateData.sampleDataMessage +
' )</p>');

// Set title/tooltip
$messageContainer.tooltip('dispose')
.prop('title', '<div class="custom-tooltip">' +
errorArray.join('') + '</div>');
$messageContainer.tooltip();
}

// Check all data elements and make replacements
for (const key in elData) {
if (elData.hasOwnProperty(key)) {
Expand Down
15 changes: 15 additions & 0 deletions ui/src/style/layout-editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -1903,3 +1903,18 @@ body[layout-editor-fs] .moveable-control-box {
color: $xibo-color-accent !important;
font-size: 1rem !important;
}

/* Custom tooltip */
.custom-tooltip {
text-align: left;
color: $xibo-color-neutral-0;
line-height: 1.2;

p {
margin: 0.3rem 0;
}

.sample-data {
color: $xibo-color-tertiary;
}
}
2 changes: 1 addition & 1 deletion ui/src/templates/viewer-element-content.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
{{/neq}}

{{#if invalidParent}}
<div class="invalid-parent" title="{{trans.invalidWidget}}">
<div class="invalid-parent" data-html="true" title="{{trans.invalidWidget}}">
<i class="fa fa-warning"></i>
</div>
{{/if}}
Expand Down
2 changes: 1 addition & 1 deletion views/common.twig
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,7 @@
left: "{{ "Left" |trans }}",
scale: "{{ "Scale"|trans }}",
layer: "{{ "Layer"|trans }}",
invalidWidget: "{{ "This widget needs to be configured before it will be shown"|trans }}",
invalidWidget: "{{ "This widget needs to be configured before it will be shown."|trans }}",
requiredElementsMessage: "{{ "This widget needs to have at least one of the following elements: %elements%." |trans }}",
dataSlot: "{{ "Data Slot"|trans }}",
dataSlotHelpText: "{{ "When there are more than one of the same element for a widget you can set the slot for each element. For example with two of the same element you'd have data slot 1 and data slot 2. If 10 items were returned slot 1 would receive items 1,3,5,7,9 and slot 2 would receive items 2,4,6,8,19."|trans }}",
Expand Down
3 changes: 2 additions & 1 deletion views/layout-designer-page.twig
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@
unlockMessage: "{% trans "The current layout will be unlocked to other users. You will also be redirected to the Layouts page" %}",
viewModeTitle: "{% trans "View" %}",
actions: "{% trans "Actions" %}",
welcomeModalMessage: "{% trans "This is published and cannot be edited. You can checkout for editing below, or continue to view it in a read only mode." %}"
welcomeModalMessage: "{% trans "This is published and cannot be edited. You can checkout for editing below, or continue to view it in a read only mode." %}",
showingSampleData: "{% trans "Showing sample data" %}",
};

var viewerTrans = {
Expand Down
Loading