Compare commits

..

No commits in common. "main" and "v0.1.1" have entirely different histories.
main ... v0.1.1

132 changed files with 2293 additions and 6177 deletions

View File

@ -7,78 +7,16 @@ All notable changes to this project will be documented in this file.
## Unreleased
#### Links
- upstream: https://code.lindeas.com/lindeas/jilo-web/compare/v0.2.1...HEAD
- codeberg: https://codeberg.org/lindeas/jilo-web/compare/v0.2.1...HEAD
- github: https://github.com/lindeas/jilo-web/compare/v0.2.1...HEAD
- gitlab: https://gitlab.com/lindeas/jilo-web/-/compare/v0.2.1...HEAD
- upstream: https://code.lindeas.com/lindeas/jilo-web/compare/v0.1.1...HEAD
- codeberg: https://codeberg.org/lindeas/jilo-web/compare/v0.1.1...HEAD
- github: https://github.com/lindeas/jilo-web/compare/v0.1.1...HEAD
- gitlab: https://gitlab.com/lindeas/jilo-web/-/compare/v0.1.1...HEAD
### Added
- Added Jilo Server check and notice on error
- Added status page
---
## 0.2.1 - 2024-10-17
#### Links
- upstream: https://code.lindeas.com/lindeas/jilo-web/compare/v0.2...v0.2.1
- codeberg: https://codeberg.org/lindeas/jilo-web/compare/v0.2...v0.2.1
- github: https://github.com/lindeas/jilo-web/compare/v0.2...v0.2.1
- gitlab: https://gitlab.com/lindeas/jilo-web/-/compare/v0.2...v0.2.1
### Added
- Added support for managing Jilo Agents
- Authenticating to Jilo Agents with JWT tokens with a shared secret key
- Added Jilo Agent functionality to fetch data, cache it and manage the cache
- Added more fields and avatar image to user profile
- Added pagination (with ellipses) for the longer listings
- Added initial support for application logs
- Added help page
- Added support for graphs by Chart.js
- Added "graphs" section in sidebar with graphs and latest data pages
### Changed
- Jitsi platforms config moved from file to SQLite database
- Left sidebar menu items reordered
### Fixed
- All output HTML sanitized
- Sanitized input forms data
- Fixed error in calculation of monthly total conferences on front page
---
## 0.2 - 2024-08-31
#### Links
- upstream: https://code.lindeas.com/lindeas/jilo-web/compare/v0.1.1...v0.2
- codeberg: https://codeberg.org/lindeas/jilo-web/compare/v0.1.1...v0.2
- github: https://github.com/lindeas/jilo-web/compare/v0.1.1...v0.2
- gitlab: https://gitlab.com/lindeas/jilo-web/-/compare/v0.1.1...v0.2
### Added
- Added collapsible front page widgets
- Added widgets to conferences, participants and components pages
- Added front page widget for monthly conferences and participants number
- Added login/registration control and messages
- Added default config file locations
- Added left collapsible sidebar
- Added logo
- Added database helper functions
- Added support for multiple Jitsi platforms
- Added app environments "production" and "development"
- Added visualisation of config.js and interface_config.js per Jitsi platform
### Changed
- MVC design - models(classes folder), views(templates folder) and controllers(pages folder)
- Changed menus - categories on sidebar menu, jitsi platforms on top menu
- Moved all the app code outside of the public web folder
- Config file now can have nested arrays
### Fixed
- Fixed SQL when conferences start and end time are not explicitly clear
- Web design fixes
- Fixed install script
---

View File

@ -26,53 +26,39 @@ To see a demo install, go to https://work.lindeas.com/jilo-web-demo/
## version
Current version: **0.2.1** released on **2024-10-17**
Current version: **0.1.1** released on **2024-07-25**
## license
This project is licensed under the GNU General Public License version 2 (GPL-2.0). See LICENSE file.
Bootstrap is used in this project and is licensed under the MIT License. See license-bootstrap file.
JQuery is used in this project and is licensed under the MIT License. See license-jquery file.
Chart.js is used in this project and is licensed under the MIT License. See license-chartjs file.
Bootstrap is used in this project and is licensed under the MIT License. See bootstrap-license file
## requirements
- web server (deb: apache | nginx)
- php support in the web server (deb: php-fpm | libapache2-mod-php)
- pdo and pdo_sqlite support in php (deb: php-db, php-sqlite3) uncomment in php.ini: ;extension=pdo_sqlite
- php-curl module
## installation
You can install it in the following ways:
- download the latest release from the **"Releases"** section here
```bash
tar -xzf jilo-web_*.tgz
cd jilo-web/doc/
./install.sh
```
- clone the **git repo**:
```bash
git clone https://github.com/lindeas/jilo-web.git
cd jilo-web
./install.sh
cd jilo
```
- DEB and RPM packages are planned, but not yet available. There are experimental build scripts in "packaging" folder.
- DEB and RPM packages are planned, but still unavailable
## config
- edit index.php and set the system path to the jilo-web.conf.php
- edit jilo-web.conf.php and set all the variables correctly
- "sqlite_file" is the sqlite db file for jilo-web itself, it goes to `app/jilo-web.db`, create it with
```bash
cd app
cat ../doc/jilo-web.schema | sqlite3 jilo-web.db
```
- "database" is the sqlite db file for jilo-web itself, create it with `cat jilo-web.schema | sqlite3 jilo-web.db`
- "jilo_database" is the sqlite db file for jilo, with data from the Jitsi logs
## database
The database is an SQLite file. You also need the sqlite db from jilo (mysql/mariadb support is planned, but not yet available).
The database is an SQLite file. You also need the sqlite db from jilo (mysql/mariadb support is planned, but still unavailable).

27
TODO.md 100644
View File

@ -0,0 +1,27 @@
# Jilo Web
## TODO
- ~~jilo-web.db outside web root~~
- ~~jilo-web.db writable by web server user~~
- major refactoring after v0.1
- - ~~add bootstrap template~~
- - clean up the code to follow model-view--controller style
- - no HTML inside PHP code
- - put all additional functions in files in a separate folder
- - reduce try/catch usage, use it only for critical errors
- - move all SQL code in the model classes, one query per method
- - add 'limit' to SQL and make pagination
- - pretty URLs routing (no htaccess, it needs to be in PHP code so that it can be used with all servers)
- add mysql/mariadb option

View File

@ -1,291 +0,0 @@
<?php
class Agent {
private $db;
public function __construct($database) {
$this->db = $database->getConnection();
}
// get details of a specified agent ID (or all) in a specified platform ID
public function getAgentDetails($platform_id, $agent_id = '') {
$sql = 'SELECT
ja.id,
ja.platform_id,
ja.agent_type_id,
ja.url,
ja.secret_key,
ja.check_period,
jat.description AS agent_description,
jat.endpoint AS agent_endpoint
FROM
jilo_agents ja
JOIN
jilo_agent_types jat ON ja.agent_type_id = jat.id
WHERE
platform_id = :platform_id';
if ($agent_id !== '') {
$sql .= ' AND ja.id = :agent_id';
}
$query = $this->db->prepare($sql);
$query->bindParam(':platform_id', $platform_id);
if ($agent_id !== '') {
$query->bindParam(':agent_id', $agent_id);
}
$query->execute();
return $query->fetchAll(PDO::FETCH_ASSOC);
}
// get details of a specified agent ID
public function getAgentIDDetails($agent_id) {
$sql = 'SELECT
ja.id,
ja.platform_id,
ja.agent_type_id,
ja.url,
ja.secret_key,
ja.check_period,
jat.description AS agent_description,
jat.endpoint AS agent_endpoint
FROM
jilo_agents ja
JOIN
jilo_agent_types jat ON ja.agent_type_id = jat.id
WHERE
ja.id = :agent_id';
$query = $this->db->prepare($sql);
$query->bindParam(':agent_id', $agent_id);
$query->execute();
return $query->fetchAll(PDO::FETCH_ASSOC);
}
// get agent types
public function getAgentTypes() {
$sql = 'SELECT *
FROM jilo_agent_types
ORDER BY id';
$query = $this->db->prepare($sql);
$query->execute();
return $query->fetchAll(PDO::FETCH_ASSOC);
}
// get agent types already configured for a platform
public function getPlatformAgentTypes($platform_id) {
$sql = 'SELECT
id,
agent_type_id
FROM
jilo_agents
WHERE
platform_id = :platform_id';
$query = $this->db->prepare($sql);
$query->bindParam(':platform_id', $platform_id);
$query->execute();
return $query->fetchAll(PDO::FETCH_ASSOC);
}
// add new agent
public function addAgent($platform_id, $newAgent) {
try {
$sql = 'INSERT INTO jilo_agents
(platform_id, agent_type_id, url, secret_key, check_period)
VALUES
(:platform_id, :agent_type_id, :url, :secret_key, :check_period)';
$query = $this->db->prepare($sql);
$query->execute([
':platform_id' => $platform_id,
':agent_type_id' => $newAgent['type_id'],
':url' => $newAgent['url'],
':secret_key' => $newAgent['secret_key'],
':check_period' => $newAgent['check_period'],
]);
return true;
} catch (Exception $e) {
return $e->getMessage();
}
}
// edit an existing agent
public function editAgent($platform_id, $updatedAgent) {
try {
$sql = 'UPDATE jilo_agents SET
agent_type_id = :agent_type_id,
url = :url,
secret_key = :secret_key,
check_period = :check_period
WHERE
id = :agent_id
AND
platform_id = :platform_id';
$query = $this->db->prepare($sql);
$query->execute([
':agent_type_id' => $updatedAgent['agent_type_id'],
':url' => $updatedAgent['url'],
':secret_key' => $updatedAgent['secret_key'],
':check_period' => $updatedAgent['check_period'],
':agent_id' => $updatedAgent['id'],
':platform_id' => $platform_id,
]);
return true;
} catch (Exception $e) {
return $e->getMessage();
}
}
// delete an agent
public function deleteAgent($agent_id) {
try {
$sql = 'DELETE FROM jilo_agents
WHERE
id = :agent_id';
$query = $this->db->prepare($sql);
$query->bindParam(':agent_id', $agent_id);
$query->execute();
return true;
} catch (Exception $e) {
return $e->getMessage();
}
}
// check for agent cache
public function checkAgentCache($agent_id) {
$agent_cache_name = 'agent' . $agent_id . '_cache';
$agent_cache_time = 'agent' . $agent_id . '_time';
return isset($_SESSION[$agent_cache_name]) && isset($_SESSION[$agent_cache_time]) && (time() - $_SESSION[$agent_cache_time] < 600);
}
// method for base64 URL encoding for JWT tokens
private function base64UrlEncode($data) {
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
// generate a JWT token for jilo agent
public function generateAgentToken($payload, $secret_key) {
// header
$header = json_encode([
'typ' => 'JWT',
'alg' => 'HS256'
]);
$base64Url_header = $this->base64UrlEncode($header);
// payload
$payload = json_encode($payload);
$base64Url_payload = $this->base64UrlEncode($payload);
// signature
$signature = hash_hmac('sha256', $base64Url_header . "." . $base64Url_payload, $secret_key, true);
$base64Url_signature = $this->base64UrlEncode($signature);
// build the JWT
$jwt = $base64Url_header . "." . $base64Url_payload . "." . $base64Url_signature;
return $jwt;
}
// fetch result from jilo agent API
public function fetchAgent($agent_id, $force = false) {
// we need agent details for URL and JWT token
$agentDetails = $this->getAgentIDDetails($agent_id);
// Safe exit in case the agent is not found
if (empty($agentDetails)) {
return json_encode(['error' => 'Agent not found']);
}
$agent = $agentDetails[0];
$agent_cache_name = 'agent' . $agent_id . '_cache';
$agent_cache_time = 'agent' . $agent_id . '_time';
// check if the cache is still valid, unless force-refresh is requested
if (!$force && $this->checkAgentCache($agent_id)) {
return $_SESSION[$agent_cache_name];
}
// generate the JWT token
$payload = [
'agent_id' => $agent_id,
'timestamp' => time()
];
$jwt = $this->generateAgentToken($payload, $agent['secret_key']);
// Make the API request
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $agent['url'] . $agent['agent_endpoint']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10); // timeout 10 seconds
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer ' . $jwt,
'Content-Type: application/json'
]);
$response = curl_exec($ch);
$curl_error = curl_error($ch);
$curl_errno = curl_errno($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// curl error
if ($curl_errno) {
return json_encode(['error' => 'curl error: ' . $curl_error]);
}
// response is not 200 OK
if ($http_code !== 200) {
return json_encode(['error' => 'HTTP error: ' . $http_code]);
}
// other custom error(s)
if (strpos($response, 'Auth header not received') !== false) {
return json_encode(['error' => 'Auth header not received']);
}
// Cache the result and the timestamp if the response is successful
// We decode it so that it's pure JSON and not escaped
$_SESSION[$agent_cache_name] = json_decode($response, true);
$_SESSION[$agent_cache_time] = time();
return $response;
}
// clear agent cache
public function clearAgentCache($agent_id) {
$_SESSION["agent{$agent_id}_cache"] = '';
$_SESSION["agent{$agent_id}_cache_time"] = '';
}
// get latest stored jilo agents data
public function getLatestData($platform_id, $agent_type, $metric_type) {
// retrieves data already stored in db from another function (or the jilo-server to-be)
}
}
?>

View File

@ -1,69 +0,0 @@
<?php
class Component {
private $db;
public function __construct($database) {
$this->db = $database->getConnection();
}
// list of component events
public function jitsiComponents($jitsi_component, $component_id, $event_type, $from_time, $until_time, $offset=0, $items_per_page='') {
// time period drill-down
// FIXME make it similar to the bash version
if (empty($from_time)) {
$from_time = '0000-01-01';
}
if (empty($until_time)) {
$until_time = '9999-12-31';
}
$from_time = htmlspecialchars(strip_tags($from_time));
$until_time = htmlspecialchars(strip_tags($until_time));
// list of jitsi component events
$sql = "
SELECT
jitsi_component, loglevel, time, component_id, event_type, event_param
FROM
jitsi_components
WHERE
jitsi_component = %s
AND
component_id = %s";
if ($event_type != '' && $event_type != 'event_type') {
$sql .= "
AND
event_type LIKE '%%%s%%'";
}
$sql .= "
AND
(time >= '%s 00:00:00' AND time <= '%s 23:59:59')
ORDER BY
time";
if ($items_per_page) {
$items_per_page = (int)$items_per_page;
$sql .= ' LIMIT ' . $offset . ',' . $items_per_page;
}
// FIXME this needs to be done with bound params instead of sprintf
if ($event_type != '' && $event_type != 'event_type') {
$sql = sprintf($sql, $jitsi_component, $component_id, $event_type, $from_time, $until_time);
$sql = str_replace("LIKE '%'", "LIKE '%", $sql);
$sql = str_replace("'%'\nAND", "%' AND", $sql);
} else {
$sql = sprintf($sql, $jitsi_component, $component_id, $from_time, $until_time);
}
$query = $this->db->prepare($sql);
$query->execute();
return $query->fetchAll(PDO::FETCH_ASSOC);
}
}
?>

View File

@ -1,335 +0,0 @@
<?php
class Conference {
private $db;
public function __construct($database) {
$this->db = $database->getConnection();
}
// search/list specific conference ID
public function conferenceById($conference_id, $from_time, $until_time, $offset=0, $items_per_page='') {
// time period drill-down
// FIXME make it similar to the bash version
if (empty($from_time)) {
$from_time = '0000-01-01';
}
if (empty($until_time)) {
$until_time = '9999-12-31';
}
// this is needed for compatibility with the bash version, so we use '%s' placeholders
$from_time = htmlspecialchars(strip_tags($from_time));
$until_time = htmlspecialchars(strip_tags($until_time));
// search for a conference by its ID for a time period (if given)
$sql = "
SELECT
pe.time,
c.conference_id,
c.conference_name,
c.conference_host,
pe.loglevel,
pe.event_type,
p.endpoint_id AS participant_id,
pe.event_param
FROM
conferences c
LEFT JOIN
conference_events ce ON c.conference_id = ce.conference_id
LEFT JOIN
participants p ON c.conference_id = p.conference_id
LEFT JOIN
participant_events pe ON p.endpoint_id = pe.participant_id
WHERE
c.conference_id = '%s'
AND (pe.time >= '%s 00:00:00' AND pe.time <= '%s 23:59:59')
UNION
SELECT
ce.time AS event_time,
c.conference_id,
c.conference_name,
c.conference_host,
ce.loglevel,
ce.conference_event AS event_type,
NULL AS participant_id,
ce.conference_param AS event_param
FROM
conferences c
LEFT JOIN
conference_events ce ON c.conference_id = ce.conference_id
WHERE
c.conference_id = '%s'
AND (event_time >= '%s 00:00:00' AND event_time <= '%s 23:59:59')
ORDER BY
pe.time";
if ($items_per_page) {
$items_per_page = (int)$items_per_page;
$sql .= ' LIMIT ' . $offset . ',' . $items_per_page;
}
$sql = sprintf($sql, $conference_id, $from_time, $until_time, $conference_id, $from_time, $until_time);
$query = $this->db->prepare($sql);
$query->execute();
return $query->fetchAll(PDO::FETCH_ASSOC);
}
// search/list specific conference name
public function conferenceByName($conference_name, $from_time, $until_time, $offset=0, $items_per_page='') {
// time period drill-down
// FIXME make it similar to the bash version
if (empty($from_time)) {
$from_time = '0000-01-01';
}
if (empty($until_time)) {
$until_time = '9999-12-31';
}
// this is needed for compatibility with the bash version, so we use '%s' placeholders
$from_time = htmlspecialchars(strip_tags($from_time));
$until_time = htmlspecialchars(strip_tags($until_time));
// search for a conference by its name for a time period (if given)
$sql = "
SELECT
pe.time,
c.conference_id,
c.conference_name,
c.conference_host,
pe.loglevel,
pe.event_type,
p.endpoint_id AS participant_id,
pe.event_param
FROM
conferences c
LEFT JOIN
conference_events ce ON c.conference_id = ce.conference_id
LEFT JOIN
participants p ON c.conference_id = p.conference_id
LEFT JOIN
participant_events pe ON p.endpoint_id = pe.participant_id
WHERE
c.conference_name = '%s'
AND (pe.time >= '%s 00:00:00' AND pe.time <= '%s 23:59:59')
UNION
SELECT
ce.time AS event_time,
c.conference_id,
c.conference_name,
c.conference_host,
ce.loglevel,
ce.conference_event AS event_type,
NULL AS participant_id,
ce.conference_param AS event_param
FROM
conferences c
LEFT JOIN
conference_events ce ON c.conference_id = ce.conference_id
WHERE
c.conference_name = '%s'
AND (event_time >= '%s 00:00:00' AND event_time <= '%s 23:59:59')
ORDER BY
pe.time";
if ($items_per_page) {
$items_per_page = (int)$items_per_page;
$sql .= ' LIMIT ' . $offset . ',' . $items_per_page;
}
$sql = sprintf($sql, $conference_name, $from_time, $until_time, $conference_name, $from_time, $until_time);
$query = $this->db->prepare($sql);
$query->execute();
return $query->fetchAll(PDO::FETCH_ASSOC);
}
// list of all conferences
public function conferencesAllFormatted($from_time, $until_time, $offset=0, $items_per_page='') {
// time period drill-down
// FIXME make it similar to the bash version
if (empty($from_time)) {
$from_time = '0000-01-01';
}
if (empty($until_time)) {
$until_time = '9999-12-31';
}
// this is needed for compatibility with the bash version, so we use '%s' placeholders
$from_time = htmlspecialchars(strip_tags($from_time));
$until_time = htmlspecialchars(strip_tags($until_time));
// list of conferences for time period (if given)
// fields: component, duration, conference ID, conference name, number of participants, name count (the conf name is found), conference host
$sql = "
SELECT DISTINCT
c.jitsi_component,
(SELECT COALESCE
(
(SELECT ce.time
FROM conference_events ce
WHERE
ce.conference_id = c.conference_id
AND
ce.conference_event = 'conference created'
),
(SELECT ce.time
FROM conference_events ce
WHERE
ce.conference_id = c.conference_id
AND
ce.conference_event = 'bridge selected'
)
)
)
AS start,
(SELECT COALESCE
(
(SELECT ce.time
FROM conference_events ce
WHERE
ce.conference_id = c.conference_id
AND
(ce.conference_event = 'conference expired' OR ce.conference_event = 'conference stopped')
),
(SELECT pe.time
FROM participant_events pe
WHERE
pe.event_param = c.conference_id
ORDER BY pe.time DESC
LIMIT 1
)
)
)
AS end,
c.conference_id,
c.conference_name,
(SELECT COUNT(pe.participant_id)
FROM participant_events pe
WHERE
pe.event_type = 'participant joining'
AND
pe.event_param = c.conference_id) AS participants,
name_counts.name_count,
c.conference_host
FROM
conferences c
JOIN (
SELECT
conference_name,
COUNT(*) AS name_count
FROM
conferences
GROUP BY
conference_name
) AS name_counts ON c.conference_name = name_counts.conference_name
JOIN
conference_events ce ON c.conference_id = ce.conference_id
WHERE (ce.time >= '%s 00:00:00' AND ce.time <= '%s 23:59:59')
ORDER BY
c.id";
if ($items_per_page) {
$items_per_page = (int)$items_per_page;
$sql .= ' LIMIT ' . $offset . ',' . $items_per_page;
}
$sql = sprintf($sql, $from_time, $until_time);
$query = $this->db->prepare($sql);
$query->execute();
return $query->fetchAll(PDO::FETCH_ASSOC);
}
// number of conferences
public function conferenceNumber($from_time, $until_time) {
// time period drill-down
// FIXME make it similar to the bash version
if (empty($from_time)) {
$from_time = '0000-01-01';
}
if (empty($until_time)) {
$until_time = '9999-12-31';
}
// this is needed for compatibility with the bash version, so we use '%s' placeholders
$from_time = htmlspecialchars(strip_tags($from_time));
$until_time = htmlspecialchars(strip_tags($until_time));
// number of conferences for time period (if given)
// FIXME sometimes there is no start/end time, find a way around this
$sql = "
SELECT COUNT(*) AS conferences
FROM (
SELECT DISTINCT
(SELECT COALESCE
(
(SELECT ce.time
FROM conference_events ce
WHERE
ce.conference_id = c.conference_id
AND
ce.conference_event = 'conference created'
),
(SELECT ce.time
FROM conference_events ce
WHERE
ce.conference_id = c.conference_id
AND
ce.conference_event = 'bridge selected'
)
)
) AS start,
(SELECT COALESCE
(
(SELECT ce.time
FROM conference_events ce
WHERE
ce.conference_id = c.conference_id
AND
(ce.conference_event = 'conference expired' OR ce.conference_event = 'conference stopped')
),
(SELECT pe.time
FROM participant_events pe
WHERE
pe.event_param = c.conference_id
ORDER BY pe.time DESC
LIMIT 1
)
)
) AS end
FROM conferences c
JOIN
conference_events ce ON c.conference_id = ce.conference_id
WHERE (start >= '%s 00:00:00' AND end <= '%s 23:59:59')
) AS subquery";
$sql = sprintf($sql, $from_time, $until_time);
$query = $this->db->prepare($sql);
$query->execute();
return $query->fetchAll(PDO::FETCH_ASSOC);
}
}
?>

View File

