Compare commits
189 Commits
Author | SHA1 | Date |
---|---|---|
Yasen Pramatarov | e9325ee57b | |
Yasen Pramatarov | 27c2682c3f | |
Yasen Pramatarov | be8f008eb4 | |
Yasen Pramatarov | 9bfb79d036 | |
Yasen Pramatarov | b3e3a78ed9 | |
Yasen Pramatarov | 2bf3a423f4 | |
Yasen Pramatarov | 8840efebdb | |
Yasen Pramatarov | fee54aa827 | |
Yasen Pramatarov | 36f287e169 | |
Yasen Pramatarov | 9b45d5df9e | |
Yasen Pramatarov | 73d429647c | |
Yasen Pramatarov | ed9ce2bb07 | |
Yasen Pramatarov | daf41efa4c | |
Yasen Pramatarov | d5bc6a3c8d | |
Yasen Pramatarov | cada9dd67c | |
Yasen Pramatarov | f3457d5240 | |
Yasen Pramatarov | e76c78fde4 | |
Yasen Pramatarov | 15d5626b20 | |
Yasen Pramatarov | 202d90d4fc | |
Yasen Pramatarov | 7e09815835 | |
Yasen Pramatarov | 0318670bce | |
Yasen Pramatarov | 6b4f344bfd | |
Yasen Pramatarov | 1d706803b1 | |
Yasen Pramatarov | 88a3907d29 | |
Yasen Pramatarov | 71c636f297 | |
Yasen Pramatarov | c04fcb7d42 | |
Yasen Pramatarov | 4b1ab93474 | |
Yasen Pramatarov | b81be03c4f | |
Yasen Pramatarov | 1756ee71cd | |
Yasen Pramatarov | 537486d591 | |
Yasen Pramatarov | 865d21e2aa | |
Yasen Pramatarov | ef698c4fa7 | |
Yasen Pramatarov | 9fcaa76644 | |
Yasen Pramatarov | 0b9e1118b8 | |
Yasen Pramatarov | d82219eea0 | |
Yasen Pramatarov | 37c5bdb4b4 | |
Yasen Pramatarov | f4a64b6887 | |
Yasen Pramatarov | b3f642c02b | |
Yasen Pramatarov | fbe646823d | |
Yasen Pramatarov | 9c187c9550 | |
Yasen Pramatarov | eb8104595e | |
Yasen Pramatarov | 2d8bc53195 | |
Yasen Pramatarov | 1468843cac | |
Yasen Pramatarov | d0ef53a176 | |
Yasen Pramatarov | 0791a4c2c9 | |
Yasen Pramatarov | 4f0601cff7 | |
Yasen Pramatarov | 4e19a1c571 | |
Yasen Pramatarov | 784532c44d | |
Yasen Pramatarov | 4e827e62f0 | |
Yasen Pramatarov | 8bb2e8c838 | |
Yasen Pramatarov | ff2bc61bda | |
Yasen Pramatarov | b6c42fd4ec | |
Yasen Pramatarov | 37b0a289b2 | |
Yasen Pramatarov | e110619835 | |
Yasen Pramatarov | b72d4ea791 | |
Yasen Pramatarov | 37398b5986 | |
Yasen Pramatarov | e1888cce8a | |
Yasen Pramatarov | a585bdaaff | |
Yasen Pramatarov | 24c5870d33 | |
Yasen Pramatarov | ed2a058c12 | |
Yasen Pramatarov | ede9ecc7b6 | |
Yasen Pramatarov | e5d1324126 | |
Yasen Pramatarov | 6cc1efff15 | |
Yasen Pramatarov | ff2a96119e | |
Yasen Pramatarov | eabbd67b9a | |
Yasen Pramatarov | 950ea8ff95 | |
Yasen Pramatarov | 1acb7126ec | |
Yasen Pramatarov | 6d07744ab7 | |
Yasen Pramatarov | 72b1442b77 | |
Yasen Pramatarov | 88e77f71ef | |
Yasen Pramatarov | 847dc280d3 | |
Yasen Pramatarov | 4786b0d1f7 | |
Yasen Pramatarov | be0cd48c01 | |
Yasen Pramatarov | 527da25cdc | |
Yasen Pramatarov | 33dfbcdeea | |
Yasen Pramatarov | 1c710bef35 | |
Yasen Pramatarov | f6362bfdc1 | |
Yasen Pramatarov | 5d06a7222c | |
Yasen Pramatarov | 76e061e1cb | |
Yasen Pramatarov | 2478f84e85 | |
Yasen Pramatarov | 8a2f082b09 | |
Yasen Pramatarov | 42b42e6fd2 | |
Yasen Pramatarov | 3170d87934 | |
Yasen Pramatarov | 6ec0981b0a | |
Yasen Pramatarov | e0eee38726 | |
Yasen Pramatarov | 7cc8da562d | |
Yasen Pramatarov | 172a545acf | |
Yasen Pramatarov | 318f5356c0 | |
Yasen Pramatarov | bed55c909d | |
Yasen Pramatarov | 8da45a06d0 | |
Yasen Pramatarov | d2154fa63c | |
Yasen Pramatarov | e195b653b1 | |
Yasen Pramatarov | 70163e1c5e | |
Yasen Pramatarov | 1efb3b6a17 | |
Yasen Pramatarov | 59c6706505 | |
Yasen Pramatarov | 6a48b54320 | |
Yasen Pramatarov | da5e5ec4ae | |
Yasen Pramatarov | 573df0fe4f | |
Yasen Pramatarov | fd949f6f4f | |
Yasen Pramatarov | 84664f8b5f | |
Yasen Pramatarov | d2f4850d28 | |
Yasen Pramatarov | f0f7d5b2d3 | |
Yasen Pramatarov | 06a534c2da | |
Yasen Pramatarov | 189d30bad5 | |
Yasen Pramatarov | 993474a754 | |
Yasen Pramatarov | d64fc5cf56 | |
Yasen Pramatarov | 2d4205916b | |
Yasen Pramatarov | de7133be3d | |
Yasen Pramatarov | 48a0cf9e86 | |
Yasen Pramatarov | dc9462260d | |
Yasen Pramatarov | b60208bea7 | |
Yasen Pramatarov | 7d85c9181d | |
Yasen Pramatarov | f88fc0f819 | |
Yasen Pramatarov | 54d6ce2ec4 | |
Yasen Pramatarov | 0bab284e99 | |
Yasen Pramatarov | cf7d417193 | |
Yasen Pramatarov | 1780233778 | |
Yasen Pramatarov | 666f3ca98b | |
Yasen Pramatarov | 1ba32d86f7 | |
Yasen Pramatarov | d2213ca3e9 | |
Yasen Pramatarov | c83baeee2f | |
Yasen Pramatarov | cd7b78a2e5 | |
Yasen Pramatarov | d854473a06 | |
Yasen Pramatarov | bf197ae96b | |
Yasen Pramatarov | 2bbc4af068 | |
Yasen Pramatarov | 1cda74cd50 | |
Yasen Pramatarov | 3abafe2de7 | |
Yasen Pramatarov | 3a5eed933e | |
Yasen Pramatarov | 909fbc2626 | |
Yasen Pramatarov | 09667920a2 | |
Yasen Pramatarov | ea4b85ddf7 | |
Yasen Pramatarov | 8ca9643fc2 | |
Yasen Pramatarov | b02d72b29d | |
Yasen Pramatarov | e84c880289 | |
Yasen Pramatarov | 1df19d9609 | |
Yasen Pramatarov | a6cf2fe99b | |
Yasen Pramatarov | f796e100f8 | |
Yasen Pramatarov | 8eed6afd46 | |
Yasen Pramatarov | 2cdfbe6e86 | |
Yasen Pramatarov | 9d3a4557c1 | |
Yasen Pramatarov | acd5ff0a23 | |
Yasen Pramatarov | 9a3024cfa6 | |
Yasen Pramatarov | 20d9dc0e73 | |
Yasen Pramatarov | 8e123cf5f2 | |
Yasen Pramatarov | 8765fe22c0 | |
Yasen Pramatarov | e6d6a10795 | |
Yasen Pramatarov | aeb837fee5 | |
Yasen Pramatarov | 91aac20998 | |
Yasen Pramatarov | c915bcba9e | |
Yasen Pramatarov | 0f868f07e2 | |
Yasen Pramatarov | 3943ba49b6 | |
Yasen Pramatarov | 8a4816a6ae | |
Yasen Pramatarov | 4b783a6b63 | |
Yasen Pramatarov | 6191d44944 | |
Yasen Pramatarov | 838b8ea9f2 | |
Yasen Pramatarov | 15c97859a4 | |
Yasen Pramatarov | 6199dad2c6 | |
Yasen Pramatarov | cb040ee408 | |
Yasen Pramatarov | 2aaf2ac03f | |
Yasen Pramatarov | 27304513d8 | |
Yasen Pramatarov | 0cfa9d811e | |
Yasen Pramatarov | 54b998a1a8 | |
Yasen Pramatarov | 77e674bab5 | |
Yasen Pramatarov | b6fc4a995d | |
Yasen Pramatarov | ca95f3c27e | |
Yasen Pramatarov | 22e502c90c | |
Yasen Pramatarov | ae7929b4e1 | |
Yasen Pramatarov | 80454de3f2 | |
Yasen Pramatarov | 8455a98ec5 | |
Yasen Pramatarov | 5d240b1dd8 | |
Yasen Pramatarov | 06ffde67f4 | |
Yasen Pramatarov | 8bbe4b6274 | |
Yasen Pramatarov | 001e6c5be7 | |
Yasen Pramatarov | 55b53ef30a | |
Yasen Pramatarov | c011321403 | |
Yasen Pramatarov | 42f9738128 | |
Yasen Pramatarov | c0c072884f | |
Yasen Pramatarov | 54ddc6577e | |
Yasen Pramatarov | d9aaf97b81 | |
Yasen Pramatarov | f76fd03794 | |
Yasen Pramatarov | 3e0729c6cc | |
Yasen Pramatarov | 16b7627b60 | |
Yasen Pramatarov | c6e52df33a | |
Yasen Pramatarov | 7d459cf508 | |
Yasen Pramatarov | 350ba053e8 | |
Yasen Pramatarov | efd7cd74a2 | |
Yasen Pramatarov | c39e53906f | |
Yasen Pramatarov | 5463ea0cee | |
Yasen Pramatarov | ba9d14f5d1 |
70
CHANGELOG.md
70
CHANGELOG.md
|
@ -7,16 +7,78 @@ All notable changes to this project will be documented in this file.
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
#### Links
|
#### Links
|
||||||
- upstream: https://code.lindeas.com/lindeas/jilo-web/compare/v0.1.1...HEAD
|
- upstream: https://code.lindeas.com/lindeas/jilo-web/compare/v0.2.1...HEAD
|
||||||
- codeberg: https://codeberg.org/lindeas/jilo-web/compare/v0.1.1...HEAD
|
- codeberg: https://codeberg.org/lindeas/jilo-web/compare/v0.2.1...HEAD
|
||||||
- github: https://github.com/lindeas/jilo-web/compare/v0.1.1...HEAD
|
- github: https://github.com/lindeas/jilo-web/compare/v0.2.1...HEAD
|
||||||
- gitlab: https://gitlab.com/lindeas/jilo-web/-/compare/v0.1.1...HEAD
|
- gitlab: https://gitlab.com/lindeas/jilo-web/-/compare/v0.2.1...HEAD
|
||||||
|
|
||||||
### Added
|
### 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
|
### Changed
|
||||||
|
- Jitsi platforms config moved from file to SQLite database
|
||||||
|
- Left sidebar menu items reordered
|
||||||
|
|
||||||
### Fixed
|
### 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
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
28
README.md
28
README.md
|
@ -26,39 +26,53 @@ To see a demo install, go to https://work.lindeas.com/jilo-web-demo/
|
||||||
|
|
||||||
## version
|
## version
|
||||||
|
|
||||||
Current version: **0.1.1** released on **2024-07-25**
|
Current version: **0.2.1** released on **2024-10-17**
|
||||||
|
|
||||||
## license
|
## license
|
||||||
|
|
||||||
This project is licensed under the GNU General Public License version 2 (GPL-2.0). See LICENSE file.
|
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 bootstrap-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.
|
||||||
|
|
||||||
## requirements
|
## requirements
|
||||||
|
|
||||||
- web server (deb: apache | nginx)
|
- web server (deb: apache | nginx)
|
||||||
- php support in the web server (deb: php-fpm | libapache2-mod-php)
|
- 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
|
- pdo and pdo_sqlite support in php (deb: php-db, php-sqlite3) uncomment in php.ini: ;extension=pdo_sqlite
|
||||||
|
- php-curl module
|
||||||
|
|
||||||
## installation
|
## installation
|
||||||
|
|
||||||
You can install it in the following ways:
|
You can install it in the following ways:
|
||||||
|
|
||||||
- download the latest release from the **"Releases"** section here
|
- 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**:
|
- clone the **git repo**:
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/lindeas/jilo-web.git
|
git clone https://github.com/lindeas/jilo-web.git
|
||||||
cd jilo
|
cd jilo-web
|
||||||
|
./install.sh
|
||||||
```
|
```
|
||||||
- DEB and RPM packages are planned, but still unavailable
|
- DEB and RPM packages are planned, but not yet available. There are experimental build scripts in "packaging" folder.
|
||||||
|
|
||||||
## config
|
## 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
|
- edit jilo-web.conf.php and set all the variables correctly
|
||||||
- "database" is the sqlite db file for jilo-web itself, create it with `cat jilo-web.schema | sqlite3 jilo-web.db`
|
- "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
|
||||||
|
```
|
||||||
- "jilo_database" is the sqlite db file for jilo, with data from the Jitsi logs
|
- "jilo_database" is the sqlite db file for jilo, with data from the Jitsi logs
|
||||||
|
|
||||||
## database
|
## database
|
||||||
|
|
||||||
The database is an SQLite file. You also need the sqlite db from jilo (mysql/mariadb support is planned, but still unavailable).
|
The database is an SQLite file. You also need the sqlite db from jilo (mysql/mariadb support is planned, but not yet available).
|
||||||
|
|
27
TODO.md
27
TODO.md
|
@ -1,27 +0,0 @@
|
||||||
# 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
|
|
|
@ -0,0 +1,291 @@
|
||||||
|
<?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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,69 @@
|
||||||
|
<?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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,335 @@
|
||||||
|
<?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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,91 @@
|
||||||
|
<?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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,79 @@
|
||||||
|
<?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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,62 @@
|
||||||
|
<?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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,315 @@
|
||||||
|
<?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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,94 @@
|
||||||
|
<?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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,38 @@
|
||||||
|
<?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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,327 @@
|
||||||
|
<?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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?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',
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,66 @@
|
||||||
|
<?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;
|
||||||
|
}
|
||||||
|
?>
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,127 @@
|
||||||
|
<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>
|
|
@ -0,0 +1,22 @@
|
||||||
|
<?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);
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,81 @@
|
||||||
|
|
||||||
|
<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>
|
|
@ -0,0 +1,48 @@
|
||||||
|
<?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;
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,39 @@
|
||||||
|
<?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
|
||||||
|
}
|
||||||
|
?>
|
|
@ -0,0 +1,51 @@
|
||||||
|
<?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']);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?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;
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,56 @@
|
||||||
|
<?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';
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,85 @@
|
||||||
|
<?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 <strong>' . $_REQUEST['name'] . '</strong>';
|
||||||
|
} elseif (isset($_REQUEST['id']) && $_REQUEST['id'] != '') {
|
||||||
|
$widget['title'] = 'Jitsi events for component ID <strong>' . $_REQUEST['id'] . '</strong>';
|
||||||
|
} else {
|
||||||
|
$widget['title'] = 'Jitsi events for <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';
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,149 @@
|
||||||
|
<?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';
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,166 @@
|
||||||
|
<?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';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,204 @@
|
||||||
|
<?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';
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,49 @@
|
||||||
|
<?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';
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
include '../app/templates/help-main.php';
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?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';
|
||||||
|
|
||||||
|
?>
|
|
@ -1,21 +1,19 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
require_once 'classes/database.php';
|
|
||||||
require 'classes/user.php';
|
|
||||||
|
|
||||||
// clear the global error var before login
|
// clear the global error var before login
|
||||||
unset($error);
|
unset($error);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$db = new Database($config['database']);
|
|
||||||
$user = new User($db);
|
// connect to database
|
||||||
|
$dbWeb = connectDB($config);
|
||||||
|
|
||||||
if ( $_SERVER['REQUEST_METHOD'] == 'POST' ) {
|
if ( $_SERVER['REQUEST_METHOD'] == 'POST' ) {
|
||||||
$username = $_POST['username'];
|
$username = $_POST['username'];
|
||||||
$password = $_POST['password'];
|
$password = $_POST['password'];
|
||||||
|
|
||||||
// login successful
|
// login successful
|
||||||
if ( $user->login($username, $password) ) {
|
if ( $userObject->login($username, $password) ) {
|
||||||
// if remember_me is checked, max out the session
|
// if remember_me is checked, max out the session
|
||||||
if (isset($_POST['remember_me'])) {
|
if (isset($_POST['remember_me'])) {
|
||||||
// 30*24*60*60 = 30 days
|
// 30*24*60*60 = 30 days
|
||||||
|
@ -31,18 +29,6 @@ try {
|
||||||
}
|
}
|
||||||
|
|
||||||
// set session lifetime and cookies
|
// set session lifetime and cookies
|
||||||
// FIXME: need to set this before session start (otherwise we need the separate cookie)
|
|
||||||
// ini_set('session.gc_maxlifetime', $gc_maxlifetime);
|
|
||||||
// session_set_cookie_params([
|
|
||||||
// 'lifetime' => $setcookie_lifetime,
|
|
||||||
// 'samesite' => 'Strict',
|
|
||||||
// 'httponly' => true,
|
|
||||||
// 'secure' => isset($_SERVER['HTTPS']),
|
|
||||||
// 'domain' => $config['domain'],
|
|
||||||
// 'path' => $config['folder']
|
|
||||||
// ]);
|
|
||||||
// session_start();
|
|
||||||
// FIXME we use separate cookie, because the above won't work
|
|
||||||
setcookie('username', $username, [
|
setcookie('username', $username, [
|
||||||
'expires' => $setcookie_lifetime,
|
'expires' => $setcookie_lifetime,
|
||||||
'path' => $config['folder'],
|
'path' => $config['folder'],
|
||||||
|
@ -54,20 +40,29 @@ try {
|
||||||
|
|
||||||
// redirect to index
|
// redirect to index
|
||||||
$_SESSION['notice'] = "Login successful";
|
$_SESSION['notice'] = "Login successful";
|
||||||
header('Location: index.php');
|
$user_id = $userObject->getUserId($username)[0]['id'];
|
||||||
|
$logObject->insertLog($user_id, "Login: User \"$username\" logged in. IP: $user_IP", 'user');
|
||||||
|
header('Location: ' . htmlspecialchars($app_root));
|
||||||
exit();
|
exit();
|
||||||
|
|
||||||
// login failed
|
// login failed
|
||||||
} else {
|
} else {
|
||||||
$_SESSION['error'] = "Login failed.";
|
$_SESSION['error'] = "Login failed.";
|
||||||
header('Location: index.php');
|
$user_id = $userObject->getUserId($username)[0]['id'];
|
||||||
|
$logObject->insertLog($user_id, "Login: Failed login attempt for user \"$username\". IP: $user_IP", 'user');
|
||||||
|
header('Location: ' . htmlspecialchars($app_root));
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$error = $e->getMessage();
|
$error = getError('There was an unexpected error. Please try again.', $e->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
include 'templates/form-login.php';
|
if (!empty($config['login_message'])) {
|
||||||
|
$notice = $config['login_message'];
|
||||||
|
include '../app/templates/block-message.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
include '../app/templates/form-login.php';
|
||||||
|
|
||||||
?>
|
?>
|
|
@ -0,0 +1,71 @@
|
||||||
|
<?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';
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,155 @@
|
||||||
|
<?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';
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,84 @@
|
||||||
|
<?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';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,48 @@
|
||||||
|
<?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';
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,52 @@
|
||||||
|
<?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';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -0,0 +1,39 @@
|
||||||
|
|
||||||
|
<!-- 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 } ?>
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?php if (isset($error)) { ?>
|
||||||
|
<div class="error"><?= $error ?></div>
|
||||||
|
<?php } ?>
|
||||||
|
|
||||||
|
<?php if (isset($notice)) { ?>
|
||||||
|
<div class="notice"><?= $notice ?></div>
|
||||||
|
<?php } ?>
|
|
@ -0,0 +1,29 @@
|
||||||
|
|
||||||
|
<!-- 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 -->
|
|
@ -0,0 +1,72 @@
|
||||||
|
|
||||||
|
<!-- 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" -->
|
|
@ -0,0 +1,50 @@
|
||||||
|
|
||||||
|
<!-- 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" -->
|
|
@ -0,0 +1,32 @@
|
||||||
|
|
||||||
|
<!-- 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" -->
|
|
@ -0,0 +1,32 @@
|
||||||
|
|
||||||
|
<!-- 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" -->
|
|
@ -0,0 +1,68 @@
|
||||||
|
|
||||||
|
<!-- 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 -->
|
|
@ -0,0 +1,36 @@
|
||||||
|
|
||||||
|
<!-- 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" -->
|
|
@ -0,0 +1,22 @@
|
||||||
|
|
||||||
|
<!-- 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" -->
|
|
@ -0,0 +1,22 @@
|
||||||
|
|
||||||
|
<!-- 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" -->
|
|
@ -0,0 +1,127 @@
|
||||||
|
|
||||||
|
<!-- 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 <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' ?>
|
||||||
|
<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" -->
|
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
|
||||||
|
<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']) ?>" -->
|
|
@ -0,0 +1,94 @@
|
||||||
|
|
||||||
|
<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']) ?>" -->
|
|
@ -0,0 +1,20 @@
|
||||||
|
<!-- 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 /> <br />
|
||||||
|
<input type="submit" class="btn btn-primary" value="Login" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- /login form -->
|
|
@ -0,0 +1,15 @@
|
||||||
|
<!-- 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 /> <br />
|
||||||
|
<input type="submit" class="btn btn-primary" value="Register" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- /registration form -->
|
|
@ -0,0 +1,140 @@
|
||||||
|
|
||||||
|
<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>
|
|
@ -0,0 +1,53 @@
|
||||||
|
|
||||||
|
<!-- 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 -->
|
|
@ -0,0 +1,45 @@
|
||||||
|
|
||||||
|
<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']) ?>" -->
|
|
@ -0,0 +1,26 @@
|
||||||
|
|
||||||
|
<!-- 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 -->
|
|
@ -0,0 +1,57 @@
|
||||||
|
|
||||||
|
<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']) ?>" -->
|
|
@ -0,0 +1,23 @@
|
||||||
|
|
||||||
|
<?php if ($page !== 'login' && $page !== 'register') { ?>
|
||||||
|
<!-- /Main content -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php } ?>
|
||||||
|
<!-- Footer -->
|
||||||
|
<div id="footer">Jilo Web <?= htmlspecialchars($config['version']) ?> ©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>
|
|
@ -0,0 +1,39 @@
|
||||||
|
<!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>
|
|
@ -0,0 +1,46 @@
|
||||||
|
|
||||||
|
<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 <?= 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 -->
|
|
@ -0,0 +1,105 @@
|
||||||
|
<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')) ?> <?= 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">
|
|
@ -0,0 +1,201 @@
|
||||||
|
|
||||||
|
<!-- 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) ?> (<?= 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>
|
|
@ -0,0 +1,87 @@
|
||||||
|
|
||||||
|
<!-- 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']) ?> <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 -->
|
|
@ -0,0 +1,14 @@
|
||||||
|
|
||||||
|
<!-- 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>
|
|
@ -0,0 +1,10 @@
|
||||||
|
|
||||||
|
<!-- 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>
|
|
@ -0,0 +1,19 @@
|
||||||
|
|
||||||
|
<!-- 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>
|
|
@ -0,0 +1,60 @@
|
||||||
|
|
||||||
|
<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']) ?>" -->
|
|
@ -0,0 +1,86 @@
|
||||||
|
|
||||||
|
<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']) ?>" -->
|
15
config.nginx
15
config.nginx
|
@ -1,15 +0,0 @@
|
||||||
server {
|
|
||||||
listen 80;
|
|
||||||
server_name $DOMAIN;
|
|
||||||
|
|
||||||
root $INSTALL_DIR;
|
|
||||||
index index.php;
|
|
||||||
|
|
||||||
location / {
|
|
||||||
try_files \$uri \$uri/ /index.php?\$args;
|
|
||||||
}
|
|
||||||
|
|
||||||
location ~ /\.ht {
|
|
||||||
deny all;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
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]
|
|
@ -4,4 +4,9 @@
|
||||||
|
|
||||||
CustomLog \${APACHE_LOG_DIR}/jilo-web_access.log combined
|
CustomLog \${APACHE_LOG_DIR}/jilo-web_access.log combined
|
||||||
ErrorLog \${APACHE_LOG_DIR}/jilo-web_error.log
|
ErrorLog \${APACHE_LOG_DIR}/jilo-web_error.log
|
||||||
|
|
||||||
|
<Directory $INSTALL_DIR>
|
||||||
|
AllowOverride All
|
||||||
|
</Directory>
|
||||||
|
|
||||||
</VirtualHost>
|
</VirtualHost>
|
|
@ -0,0 +1,22 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ if [ "$EUID" -ne 0 ] && [ -z "$SUDO_USER" ]; then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
VERSION=`grep version jilo-web.conf.php | cut -d "'" -f 4`
|
VERSION=`grep version ../app/config/jilo-web.conf.php | cut -d "'" -f 4`
|
||||||
|
|
||||||
# main install function
|
# main install function
|
||||||
function install() {
|
function install() {
|
||||||
|
@ -20,23 +20,24 @@ function install() {
|
||||||
WEB_DIR=${WEB_DIR:-jilo-web}
|
WEB_DIR=${WEB_DIR:-jilo-web}
|
||||||
|
|
||||||
INSTALL_DIR="/opt/jilo-web/public_html"
|
INSTALL_DIR="/opt/jilo-web/public_html"
|
||||||
|
APP_DIR="/opt/jilo-web/app"
|
||||||
DOC_DIR="/opt/jilo-web/doc"
|
DOC_DIR="/opt/jilo-web/doc"
|
||||||
ETC_DIR="/opt/jilo-web/etc"
|
ETC_DIR="/opt/jilo-web/etc"
|
||||||
|
|
||||||
mkdir -p $INSTALL_DIR
|
mkdir -p $INSTALL_DIR
|
||||||
cp -r ./public_html/* $INSTALL_DIR
|
cp -r ../public_html/* $INSTALL_DIR
|
||||||
|
|
||||||
mkdir -p $DOC_DIR
|
mkdir -p $DOC_DIR
|
||||||
cp CHANGELOG.md $DOC_DIR
|
cp ../CHANGELOG.md $DOC_DIR
|
||||||
cp LICENSE $DOC_DIR
|
cp ../LICENSE $DOC_DIR
|
||||||
cp README.md $DOC_DIR
|
cp ../README.md $DOC_DIR
|
||||||
cp TODO.md $DOC_DIR
|
cp ../TODO.md $DOC_DIR
|
||||||
cp config.apache $DOC_DIR
|
cp ../license-bootstrap $DOC_DIR
|
||||||
cp config.nginx $DOC_DIR
|
cp ../license-jquery $DOC_DIR
|
||||||
|
cp -r ../doc/ $DOC_DIR
|
||||||
|
|
||||||
mkdir -p $ETC_DIR
|
mkdir -p $ETC_DIR
|
||||||
cp jilo-web.conf.php $ETC_DIR
|
cp ../app/config/jilo-web.conf.php $ETC_DIR
|
||||||
cp jilo-web.schema $ETC_DIR
|
|
||||||
|
|
||||||
#FIXME
|
#FIXME
|
||||||
#mkdir -p "jilo-web-$VERSION/usr/share/man/man8"
|
#mkdir -p "jilo-web-$VERSION/usr/share/man/man8"
|
|
@ -0,0 +1,5 @@
|
||||||
|
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');
|
|
@ -0,0 +1,13 @@
|
||||||
|
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');
|
|
@ -0,0 +1,65 @@
|
||||||
|
|
||||||
|
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)
|
||||||
|
);
|
|
@ -1,5 +1,5 @@
|
||||||
Package: jilo-web
|
Package: jilo-web
|
||||||
Version: 0.1.1
|
Version: 0.2.1
|
||||||
Section: web
|
Section: web
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Architecture: all
|
Architecture: all
|
|
@ -1,4 +1,4 @@
|
||||||
.TH JILO-WEB "8" "July 2024" "jilo-web 0.1.1"
|
.TH JILO-WEB "8" "October 2024" "jilo-web 0.2.1"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
jilo-web \- PHP frontent to jilo (jitsi logs observer) database.
|
jilo-web \- PHP frontent to jilo (jitsi logs observer) database.
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
|
@ -17,7 +17,7 @@ Written and maintained by Yasen Pramatarov <yasen@lindeas.com>
|
||||||
https://lindeas.com/jilo
|
https://lindeas.com/jilo
|
||||||
|
|
||||||
.SH VERSION
|
.SH VERSION
|
||||||
0.1.1
|
0.2.1
|
||||||
|
|
||||||
.SH SEE ALSO
|
.SH SEE ALSO
|
||||||
jilo(8), jilo-cli(8)
|
jilo(8), jilo-cli(8)
|
|
@ -1,5 +1,5 @@
|
||||||
Name: jilo-web
|
Name: jilo-web
|
||||||
Version: 0.1.1
|
Version: 0.2.1
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: Jitsi logs web observer
|
Summary: Jitsi logs web observer
|
||||||
|
|
||||||
|
@ -54,6 +54,10 @@ cp %{sourcedir}/man-jilo.8 %{buildroot}/usr/share/man/man8/%{name}.8
|
||||||
/usr/share/man/man8/%{name}.8.gz
|
/usr/share/man/man8/%{name}.8.gz
|
||||||
|
|
||||||
%changelog
|
%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
|
* Thu Jul 25 2024 Yasen Pramatarov <yasen@lindeas.com> 0.1.1
|
||||||
- Build of upstream v0.1.1
|
- Build of upstream v0.1.1
|
||||||
* Wed Jul 12 2024 Yasen Pramatarov <yasen@lindeas.com> 0.1
|
* Wed Jul 12 2024 Yasen Pramatarov <yasen@lindeas.com> 0.1
|
|
@ -1,11 +0,0 @@
|
||||||
<?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',
|
|
||||||
];
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1,5 +0,0 @@
|
||||||
CREATE TABLE users (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
username TEXT NOT NULL UNIQUE,
|
|
||||||
password TEXT NOT NULL
|
|
||||||
);
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
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.
|
|
@ -0,0 +1,20 @@
|
||||||
|
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.
|
|
@ -1,40 +0,0 @@
|
||||||
<?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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1,90 +0,0 @@
|
||||||
<?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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1,34 +0,0 @@
|
||||||
<?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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1,115 +0,0 @@
|
||||||
<?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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue