Cleans up old files
parent
e6e91b19d0
commit
f952257c20
|
@ -1,228 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
// Get any new messages
|
|
||||||
include '../app/includes/messages.php';
|
|
||||||
include '../app/includes/messages-show.php';
|
|
||||||
|
|
||||||
$action = $_REQUEST['action'] ?? '';
|
|
||||||
$agent = $_REQUEST['agent'] ?? '';
|
|
||||||
|
|
||||||
require '../app/classes/settings.php';
|
|
||||||
require '../app/classes/agent.php';
|
|
||||||
require '../app/classes/conference.php';
|
|
||||||
require '../app/classes/host.php';
|
|
||||||
|
|
||||||
$settingsObject = new Settings();
|
|
||||||
$agentObject = new Agent($dbWeb);
|
|
||||||
$hostObject = new Host($dbWeb);
|
|
||||||
|
|
||||||
// connect to Jilo database
|
|
||||||
$response = connectDB($config, 'jilo', $platformDetails[0]['jilo_database'], $platform_id);
|
|
||||||
|
|
||||||
// if DB connection has error, display it and stop here
|
|
||||||
if ($response['db'] === null) {
|
|
||||||
Messages::flash('ERROR', 'DEFAULT', $response['error']);
|
|
||||||
|
|
||||||
// otherwise if DB connection is OK, go on
|
|
||||||
} else {
|
|
||||||
$db = $response['db'];
|
|
||||||
|
|
||||||
$conferenceObject = new Conference($db);
|
|
||||||
|
|
||||||
switch ($item) {
|
|
||||||
|
|
||||||
case 'graphs':
|
|
||||||
// Connect to Jilo database for log data
|
|
||||||
$jilo_response = connectDB($config, 'jilo', $platformDetails[0]['jilo_database'], $platform_id);
|
|
||||||
if ($jilo_response['db'] === null) {
|
|
||||||
Messages::flash('ERROR', 'DEFAULT', $jilo_response['error']);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
$jilo_db = $jilo_response['db'];
|
|
||||||
|
|
||||||
// Get date range for the last 7 days
|
|
||||||
$from_time = date('Y-m-d', strtotime('-7 days'));
|
|
||||||
$until_time = date('Y-m-d');
|
|
||||||
|
|
||||||
// Define graphs to show
|
|
||||||
$graphs = [
|
|
||||||
[
|
|
||||||
'graph_name' => 'conferences',
|
|
||||||
'graph_title' => 'Conferences in "' . htmlspecialchars($platformDetails[0]['name']) . '" over time',
|
|
||||||
'datasets' => []
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'graph_name' => 'participants',
|
|
||||||
'graph_title' => 'Participants in "' . htmlspecialchars($platformDetails[0]['name']) . '" over time',
|
|
||||||
'datasets' => []
|
|
||||||
]
|
|
||||||
];
|
|
||||||
|
|
||||||
// Get Jitsi API data
|
|
||||||
$conferences_api = $agentObject->getHistoricalData(
|
|
||||||
$platform_id,
|
|
||||||
'jicofo',
|
|
||||||
'conferences',
|
|
||||||
$from_time,
|
|
||||||
$until_time
|
|
||||||
);
|
|
||||||
$graphs[0]['datasets'][] = [
|
|
||||||
'data' => $conferences_api,
|
|
||||||
'label' => 'Conferences from Jitsi API',
|
|
||||||
'color' => 'rgba(75, 192, 192, 1)'
|
|
||||||
];
|
|
||||||
|
|
||||||
// Get conference data from logs
|
|
||||||
$conferences_logs = $conferenceObject->conferenceNumber(
|
|
||||||
$from_time,
|
|
||||||
$until_time
|
|
||||||
);
|
|
||||||
$graphs[0]['datasets'][] = [
|
|
||||||
'data' => $conferences_logs,
|
|
||||||
'label' => 'Conferences from Logs',
|
|
||||||
'color' => 'rgba(255, 99, 132, 1)'
|
|
||||||
];
|
|
||||||
|
|
||||||
// Get participants data
|
|
||||||
$participants_api = $agentObject->getHistoricalData(
|
|
||||||
$platform_id,
|
|
||||||
'jicofo',
|
|
||||||
'participants',
|
|
||||||
$from_time,
|
|
||||||
$until_time
|
|
||||||
);
|
|
||||||
$graphs[1]['datasets'][] = [
|
|
||||||
'data' => $participants_api,
|
|
||||||
'label' => 'Participants from Jitsi API',
|
|
||||||
'color' => 'rgba(75, 192, 192, 1)'
|
|
||||||
];
|
|
||||||
|
|
||||||
// Prepare data for template
|
|
||||||
$graph = $graphs;
|
|
||||||
|
|
||||||
// prepare the widget
|
|
||||||
$widget['full'] = false;
|
|
||||||
$widget['name'] = 'Graphs';
|
|
||||||
$widget['title'] = 'Jitsi graphs';
|
|
||||||
|
|
||||||
include '../app/templates/graphs-combined.php';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'latest':
|
|
||||||
// Define metrics to display
|
|
||||||
$metrics = [
|
|
||||||
'Basic stats' => [
|
|
||||||
'conferences' => ['label' => 'Current conferences', 'link' => 'conferences'],
|
|
||||||
'participants' => ['label' => 'Current participants', 'link' => 'participants'],
|
|
||||||
'total_conferences_created' => ['label' => 'Total conferences created'],
|
|
||||||
'total_participants' => ['label' => 'Total participants']
|
|
||||||
],
|
|
||||||
'Bridge stats' => [
|
|
||||||
'bridge_selector.bridge_count' => ['label' => 'Bridge count'],
|
|
||||||
'bridge_selector.operational_bridge_count' => ['label' => 'Operational bridges'],
|
|
||||||
'bridge_selector.in_shutdown_bridge_count' => ['label' => 'Bridges in shutdown']
|
|
||||||
],
|
|
||||||
'Jibri stats' => [
|
|
||||||
'jibri_detector.count' => ['label' => 'Jibri count'],
|
|
||||||
'jibri_detector.available' => ['label' => 'Jibri idle'],
|
|
||||||
'jibri.live_streaming_active' => ['label' => 'Jibri active streaming'],
|
|
||||||
'jibri.recording_active' => ['label' => 'Jibri active recording'],
|
|
||||||
],
|
|
||||||
'System stats' => [
|
|
||||||
'threads' => ['label' => 'Threads'],
|
|
||||||
'stress_level' => ['label' => 'Stress level'],
|
|
||||||
'version' => ['label' => 'Version']
|
|
||||||
]
|
|
||||||
];
|
|
||||||
|
|
||||||
// Get all hosts for this platform
|
|
||||||
$hosts = $hostObject->getHostDetails($platform_id);
|
|
||||||
$hostsData = [];
|
|
||||||
|
|
||||||
// For each host, get its agents and their metrics
|
|
||||||
foreach ($hosts as $host) {
|
|
||||||
$hostData = [
|
|
||||||
'id' => $host['id'],
|
|
||||||
'name' => $host['name'] ?: $host['address'],
|
|
||||||
'address' => $host['address'],
|
|
||||||
'agents' => []
|
|
||||||
];
|
|
||||||
|
|
||||||
// Get agents for this host
|
|
||||||
$hostAgents = $agentObject->getAgentDetails($host['id']);
|
|
||||||
foreach ($hostAgents as $agent) {
|
|
||||||
$agentData = [
|
|
||||||
'id' => $agent['id'],
|
|
||||||
'type' => $agent['agent_description'],
|
|
||||||
'name' => strtoupper($agent['agent_description']),
|
|
||||||
'metrics' => [],
|
|
||||||
'timestamp' => null
|
|
||||||
];
|
|
||||||
|
|
||||||
// Fetch all metrics for this agent
|
|
||||||
foreach ($metrics as $section => $section_metrics) {
|
|
||||||
foreach ($section_metrics as $metric => $metricConfig) {
|
|
||||||
// Get latest data
|
|
||||||
$latestData = $agentObject->getLatestData($host['id'], $agent['agent_description'], $metric);
|
|
||||||
|
|
||||||
if ($latestData !== null) {
|
|
||||||
// Get the previous record
|
|
||||||
$previousData = $agentObject->getPreviousRecord(
|
|
||||||
$host['id'],
|
|
||||||
$agent['agent_description'],
|
|
||||||
$metric,
|
|
||||||
$latestData['timestamp']
|
|
||||||
);
|
|
||||||
|
|
||||||
$agentData['metrics'][$section][$metric] = [
|
|
||||||
'latest' => [
|
|
||||||
'value' => $latestData['value'],
|
|
||||||
'timestamp' => $latestData['timestamp']
|
|
||||||
],
|
|
||||||
'previous' => $previousData,
|
|
||||||
'label' => $metricConfig['label'],
|
|
||||||
'link' => isset($metricConfig['link']) ? $metricConfig['link'] : null
|
|
||||||
];
|
|
||||||
|
|
||||||
// Use the most recent timestamp for the agent
|
|
||||||
if ($agentData['timestamp'] === null || strtotime($latestData['timestamp']) > strtotime($agentData['timestamp'])) {
|
|
||||||
$agentData['timestamp'] = $latestData['timestamp'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($agentData['metrics'])) {
|
|
||||||
$hostData['agents'][] = $agentData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($hostData['agents'])) {
|
|
||||||
$hostsData[] = $hostData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load the template
|
|
||||||
include '../app/templates/latest-data.php';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'configjs':
|
|
||||||
$mode = $_REQUEST['mode'] ?? '';
|
|
||||||
$raw = ($mode === 'raw');
|
|
||||||
$platformConfigjs = $settingsObject->getPlatformConfigjs($platformDetails[0]['jitsi_url'], $raw);
|
|
||||||
include '../app/templates/data-configjs.php';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'interfaceconfigjs':
|
|
||||||
$mode = $_REQUEST['mode'] ?? '';
|
|
||||||
$raw = ($mode === 'raw');
|
|
||||||
$platformInterfaceConfigjs = $settingsObject->getPlatformInterfaceConfigjs($platformDetails[0]['jitsi_url'], $raw);
|
|
||||||
include '../app/templates/data-interfaceconfigjs.php';
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1,142 +0,0 @@
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="card w-auto bg-light border-light card-body" style="flex-direction: row;"><?= $widget['title'] ?></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="card w-auto bg-light border-light card-body filter-results">
|
|
||||||
<div class="btn-group" role="group">
|
|
||||||
<input type="button" class="button" style="margin-right: 3px;" onclick="setTimeRange('today'); setActive(this)" value="today" />
|
|
||||||
<input type="button" class="button" style="margin-right: 3px;" onclick="setTimeRange('last2days'); setActive(this)" value="last 2 days" />
|
|
||||||
<input type="button" class="button active" style="margin-right: 3px;" onclick="setTimeRange('last7days'); setActive(this)" value="last 7 days" />
|
|
||||||
<input type="button" class="button" style="margin-right: 3px;" onclick="setTimeRange('thisMonth'); setActive(this)" value="month" />
|
|
||||||
<input type="button" class="button" style="margin-right: 18px;" onclick="setTimeRange('thisYear'); setActive(this)" value="year" />
|
|
||||||
|
|
||||||
<input type="date" style="margin-right: 3px;" id="start-date">
|
|
||||||
|
|
||||||
<input type="date" style="margin-right: 3px;" id="end-date">
|
|
||||||
<input type="button" id="custom_range" class="button" onclick="setCustomTimeRange(); setActive(this)" value="custom range" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// Define an array to store all graph instances
|
|
||||||
var graphs = [];
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<?php
|
|
||||||
|
|
||||||
foreach ($graph as $data) {
|
|
||||||
include '../app/helpers/graph.php';
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// Function to update the label and propagate zoom across charts
|
|
||||||
function propagateZoom(chart) {
|
|
||||||
var startDate = chart.scales.x.min;
|
|
||||||
var endDate = chart.scales.x.max;
|
|
||||||
|
|
||||||
// Update the datetime input fields
|
|
||||||
document.getElementById('start-date').value = new Date(startDate).toISOString().slice(0, 10);
|
|
||||||
document.getElementById('end-date').value = new Date(endDate).toISOString().slice(0, 10);
|
|
||||||
|
|
||||||
// Update all charts with the new date range
|
|
||||||
graphs.forEach(function(graphObj) {
|
|
||||||
if (graphObj.graph !== chart) {
|
|
||||||
graphObj.graph.options.scales.x.min = startDate;
|
|
||||||
graphObj.graph.options.scales.x.max = endDate;
|
|
||||||
graphObj.graph.update(); // Redraw chart with new range
|
|
||||||
}
|
|
||||||
updatePeriodLabel(graphObj.graph, graphObj.label); // Update period label
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Predefined time range buttons
|
|
||||||
function setTimeRange(range) {
|
|
||||||
var startDate, endDate;
|
|
||||||
var now = new Date();
|
|
||||||
|
|
||||||
switch (range) {
|
|
||||||
case 'today':
|
|
||||||
startDate = new Date(now.setHours(0, 0, 0, 0));
|
|
||||||
endDate = new Date(now.setHours(23, 59, 59, 999));
|
|
||||||
timeRangeName = 'today';
|
|
||||||
break;
|
|
||||||
case 'last2days':
|
|
||||||
startDate = new Date(now.setDate(now.getDate() - 2));
|
|
||||||
endDate = new Date();
|
|
||||||
timeRangeName = 'last 2 days';
|
|
||||||
break;
|
|
||||||
case 'last7days':
|
|
||||||
startDate = new Date(now.setDate(now.getDate() - 7));
|
|
||||||
endDate = new Date();
|
|
||||||
timeRangeName = 'last 7 days';
|
|
||||||
break;
|
|
||||||
case 'thisMonth':
|
|
||||||
startDate = new Date(now.getFullYear(), now.getMonth(), 1);
|
|
||||||
endDate = new Date();
|
|
||||||
timeRangeName = 'this month so far';
|
|
||||||
break;
|
|
||||||
case 'thisYear':
|
|
||||||
startDate = new Date(now.getFullYear(), 0, 1);
|
|
||||||
endDate = new Date();
|
|
||||||
timeRangeName = 'this year so far';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We set the date input fields to match the selected period
|
|
||||||
document.getElementById('start-date').value = startDate.toISOString().slice(0, 10);
|
|
||||||
document.getElementById('end-date').value = endDate.toISOString().slice(0, 10);
|
|
||||||
|
|
||||||
// Loop through all graphs and update their time range and label
|
|
||||||
graphs.forEach(function(graphObj) {
|
|
||||||
graphObj.graph.options.scales.x.min = startDate;
|
|
||||||
graphObj.graph.options.scales.x.max = endDate;
|
|
||||||
graphObj.graph.update();
|
|
||||||
updatePeriodLabel(graphObj.graph, graphObj.label); // Update the period label
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Custom date range
|
|
||||||
function setCustomTimeRange() {
|
|
||||||
var startDate = document.getElementById('start-date').value;
|
|
||||||
var endDate = document.getElementById('end-date').value;
|
|
||||||
|
|
||||||
if (!startDate || !endDate) return;
|
|
||||||
|
|
||||||
// Convert the input dates to JavaScript Date objects
|
|
||||||
startDate = new Date(startDate);
|
|
||||||
endDate = new Date(endDate);
|
|
||||||
timeRangeName = 'custom range';
|
|
||||||
|
|
||||||
// Loop through all graphs and update the custom time range
|
|
||||||
graphs.forEach(function(graphObj) {
|
|
||||||
graphObj.graph.options.scales.x.min = startDate;
|
|
||||||
graphObj.graph.options.scales.x.max = endDate;
|
|
||||||
graphObj.graph.update();
|
|
||||||
updatePeriodLabel(graphObj.graph, graphObj.label); // Update the period label
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the clicked button state to active
|
|
||||||
function setActive(element) {
|
|
||||||
// Remove 'active' class from all buttons
|
|
||||||
var buttons = document.querySelectorAll('.button');
|
|
||||||
buttons.forEach(function(btn) {
|
|
||||||
btn.classList.remove('active');
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add 'active' class only to the clicked button
|
|
||||||
element.classList.add('active');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call setTimeRange('last7days') on page load to pre-load last 7 days by default
|
|
||||||
window.onload = function() {
|
|
||||||
setTimeRange('last7days');
|
|
||||||
};
|
|
||||||
</script>
|
|
|
@ -1,117 +0,0 @@
|
||||||
|
|
||||||
<!-- latest data -->
|
|
||||||
<div class="container-fluid mt-2">
|
|
||||||
<div class="row mb-4">
|
|
||||||
<div class="col-12 mb-4">
|
|
||||||
<h2 class="mb-0">Latest data from Jilo Agents</h2>
|
|
||||||
<small>gathered for platform <?= htmlspecialchars($platform_id) ?> (<?= htmlspecialchars($platformDetails[0]['name']) ?>)</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="mb-4">
|
|
||||||
<?php if (!empty($hostsData)) { ?>
|
|
||||||
<?php foreach ($hostsData as $host) { ?>
|
|
||||||
<div class="card mb-4">
|
|
||||||
<div class="card-header">
|
|
||||||
<h5 class="mb-0">
|
|
||||||
<i class="fas fa-network-wired me-2 text-secondary"></i>
|
|
||||||
<?= htmlspecialchars($host['name']) ?><small class="text-muted"> (<?= htmlspecialchars($host['address']) ?>)</small>
|
|
||||||
</h5>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<?php foreach ($host['agents'] as $agent) { ?>
|
|
||||||
<div class="mb-4">
|
|
||||||
<h6 class="border-bottom pb-2">
|
|
||||||
<i class="fas fa-robot me-2 text-secondary"></i>
|
|
||||||
<?= htmlspecialchars($agent['name']) ?> agent
|
|
||||||
</h6>
|
|
||||||
<table class="table table-results table-striped table-hover table-bordered">
|
|
||||||
<thead class="align-top">
|
|
||||||
<tr>
|
|
||||||
<th>Metric</th>
|
|
||||||
<th>
|
|
||||||
Latest value
|
|
||||||
<br>
|
|
||||||
<small class="text-muted"><?= date('d M Y H:i:s', strtotime($agent['timestamp'])) ?></small>
|
|
||||||
</th>
|
|
||||||
<th>
|
|
||||||
Previous value
|
|
||||||
<?php
|
|
||||||
// Find first metric with previous data to get timestamp
|
|
||||||
$prevTimestamp = null;
|
|
||||||
foreach ($metrics as $m_section => $m_metrics) {
|
|
||||||
foreach ($m_metrics as $m_metric => $m_config) {
|
|
||||||
if (isset($agent['metrics'][$m_section][$m_metric]['previous'])) {
|
|
||||||
$prevTimestamp = $agent['metrics'][$m_section][$m_metric]['previous']['timestamp'];
|
|
||||||
break 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($prevTimestamp) { ?>
|
|
||||||
<br>
|
|
||||||
<small class="text-muted"><?= date('d M Y H:i:s', strtotime($prevTimestamp)) ?></small>
|
|
||||||
<?php } ?>
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<?php foreach ($metrics as $section => $section_metrics) { ?>
|
|
||||||
<?php
|
|
||||||
// Check if this section has any data for this agent
|
|
||||||
$hasData = false;
|
|
||||||
foreach ($section_metrics as $metric => $metricConfig) {
|
|
||||||
if (isset($agent['metrics'][$section][$metric])) {
|
|
||||||
$hasData = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!$hasData) continue;
|
|
||||||
?>
|
|
||||||
<tr class="table-secondary">
|
|
||||||
<th colspan="3"><?= htmlspecialchars($section) ?></th>
|
|
||||||
</tr>
|
|
||||||
<?php foreach ($section_metrics as $metric => $metricConfig) { ?>
|
|
||||||
<?php if (isset($agent['metrics'][$section][$metric])) {
|
|
||||||
$metric_data = $agent['metrics'][$section][$metric];
|
|
||||||
?>
|
|
||||||
<tr>
|
|
||||||
<td><?= htmlspecialchars($metricConfig['label']) ?></td>
|
|
||||||
<td>
|
|
||||||
<?php if ($metric_data['link']) { ?>
|
|
||||||
<a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=<?= htmlspecialchars($metric_data['link']) ?>&from_time=<?= htmlspecialchars($metric_data['latest']['timestamp']) ?>&until_time=<?= htmlspecialchars($metric_data['latest']['timestamp']) ?>"><?= htmlspecialchars($metric_data['latest']['value']) ?></a>
|
|
||||||
<?php } else { ?>
|
|
||||||
<?= htmlspecialchars($metric_data['latest']['value']) ?>
|
|
||||||
<?php } ?>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<?php if ($metric_data['previous']) { ?>
|
|
||||||
<?php if ($metric_data['link']) { ?>
|
|
||||||
<a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=<?= htmlspecialchars($metric_data['link']) ?>&from_time=<?= htmlspecialchars($metric_data['previous']['timestamp']) ?>&until_time=<?= htmlspecialchars($metric_data['previous']['timestamp']) ?>"><?= htmlspecialchars($metric_data['previous']['value']) ?></a>
|
|
||||||
<?php } else { ?>
|
|
||||||
<?= htmlspecialchars($metric_data['previous']['value']) ?>
|
|
||||||
<?php } ?>
|
|
||||||
<?php } else { ?>
|
|
||||||
<span class="text-muted">No previous data</span>
|
|
||||||
<?php } ?>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<?php } ?>
|
|
||||||
<?php } ?>
|
|
||||||
<?php } ?>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<?php } ?>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<?php } ?>
|
|
||||||
<?php } else { ?>
|
|
||||||
<div class="alert alert-info m-3" role="alert">
|
|
||||||
No data available from any agents. Please check agent configuration and connectivity.
|
|
||||||
</div>
|
|
||||||
<?php } ?>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- /latest data -->
|
|
Loading…
Reference in New Issue