@ -1,91 +0,0 @@
<?php
class Config {
// loading the config.js
public function getPlatformConfigjs($jitsiUrl, $raw = false) {
// constructing the URL
$configjsFile = $jitsiUrl . '/config.js';
// default content, if we can't get the file contents
$platformConfigjs = "The file $configjsFile can't be loaded.";
// ssl options
$contextOptions = [
'ssl' => [
'verify_peer' => true,
'verify_peer_name' => true,
],
];
$context = stream_context_create($contextOptions);
// get the file
$fileContent = @file_get_contents($configjsFile, false, $context);
if ($fileContent !== false) {
// when we need only uncommented values
if ($raw === false) {
// remove block comments
$platformConfigjs = preg_replace('!/\*.*?\*/!s', '', $fileContent);
// remove single-line comments
$platformConfigjs = preg_replace('/\/\/[^\n]*/', '', $platformConfigjs);
// remove empty lines
$platformConfigjs = preg_replace('/^\s*[\r\n]/m', '', $platformConfigjs);
// when we need the full file as it is
} else {
$platformConfigjs = $fileContent;
}
}
return $platformConfigjs;
}
// loading the interface_config.js
public function getPlatformInterfaceConfigjs($jitsiUrl, $raw = false) {
// constructing the URL
$interfaceConfigjsFile = $jitsiUrl . '/interface_config.js';
// default content, if we can't get the file contents
$platformInterfaceConfigjs = "The file $interfaceConfigjsFile can't be loaded.";
// ssl options
$contextOptions = [
'ssl' => [
'verify_peer' => true,
'verify_peer_name' => true,
],
];
$context = stream_context_create($contextOptions);
// get the file
$fileContent = @file_get_contents($interfaceConfigjsFile, false, $context);
if ($fileContent !== false) {
// when we need only uncommented values
if ($raw === false) {
// remove block comments
$platformInterfaceConfigjs = preg_replace('!/\*.*?\*/!s', '', $fileContent);
// remove single-line comments
$platformInterfaceConfigjs = preg_replace('/\/\/[^\n]*/', '', $platformInterfaceConfigjs);
// remove empty lines
$platformInterfaceConfigjs = preg_replace('/^\s*[\r\n]/m', '', $platformInterfaceConfigjs);
// when we need the full file as it is
} else {
$platformInterfaceConfigjs = $fileContent;
}
}
return $platformInterfaceConfigjs;
}
}
?>

View File

@ -1,79 +0,0 @@
<?php
class Database {
private $pdo;
public function __construct($options) {
// pdo needed
if ( !extension_loaded('pdo') ) {
$error = getError('PDO extension not loaded.');
}
// options check
if (empty($options['type'])) {
$error = getError('Database type is not set.');
}
// database type
switch ($options['type']) {
case 'sqlite':
$this->connectSqlite($options);
break;
case 'mysql' || 'mariadb':
$this->connectMysql($options);
break;
default:
$error = getError("Database type \"{$options['type']}\" is not supported.");
}
}
private function connectSqlite($options) {
// pdo_sqlite extension is needed
if (!extension_loaded('pdo_sqlite')) {
$error = getError('PDO extension for SQLite not loaded.');
}
// SQLite options
if (empty($options['dbFile']) || !file_exists($options['dbFile'])) {
$error = getError("SQLite database file \"{$dbFile}\" not found.");
}
// connect to SQLite
try {
$this->pdo = new PDO("sqlite:" . $options['dbFile']);
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// enable foreign key constraints (not ON by default in SQLite3)
$this->pdo->exec('PRAGMA foreign_keys = ON;');
} catch (PDOException $e) {
$error = getError('SQLite connection failed: ', $e->getMessage());
}
}
private function connectMysql($options) {
// pdo_mysql extension is needed
if (!extension_loaded('pdo_mysql')) {
$error = getError('PDO extension for MySQL not loaded.');
}
// MySQL options
if (empty($options['host']) || empty($options['dbname']) || empty($options['user'])) {
$error = getError('MySQL connection data is missing.');
}
// Connect to MySQL
try {
$dsn = "mysql:host={$options['host']};port={$options['port']};dbname={$options['dbname']};charset=utf8";
$this->pdo = new PDO($dsn, $options['user'], $options['password'] ?? '');
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
$error = getError('MySQL connection failed: ', $e->getMessage(), $config['environment']);
}
}
public function getConnection() {
return $this->pdo;
}
}
?>

View File

@ -1,62 +0,0 @@
<?php
class Log {
private $db;
public function __construct($database) {
$this->db = $database->getConnection();
}
// insert log event
public function insertLog($user_id, $message, $scope='user') {
try {
$sql = 'INSERT INTO logs
(user_id, scope, message)
VALUES
(:user_id, :scope, :message)';
$query = $this->db->prepare($sql);
$query->execute([
':user_id' => $user_id,
':scope' => $scope,
':message' => $message,
]);
return true;
} catch (Exception $e) {
return $e->getMessage();
}
}
// read logs
public function readLog($user_id, $scope, $offset=0, $items_per_page='') {
if ($scope === 'user') {
$sql = 'SELECT * FROM logs WHERE user_id = :user_id ORDER BY time DESC';
if ($items_per_page) {
$items_per_page = (int)$items_per_page;
$sql .= ' LIMIT ' . $offset . ',' . $items_per_page;
}
$query = $this->db->prepare($sql);
$query->execute([
':user_id' => $user_id,
]);
}
if ($scope === 'system') {
$sql = 'SELECT * FROM logs ORDER BY time DESC';
if ($items_per_page) {
$items_per_page = (int)$items_per_page;
$sql .= ' LIMIT ' . $offset . ',' . $items_per_page;
}
$query = $this->db->prepare($sql);
$query->execute();
}
return $query->fetchAll(PDO::FETCH_ASSOC);
}
}
?>

View File

@ -1,315 +0,0 @@
<?php
class Participant {
private $db;
public function __construct($database) {
$this->db = $database->getConnection();
}
// search/list specific participant ID
public function conferenceByParticipantId($participant_id, $from_time, $until_time, $offset=0, $items_per_page='') {
// time period drill-down
// FIXME make it similar to the bash version
if (empty($from_time)) {
$from_time = '0000-01-01';
}
if (empty($until_time)) {
$until_time = '9999-12-31';
}
// this is needed for compatibility with the bash version, so we use '%s' placeholders
$from_time = htmlspecialchars(strip_tags($from_time));
$until_time = htmlspecialchars(strip_tags($until_time));
// list conferences where participant ID (endpoint_id) is found
$sql = "
SELECT
pe.time,
c.conference_id,
c.conference_name,
c.conference_host,
pe.loglevel,
pe.event_type,
p.endpoint_id AS participant_id,
pe.event_param
FROM
conferences c
LEFT JOIN
conference_events ce ON c.conference_id = ce.conference_id
LEFT JOIN
participants p ON c.conference_id = p.conference_id
LEFT JOIN
participant_events pe ON p.endpoint_id = pe.participant_id
WHERE
p.endpoint_id = '%s'
AND (pe.time >= '%s 00:00:00' AND pe.time <= '%s 23:59:59')
UNION
SELECT
ce.time AS event_time,
c.conference_id,
c.conference_name,
c.conference_host,
ce.loglevel,
ce.conference_event AS event_type,
NULL AS participant_id,
ce.conference_param AS event_param
FROM
conferences c
LEFT JOIN
conference_events ce ON c.conference_id = ce.conference_id
WHERE
participant_id = '%s'
AND (event_time >= '%s 00:00:00' AND event_time <= '%s 23:59:59')
ORDER BY
pe.time";
if ($items_per_page) {
$items_per_page = (int)$items_per_page;
$sql .= ' LIMIT ' . $offset . ',' . $items_per_page;
}
$sql = sprintf($sql, $participant_id, $from_time, $until_time, $participant_id, $from_time, $until_time);
$query = $this->db->prepare($sql);
$query->execute();
return $query->fetchAll(PDO::FETCH_ASSOC);
}
// search/list specific participant name (stats_id)
public function conferenceByParticipantName($participant_name, $from_time, $until_time, $offset=0, $items_per_page='') {
// time period drill-down
// FIXME make it similar to the bash version
if (empty($from_time)) {
$from_time = '0000-01-01';
}
if (empty($until_time)) {
$until_time = '9999-12-31';
}
// this is needed for compatibility with the bash version, so we use '%s' placeholders
$from_time = htmlspecialchars(strip_tags($from_time));
$until_time = htmlspecialchars(strip_tags($until_time));
// list conferences where participant name (stats_id) is found
$sql = "
SELECT
pe.time,
c.conference_id,
c.conference_name,
c.conference_host,
pe.loglevel,
pe.event_type,
p.endpoint_id AS participant_id,
pe.event_param
FROM
conferences c
LEFT JOIN
conference_events ce ON c.conference_id = ce.conference_id
LEFT JOIN
participants p ON c.conference_id = p.conference_id
LEFT JOIN
participant_events pe ON p.endpoint_id = pe.participant_id
WHERE
pe.event_type = 'stats_id' AND pe.event_param LIKE '%%%s%%'
AND (pe.time >= '%s 00:00:00' AND pe.time <= '%s 23:59:59')
UNION
SELECT
ce.time AS event_time,
c.conference_id,
c.conference_name,
c.conference_host,
ce.loglevel,
ce.conference_event AS event_type,
NULL AS participant_id,
ce.conference_param AS event_param
FROM
conferences c
LEFT JOIN
conference_events ce ON c.conference_id = ce.conference_id
WHERE
event_type = 'stats_id' AND event_param LIKE '%%%s%%'
AND (event_time >= '%s 00:00:00' AND event_time <= '%s 23:59:59')
ORDER BY
pe.time";
if ($items_per_page) {
$items_per_page = (int)$items_per_page;
$sql .= ' LIMIT ' . $offset . ',' . $items_per_page;
}
$sql = sprintf($sql, $participant_name, $from_time, $until_time, $participant_name, $from_time, $until_time);
$query = $this->db->prepare($sql);
$query->execute();
return $query->fetchAll(PDO::FETCH_ASSOC);
}
// search/list specific participant IP
public function conferenceByParticipantIP($participant_ip, $from_time, $until_time, $offset=0, $items_per_page='') {
// time period drill-down
// FIXME make it similar to the bash version
if (empty($from_time)) {
$from_time = '0000-01-01';
}
if (empty($until_time)) {
$until_time = '9999-12-31';
}
// this is needed for compatibility with the bash version, so we use '%s' placeholders
$from_time = htmlspecialchars(strip_tags($from_time));
$until_time = htmlspecialchars(strip_tags($until_time));
// list conferences where participant IP is found
$sql = "
SELECT
pe.time,
c.conference_id,
c.conference_name,
c.conference_host,
pe.loglevel,
pe.event_type,
p.endpoint_id AS participant_id,
pe.event_param
FROM
conferences c
LEFT JOIN
conference_events ce ON c.conference_id = ce.conference_id
LEFT JOIN
participants p ON c.conference_id = p.conference_id
LEFT JOIN
participant_events pe ON p.endpoint_id = pe.participant_id
WHERE
pe.event_type = 'pair selected' AND pe.event_param = '%s'
AND (pe.time >= '%s 00:00:00' AND pe.time <= '%s 23:59:59')
UNION
SELECT
ce.time AS event_time,
c.conference_id,
c.conference_name,
c.conference_host,
ce.loglevel,
ce.conference_event AS event_type,
NULL AS participant_id,
ce.conference_param AS event_param
FROM
conferences c
LEFT JOIN
conference_events ce ON c.conference_id = ce.conference_id
WHERE
event_type = 'pair selected' AND event_param = '%s'
AND (event_time >= '%s 00:00:00' AND event_time <= '%s 23:59:59')
ORDER BY
pe.time";
if ($items_per_page) {
$items_per_page = (int)$items_per_page;
$sql .= ' LIMIT ' . $offset . ',' . $items_per_page;
}
$sql = sprintf($sql, $participant_ip, $from_time, $until_time, $participant_ip, $from_time, $until_time);
$query = $this->db->prepare($sql);
$query->execute();
return $query->fetchAll(PDO::FETCH_ASSOC);
}
// list of all participants
public function participantsAll($from_time, $until_time, $offset=0, $items_per_page='') {
// time period drill-down
// FIXME make it similar to the bash version
if (empty($from_time)) {
$from_time = '0000-01-01';
}
if (empty($until_time)) {
$until_time = '9999-12-31';
}
// this is needed for compatibility with the bash version, so we use '%s' placeholders
$from_time = htmlspecialchars(strip_tags($from_time));
$until_time = htmlspecialchars(strip_tags($until_time));
// list all participants
$sql = "
SELECT DISTINCT
p.jitsi_component, p.endpoint_id, p.conference_id
FROM
participants p
JOIN
participant_events pe ON p.endpoint_id = pe.participant_id
WHERE
pe.time >= '%s 00:00:00' AND pe.time <= '%s 23:59:59'
ORDER BY p.id";
if ($items_per_page) {
$items_per_page = (int)$items_per_page;
$sql .= ' LIMIT ' . $offset . ',' . $items_per_page;
}
$sql = sprintf($sql, $from_time, $until_time);
$query = $this->db->prepare($sql);
$query->execute();
return $query->fetchAll(PDO::FETCH_ASSOC);
}
// number of participants
public function participantNumber($from_time, $until_time) {
// time period drill-down
// FIXME make it similar to the bash version
if (empty($from_time)) {
$from_time = '0000-01-01';
}
if (empty($until_time)) {
$until_time = '9999-12-31';
}
// this is needed for compatibility with the bash version, so we use '%s' placeholders
$from_time = htmlspecialchars(strip_tags($from_time));
$until_time = htmlspecialchars(strip_tags($until_time));
// number of participants for time period (if given)
$sql = "
SELECT COUNT(DISTINCT p.endpoint_id) as participants
FROM
participants p
LEFT JOIN
participant_events pe ON p.endpoint_id = pe.participant_id
WHERE
(pe.time >= '%s 00:00:00' AND pe.time <= '%s 23:59:59')
AND pe.event_type = 'participant joining'";
$sql = sprintf($sql, $from_time, $until_time);
$query = $this->db->prepare($sql);
$query->execute();
return $query->fetchAll(PDO::FETCH_ASSOC);
}
}
?>

View File

@ -1,94 +0,0 @@
<?php
class Platform {
private $db;
public function __construct($database) {
$this->db = $database->getConnection();
}
// get details of a specified platform ID (or all)
public function getPlatformDetails($platform_id = '') {
$sql = 'SELECT * FROM platforms';
if ($platform_id !== '') {
$sql .= ' WHERE id = :platform_id';
$query = $this->db->prepare($sql);
$query->bindParam(':platform_id', $platform_id);
} else {
$query = $this->db->prepare($sql);
}
$query->execute();
return $query->fetchAll(PDO::FETCH_ASSOC);
}
// add new platform
public function addPlatform($newPlatform) {
try {
$sql = 'INSERT INTO platforms
(name, jitsi_url, jilo_database)
VALUES
(:name, :jitsi_url, :jilo_database)';
$query = $this->db->prepare($sql);
$query->execute([
':name' => $newPlatform['name'],
':jitsi_url' => $newPlatform['jitsi_url'],
':jilo_database' => $newPlatform['jilo_database'],
]);
return true;
} catch (Exception $e) {
return $e->getMessage();
}
}
// edit an existing platform
public function editPlatform($platform_id, $updatedPlatform) {
try {
$sql = 'UPDATE platforms SET
name = :name,
jitsi_url = :jitsi_url,
jilo_database = :jilo_database
WHERE
id = :platform_id';
$query = $this->db->prepare($sql);
$query->execute([
':name' => $updatedPlatform['name'],
':jitsi_url' => $updatedPlatform['jitsi_url'],
':jilo_database' => $updatedPlatform['jilo_database'],
':platform_id' => $platform_id,
]);
return true;
} catch (Exception $e) {
return $e->getMessage();
}
}
// delete a platform
public function deletePlatform($platform_id) {
try {
$sql = 'DELETE FROM platforms
WHERE
id = :platform_id';
$query = $this->db->prepare($sql);
$query->bindParam(':platform_id', $platform_id);
$query->execute();
return true;
} catch (Exception $e) {
return $e->getMessage();
}
}
}
?>

View File

@ -1,38 +0,0 @@
<?php
class Router {
private $routes = [];
public function add() {
$this->routes[$pattern] = $callback;
}
public function dispatch($url) {
// remove variables from url
$url = strtok($url, '?');
foreach ($this->routes as $pattern => $callback) {
if (preg_match('#^' . $pattern . '$#', $url, $matches)) {
// move any exact match
array_shift($matches);
return $this->invoke($callback, $matches);
}
}
// if there was no match at all, return 404
http_response_code(404);
echo '404 page not found';
}
private function invoke($callback, $params) {
list($controllerName, $methodName) = explode('@', $callback);
// $controllerClass = "\\App\\Controllers\\$controllerName";
$controllerClass = "../pages/$pageName";
$controller = new $controllerClass();
call_user_func_array([$controller, $methodName], $params);
}
}
?>

View File

@ -1,34 +0,0 @@
<?php
class Server {
private $db;
public function __construct($database) {
$this->db = $database->getConnection();
}
// get Jilo Server status
public function getServerStatus($host = '127.0.0.1', $port = 8080, $endpoint = '/health') {
$url = "http://$host:$port$endpoint";
$options = [
'http' => [
'method' => 'GET',
'timeout' => 3,
],
];
$context = stream_context_create($options);
$response = @file_get_contents($url, false, $context);
// We check the response if it's 200 OK
if ($response !== false && isset($http_response_header) && strpos($http_response_header[0], '200 OK') !== false) {
return true;
}
// If it's not 200 OK
return false;
}
}
?>

View File

@ -1,327 +0,0 @@
<?php
class User {
private $db;
public function __construct($database) {
$this->db = $database->getConnection();
}
// registration
public function register($username, $password) {
try {
// we have two inserts, start a transaction
$this->db->beginTransaction();
// hash the password, don't store it plain
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
// insert into users table
$sql = 'INSERT
INTO users (username, password)
VALUES (:username, :password)';
$query = $this->db->prepare($sql);
$query->bindValue(':username', $username);
$query->bindValue(':password', $hashedPassword);
// execute the first query
if (!$query->execute()) {
// rollback on error
$this->db->rollBack();
return false;
}
// insert the last user id into users_meta table
$sql2 = 'INSERT
INTO users_meta (user_id)
VALUES (:user_id)';
$query2 = $this->db->prepare($sql2);
$query2->bindValue(':user_id', $this->db->lastInsertId());
// execute the second query
if (!$query2->execute()) {
// rollback on error
$this->db->rollBack();
return false;
}
// if all is OK, commit the transaction
$this->db->commit();
return true;
} catch (Exception $e) {
// rollback on any error
$this->db->rollBack();
return $e->getMessage();
}
}
// login
public function login($username, $password) {
$query = $this->db->prepare("SELECT * FROM users WHERE username = :username");
$query->bindParam(':username', $username);
$query->execute();
$user = $query->fetch(PDO::FETCH_ASSOC);
if ( $user && password_verify($password, $user['password'])) {
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
return true;
} else {
return false;
}
}
// get user ID from username
// FIXME not used now?
public function getUserId($username) {
$sql = 'SELECT id FROM users WHERE username = :username';
$query = $this->db->prepare($sql);
$query->bindParam(':username', $username);
$query->execute();
return $query->fetchAll(PDO::FETCH_ASSOC);
}
// get user details
public function getUserDetails($user_id) {
$sql = 'SELECT
um.*,
u.username
FROM
users_meta um
LEFT JOIN users u
ON um.user_id = u.id
WHERE
u.id = :user_id';
$query = $this->db->prepare($sql);
$query->execute([
':user_id' => $user_id,
]);
return $query->fetchAll(PDO::FETCH_ASSOC);
}
// add user right
public function addUserRight($user_id, $right_id) {
$sql = 'INSERT INTO users_rights
(user_id, right_id)
VALUES
(:user_id, :right_id)';
$query = $this->db->prepare($sql);
$query->execute([
':user_id' => $user_id,
':right_id' => $right_id,
]);
}
// remove user right
public function removeUserRight($user_id, $right_id) {
$sql = 'DELETE FROM users_rights
WHERE
user_id = :user_id
AND
right_id = :right_id';
$query = $this->db->prepare($sql);
$query->execute([
':user_id' => $user_id,
':right_id' => $right_id,
]);
}
// get all rights
public function getAllRights() {
$sql = 'SELECT
id AS right_id,
name AS right_name
FROM rights
ORDER BY id ASC';
$query = $this->db->prepare($sql);
$query->execute();
return $query->fetchAll(PDO::FETCH_ASSOC);
}
// get user rights
public function getUserRights($user_id) {
$sql = 'SELECT
u.id AS user_id,
r.id AS right_id,
r.name AS right_name
FROM
users u
LEFT JOIN users_rights ur
ON u.id = ur.user_id
LEFT JOIN rights r
ON ur.right_id = r.id
WHERE
u.id = :user_id';
$query = $this->db->prepare($sql);
$query->execute([
':user_id' => $user_id,
]);
$result = $query->fetchAll(PDO::FETCH_ASSOC);
// ensure specific entries are included in the result
$specialEntries = [];
// user 1 is always superuser
if ($user_id == 1) {
$specialEntries = [
[
'user_id' => 1,
'right_id' => 1,
'right_name' => 'superuser'
]
];
// user 2 is always demo
} elseif ($user_id == 2) {
$specialEntries = [
[
'user_id' => 2,
'right_id' => 100,
'right_name' => 'demo user'
]
];
}
// merge the special entries with the existing results
$result = array_merge($specialEntries, $result);
// remove duplicates if necessary
$result = array_unique($result, SORT_REGULAR);
// return the modified result
return $result;
}
// check if the user has a specific right
function hasRight($user_id, $right_name) {
$userRights = $this->getUserRights($user_id);
$userHasRight = false;
// superuser always has all the rights
if ($user_id === 1) {
$userHasRight = true;
}
foreach ($userRights as $right) {
if ($right['right_name'] === $right_name) {
$userHasRight = true;
break;
}
}
return $userHasRight;
}
// update an existing user
public function editUser($user_id, $updatedUser) {
try {
$sql = 'UPDATE users_meta SET
name = :name,
email = :email,
timezone = :timezone,
bio = :bio
WHERE user_id = :user_id';
$query = $this->db->prepare($sql);
$query->execute([
':user_id' => $user_id,
':name' => $updatedUser['name'],
':email' => $updatedUser['email'],
':timezone' => $updatedUser['timezone'],
':bio' => $updatedUser['bio']
]);
return true;
} catch (Exception $e) {
return $e->getMessage();
}
}
// remove an avatar
public function removeAvatar($user_id, $old_avatar = '') {
try {
// remove from database
$sql = 'UPDATE users_meta SET
avatar = NULL
WHERE user_id = :user_id';
$query = $this->db->prepare($sql);
$query->execute([
':user_id' => $user_id,
]);
// delete the old avatar file
if ($old_avatar && file_exists($old_avatar)) {
unlink($old_avatar);
}
return true;
} catch (Exception $e) {
return $e->getMessage();
}
}
// change an avatar
public function changeAvatar($user_id, $avatar_file, $avatars_path) {
try {
// check if the file was uploaded
if (isset($avatar_file) && $avatar_file['error'] === UPLOAD_ERR_OK) {
$fileTmpPath = $avatar_file['tmp_name'];
$fileName = $avatar_file['name'];
$fileExtension = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
// validate file extension
if (in_array($fileExtension, ['jpg', 'png', 'jpeg'])) {
$newFileName = md5(time() . $fileName) . '.' . $fileExtension;
$dest_path = $avatars_path . $newFileName;
// move the file to avatars folder
if (move_uploaded_file($fileTmpPath, $dest_path)) {
try {
// update user's avatar path in DB
$sql = 'UPDATE users_meta SET
avatar = :avatar
WHERE user_id = :user_id';
$query = $this->db->prepare($sql);
$query->execute([
':avatar' => $newFileName,
':user_id' => $user_id
]);
// all went OK
$_SESSION['notice'] .= 'Avatar updated successfully. ';
return true;
} catch (Exception $e) {
return $e->getMessage();
}
} else {
$_SESSION['error'] .= 'Error moving the uploaded file. ';
}
} else {
$_SESSION['error'] .= 'Invalid avatar file type. ';
}
} else {
$_SESSION['error'] .= 'Error uploading the avatar file. ';
}
} catch (Exception $e) {
return $e->getMessage();
}
}
}
?>

View File

