maintenance.php 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732
  1. <?php
  2. /*
  3. * FusionPBX
  4. * Version: MPL 1.1
  5. *
  6. * The contents of this file are subject to the Mozilla Public License Version
  7. * 1.1 (the "License"); you may not use this file except in compliance with
  8. * the License. You may obtain a copy of the License at
  9. * http://www.mozilla.org/MPL/
  10. *
  11. * Software distributed under the License is distributed on an "AS IS" basis,
  12. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  13. * for the specific language governing rights and limitations under the
  14. * License.
  15. *
  16. * The Original Code is FusionPBX
  17. *
  18. * The Initial Developer of the Original Code is
  19. * Mark J Crane <[email protected]>
  20. * Portions created by the Initial Developer are Copyright (C) 2008-2024
  21. * the Initial Developer. All Rights Reserved.
  22. *
  23. * Contributor(s):
  24. * Mark J Crane <[email protected]>
  25. * Tim Fry <[email protected]>
  26. */
  27. /**
  28. * Description of maintenance_app
  29. *
  30. * @author Tim Fry <[email protected]>
  31. */
  32. class maintenance {
  33. const APP_NAME = 'Maintenance';
  34. const APP_UUID = '8a209bac-3bba-46eb-828e-bd4087fb9cee';
  35. const PERMISSION_PREFIX = 'maintenance_';
  36. const LIST_PAGE = 'maintenance.php';
  37. const TABLE = 'maintenance';
  38. const UUID_PREFIX = 'maintenance_';
  39. const TOGGLE_FIELD = 'maintenance_enabled';
  40. const TOGGLE_VALUES = ['true', 'false'];
  41. const DATABASE_SUBCATEGORY = 'database_retention_days';
  42. const FILESYSTEM_SUBCATEGORY = 'filesystem_retention_days';
  43. private static $app_config_list = null;
  44. static $classes = [];
  45. /**
  46. * Returns an array of domain names with their domain UUID as the array key
  47. * @param database $database
  48. * @param bool $ignore_domain_enabled Omit the SQL where clause for domain_enabled
  49. * @param bool $domain_status When the <code>$ignore_domain_enabled</code> is false, set the status to true or false
  50. * @return array
  51. */
  52. public static function get_domains(database $database, bool $ignore_domain_enabled = false, bool $domain_status = true): array {
  53. $domains = [];
  54. $status_string = $domain_status ? 'true' : 'false';
  55. $sql = "select domain_uuid, domain_name from v_domains";
  56. if (!$ignore_domain_enabled) {
  57. $sql .= " where domain_enabled='$status_string'";
  58. }
  59. $result = $database->select($sql);
  60. if (!empty($result)) {
  61. foreach ($result as $row) {
  62. $domains[$row['domain_uuid']] = $row['domain_name'];
  63. }
  64. }
  65. return $domains;
  66. }
  67. /**
  68. * Registers new applications by searching for files in the project that have the signature for a method called
  69. * <code>public static function database_maintenance</code> or the method <code>public static function
  70. * filesystem_maintenance</code>. When found, they are added to the <code>default_settings</code> category of
  71. * <b>maintenance</b> and put in to subcategory array <b>application</b> in default settings table.
  72. * This function is intended to be called by the upgrade method.
  73. * @param database $database
  74. */
  75. public static function app_defaults(database $database) {
  76. //get the maintenance apps
  77. $database_maintenance_apps = self::find_classes_by_method('database_maintenance');
  78. $filesystem_maintenance_apps = self::find_classes_by_method('filesystem_maintenance');
  79. $maintenance_apps = $database_maintenance_apps + $filesystem_maintenance_apps;
  80. if (!empty($maintenance_apps)) {
  81. self::register_applications($database, $maintenance_apps);
  82. }
  83. }
  84. /**
  85. * Registers the list of applications given in the $maintenance_apps array to the global default settings
  86. * @param database $database
  87. * @param array $maintenance_apps
  88. * @return bool
  89. * @access public
  90. */
  91. public static function register_applications(database $database, array $maintenance_apps): bool {
  92. //make sure there is something to do
  93. if (count($maintenance_apps) === 0) {
  94. return false;
  95. }
  96. //query the database for the already registered applications
  97. $registered_apps = self::get_registered_applications($database);
  98. //load the text for the description
  99. $text = (new text())->get(null, 'app/' . __CLASS__);
  100. //register each app
  101. $new_maintenance_apps = [];
  102. $index = 0;
  103. foreach ($maintenance_apps as $application => $file) {
  104. //format the array for what the database object needs for saving data in the global default settings
  105. self::add_maintenance_app_to_array($registered_apps, $application, $text['description-default_settings_app'], $new_maintenance_apps, $index);
  106. //get the application settings from the class for database maintenance
  107. //self::add_database_maintenance_to_array($database, $application, $text['description-retention_days'], $new_maintenance_apps, $index);
  108. //get the application settings from the class for filesystem maintenance
  109. //self::add_filesystem_maintenance_to_array($database, $application, $text['description-retention_days'], $new_maintenance_apps, $index);
  110. }
  111. if (count($new_maintenance_apps) > 0) {
  112. $database->app_name = self::APP_NAME;
  113. $database->app_uuid = self::APP_UUID;
  114. $database->save($new_maintenance_apps);
  115. return true;
  116. }
  117. return false;
  118. }
  119. /**
  120. * Returns the class specified category to be used for the maintenance service. If the class does not have a method that
  121. * returns a string with the category to use then the class name will be used as the category.
  122. * @param object|string $class_name
  123. * @return string
  124. */
  125. public static function get_database_category($class_name): string {
  126. if (method_exists($class_name, 'database_maintenance_category')) {
  127. $default_value = $class_name::database_maintenance_category();
  128. } else {
  129. $default_value = $class_name;
  130. }
  131. return $default_value;
  132. }
  133. /**
  134. * Returns the class specified subcategory to be used for the maintenance service. If the class does not have a method that
  135. * returns a string with the subcategory to use then the class name will be used as the subcategory.
  136. * @param object|string $class_name
  137. * @return string
  138. */
  139. public static function get_database_subcategory($class_name): string {
  140. if (method_exists($class_name, 'database_maintenance_subcategory')) {
  141. $default_value = $class_name::database_maintenance_subcategory();
  142. } else {
  143. $default_value = self::DATABASE_SUBCATEGORY;
  144. }
  145. return $default_value;
  146. }
  147. public static function get_database_retention_days(settings $settings, $class_name, $default_value = ''): string {
  148. return $settings->get(self::get_database_category($class_name), self::get_database_subcategory($class_name), $default_value);
  149. }
  150. /**
  151. * Returns the class specified category to be used for the maintenance service. If the class does not have a method that
  152. * returns a string with the category to use then the class name will be used as the category.
  153. * @param object|string $class_name
  154. * @return string
  155. */
  156. public static function get_filesystem_category($class_name): string {
  157. if (method_exists($class_name, 'filesystem_maintenance_category')) {
  158. $default_value = $class_name::filesystem_maintenance_category();
  159. } else {
  160. $default_value = $class_name;
  161. }
  162. return $default_value;
  163. }
  164. /**
  165. * Returns the class specified subcategory to be used for the maintenance service. If the class does not have a method that
  166. * returns a string with the subcategory to use then the class name will be used as the subcategory.
  167. * @param object|string $class_name
  168. * @return string
  169. */
  170. public static function get_filesystem_subcategory($class_name): string {
  171. if (method_exists($class_name, 'filesystem_maintenance_subcategory')) {
  172. $default_value = $class_name::filesystem_maintenance_subcategory();
  173. } else {
  174. $default_value = self::FILESYSTEM_SUBCATEGORY;
  175. }
  176. return $default_value;
  177. }
  178. public static function get_filesystem_retention_days(settings $settings, $class_name, $default_value = ''): string {
  179. return $settings->get(self::get_filesystem_category($class_name), self::get_filesystem_subcategory($class_name), $default_value);
  180. }
  181. /**
  182. * Returns a list of maintenance applications already in the default settings table ignoring default_setting_enabled
  183. * @param database $database
  184. * @return array
  185. * @access public
  186. */
  187. public static function get_registered_applications(database $database): array {
  188. //get the already registered applications from the global default settings table
  189. $sql = "select default_setting_value"
  190. . " from v_default_settings"
  191. . " where default_setting_category = 'maintenance'"
  192. . " and default_setting_subcategory = 'application'";
  193. $result = $database->select($sql);
  194. if (!empty($result)) {
  195. $registered_applications = array_map(function ($row) { return $row['default_setting_value']; }, $result);
  196. }
  197. else {
  198. $registered_applications = [];
  199. }
  200. return $registered_applications;
  201. }
  202. /**
  203. * Get a value from the app_config.php file default settings
  204. * This function will load all app_config.php files and then load them in a class array only once. The first call will
  205. * have a performance impact but subsequent calls will have minimal impact as no files will be loaded.
  206. * @param string $category
  207. * @param string $subcategory
  208. * @return array|string|null If no value is found then null will be returned
  209. */
  210. public static function get_app_config_value(string $category, string $subcategory) {
  211. $return_value = null;
  212. //check if this is the first time loading the files
  213. if (self::$app_config_list === null) {
  214. //load the app_config files once
  215. self::load_app_config_list();
  216. }
  217. if (!empty(self::$app_config_list[$category][$subcategory])) {
  218. $return_value = self::$app_config_list[$category][$subcategory];
  219. }
  220. return $return_value;
  221. }
  222. /**
  223. * updates the array with a maintenance app using a format the database object save method can use to save in the default settings
  224. * default settings category: maintenance, subcategory: application, value: name of new application
  225. * @param array $registered_applications List of already registered applications
  226. * @param string $application Application class name
  227. * @param array $array Array in a format ready to use for the database save method
  228. * @param int $index Index pointing to the location to save within $array
  229. * @access private
  230. */
  231. private static function add_maintenance_app_to_array(&$registered_applications, $application, $description, &$array, &$index) {
  232. //verify that the application we need to add is not already listed in the registered applications array
  233. if (!in_array($application, $registered_applications)) {
  234. $array['default_settings'][$index]['default_setting_uuid'] = uuid();
  235. $array['default_settings'][$index]['default_setting_category'] = 'maintenance';
  236. $array['default_settings'][$index]['default_setting_subcategory'] = 'application';
  237. $array['default_settings'][$index]['default_setting_name'] = 'array';
  238. $array['default_settings'][$index]['default_setting_value'] = $application;
  239. $array['default_settings'][$index]['default_setting_enabled'] = 'true';
  240. $array['default_settings'][$index]['default_setting_description'] = $description;
  241. $index++;
  242. }
  243. }
  244. /**
  245. * Updates the array with a database maintenance app using a format the database object save method can use in default settings table
  246. * <p><b>default setting category</b>: class name that has the <code>implements database_maintenance;</code> statement<br>
  247. * <b>default setting subcategory</b>: "database_retention_days" (The class can override this setting to a custom value)<br>
  248. * <b>default setting value</b>: "30" (The class can override this setting to a custom value)<br>
  249. * <b>description</b>: "Number of days the maintenance application will keep the information."<br>
  250. * </p>
  251. * @param database $database Database object
  252. * @param string $application Application class name
  253. * @param string $description Description to be added in to the default settings table
  254. * @param array $array Array formatted for use in the database save method
  255. * @param int $index Index pointer to the save location in $array
  256. * @access private
  257. */
  258. private static function add_database_maintenance_to_array($database, $application, $description, &$array, &$index) {
  259. //get the application settings from the object for database maintenance
  260. if (method_exists($application, 'database_maintenance')) {
  261. $category = $application;
  262. $subcategory = self::DATABASE_SUBCATEGORY;
  263. //check if the default setting already exists in global default settings table
  264. $uuid = self::default_setting_uuid($database, $category, $subcategory);
  265. if (empty($uuid)) {
  266. //does not exist so create it
  267. $array['default_settings'][$index]['default_setting_category'] = $category;
  268. $array['default_settings'][$index]['default_setting_subcategory'] = $subcategory;
  269. $array['default_settings'][$index]['default_setting_uuid'] = uuid();
  270. $array['default_settings'][$index]['default_setting_name'] = 'numeric';
  271. $array['default_settings'][$index]['default_setting_value'] = '30';
  272. $array['default_settings'][$index]['default_setting_enabled'] = 'false';
  273. $array['default_settings'][$index]['default_setting_description'] = $description;
  274. $index++;
  275. } else {
  276. //already exists
  277. }
  278. }
  279. }
  280. /**
  281. * Query the database for an existing UUID of a maintenance application
  282. * @param database $database Database object
  283. * @param string $category Category to look for in the database
  284. * @param string $subcategory Subcategory or name of the setting in the default settings table
  285. * @return string Empty string if not found or a UUID
  286. */
  287. public static function default_setting_uuid(database $database, string $category, string $subcategory): string {
  288. $sql = 'select default_setting_uuid'
  289. . ' from v_default_settings'
  290. . ' where default_setting_category = :category'
  291. . ' and default_setting_subcategory = :subcategory'
  292. ;
  293. $params = [];
  294. $params['category'] = $category;
  295. $params['subcategory'] = $subcategory;
  296. return $database->select($sql, $params, 'column');
  297. }
  298. /**
  299. * Updates the array with a file system maintenance app using a format the database object save method can use in default settings table
  300. * <p><b>default setting category:</b> class name that has the <code>use filesystem_maintenance;</code> statement<br>
  301. * <b>default setting subcategory:</b> "filesystem_retention_days" (The class can override this setting to a custom value)<br>
  302. * <b>default setting value:</b> "30" (The class can override this setting to a custom value)<br>
  303. * <b>description:</b> "Number of days the maintenance application will keep the information."<br>
  304. * </p>
  305. * @param database $database Database object
  306. * @param string $application Application class name
  307. * @param string $description Description to be added in to the default settings table
  308. * @param array $array Array formatted for use in the database save method
  309. * @param int $index Index pointer to the save location in $array
  310. * @access private
  311. */
  312. private static function add_filesystem_maintenance_to_array($database, $application, $description, &$array, &$index) {
  313. if (method_exists($application, 'filesystem_maintenance')) {
  314. //the trait has this value defined
  315. $category = $application;
  316. //the trait has this value defined
  317. $subcategory = 'filesystem_retention_days';
  318. //check if the default setting already exists in global settings
  319. $uuid = self::default_setting_uuid($database, $category, $subcategory);
  320. if (empty($uuid)) {
  321. $array['default_settings'][$index]['default_setting_category'] = $category;
  322. $array['default_settings'][$index]['default_setting_subcategory'] = $subcategory;
  323. $array['default_settings'][$index]['default_setting_uuid'] = uuid();
  324. $array['default_settings'][$index]['default_setting_name'] = 'numeric';
  325. $array['default_settings'][$index]['default_setting_value'] = '30';
  326. $array['default_settings'][$index]['default_setting_enabled'] = 'false';
  327. $array['default_settings'][$index]['default_setting_description'] = $description;
  328. $index++;
  329. }
  330. }
  331. }
  332. /**
  333. * Finds class names that match the given method name
  334. * @param string $method_name Name of method to match in the class
  335. * @return array Names of classes or empty array if none found
  336. */
  337. public static function find_classes_by_method(string $method_name): array {
  338. $found_classes = [];
  339. $class_files = self::get_classes();
  340. //iterate over the files
  341. foreach ($class_files as $file) {
  342. $contents = file_get_contents($file);
  343. if (str_contains($contents, 'public static function ' . $method_name . '(settings $settings): void {')) {
  344. $class = basename($file, '.php');
  345. $found_classes[$class] = $file;
  346. }
  347. }
  348. return $found_classes;
  349. }
  350. private static function get_classes() {
  351. //set project path using magic dir constant
  352. $project_path = dirname(__DIR__, 4);
  353. //build the array of all locations for classes in specific order
  354. $search_path = [
  355. $project_path . '/resources/interfaces/*.php',
  356. $project_path . '/resources/traits/*.php',
  357. $project_path . '/resources/classes/*.php',
  358. $project_path . '/*/*/resources/interfaces/*.php',
  359. $project_path . '/*/*/resources/traits/*.php',
  360. $project_path . '/*/*/resources/classes/*.php',
  361. $project_path . '/core/authentication/resources/classes/plugins/*.php',
  362. ];
  363. //get all php files for each path
  364. $files = [];
  365. foreach ($search_path as $path) {
  366. $files = array_merge($files, glob($path));
  367. }
  368. //reset the current array
  369. $class_list = [];
  370. //interfaces are ignored
  371. $interface_list = [];
  372. //store the class name (key) and the path (value)
  373. foreach ($files as $file) {
  374. $file_content = file_get_contents($file);
  375. // Remove block comments
  376. $file_content = preg_replace('/\/\*.*?\*\//s', '', $file_content);
  377. // Remove single-line comments
  378. $file_content = preg_replace('/(\/\/|#).*$/m', '', $file_content);
  379. // Detect the namespace
  380. $namespace = '';
  381. if (preg_match('/\bnamespace\s+([^;{]+)[;{]/', $file_content, $namespace_match)) {
  382. $namespace = trim($namespace_match[1]) . '\\';
  383. }
  384. // Regex to capture class, interface, or trait declarations
  385. // It optionally captures an "implements" clause
  386. // Note: This regex is a simplified version and may need adjustments for edge cases
  387. $pattern = '/\b(class|interface|trait)\s+(\w+)(?:\s+extends\s+\w+)?(?:\s+implements\s+([^\\{]+))?/';
  388. if (preg_match_all($pattern, $file_content, $matches, PREG_SET_ORDER)) {
  389. foreach ($matches as $match) {
  390. // "class", "interface", or "trait"
  391. $type = $match[1];
  392. // The class/interface/trait name
  393. $name = trim($match[2], " \n\r\t\v\x00\\");
  394. // Combine the namespace and name
  395. $full_name = $namespace . $name;
  396. // Store the class/interface/trait with its file overwriting any existing declaration.
  397. $class_list[$full_name] = $file;
  398. // If it's a class that implements interfaces, process the implements clause.
  399. if ($type === 'class' && isset($match[3]) && trim($match[3]) !== '') {
  400. // Split the interface list by commas.
  401. $interface_list = explode(',', $match[3]);
  402. foreach ($interface_list as $interface) {
  403. $interface_name = trim($interface, " \n\r\t\v\x00\\");
  404. // Check that it is declared as an array so we can record the classes
  405. if (empty($interface_list[$interface_name]))
  406. $interface_list[$interface_name] = [];
  407. // Record the classes that implement interface sorting by namspace and class name
  408. $interface_list[$interface_name][] = $full_name;
  409. }
  410. }
  411. }
  412. } else {
  413. //
  414. // When the file is in the classes|interfaces|traits folder then
  415. // we must assume it is a valid class as IonCube will encode the
  416. // class name. So, we use the file name as the class name in the
  417. // global namespace and set it, checking first to ensure the
  418. // basename does not override an already declared class file in
  419. // order to mimic previous behaviour.
  420. //
  421. // use the basename as the class name
  422. $class_name = basename($file, '.php');
  423. if (!isset($class_list[$class_name])) {
  424. $class_list[$class_name] = $file;
  425. }
  426. }
  427. }
  428. return $class_list;
  429. }
  430. private static function load_app_config_list() {
  431. //app_config files use the array $apps to define the default_settings
  432. global $apps;
  433. //initialize the config list
  434. self::$app_config_list = [];
  435. //get the list of app_config files
  436. $project_dir = dirname(__DIR__, 4);
  437. $app_config_files = glob($project_dir . '/app/*/app_config.php');
  438. $core_config_files = glob($project_dir . '/core/*/app_config.php');
  439. $config_files = array_merge($app_config_files, $core_config_files);
  440. //iterate over list
  441. foreach ($config_files as $x => $file) {
  442. //include the app_config file
  443. include_once $file;
  444. //create a classname
  445. //get the array from the included file
  446. if (!empty($apps[$x]['default_settings'])) {
  447. foreach ($apps[$x]['default_settings'] as $setting) {
  448. //get the subcategory
  449. $category = $setting['default_setting_category'] ?? '';
  450. $subcategory = $setting['default_setting_subcategory'] ?? '';
  451. $value = $setting['default_setting_value'] ?? '';
  452. $type = $setting['default_setting_name'] ?? '';
  453. //check for array type
  454. if ($type !== 'array') {
  455. //store the values
  456. self::$app_config_list[$category][$subcategory] = $value;
  457. } else {
  458. $order = intval($setting['default_setting_order'] ?? '0');
  459. self::$app_config_list[$category][$subcategory][$order] = $value;
  460. }
  461. }
  462. }
  463. }
  464. }
  465. /**
  466. * Finds the UUID of a maintenance setting searching in the default settings, domain settings, and user settings tables.
  467. * This is primarily used for the dashboard display as there was a need to detect a setting even if it was disabled.
  468. * <p>NOTE:<br>
  469. * This will not deal with an array of returned values (such as maintenance_application) appropriately at this time as it has a limited scope of detecting a
  470. * string value that is unique with the category and subcategory combination across the tables.</p>
  471. * @param database $database Already connected database object
  472. * @param string $category Main category
  473. * @param string $subcategory Subcategory or name of the setting
  474. * @param bool $status Used for internal use but could be used to find a setting that is currently disabled
  475. * @return array Two-dimensional array assigned but using key/value pairs. The keys are:<br>
  476. * <ul>
  477. * <li>uuid: Primary UUID that would be chosen by the settings object
  478. * <li>uuids: Array of all matching category and subcategory strings
  479. * <li>table: Table name that the primary UUID was found
  480. * <li>status: bool true/false
  481. * </ul>
  482. * @access public
  483. */
  484. public static function find_uuid(database $database, string $category, string $subcategory, bool $status = true): array {
  485. //first look for false setting then override with enabled setting
  486. if ($status) {
  487. $uuids = self::find_uuid($database, $category, $subcategory, false);
  488. } else {
  489. //set defaults to not found
  490. $uuids = [];
  491. $uuids['uuid'] = '';
  492. $uuids['uuids'] = [];
  493. $uuids['table'] = '';
  494. $uuids['status'] = false;
  495. }
  496. $status_string = ($status) ? 'true' : 'false';
  497. //
  498. // Get the settings for false first then override the 'false' setting with the setting that is set to 'true'
  499. //
  500. //get global setting
  501. $result = self::get_uuids($database, 'default', $category, $subcategory, $status_string);
  502. if (!empty($result)) {
  503. $uuids['uuid'] = $result[0];
  504. $uuids['count'] = count($result);
  505. if (count($result) > 1) {
  506. $uuids['uuids'] = $result;
  507. } else {
  508. $uuids['uuids'] = [];
  509. }
  510. $uuids['table'] = 'default';
  511. $uuids['status'] = $status;
  512. }
  513. //override default with domain setting
  514. $result = self::get_uuids($database, 'domain', $category, $subcategory, $status_string);
  515. if (!empty($result)) {
  516. if ($uuids['count'] > 0) {
  517. $uuids['count'] += count($result);
  518. if (count($uuids['uuids']) > 0) {
  519. array_merge($uuids['uuids'], $result);
  520. } else {
  521. $ids[] = $uuids['uuid'];
  522. $uuids['uuids'] = array_merge($result, $ids);
  523. }
  524. } else {
  525. $uuids['count'] = count($result);
  526. if (count($result) > 1) {
  527. $uuids['uuids'] = $result;
  528. } else {
  529. $uuids['uuids'] = [];
  530. }
  531. }
  532. $uuids['uuid'] = $result[0];
  533. $uuids['table'] = 'domain';
  534. $uuids['status'] = $status;
  535. }
  536. //override domain with user setting
  537. $result = self::get_uuids($database, 'user', $category, $subcategory, $status_string);
  538. if (!empty($result)) {
  539. $uuids['uuid'] = $result[0];
  540. $uuids['count'] = count($result);
  541. if (count($result) > 1) {
  542. $uuids['uuids'] = $result;
  543. } else {
  544. $uuids['uuids'] = [];
  545. }
  546. $uuids['table'] = 'user';
  547. $uuids['status'] = $status;
  548. }
  549. return $uuids;
  550. }
  551. /**
  552. * Finds all UUIDs of a maintenance setting searching in the default settings, domain settings, and user settings tables.
  553. * @param database $database Already connected database object
  554. * @param string $category Main category
  555. * @param string $subcategory Subcategory or name of the setting
  556. * @param bool $status Used for internal use but could be used to find a setting that is currently disabled
  557. * @return array Two-dimensional array of matching database records
  558. * @access public
  559. */
  560. public static function find_all_uuids(database $database, string $category, string $subcategory, bool $status = true): array {
  561. $matches = [];
  562. //first look for false settings
  563. if ($status) {
  564. $matches = self::find_all_uuids($database, $category, $subcategory, false);
  565. }
  566. $status_string = ($status) ? 'true' : 'false';
  567. $tables = ['default', 'domain', 'user'];
  568. foreach ($tables as $table) {
  569. $sql = "select {$table}_setting_uuid, {$table}_setting_value from v_{$table}_settings s";
  570. $sql .= " where s.{$table}_setting_category = :category";
  571. $sql .= " and s.{$table}_setting_subcategory = :subcategory";
  572. $sql .= " and s.{$table}_setting_enabled = '$status_string'";
  573. //set search params
  574. $params = [];
  575. $params['category'] = $category;
  576. $params['subcategory'] = $subcategory;
  577. $result = $database->select($sql, $params, 'all');
  578. if (!empty($result)) {
  579. foreach ($result as $record) {
  580. $uuid = $record["{$table}_setting_uuid"];
  581. $value = $record["{$table}_setting_value"];
  582. $domain_uuid = $database->select("select domain_uuid from v_{$table}_settings where {$table}_setting_uuid = '$uuid'", null, 'column');
  583. if ($domain_uuid == false) {
  584. $domain_uuid = null;
  585. }
  586. $matches[] = [
  587. 'uuid' => $uuid,
  588. 'table' => $table,
  589. 'category' => $category,
  590. 'subcategory' => $subcategory,
  591. 'status' => $status,
  592. 'value' => $value,
  593. 'domain_uuid' => $domain_uuid,
  594. ];
  595. }
  596. }
  597. }
  598. return $matches;
  599. }
  600. /**
  601. * Called by the find_uuid function to actually search database using prepared data structures
  602. * @param database $database Database object
  603. * @param string $table Either 'default' or 'domain'
  604. * @param string $category Category value to match
  605. * @param string $subcategory Subcategory value to match
  606. * @param string $status Either 'true' or 'false'
  607. * @return array
  608. */
  609. public static function get_uuids(database $database, string $table, string $category, string $subcategory, string $status): array {
  610. $uuid = [];
  611. $sql = "select {$table}_setting_uuid from v_{$table}_settings s";
  612. $sql .= " where s.{$table}_setting_category = :category";
  613. $sql .= " and s.{$table}_setting_subcategory = :subcategory";
  614. $sql .= " and s.{$table}_setting_enabled = '$status'";
  615. //set search params
  616. $params = [];
  617. $params['category'] = $category;
  618. $params['subcategory'] = $subcategory;
  619. if ($table === 'domain' && !empty($_SESSION['domain_uuid']) && is_uuid($_SESSION['domain_uuid'])) {
  620. $sql .= " and s.domain_uuid = :domain_uuid";
  621. $params['domain_uuid'] = $_SESSION['domain_uuid'];
  622. }
  623. if ($table === 'user' && !empty($_SESSION['user_uuid']) && is_uuid($_SESSION['user_uuid'])) {
  624. $sql .= " and s.user_uuid = :user_uuid";
  625. $params['user_uuid'] = $_SESSION['user_uuid'];
  626. }
  627. $result = $database->select($sql, $params);
  628. if (!empty($result)) {
  629. if (is_array($result)) {
  630. $uuids = array_map(function ($value) use ($table) {
  631. if (is_array($value)) {
  632. return $value["{$table}_setting_uuid"];
  633. } else {
  634. return $value;
  635. }
  636. }, $result);
  637. $uuid = $uuids;
  638. } else {
  639. $uuid[] = $result;
  640. }
  641. }
  642. return $uuid;
  643. }
  644. /**
  645. * Returns the record set of the UUID in the table or an empty array
  646. * @param database $database
  647. * @param string $table Either 'domain' or 'default'
  648. * @param string $uuid
  649. * @return array
  650. */
  651. public static function get_value_by_uuid(database $database, string $table, string $uuid): array {
  652. if ($table === 'domain' || $table === 'default') {
  653. $sql = "select * from v_{$table}_settings"
  654. . " where {$table}_setting_uuid = :uuid";
  655. $parameters = [];
  656. $parameters['uuid'] = $uuid;
  657. $result = $database->select($sql, $parameters, 'row');
  658. if (!empty($result)) {
  659. return $result;
  660. }
  661. }
  662. return [];
  663. }
  664. public static function has_database_maintenance($object_or_class): bool {
  665. return method_exists($object_or_class, 'database_maintenance');
  666. }
  667. public static function has_filesystem_maintenance($object_or_class): bool {
  668. return method_exists($object_or_class, 'filesystem_maintenance');
  669. }
  670. }