Merge branch 'master' into use_javascript_for_phrase_edit

This commit is contained in:
FusionPBX
2025-03-13 14:20:09 -06:00
committed by GitHub
403 changed files with 15352 additions and 4178 deletions
+295 -65
View File
@@ -1,93 +1,323 @@
<?php
/*
FusionPBX
Version: MPL 1.1
FusionPBX
Version: MPL 1.1
The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/
The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.
The Original Code is FusionPBX
The Original Code is FusionPBX
The Initial Developer of the Original Code is
Mark J Crane <markjcrane@fusionpbx.com>
Portions created by the Initial Developer are Copyright (C) 2008-2024
the Initial Developer. All Rights Reserved.
The Initial Developer of the Original Code is
Mark J Crane <markjcrane@fusionpbx.com>
Portions created by the Initial Developer are Copyright (C) 2008-2024
the Initial Developer. All Rights Reserved.
Contributor(s):
Mark J Crane <markjcrane@fusionpbx.com>
*/
Contributor(s):
Mark J Crane <markjcrane@fusionpbx.com>
*/
/**
* Auto Loader class
* Searches for project files when a class is required. Debugging mode can be set using:
* - export DEBUG=1
* OR
* - debug=true is appended to the url
*/
class auto_loader {
public function __construct() {
const FILE = 'autoloader_cache.php';
const CACHE_KEY = 'autoloader_classes';
private $classes;
/**
* Tracks the APCu extension for caching to RAM drive across requests
* @var bool
*/
private $apcu_enabled;
/**
* Cache path and file name
* @var string
*/
private static $cache_file = null;
public function __construct($project_path = '') {
//set if we can use RAM cache
$this->apcu_enabled = function_exists('apcu_enabled') && apcu_enabled();
//set cache location
self::$cache_file = sys_get_temp_dir() . DIRECTORY_SEPARATOR . self::FILE;
//classes must be loaded before this object is registered
if (!$this->load_cache()) {
//cache miss so load them
$this->reload_classes($project_path);
//update the cache after loading classes array
$this->update_cache();
}
//register this object to load any unknown classes
spl_autoload_register(array($this, 'loader'));
}
public static function autoload_search($array) : string {
if (!is_array($array) && count($path) != 0) {
return '';
public function update_cache(string $file = ''): bool {
//guard against writing an empty file
if (empty($this->classes)) {
return false;
}
foreach($array as $path) {
if (is_array($path) && count($path) != 0) {
foreach($path as $sub_path) {
if (!empty($sub_path) && file_exists($sub_path)) {
return $sub_path;
}
}
}
elseif (!empty($path) && file_exists($path)) {
return $path;
}
//update RAM cache when available
if ($this->apcu_enabled) {
$success = apcu_store(self::CACHE_KEY, $this->classes);
//do not save to drive when we are using apcu
if ($success) return true;
}
return '';
//ensure we have somewhere to put the file
if (empty($file)) {
$file = self::$cache_file;
}
//export the classes array using PHP engine
$data = var_export($this->classes, true);
//put the array in a form that it can be loaded directly to an array
$result = file_put_contents($file, "<?php\n return " . $data . ";\n");
if ($result !== false) {
return true;
}
//file failed to save - send error to syslog when debugging
$error_array = error_get_last();
self::log(LOG_WARNING, $error_array['message'] ?? '');
return false;
}
private function loader($class_name) : bool {
public function load_cache(string $file = ''): bool {
$this->classes = [];
//use apcu when available
if ($this->apcu_enabled && apcu_exists(self::CACHE_KEY)) {
$this->classes = apcu_fetch(self::CACHE_KEY, $success);
if ($success) return true;
}
//use a standard file
if (empty($file)) {
$file = self::$cache_file;
}
//use PHP engine to parse it
if (file_exists($file)) {
$this->classes = include $file;
}
//catch edge case of first time using apcu cache
if ($this->apcu_enabled) {
apcu_store(self::CACHE_KEY, $this->classes);
}
//return true when we have classes and false if the array is still empty
return !empty($this->classes);
}
public function reload_classes($project_path = '') {
//set project path using magic dir constant
if (empty($project_path)) {
$project_path = dirname(__DIR__, 2);
}
//build the array of all locations for classes in specific order
$search_path = [
$project_path . '/resources/interfaces/*.php',
$project_path . '/resources/traits/*.php',
$project_path . '/resources/classes/*.php',
$project_path . '/*/*/resources/interfaces/*.php',
$project_path . '/*/*/resources/traits/*.php',
$project_path . '/*/*/resources/classes/*.php',
$project_path . '/core/authentication/resources/classes/plugins/*.php',
];
//get all php files for each path
$files = [];
foreach ($search_path as $path) {
$files = array_merge($files, glob($path));
}
//reset the current array
$this->classes = [];
//store PHP language declared classes, interfaces, and traits
$current_classes = get_declared_classes();
$current_interfaces = get_declared_interfaces();
$current_traits = get_declared_traits();
//store the class name (key) and the path (value)
foreach ($files as $file) {
//include the new class
try {
include_once $file;
} catch (Exception $e) {
//report the error
self::log(LOG_ERR, "Exception while trying to include file '$file': " . $e->getMessage());
continue;
}
//get the new classes
$new_classes = get_declared_classes();
$new_interfaces = get_declared_interfaces();
$new_traits = get_declared_traits();
//check for a new class
$classes = array_diff($new_classes, $current_classes);
if (!empty($classes)) {
foreach ($classes as $class) {
$this->classes[$class] = $file;
}
//overwrite previous array with new values
$current_classes = $new_classes;
}
//check for a new interface
$interfaces = array_diff($new_interfaces, $current_interfaces);
if (!empty($interfaces)) {
foreach ($interfaces as $interface) {
$this->classes[$interface] = $file;
}
//overwrite previous array with new values
$current_interfaces = $new_interfaces;
}
//check for a new trait
$traits = array_diff($new_traits, $current_traits);
if (!empty($traits)) {
foreach ($traits as $trait) {
$this->classes[$trait] = $file;
}
//overwrite previous array with new values
$current_traits = $new_traits;
}
}
}
/**
* The loader is set to private because only the PHP engine should be calling this method
* @param string $class_name The class name that needs to be loaded
* @return bool True if the class is loaded or false when the class is not found
* @access private
*/
private function loader($class_name): bool {
//sanitize the class name
$class_name = preg_replace('[^a-zA-Z0-9_]', '', $class_name);
//use glob for a more extensive search for the classes (note: GLOB_BRACE doesn't work on some systems)
if (!class_exists($class_name)) {
//set project path using magic dir constant
$project_path = dirname(__DIR__, 2);
//find the path using the class_name as the key in the classes array
if (isset($this->classes[$class_name])) {
//include the class or interface
include_once $this->classes[$class_name];
//build the search path array
$search_path[] = glob($project_path . "/resources/classes/".$class_name.".php");
$search_path[] = glob($project_path . "/resources/interfaces/".$class_name.".php");
$search_path[] = glob($project_path . "/resources/traits/".$class_name.".php");
$search_path[] = glob($project_path . "/*/*/resources/classes/".$class_name.".php");
$search_path[] = glob($project_path . "/*/*/resources/interfaces/".$class_name.".php");
$search_path[] = glob($project_path . "/*/*/resources/traits/".$class_name.".php");
//find the path
$path = self::autoload_search($search_path);
if (!empty($path)) {
//send to syslog
if (!empty($_REQUEST['debug']) && $_REQUEST['debug'] == 'true') {
openlog("PHP", LOG_PID | LOG_PERROR, LOG_LOCAL0);
syslog(LOG_WARNING, "[php][autoloader] name: ".$class_name.", path: ".$path.", line: ".__line__);
closelog();
}
//include the class or interface
include $path;
//return boolean
return true;
}
//return boolean
return true;
}
//Smarty has it's own autoloader so reject the request
if ($class_name === 'Smarty_Autoloader') {
return false;
}
//cache miss
self::log(LOG_WARNING, "class '$class_name' not found in cache");
//set project path using magic dir constant
$project_path = dirname(__DIR__, 2);
//build the search path array
$search_path[] = glob($project_path . "/resources/interfaces/" . $class_name . ".php");
$search_path[] = glob($project_path . "/resources/traits/" . $class_name . ".php");
$search_path[] = glob($project_path . "/resources/classes/" . $class_name . ".php");
$search_path[] = glob($project_path . "/*/*/resources/interfaces/" . $class_name . ".php");
$search_path[] = glob($project_path . "/*/*/resources/traits/" . $class_name . ".php");
$search_path[] = glob($project_path . "/*/*/resources/classes/" . $class_name . ".php");
//collapse all entries to only the matched entry
$matches = array_filter($search_path);
if (!empty($matches)) {
$path = array_pop($matches)[0];
//include the class, interface, or trait
include_once $path;
//inject the class in to the array
$this->classes[$class_name] = $path;
//update the cache with new classes
$this->update_cache();
//return boolean
return true;
}
//send to syslog when debugging
self::log(LOG_ERR, "class '$class_name' not found name");
//return boolean
return false;
}
}
?>
/**
* Returns a list of classes loaded by the auto_loader. If no classes have been loaded an empty array is returned.
* @return array List of classes loaded by the auto_loader or empty array
*/
public function get_class_list(): array {
if (!empty($this->classes)) {
return $this->classes;
}
return [];
}
public static function clear_cache(string $file = '') {
//check for apcu cache
if (function_exists('apcu_enabled') && apcu_enabled()) {
apcu_delete(self::CACHE_KEY);
}
//set default file
if (empty(self::$cache_file)) {
self::$cache_file = sys_get_temp_dir() . DIRECTORY_SEPARATOR . self::FILE;
}
//set file to clear
if (empty($file)) {
$file = self::$cache_file;
}
//remove the file when it exists
if (file_exists($file)) {
@unlink($file);
$error_array = error_get_last();
//send to syslog when debugging with either environment variable or debug in the url
self::log(LOG_WARNING, $error_array['message'] ?? '');
}
}
private static function log(int $level, string $message): void {
if (filter_var($_REQUEST['debug'] ?? false, FILTER_VALIDATE_BOOL) || filter_var(getenv('DEBUG') ?? false, FILTER_VALIDATE_BOOL)) {
openlog("PHP", LOG_PID | LOG_PERROR, LOG_LOCAL0);
syslog($level, "[auto_loader] " . $message);
closelog();
}
}
}
-5
View File
@@ -25,7 +25,6 @@
Mark J Crane <markjcrane@fusionpbx.com>
*/
if (!class_exists('button')) {
class button {
public static $collapse = 'hide-md-dn';
@@ -127,7 +126,6 @@ if (!class_exists('button')) {
$button = $anchor.$button."</a>";
}
return $button;
unset($button);
}
private static function quote($value) {
@@ -135,7 +133,6 @@ if (!class_exists('button')) {
}
}
}
/*
@@ -236,5 +233,3 @@ if (!class_exists('button')) {
*/
?>
+11
View File
@@ -187,6 +187,17 @@ class cache {
closelog();
}
//check for apcu extension
if (function_exists('apcu_enabled') && apcu_enabled()) {
//flush everything
apcu_clear_cache();
}
//remove the autoloader file cache
if (file_exists(sys_get_temp_dir() . '/' . auto_loader::FILE)) {
@unlink(sys_get_temp_dir() . '/' . auto_loader::FILE);
}
//cache method memcache
if ($this->method === "memcache") {
// connect to event socket
+203 -58
View File
@@ -297,7 +297,6 @@
//driver and type point to the same value
$this->driver = $config->get('database.0.type', 'pgsql');
$this->driver = $config->get('database.0.type', 'pgsql');
$this->type = $config->get('database.0.type', 'pgsql');
$this->host = $config->get('database.0.host', '127.0.0.1');
$this->port = $config->get('database.0.port', '5432');
@@ -545,7 +544,10 @@
$this->db->sqliteCreateFunction('strright', 'php_right', 2);
}
else {
echo "not found";
$error_message = "file not found";
$message['message'] = $error_message;
$this->message = $message;
return false;
}
}
@@ -572,9 +574,15 @@
}
}
}
catch (PDOException $error) {
print "error: " . $error->getMessage() . "<br/>";
die();
catch (PDOException $e) {
$message['message'] = $e->getMessage();
$message['code'] = $e->getCode();
$message['line'] = $e->getLine();
$message['file'] = $e->getFile();
$message['trace'] = $e->getTraceAsString();
$message['debug'] = debug_backtrace();
$this->message = $message;
return false;
}
}
@@ -594,9 +602,15 @@
$this->db = new PDO("pgsql:dbname=$this->db_name user=$this->username password=$this->password");
}
}
catch (PDOException $error) {
print "error: " . $error->getMessage() . "<br/>";
die();
catch (PDOException $e) {
$message['message'] = $e->getMessage();
$message['code'] = $e->getCode();
$message['line'] = $e->getLine();
$message['file'] = $e->getFile();
$message['trace'] = $e->getTraceAsString();
$message['debug'] = debug_backtrace();
$this->message = $message;
return false;
}
}
@@ -606,9 +620,19 @@
$this->db = new PDO("odbc:".$this->db_name, $this->username, $this->password);
}
catch (PDOException $e) {
echo 'Connection failed: ' . $e->getMessage();
$message['message'] = $e->getMessage();
$message['code'] = $e->getCode();
$message['line'] = $e->getLine();
$message['file'] = $e->getFile();
$message['trace'] = $e->getTraceAsString();
$message['debug'] = debug_backtrace();
$this->message = $message;
return false;
}
}
//connected to the database
return true;
}
/**
@@ -730,13 +754,14 @@
//if unable to connect to the database
if (!$this->db) {
$backtrace = debug_backtrace();
echo "Connection Failed<br />\n";
echo "line number ".__line__."<br />\n";
echo "<pre>";
print_r($backtrace);
echo "</pre>";
exit;
$message['message'] = 'Unable to connect to database';
$message['code'] = '500';
$message['line'] = __LINE__;
$message['file'] = __FILE__;
$message['trace'] = '';
$message['debug'] = debug_backtrace();
$this->message = $message;
return false;
}
//query table store to see if the table exists
@@ -761,6 +786,89 @@
}
}
/**
* Checks if the column exists in the database.
* <p><b>Note:</b><br>
* Tables and Column names must be sanitized. Otherwise, a warning will be
* emitted and false will be returned.</p>
* @param type $table_name Sanitized name of the table to search for.
* @param type $column_name Sanitized name of the column to search for.
* @return boolean Returns <i>true</i> if the column exists and <i>false</i> if it does not.
* @depends connect()
*/
public function column_exists ($table_name, $column_name) {
//sanitize the table name
if (self::sanitize($table_name) != $table_name) {
trigger_error('Table Name must be sanitized', E_USER_WARNING);
return false;
}
//sanitize the column name
if (self::sanitize($column_name) != $column_name) {
trigger_error('Column Name must be sanitized', E_USER_WARNING);
return false;
}
//connect to the database if needed
if (!$this->db) {
$this->connect();
}
//if unable to connect to the database
if (!$this->db) {
$backtrace = debug_backtrace();
echo "Connection Failed<br />\n";
echo "line number ".__line__."<br />\n";
echo "<pre>";
print_r($backtrace);
echo "</pre>";
return false;
}
//check the sqlite database to see if the column exists
//if ($this->db_type == "sqlite") {
// $table_info = $this->table_info($table_name);
// if ($this->sqlite_column_exists($table_info, $column_name)) {
// return true;
// }
// else {
// return false;
// }
//}
//check the postgresql database to see if the column exists
if ($this->type == "pgsql") {
$sql = "SELECT attname FROM pg_attribute WHERE attrelid = (SELECT oid FROM pg_class WHERE relname = '$table_name' limit 1) AND attname = '$column_name'; ";
}
//check the mysql database to see if the column exists
if ($this->type == "mysql") {
//$sql .= "SELECT * FROM information_schema.COLUMNS where TABLE_SCHEMA = '$db_name' and TABLE_NAME = '$table_name' and COLUMN_NAME = '$column_name' ";
$sql = "show columns from $table_name where field = '$column_name' ";
}
//return the results from the sql query
if (empty($sql)) {
return false;
}
else {
$prep_statement = $this->db->prepare($sql);
$prep_statement->execute();
$result = $prep_statement->fetchAll(PDO::FETCH_NAMED);
if (!$result) {
return false;
}
if (count($result) > 0) {
return true;
}
else {
return false;
}
unset ($prep_statement);
}
}
/**
* Queries {@link database::table_info()} to return the fields.
* @access public
@@ -973,13 +1081,12 @@
}
}
catch(PDOException $e) {
$message["type"] = 'error';
$message["code"] = $e->getCode();
$message["message"] = $e->getMessage();
$message["sql"] = $sql;
if (!empty($parameters)) {
$message["parameters"] = $parameters;
}
$message['message'] = $e->getMessage();
$message['code'] = $e->getCode();
$message['line'] = $e->getLine();
$message['file'] = $e->getFile();
$message['trace'] = $e->getTraceAsString();
$message['debug'] = debug_backtrace();
$this->message = $message;
return false;
}
@@ -1055,20 +1162,18 @@
//prepare the sql and parameters and then run the query
try {
//$this->sql = $sql;
//$this->db->exec($sql);
$prep_statement = $this->db->prepare($sql);
$prep_statement->execute($params);
}
catch(PDOException $e) {
echo "<b>Error:</b><br />\n";
echo "<table>\n";
echo "<tr>\n";
echo "<td>\n";
echo $e->getMessage();
echo "</td>\n";
echo "</tr>\n";
echo "</table>\n";
$message['message'] = $e->getMessage();
$message['code'] = $e->getCode();
$message['line'] = $e->getLine();
$message['file'] = $e->getFile();
$message['trace'] = $e->getTraceAsString();
$message['debug'] = debug_backtrace();
$this->message = $message;
}
unset($sql, $prep_statement, $this->fields);
}
@@ -1369,8 +1474,7 @@
$this->message = $message;
$m++;
unset($sql);
unset($statement);
unset($sql, $statement);
}
catch(PDOException $e) {
$retval = false;
@@ -1570,9 +1674,11 @@
//unable to connect to the database
if (!$this->db) {
echo "Connection Failed<br />\n";
echo "line number ".__line__."<br />\n";
exit;
$error_message = "Connection Failed<br />\n";
$error_message .= "line number ".__line__."<br />\n";
$message['message'] = $error_message;
$this->message = $message;
return false;
}
//set the error mode
@@ -1615,14 +1721,12 @@
}
}
catch(PDOException $e) {
$message["message"] = "Bad Request";
$message["code"] = "400";
$message["error"]["message"] = $e->getMessage();
$message["sql"] = $sql;
if (is_array($parameters)) {
$message["parameters"] = $parameters;
}
$message['message'] = $e->getMessage();
$message['code'] = $e->getCode();
$message['line'] = $e->getLine();
$message['file'] = $e->getFile();
$message['trace'] = $e->getTraceAsString();
$message['debug'] = debug_backtrace();
$this->message = $message;
return false;
}
@@ -2197,10 +2301,12 @@
$result = $prep_statement->fetchAll(PDO::FETCH_ASSOC);
}
catch(PDOException $e) {
echo $sql."<br />\n";
echo 'Caught exception: '. $e->getMessage()."<br /><br />\n";
echo $sql. "<br /><br />\n";
exit;
$message["type"] = 'error';
$message["code"] = $e->getCode();
$message["message"] = $e->getMessage();
$message["sql"] = $sql;
$this->message = $message;
return false;
}
//set the action
@@ -2523,10 +2629,14 @@
unset($prep_statement);
}
catch(PDOException $e) {
echo $sql."<br />\n";
echo 'Caught exception: '. $e->getMessage()."<br /><br />\n";
echo $sql. "<br /><br />\n";
exit;
$message['message'] = $e->getMessage();
$message['code'] = $e->getCode();
$message['line'] = $e->getLine();
$message['file'] = $e->getFile();
$message['trace'] = $e->getTraceAsString();
$message['debug'] = debug_backtrace();
$this->message = $message;
return false;
}
}
@@ -2907,13 +3017,48 @@
unset($sql);
}
catch(PDOException $e) {
echo $e->getMessage();
exit;
$message['message'] = $e->getMessage();
$message['code'] = $e->getCode();
$message['line'] = $e->getLine();
$message['file'] = $e->getFile();
$message['trace'] = $e->getTraceAsString();
$message['debug'] = debug_backtrace();
$this->message = $message;
return false;
}
}
return $this->message;
} //save method
/**
* Ensure the database is still connected and active.
* <p>NOTE:<br>
* There is no method in PDO that can reliably detect if the connection is active. Therefor, a lightweight
* query is executed using the statement <code>select 1</code>.</p>
* @return bool True if the database is connected. False otherwise.
*/
public function is_connected(): bool {
try {
$stmt = false;
if ($this->db !== null) $stmt = $this->db->query('SELECT 1');
return $stmt !== false;
} catch (PDOException $ex) {
//database is not connected
return false;
} catch (Exception $e) {
//some other error has occurred so record it
$message['message'] = $e->getMessage();
$message['code'] = $e->getCode();
$message['line'] = $e->getLine();
$message['file'] = $e->getFile();
$message['trace'] = $e->getTraceAsString();
$message['debug'] = debug_backtrace();
$this->message = $message;
return false;
}
return true;
}
/**
* Converts a plural English word to singular.
* @param string $word English word
@@ -3137,7 +3282,9 @@
public static function new(array $params = []) {
if (self::$database === null) {
self::$database = new database($params);
self::$database->connect();
if (!self::$database->is_connected()) {
self::$database->connect();
}
}
return self::$database;
}
@@ -3179,7 +3326,6 @@
/*
//example usage
//find
require_once "resources/classes/database.php";
$database = new database;
$database->domain_uuid = $_SESSION["domain_uuid"];
$database->type = $db_type;
@@ -3196,7 +3342,6 @@
$database->find();
print_r($database->result);
//insert
require_once "resources/classes/database.php";
$database = new database;
$database->domain_uuid = $_SESSION["domain_uuid"];
$database->table = "v_ivr_menus";
-7
View File
@@ -28,11 +28,7 @@
/**
* domains class
*
* @method null delete
* @method null toggle
* @method null copy
*/
if (!class_exists('domains')) {
class domains {
/**
@@ -786,6 +782,3 @@ if (!class_exists('domains')) {
}
}
}
?>
+3 -7
View File
@@ -27,9 +27,7 @@
/**
* email class
*
* @method boolean send
*/
if (!class_exists('email')) {
class email {
/**
@@ -445,7 +443,7 @@ if (!class_exists('email')) {
$smtp['password'] = $this->settings->get('email','smtp_password');
$smtp['from'] = $this->settings->get('voicemail','smtp_from') ?? $this->settings->get('email','smtp_from');
$smtp['from_name'] = $this->settings->get('voicemail','smtp_from_name') ?? $this->settings->get('email','smtp_from_name');
$smtp['validate_certificate'] = $this->settings->get('email','smtp_validate_certificate');
$smtp['validate_certificate'] = $this->settings->get('email','smtp_validate_certificate', true);
$smtp['crypto_method'] = $this->settings->get('email','smtp_crypto_method') ?? null;
//override the domain-specific smtp server settings, if any
@@ -506,7 +504,7 @@ if (!class_exists('email')) {
$mail->SMTPSecure = $smtp['secure'];
}
if ($smtp_secure && isset($smtp['validate_certificate']) && $smtp['validate_certificate'] == "false") {
if ($smtp_secure && isset($smtp['validate_certificate']) && !$smtp['validate_certificate']) {
//bypass certificate check e.g. for self-signed certificates
$smtp_options['ssl']['verify_peer'] = false;
$smtp_options['ssl']['verify_peer_name'] = false;
@@ -625,7 +623,7 @@ if (!class_exists('email')) {
}
}
}
/*
$email = new email;
@@ -638,5 +636,3 @@ $email->attachments = $email_attachments;
$response = $mail->error;
$sent = $email->send();
*/
?>
-7
View File
@@ -27,11 +27,7 @@
/**
* groups class provides methods for add, delete groups, and add default groups
*
* @method null delete
* @method null toggle
* @method null copy
*/
if (!class_exists('groups')) {
class groups {
/**
@@ -534,6 +530,3 @@ if (!class_exists('groups')) {
$_SESSION["user"]["group_level"] = $this->group_level;
}
}
}
?>
+12 -9
View File
@@ -27,7 +27,6 @@
/**
* menu class
*/
if (!class_exists('menu')) {
class menu {
/**
@@ -427,6 +426,7 @@ if (!class_exists('menu')) {
$menu_item_parent_uuid = $uuid_array[$menu['parent_uuid']] ?? null;
$menu_item_category = $menu['category'];
$menu_item_icon = $menu['icon'] ?? null;
$menu_item_icon_color = $menu['icon_color'] ?? null;
$menu_item_path = $menu['path'];
$menu_item_order = $menu['order'] ?? null;
$menu_item_description = $menu['desc'] ?? null;
@@ -445,6 +445,7 @@ if (!class_exists('menu')) {
//item exists in the database
if ($menu_item_exists) {
$parent_menu_item_protected = 'false';
//get parent_menu_item_protected
foreach ($menu_items as $item) {
if ($item['uuid'] == $menu['parent_uuid']) {
@@ -470,6 +471,7 @@ if (!class_exists('menu')) {
$array['menu_items'][$x]['menu_item_link'] = $menu_item_path;
$array['menu_items'][$x]['menu_item_category'] = $menu_item_category;
$array['menu_items'][$x]['menu_item_icon'] = $menu_item_icon;
$array['menu_items'][$x]['menu_item_icon_color'] = $menu_item_icon_color;
if (!empty($menu_item_order)) {
$array['menu_items'][$x]['menu_item_order'] = $menu_item_order;
}
@@ -744,7 +746,7 @@ if (!class_exists('menu')) {
//get the menu from the database
$sql = "select i.menu_item_link, l.menu_item_title as menu_language_title, ";
$sql .= "i.menu_item_title, i.menu_item_protected, i.menu_item_category, ";
$sql .= "i.menu_item_icon, i.menu_item_uuid, i.menu_item_parent_uuid ";
$sql .= "i.menu_item_icon, i.menu_item_icon_color, i.menu_item_uuid, i.menu_item_parent_uuid ";
$sql .= "from v_menu_items as i, v_menu_languages as l ";
$sql .= "where i.menu_item_uuid = l.menu_item_uuid ";
$sql .= "and l.menu_language = :menu_language ";
@@ -813,7 +815,7 @@ if (!class_exists('menu')) {
}
//get the child menu from the database
$sql = "select i.menu_item_link, l.menu_item_title as menu_language_title, i.menu_item_title, i.menu_item_protected, i.menu_item_category, i.menu_item_icon, i.menu_item_uuid, i.menu_item_parent_uuid ";
$sql = "select i.menu_item_link, l.menu_item_title as menu_language_title, i.menu_item_title, i.menu_item_protected, i.menu_item_category, i.menu_item_icon, i.menu_item_icon_color, i.menu_item_uuid, i.menu_item_parent_uuid ";
$sql .= "from v_menu_items as i, v_menu_languages as l ";
$sql .= "where i.menu_item_uuid = l.menu_item_uuid ";
$sql .= "and l.menu_language = :menu_language ";
@@ -853,6 +855,7 @@ if (!class_exists('menu')) {
$menu_item_link = $row['menu_item_link'];
$menu_item_category = $row['menu_item_category'];
$menu_item_icon = $row['menu_item_icon'];
$menu_item_icon_color = $row['menu_item_icon_color'];
$menu_item_uuid = $row['menu_item_uuid'];
$menu_item_parent_uuid = $row['menu_item_parent_uuid'];
@@ -1009,7 +1012,7 @@ if (!class_exists('menu')) {
$mod_a_3 = ($menu_parent['menu_item_category'] == 'external') ? "target='_blank' " : null;
if ($this->settings->get('theme', 'menu_main_icons', true) === true) {
if (!empty($menu_parent['menu_item_icon']) && substr($menu_parent['menu_item_icon'], 0, 3) == 'fa-') { // font awesome icon
$menu_main_icon = "<span class='".escape($menu_parent['menu_item_icon'])."' title=\"".escape($menu_parent['menu_language_title'])."\"></span>";
$menu_main_icon = "<span class='".escape($menu_parent['menu_item_icon'])."' ".(!empty($menu_parent['menu_item_icon_color']) ? "style='color: ".$menu_parent['menu_item_icon_color']." !important;'" : null)." title=\"".escape($menu_parent['menu_language_title'])."\"></span>";
}
else {
$menu_main_icon = null;
@@ -1042,13 +1045,13 @@ if (!class_exists('menu')) {
$menu_sub_icon = null;
if ($this->settings->get('theme', 'menu_sub_icons', true) !== false) {
if (!empty($menu_sub['menu_item_icon']) && substr($menu_sub['menu_item_icon'], 0, 3) == 'fa-') { // font awesome icon
$menu_sub_icon = "<span class='".escape($menu_sub['menu_item_icon'])."'></span>";
$menu_sub_icon = "<span class='".escape($menu_sub['menu_item_icon'])."' style='".(!empty($menu_sub['menu_item_icon_color']) ? "color: ".$menu_sub['menu_item_icon_color']." !important;" : "opacity: 0.3;")."'></span>";
}
else {
$menu_sub_icon = null;
}
}
$html .= " <li class='nav-item'><a class='nav-link' href='".$mod_a_2."' ".$mod_a_3.">".($this->settings->get('theme', 'menu_sub_icons', true) != false ? "<span class='fa-solid fa-minus d-inline-block d-sm-none float-left' style='margin: 4px 10px 0 25px;'></span>" : '').escape($menu_sub['menu_language_title']).$menu_sub_icon."</a></li>\n";
$html .= " <li class='nav-item'><a class='nav-link' href='".$mod_a_2."' ".$mod_a_3." onclick='event.stopPropagation();'>".($this->settings->get('theme', 'menu_sub_icons', true) != false ? "<span class='fa-solid fa-minus d-inline-block d-sm-none float-left' style='margin: 4px 10px 0 25px;'></span>" : '').escape($menu_sub['menu_language_title']).$menu_sub_icon."</a></li>\n";
if ($columns > 1 && $column_current == 1 && ($index_sub+1) > (ceil(@sizeof($menu_parent['menu_items'])/2)-1)) {
$html .= " </ul>\n";
$html .= " </div>\n";
@@ -1210,7 +1213,7 @@ if (!class_exists('menu')) {
$html .= " <div class='menu_side_item_main_sub_icons' style='float: right; margin-right: -1px; ".($menu_side_state != 'expanded' ? "display: none;" : null)."'><i id='sub_arrow_".$menu_item_main['menu_item_uuid']."' class='sub_arrows ".$this->settings->get('theme', 'menu_side_item_main_sub_icon_expand', 'fa-solid fa-chevron-down')." fa-xs'></i></div>\n";
}
if (!empty($menu_item_main['menu_item_icon']) && substr($menu_item_main['menu_item_icon'], 0, 3) == 'fa-') { // font awesome icon
$html .= "<i class='menu_side_item_icon ".$menu_item_main['menu_item_icon']." fa-fw' style='z-index: 99800; margin-right: 8px;'></i>";
$html .= "<i class='menu_side_item_icon ".$menu_item_main['menu_item_icon']." fa-fw' style='z-index: 99800; margin-right: 8px; ".(!empty($menu_item_main['menu_item_icon_color']) ? "color: ".$menu_item_main['menu_item_icon_color']." !important;" : null)."'></i>";
}
$html .= "<span class='menu_side_item_title' style='".($menu_side_state != 'expanded' ? "display: none;" : null)."'>".$menu_item_main['menu_language_title']."</span>";
$html .= "</a>\n";
@@ -1221,7 +1224,7 @@ if (!class_exists('menu')) {
$menu_sub_icon = null;
if ($this->settings->get('theme', 'menu_sub_icons', true) !== false) {
if (!empty($menu_item_sub['menu_item_icon']) && substr($menu_item_sub['menu_item_icon'], 0, 3) == 'fa-') { // font awesome icon
$menu_sub_icon = "<span class='".escape($menu_item_sub['menu_item_icon']).(substr($menu_item_sub['menu_item_icon'], 0, 3) == 'fa-' ? ' fa-fw' : null)."'></span>";
$menu_sub_icon = "<span class='".escape($menu_item_sub['menu_item_icon']).(substr($menu_item_sub['menu_item_icon'], 0, 3) == 'fa-' ? ' fa-fw' : null)."' style='".(!empty($menu_item_sub['menu_item_icon_color']) ? "color: ".$menu_item_sub['menu_item_icon_color']." !important;" : "opacity: 0.3;")."'></span>";
}
else {
$menu_sub_icon = null;
@@ -1237,6 +1240,7 @@ if (!class_exists('menu')) {
$html .= " <div style='height: 100px;'></div>\n";
}
$html .= "</div>\n";
$content_container_onclick = "";
if ($menu_side_state != 'expanded') {
$content_container_onclick = "onclick=\"clearTimeout(menu_side_contract_timer); if ($(window).width() >= 576) { menu_side_contract(); }\"";
}
@@ -1334,4 +1338,3 @@ if (!class_exists('menu')) {
}
}
}
-4
View File
@@ -25,7 +25,6 @@
Matthew Vale <github@mafoo.org>
*/
if (!class_exists('message')) {
class message {
static function add($message, $mood = null, $delay = null) {
@@ -71,6 +70,3 @@ if (!class_exists('message')) {
return $html;
}
}
}
?>
-5
View File
@@ -25,7 +25,6 @@
Mark J Crane <markjcrane@fusionpbx.com>
*/
if (!class_exists('modal')) {
class modal {
static function create($array) {
@@ -65,11 +64,7 @@ if (!class_exists('modal')) {
$modal .= "</div>";
return $modal;
unset($modal);
}
}
}
?>
File diff suppressed because it is too large Load Diff
+100 -53
View File
@@ -53,6 +53,12 @@ class settings {
*/
private $database;
/**
* Tracks if the APCu extension is loaded for database queries
* @var bool
*/
private $apcu_enabled;
/**
* Create a settings object using key/value pairs in the $setting_array.
*
@@ -63,6 +69,16 @@ class settings {
*/
public function __construct($setting_array = []) {
//try to use RAM cache by default
if (!isset($setting_array['allow_caching'])) {
$setting_array['allow_caching'] = true;
}
//track ram caching
if ($setting_array['allow_caching']) {
$this->apcu_enabled = function_exists('apcu_enabled') && apcu_enabled();
}
//open a database connection
if (empty($setting_array['database'])) {
$this->database = database::new();
@@ -166,12 +182,12 @@ class settings {
* @param string $uuid uuid of the setting if available. If set to an empty string then a new uuid will be created.
* @param string $category Category of the setting.
* @param string $subcategory Subcategory of the setting.
* @param string $type Type of the setting (array, numeric, text, etc)
* @param string $value (optional) Value to set. Default is empty string.
* @param string $type Type of the setting (array, numeric, text, etc)
* @param bool $enabled (optional) True or False. Default is True.
* @param string $description (optional) Description. Default is empty string.
*/
public function set(string $table_prefix, string $uuid, string $category, string $subcategory, string $type = 'text', string $value = "", bool $enabled = true, string $description = "") {
public function set(string $table_prefix, string $uuid, string $category, string $subcategory, string $value = "", string $type = 'text', bool $enabled = true, string $description = "") {
//set the table name
$table_name = $table_prefix.'_settings';
@@ -223,6 +239,15 @@ class settings {
*/
private function default_settings() {
//set the key for global defaults
$key = 'settings_global_' . $this->category;
//if the apcu extension is loaded get the already parsed array
if ($this->apcu_enabled && apcu_exists($key)) {
$this->settings = apcu_fetch($key);
return;
}
//get the default settings
$sql = "select * from v_default_settings ";
$sql .= "where default_setting_enabled = 'true' ";
@@ -239,17 +264,7 @@ class settings {
$subcategory = $row['default_setting_subcategory'];
if (isset($row['default_setting_value']) && $row['default_setting_value'] !== '') {
if ($name == "boolean") {
if (gettype($row['default_setting_value']) === 'string') {
if ($row['default_setting_value'] === 'true') {
$this->settings[$category][$subcategory] = true;
}
else {
$this->settings[$category][$subcategory] = false;
}
}
elseif (gettype($row['default_setting_value']) === 'boolean') {
$this->settings[$category][$subcategory] = $row['default_setting_value'];
}
$this->settings[$category][$subcategory] = filter_var($row['default_setting_value'], FILTER_VALIDATE_BOOLEAN);
}
elseif ($name == "array") {
if (!isset($this->settings[$category][$subcategory]) || !is_array($this->settings[$category][$subcategory])) {
@@ -263,7 +278,11 @@ class settings {
}
}
}
unset($sql, $result, $row);
//if the apcu extension is loaded store the result
if ($this->apcu_enabled) {
apcu_store($key, $this->settings);
}
}
/**
@@ -271,13 +290,22 @@ class settings {
* @access private
*/
private function domain_settings() {
$sql = "select * from v_domain_settings ";
$sql .= "where domain_uuid = :domain_uuid ";
$sql .= "and domain_setting_enabled = 'true' ";
$parameters['domain_uuid'] = $this->domain_uuid;
$result = $this->database->select($sql, $parameters, 'all');
unset($sql, $parameters);
$key = 'settings_domain_'.$this->domain_uuid;
$result = '';
//if the apcu extension is loaded get the cached database result
if ($this->apcu_enabled && apcu_exists($key)) {
$result = apcu_fetch($key);
} else {
$sql = "select * from v_domain_settings ";
$sql .= "where domain_uuid = :domain_uuid ";
$sql .= "and domain_setting_enabled = 'true' ";
$parameters['domain_uuid'] = $this->domain_uuid;
$result = $this->database->select($sql, $parameters, 'all');
//if the apcu extension is loaded store the result
if ($this->apcu_enabled) {
apcu_store($key, $result);
}
}
if (!empty($result)) {
//domain setting array types override the default settings set as type array
foreach ($result as $row) {
@@ -296,17 +324,7 @@ class settings {
$subcategory = $row['domain_setting_subcategory'];
if (isset($row['domain_setting_value']) && $row['domain_setting_value'] !== '') {
if ($name == "boolean") {
if (gettype($row['domain_setting_value']) === 'string') {
if ($row['domain_setting_value'] === 'true') {
$this->settings[$category][$subcategory] = true;
}
else {
$this->settings[$category][$subcategory] = false;
}
}
elseif (gettype($row['domain_setting_value']) === 'boolean') {
$this->settings[$category][$subcategory] = $row['domain_setting_value'];
}
$this->settings[$category][$subcategory] = filter_var($row['domain_setting_value'], FILTER_VALIDATE_BOOLEAN);
}
if ($name == "array") {
if (!isset($this->settings[$category][$subcategory]) || !is_array($this->settings[$category][$subcategory])) {
@@ -321,7 +339,6 @@ class settings {
}
}
unset($result, $row);
}
/**
@@ -330,15 +347,25 @@ class settings {
* @depends $this->domain_uuid
*/
private function user_settings() {
$sql = "select * from v_user_settings ";
$sql .= "where domain_uuid = :domain_uuid ";
$sql .= "and user_uuid = :user_uuid ";
$sql .= " order by user_setting_order asc ";
$parameters['domain_uuid'] = $this->domain_uuid;
$parameters['user_uuid'] = $this->user_uuid;
$result = $this->database->select($sql, $parameters, 'all');
if (is_array($result)) {
$key = 'settings_user_'.$this->user_uuid;
$result = '';
//if the apcu extension is loaded get the cached database result
if ($this->apcu_enabled && apcu_exists($key)) {
$result = apcu_fetch($key);
} else {
$sql = "select * from v_user_settings ";
$sql .= "where domain_uuid = :domain_uuid ";
$sql .= "and user_uuid = :user_uuid ";
$sql .= " order by user_setting_order asc ";
$parameters['domain_uuid'] = $this->domain_uuid;
$parameters['user_uuid'] = $this->user_uuid;
$result = $this->database->select($sql, $parameters, 'all');
//if the apcu extension is loaded store the result
if ($this->apcu_enabled) {
apcu_store($key, $result);
}
}
if (!empty($result)) {
foreach ($result as $row) {
if ($row['user_setting_enabled'] == 'true') {
$name = $row['user_setting_name'];
@@ -346,17 +373,7 @@ class settings {
$subcategory = $row['user_setting_subcategory'];
if (isset($row['user_setting_value']) && $row['user_setting_value'] !== '') {
if ($name == "boolean") {
if (gettype($row['user_setting_value']) === 'string') {
if ($row['user_setting_value'] === 'true') {
$this->settings[$category][$subcategory] = true;
}
else {
$this->settings[$category][$subcategory] = false;
}
}
elseif (gettype($row['user_setting_value']) === 'boolean') {
$this->settings[$category][$subcategory] = $row['user_setting_value'];
}
$this->settings[$category][$subcategory] = filter_var($row['user_setting_value'], FILTER_VALIDATE_BOOLEAN);
}
elseif ($name == "array") {
$this->settings[$category][$subcategory][] = $row['user_setting_value'];
@@ -422,4 +439,34 @@ class settings {
}
}
}
/**
* Clears the settings cache
* @param string $type Empty clears all settings. Values can be global, domain, or user.
*/
public static function clear_cache(string $type = '') {
if (function_exists('apcu_enabled') && apcu_enabled()) {
switch ($type) {
case 'all':
case '':
default:
$type = "";
break;
case 'global':
case 'domain':
case 'user':
$type .= "_";
break;
}
$cache = apcu_cache_info(false);
if (!empty($cache['cache_list'])) {
foreach ($cache['cache_list'] as $entry) {
$key = $entry['info'];
if (str_starts_with($key, 'settings_' . $type)) {
apcu_delete($key);
}
}
}
}
}
}
-4
View File
@@ -5,7 +5,6 @@
*
* @method settings will add missing switch directories to default settings
*/
if (!class_exists('switch_settings')) {
class switch_settings {
public $event_socket_ip_address;
@@ -276,6 +275,3 @@ if (!class_exists('switch_settings')) {
unset($array);
}
}
}
?>
-4
View File
@@ -25,7 +25,6 @@
*/
//define the template class
if (!class_exists('template')) {
class template {
public $engine;
@@ -90,6 +89,3 @@
}
}
}
}
?>
+2 -6
View File
@@ -25,7 +25,6 @@
Matthew Vale <github@mafoo.org>
*/
if (!class_exists('tones')) {
class tones {
//define variables
@@ -33,7 +32,7 @@ if (!class_exists('tones')) {
private $music_list;
private $recordings_list;
private $default_tone_label;
//class constructor
public function __construct() {
//add multi-lingual support
@@ -61,11 +60,8 @@ if (!class_exists('tones')) {
$this->tones = $tone_list ?? '';
unset($sql, $tones, $tone, $tone_list);
}
public function tones_list() {
return $this->tones;
}
}
}
?>
-4
View File
@@ -1,6 +1,5 @@
<?php
if (!class_exists('xml')) {
class xml {
/**
@@ -12,6 +11,3 @@ if (!class_exists('xml')) {
}
}
}
?>