@ -1,40 +0,0 @@
<?php
return [
//*******************
// edit to customize
//*******************
// domain for the web app
'domain' => 'localhost',
// subfolder for the web app, if any
'folder' => '/jilo-web/',
// set to false to disable new registrations
'registration_enabled' => true,
// will be displayed on login screen
'login_message' => '',
//*******************************************
// edit only if needed for tests or debugging
//*******************************************
// database
'db' => [
// DB type for the web app, currently only "sqlite" is used
'db_type' => 'sqlite',
// default is ../app/jilo-web.db
'sqlite_file' => '../app/jilo-web.db',
],
// avatars path
'avatars_path' => 'uploads/avatars/',
// default avatar
'default_avatar' => 'static/default_avatar.png',
// system info
'version' => '0.2.1',
// development has verbose error messages, production has not
'environment' => 'development',
];
?>

View File

View File

@ -1,66 +0,0 @@
<?php
// connect to database
function connectDB($config, $database = '', $dbFile = '', $platformId = '') {
// connecting ti a jilo sqlite database
if ($database === 'jilo') {
try {
if (!$dbFile || !file_exists($dbFile)) {
throw new Exception(getError("Invalid platform ID \"{$platformId}\", database file \"{$dbFile}\" not found."));
}
$db = new Database([
'type' => 'sqlite',
'dbFile' => $dbFile,
]);
} catch (Exception $e) {
$error = getError('Error connecting to DB.', $e->getMessage());
include '../app/templates/block-message.php';
exit();
}
// connecting to a jilo-web database of the web app
} else {
// sqlite database file
if ($config['db']['db_type'] === 'sqlite') {
try {
$db = new Database([
'type' => $config['db']['db_type'],
'dbFile' => $config['db']['sqlite_file'],
]);
$pdo = $db->getConnection();
} catch (Exception $e) {
$error = getError('Error connecting to DB.', $e->getMessage());
include '../app/templates/block-message.php';
exit();
}
// mysql/mariadb database
} elseif ($config['db']['db_type'] === 'mysql' || $config['db']['db_type'] === 'mariadb') {
try {
$db = new Database([
'type' => $config['db']['db_type'],
'host' => $config['db']['sql_host'] ?? 'localhost',
'port' => $config['db']['sql_port'] ?? '3306',
'dbname' => $config['db']['sql_database'],
'user' => $config['db']['sql_username'],
'password' => $config['db']['sql_password'],
]);
$pdo = $db->getConnection();
} catch (Exception $e) {
$error = getError('Error connecting to DB.', $e->getMessage());
include '../app/templates/block-message.php';
exit();
}
// unknown database
} else {
$error = "Error: unknow database type \"{$config['db']['db_type']}\"";
include '../app/templates/block-message.php';
exit();
}
}
return $db;
}
?>

View File

@ -1,14 +0,0 @@
<?php
function getError($message, $error = '', $environment = null) {
global $config;
$environment = $config['environment'] ?? 'production';
if ($environment === 'production') {
return 'There was an unexpected error. Please try again.';
} else {
return $error ?: $message;
}
}
?>

View File

@ -1,127 +0,0 @@
<div style="position: relative; width: 800px; height: 400px;">
<div id="current-period-<?= $data['graph_name'] ?>" style="text-align: center; position: absolute; top: 0; left: 0; right: 0; z-index: 10; font-size: 14px; background-color: rgba(255, 255, 255, 0.7);"></div>
<canvas id="graph_<?= $data['graph_name'] ?>" style="margin-top: 20px; margin-bottom: 50px;"></canvas>
</div>
<script>
var ctx = document.getElementById('graph_<?= $data['graph_name'] ?>').getContext('2d');
var chartData0 = <?php echo json_encode($data['data0']); ?>;
var chartData1 = <?php echo json_encode($data['data1']); ?>;
var timeRangeName = '';
var labels = chartData0.map(function(item) {
return item.date;
});
var values0 = chartData0.map(function(item) {
return item.value;
});
var values1 = chartData1.map(function(item) {
return item.value;
});
var graph_<?= $data['graph_name'] ?> = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [
{
label: '<?= $data['graph_data0_label'] ?? '' ?>',
data: values0,
borderColor: 'rgba(75, 192, 192, 1)',
borderWidth: 1,
fill: false
},
{
label: '<?= $data['graph_data1_label'] ?? '' ?>',
data: values1,
borderColor: 'rgba(255, 99, 132, 1)',
borderWidth: 1,
fill: false
}
]
},
options: {
layout: {
padding: {
top: 30
}
},
scales: {
x: {
type: 'time',
time: {
unit: 'day'
}
},
y: {
beginAtZero: true
}
},
plugins: {
zoom: {
pan: {
enabled: true,
mode: 'x'
},
zoom: {
// enabled: true,
mode: 'x',
drag: {
enabled: true, // Enable drag to select range
borderColor: 'rgba(255, 99, 132, 0.3)',
borderWidth: 1
},
onZoom: function({ chart }) {
propagateZoom(chart); // Propagate the zoom to all graphs
setActive(document.getElementById('custom_range'));
}
}
},
legend: {
position: 'bottom',
labels: {
boxWidth: 20,
padding: 10
}
},
title: {
display: true,
text: '<?= $data['graph_title'] ?>',
font: {
size: 16,
weight: 'bold'
},
padding: {
bottom: 10
}
}
}
}
});
// Store the graphs in an array
graphs.push({
graph: graph_<?= $data['graph_name'] ?>,
label: document.getElementById('current-period-<?= $data['graph_name'] ?>')
});
// Update the time range label
function updatePeriodLabel(chart, labelElement) {
var startDate = chart.scales.x.min;
var endDate = chart.scales.x.max;
if (timeRangeName == 'today') {
labelElement.innerHTML = 'Currently displaying: ' + timeRangeName + ' (' + new Date(startDate).toLocaleDateString() + ')';
} else {
labelElement.innerHTML = 'Currently displaying: ' + timeRangeName + ' (' + new Date(startDate).toLocaleDateString() + ' - ' + new Date(endDate).toLocaleDateString() + ')';
}
}
// Attach the update function to the 'zoom' event
graph_<?= $data['graph_name'] ?>.options.plugins.zoom.onZoom = function({ chart }) {
updatePeriodLabel(chart, document.getElementById('current-period-<?= $data['graph_name'] ?>'));
};
// Update the label initially when the chart is rendered
updatePeriodLabel(graph_<?= $data['graph_name'] ?>, document.getElementById('current-period-<?= $data['graph_name'] ?>'));
</script>

View File

@ -1,22 +0,0 @@
<?php
function getUserIP() {
// get directly the user IP
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
// if user is behind some proxy
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
$ip = $_SERVER['REMOTE_ADDR'];
}
// get only the first IP if there are more
if (strpos($ip, ',') !== false) {
$ip = explode(',', $ip)[0];
}
return trim($ip);
}
?>

View File

@ -1,81 +0,0 @@
<div class="text-center">
<div class="pagination">
<?php
$param = '';
if (isset($_REQUEST['id'])) {
$param .= '&id=' . htmlspecialchars($_REQUEST['id']);
}
if (isset($_REQUEST['name'])) {
$param .= '&name=' . htmlspecialchars($_REQUEST['name']);
}
if (isset($_REQUEST['ip'])) {
$param .= '&ip=' . htmlspecialchars($_REQUEST['ip']);
}
if (isset($_REQUEST['event'])) {
$param .= '&event=' . htmlspecialchars($_REQUEST['event']);
}
if (isset($_REQUEST['from_time'])) {
$param .= '&from_time=' . htmlspecialchars($from_time);
}
if (isset($_REQUEST['until_time'])) {
$param .= '&until_time=' . htmlspecialchars($until_time);
}
$max_visible_pages = 10;
$step_pages = 10;
if ($browse_page > 1) {
echo '<span><a href="' . htmlspecialchars($url) . '&p=1">first</a></span>';
} else {
echo '<span>first</span>';
}
for ($i = 1; $i <= $page_count; $i++) {
// always show the first, last, step pages (10, 20, 30, etc.),
// and the pages close to the current one
if (
$i === 1 || // first page
$i === $page_count || // last page
$i === $browse_page || // current page
$i === $browse_page -1 ||
$i === $browse_page +1 ||
$i === $browse_page -2 ||
$i === $browse_page +2 ||
($i % $step_pages === 0 && $i > $max_visible_pages) // the step pages - 10, 20, etc.
) {
if ($i === $browse_page) {
// current page, no link
if ($browse_page > 1) {
echo '<span><a href="' . htmlspecialchars($app_root) . '?platform=' . htmlspecialchars($platform_id) . '&page=' . htmlspecialchars($page) . $param . '&p=' . (htmlspecialchars($browse_page) -1) . '"><<</a></span>';
} else {
echo '<span><<</span>';
}
echo '[' . htmlspecialchars($i) . ']';
if ($browse_page < $page_count) {
echo '<span><a href="' . htmlspecialchars($app_root) . '?platform=' . htmlspecialchars($platform_id) . '&page=' . htmlspecialchars($page) . $param . '&p=' . (htmlspecialchars($browse_page) +1) . '">>></a></span>';
} else {
echo '<span>>></span>';
}
} else {
// other pages
echo '<span><a href="' . htmlspecialchars($app_root) . '?platform=' . htmlspecialchars($platform_id) . '&page=' . htmlspecialchars($page) . $param . '&p=' . htmlspecialchars($i) . '">[' . htmlspecialchars($i) . ']</a></span>';
}
// show ellipses between distant pages
} elseif (
$i === $browse_page -3 ||
$i === $browse_page +3
) {
echo '<span>...</span>';
}
}
if ($browse_page < $page_count) {
echo '<span><a href="' . htmlspecialchars($app_root) . '?platform=' . htmlspecialchars($platform_id) . '&page=' . htmlspecialchars($page) . $param . '&p=' . (htmlspecialchars($page_count)) . '">last</a></span>';
} else {
echo '<span>last</span>';
}
?>
</div>
</div>

View File

@ -1,48 +0,0 @@
<?php
// get the UTC offset of a specified timezone
function getUTCOffset($timezone) {
$formattedOffset = '';
if (isset($timezone)) {
$datetime = new DateTime("now", new DateTimeZone($timezone));
$offsetInSeconds = $datetime->getOffset();
$hours = intdiv($offsetInSeconds, 3600);
$minutes = ($offsetInSeconds % 3600) / 60;
$formattedOffset = sprintf("UTC%+03d:%02d", $hours, $minutes); // Format UTC+01:00
}
return $formattedOffset;
}
// switch platforms
function switchPlatform($platform_id) {
// get the current URL and parse it
$scheme = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http';
$current_url = "$scheme://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
$url_components = parse_url($current_url);
// parse query parameters if they exist
parse_str($url_components['query'] ?? '', $query_params);
// check if the 'platform' parameter is set
if (isset($query_params['platform'])) {
// change the platform to the new platform_id
$query_params['platform'] = $platform_id;
$new_query_string = http_build_query($query_params);
// there is no 'platform', we redirect to front page of the new platform_id
} else {
$new_query_string = 'platform=' . $platform_id;
}
// rebuild the query and the URL
$new_url = $scheme . '://' . $url_components['host'] . $url_components['path'] . '?' . $new_query_string;
// return the new URL with the new platform_id
return $new_url;
}
?>

View File

@ -1,39 +0,0 @@
<?php
// render config variables array
function renderConfig($configPart, $indent, $platform=false, $parent='') {
global $app_root;
global $config;
?>
<div style="padding-left: <?= $indent ?>px; padding-bottom: 20px;">
<?php foreach ($configPart as $config_item => $config_value) { ?>
<div class="row mb-1" style="padding-left: <?= $indent ?>px;">
<div class="col-md-4 text-end">
<?= htmlspecialchars($config_item) ?>:
</div>
<?php
if (is_array($config_value)) {
// here we render recursively nested arrays
$indent = $indent + 50;
if ($parent === 'platforms') {
$indent = 100;
}
if ($config_item === 'platforms') {
renderConfig($config_value, $indent, $platform, 'platforms');
} else {
renderConfig($config_value, $indent, $platform);
}
$indent = 0;
} else {
// if it's not array, just display it
?>
<div class="border col-md-8 text-start">
<?= htmlspecialchars($config_value ?? '')?>
</div>
<?php } ?>
</div>
<?php } ?>
</div>
<?php
}
?>

View File

@ -1,51 +0,0 @@
<?php
// sanitize all input vars that may end up in URLs or forms
$platform_id = htmlspecialchars($_REQUEST['platform']);
if (isset($_REQUEST['page'])) {
$page = htmlspecialchars($_REQUEST['page']);
} else {
$page = 'dashboard';
}
if (isset($_REQUEST['item'])) {
$item = htmlspecialchars($_REQUEST['item']);
} else {
$item = '';
}
if (isset($_REQUEST['from_time'])) {
$from_time = htmlspecialchars($_REQUEST['from_time']);
}
if (isset($_REQUEST['until_time'])) {
$until_time = htmlspecialchars($_REQUEST['until_time']);
}
if (isset($_SESSION['notice'])) {
$notice = htmlspecialchars($_SESSION['notice']); // 'notice' for all non-critical messages
}
if (isset($_SESSION['error'])) {
$error = htmlspecialchars($_SESSION['error']); // 'error' for errors
}
// agents
if (isset($_POST['type'])) {
$type = htmlspecialchars($_POST['type']);
}
if (isset($_POST['url'])) {
$url = htmlspecialchars($_POST['url']);
}
if (isset($_POST['secret_key'])) {
$secret_key = htmlspecialchars($_POST['secret_key']);
}
if (isset($_POST['check_period'])) {
$check_period = htmlspecialchars($_POST['check_period']);
}
// platforms
if (isset($_POST['name'])) {
$name = htmlspecialchars($_POST['name']);
}
?>

View File

@ -1,15 +0,0 @@
<?php
$time_range_specified = false;
if (!isset($from_time) || (isset($from_time) && $from_time == '')) {
$from_time = '0000-01-01';
} else {
$time_range_specified = true;
}
if (!isset($until_time) || (isset($until_time) && $until_time == '')) {
$until_time = '9999-12-31';
} else {
$time_range_specified = true;
}
?>

View File

@ -1,56 +0,0 @@
<?php
$action = $_REQUEST['action'] ?? '';
$agent = $_REQUEST['agent'] ?? '';
require '../app/classes/agent.php';
$agentObject = new Agent($dbWeb);
// if it's a POST request, it's saving to cache
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
// read the JSON sent from javascript
$data = file_get_contents("php://input");
$result = json_decode($data, true);
// store the data in the session
if ($result) {
$_SESSION["agent{$agent}_cache"] = $result;
$_SESSION["agent{$agent}_cache_time"] = time(); // store the cache time
echo json_encode([
'status' => 'success',
'message' => "Cache for agent {$agent} is stored."
]);
} elseif ($result === null && !empty($agent)) {
unset($_SESSION["agent{$agent}_cache"]);
unset($_SESSION["agent{$agent}_cache_time"]);
echo json_encode([
'status' => 'success',
'message' => "Cache for agent {$agent} is cleared."
]);
} else {
echo json_encode([
'status' => 'error',
'message' => 'Invalid data'
]);
}
//// if it's a GET request, it's read/load from cache
//} elseif ($loadcache === true) {
//
// // check if cached data exists in session
// if (isset($_SESSION["agent{$agent}_cache"])) {
// // return the cached data in JSON format
// echo json_encode(['status' => 'success', 'data' => $_SESSION["agent{$agent}_cache"]]);
// } else {
// // if no cached data exists
// echo json_encode(['status' => 'error', 'message' => 'No cached data found']);
// }
// no form submitted, show the templates
} else {
$agentDetails = $agentObject->getAgentDetails($platform_id);
include '../app/templates/agent-list.php';
}
?>

View File

@ -1,85 +0,0 @@
<?php
require '../app/classes/component.php';
// connect to database
$db = connectDB($config, 'jilo', $platformDetails[0]['jilo_database'], $platform_id);
// specify time range
include '../app/helpers/time_range.php';
// jitsi component events list
// we use $_REQUEST, so that both links and forms work
// if it's there, but empty, we make it same as the field name; otherwise assign the value
$jitsi_component = !empty($_REQUEST['name']) ? "'" . $_REQUEST['name'] . "'" : 'jitsi_component';
$component_id = !empty($_REQUEST['id']) ? "'" . $_REQUEST['id'] . "'" : 'component_id';
$event_type = !empty($_REQUEST['event']) ? "'" . $_REQUEST['event'] . "'" : 'event_type';
//
// Component events listings
//
// list of all component events (default)
$componentObject = new Component($db);
// pagination variables
$items_per_page = 15;
$browse_page = $_REQUEST['p'] ?? 1;
$browse_page = (int)$browse_page;
$offset = ($browse_page -1) * $items_per_page;
// prepare the result
$search = $componentObject->jitsiComponents($jitsi_component, $component_id, $event_type, $from_time, $until_time, $offset, $items_per_page);
$search_all = $componentObject->jitsiComponents($jitsi_component, $component_id, $event_type, $from_time, $until_time);
if (!empty($search)) {
// we get total items and number of pages
$item_count = count($search_all);
$page_count = ceil($item_count / $items_per_page);
$components = array();
$components['records'] = array();
foreach ($search as $item) {
extract($item);
$component_record = array(
// assign title to the field in the array record
'component' => $jitsi_component,
'loglevel' => $loglevel,
'time' => $time,
'component ID' => $component_id,
'event' => $event_type,
'param' => $event_param,
);
// populate the result array
array_push($components['records'], $component_record);
}
}
// prepare the widget
$widget['full'] = false;
$widget['name'] = 'AllComponents';
$widget['filter'] = true;
$widget['pagination'] = true;
// widget title
if (isset($_REQUEST['name']) && $_REQUEST['name'] != '') {
$widget['title'] = 'Jitsi events for component&nbsp;<strong>' . $_REQUEST['name'] . '</strong>';
} elseif (isset($_REQUEST['id']) && $_REQUEST['id'] != '') {
$widget['title'] = 'Jitsi events for component ID&nbsp;<strong>' . $_REQUEST['id'] . '</strong>';
} else {
$widget['title'] = 'Jitsi events for&nbsp;<strong>all components</strong>';
}
// widget records
if (!empty($components['records'])) {
$widget['full'] = true;
$widget['table_headers'] = array_keys($components['records'][0]);
$widget['table_records'] = $components['records'];
}
// display the widget
include '../app/templates/event-list-components.php';
?>

View File

@ -1,149 +0,0 @@
<?php
require '../app/classes/conference.php';
// connect to database
$db = connectDB($config, 'jilo', $platformDetails[0]['jilo_database'], $platform_id);
// specify time range
include '../app/helpers/time_range.php';
// conference id/name are specified when searching specific conference(s)
// we use $_REQUEST, so that both links and forms work
// if it's there, but empty, we make it same as the field name; otherwise assign the value
//$conferenceName = !empty($_REQUEST['name']) ? "'" . $_REQUEST['name'] . "'" : 'conference_name';
//$conferenceId = !empty($_REQUEST['id']) ? "'" . $_REQUEST['id'] . "'" : 'conference_id';
if (isset($_REQUEST['id']) && $_REQUEST['id'] != '') {
$conferenceId = $_REQUEST['id'];
unset($_REQUEST['name']);
unset($conferenceName);
} elseif (isset($_REQUEST['name']) && $_REQUEST['name'] != '') {
unset($conferenceId);
$conferenceName = $_REQUEST['name'];
} else {
unset($conferenceId);
unset($conferenceName);
}
//
// Conference listings
//
$conferenceObject = new Conference($db);
// pagination variables
$items_per_page = 15;
$browse_page = $_REQUEST['p'] ?? 1;
$browse_page = (int)$browse_page;
$offset = ($browse_page -1) * $items_per_page;
// search and list specific conference ID
if (isset($conferenceId)) {
$search = $conferenceObject->conferenceById($conferenceId, $from_time, $until_time, $offset, $items_per_page);
$search_all = $conferenceObject->conferenceById($conferenceId, $from_time, $until_time);
// search and list specific conference name
} elseif (isset($conferenceName)) {
$search = $conferenceObject->conferenceByName($conferenceName, $from_time, $until_time, $offset, $items_per_page);
$search_all = $conferenceObject->conferenceByName($conferenceName, $from_time, $until_time);
// list of all conferences (default)
} else {
$search = $conferenceObject->conferencesAllFormatted($from_time, $until_time, $offset, $items_per_page);
$search_all = $conferenceObject->conferencesAllFormatted($from_time, $until_time);
}
if (!empty($search)) {
// we get total items and number of pages
$item_count = count($search_all);
$page_count = ceil($item_count / $items_per_page);
$conferences = array();
$conferences['records'] = array();
foreach ($search as $item) {
extract($item);
// we don't have duration field, so we calculate it
if (!empty($start) && !empty($end)) {
$duration = gmdate("H:i:s", abs(strtotime($end) - strtotime($start)));
} else {
$duration = '';
}
// search and list specific conference ID
if (isset($conferenceId)) {
$conference_record = array(
// assign title to the field in the array record
'time' => $time,
'conference ID' => $conference_id,
'conference name' => $conference_name,
'conference host' => $conference_host,
'loglevel' => $loglevel,
'participant ID' => $participant_id,
'event' => $event_type,
'parameter' => $event_param
);
// search and list specific conference name
} elseif (isset($conferenceName)) {
$conference_record = array(
// assign title to the field in the array record
'time' => $time,
'conference ID' => $conference_id,
'conference name' => $conference_name,
'conference host' => $conference_host,
'loglevel' => $loglevel,
'participant ID' => $participant_id,
'event' => $event_type,
'parameter' => $event_param
);
// list of all conferences (default)
} else {
$conference_record = array(
// assign title to the field in the array record
'component' => $jitsi_component,
'start' => $start,
'end' => $end,
'duration' => $duration,
'conference ID' => $conference_id,
'conference name' => $conference_name,
'participants' => $participants,
'name count' => $name_count,
'conference host' => $conference_host
);
}
// populate the result array
array_push($conferences['records'], $conference_record);
}
}
// prepare the widget
$widget['full'] = false;
$widget['name'] = 'Conferences';
$widget['collapsible'] = false;
$widget['collapsed'] = false;
$widget['filter'] = true;
$widget['pagination'] = true;
// widget title
if (isset($_REQUEST['name']) && $_REQUEST['name'] != '') {
$widget['title'] = 'Conferences with name matching "<strong>' . $_REQUEST['name'] . '"</strong>';
} elseif (isset($_REQUEST['id']) && $_REQUEST['id'] != '') {
$widget['title'] = 'Conference with ID "<strong>' . $_REQUEST['id'] . '"</strong>';
} else {
$widget['title'] = 'All conferences';
}
// widget records
if (!empty($conferences['records'])) {
$widget['full'] = true;
$widget['table_headers'] = array_keys($conferences['records'][0]);
$widget['table_records'] = $conferences['records'];
}
// display the widget
include '../app/templates/event-list-conferences.php';
?>

View File

