Files
Cloud-CMS/views/dataset-data-connector-page.twig
Matt Batchelder 05ce0da296 Initial Upload
2025-12-02 10:32:59 -05:00

277 lines
14 KiB
Twig

{#
/*
* Copyright (C) 2024 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
#}
{% extends "authed.twig" %}
{% import "inline.twig" as inline %}
{% import "forms.twig" as forms %}
{% block title %}{% set dataSetName = dataSet.dataSet %}{% trans %}{{ dataSetName }} - Data Connector{% endtrans %} | {% endblock %}
{% set hideNavigation = "1" %}
{% block pageContent %}
<div id="data-connector-builder"
data-data-set-id="{{ dataSet.dataSetId }}">
<div class="back-button">
<a id="backBtn" class="btn btn-primary" href="{{ url_for("dataset.view") }}">
<i class="fa fa-angle-left"></i>
<span>{{ "Back"|trans }}</span>
</a>
</div>
<div class="widget mt-3">
<div class="widget-body">
<div class="row">
<div class="col-12">
<div class="data-set-title">
<h1>{{ dataSetName }}</h1>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-6 {% if dataSet.dataConnectorSource != 'user_defined' %}hidden{% endif %}">
<form id="dataconnector-builder-form" class="XiboForm form-horizontal"
method="put"
action="{{ url_for("dataSet.dataConnector.update", {id: dataSet.dataSetId}) }}"
data-submit-call-back="onSubmitCallback"
>
<div class="form-group row code-input-group xibo-code-input">
<div class="col-sm-12">
<small class="form-text text-muted">{{ "Data Connector JavaScript"|trans }}</small>
<textarea class="form-control d-none code-input" id="input_script" name="dataConnectorScript" rows="30" data-code-type="javascript">{% if script %}{{ script }}{% else %}window.onInit = function() {
}{% endif %}</textarea>
<div class="code-input-editor-container" style="height: 70vh;">
<div class="code-input-editor"></div>
</div>
</div>
</div>
{{ forms.button("Save"|trans, "submit", null, null, null, "btn-success " ~ (dataSet.dataConnectorSource != 'user_defined' ? 'disabled' : '')) }}
</form>
</div>
<div class="col-lg-6">
<div class="row">
<div class="col-md-12">
<ul class="nav nav-tabs" role="tablist">
<li class="nav-item">
<a class="nav-link" href="#tab-testParams" role="tab" data-toggle="tab">
<span>{% trans "Test Params" %}</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="#tab-logs" role="tab" data-toggle="tab">
<span>{% trans "Logs" %}</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#tab-dataSet" role="tab" data-toggle="tab">
<span>{% trans "DataSet Data" %}</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#tab-otherData" role="tab" data-toggle="tab">
<span>{% trans "Other Data" %}</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#tab-scheduleCriteria" role="tab" data-toggle="tab">
<span>{% trans "Schedule Criteria" %}</span>
</a>
</li>
</ul>
<div class="tab-content">
<div class="tab-pane" id="tab-testParams">
{{ inline.message("You can test passing parameters that would otherwise be set when this Data Connector is scheduled."|trans, "alert alert-info") }}
{{ inline.input("dataSetRealtimeTestParams", "Test Parameters"|trans) }}
</div>
<div class="tab-pane active" id="tab-logs">
<pre id="dataconnector-logs"></pre>
</div>
<div class="tab-pane" id="tab-dataSet">
<div class="table-container">
<table id="dataconnector-main-data" class="table">
<thead>
{% for column in dataSet.getColumn() %}
<th>{{ column.heading }}</th>
{% endfor %}
<th>Unmapped</th>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
<div class="tab-pane" id="tab-otherData">
<pre id="dataconnector-other-data"></pre>
</div>
<div class="tab-pane" id="tab-scheduleCriteria">
<div class="table-container">
<table id="dataconnector-schedule-criteria" class="table">
<thead>
<th>{{ "Metric"|trans }}</th>
<th>{{ "Value"|trans }}</th>
<th>TTL</th>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row" style="display: none;" id="dataconnector-script"></div>
</div>
{% endblock %}
{% block javaScript %}
<script type="text/javascript" nonce="{{ cspNonce }}">
$(function() {
const $script = $('#dataconnector-script');
const $scriptParams = $('#dataSetRealtimeTestParams');
const $mainData = $('#dataconnector-main-data');
const $otherData = $('#dataconnector-other-data');
const $scheduleCriteria = $('#dataconnector-schedule-criteria');
const $logs = $('#dataconnector-logs');
let otherData = {};
let criteria = {};
// Set up a channel which will broadcast data
const channel = new BroadcastChannel('xiboDC');
// Set our script params from local storage if we have them
$scriptParams.val(localStorage.getItem('dataSetRealtimeTestParams'));
// Output the iframe containing the window
$script.html('<iframe src="{{ url_for("dataSet.dataConnector.test", {id: dataSet.dataSetId}) }}" />');
// Window message to receive data and logs.
window.receiveData = function(type, data) {
if (type === 'loaded') {
console.debug('Script loaded');
$script.find('iframe')[0].contentWindow.xiboDC.initialise({{ dataSet.dataSetId }}, $scriptParams.val());
} else if (type === 'log') {
$logs.prepend('[' + moment().format('YY-MM-DD HH:mm:ss') + '] ' + data + '\n');
} else if (type === 'set') {
// Update the table
// if the dataKey matches my connector's DataSetId, then render out a table
if (data.dataKey == '{{ dataSet.dataSetId }}') {
// Data is always set as a string
const events = JSON.parse(data.data);
if (Array.isArray(events)) {
const $tableBody = $mainData.find('tbody');
$tableBody.find('tr').remove();
$.each(events, function (rowIndex, row) {
// Make a new row
let html = '<tr>';
{% for column in dataSet.getColumn() %}
html += '<td data-id="{{ column.heading }}"></td>';
{% endfor %}
html += '<td data-id="unmatched"></td></tr>';
const $newRow = $(html);
$tableBody.append($newRow);
// Do we have a column for this item
$.each(row, function (colIndex, col) {
if ($newRow.find('td[data-id=' + colIndex).length > 0) {
$newRow.find('td[data-id=' + colIndex).append(row[colIndex]);
} else {
$newRow.find('td[data-id=unmatched').append(colIndex + ': ' + row[colIndex] + '<br/>');
}
});
});
} else {
// Treat it as other data.
otherData[data.dataKey] = data.data;
$otherData.html(JSON.stringify(otherData, null, 4));
}
} else {
// Grab the existing "other data" and see if there is a matching key.
otherData[data.dataKey] = data.data;
$otherData.html(JSON.stringify(otherData, null, 4));
}
// Broadcast to interested parties.
// Use the original data.data (which is a string)
channel.postMessage({type: 'xiboDC_data', dataKey: data.dataKey, data: data.data});
} else if (type === 'notify') {
// Log
$logs.prepend('[' + moment().format('YY-MM-DD HH:mm:ss') + '] Notify for ' + data + '\n');
channel.postMessage({type: "xiboDC_notify", dataKey: data});
} else if (type === 'criteria') {
// Schedule criteria, update in the table.
criteria[data.dataKey] = data.data;
const $tableBody = $scheduleCriteria.find('tbody');
$.each(criteria, function (key, value) {
$tableBody.append('<tr><td>' + key + '</td><td>' + value.value + '</td><td>' + value.ttl + '</td></tr>');
});
}
}
window.makeRequest = function (path, {type, headers, data, done, error} = {}) {
$.ajax('{{ url_for("dataSet.dataConnector.request", {id: dataSet.dataSetId}) }}', {
data: {
url: path,
method: type,
headers: headers,
body: data
},
success: function(data, textStatus, jqXHR) {
if (typeof(done) == 'function') {
done(jqXHR.status, data);
}
},
error: function(jqXHR, textStatus, errorThrown) {
if (typeof(done) == 'function') {
error(jqXHR.status, jqXHR.responseText);
}
}
});
}
// Refresh the iframe.
window.onSubmitCallback = function(xhr, form) {
$script.find('iframe')[0].contentWindow.location.reload();
}
$scriptParams.on('change', function() {
$script.find('iframe')[0].contentWindow.xiboDC.initialise({{ dataSet.dataSetId }}, $scriptParams.val());
localStorage.setItem('dataSetRealtimeTestParams', $scriptParams.val());
});
});
</script>
{# Add code editor bundle #}
<script type="text/javascript" src="{{ theme.rootUri() }}dist/codeEditor.bundle.min.js?v={{ version }}&rev={{revision}}" nonce="{{ cspNonce }}" defer></script>
{% endblock %}