@ -1,166 +0,0 @@
<?php
$action = $_REQUEST['action'] ?? '';
$agent = $_REQUEST['agent'] ?? '';
require '../app/classes/config.php';
require '../app/classes/agent.php';
$configObject = new Config();
$agentObject = new Agent($dbWeb);
// if a form is submitted, it's from the edit page
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
// FIXME - if editing the flat file is no more needed, remove this
// // load the config file and initialize a copy
// $content = file_get_contents($config_file);
// $updatedContent = $content;
// new agent adding
if (isset($_POST['new']) && isset($_POST['item']) && $_POST['new'] === 'true' && $_POST['item'] === 'agent') {
$newAgent = [
'type_id' => $type,
'url' => $url,
'secret_key' => $secret_key,
'check_period' => $check_period,
];
$result = $agentObject->addAgent($platform_id, $newAgent);
if ($result === true) {
$_SESSION['notice'] = "New Jilo Agent added.";
} else {
$_SESSION['error'] = "Adding the agent failed. Error: $result";
}
// new platform adding
} elseif (isset($_POST['new']) && $_POST['new'] === 'true') {
$newPlatform = [
'name' => $name,
'jitsi_url' => $_POST['jitsi_url'],
'jilo_database' => $_POST['jilo_database'],
];
$platformObject->addPlatform($newPlatform);
// deleting an agent
} elseif (isset($_POST['delete']) && isset($_POST['agent']) && $_POST['delete'] === 'true') {
$result = $agentObject->deleteAgent($agent);
if ($result === true) {
$_SESSION['notice'] = "Agent id \"{$_REQUEST['agent']}\" deleted.";
} else {
$_SESSION['error'] = "Deleting the agent failed. Error: $result";
}
// deleting a platform
} elseif (isset($_POST['delete']) && $_POST['delete'] === 'true') {
$platform = $_POST['platform'];
$platformObject->deletePlatform($platform);
// an update to an existing agent
} elseif (isset($_POST['agent'])) {
$updatedAgent = [
'id' => $agent,
'agent_type_id' => $type,
'url' => $url,
'secret_key' => $secret_key,
'check_period' => $check_period,
];
$result = $agentObject->editAgent($platform_id, $updatedAgent);
if ($result === true) {
$_SESSION['notice'] = "Agent id \"{$_REQUEST['agent']}\" edited.";
} else {
$_SESSION['error'] = "Editing the agent failed. Error: $result";
}
// an update to an existing platform
} else {
$platform = $_POST['platform'];
$updatedPlatform = [
'name' => $name,
'jitsi_url' => $_POST['jitsi_url'],
'jilo_database' => $_POST['jilo_database'],
];
$platformObject->editPlatform($platform, $updatedPlatform);
}
// FIXME - if this is not needed for editing the flat file, remove it
// // check if file is writable
// if (!is_writable($config_file)) {
// $_SESSION['error'] = getError('Configuration file is not writable.');
// header("Location: $app_root?platform=$platform_id&page=config");
// exit();
// }
//
// // try to update the config file
// if (file_put_contents($config_file, $updatedContent) !== false) {
// // update successful
// $_SESSION['notice'] = "Configuration for {$_POST['name']} is updated.";
// } else {
// // unsuccessful
// $error = error_get_last();
// $_SESSION['error'] = getError('Error updating the config: ' . ($error['message'] ?? 'unknown error'));
// }
// FIXME the new file is not loaded on first page load
unset($config);
header("Location: $app_root?platform=$platform_id&page=config");
exit();
// no form submitted, show the templates
} else {
// $item - config.js and interface_config.js are special case; remote loaded files
switch ($item) {
case 'configjs':
$mode = $_REQUEST['mode'] ?? '';
$raw = ($mode === 'raw');
$platformConfigjs = $configObject->getPlatformConfigjs($platformDetails[0]['jitsi_url'], $raw);
include '../app/templates/config-list-configjs.php';
break;
case 'interfaceconfigjs':
$mode = $_REQUEST['mode'] ?? '';
$raw = ($mode === 'raw');
$platformInterfaceConfigjs = $configObject->getPlatformInterfaceConfigjs($platformDetails[0]['jitsi_url'], $raw);
include '../app/templates/config-list-interfaceconfigjs.php';
break;
// if there is no $item, we work on the local config DB
default:
switch ($action) {
case 'add-agent':
$jilo_agent_types = $agentObject->getAgentTypes();
$jilo_agents_in_platform = $agentObject->getPlatformAgentTypes($platform_id);
$jilo_agent_types_in_platform = array_column($jilo_agents_in_platform, 'agent_type_id');
include '../app/templates/config-add-agent.php';
break;
case 'add':
include '../app/templates/config-add-platform.php';
break;
case 'edit':
if (isset($_GET['agent'])) {
$agentDetails = $agentObject->getAgentDetails($platform_id, $agent);
$jilo_agent_types = $agentObject->getAgentTypes();
include '../app/templates/config-edit-agent.php';
} else {
include '../app/templates/config-edit-platform.php';
}
break;
case 'delete':
if (isset($_GET['agent'])) {
$agentDetails = $agentObject->getAgentDetails($platform_id, $agent);
include '../app/templates/config-delete-agent.php';
} else {
include '../app/templates/config-delete-platform.php';
}
break;
default:
if ($userObject->hasRight($user_id, 'view config file')) {
include '../app/templates/config-list.php';
} else {
include '../app/templates/error-unauthorized.php';
}
}
}
}
?>

View File

@ -1,204 +0,0 @@
<?php
require '../app/classes/conference.php';
require '../app/classes/participant.php';
// connect to database
$db = connectDB($config, 'jilo', $platformDetails[0]['jilo_database'], $platform_id);
//
// dashboard widget listings
//
////
// monthly usage
$conferenceObject = new Conference($db);
$participantObject = new Participant($db);
// monthly conferences for the last year
$fromMonth = (new DateTime())->sub(new DateInterval('P1Y'));
$fromMonth->modify('first day of this month');
$thisMonth = new DateTime();
$from_time = $fromMonth->format('Y-m-d');
$until_time = $thisMonth->format('Y-m-d');
$widget['records'] = array();
// loop 1 year in the past
$i = 0;
while ($fromMonth < $thisMonth) {
$untilMonth = clone $fromMonth;
$untilMonth->modify('last day of this month');
$from_time = $fromMonth->format('Y-m-d');
$until_time = $untilMonth->format('Y-m-d');
$searchConferenceNumber = $conferenceObject->conferenceNumber($from_time, $until_time);
$searchParticipantNumber = $participantObject->participantNumber($from_time, $until_time);
// pretty format for displaying the month in the widget
$month = $fromMonth->format('F Y');
// populate the records
$widget['records'][$i] = array(
'from_time' => $from_time,
'until_time' => $until_time,
'table_headers' => $month,
'conferences' => $searchConferenceNumber[0]['conferences'],
'participants' => $searchParticipantNumber[0]['participants'],
);
// move everything one month in future
$untilMonth->add(new DateInterval('P1M'));
$fromMonth->add(new DateInterval('P1M'));
$i++;
}
$time_range_specified = true;
// prepare the widget
$widget['full'] = false;
$widget['name'] = 'LastYearMonths';
$widget['title'] = 'Conferences monthly stats for the last year';
$widget['collapsible'] = true;
$widget['collapsed'] = false;
$widget['filter'] = false;
if (!empty($searchConferenceNumber) && !empty($searchParticipantNumber)) {
$widget['full'] = true;
}
$widget['pagination'] = false;
// display the widget
include '../app/templates/widget-monthly.php';
////
// conferences in last 2 days
// time range limit
$from_time = date('Y-m-d', time() - 60 * 60 * 24 * 2);
$until_time = date('Y-m-d', time());
$time_range_specified = true;
// prepare the result
$search = $conferenceObject->conferencesAllFormatted($from_time, $until_time);
if (!empty($search)) {
$conferences = array();
$conferences['records'] = array();
foreach ($search as $item) {
extract($item);
// we don't have duration field, so we calculate it
if (!empty($start) && !empty($end)) {
$duration = gmdate("H:i:s", abs(strtotime($end) - strtotime($start)));
} else {
$duration = '';
}
$conference_record = array(
// assign title to the field in the array record
'component' => $jitsi_component,
'start' => $start,
'end' => $end,
'duration' => $duration,
'conference ID' => $conference_id,
'conference name' => $conference_name,
'participants' => $participants,
'name count' => $name_count,
'conference host' => $conference_host
);
// populate the result array
array_push($conferences['records'], $conference_record);
}
}
// prepare the widget
$widget['full'] = false;
$widget['name'] = 'LastDays';
$widget['title'] = 'Conferences for the last 2 days';
$widget['collapsible'] = true;
$widget['collapsed'] = false;
$widget['filter'] = false;
if (!empty($conferences['records'])) {
$widget['full'] = true;
$widget['table_headers'] = array_keys($conferences['records'][0]);
$widget['table_records'] = $conferences['records'];
}
$widget['pagination'] = false;
// display the widget
include '../app/templates/widget.php';
////
// last 10 conferences
// all time
$from_time = '0000-01-01';
$until_time = '9999-12-31';
$time_range_specified = false;
// number of conferences to show
$conference_number = 10;
// prepare the result
$search = $conferenceObject->conferencesAllFormatted($from_time, $until_time);
if (!empty($search)) {
$conferences = array();
$conferences['records'] = array();
$i = 0;
foreach ($search as $item) {
extract($item);
// we don't have duration field, so we calculate it
if (!empty($start) && !empty($end)) {
$duration = gmdate("H:i:s", abs(strtotime($end) - strtotime($start)));
} else {
$duration = '';
}
$conference_record = array(
// assign title to the field in the array record
'component' => $jitsi_component,
'start' => $start,
'end' => $end,
'duration' => $duration,
'conference ID' => $conference_id,
'conference name' => $conference_name,
'participants' => $participants,
'name count' => $name_count,
'conference host' => $conference_host
);
// populate the result array
array_push($conferences['records'], $conference_record);
// we only take the first 10 results
$i++;
if ($i == 10) break;
}
}
// prepare the widget
$widget['full'] = false;
$widget['name'] = 'LastConferences';
$widget['title'] = 'The last ' . $conference_number . ' conferences';
$widget['collapsible'] = true;
$widget['collapsed'] = false;
$widget['filter'] = false;
$widget['pagination'] = false;
if (!empty($conferences['records'])) {
$widget['full'] = true;
$widget['table_headers'] = array_keys($conferences['records'][0]);
$widget['table_records'] = $conferences['records'];
}
// display the widget
include '../app/templates/widget.php';
?>

View File

@ -1,49 +0,0 @@
<?php
// FIXME example data
$one = date('Y-m-d',strtotime("-5 days"));
$two = date('Y-m-d',strtotime("-4 days"));
$three = date('Y-m-d',strtotime("-2 days"));
$four = date('Y-m-d',strtotime("-1 days"));
$graph[0]['data0'] = [
['date' => $one, 'value' => 10],
['date' => $two, 'value' => 20],
['date' => $three, 'value' => 15],
['date' => $four, 'value' => 25],
];
$graph[0]['data1'] = [
['date' => $one, 'value' => 12],
['date' => $two, 'value' => 23],
['date' => $three, 'value' => 11],
['date' => $four, 'value' => 27],
];
$graph[0]['graph_name'] = 'conferences';
$graph[0]['graph_title'] = 'Conferences in "' . htmlspecialchars($platformDetails[0]['name']) . '" over time';
$graph[0]['graph_data0_label'] = 'Conferences from Jitsi logs (Jilo)';
$graph[0]['graph_data1_label'] = 'Conferences from Jitsi API (Jilo Agents)';
$graph[1]['data0'] = [
['date' => $one, 'value' => 20],
['date' => $two, 'value' => 30],
['date' => $three, 'value' => 15],
['date' => $four, 'value' => 55],
];
$graph[1]['data1'] = [
['date' => $one, 'value' => 22],
['date' => $two, 'value' => 33],
['date' => $three, 'value' => 11],
['date' => $four, 'value' => 57],
];
$graph[1]['graph_name'] = 'participants';
$graph[1]['graph_title'] = 'Participants in "' . htmlspecialchars($platformDetails[0]['name']) . '" over time';
$graph[1]['graph_data0_label'] = 'Participants from Jitsi logs (Jilo)';
$graph[1]['graph_data1_label'] = 'Participants from Jitsi API (Jilo Agents)';
include '../app/templates/graphs-combined.php';
?>

View File

@ -1,5 +0,0 @@
<?php
include '../app/templates/help-main.php';
?>

View File

@ -1,29 +0,0 @@
<?php
require '../app/classes/agent.php';
$agentObject = new Agent($dbWeb);
$latestJvbConferences = $agentObject->getLatestData($platform_id, 'jvb', 'conferences');
$latestJvbParticipants = $agentObject->getLatestData($platform_id, 'jvb', 'participants');
$latestJicofoConferences = $agentObject->getLatestData($platform_id, 'jicofo', 'conferences');
$latestJicofoParticipants = $agentObject->getLatestData($platform_id, 'jicofo', 'participants');
$widget['records'] = array();
// prepare the widget
$widget['full'] = false;
$widget['name'] = 'LatestData';
$widget['title'] = 'Latest data from Jilo Agents';
$widget['collapsible'] = false;
$widget['collapsed'] = false;
$widget['filter'] = false;
if (!empty($latestJvbConferences) && !empty($latestJvbParticipants) && !empty($latestJicofoConferences) && !empty($latestJicofoParticipants)) {
$widget['full'] = true;
}
$widget['pagination'] = true;
include '../app/templates/latest-data.php';
?>

View File

@ -1,71 +0,0 @@
<?php
//
// logs listings
//
// specify time range
include '../app/helpers/time_range.php';
// pagination variables
$items_per_page = 15;
$browse_page = $_REQUEST['p'] ?? 1;
$browse_page = (int)$browse_page;
$offset = ($browse_page -1) * $items_per_page;
// user or system
$scope = 'user';
// prepare the result
$search = $logObject->readLog($user_id, $scope, $offset, $items_per_page);
$search_all = $logObject->readLog($user_id, $scope);
if (!empty($search)) {
// we get total items and number of pages
$item_count = count($search_all);
$page_count = ceil($item_count / $items_per_page);
$logs = array();
$logs['records'] = array();
foreach ($search as $item) {
// when we show only user's logs, omit user_id column
if ($scope === 'user') {
$log_record = array(
// assign title to the field in the array record
'time' => $item['time'],
'log message' => $item['message']
);
} else {
$log_record = array(
// assign title to the field in the array record
'userID' => $item['user_id'],
'time' => $item['time'],
'log message' => $item['message']
);
}
// populate the result array
array_push($logs['records'], $log_record);
}
}
// prepare the widget
$widget['full'] = false;
$widget['collapsible'] = false;
$widget['name'] = 'Logs';
$username = $userObject->getUserDetails($user_id)[0]['username'];
$widget['title'] = "Log events for user \"$username\"";
$widget['filter'] = true;
if (!empty($logs['records'])) {
$widget['full'] = true;
$widget['table_headers'] = array_keys($logs['records'][0]);
$widget['table_records'] = $logs['records'];
}
$widget['pagination'] = true;
// display the widget
include '../app/templates/logs-list.php';
?>

View File

@ -1,155 +0,0 @@
<?php
require '../app/classes/participant.php';
// connect to database
$db = connectDB($config, 'jilo', $platformDetails[0]['jilo_database'], $platform_id);
// specify time range
include '../app/helpers/time_range.php';
// participant id/name/IP are specified when searching specific participant(s)
// participant name - this is 'stats_id' in the db
// either id, name, OR IP - in that order
// we use $_REQUEST, so that both links and forms work
if (isset($_REQUEST['id']) && $_REQUEST['id'] != '') {
$participantId = $_REQUEST['id'];
unset($_REQUEST['name']);
unset($participantName);
} elseif (isset($_REQUEST['name']) && $_REQUEST['name'] != '') {
unset($participantId);
$participantName = $_REQUEST['name'];
} elseif (isset($_REQUEST['ip']) && $_REQUEST['ip'] != '') {
unset($participantId);
$participantIp = $_REQUEST['ip'];
} else {
unset($participantId);
unset($participantName);
}
//
// Participant listings
//
$participantObject = new Participant($db);
// pagination variables
$items_per_page = 15;
$browse_page = $_REQUEST['p'] ?? 1;
$browse_page = (int)$browse_page;
$offset = ($browse_page -1) * $items_per_page;
// search and list specific participant ID
if (isset($participantId)) {
$search = $participantObject->conferenceByParticipantId($participantId, $from_time, $until_time, $offset, $items_per_page);
$search_all = $participantObject->conferenceByParticipantId($participantId, $from_time, $until_time);
// search and list specific participant name (stats_id)
} elseif (isset($participantName)) {
$search = $participantObject->conferenceByParticipantName($participantName, $from_time, $until_time, $offset, $items_per_page);
$search_all = $participantObject->conferenceByParticipantName($participantName, $from_time, $until_time);
// search and list specific participant IP
} elseif (isset($participantIp)) {
$search = $participantObject->conferenceByParticipantIP($participantIp, $from_time, $until_time, $offset, $items_per_page);
$search_all = $participantObject->conferenceByParticipantIP($participantIp, $from_time, $until_time);
// list of all participants (default)
} else {
// prepare the result
$search = $participantObject->participantsAll($from_time, $until_time, $offset, $items_per_page);
$search_all = $participantObject->participantsAll($from_time, $until_time);
}
if (!empty($search)) {
// we get total items and number of pages
$item_count = count($search_all);
$page_count = ceil($item_count / $items_per_page);
$participants = array();
$participants['records'] = array();
foreach ($search as $item) {
extract($item);
// search and list specific participant ID
if (isset($participantId)) {
$participant_record = array(
// assign title to the field in the array record
'time' => $time,
'conference ID' => $conference_id,
'conference name' => $conference_name,
'conference host' => $conference_host,
'loglevel' => $loglevel,
'participant ID' => $participant_id,
'event' => $event_type,
'parameter' => $event_param
);
// search and list specific participant name (stats_id)
} elseif (isset($participantName)) {
$participant_record = array(
// assign title to the field in the array record
'time' => $time,
'conference ID' => $conference_id,
'conference name' => $conference_name,
'conference host' => $conference_host,
'loglevel' => $loglevel,
'participant ID' => $participant_id,
'event' => $event_type,
'parameter' => $event_param
);
// search and list specific participant IP
} elseif (isset($participantIp)) {
$participant_record = array(
// assign title to the field in the array record
'time' => $time,
'conference ID' => $conference_id,
'conference name' => $conference_name,
'conference host' => $conference_host,
'loglevel' => $loglevel,
'participant ID' => $participant_id,
'event' => $event_type,
'parameter' => $event_param
);
// list of all participants (default)
} else {
$participant_record = array(
// assign title to the field in the array record
'component' => $jitsi_component,
'participant ID' => $endpoint_id,
'conference ID' => $conference_id
);
}
// populate the result array
array_push($participants['records'], $participant_record);
}
}
// prepare the widget
$widget['full'] = false;
$widget['name'] = 'Participants';
$widget['collapsible'] = false;
$widget['collapsed'] = false;
$widget['filter'] = true;
$widget['pagination'] = true;
// widget title
if (isset($_REQUEST['name']) && $_REQUEST['name'] != '') {
$widget['title'] = 'Conferences with participant name (stats_id) matching "<strong>' . $_REQUEST['name'] . '"</strong>';
} elseif (isset($_REQUEST['id']) && $_REQUEST['id'] != '') {
$widget['title'] = 'Conference with participant ID matching "<strong>' . $_REQUEST['id'] . '"</strong>';
} elseif (isset($participantIp)) {
$widget['title'] = 'Conference with participant IP matching "<strong>' . $participantIp . '"</strong>';
} else {
$widget['title'] = 'All participants';
}
// widget records
if (!empty($participants['records'])) {
$widget['full'] = true;
$widget['table_headers'] = array_keys($participants['records'][0]);
$widget['table_records'] = $participants['records'];
}
// display the widget
include '../app/templates/widget.php';
?>

View File

@ -1,84 +0,0 @@
<?php
$action = $_REQUEST['action'] ?? '';
// if a form is submitted, it's from the edit page
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$item = $_REQUEST['item'] ?? '';
// avatar removal
if ($item === 'avatar' && $action === 'remove') {
$result = $userObject->removeAvatar($user_id, $config['avatars_path'].$userDetails[0]['avatar']);
if ($result === true) {
$_SESSION['notice'] .= "Avatar for user \"{$userDetails[0]['username']}\" is removed. ";
} else {
$_SESSION['error'] .= "Removing the avatar failed. Error: $result ";
}
header("Location: $app_root?page=profile");
exit();
}
// update the profile
$updatedUser = [
'name' => $_POST['name'] ?? '',
'email' => $_POST['email'] ?? '',
'timezone' => $_POST['timezone'] ?? '',
'bio' => $_POST['bio'] ?? '',
];
$result = $userObject->editUser($user_id, $updatedUser);
if ($result === true) {
$_SESSION['notice'] .= "User details for \"{$updatedUser['name']}\" are edited. ";
} else {
$_SESSION['error'] .= "Editing the user details failed. Error: $result ";
}
// update the rights
$newRights = $_POST['rights'] ?? array();
// extract the new right_ids
$userRightsIds = array_column($userRights, 'right_id');
// what rights we need to add
$rightsToAdd = array_diff($newRights, $userRightsIds);
if (!empty($rightsToAdd)) {
foreach ($rightsToAdd as $rightId) {
$userObject->addUserRight($user_id, $rightId);
}
}
// what rights we need to remove
$rightsToRemove = array_diff($userRightsIds, $newRights);
if (!empty($rightsToRemove)) {
foreach ($rightsToRemove as $rightId) {
$userObject->removeUserRight($user_id, $rightId);
}
}
// update the avatar
if (!empty($_FILES['avatar_file']['tmp_name'])) {
$result = $userObject->changeAvatar($user_id, $_FILES['avatar_file'], $config['avatars_path']);
}
header("Location: $app_root?page=profile");
exit();
// no form submitted, show the templates
} else {
$avatar = !empty($userDetails[0]['avatar']) ? $config['avatars_path'] . $userDetails[0]['avatar'] : $config['default_avatar'];
$default_avatar = empty($userDetails[0]['avatar']) ? true : false;
switch ($action) {
case 'edit':
$allRights = $userObject->getAllRights();
$allTimezones = timezone_identifiers_list();
// if timezone is already set, we pass a flag for JS to not autodetect browser timezone
$isTimezoneSet = !empty($userDetails[0]['timezone']);
include '../app/templates/profile-edit.php';
break;
default:
include '../app/templates/profile.php';
}
}
?>

View File

@ -1,48 +0,0 @@
<?php
// registration is allowed, go on
if ($config['registration_enabled'] === true) {
// require '../app/classes/user.php';
unset($error);
try {
// connect to database
$dbWeb = connectDB($config);
// $userObject = new User($dbWeb);
if ( $_SERVER['REQUEST_METHOD'] == 'POST' ) {
$username = $_POST['username'];
$password = $_POST['password'];
// registering
$result = $userObject->register($username, $password);
// redirect to login
if ($result === true) {
$_SESSION['notice'] = "Registration successful.<br />You can log in now.";
header('Location: ' . htmlspecialchars($app_root));
exit();
// registration fail, redirect to login
} else {
$_SESSION['error'] = "Registration failed. $result";
header('Location: ' . htmlspecialchars($app_root));
exit();
}
}
} catch (Exception $e) {
$error = $e->getMessage();
}
include '../app/templates/block-message.php';
include '../app/templates/form-register.php';
// registration disabled
} else {
$notice = 'Registration is disabled';
include '../app/templates/block-message.php';
}
?>

View File

@ -1,52 +0,0 @@
<?php
// Jilo components status checks
//
require '../app/classes/agent.php';
$agentObject = new Agent($dbWeb);
include '../app/templates/status-server.php';
foreach ($platformsAll as $platform) {
include '../app/templates/status-platform.php';
$agentDetails = $agentObject->getAgentDetails($platform['id']);
foreach ($agentDetails as $agent) {
$agent_url = parse_url($agent['url']);
$agent_protocol = isset($agent_url['scheme']) ? $agent_url['scheme']: '';
$agent_host = isset($agent_url['host']) ? $agent_url['host']: '';
$agent_port = isset($agent_url['port']) ? $agent_url['port']: '';
// we get agent data to check availability
$agent_response = $agentObject->fetchAgent($agent['id'], true);
$agent_data = json_decode($agent_response);
if (json_last_error() === JSON_ERROR_NONE) {
$agent_availability = '<span class="text-warning">unknown</span>';
foreach ($agent_data as $key => $value) {
if ($key === 'error') {
$agent_availability = '<span class="text-danger">' . htmlspecialchars($value) . '</span>';
break;
}
if (preg_match('/_state$/', $key)) {
if ($value === 'error') {
$agent_availability = '<span class="text-danger">not running</span>';
break;
}
if ($value === 'running') {
$agent_availability = '<span class="text-success">running</span>';
break;
}
}
}
} else {
$agent_availability = 'json error';
}
include '../app/templates/status-agent.php';
}
}
?>

View File

@ -1,39 +0,0 @@
<!-- jilo agents -->
<div class="card text-center w-75 mx-lef">
<p class="h4 card-header">Jilo Agents on platform <?= htmlspecialchars($platform_id) ?> (<?= htmlspecialchars($platformDetails[0]['name']) ?>)</p>
<div class="card-body">
<?php foreach ($agentDetails as $agent) { ?>
<p class="card-text text-left" style="text-align: left;">
agent id: <strong><?= htmlspecialchars($agent['id']) ?></strong>
agent type: <?= htmlspecialchars($agent['agent_type_id']) ?> (<strong><?= htmlspecialchars($agent['agent_description']) ?></strong>)
<br />
endpoint: <strong><?= htmlspecialchars($agent['url']) ?><?= htmlspecialchars($agent['agent_endpoint']) ?></strong>
<br />
<?php
$payload = [
'iss' => 'Jilo Web',
'aud' => $config['domain'],
'iat' => time(),
'exp' => time() + 3600,
'agent_id' => $agent['id']
];
$jwt = $agentObject->generateAgentToken($payload, $agent['secret_key']);
// print_r($_SESSION);
?>
<?php if (isset($_SESSION["agent{$agent['id']}_cache"])) { ?>
<button id="agent<?= htmlspecialchars($agent['id']) ?>-status" class="btn btn-primary" data-toggle="tooltip" data-trigger="hover" data-placement="bottom" title="get the agent status" onclick="fetchData('<?= htmlspecialchars($agent['id']) ?>', '<?= htmlspecialchars($agent['url']) ?>', '/status', '<?= htmlspecialchars($jwt) ?>', true)">get status</button>
<button id="agent<?= htmlspecialchars($agent['id']) ?>-fetch" class="btn btn-primary" data-toggle="tooltip" data-trigger="hover" data-placement="bottom" title="get data from the agent" onclick="fetchData('<?= htmlspecialchars($agent['id']) ?>', '<?= htmlspecialchars($agent['url']) ?>', '<?= htmlspecialchars($agent['agent_endpoint']) ?>', '<?= htmlspecialchars($jwt) ?>', true)">fetch data</button>
<button id="agent<?= htmlspecialchars($agent['id']) ?>-cache" class="btn btn-secondary" data-toggle="tooltip" data-trigger="hover" data-placement="bottom" title="load cache" onclick="loadCache('<?= htmlspecialchars($agent['id']) ?>')">load cache</button>
<button id="agent<?= htmlspecialchars($agent['id']) ?>-clear" class="btn btn-danger" data-toggle="tooltip" data-trigger="hover" data-placement="bottom" title="clear cache" onclick="clearCache('<?= htmlspecialchars($agent['id']) ?>')">clear cache</button>
<span id="cacheInfo<?= htmlspecialchars($agent['id']) ?>" style="margin: 5px 0;"></span>
<?php } else { ?>
<button id="agent<?= htmlspecialchars($agent['id']) ?>-status" class="btn btn-primary" data-toggle="tooltip" data-trigger="hover" data-placement="bottom" title="get the agent status" onclick="fetchData('<?= htmlspecialchars($agent['id']) ?>', '<?= htmlspecialchars($agent['url']) ?>', '/status', '<?= htmlspecialchars($jwt) ?>', true)">get status</button>
<button id="agent<?= htmlspecialchars($agent['id']) ?>-fetch" class="btn btn-primary" data-toggle="tooltip" data-trigger="hover" data-placement="bottom" title="get data from the agent" onclick="fetchData('<?= htmlspecialchars($agent['id']) ?>', '<?= htmlspecialchars($agent['url']) ?>', '<?= htmlspecialchars($agent['agent_endpoint']) ?>', '<?= htmlspecialchars($jwt) ?>')">fetch data</button>
<button style="display: none" disabled id="agent<?= htmlspecialchars($agent['id']) ?>-cache" class="btn btn-secondary" data-toggle="tooltip" data-trigger="hover" data-placement="bottom" title="load cache" onclick="loadCache('<?= htmlspecialchars($agent['id']) ?>')">load cache</button>
<button style="display: none" disabled id="agent<?= htmlspecialchars($agent['id']) ?>-clear" class="btn btn-danger" data-toggle="tooltip" data-trigger="hover" data-placement="bottom" title="clear cache" onclick="clearCache('<?= htmlspecialchars($agent['id']) ?>')">clear cache</button>
<span style="display: none" id="cacheInfo<?= htmlspecialchars($agent['id']) ?>" style="margin: 5px 0;"></span>
<?php } ?>
</p>
<pre class="results" id="result<?= htmlspecialchars($agent['id']) ?>">click a button to display data from the agent.</pre>
<?php } ?>

View File

@ -1,7 +0,0 @@
<?php if (isset($error)) { ?>
<div class="error"><?= $error ?></div>
<?php } ?>
<?php if (isset($notice)) { ?>
<div class="notice"><?= $notice ?></div>
<?php } ?>

View File

@ -1,29 +0,0 @@
<!-- Results filter -->
<div class="card w-auto bg-light border-light card-body text-right" style="text-align: right;">
<form method="POST" id="filter_form" class="filter-results" action="?platform=<?= htmlspecialchars($platform_id) ?>&page=<?= htmlspecialchars($page) ?>">
<label for="from_time">from</label>
<input type="date" id="from_time" name="from_time"<?php if (isset($_REQUEST['from_time'])) echo " value=\"" . htmlspecialchars($from_time) . "\"" ?> />
<label for="until_time">until</label>
<input type="date" id="until_time" name="until_time"<?php if (isset($_REQUEST['until_time'])) echo " value=\"" . htmlspecialchars($until_time) . "\"" ?> />
<input type="text" name="id" placeholder="ID"<?php if (isset($_REQUEST['id'])) echo " value=\"" . htmlspecialchars($_REQUEST['id']) . "\"" ?> />
<input type="text" name="name" placeholder="name"<?php if (isset($_REQUEST['name'])) echo " value=\"" . htmlspecialchars($_REQUEST['name']) . "\"" ?> />
<?php if ($page == 'participants') { ?>
<input type="text" name="ip" placeholder="ip address"<?php if (isset($_REQUEST['ip'])) echo " value=\"" . htmlspecialchars($_REQUEST['ip']) . "\"" ?> maxlength="15" size="15" />
<?php } ?>
<input type="button" onclick="clearFilter()" value="clear" />
<input type="submit" value="search" />
</form>
<script>
function clearFilter() {
document.getElementById("filter_form").reset();
const filterFields = document.querySelectorAll("#filter_form input");
filterFields.forEach(input => {
if (input.type === 'text' ||input.type === 'date') {
input.value = '';
}
});
}
</script>
</div>
<!-- /Results filter -->

View File

@ -1,72 +0,0 @@
<!-- widget "agents" -->
<div class="card text-center w-50 mx-auto">
<p class="h4 card-header">Add new Jilo Agent to Jitsi platform "<strong><?= htmlspecialchars($platformDetails[0]['name']) ?></strong>"</p>
<div class="card-body">
<!--p class="card-text">add new agent:</p-->
<form method="POST" action="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=config">
<div class="row mb-3">
<div class="col-md-4 text-end">
<label for="type" class="form-label">type</label>
<span class="text-danger" style="margin-right: -12px;">*</span>
</div>
<div class="col-md-8">
<select class="form-control" type="text" name="type" id="agent_type_id" required>
<option></option>
<?php foreach ($jilo_agent_types as $agent_type) { ?>
<option value="<?= htmlspecialchars($agent_type['id']) ?>"<?php
if (in_array($agent_type['id'], $jilo_agent_types_in_platform)) {
echo 'disabled="disabled"';
} ?>>
<?= htmlspecialchars($agent_type['description']) ?>
</option>
<?php } ?>
</select>
<p class="text-start"><small>type of agent (meet, jvb, jibri, etc.)<br />if a type has already been aded, it's disabled here</small></p>
</div>
</div>
<div class="row mb-3">
<div class="col-md-4 text-end">
<label for="url" class="form-label">URL</label>
<span class="text-danger" style="margin-right: -12px;">*</span>
</div>
<div class="col-md-8">
<input class="form-control" type="text" name="url" value="https://" required />
<p class="text-start"><small>URL of the Jilo Agent API (https://example.com:8081)</small></p>
</div>
</div>
<div class="row mb-3">
<div class="col-md-4 text-end">
<label for="secret_key" class="form-label">secret key</label>
<span class="text-danger" style="margin-right: -12px;">*</span>
</div>
<div class="col-md-8">
<input class="form-control" type="text" name="secret_key" value="" required />
<p class="text-start"><small>secret key for generating the access JWT token</small></p>
</div>
</div>
<div class="row mb-3">
<div class="col-md-4 text-end">
<label for="check_period" class="form-label">check period</label>
<span class="text-danger" style="margin-right: -12px;">*</span>
</div>
<div class="col-md-8">
<input class="form-control" type="text" name="check_period" value="0" required />
<p class="text-start"><small>period in minutes for the automatic agent check (0 disables it)</small></p>
</div>
</div>
<input type="hidden" name="new" value="true" />
<input type="hidden" name="item" value="agent" />
<br />
<a class="btn btn-secondary" href="<?= htmlspecialchars($app_root) ?>?page=config" />Cancel</a>
<input type="submit" class="btn btn-primary" value="Save" />
</form>
</div>
</div>
<!-- /widget "agents" -->

View File

@ -1,50 +0,0 @@
<!-- widget "config" -->
<div class="card text-center w-50 mx-auto">
<p class="h4 card-header">Add new Jitsi platform</p>
<div class="card-body">
<!--p class="card-text">add new platform:</p-->
<form method="POST" action="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=config">
<div class="row mb-3">
<div class="col-md-4 text-end">
<label for="name" class="form-label">name</label>
<span class="text-danger" style="margin-right: -12px;">*</span>
</div>
<div class="col-md-8">
<input class="form-control" type="text" name="name" value="" required autofocus />
<p class="text-start"><small>descriptive name for the platform</small></p>
</div>
</div>
<div class="row mb-3">
<div class="col-md-4 text-end">
<label for="jitsi_url" class="form-label">Jitsi URL</label>
<span class="text-danger" style="margin-right: -12px;">*</span>
</div>
<div class="col-md-8">
<input class="form-control" type="text" name="jitsi_url" value="https://" required />
<p class="text-start"><small>URL of the Jitsi Meet (used for checks and for loading config.js)</small></p>
</div>
</div>
<div class="row mb-3">
<div class="col-md-4 text-end">
<label for="jilo_database" class="form-label">jilo_database</label>
<span class="text-danger" style="margin-right: -12px;">*</span>
</div>
<div class="col-md-8">
<input class="form-control" type="text" name="jilo_database" value="" required />
<p class="text-start"><small>path to the database file (relative to the app root)</small></p>
</div>
</div>
<input type="hidden" name="new" value="true" />
<br />
<a class="btn btn-secondary" href="<?= htmlspecialchars($app_root) ?>?page=config" />Cancel</a>
<input type="submit" class="btn btn-primary" value="Save" />
</form>
</div>
</div>
<!-- /widget "config" -->

View File

@ -1,32 +0,0 @@
<!-- widget "agents" -->
<div class="card text-center w-50 mx-auto">
<p class="h4 card-header">Jilo Agent configuration for Jitsi platform <strong>"<?= htmlspecialchars($platformDetails[0]['name']) ?>"</strong></p>
<div class="card-body">
<p class="card-text">delete an agent:</p>
<form method="POST" action="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=config">
<?php
foreach ($agentDetails[0] as $key => $value) {
// if ($key === 'id') continue;
?>
<div class="row mb-3">
<div class="col-md-4 text-end">
<label for="<?= htmlspecialchars($key) ?>" class="form-label"><?= htmlspecialchars($key) ?>:</label>
</div>
<div class="col-md-8">
<div class="text-start"><?= htmlspecialchars($value ?? '') ?></div>
<input type="hidden" name="<?= htmlspecialchars($key) ?>" value="<?= htmlspecialchars($value ?? '') ?>" />
</div>
</div>
<?php } ?>
<br />
<input type="hidden" name="agent" value="<?= htmlspecialchars($agentDetails[0]['id']) ?>" />
<input type="hidden" name="delete" value="true" />
<p class="h5 text-danger">Are you sure you want to delete this agent?</p>
<br />
<a class="btn btn-secondary" href="<?= htmlspecialchars($app_root) ?>?page=config#platform<?= htmlspecialchars($platform_id) ?>agent<?= htmlspecialchars($agentDetails[0]['id']) ?>" />Cancel</a>
<input type="submit" class="btn btn-danger" value="Delete" />
</form>
</div>
</div>
<!-- /widget "agents" -->

View File

@ -1,32 +0,0 @@
<!-- widget "config" -->
<div class="card text-center w-50 mx-auto">
<p class="h4 card-header">Jilo web configuration for Jitsi platform <strong>"<?= htmlspecialchars($platformDetails[0]['name']) ?>"</strong></p>
<div class="card-body">
<p class="card-text">delete a platform:</p>
<form method="POST" action="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=config">
<?php
foreach ($platformDetails[0] as $key => $value) {
if ($key === 'id') continue;
?>
<div class="row mb-3">
<div class="col-md-4 text-end">
<label for="<?= htmlspecialchars($key) ?>" class="form-label"><?= htmlspecialchars($key) ?>:</label>
</div>
<div class="col-md-8">
<div class="text-start"><?= htmlspecialchars($value) ?? '' ?></div>
<input type="hidden" name="<?= htmlspecialchars($key) ?>" value="<?= htmlspecialchars($value ?? '')?>" />
</div>
</div>
<?php } ?>
<br />
<input type="hidden" name="platform" value="<?= htmlspecialchars($platform_id) ?>" />
<input type="hidden" name="delete" value="true" />
<p class="h5 text-danger">Are you sure you want to delete this platform?</p>
<br />
<a class="btn btn-secondary" href="<?= htmlspecialchars($app_root) ?>?page=config#platform<?= htmlspecialchars($platform_id) ?>" />Cancel</a>
<input type="submit" class="btn btn-danger" value="Delete" />
</form>
</div>
</div>
<!-- /widget "config" -->

View File

@ -1,68 +0,0 @@
<!-- agents -->
<div class="card text-center w-50 mx-auto">
<p class="h4 card-header">Jilo Agent configuration for Jitsi platform <strong>"<?= htmlspecialchars($platformDetails[0]['name']) ?>"</strong></p>
<div class="card-body">
<p class="card-text">edit the agent details:</p>
<form method="POST" action="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=config">
<div class="row mb-3">
<div class="col-md-4 text-end">
<label for="type_id" class="form-label">type</label>
<span class="text-danger" style="margin-right: -12px;">*</span>
</div>
<div class="col-md-8">
<select class="form-control" type="text" name="type" id="agent_type_id" required>
<option></option>
<?php foreach ($jilo_agent_types as $agent_type) { ?>
<option value="<?= htmlspecialchars($agent_type['id']) ?>" <?php if ($agentDetails[0]['agent_type_id'] === $agent_type['id']) echo 'selected'; ?>>
<?= htmlspecialchars($agent_type['description']) ?>
</option>
<?php } ?>
</select>
<p class="text-start"><small>type of agent (meet, jvb, jibri, all)</small></p>
</div>
</div>
<div class="row mb-3">
<div class="col-md-4 text-end">
<label for="url" class="form-label">URL</label>
<span class="text-danger" style="margin-right: -12px;">*</span>
</div>
<div class="col-md-8">
<input class="form-control" type="text" name="url" value="<?= htmlspecialchars($agentDetails[0]['url']) ?>" required />
<p class="text-start"><small>URL of the Jilo Agent API (https://example.com:8081)</small></p>
</div>
</div>
<div class="row mb-3">
<div class="col-md-4 text-end">
<label for="secret_key" class="form-label">secret key</label>
<span class="text-danger" style="margin-right: -12px;">*</span>
</div>
<div class="col-md-8">
<input class="form-control" type="text" name="secret_key" value="<?= htmlspecialchars($agentDetails[0]['secret_key']) ?>" required />
<p class="text-start"><small>secret key for generating the access JWT token</small></p>
</div>
</div>
<div class="row mb-3">
<div class="col-md-4 text-end">
<label for="check_period" class="form-label">check period</label>
<span class="text-danger" style="margin-right: -12px;">*</span>
</div>
<div class="col-md-8">
<input class="form-control" type="text" name="check_period" value="<?= htmlspecialchars($agentDetails[0]['check_period']) ?>" required />
<p class="text-start"><small>period in minutes for the automatic agent check (0 disables it)</small></p>
</div>
</div>
<br />
<input type="hidden" name="agent" value="<?= htmlspecialchars($agentDetails[0]['id']) ?>" />
<a class="btn btn-secondary" href="<?= htmlspecialchars($app_root) ?>?page=config#platform<?= htmlspecialchars($platform_id) ?>agent<?= htmlspecialchars($agentDetails[0]['id']) ?>" />Cancel</a>
<input type="submit" class="btn btn-primary" value="Save" />
</form>
</div>
</div>
<!-- /agents -->

View File

@ -1,36 +0,0 @@
<!-- widget "config" -->
<div class="card text-center w-50 mx-auto">
<p class="h4 card-header">Jilo web configuration for Jitsi platform <strong>"<?= htmlspecialchars($platformDetails[0]['name']) ?>"</strong></p>
<div class="card-body">
<p class="card-text">edit the platform details:</p>
<form method="POST" action="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=config">
<?php
foreach ($platformDetails[0] as $key => $value) {
if ($key === 'id') continue;
?>
<div class="row mb-3">
<div class="col-md-4 text-end">
<label for="<?= htmlspecialchars($config_item) ?>" class="form-label"><?= htmlspecialchars($key) ?></label>
<span class="text-danger" style="margin-right: -12px;">*</span>
</div>
<div class="col-md-8">
<input class="form-control" type="text" name="<?= htmlspecialchars($key) ?>" value="<?= htmlspecialchars($value ?? '') ?>" required autofocus />
<?php if ($key === 'name') { ?>
<p class="text-start"><small>descriptive name for the platform</small></p>
<?php } elseif ($key === 'jitsi_url') { ?>
<p class="text-start"><small>URL of the Jitsi Meet (used for checks and for loading config.js)</small></p>
<?php } elseif ($key === 'jilo_database') { ?>
<p class="text-start"><small>path to the database file (relative to the app root)</small></p>
<?php } ?>
</div>
</div>
<?php } ?>
<br />
<input type="hidden" name="platform" value="<?= htmlspecialchars($platform_id) ?>" />
<a class="btn btn-secondary" href="<?= htmlspecialchars($app_root) ?>?page=config#platform<?= htmlspecialchars($platform_id) ?>" />Cancel</a>
<input type="submit" class="btn btn-primary" value="Save" />
</form>
</div>
</div>
<!-- /widget "config" -->

View File

@ -1,22 +0,0 @@
<!-- widget "config" -->
<div class="card text-center w-75 mx-lef">
<p class="h4 card-header">Configuration of the Jitsi platform <strong><?= htmlspecialchars($platformDetails[0]['name']) ?></strong></p>
<div class="card-body">
<p class="card-text">
<span class="m-3">URL: <?= htmlspecialchars($platformDetails[0]['jitsi_url']) ?></span>
<span class="m-3">FILE: config.js</span>
<?php if ($mode === 'raw') { ?>
<span class="m-3"><a class="btn btn-light" href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=config&item=configjs">view only active lines</a></span>
<?php } else { ?>
<span class="m-3"><a class="btn btn-light" href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=config&item=configjs&mode=raw">view raw file contents</a></span>
<?php } ?>
</p>
<pre class="results">
<?php
echo htmlspecialchars($platformConfigjs);
?>
</pre>
</div>
</div>
<!-- /widget "config" -->

View File

@ -1,22 +0,0 @@
<!-- widget "config" -->
<div class="card text-center w-75 mx-lef">
<p class="h4 card-header">Configuration of the Jitsi platform <strong><?= htmlspecialchars($platformDetails[0]['name']) ?></strong></p>
<div class="card-body">
<p class="card-text">
<span class="m-3">URL: <?= htmlspecialchars($platformDetails[0]['jitsi_url']) ?></span>
<span class="m-3">FILE: interface_config.js</span>
<?php if ($mode === 'raw') { ?>
<span class="m-3"><a class="btn btn-light" href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=config&item=interfaceconfigjs">view only active lines</a></span>
<?php } else { ?>
<span class="m-3"><a class="btn btn-light" href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=config&item=interfaceconfigjs&mode=raw">view raw file contents</a></span>
<?php } ?>
</p>
<pre class="results">
<?php
echo htmlspecialchars($platformInterfaceConfigjs);
?>
</pre>
</div>
</div>
<!-- /widget "config" -->

View File

@ -1,127 +0,0 @@
<!-- widget "config" -->
<div class="card text-center w-75 mx-lef">
<p class="h4 card-header">Jilo web configuration</p>
<div class="card-body">
<p class="card-text">main variables</p>
<?php
include '../app/helpers/render.php';
renderConfig($config, '0');
echo "\n";
?>
<hr />
<p class="card-text">platforms configuration &nbsp;<a class="btn btn-secondary" style="padding: 0px;" href="<?= htmlspecialchars($app_root) ?>?page=config&item=platform&action=add">add new</a></p>
<?php foreach ($platformsAll as $platform_array) {
$agents = $agentObject->getAgentDetails($platform_array['id']);
?>
<a name="platform<?= htmlspecialchars($platform_array['id']) ?>"></a>
<div class="row mb-3" style="padding-left: 0px;">
<div class="border bg-light" style="padding-left: 50px; padding-bottom: 0px; padding-top: 0px;">
<a style="text-decoration: none;" data-toggle="collapse" href="#collapsePlatform<?= htmlspecialchars($platform_array['id']) ?>" role="button" aria-expanded="true" aria-controls="collapsePlatform<?= htmlspecialchars($platform_array['id']) ?>">
<div class="border bg-white text-start mb-3 rounded mt-3" data-toggle="tooltip" data-placement="bottom" title="configuration for platform <?= htmlspecialchars($platform_array['id']) ?>">
<i class="fas fa-wrench"></i>
<small>platform <?= htmlspecialchars($platform_array['id']) ?> (<?= htmlspecialchars($platform_array['name']) ?>)</small>
</div>
</a>
<div class="collapse show" id="collapsePlatform<?= htmlspecialchars($platform_array['id']) ?>">
<div class="row mb-1" style="padding-left: 0px;">
<div class="col-md-8 text-start">
<div class="row mb-1">
<div class="col-md-8 text-start">
<a class="btn btn-secondary" style="padding: 2px;" href="<?= htmlspecialchars($app_root) ?>?page=config&platform=<?= htmlspecialchars($platform_array['id']) ?>&action=edit">edit platform</a>
<?php if (count($platformsAll) <= 1) { ?>
<span class="btn btn-light" style="padding: 2px;" href="#" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="can't delete the last platform">delete platform</span>
<?php } else { ?>
<a class="btn btn-danger" style="padding: 2px;" href="<?= htmlspecialchars($app_root) ?>?page=config&platform=<?= htmlspecialchars($platform_array['id']) ?>&action=delete">delete platform</a>
<?php } ?>
</div>
</div>
</div>
<div style="padding-left: 100px; padding-bottom: 20px;">
<?php foreach ($platform_array as $key => $value) {
if ($key === 'id') continue;
?>
<div class="row mb-1" style="padding-left: 100px;">
<div class="col-md-4 text-end">
<?= htmlspecialchars($key) ?>:
</div>
<div class="border col-md-8 text-start">
<?= htmlspecialchars($value) ?>
</div>
</div>
<?php } ?>
</div>
<hr />
<p class="card-text">jilo agents on platform <?= htmlspecialchars($platform_array['id']) ?> (<?= htmlspecialchars($platform_array['name']) ?>)
<br />
total <?= htmlspecialchars(count($agents)) ?> <?= htmlspecialchars(count($agents)) === 1 ? 'jilo agent' : 'jilo agents' ?>&nbsp;
<a class="btn btn-secondary" style="padding: 0px;" href="<?= htmlspecialchars($app_root) ?>?page=config&platform=<?= htmlspecialchars($platform_array['id']) ?>&action=add-agent">
add new
</a>
</p>
<?php foreach ($agents as $agent_array) { ?>
<a name="platform<?= htmlspecialchars($platform_array['id']) ?>agent<?= htmlspecialchars($agent_array['id']) ?>"></a>
<div class="row mb-3" style="padding-left: 0px;">
<div class="border rounded bg-light" style="padding-left: 50px; padding-bottom: 20px; padding-top: 20px;">
<div class="row mb-1" style="padding-left: 0px;">
<div class="col-md-4 text-end">
agent id <?= htmlspecialchars($agent_array['id']) ?>:
</div>
<div class="col-md-8 text-start">
<a class="btn btn-secondary" style="padding: 2px;" href="<?= htmlspecialchars($app_root) ?>?page=config&platform=<?= htmlspecialchars($agent_array['platform_id']) ?>&agent=<?= htmlspecialchars($agent_array['id']) ?>&action=edit">edit agent</a>
<a class="btn btn-danger" style="padding: 2px;" href="<?= htmlspecialchars($app_root) ?>?page=config&platform=<?= htmlspecialchars($agent_array['platform_id']) ?>&agent=<?= htmlspecialchars($agent_array['id']) ?>&action=delete">delete agent</a>
</div>
<div style="padding-left: 100px; padding-bottom: 20px;">
<div class="row mb-1" style="padding-left: 100px;">
<div class="col-md-4 text-end">
agent type:
</div>
<div class="border col-md-8 text-start">
<?= htmlspecialchars($agent_array['agent_description']) ?>
</div>
</div>
<div class="row mb-1" style="padding-left: 100px;">
<div class="col-md-4 text-end">
endpoint:
</div>
<div class="border col-md-8 text-start">
<?= htmlspecialchars($agent_array['url'].$agent_array['agent_endpoint']) ?>
</div>
</div>
<?php if (isset($agent_array['check_period']) && $agent_array['check_period'] !== 0) { ?>
<div class="row mb-1" style="padding-left: 100px;">
<div class="col-md-4 text-end">
check period:
</div>
<div class="border col-md-8 text-start">
<?= htmlspecialchars($agent_array['check_period']) ?> <?= ($agent_array['check_period'] == 1 ? 'minute' : 'minutes') ?>
</div>
</div>
<?php } ?>
</div>
</div>
</div>
</div>
<?php } ?>
</div>
</div>
</div>
</div>
<?php } ?>
</div>
</div>
<!-- /widget "config" -->

View File

@ -1,8 +0,0 @@
<div class="text-center">
<div class="mt-3 h5">The page is not found.</div>
<div>
<small>go to <a href="<?= htmlspecialchars($app_root) ?>">front page</a> or to <a href="<?= htmlspecialchars($app_root) ?>?page=profile">your profile</a></small>
</div>
</div>

View File

@ -1,8 +0,0 @@
<div class="text-center">
<div class="mt-3 h5">You have no access to this page.</div>
<div>
<small>go to <a href="<?= htmlspecialchars($app_root) ?>">front page</a> or to <a href="<?= htmlspecialchars($app_root) ?>?page=profile">your profile</a></small>
</div>
</div>

View File

@ -1,76 +0,0 @@
<div class="row">
<div class="card w-auto bg-light border-light card-body" style="flex-direction: row;"><?= $widget['title'] ?></div>
<!-- Results filter -->
<div class="card w-auto bg-light border-light card-body text-right" style="text-align: right;">
<form method="POST" id="filter_form" class="filter-results" action="?platform=<?= htmlspecialchars($platform_id) ?>&page=<?= htmlspecialchars($page) ?>">
<label for="from_time">from</label>
<input type="date" id="from_time" name="from_time"<?php if (isset($_REQUEST['from_time'])) echo " value=\"" . htmlspecialchars($from_time) . "\"" ?> />
<label for="until_time">until</label>
<input type="date" id="until_time" name="until_time"<?php if (isset($_REQUEST['until_time'])) echo " value=\"" . htmlspecialchars($until_time) . "\"" ?> />
<input type="text" name="id" placeholder="component ID"<?php if (isset($_REQUEST['id'])) echo " value=\"" . htmlspecialchars($_REQUEST['id']) . "\"" ?> />
<input type="text" name="name" placeholder="component name"<?php if (isset($_REQUEST['name'])) echo " value=\"" . htmlspecialchars($_REQUEST['name']) . "\"" ?> />
<input type="text" name="event" placeholder="event name"<?php if (isset($_REQUEST['event'])) echo " value=\"" . htmlspecialchars($_REQUEST['event']) . "\"" ?> />
<input type="button" onclick="clearFilter()" value="clear" />
<input type="submit" value="search" />
</form>
<script>
function clearFilter() {
document.getElementById("filter_form").reset();
const filterFields = document.querySelectorAll("#filter_form input");
filterFields.forEach(input => {
if (input.type === 'text' ||input.type === 'date') {
input.value = '';
}
});
}
</script>
</div>
<!-- /Results filter -->
</div>
<!-- widget "<?= htmlspecialchars($widget['name']) ?>" -->
<div class="collapse show" id="collapse<?= htmlspecialchars($widget['name']) ?>">
<?php if ($time_range_specified) { ?>
<p class="m-3">time period: <strong><?= htmlspecialchars($from_time) ?> - <?= htmlspecialchars($until_time) ?></strong></p>
<?php } ?>
<div class="mb-5">
<?php if ($widget['full'] === true) { ?>
<table class="table table-results table-striped table-hover table-bordered">
<thead class="thead-dark">
<tr>
<?php foreach ($widget['table_headers'] as $header) { ?>
<th scope="col"><?= htmlspecialchars($header) ?></th>
<?php } ?>
</tr>
</thead>
<tbody>
<?php foreach ($widget['table_records'] as $row) { ?>
<tr>
<?php foreach ($row as $key => $column) { ?>
<?php if ($key === 'component ID') { ?>
<td><a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=components&id=<?= htmlspecialchars($column ?? '') ?>"><?= htmlspecialchars($column ?? '') ?></a></td>
<?php } elseif ($key === 'component') { ?>
<td><a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=components&name=<?= htmlspecialchars($column ?? '') ?>"><?= htmlspecialchars($column ?? '') ?></a></td>
<?php } else { ?>
<td><?= htmlspecialchars($column ?? '') ?></td>
<?php }
} ?>
</tr>
<?php } ?>
</tbody>
</table>
<?php
if ($widget['pagination'] && $item_count > $items_per_page) {
$url = "$app_root?platform=$platform_id&page=$page";
include '../app/helpers/pagination.php';
}
?>
<?php } else { ?>
<p class="m-3">No matching records found.</p>
<?php } ?>
</div>
</div>
<!-- /widget "<?= htmlspecialchars($widget['name']) ?>" -->

View File

@ -1,94 +0,0 @@
<div class="row">
<div class="card w-auto bg-light border-light card-body" style="flex-direction: row;"><?= $widget['title'] ?></div>
<!-- Results filter -->
<div class="card w-auto bg-light border-light card-body text-right" style="text-align: right;">
<form method="POST" id="filter_form" class="filter-results" action="?platform=<?= htmlspecialchars($platform_id) ?>&page=<?= htmlspecialchars($page) ?>">
<label for="from_time">from</label>
<input type="date" id="from_time" name="from_time"<?php if (isset($_REQUEST['from_time'])) echo " value=\"" . htmlspecialchars($from_time) . "\"" ?> />
<label for="until_time">until</label>
<input type="date" id="until_time" name="until_time"<?php if (isset($_REQUEST['until_time'])) echo " value=\"" . htmlspecialchars($until_time) . "\"" ?> />
<input type="text" name="id" placeholder="conference ID"<?php if (isset($_REQUEST['id'])) echo " value=\"" . htmlspecialchars($_REQUEST['id']) . "\"" ?> />
<input type="text" name="name" placeholder="conference name"<?php if (isset($_REQUEST['name'])) echo " value=\"" . htmlspecialchars($_REQUEST['name']) . "\"" ?> />
<input type="button" onclick="clearFilter()" value="clear" />
<input type="submit" value="search" />
</form>
<script>
function clearFilter() {
document.getElementById("filter_form").reset();
const filterFields = document.querySelectorAll("#filter_form input");
filterFields.forEach(input => {
if (input.type === 'text' ||input.type === 'date') {
input.value = '';
}
});
}
</script>
</div>
<!-- /Results filter -->
</div>
<!-- widget "<?= htmlspecialchars($widget['name']) ?>" -->
<div class="collapse show" id="collapse<?= htmlspecialchars($widget['name']) ?>">
<?php if ($time_range_specified) { ?>
<p class="m-3">time period: <strong><?= htmlspecialchars($from_time) ?> - <?= htmlspecialchars($until_time) ?></strong></p>
<?php } ?>
<div class="mb-5">
<?php if ($widget['full'] === true) { ?>
<table class="table table-results table-striped table-hover table-bordered">
<thead class="thead-dark">
<tr>
<?php foreach ($widget['table_headers'] as $header) { ?>
<th scope="col"><?= htmlspecialchars($header) ?></th>
<?php } ?>
</tr>
</thead>
<tbody>
<?php foreach ($widget['table_records'] as $row) { ?>
<tr>
<?php $stats_id = false;
$participant_ip = false;
if (isset($row['event']) && $row['event'] === 'stats_id') $stats_id = true;
if (isset($row['event']) && $row['event'] === 'pair selected') $participant_ip = true;
foreach ($row as $key => $column) {
if ($key === 'conference ID' && isset($conferenceId) && $conferenceId === $column) { ?>
<td><strong><?= htmlspecialchars($column ?? '') ?></strong></td>
<?php } elseif ($key === 'conference ID') { ?>
<td><a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=conferences&id=<?= htmlspecialchars($column ?? '') ?>"><?= htmlspecialchars($column ?? '') ?></a></td>
<?php } elseif ($key === 'conference name' && isset($conferenceName) && $conferenceName === $column) { ?>
<td><strong><?= htmlspecialchars($column ?? '') ?></strong></td>
<?php } elseif ($key === 'conference name') { ?>
<td><a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=conferences&name=<?= htmlspecialchars($column ?? '') ?>"><?= htmlspecialchars($column ?? '') ?></a></td>
<?php } elseif ($key === 'participant ID') { ?>
<td><a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=participants&id=<?= htmlspecialchars($column ?? '') ?>"><?= htmlspecialchars($column ?? '') ?></a></td>
<?php } elseif ($stats_id && $key === 'parameter') { ?>
<td><a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=participants&name=<?= htmlspecialchars($column ?? '') ?>"><?= htmlspecialchars($column ?? '') ?></a></td>
<?php } elseif ($participant_ip && $key === 'parameter') { ?>
<td><a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=participants&ip=<?= htmlspecialchars($column ?? '') ?>"><?= htmlspecialchars($column ?? '') ?></a></td>
<?php
// in general listings we don't show seconds and miliseconds
} elseif ($key === 'start' || $key === 'end') { ?>
<td><?= htmlspecialchars(substr($column ?? '', 0, -7)) ?></td>
<?php } else { ?>
<td><?= htmlspecialchars($column ?? '') ?></td>
<?php }
} ?>
</tr>
<?php } ?>
</tbody>
</table>
<?php
if ($widget['pagination'] && $item_count > $items_per_page) {
$url = "$app_root?platform=$platform_id&page=$page";
include '../app/helpers/pagination.php';
}
?>
<?php } else { ?>
<p class="m-3">No matching records found.</p>
<?php } ?>
</div>
</div>
<!-- /widget "<?= htmlspecialchars($widget['name']) ?>" -->

View File

@ -1,20 +0,0 @@
<!-- login form -->
<div class="card text-center w-50 mx-auto">
<h2 class="card-header">Login</h2>
<div class="card-body">
<p class="card-text"><strong>Welcome to JILO!</strong><br />Please enter login credentials:</p>
<form method="POST" action="<?= htmlspecialchars($app_root) ?>?page=login">
<input type="text" name="username" placeholder="Username" required autofocus />
<br />
<input type="password" name="password" placeholder="Password" required />
<br />
<label for="remember_me">
<input type="checkbox" id="remember_me" name="remember_me" />
remember me
</label>
<br />&nbsp;<br />
<input type="submit" class="btn btn-primary" value="Login" />
</form>
</div>
</div>
<!-- /login form -->

View File

@ -1,15 +0,0 @@
<!-- registration form -->
<div class="card text-center w-50 mx-auto">
<h2 class="card-header">Register</h2>
<div class="card-body">
<p class="card-text">Enter credentials for registration:</p>
<form method="POST" action="<?= htmlspecialchars($app_root) ?>?page=register">
<input type="text" name="username" placeholder="Username" required autofocus />
<br />
<input type="password" name="password" placeholder="Password" required />
<br />&nbsp;<br />
<input type="submit" class="btn btn-primary" value="Register" />
</form>
</div>
</div>
<!-- /registration form -->

View File

@ -1,140 +0,0 @@
<hr /><p class="m-3">NB: This functionality is still under development. The data is just an example.</p><hr /><!-- FIXME remove when implemented -->
<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>

View File

@ -1,53 +0,0 @@
<!-- help -->
<div class="card text-center w-100 mx-auto">
<p class="h4 card-header">Jilo Help</p>
<div class="card-body">
<div style="text-align: left; font-family: monospace; font-size: 0.75em; white-space: pre-wrap;">
<a href="https://lindeas.com/jilo">Jilo</a> is a software tools suite developed by <a href="https://lindeas.com">Lindeas Ltd.</a> designed to help in maintaining a Jitsi Meet platform.
It consists of several parts meant to run together, although some of them can be used separately.
<hr /><strong>"JILO"</strong>
This is the command-line tool for extracting information about important events from the Jitsi Meet log files, storing them in a database and searching through that database.
Jilo is written in Bash and has very minimal external dependencies. That means that you can run it on almost any Linux system with jitsi log files.
It can either:
- show the data directly in the terminal,
- provide it to an instance of the web application "Jilo Web" for displaying statistics (the Jilo Web server needs to have access to the sqlite database file from Jilo),
- or send the data output to a "Jilo Agent" that can then allow a remote Jilo Web access it.
This way Jilo is always available on each host in the Jitsi Meet platform for quick command-line search, while also providing data for the statistics on a central Jilo Web server, without any need to put additional software on each server.
<hr /><strong>"Jilo Agent"</strong>
The Jilo Agent is a small program, written in Go. It runs on remote servers in the Jitsi Meet platform, and provides info about the operation of the different components of the platform.
It listens for connections from the central Jilo Web app on a special port and the connection is secured with JWT tokens authentication. It can be configured to use HTTPS. In a firewall you only need to allow the agent's port for incoming connections from the Jilo Web server.
All information about the different services (jvb, jicofo, jigasi, nginx, prosody) is transmitted over that single port. This, plus the authentication and the fact that Jilo Agent doesn't need any additional external programs or libraries to operate, make it very easy for deploying and automating the deployment in a large Jitsi Meet setup.
<hr /><strong>"Jilo Web"</strong>
Jilo Web is the web app that combines all the information received from Jilo and Jilo Agents and shows statistics and graphs of the usage, the events and the issues.
It's a multi-user web tool with user levels and access rights integrated into it, making it suitable for the different levels in an enterprise.
The current website you are looking at is running a Jilo Web instance.
<hr /><strong>"Jilo Server"</strong>
Jilo Server is a server component written in Go, meant to work alongside Jilo Web. It is responsible for all automated tasks - health checks, periodic retrieval of data from the remote Jilo Agents, etc.
It generally works on the same machine as the web interface Jilo Web and shares its database, although if needed it could be deployed on a separate machine.
Jilo Web checks for the Jilo Server availability and displays a warning if there is a problem with the server.
</div>
</div>
</div>
<!-- /help -->

View File

@ -1,45 +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="collapse show" id="collapse<?= htmlspecialchars($widget['name']) ?>">
<div class="mb-5">
<hr /><p class="m-3">NB: This functionality is still under development. The data is just an example.</p><hr /><!-- FIXME remove when implemented -->
<?php if ($widget['full'] === true) { ?>
<table class="table table-results table-striped table-hover table-bordered">
<thead class="thead-dark">
<tr>
<th scope="col"></th>
<?php foreach ($widget['records'] as $record) { ?>
<th scope="col"><?= htmlspecialchars($record['table_headers']) ?></th>
<?php } ?>
</tr>
</thead>
<tbody>
<tr>
<td>conferences</td>
<?php foreach ($widget['records'] as $record) { ?>
<td><?php if (!empty($record['conferences'])) { ?>
<a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=conferences&from_time=<?= htmlspecialchars($record['from_time']) ?>&until_time=<?= htmlspecialchars($record['until_time']) ?>"><?= htmlspecialchars($record['conferences']) ?></a> <?php } else { ?>
0<?php } ?>
</td>
<?php } ?>
</tr>
<tr>
<td>participants</td>
<?php foreach ($widget['records'] as $record) { ?>
<td><?php if (!empty($record['participants'])) { ?>
<a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=participants&from_time=<?= htmlspecialchars($record['from_time']) ?>&until_time=<?= htmlspecialchars($record['until_time']) ?>"><?= htmlspecialchars($record['participants']) ?></a> <?php } else { ?>
0<?php } ?>
</td>
<?php } ?>
</tr>
</tbody>
</table>
<?php } else { ?>
<p class="m-3">No records found.</p>
<?php } ?>
</div>
</div>
<!-- /widget "<?= htmlspecialchars($widget['name']) ?>" -->

View File

@ -1,26 +0,0 @@
<!-- Logs filter -->
<div class="card w-auto bg-light border-light card-body text-right" style="text-align: right;">
<form method="POST" id="filter_form" class="filter-results" action="?page=logs">
<label for="from_time">from</label>
<input type="date" id="from_time" name="from_time"<?php if (isset($_REQUEST['from_time'])) echo " value=\"" . htmlspecialchars($from_time) . "\"" ?> />
<label for="until_time">until</label>
<input type="date" id="until_time" name="until_time"<?php if (isset($_REQUEST['until_time'])) echo " value=\"" . htmlspecialchars($until_time) . "\"" ?> />
<input type="text" name="id" placeholder="user ID"<?php if (isset($_REQUEST['id'])) echo " value=\"" . htmlspecialchars($_REQUEST['id']) . "\"" ?> />
<input type="text" name="message" placeholder="message"<?php if (isset($_REQUEST['message'])) echo " value=\"" . htmlspecialchars($_REQUEST['message']) . "\"" ?> />
<input type="button" onclick="clearFilter()" value="clear" />
<input type="submit" value="search" />
</form>
<script>
function clearFilter() {
document.getElementById("filter_form").reset();
const filterFields = document.querySelectorAll("#filter_form input");
filterFields.forEach(input => {
if (input.type === 'text' ||input.type === 'date') {
input.value = '';
}
});
}
</script>
</div>
<!-- /Logs filter -->

View File

@ -1,57 +0,0 @@
<div class="row">
<?php if ($widget['collapsible'] === true) { ?>
<a style="text-decoration: none;" data-toggle="collapse" href="#collapse<?= htmlspecialchars($widget['name']) ?>" role="button" aria-expanded="true" aria-controls="collapse<?= htmlspecialchars($widget['name']) ?>">
<div class="card w-auto bg-light card-body" style="flex-direction: row;"><?= htmlspecialchars($widget['title']) ?></div>
<?php } else { ?>
<div class="card w-auto bg-light border-light card-body" style="flex-direction: row;"><?= htmlspecialchars($widget['title']) ?></div>
<?php } ?>
<?php if ($widget['filter'] === true) {
include '../app/templates/logs-filter.php'; } ?>
<?php if ($widget['collapsible'] === true) { ?>
</a>
<?php } ?>
</div>
<!-- widget "<?= htmlspecialchars($widget['name']) ?>" -->
<div class="collapse show" id="collapse<?= htmlspecialchars($widget['name']) ?>">
<?php if ($time_range_specified) { ?>
<p class="m-3">time period: <strong><?= htmlspecialchars($from_time) ?> - <?= htmlspecialchars($until_time) ?></strong></p>
<?php } ?>
<div class="mb-5">
<?php if ($widget['full'] === true) { ?>
<table class="table table-results table-striped table-hover table-bordered">
<thead class="thead-dark">
<tr>
<?php foreach ($widget['table_headers'] as $header) { ?>
<th scope="col" class="th-<?= htmlspecialchars($header) ?>"><?= htmlspecialchars($header) ?></th>
<?php } ?>
</tr>
</thead>
<tbody>
<?php foreach ($widget['table_records'] as $row) { ?>
<tr>
<?php
foreach ($row as $key => $column) {
if ($key === 'user ID' && isset($user_id) && $user_id === $column) { ?>
<td><strong><?= htmlspecialchars($column ?? '') ?></strong></td>
<?php } else { ?>
<td><?= htmlspecialchars($column ?? '') ?></td>
<?php }
} ?>
</tr>
<?php } ?>
</tbody>
</table>
<?php
if ($widget['pagination'] && $item_count > $items_per_page) {
$url = "$app_root?platform=$platform_id&page=$page";
include '../app/helpers/pagination.php';
}
?>
<?php } else { ?>
<p class="m-3">No matching records found.</p>
<?php } ?>
</div>
</div>
<!-- /widget "<?= htmlspecialchars($widget['name']) ?>" -->

View File

@ -1,23 +0,0 @@
<?php if ($page !== 'login' && $page !== 'register') { ?>
<!-- /Main content -->
</div>
</div>
<?php } ?>
<!-- Footer -->
<div id="footer">Jilo Web <?= htmlspecialchars($config['version']) ?> &copy;2024 - web interface for <a href="https://lindeas.com/jilo">Jilo</a></div>
<!-- /Footer -->
</div>
<script src="static/sidebar.js"></script>
<script>
$(document).ready(function(){
$('[data-toggle="tooltip"]').tooltip();
});
</script>
</body>
</html>

View File

@ -1,39 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="<?= htmlspecialchars($app_root) ?>static/bootstrap/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="<?= htmlspecialchars($app_root) ?>static/css/main.css">
<?php if ($page === 'logs') { ?>
<link rel="stylesheet" type="text/css" href="<?= htmlspecialchars($app_root) ?>static/css/logs.css">
<?php } ?>
<?php if ($page === 'profile') { ?>
<link rel="stylesheet" type="text/css" href="<?= htmlspecialchars($app_root) ?>static/css/profile.css">
<?php } ?>
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.12.9/dist/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.0/css/all.css" integrity="sha384-lZN37f5QGtY3VHgisS14W3ExzMWZxybE1SJSEsQp9S+oqd12jhcu+A56Ebc1zFSJ" crossorigin="anonymous">
<script>
// restore sidebar state before the page is rendered
(function () {
var savedState = localStorage.getItem('sidebarState');
if (savedState === 'collapsed') {
document.documentElement.classList.add('sidebar-collapsed');
}
})();
</script>
<?php if ($page === 'agents') { ?>
<script src="<?= htmlspecialchars($app_root) ?>static/agents.js"></script>
<?php } ?>
<?php if ($page === 'graphs') { ?>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/moment@2.29.1"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-moment@1.0.0"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom@1.2.1/dist/chartjs-plugin-zoom.min.js"></script>
<?php } ?>
<title>Jilo Web</title>
<link rel="icon" type="image/x-icon" href="<?= htmlspecialchars($app_root) ?>static/favicon.ico">
</head>
<body>

View File

@ -1,46 +0,0 @@
<div class="container-fluid">
<!-- Menu -->
<div class="menu-container">
<ul class="menu-left">
<div class="container">
<div class="row">
<a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>" class="logo-link"><div class="col-4"><img class="logo" src="<?= htmlspecialchars($app_root) ?>static/jilo-logo.png" alt="JILO"/></div></a>
</div>
</div>
<li class="font-weight-light text-uppercase" style="font-size: 0.5em; color: whitesmoke; margin-right: 70px; align-content: center;">version&nbsp;<?= htmlspecialchars($config['version']) ?></li>
<?php if ( isset($_SESSION['username']) ) { ?>
<?php foreach ($platformsAll as $platform) {
$platform_switch_url = switchPlatform($platform['id']);
?>
<li style="margin-right: 3px;">
<?php if ((isset($_REQUEST['platform']) || empty($_SERVER['QUERY_STRING'])) && $platform['id'] == $platform_id) { ?>
<span style="background-color: #fff; border: 1px solid #111; color: #111; border-bottom-color: #fff; padding-bottom: 12px;">
<?= htmlspecialchars($platform['name']) ?>
</span>
<?php } else { ?>
<a href="<?= htmlspecialchars($platform_switch_url) ?>">
<?= htmlspecialchars($platform['name']) ?>
</a>
<?php } ?>
</li>
<?php } ?>
<?php } ?>
</ul>
<ul class="menu-right">
<?php if ( isset($_SESSION['username']) ) { ?>
<li><a href="<?= htmlspecialchars($app_root) ?>?page=profile"><?= htmlspecialchars($currentUser) ?></a></li>
<li><a href="<?= htmlspecialchars($app_root) ?>?page=logout">logout</a></li>
<?php } else { ?>
<li><a href="<?= htmlspecialchars($app_root) ?>?page=login">login</a></li>
<li><a href="<?= htmlspecialchars($app_root) ?>?page=register">register</a></li>
<?php } ?>
</ul>
</div>
<!-- /Menu -->

View File

@ -1,105 +0,0 @@
<div class="row">
<!-- Sidebar -->
<div class="col-md-3 sidebar-wrapper bg-light" id="sidebar">
<div class="text-center" style="border: 1px solid #0dcaf0; height: 22px;" id="time_now">
<?php
$timeNow = new DateTime('now', new DateTimeZone($userTimezone));
?>
<!--span style="vertical-align: top; font-size: 12px;"><?= htmlspecialchars($timeNow->format('d M Y H:i')) ?> <?= htmlspecialchars($userTimezone) ?></span-->
<span style="vertical-align: top; font-size: 12px;"><?= htmlspecialchars($timeNow->format('H:i')) ?>&nbsp;&nbsp;<?= htmlspecialchars($userTimezone) ?></span>
</div>
<div class="col-4"><button class="btn btn-sm btn-info toggle-sidebar-button" type="button" id="toggleSidebarButton" value=">>"></button></div>
<div class="sidebar-content card ml-3 mt-3">
<ul class="list-group">
<a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=dashboard">
<li class="list-group-item<?php if ($page === 'dashboard') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
<i class="fas fa-chart-line" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="general jitsi stats"></i>general stats
</li>
</a>
<li class="list-group-item bg-light" style="border: none;"><p class="text-end mb-0"><small>logs statistics</small></p></li>
<a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=conferences">
<li class="list-group-item<?php if ($page === 'conferences') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
<i class="fas fa-video" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="conferences"></i>conferences
</li>
</a>
<a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=participants">
<li class="list-group-item<?php if ($page === 'participants') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
<i class="fas fa-users" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="participants"></i>participants
</li>
</a>
<a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=components">
<li class="list-group-item<?php if ($page === 'components') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
<i class="fas fa-puzzle-piece" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="components"></i>components
</li>
</a>
<li class="list-group-item bg-light" style="border: none;"><p class="text-end mb-0"><small>graphs</small></p></li>
<a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=graphs">
<li class="list-group-item<?php if ($page === 'graphs') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
<i class="fas fa-chart-bar" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="combined graphs"></i>combined graphs
</li>
</a>
<a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=latest">
<li class="list-group-item<?php if ($page === 'latest') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
<i class="fas fa-list" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="latest data"></i>latest data
</li>
</a>
<li class="list-group-item bg-light" style="border: none;"><p class="text-end mb-0"><small>live data</small></p></li>
<a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=config&item=configjs">
<li class="list-group-item<?php if ($page === 'config' && $item === 'configjs') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
<i class="fas fa-tv" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="config.js"></i>config.js
</li>
</a>
<a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=config&item=interfaceconfigjs">
<li class="list-group-item<?php if ($page === 'config' && $item === 'interfaceconfigjs') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
<i class="fas fa-th" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="interface_config.js"></i>interface_config.js
</li>
</a>
<a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=agents">
<li class="list-group-item<?php if ($page === 'agents') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
<i class="fas fa-mask" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="jilo agents"></i>jilo agents
</li>
</a>
<li class="list-group-item bg-light" style="border: none;"><p class="text-end mb-0"><small>system</small></p></li>
<?php if ($userObject->hasRight($user_id, 'view config file')) {?>
<a href="<?= htmlspecialchars($app_root) ?>?page=config">
<li class="list-group-item<?php if ($page === 'config' && $item === '') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
<i class="fas fa-wrench" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="configuration"></i>config
</li>
</a>
<?php } ?>
<a href="<?= htmlspecialchars($app_root) ?>?page=status">
<li class="list-group-item<?php if ($page === 'status' && $item === '') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
<i class="fas fa-heartbeat" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="status"></i>status
</li>
</a>
<?php if ($userObject->hasRight($user_id, 'view app logs')) {?>
<a href="<?= htmlspecialchars($app_root) ?>?page=logs">
<li class="list-group-item<?php if ($page === 'logs') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
<i class="fas fa-shoe-prints" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="logs"></i>logs
</li>
</a>
<?php } ?>
<a href="<?= htmlspecialchars($app_root) ?>?page=help">
<li class="list-group-item<?php if ($page === 'help') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
<i class="fas fa-question-circle" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="help"></i>help
</li>
</a>
</ul>
</div>
</div>
<!-- /Sidebar -->
<!-- Main content -->
<div class="col-md-9 main-content" id="mainContent">

View File

@ -1,201 +0,0 @@
<!-- user profile -->
<div class="card text-center w-50 mx-auto">
<p class="h4 card-header">Profile of <?= htmlspecialchars($userDetails[0]['username']) ?></p>
<div class="card-body">
<form method="POST" action="<?= htmlspecialchars($app_root) ?>?page=profile" enctype="multipart/form-data">
<div class="row">
<p class="border rounded bg-light mb-4"><small>edit the profile fields</small></p>
<div class="col-md-4 avatar-container">
<div class="avatar-wrapper">
<img class="avatar-img" src="<?= htmlspecialchars($app_root) . htmlspecialchars($avatar) ?>" alt="avatar" />
<div class="avatar-btn-container">
<label for="avatar-upload" class="avatar-btn avatar-btn-select btn btn-primary">
<i class="fas fa-folder" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="select new avatar"></i>
</label>
<input type="file" id="avatar-upload" name="avatar_file" accept="image/*" style="display:none;">
<?php if ($default_avatar) { ?>
<button type="button" class="avatar-btn avatar-btn-remove btn btn-secondary" data-toggle="modal" data-target="#confirmDeleteModal" disabled>
<?php } else { ?>
<button type="button" class="avatar-btn avatar-btn-remove btn btn-danger" data-toggle="modal" data-target="#confirmDeleteModal">
<?php } ?>
<i class="fas fa-trash" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="remove current avatar"></i>
</button>
</div>
</div>
</div>
<div class="col-md-8">
<!--div class="row mb-3">
<div class="col-md-4 text-end">
<label for="username" class="form-label"><small>username:</small></label>
<span class="text-danger" style="margin-right: -12px;">*</span>
</div>
<div class="col-md-8 text-start bg-light">
<input class="form-control" type="text" name="username" value="<?= htmlspecialchars($userDetails[0]['username']) ?>" required />
</div>
</div-->
<div class="row mb-3">
<div class="col-md-4 text-end">
<label for="name" class="form-label"><small>name:</small></label>
</div>
<div class="col-md-8 text-start bg-light">
<input class="form-control" type="text" name="name" value="<?= htmlspecialchars($userDetails[0]['name']) ?>" autofocus />
</div>
</div>
<div class="row mb-3">
<div class="col-md-4 text-end">
<label for="email" class="form-label"><small>email:</small></label>
</div>
<div class="col-md-8 text-start bg-light">
<input class="form-control" type="text" name="email" value="<?= htmlspecialchars($userDetails[0]['email']) ?>" />
</div>
</div>
<div class="row mb-3">
<div class="col-md-4 text-end">
<label for="timezone" class="form-label"><small>timezone:</small></label>
</div>
<div class="col-md-8 text-start bg-light">
<select class="form-control" name="timezone" id="timezone">
<?php foreach ($allTimezones as $timezone) { ?>
<option value="<?= htmlspecialchars($timezone) ?>" <?= $timezone === $userTimezone ? 'selected' : '' ?>>
<?= htmlspecialchars($timezone) ?>&nbsp;&nbsp;(<?= htmlspecialchars(getUTCOffset($timezone)) ?>)
</option>
<?php } ?>
</select>
</div>
</div>
<div class="row mb-3">
<div class="col-md-4 text-end">
<label for="bio" class="form-label"><small>bio:</small></label>
</div>
<div class="col-md-8 text-start bg-light">
<textarea class="form-control" name="bio" rows="10"><?= htmlspecialchars($userDetails[0]['bio'] ?? '') ?></textarea>
</div>
</div>
<div class="row mb-3">
<div class="col-md-4 text-end">
<label for="rights" class="form-label"><small>rights:</small></label>
</div>
<div class="col-md-8 text-start bg-light">
<?php foreach ($allRights as $right) {
// Check if the current right exists in $userRights
$isChecked = false;
foreach ($userRights as $userRight) {
if ($userRight['right_id'] === $right['right_id']) {
$isChecked = true;
break;
}
} ?>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="rights[]" value="<?= htmlspecialchars($right['right_id']) ?>" id="right_<?= htmlspecialchars($right['right_id']) ?>" <?= $isChecked ? 'checked' : '' ?> />
<label class="form-check-label" for="right_<?= htmlspecialchars($right['right_id']) ?>"><?= htmlspecialchars($right['right_name']) ?></label>
</div>
<?php } ?>
</div>
</div>
</div>
<p>
<a href="<?= htmlspecialchars($app_root) ?>?page=profile" class="btn btn-secondary">Cancel</a>
<input type="submit" class="btn btn-primary" value="Save" />
</p>
</div>
</form>
<!-- avatar removal modal confirmation -->
<div class="modal fade" id="confirmDeleteModal" tabindex="-1" aria-labelledby="confirmDeleteModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="confirmDeleteModalLabel">Confirm Avatar Deletion</h5>
<button type="button" class="btn-close" data-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<img class="avatar-img" src="<?= htmlspecialchars($app_root) . htmlspecialchars($avatar) ?>" alt="avatar" />
<br />
Are you sure you want to delete your avatar?
<br />
This action cannot be undone.
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<form id="remove-avatar-form" data-action="remove-avatar" method="POST" action="<?= htmlspecialchars($app_root) ?>?page=profile&action=remove&item=avatar">
<button type="button" class="btn btn-danger" id="confirm-delete">Delete Avatar</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- /user profile -->
<script>
// Preview the uploaded avatar
document.getElementById('avatar-upload').addEventListener('change', function(event) {
const reader = new FileReader();
reader.onload = function() {
document.querySelector('.avatar-img').src = reader.result;
};
reader.readAsDataURL(event.target.files[0]);
});
// Avatar file size and type control
document.getElementById('avatar-upload').addEventListener('change', function() {
const maxFileSize = 500 * 1024; // 500 KB in bytes
const currentAvatar = '<?= htmlspecialchars($app_root) . htmlspecialchars($avatar) ?>'; // current avatar
const file = this.files[0];
if (file) {
// Check file size
if (file.size > maxFileSize) {
alert('File size exceeds 500 KB. Please select a smaller file.');
this.value = ''; // Clear the file input
document.querySelector('.avatar-img').src = currentAvatar;
}
}
});
// Submitting the avatar deletion confirmation modal form
document.getElementById('confirm-delete').addEventListener('click', function(event) {
event.preventDefault(); // Prevent the outer form from submitting
document.getElementById('remove-avatar-form').submit();
});
// Function to detect user's timezone and select it in the dropdown
function setTimezone() {
const userTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
const timezoneSelect = document.getElementById("timezone");
timezoneSelect.className = 'form-control border border-danger';
// Loop through the options to find and select the user's timezone
for (let i = 0; i < timezoneSelect.options.length; i++) {
if (timezoneSelect.options[i].value === userTimezone) {
timezoneSelect.selectedIndex = i;
break;
}
}
}
// Run the function on page load
window.onload = function() {
const isTimezoneSet = <?php echo json_encode($isTimezoneSet); ?>; // Pass PHP flag to JavaScript
// If timezone is not set, run setTimezone()
if (!isTimezoneSet) {
setTimezone();
}
};
</script>

View File

@ -1,87 +0,0 @@
<!-- user profile -->
<div class="card text-center w-50 mx-auto">
<p class="h4 card-header">Profile of <?= htmlspecialchars($userDetails[0]['username']) ?></p>
<div class="card-body">
<div class="row">
<div class="col-md-4 avatar-container">
<div>
<img class="avatar-img" src="<?= htmlspecialchars($app_root) . htmlspecialchars($avatar) ?>" alt="avatar" />
</div>
</div>
<div class="col-md-8">
<!--div class="row mb-3">
<div class="col-md-4 text-end">
<label class="form-label"><small>username:</small></label>
</div>
<div class="col-md-8 text-start bg-light">
<?= htmlspecialchars($userDetails[0]['username']) ?>
</div>
</div-->
<div class="row mb-3">
<div class="col-md-4 text-end">
<label class="form-label"><small>name:</small></label>
</div>
<div class="col-md-8 text-start bg-light">
<?= htmlspecialchars($userDetails[0]['name']) ?>
</div>
</div>
<div class="row mb-3">
<div class="col-md-4 text-end">
<label class="form-label"><small>email:</small></label>
</div>
<div class="col-md-8 text-start bg-light">
<?= htmlspecialchars($userDetails[0]['email']) ?>
</div>
</div>
<div class="row mb-3">
<div class="col-md-4 text-end">
<label class="form-label"><small>timezone:</small></label>
</div>
<div class="col-md-8 text-start bg-light">
<?php if (isset($userDetails[0]['timezone'])) { ?>
<?= htmlspecialchars($userDetails[0]['timezone']) ?>&nbsp;&nbsp;<span style="font-size: 0.66em;">(<?= htmlspecialchars(getUTCOffset($userDetails[0]['timezone'])) ?>)</span>
<?php } ?>
</div>
</div>
<div class="row mb-3">
<div class="col-md-4 text-end">
<label class="form-label"><small>bio:</small></label>
</div>
<div class="col-md-8 text-start bg-light">
<textarea class="scroll-box" rows="10" readonly><?= htmlspecialchars($userDetails[0]['bio'] ?? '') ?></textarea>
</div>
</div>
<div class="row mb-3">
<div class="col-md-4 text-end">
<label class="form-label"><small>rights:</small></label>
</div>
<div class="col-md-8 text-start bg-light">
<?php foreach ($userRights as $right) { ?>
<?= htmlspecialchars($right['right_name'] ?? '') ?>
<br />
<?php } ?>
</div>
</div>
</div>
<p>
<a href="<?= htmlspecialchars($app_root) ?>?page=profile&action=edit" class="btn btn-primary">Edit</a>
</p>
</div>
</div>
</div>
<!-- /user profile -->

View File

@ -1,14 +0,0 @@
<!-- jilo agent status -->
<div class="card text-center w-75 mx-lef" style="padding-left: 80px;">
<div class="card-body">
<p class="card-text text-left" style="text-align: left;">
Jilo Agent <a href="<?= htmlspecialchars($app_root) ?>?page=config#platform<?= htmlspecialchars($platform['id']) ?>agent<?= htmlspecialchars($agent['id']) ?>"><?= htmlspecialchars($agent['agent_description']) ?></a>:
<strong><?= $agent_availability ?></strong>
<br />
host: <strong><?= htmlspecialchars($agent_host) ?></strong>,
port: <strong><?= htmlspecialchars($agent_port) ?></strong>,
endpoint: <strong><?= htmlspecialchars($agent['agent_endpoint']) ?></strong>
</p>
</div>
</div>

View File

@ -1,10 +0,0 @@
<!-- jitsi platform status -->
<br />
<div class="card text-center w-75 mx-lef" style="padding-left: 40px;">
<div class="card-body">
<p class="card-text text-left" style="text-align: left;">
Jitsi Meet platform: <a href="<?= htmlspecialchars($app_root) ?>?page=config#platform<?= htmlspecialchars($platform['id']) ?>"><?= htmlspecialchars($platform['name']) ?></a>
</p>
</div>
</div>

View File

@ -1,19 +0,0 @@
<!-- jilo status -->
<div class="card text-center w-75 mx-lef">
<p class="h4 card-header">Jilo platform status</p>
<div class="card-body">
<p class="card-text text-left" style="text-align: left;">
Jilo Server:
<?php if ($server_status) { ?>
<strong><span class="text-success">running</span></strong>
<?php } else { ?>
<strong><span class="text-danger">not running</span></strong>
<?php } ?>
<br />
host: <strong><?= htmlspecialchars($server_host) ?></strong>,
port: <strong><?= htmlspecialchars($server_port) ?></strong>,
endpoint: <strong><?= htmlspecialchars($server_endpoint) ?></strong>
</p>
</div>
</div>

View File

@ -1,60 +0,0 @@
<div class="row">
<?php if ($widget['collapsible'] === true) { ?>
<a style="text-decoration: none;" data-toggle="collapse" href="#collapse<?= htmlspecialchars($widget['name']) ?>" role="button" aria-expanded="true" aria-controls="collapse<?= htmlspecialchars($widget['name']) ?>">
<div class="card w-auto bg-light card-body" style="flex-direction: row;"><?= $widget['title'] ?></div>
<?php } else { ?>
<div class="card w-auto bg-light border-light card-body" style="flex-direction: row;"><?= $widget['title'] ?></div>
<?php } ?>
<?php if ($widget['filter'] === true) {
include '../app/templates/block-results-filter.php'; } ?>
<?php if ($widget['collapsible'] === true) { ?>
</a>
<?php } ?>
</div>
<!-- widget "<?= htmlspecialchars($widget['name']) ?>" -->
<div class="collapse show" id="collapse<?= htmlspecialchars($widget['name']) ?>">
<?php if ($time_range_specified) { ?>
<p class="m-3">time period: <strong><?= htmlspecialchars($from_time) ?> - <?= htmlspecialchars($until_time) ?></strong></p>
<?php } ?>
<div class="mb-5">
<?php if ($widget['full'] === true) { ?>
<table class="table table-results table-striped table-hover table-bordered">
<thead class="thead-dark">
<tr>
<th scope="col"></th>
<?php foreach ($widget['records'] as $record) { ?>
<th scope="col"><?= htmlspecialchars($record['table_headers']) ?></th>
<?php } ?>
</tr>
</thead>
<tbody>
<tr>
<td>conferences</td>
<?php foreach ($widget['records'] as $record) { ?>
<td><?php if (!empty($record['conferences'])) { ?>
<a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=conferences&from_time=<?= htmlspecialchars($record['from_time']) ?>&until_time=<?= htmlspecialchars($record['until_time']) ?>"><?= htmlspecialchars($record['conferences']) ?></a> <?php } else { ?>
0<?php } ?>
</td>
<?php } ?>
</tr>
<tr>
<td>participants</td>
<?php foreach ($widget['records'] as $record) { ?>
<td><?php if (!empty($record['participants'])) { ?>
<a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=participants&from_time=<?= htmlspecialchars($record['from_time']) ?>&until_time=<?= htmlspecialchars($record['until_time']) ?>"><?= htmlspecialchars($record['participants']) ?></a> <?php } else { ?>
0<?php } ?>
</td>
<?php } ?>
</tr>
</tbody>
</table>
<?php } else { ?>
<p class="m-3">No matching records found.</p>
<?php } ?>
</div>
</div>
<!-- /widget "<?= htmlspecialchars($widget['name']) ?>" -->

View File

@ -1,86 +0,0 @@
<div class="row">
<?php if ($widget['collapsible'] === true) { ?>
<a style="text-decoration: none;" data-toggle="collapse" href="#collapse<?= htmlspecialchars($widget['name']) ?>" role="button" aria-expanded="true" aria-controls="collapse<?= htmlspecialchars($widget['name']) ?>">
<div class="card w-auto bg-light card-body" style="flex-direction: row;"><?= $widget['title'] ?></div>
<?php } else { ?>
<div class="card w-auto bg-light border-light card-body" style="flex-direction: row;"><?= $widget['title'] ?></div>
<?php } ?>
<?php if ($widget['filter'] === true) {
include '../app/templates/block-results-filter.php'; } ?>
<?php if ($widget['collapsible'] === true) { ?>
</a>
<?php } ?>
</div>
<!-- widget "<?= htmlspecialchars($widget['name']) ?>" -->
<div class="collapse show" id="collapse<?= htmlspecialchars($widget['name']) ?>">
<?php if ($time_range_specified) { ?>
<p class="m-3">time period: <strong><?= htmlspecialchars($from_time) ?> - <?= htmlspecialchars($until_time) ?></strong></p>
<?php } ?>
<div class="mb-5">
<?php if ($widget['full'] === true) { ?>
<table class="table table-results table-striped table-hover table-bordered">
<thead class="thead-dark">
<tr>
<?php foreach ($widget['table_headers'] as $header) { ?>
<th scope="col"><?= htmlspecialchars($header) ?></th>
<?php } ?>
</tr>
</thead>
<tbody>
<?php foreach ($widget['table_records'] as $row) { ?>
<tr>
<?php $stats_id = false;
$participant_ip = false;
if (isset($row['event']) && $row['event'] === 'stats_id') $stats_id = true;
if (isset($row['event']) && $row['event'] === 'pair selected') $participant_ip = true;
foreach ($row as $key => $column) {
if ($key === 'conference ID' && isset($conferenceId) && $conferenceId === $column) { ?>
<td><strong><?= htmlspecialchars($column ?? '') ?></strong></td>
<?php } elseif ($key === 'conference ID') { ?>
<td><a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=conferences&id=<?= htmlspecialchars($column ?? '') ?>"><?= htmlspecialchars($column ?? '') ?></a></td>
<?php } elseif ($key === 'conference name' && isset($conferenceName) && $conferenceName === $column) { ?>
<td><strong><?= htmlspecialchars($column ?? '') ?></strong></td>
<?php } elseif ($key === 'conference name') { ?>
<td><a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=conferences&name=<?= htmlspecialchars($column ?? '') ?>"><?= htmlspecialchars($column ?? '') ?></a></td>
<?php } elseif ($key === 'participant ID' && isset($participantId) && $participantId === $column) { ?>
<td><strong><?= htmlspecialchars($column ?? '') ?></strong></td>
<?php } elseif ($key === 'participant ID') { ?>
<td><a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=participants&id=<?= htmlspecialchars($column ?? '') ?>"><?= htmlspecialchars($column ?? '') ?></a></td>
<?php } elseif ($key === 'component ID') { ?>
<td><a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=components&id=<?= htmlspecialchars($column ?? '') ?>"><?= htmlspecialchars($column ?? '') ?></a></td>
<?php } elseif ($stats_id && $key === 'parameter' && isset($participantName) && $participantName === $column) { ?>
<td><strong><?= htmlspecialchars($column ?? '') ?></strong></td>
<?php } elseif ($stats_id && $key === 'parameter') { ?>
<td><a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=participants&name=<?= htmlspecialchars($column ?? '') ?>"><?= htmlspecialchars($column ?? '') ?></a></td>
<?php } elseif ($participant_ip && $key === 'parameter' && isset($participantIp) && $participantIp === $column) { ?>
<td><strong><?= htmlspecialchars($column ?? '') ?></strong></td>
<?php } elseif ($participant_ip && $key === 'parameter') { ?>
<td><a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=participants&ip=<?= htmlspecialchars($column ?? '') ?>"><?= htmlspecialchars($column ?? '') ?></a></td>
<?php } elseif ($key === 'component') { ?>
<td><a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=components&name=<?= htmlspecialchars($column ?? '') ?>"><?= htmlspecialchars($column ?? '') ?></a></td>
<?php
// in general listings we don't show seconds and miliseconds
} elseif ($key === 'start' || $key === 'end') { ?>
<td><?= htmlspecialchars(substr($column ?? '', 0, -7)) ?></td>
<?php } else { ?>
<td><?= htmlspecialchars($column ?? '') ?></td>
<?php }
} ?>
</tr>
<?php } ?>
</tbody>
</table>
<?php
if ($widget['pagination'] && $item_count > $items_per_page) {
$url = "$app_root?platform=$platform_id&page=$page";
include '../app/helpers/pagination.php';
}
?>
<?php } else { ?>
<p class="m-3">No matching records found.</p>
<?php } ?>
</div>
</div>
<!-- /widget "<?= htmlspecialchars($widget['name']) ?>" -->

View File

@ -4,9 +4,4 @@
CustomLog \${APACHE_LOG_DIR}/jilo-web_access.log combined
ErrorLog \${APACHE_LOG_DIR}/jilo-web_error.log
<Directory $INSTALL_DIR>
AllowOverride All
</Directory>
</VirtualHost>

15
config.nginx 100644
View File

@ -0,0 +1,15 @@
server {
listen 80;
server_name $DOMAIN;
root $INSTALL_DIR;
index index.php;
location / {
try_files \$uri \$uri/ /index.php?\$args;
}
location ~ /\.ht {
deny all;
}
}

View File

@ -1,14 +0,0 @@
RewriteEngine On
# limit access to .htaccess
<Files .htaccess>
Order Allow,Deny
Deny from all
</Files>
# don't rewrite CSS, JS, etc.
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# all other go to index.php
RewriteRule ^(.*)$ index.php?page=$1 [L,QSA]

View File

@ -1,22 +0,0 @@
server {
listen 80;
server_name $DOMAIN;
root $INSTALL_DIR;
index index.php;
location / {
try_files \$uri \$uri/ /index.php?\$args;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.ht {
deny all;
}
}

View File

@ -1,5 +0,0 @@
INSERT INTO jilo_agent_types VALUES(1,'jvb','/jvb');
INSERT INTO jilo_agent_types VALUES(2,'jicofo','/jicofo');
INSERT INTO jilo_agent_types VALUES(3,'prosody','/prosody');
INSERT INTO jilo_agent_types VALUES(4,'nginx','/nginx');
INSERT INTO jilo_agent_types VALUES(5,'jibri','/jibri');

View File

@ -1,13 +0,0 @@
INSERT INTO rights VALUES(1,'superuser');
INSERT INTO rights VALUES(2,'edit users');
INSERT INTO rights VALUES(3,'view config file');
INSERT INTO rights VALUES(4,'edit config file');
INSERT INTO rights VALUES(5,'view own profile');
INSERT INTO rights VALUES(6,'edit own profile');
INSERT INTO rights VALUES(7,'view all profiles');
INSERT INTO rights VALUES(8,'edit all profiles');
INSERT INTO rights VALUES(9,'view app logs');
INSERT INTO rights VALUES(10,'view all platforms');
INSERT INTO rights VALUES(11,'edit all platforms');
INSERT INTO rights VALUES(12,'view all agents');
INSERT INTO rights VALUES(13,'edit all agents');

View File

@ -1,65 +0,0 @@
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
password TEXT NOT NULL
);
CREATE TABLE users_meta (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
name TEXT,
email TEXT,
timezone TEXT,
avatar TEXT,
bio TEXT,
FOREIGN KEY (user_id) REFERENCES users(id)
);
CREATE TABLE users_rights (
user_id INTEGER,
right_id INTEGER,
PRIMARY KEY (user_id, right_id),
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (right_id) REFERENCES rights(id)
);
CREATE TABLE rights (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE
);
CREATE TABLE platforms (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE,
jitsi_url TEXT NOT NULL,
jilo_database TEXT NOT NULL
);
CREATE TABLE jilo_agents (
id INTEGER PRIMARY KEY AUTOINCREMENT,
platform_id INTEGER NOT NULL,
agent_type_id INTEGER NOT NULL,
url TEXT NOT NULL,
secret_key TEXT,
check_period INTEGER DEFAULT 0,
FOREIGN KEY (platform_id) REFERENCES platforms(id),
FOREIGN KEY (agent_type_id) REFERENCES jilo_agent_types(id)
);
CREATE TABLE jilo_agent_types (
id INTEGER PRIMARY KEY AUTOINCREMENT,
description TEXT,
endpoint TEXT
);
CREATE TABLE jilo_agent_checks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
agent_id INTEGER,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
status_code INTEGER,
response_time_ms INTEGER,
response_content TEXT,
FOREIGN KEY (agent_id) REFERENCES jilo_agents(id)
);
CREATE TABLE logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGET NOT NULL,
time TEXT DEFAULT (DATETIME('now')),
scope TEXT NOT NULL,
message TEXT NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id)
);

View File

@ -6,7 +6,7 @@ if [ "$EUID" -ne 0 ] && [ -z "$SUDO_USER" ]; then
exit 1
fi
VERSION=`grep version ../app/config/jilo-web.conf.php | cut -d "'" -f 4`
VERSION=`grep version jilo-web.conf.php | cut -d "'" -f 4`
# main install function
function install() {
@ -20,24 +20,23 @@ function install() {
WEB_DIR=${WEB_DIR:-jilo-web}
INSTALL_DIR="/opt/jilo-web/public_html"
APP_DIR="/opt/jilo-web/app"
DOC_DIR="/opt/jilo-web/doc"
ETC_DIR="/opt/jilo-web/etc"
mkdir -p $INSTALL_DIR
cp -r ../public_html/* $INSTALL_DIR
cp -r ./public_html/* $INSTALL_DIR
mkdir -p $DOC_DIR
cp ../CHANGELOG.md $DOC_DIR
cp ../LICENSE $DOC_DIR
cp ../README.md $DOC_DIR
cp ../TODO.md $DOC_DIR
cp ../license-bootstrap $DOC_DIR
cp ../license-jquery $DOC_DIR
cp -r ../doc/ $DOC_DIR
cp CHANGELOG.md $DOC_DIR
cp LICENSE $DOC_DIR
cp README.md $DOC_DIR
cp TODO.md $DOC_DIR
cp config.apache $DOC_DIR
cp config.nginx $DOC_DIR
mkdir -p $ETC_DIR
cp ../app/config/jilo-web.conf.php $ETC_DIR
cp jilo-web.conf.php $ETC_DIR
cp jilo-web.schema $ETC_DIR
#FIXME
#mkdir -p "jilo-web-$VERSION/usr/share/man/man8"

11
jilo-web.conf.php 100644
View File

@ -0,0 +1,11 @@
<?php
$config = [
'domain' => 'localhost',
'folder' => '/jilo-web/',
'database' => '/home/yasen/work/code/git/lindeas-code/jilo-web/jilo-web.db',
'jilo_database' => '/home/yasen/work/code/git/lindeas-code/jilo/jilo.db',
'version' => '0.1.1',
];
?>

5
jilo-web.schema 100644
View File

@ -0,0 +1,5 @@
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
password TEXT NOT NULL
);

View File

@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2014-2024 Chart.js Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -1,20 +0,0 @@
Copyright OpenJS Foundation and other contributors, https://openjsf.org/
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,5 +1,5 @@
Package: jilo-web
Version: 0.2.1
Version: 0.1.1
Section: web
Priority: optional
Architecture: all

View File

@ -1,4 +1,4 @@
.TH JILO-WEB "8" "October 2024" "jilo-web 0.2.1"
.TH JILO-WEB "8" "July 2024" "jilo-web 0.1.1"
.SH NAME
jilo-web \- PHP frontent to jilo (jitsi logs observer) database.
.SH DESCRIPTION
@ -17,7 +17,7 @@ Written and maintained by Yasen Pramatarov <yasen@lindeas.com>
https://lindeas.com/jilo
.SH VERSION
0.2.1
0.1.1
.SH SEE ALSO
jilo(8), jilo-cli(8)

View File

@ -1,5 +1,5 @@
Name: jilo-web
Version: 0.2.1
Version: 0.1.1
Release: 1%{?dist}
Summary: Jitsi logs web observer
@ -54,10 +54,6 @@ cp %{sourcedir}/man-jilo.8 %{buildroot}/usr/share/man/man8/%{name}.8
/usr/share/man/man8/%{name}.8.gz
%changelog
* Thu Oct 17 2024 Yasen Pramatarov <yasen@lindeas.com> 0.2.1
- Build of upstream v0.2.1
* Sat Aug 31 2024 Yasen Pramatarov <yasen@lindeas.com> 0.2
- Build of upstream v0.2
* Thu Jul 25 2024 Yasen Pramatarov <yasen@lindeas.com> 0.1.1
- Build of upstream v0.1.1
* Wed Jul 12 2024 Yasen Pramatarov <yasen@lindeas.com> 0.1

View File

@ -0,0 +1,40 @@
<?php
class Component {
private $db;
private $queries;
public function __construct($database) {
$this->db = $database->getConnection();
$this->queries = include('queries.php');
}
// list of component events
public function jitsiComponents($jitsi_component, $component_id, $from_time, $until_time) {
// time period drill-down
// FIXME make it similar to the bash version
if (empty($from_time)) {
$from_time = '0000-01-01';
}
if (empty($until_time)) {
$until_time = '9999-12-31';
}
// this is needed for compatibility with the bash version, so we use '%s' placeholders
$from_time = htmlspecialchars(strip_tags($from_time));
$until_time = htmlspecialchars(strip_tags($until_time));
$sql = $this->queries['jitsi_components'];
$sql = sprintf($sql, $jitsi_component, $component_id, $from_time, $until_time);
$query = $this->db->prepare($sql);
$query->execute();
return $query->fetchAll(PDO::FETCH_ASSOC);
}
}
?>

View File

@ -0,0 +1,90 @@
<?php
class Conference {
private $db;
private $queries;
public function __construct($database) {
$this->db = $database->getConnection();
$this->queries = include('queries.php');
}
// search/list specific conference ID
public function conferenceById($conference_id, $from_time, $until_time) {
// time period drill-down
// FIXME make it similar to the bash version
if (empty($from_time)) {
$from_time = '0000-01-01';
}
if (empty($until_time)) {
$until_time = '9999-12-31';
}
// this is needed for compatibility with the bash version, so we use '%s' placeholders
$from_time = htmlspecialchars(strip_tags($from_time));
$until_time = htmlspecialchars(strip_tags($until_time));
$sql = $this->queries['conference_by_id'];
$sql = sprintf($sql, $conference_id, $from_time, $until_time, $conference_id, $from_time, $until_time);
$query = $this->db->prepare($sql);
$query->execute();
return $query->fetchAll(PDO::FETCH_ASSOC);
}
// search/list specific conference name
public function conferenceByName($conference_name, $from_time, $until_time) {
// time period drill-down
// FIXME make it similar to the bash version
if (empty($from_time)) {
$from_time = '0000-01-01';
}
if (empty($until_time)) {
$until_time = '9999-12-31';
}
// this is needed for compatibility with the bash version, so we use '%s' placeholders
$from_time = htmlspecialchars(strip_tags($from_time));
$until_time = htmlspecialchars(strip_tags($until_time));
$sql = $this->queries['conference_by_name'];
$sql = sprintf($sql, $conference_name, $from_time, $until_time, $conference_name, $from_time, $until_time);
$query = $this->db->prepare($sql);
$query->execute();
return $query->fetchAll(PDO::FETCH_ASSOC);
}
// list of all conferences
public function conferencesAllFormatted($from_time, $until_time) {
// time period drill-down
// FIXME make it similar to the bash version
if (empty($from_time)) {
$from_time = '0000-01-01';
}
if (empty($until_time)) {
$until_time = '9999-12-31';
}
// this is needed for compatibility with the bash version, so we use '%s' placeholders
$from_time = htmlspecialchars(strip_tags($from_time));
$until_time = htmlspecialchars(strip_tags($until_time));
$sql = $this->queries['conferences_all_formatted'];
$sql = sprintf($sql, $from_time, $until_time);
$query = $this->db->prepare($sql);
$query->execute();
return $query->fetchAll(PDO::FETCH_ASSOC);
}
}
?>

View File

@ -0,0 +1,34 @@
<?php
class Database {
private $pdo;
public function __construct($dbFile) {
// pdo and pdo_sqlite needed
if ( !extension_loaded('pdo_sqlite') ) {
throw new Exception('PDO extension for SQLite not loaded.');
}
// database file check
if (empty($dbFile) || !file_exists($dbFile)) {
throw new Exception('Database file is not found.');
}
// connect to database
// FIXME: add mysql/mariadb option
try {
$this->pdo = new PDO("sqlite:" . $dbFile);
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
throw new Exception('DB connection failed: ' . $e->getMessage());
}
}
public function getConnection() {
return $this->pdo;
}
}
?>

View File

@ -0,0 +1,115 @@
<?php
class Participant {
private $db;
private $queries;
public function __construct($database) {
$this->db = $database->getConnection();
$this->queries = include('queries.php');
}
// search/list specific participant ID
public function conferenceByParticipantId($participant_id, $from_time, $until_time) {
// time period drill-down
// FIXME make it similar to the bash version
if (empty($from_time)) {
$from_time = '0000-01-01';
}
if (empty($until_time)) {
$until_time = '9999-12-31';
}
// this is needed for compatibility with the bash version, so we use '%s' placeholders
$from_time = htmlspecialchars(strip_tags($from_time));
$until_time = htmlspecialchars(strip_tags($until_time));
$sql = $this->queries['conference_by_participant_id'];
$sql = sprintf($sql, $participant_id, $from_time, $until_time, $participant_id, $from_time, $until_time);
$query = $this->db->prepare($sql);
$query->execute();
return $query->fetchAll(PDO::FETCH_ASSOC);
}
// search/list specific participant name (stats_id)
public function conferenceByParticipantName($participant_name, $from_time, $until_time) {
// time period drill-down
// FIXME make it similar to the bash version
if (empty($from_time)) {
$from_time = '0000-01-01';
}
if (empty($until_time)) {
$until_time = '9999-12-31';
}
// this is needed for compatibility with the bash version, so we use '%s' placeholders
$from_time = htmlspecialchars(strip_tags($from_time));
$until_time = htmlspecialchars(strip_tags($until_time));
$sql = $this->queries['participant_by_stats_id'];
$sql = sprintf($sql, $participant_name, $from_time, $until_time, $participant_name, $from_time, $until_time);
$query = $this->db->prepare($sql);
$query->execute();
return $query->fetchAll(PDO::FETCH_ASSOC);
}
// search/list specific participant IP
public function conferenceByParticipantIP($participant_ip, $from_time, $until_time) {
// time period drill-down
// FIXME make it similar to the bash version
if (empty($from_time)) {
$from_time = '0000-01-01';
}
if (empty($until_time)) {
$until_time = '9999-12-31';
}
// this is needed for compatibility with the bash version, so we use '%s' placeholders
$from_time = htmlspecialchars(strip_tags($from_time));
$until_time = htmlspecialchars(strip_tags($until_time));
$sql = $this->queries['participant_by_ip'];
$sql = sprintf($sql, $participant_ip, $from_time, $until_time, $participant_ip, $from_time, $until_time);
$query = $this->db->prepare($sql);
$query->execute();
return $query->fetchAll(PDO::FETCH_ASSOC);
}
// list of all conferences
public function participantsAll($from_time, $until_time) {
// time period drill-down
// FIXME make it similar to the bash version
if (empty($from_time)) {
$from_time = '0000-01-01';
}
if (empty($until_time)) {
$until_time = '9999-12-31';
}
// this is needed for compatibility with the bash version, so we use '%s' placeholders
$from_time = htmlspecialchars(strip_tags($from_time));
$until_time = htmlspecialchars(strip_tags($until_time));
$sql = $this->queries['participants_all'];
$sql = sprintf($sql, $from_time, $until_time);
$query = $this->db->prepare($sql);
$query->execute();
return $query->fetchAll(PDO::FETCH_ASSOC);
}
}
?>

View File

@ -0,0 +1,314 @@
<?php
// all sql queries for the jilo database in one place
return [
// list of conferences for time period (if given)
// fields: component, duration, conference ID, conference name, number of participants, name count (the conf name is found), conference host
'conferences_all_formatted' => "
SELECT DISTINCT
c.jitsi_component,
(SELECT ce.time
FROM conference_events ce
WHERE
ce.conference_id = c.conference_id
AND
ce.conference_event = 'conference created')
AS start,
(SELECT ce.time
FROM conference_events ce
WHERE
ce.conference_id = c.conference_id
AND
ce.conference_event = 'conference expired')
AS end,
c.conference_id,
c.conference_name,
(SELECT COUNT(pe.participant_id)
FROM participant_events pe
WHERE
pe.event_type = 'participant joining'
AND
pe.event_param = c.conference_id) AS participants,
name_counts.name_count,
c.conference_host
FROM
conferences c
JOIN (
SELECT
conference_name,
COUNT(*) AS name_count
FROM
conferences
GROUP BY
conference_name
) AS name_counts ON c.conference_name = name_counts.conference_name
JOIN
conference_events ce ON c.conference_id = ce.conference_id
WHERE (ce.time >= '%s 00:00:00' AND ce.time <= '%s 23:59:59')
ORDER BY
c.id;",
// search for a conference by its ID for a time period (if given)
'conference_by_id' => "
SELECT
pe.time,
c.conference_id,
c.conference_name,
c.conference_host,
pe.loglevel,
pe.event_type,
p.endpoint_id AS participant_id,
pe.event_param
FROM
conferences c
LEFT JOIN
conference_events ce ON c.conference_id = ce.conference_id
LEFT JOIN
participants p ON c.conference_id = p.conference_id
LEFT JOIN
participant_events pe ON p.endpoint_id = pe.participant_id
WHERE
c.conference_id = '%s'
AND (pe.time >= '%s 00:00:00' AND pe.time <= '%s 23:59:59')
UNION
SELECT
ce.time AS event_time,
c.conference_id,
c.conference_name,
c.conference_host,
ce.loglevel,
ce.conference_event AS event_type,
NULL AS participant_id,
ce.conference_param AS event_param
FROM
conferences c
LEFT JOIN
conference_events ce ON c.conference_id = ce.conference_id
WHERE
c.conference_id = '%s'
AND (event_time >= '%s 00:00:00' AND event_time <= '%s 23:59:59')
ORDER BY
pe.time;",
// search for a conference by its name for a time period (if given)
'conference_by_name' => "
SELECT
pe.time,
c.conference_id,
c.conference_name,
c.conference_host,
pe.loglevel,
pe.event_type,
p.endpoint_id AS participant_id,
pe.event_param
FROM
conferences c
LEFT JOIN
conference_events ce ON c.conference_id = ce.conference_id
LEFT JOIN
participants p ON c.conference_id = p.conference_id
LEFT JOIN
participant_events pe ON p.endpoint_id = pe.participant_id
WHERE
c.conference_name = '%s'
AND (pe.time >= '%s 00:00:00' AND pe.time <= '%s 23:59:59')
UNION
SELECT
ce.time AS event_time,
c.conference_id,
c.conference_name,
c.conference_host,
ce.loglevel,
ce.conference_event AS event_type,
NULL AS participant_id,
ce.conference_param AS event_param
FROM
conferences c
LEFT JOIN
conference_events ce ON c.conference_id = ce.conference_id
WHERE
c.conference_name = '%s'
AND (event_time >= '%s 00:00:00' AND event_time <= '%s 23:59:59')
ORDER BY
pe.time;",
// list all participants
'participants_all' => "
SELECT DISTINCT
p.jitsi_component, p.endpoint_id, p.conference_id
FROM
participants p
JOIN
participant_events pe ON p.endpoint_id = pe.participant_id
WHERE
pe.time >= '%s 00:00:00' AND pe.time <= '%s 23:59:59'
ORDER BY p.id;",
// list conferences where participant ID (endpoint_id) is found
'conference_by_participant_id' => "
SELECT
pe.time,
c.conference_id,
c.conference_name,
c.conference_host,
pe.loglevel,
pe.event_type,
p.endpoint_id AS participant_id,
pe.event_param
FROM
conferences c
LEFT JOIN
conference_events ce ON c.conference_id = ce.conference_id
LEFT JOIN
participants p ON c.conference_id = p.conference_id
LEFT JOIN
participant_events pe ON p.endpoint_id = pe.participant_id
WHERE
p.endpoint_id = '%s'
AND (pe.time >= '%s 00:00:00' AND pe.time <= '%s 23:59:59')
UNION
SELECT
ce.time AS event_time,
c.conference_id,
c.conference_name,
c.conference_host,
ce.loglevel,
ce.conference_event AS event_type,
NULL AS participant_id,
ce.conference_param AS event_param
FROM
conferences c
LEFT JOIN
conference_events ce ON c.conference_id = ce.conference_id
WHERE
participant_id = '%s'
AND (event_time >= '%s 00:00:00' AND event_time <= '%s 23:59:59')
ORDER BY
pe.time;",
// list conferences where participant name (stats_id) is found
'participant_by_stats_id' => "
SELECT
pe.time,
c.conference_id,
c.conference_name,
c.conference_host,
pe.loglevel,
pe.event_type,
p.endpoint_id AS participant_id,
pe.event_param
FROM
conferences c
LEFT JOIN
conference_events ce ON c.conference_id = ce.conference_id
LEFT JOIN
participants p ON c.conference_id = p.conference_id
LEFT JOIN
participant_events pe ON p.endpoint_id = pe.participant_id
WHERE
pe.event_type = 'stats_id' AND pe.event_param LIKE '%%%s%%'
AND (pe.time >= '%s 00:00:00' AND pe.time <= '%s 23:59:59')
UNION
SELECT
ce.time AS event_time,
c.conference_id,
c.conference_name,
c.conference_host,
ce.loglevel,
ce.conference_event AS event_type,
NULL AS participant_id,
ce.conference_param AS event_param
FROM
conferences c
LEFT JOIN
conference_events ce ON c.conference_id = ce.conference_id
WHERE
event_type = 'stats_id' AND event_param LIKE '%%%s%%'
AND (event_time >= '%s 00:00:00' AND event_time <= '%s 23:59:59')
ORDER BY
pe.time;",
// list conferences where participant IP is found
'participant_by_ip' => "
SELECT
pe.time,
c.conference_id,
c.conference_name,
c.conference_host,
pe.loglevel,
pe.event_type,
p.endpoint_id AS participant_id,
pe.event_param
FROM
conferences c
LEFT JOIN
conference_events ce ON c.conference_id = ce.conference_id
LEFT JOIN
participants p ON c.conference_id = p.conference_id
LEFT JOIN
participant_events pe ON p.endpoint_id = pe.participant_id
WHERE
pe.event_type = 'pair selected' AND pe.event_param = '%s'
AND (pe.time >= '%s 00:00:00' AND pe.time <= '%s 23:59:59')
UNION
SELECT
ce.time AS event_time,
c.conference_id,
c.conference_name,
c.conference_host,
ce.loglevel,
ce.conference_event AS event_type,
NULL AS participant_id,
ce.conference_param AS event_param
FROM
conferences c
LEFT JOIN
conference_events ce ON c.conference_id = ce.conference_id
WHERE
event_type = 'pair selected' AND event_param = '%s'
AND (event_time >= '%s 00:00:00' AND event_time <= '%s 23:59:59')
ORDER BY
pe.time;",
// list of jitsi component events
'jitsi_components' => "
SELECT jitsi_component, loglevel, time, component_id, event_type, event_param
FROM
jitsi_components
WHERE
jitsi_component = %s
AND
component_id = %s
AND
(time >= '%s 00:00:00' AND time <= '%s 23:59:59')
ORDER BY
time;",
];
?>

Some files were not shown because too many files have changed in this diff Show More