diff --git a/app/phrases/phrase.php b/app/phrases/phrase.php new file mode 100644 index 0000000000..7e304b6512 --- /dev/null +++ b/app/phrases/phrase.php @@ -0,0 +1,30 @@ + + * Portions created by the Initial Developer are Copyright (C) 2008-2024 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark J Crane + * Tim Fry + */ + +echo ""; +echo ""; diff --git a/app/phrases/phrase_edit.php b/app/phrases/phrase_edit.php index 00b632083a..878542bc0d 100644 --- a/app/phrases/phrase_edit.php +++ b/app/phrases/phrase_edit.php @@ -25,244 +25,286 @@ */ //includes files - require_once dirname(__DIR__, 2) . "/resources/require.php"; - require_once "resources/check_auth.php"; +require_once dirname(__DIR__, 2) . "/resources/require.php"; +require_once "resources/check_auth.php"; //check permissions - if (permission_exists('phrase_add') || permission_exists('phrase_edit')) { - //access granted - } - else { - echo "access denied"; - exit; +if (permission_exists('phrase_add') || permission_exists('phrase_edit')) { + //access granted +} +else { + echo "access denied"; + exit; +} + +function build_data_array_from_post(settings $settings) { + global $domain_uuid, $drop_rows; + $phrase_uuid = $_POST['phrase_uuid']; + $array = []; + $drop_rows = []; + $drop_row_count = 0; + + //load sound files from the switch so we can validate selections + $sound_files = (new file)->sounds(); + + //update the phrase information + $array['phrases'][0]['domain_uuid'] = $domain_uuid; + $array['phrases'][0]['phrase_uuid'] = $phrase_uuid; + $array['phrases'][0]['phrase_name'] = $_POST['phrase_name']; + $array['phrases'][0]['phrase_language'] = $_POST['phrase_language']; + $array['phrases'][0]['phrase_enabled'] = $_POST['phrase_enabled']; + $array['phrases'][0]['phrase_description'] = $_POST['phrase_description']; + + //recording_files are: + // 'recording_uuid' => 'recording.wav' + // OR + // 'recording_uuid' => '${lua streamfile.lua ' . base64_data .'}' + $recording_files = phrases::get_all_domain_recordings($settings); + + // + // Create two arrays - one for rows to delete and one for new/updated rows + // + for ($i = 0; $i < count($_POST['phrase_detail_function']); $i++) { + //check for function to perform + $phrase_detail_function = $_POST['phrase_detail_function'][$i]; + $phrase_detail_data = null; + $recording_uuid_or_file = ''; + $phrase_detail_uuid = ''; + //check for the empty rows to delete -- 0,false,null is valid + if (strlen($_POST['phrase_detail_data'][$i]) === 0 + && !empty($_POST['phrase_detail_uuid'][$i]) + && empty($_POST['slider'][$i]) + && empty($_POST['phrase_detail_text'][$i])) { + $drop_rows['phrase_details'][$drop_row_count++]['phrase_detail_uuid'] = $_POST['phrase_detail_uuid'][$i]; + continue; + } + switch ($phrase_detail_function) { + case 'play-file': + //only save rows with data + if (!empty($_POST['phrase_detail_data'][$i])) { + $recording_uuid_or_file = $_POST['phrase_detail_data'][$i]; + //check for valid recordings and files + if (is_uuid($recording_uuid_or_file)) { + //recording UUID + $phrase_detail_data = $recording_files[$recording_uuid_or_file]; + } else { + //not a recording so must be valid path inside the switch recording files + if (in_array($recording_uuid_or_file, $sound_files)) { + //valid switch audio file + $phrase_detail_data = $recording_uuid_or_file; + } else { + //ignore an invalid audio file + continue(2); + } + } + //build data array + if ($_POST['phrase_detail_function'][$i] == 'execute' && substr($_POST['phrase_detail_data'][$i], 0,5) != "sleep" && !permission_exists("phrase_execute")) { + header("Location: phrase_edit.php?id=".$phrase_uuid); + exit; + } + } + break; + case 'pause': + //check for value + $phrase_detail_data = $_POST['slider'][$i]; + break; + case 'execute': + //check for the empty rows to delete + if (empty($_POST['phrase_detail_text'][$i]) && !empty($_POST['phrase_detail_uuid'][$i])) { + $drop_rows['phrase_details'][$drop_row_count++]['phrase_detail_uuid'] = $_POST['phrase_detail_uuid'][$i]; + continue(2); + } + $phrase_detail_data = $_POST['phrase_detail_text'][$i]; + break; + } + + $_POST['phrase_detail_tag'] = 'action'; // default, for now + $_POST['phrase_detail_group'] = "0"; // one group, for now + + if ($phrase_detail_data !== null) { + if (!empty($_POST['phrase_detail_uuid'][$i])) { + //update existing records in the database + $phrase_detail_uuid = $_POST['phrase_detail_uuid'][$i]; + } else { + //new record + $phrase_detail_uuid = uuid(); + } + $array['phrase_details'][$i]['phrase_detail_uuid'] = $phrase_detail_uuid; + $array['phrase_details'][$i]['phrase_uuid'] = $phrase_uuid; + $array['phrase_details'][$i]['domain_uuid'] = $domain_uuid; + $array['phrase_details'][$i]['phrase_detail_order'] = $i; + $array['phrase_details'][$i]['phrase_detail_tag'] = $_POST['phrase_detail_tag']; + $array['phrase_details'][$i]['phrase_detail_pattern'] = $_POST['phrase_detail_pattern'] ?? null; + $array['phrase_details'][$i]['phrase_detail_function'] = $phrase_detail_function; + $array['phrase_details'][$i]['phrase_detail_data'] = $phrase_detail_data; //path and filename of recording + $array['phrase_details'][$i]['phrase_detail_method'] = $_POST['phrase_detail_method'] ?? null; + $array['phrase_details'][$i]['phrase_detail_type'] = $_POST['phrase_detail_type'] ?? null; + $array['phrase_details'][$i]['phrase_detail_group'] = $_POST['phrase_detail_group']; + } } + return $array; +} + +//set default domain +if (empty($domain_uuid)) { + $domain_uuid = $_SESSION['domain_uuid'] ?? ''; +} + +//set default user +if (empty($user_uuid)) { + $user_uuid = $_SESSION['user_uuid'] ?? ''; +} //add multi-lingual support - $language = new text; - $text = $language->get(); +$language = new text; +$text = $language->get(); + +//ensure we have a database object +$database = database::new(); + +//ensure we have a settings object +$settings = new settings(['database' => $database, 'domain_uuid' => $domain_uuid, 'user_uuid' => $user_uuid]); //add the defaults - $phrase_name = ''; - $phrase_language = ''; - $phrase_description = ''; +$phrase_name = ''; +$phrase_language = ''; +$phrase_description = ''; //set the action as an add or an update - if (!empty($_REQUEST["id"])) { - $action = "update"; - $phrase_uuid = $_REQUEST["id"]; - } - else { - $action = "add"; - } +if (!empty($_REQUEST["id"])) { + $action = "update"; + $phrase_uuid = $_REQUEST["id"]; +} +else { + $action = "add"; +} //get the form value and set to php variables - if (count($_POST) > 0) { +if (count($_POST) > 0) { - //process the http post data by submitted action - if (!empty($_POST['action']) && is_uuid($_POST['phrase_uuid'])) { - $array[0]['checked'] = 'true'; - $array[0]['uuid'] = $_POST['phrase_uuid']; + //process the http post data by submitted action + if (!empty($_POST['action']) && is_uuid($_POST['phrase_uuid'])) { + $array[0]['checked'] = 'true'; + $array[0]['uuid'] = $_POST['phrase_uuid']; - switch ($_POST['action']) { - case 'delete': - if (permission_exists('phrase_delete')) { - $obj = new phrases; - $obj->delete($array); - } - break; - } - - header('Location: phrases.php'); - exit; + switch ($_POST['action']) { + case 'delete': + if (permission_exists('phrase_delete')) { + $obj = new phrases; + $obj->delete($array); + } + break; } - if (permission_exists('phrase_domain')) { - $domain_uuid = $_POST["domain_uuid"]; + header('Location: phrases.php'); + exit; } - $phrase_name = $_POST["phrase_name"]; - $phrase_language = $_POST["phrase_language"]; - $phrase_enabled = $_POST["phrase_enabled"] ?? 'false'; - $phrase_description = $_POST["phrase_description"]; - $phrase_details_delete = $_POST["phrase_details_delete"] ?? ''; - //clean the name - $phrase_name = str_replace(" ", "_", $phrase_name); - $phrase_name = str_replace("'", "", $phrase_name); + if (permission_exists('phrase_domain')) { + $domain_uuid = $_POST["domain_uuid"]; } + $phrase_name = $_POST["phrase_name"]; + $phrase_language = $_POST["phrase_language"]; + $phrase_enabled = $_POST["phrase_enabled"] ?? 'false'; + $phrase_description = $_POST["phrase_description"]; + $phrase_details_delete = $_POST["phrase_details_delete"] ?? ''; + + //clean the name + $phrase_name = str_replace(" ", "_", $phrase_name); + $phrase_name = str_replace("'", "", $phrase_name); +} //process the changes from the http post if (count($_POST) > 0 && empty($_POST["persistformvar"])) { - + //get the uuid - if ($action == "update") { - $phrase_uuid = $_POST["phrase_uuid"]; - } + if ($action == "update") { + $phrase_uuid = $_POST["phrase_uuid"]; + } //validate the token - $token = new token; - if (!$token->validate($_SERVER['PHP_SELF'])) { - message::add($text['message-invalid_token'],'negative'); - header('Location: phrases.php'); - exit; - } + $token = new token; + if (!$token->validate($_SERVER['PHP_SELF'])) { + message::add($text['message-invalid_token'],'negative'); + header('Location: phrases.php'); + exit; + } //check for all required data - $msg = ''; - if (empty($phrase_name)) { $msg .= $text['message-required']." ".$text['label-name']."
\n"; } - if (empty($phrase_language)) { $msg .= $text['message-required']." ".$text['label-language']."
\n"; } - if (!empty($msg) && empty($_POST["persistformvar"])) { - require_once "resources/header.php"; - require_once "resources/persist_form_var.php"; - echo "
\n"; - echo "
\n"; - echo $msg."
"; - echo "
\n"; - persistformvar($_POST); - echo "
\n"; - require_once "resources/footer.php"; - return; - } + $msg = ''; + if (empty($phrase_name)) { $msg .= $text['message-required']." ".$text['label-name']."
\n"; } + if (empty($phrase_language)) { $msg .= $text['message-required']." ".$text['label-language']."
\n"; } + if (!empty($msg) && empty($_POST["persistformvar"])) { + require_once "resources/header.php"; + require_once "resources/persist_form_var.php"; + echo "
\n"; + echo "
\n"; + echo $msg."
"; + echo "
\n"; + persistformvar($_POST); + echo "
\n"; + require_once "resources/footer.php"; + return; + } //add the phrase - if (empty($_POST["persistformvar"]) || $_POST["persistformvar"] != "true") { - if ($action == "add" && permission_exists('phrase_add')) { - //build data array - $phrase_uuid = uuid(); - $array['phrases'][0]['domain_uuid'] = $domain_uuid; - $array['phrases'][0]['phrase_uuid'] = $phrase_uuid; - $array['phrases'][0]['phrase_name'] = $phrase_name; - $array['phrases'][0]['phrase_language'] = $phrase_language; - $array['phrases'][0]['phrase_enabled'] = $phrase_enabled; - $array['phrases'][0]['phrase_description'] = $phrase_description; - - if ($_POST['phrase_detail_function'] != '') { - if ($_POST['phrase_detail_function'] == 'execute' && substr($_POST['phrase_detail_data'], 0,5) != "sleep" && !permission_exists("phrase_execute")) { - header("Location: phrase_edit.php"); - exit; - } - $_POST['phrase_detail_tag'] = 'action'; // default, for now - $_POST['phrase_detail_group'] = "0"; // one group, for now - - if ($_POST['phrase_detail_data'] != '') { - $phrase_detail_uuid = uuid(); - $array['phrase_details'][0]['phrase_detail_uuid'] = $phrase_detail_uuid; - $array['phrase_details'][0]['phrase_uuid'] = $phrase_uuid; - $array['phrase_details'][0]['domain_uuid'] = $domain_uuid; - $array['phrase_details'][0]['phrase_detail_order'] = $_POST['phrase_detail_order']; - $array['phrase_details'][0]['phrase_detail_tag'] = $_POST['phrase_detail_tag']; - $array['phrase_details'][0]['phrase_detail_pattern'] = $_POST['phrase_detail_pattern'] ?? null; - $array['phrase_details'][0]['phrase_detail_function'] = $_POST['phrase_detail_function']; - $array['phrase_details'][0]['phrase_detail_data'] = $_POST['phrase_detail_data']; - $array['phrase_details'][0]['phrase_detail_method'] = $_POST['phrase_detail_method'] ?? null; - $array['phrase_details'][0]['phrase_detail_type'] = $_POST['phrase_detail_type'] ?? null; - $array['phrase_details'][0]['phrase_detail_group'] = $_POST['phrase_detail_group']; - } + if (empty($_POST["persistformvar"]) || $_POST["persistformvar"] != "true") { + $message = ''; + switch ($action) { + case 'add': + //redirect when they don't have permission to add a phrase + if (!permission_exists('phrase_add')) { + header('Location: phrases.php'); + exit(); + } + //set user feedback message to add + $message = $text['message-add']; + $phrase_uuid = uuid(); + //do not break + case 'update': + //redirect when not adding and don't have permission to edit a phrase + if (empty($message)) { + if (!permission_exists('phrase_edit')) { + header('Location: phrases.php'); + exit(); } - - //execute insert - $p = permissions::new(); - $p->add('phrase_detail_add', 'temp'); - - $database = new database; - $database->app_name = 'phrases'; - $database->app_uuid = '5c6f597c-9b78-11e4-89d3-123b93f75cba'; - $database->save($array); - unset($array); - - $p->delete('phrase_detail_add', 'temp'); - - //save the xml to the file system if the phrase directory is set - //save_phrases_xml(); - - //clear the cache - $cache = new cache; - $cache->delete("languages:".$phrase_language.".".$phrase_uuid); - - //clear the destinations session array - if (isset($_SESSION['destinations']['array'])) { - unset($_SESSION['destinations']['array']); - } - - //send a redirect - message::add($text['message-add']); - header("Location: phrase_edit.php?id=".$phrase_uuid); - exit; - } - - //update the phrase - if ($action == "update" && permission_exists('phrase_edit')) { - //build data array - $array['phrases'][0]['domain_uuid'] = $domain_uuid; - $array['phrases'][0]['phrase_uuid'] = $phrase_uuid; - $array['phrases'][0]['phrase_name'] = $phrase_name; - $array['phrases'][0]['phrase_language'] = $phrase_language; - $array['phrases'][0]['phrase_enabled'] = $phrase_enabled; - $array['phrases'][0]['phrase_description'] = $phrase_description; - - if ($_POST['phrase_detail_function'] != '') { - if ($_POST['phrase_detail_function'] == 'execute' && substr($_POST['phrase_detail_data'], 0,5) != "sleep" && !permission_exists("phrase_execute")) { - header("Location: phrase_edit.php?id=".$phrase_uuid); - exit; - } - $_POST['phrase_detail_tag'] = 'action'; // default, for now - $_POST['phrase_detail_group'] = "0"; // one group, for now - - if ($_POST['phrase_detail_data'] != '') { - $phrase_detail_uuid = uuid(); - $array['phrase_details'][0]['phrase_detail_uuid'] = $phrase_detail_uuid; - $array['phrase_details'][0]['phrase_uuid'] = $phrase_uuid; - $array['phrase_details'][0]['domain_uuid'] = $domain_uuid; - $array['phrase_details'][0]['phrase_detail_order'] = $_POST['phrase_detail_order']; - $array['phrase_details'][0]['phrase_detail_tag'] = $_POST['phrase_detail_tag']; - $array['phrase_details'][0]['phrase_detail_pattern'] = $_POST['phrase_detail_pattern'] ?? null; - $array['phrase_details'][0]['phrase_detail_function'] = $_POST['phrase_detail_function']; - $array['phrase_details'][0]['phrase_detail_data'] = $_POST['phrase_detail_data']; - $array['phrase_details'][0]['phrase_detail_method'] = $_POST['phrase_detail_method'] ?? null; - $array['phrase_details'][0]['phrase_detail_type'] = $_POST['phrase_detail_type'] ?? null; - $array['phrase_details'][0]['phrase_detail_group'] = $_POST['phrase_detail_group']; - } - } - + //set user feedback message to update + $message = $text['message-update']; + } + if (!empty($_POST['phrase_detail_function'])) { + $array = build_data_array_from_post($settings); + } //execute update/insert - $p = permissions::new(); - $p->add('phrase_detail_add', 'temp'); - - $database = new database; - $database->app_name = 'phrases'; - $database->app_uuid = '5c6f597c-9b78-11e4-89d3-123b93f75cba'; + $p = permissions::new(); + $p->add('phrase_detail_add', 'temp'); + $p->add('phrase_detail_edit', 'temp'); + $p->add('phrase_detail_delete', 'temp'); + $database->app_name = 'phrases'; + $database->app_uuid = '5c6f597c-9b78-11e4-89d3-123b93f75cba'; + if (count($array) > 0) { $database->save($array); unset($array); - - $p->delete('phrase_detail_add', 'temp'); - - //remove checked phrase details - if ( - is_array($phrase_details_delete) - && @sizeof($phrase_details_delete) != 0 - ) { - $obj = new phrases; - $obj->phrase_uuid = $phrase_uuid; - $obj->delete_details($phrase_details_delete); - } - + } + if (count($drop_rows) > 0) { + $database->delete($drop_rows); + unset($drop_rows); + } + $p->delete('phrase_detail_add', 'temp'); //clear the cache - $cache = new cache; - $cache->delete("languages:".$phrase_language.".".$phrase_uuid); + $cache = new cache; + $cache->delete("languages:".$phrase_language.".".$phrase_uuid); //clear the destinations session array - if (isset($_SESSION['destinations']['array'])) { - unset($_SESSION['destinations']['array']); - } + if (isset($_SESSION['destinations']['array'])) { + unset($_SESSION['destinations']['array']); + } //send a redirect - message::add($text['message-update']); - header("Location: phrase_edit.php?id=".$phrase_uuid); - exit;; - - } - + message::add($message); + header("Location: phrase_edit.php?id=".$phrase_uuid); + exit; } - + } } //pre-populate the form @@ -276,7 +318,6 @@ $sql .= "and phrase_uuid = :phrase_uuid "; $parameters['domain_uuid'] = $domain_uuid; $parameters['phrase_uuid'] = $phrase_uuid; - $database = new database; $row = $database->select($sql, $parameters, 'row'); if (is_array($row) && @sizeof($row) != 0) { $phrase_name = $row["phrase_name"]; @@ -298,20 +339,22 @@ $sql .= "order by phrase_detail_order asc "; $parameters['domain_uuid'] = $_SESSION['domain_uuid']; $parameters['phrase_uuid'] = $phrase_uuid; - $database = new database; $phrase_details = $database->select($sql, $parameters, 'all'); unset($sql, $parameters); } //get the recording names from the database. - $sql = "select recording_name, recording_filename from v_recordings "; + $sql = "select recording_uuid, recording_name, recording_filename, domain_uuid from v_recordings "; $sql .= "where domain_uuid = :domain_uuid "; $sql .= "order by recording_name asc "; $parameters['domain_uuid'] = $_SESSION['domain_uuid']; - $database = new database; $recordings = $database->select($sql, $parameters, 'all'); unset($sql, $parameters); +//get the switch sound files + $file = new file; + $sound_files = $file->sounds(); + //create token $object = new token; $token = $object->create($_SERVER['PHP_SELF']); @@ -321,126 +364,76 @@ if ($action == 'update') { $document['title'] = $text['title-edit_phrase']; } require_once "resources/header.php"; -//js to control action form input - echo "\n"; +//javascript to control action form input using drag and drop + echo "\n"; + //show the content echo "
\n"; @@ -458,7 +451,7 @@ if ($action == "update" && permission_exists('phrase_delete')) { echo button::create(['type'=>'button','label'=>$text['button-delete'],'icon'=>$_SESSION['theme']['button_icon_delete'],'name'=>'btn_delete','style'=>'margin-left: 15px;','onclick'=>"modal_open('modal-delete','btn_delete');"]); } - echo button::create(['type'=>'submit','label'=>$text['button-save'],'icon'=>$_SESSION['theme']['button_icon_save'],'id'=>'btn_save','style'=>'margin-left: 15px;']); + echo button::create(['type'=>'submit','onclick'=>'submit_phrase()','label'=>$text['button-save'],'icon'=>$_SESSION['theme']['button_icon_save'],'id'=>'btn_save','style'=>'margin-left: 15px;']); echo " \n"; echo "
\n"; echo "\n"; @@ -491,88 +484,62 @@ echo " ".$text['description-language']."\n"; echo "\n"; echo "\n"; - + //structure row echo ""; echo "".$text['label-structure'].""; echo ""; - echo " \n"; + //style for dragging rows + echo " "; + //structure table + echo "
\n"; + //headings + echo " \n"; echo " \n"; - echo " \n"; + echo " \n"; echo " \n"; - echo " \n"; - if (!empty($phrase_details)) { - echo " \n"; - } + echo " \n"; echo " \n"; - if (!empty($phrase_details)) { - foreach($phrase_details as $x => $field) { - //clean up output for display - if ($field['phrase_detail_function'] == 'play-file' && substr($field['phrase_detail_data'], 0, 21) == '${lua streamfile.lua ') { - $phrase_detail_function = $text['label-play']; - $phrase_detail_data = str_replace('${lua streamfile.lua ', '', $field['phrase_detail_data']); - $phrase_detail_data = str_replace('}', '', $phrase_detail_data); - } - else if ($field['phrase_detail_function'] == 'execute' && substr($field['phrase_detail_data'], 0, 6) == 'sleep(') { - $phrase_detail_function = $text['label-pause']; - $phrase_detail_data = str_replace('sleep(', '', $field['phrase_detail_data']); - $phrase_detail_data = str_replace(')', '', $phrase_detail_data); - $phrase_detail_data = ($phrase_detail_data / 1000).'s'; // seconds - } - else if ($field['phrase_detail_function'] == 'play-file') { - $phrase_detail_function = $text['label-play']; - $phrase_detail_data = str_replace($_SESSION['switch']['recordings']['dir'].'/'.$_SESSION['domain_name'].'/', '', $field['phrase_detail_data']); - } - else { - $phrase_detail_function = $field['phrase_detail_function']; - $phrase_detail_data = $field['phrase_detail_data']; - } - echo "\n"; - echo " \n"; - echo " \n"; - echo " \n"; - echo " \n"; - echo "\n"; - } - } - unset($phrase_details, $field); - echo "\n"; + echo " \n"; + //draggable rows are initially empty + echo "\n"; + echo ""; + //show loading + echo "\n"; + //cloning row and buttons created outside of 'structure' table body + echo ""; + echo "\n"; + echo " "; echo " \n"; echo " \n"; - echo " \n"; - echo " \n"; - - echo " \n"; + echo "\n"; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo "\n"; echo "
".$text['label-function']."" . ($text['label-order'] ?? 'Order') . "".$text['label-action']."".$text['label-order']."\n"; - echo " ".$text['label-delete']."\n"; - echo " \n"; - echo " ".($text['label-recording'] ?? 'Recording')."
".escape($phrase_detail_function)." ".escape($phrase_detail_data)." ".$field['phrase_detail_order']." "; - if (is_uuid($field['phrase_detail_uuid'])) { - echo " \n"; - echo " \n"; - } - echo "
 
Loading...
 
 
"; + echo button::create(['type'=>'button','icon'=>$_SESSION['theme']['button_icon_add'], 'label' => $text['label-add'], 'onclick' => 'add_row()']); + echo button::create(['type'=>'button','icon'=>'fa-solid fa-minus', 'label' => $text['label-delete'], 'onclick' => 'remove_row()']); + echo "
 
\n"; echo " ".$text['description-structure']."\n"; @@ -650,5 +617,3 @@ //include the footer require_once "resources/footer.php"; - -?> diff --git a/app/phrases/phrase_responder.php b/app/phrases/phrase_responder.php new file mode 100644 index 0000000000..004042d8a4 --- /dev/null +++ b/app/phrases/phrase_responder.php @@ -0,0 +1,199 @@ + + * Portions created by the Initial Developer are Copyright (C) 2008-2024 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark J Crane + * Tim Fry + */ + +// require the class loader and global functions +//require dirname(__DIR__, 2) . '/resources/classes/auto_loader.php'; new auto_loader(); +//require dirname(__DIR__, 2) . '/resources/functions.php'; + +require_once dirname(__DIR__, 2) . '/resources/require.php'; + +// Disable output buffering and compression +ini_set('output_buffering', 'off'); +ini_set('zlib.output_compression', 'off'); +ini_set('implicit_flush', 1); +ob_implicit_flush(1); + +// Set headers to ensure immediate response +header('Content-Type: text/plain'); +header('Cache-Control: no-cache'); +header('Content-Encoding: none'); + +function fetch_recordings(database $database): array { + global $domain_uuid; + // guard against corrupt data + if (empty($domain_uuid) || !is_uuid($domain_uuid)) { + throw new Exception('Domain is invalid'); + } + // always return an array + $return_value = []; + $sql = "select recording_uuid, recording_name, recording_filename, domain_uuid from v_recordings "; + $sql .= "where domain_uuid = :domain_uuid "; + $sql .= "order by recording_name asc "; + $parameters['domain_uuid'] = $domain_uuid; + $recordings = $database->select($sql, $parameters, 'all'); + if (!empty($recordings)) { + $return_value = $recordings; + } + return $return_value; +} + +function fetch_sound_files(settings $settings) { + $return_value = []; + //get the switch sound files + $file = new file($settings); + $sound_files = $file->sounds(); + + //try finding display_name in the switch sound files + if (!empty($sound_files)) { + $return_value = $sound_files; + } + return $return_value; +} + +function fetch_phrase_details(settings $settings, string $phrase_uuid): array { + global $domain_uuid; + // guard against corrupt data + if (empty($domain_uuid) || !is_uuid($domain_uuid)) { + throw new Exception('Domain is invalid'); + } + if (empty($phrase_uuid) || !is_uuid($phrase_uuid)) { + throw new Exception('Phrase UUID is invalid'); + } + + $database = $settings->database(); + // set the return value to be an empty array + $return_value = []; + // get the phrase details + if (!empty($phrase_uuid)) { + $sql = "select * from v_phrase_details "; + $sql .= "where domain_uuid = :domain_uuid "; + $sql .= "and phrase_uuid = :phrase_uuid "; + $sql .= "order by phrase_detail_order asc "; + $parameters['domain_uuid'] = $domain_uuid; + $parameters['phrase_uuid'] = $phrase_uuid; + $phrase_details = $database->select($sql, $parameters, 'all'); + } + //existing details + if (!empty($phrase_details)) { + $recordings = fetch_recordings($database); + $sound_files = fetch_sound_files($settings); + //update the array to include the recording name for display in select box + foreach ($phrase_details as &$row) { + $row['display_name'] = ''; + $file = basename($row['phrase_detail_data']); + //get the display_name from recording name based on the file matched + foreach ($recordings as $key => $recordings_row) { + //match on filename first and then domain_uuid + if ($recordings_row['recording_filename'] === $file && $recordings_row['domain_uuid'] === $row['domain_uuid']) { + $row['display_name'] = $recordings[$key]['recording_name']; + break; + } + } + //check if display_name was not found in the recording names + if (strlen($row['display_name']) === 0) { + //try finding display_name in the switch sound files + if (!empty($sound_files)) { + //use optimized php function with strict comparison + $i = array_search($row['phrase_detail_data'], $sound_files, true); + //if found in the switch sound files + if ($i !== false) { + //set the display_name to the switch sound file name + $row['display_name'] = $sound_files[$i]; + } + } + } + } + $return_value = $phrase_details; + } + return $return_value; +} + +function fetch_domain_uuid(database $database): string { + $domain = $_SERVER['HTTP_HOST']; + $domain_uuid = ''; + $sql = 'select domain_uuid from v_domains where domain_name = :domain'; + $parameters = []; + $parameters['domain'] = $domain; + $result = $database->select($sql, $parameters, 'column'); + if (!empty($result)) { + $domain_uuid = $result; + } + return $domain_uuid; +} + +function send_message(string $json_data) { + echo $json_data . "\n"; + ob_flush(); + flush(); +} + +$config = config::load(); +$database = database::new(['config' => $config]); +$domain_uuid = fetch_domain_uuid($database); +$settings = new settings(['database' => $database, 'domain_uuid' => $domain_uuid]); + +// Set default response +$response = ['code' => 200, 'message' => '']; + +// Check if the request method is POST +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $message = ''; + // Get the raw POST data + $input = file_get_contents('php://input'); + + // Parse JSON data + $data = json_decode($input, true); // Decode JSON as associative array + + if (isset($data['request'])) { + try { + //check the data source requested + switch ($data['request']) { + case 'sound_files': + $message = fetch_sound_files($settings); + break; + case 'recordings': + $message = fetch_recordings($database); + break; + case 'phrase_details': + $phrase_uuid = $data['data']; + $message = fetch_phrase_details($settings, $phrase_uuid); + break; + } + } catch (Exception $e) { + $response['code'] = 500; + $message = $e->getMessage(); + } + } + //save the message + $response['message'] = $message; + //send the response + send_message(json_encode($response)); + exit(); +} + +exit(); diff --git a/app/phrases/resources/classes/phrases.php b/app/phrases/resources/classes/phrases.php index 8fa677fe8a..408d11eaa6 100644 --- a/app/phrases/resources/classes/phrases.php +++ b/app/phrases/resources/classes/phrases.php @@ -75,7 +75,7 @@ $token = new token; if (!$token->validate($_SERVER['PHP_SELF'])) { message::add($text['message-invalid_token'],'negative'); - header('Location: '.$this->list_page); + header('Location: '.$this->list_page);$is_uuid exit; } @@ -429,4 +429,70 @@ } } //method - } //class + /** + * Returns a path and filename of the recording_uuid provided from the database. + * If the recording is a base64 encoded file the file and path may be empty. + * @param database $database Database object + * @param string $recording_uuid Recording UUID + * @return string recording path and filename or an empty string when not found or base64 + */ + public static function get_recording_filename(database $database, string $recording_uuid): string { + $sql = "select recording_filename from v_recordings"; + $sql .= " where recording_uuid = :recording_uuid"; + $parameters = []; + $parameters['recording_uuid'] = $recording_uuid; + $result = $database->select($sql, $parameters, 'column'); + if ($result !== false) { + return $result; + } + return ""; + } + + /** + * Returns an associative array of recordings with the uuid as key and recording filename as value. + * When the recording is a base64 encoded recording, the filename returned is the filename only with no path. + * @param settings $settings Settings object + * @param int $limit (Optional) Limit the number of results returned + * @return array + */ + public static function get_all_domain_recordings(settings $settings, int $limit = 0): array { + //set defaults + $recordings = []; + $parameters = []; + //get the database object from the settings object + $database = $settings->database(); + //get the domain name using the domain_uuid in the database object + $domain_uuid = $settings->domain_uuid(); + $domain_name = $database->select("SELECT domain_name from v_domains where domain_uuid = :domain_uuid", ['domain_uuid' => $domain_uuid], 'column'); + //get the recording directory + $recordings_dir = $settings->get('switch', 'recordings', '/var/lib/freeswitch/recordings') . DIRECTORY_SEPARATOR . $domain_name; + //build initial sql that ignores the domain_uuid + $sql = "SELECT recording_uuid, recording_filename, recording_base64 IS NOT NULL AS has_base64_recording FROM v_recordings"; + //add domain_uuid to sql when available + if (!empty($domain_name) && is_uuid($domain_uuid)) { + $sql .= " where domain_uuid = :domain_uuid"; + $parameters['domain_uuid'] = $domain_uuid; + } + //add limit to sql if needed + if (!empty($limit)) { + $sql .= " limit $limit"; + } + //get the result + $rows = $database->select($sql, $parameters); + //iterate over all rows returned to remap them to uuid => filename + if (!empty($rows)) { + //set the path and filename for each of the uuids + foreach($rows as $row) { + if ($row['has_base64_recording']) { + $recordings[$row['recording_uuid']] = '${lua streamfile.lua ' . basename($row['recording_filename']) .'}'; + } else { + $recordings[$row['recording_uuid']] = $recordings_dir . DIRECTORY_SEPARATOR . $row['recording_filename']; + } + } + } + //return recordings or empty array + return $recordings; + } + } +} + diff --git a/app/phrases/resources/javascript/phrase_edit.js b/app/phrases/resources/javascript/phrase_edit.js new file mode 100644 index 0000000000..fa38b1b2ba --- /dev/null +++ b/app/phrases/resources/javascript/phrase_edit.js @@ -0,0 +1,377 @@ +// use an async function so page works without delays +document.addEventListener("DOMContentLoaded", async function () { + // Initialize the select options + const select = document.getElementById('phrase_detail_data_empty'); + const grp_rec = document.createElement('optgroup'); + const grp_snd = document.createElement('optgroup'); + + // Add a blank entry + select.appendChild(new Option('', '')); + + // Add recordings + grp_rec.label = window.phrase_label_recordings; + try { + const phrase_recordings = await fetch_data({request: 'recordings', data: ''}); + for (let i = 0; i < phrase_recordings.length; i++) { + grp_rec.appendChild(new Option(phrase_recordings[i].recording_name, phrase_recordings[i].recording_uuid)); + } + select.appendChild(grp_rec); + } catch (error) { + console.error("Error fetching recordings:", error); + } + + // Add sounds + grp_snd.label = window.phrase_label_sounds; + try { + const phrase_sounds = await fetch_data({request: 'sound_files', data: ''}); + for (let i = 0; i < phrase_sounds.length; i++) { + grp_snd.appendChild(new Option(phrase_sounds[i], phrase_sounds[i])); + } + select.appendChild(grp_snd); + } catch (error) { + console.error("Error fetching recordings:", error); + } + + // add the existing data + add_existing(); + + // add empty row + add_row(); + + // Initialize draggable rows + add_draggable_rows(); +}); + +function remove_loading() { + //remove loading + const loading = document.getElementById('loading'); + if (loading) { + loading.remove(); + } +} + +async function fetch_data(command) { + try { + const response = await fetch('resources/phrase_responder.php', { + method: 'POST', // or 'GET' depending on your requirement + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(command) // If sending data with POST + }); + + const data = await response.json(); + + return data.message; + + } catch (error) { + console.error('Error:', error); + } +} + +// +// Inserts all existing records before the empty one +// +async function add_existing() { + //get the body structure + const tbody = document.getElementById('structure'); + //get the phrase id + const urlParams = new URLSearchParams(window.location.search); + const phrase_uuid = urlParams.get('id'); + //fetch the phrase details from the database + const phrase_details = await fetch_data({request: 'phrase_details', data: phrase_uuid}); + //display the phrase details + for (let index=0; index < phrase_details.length; index++) { + //add an empty row + add_row(); + //get the action select box + const select_action = document.getElementById('phrase_detail_function[' + index + ']'); + //set the chosen option + select_by_value(select_action, phrase_details[index].phrase_detail_function); + //get the data select box + const select_data = document.getElementById('phrase_detail_data[' + index + ']'); + //set the chosen option + select_by_text(select_data, phrase_details[index]['display_name']); + const uuid_field = document.getElementById('phrase_detail_uuid[' +index+']'); + uuid_field.value = phrase_details[index]['phrase_detail_uuid']; + //set the slider value + const slider = document.getElementById('slider['+index+']'); + const sleep = document.getElementById('sleep['+index+']'); + const phrase_detail_text = document.getElementById('phrase_detail_text['+index+']'); + //update the sleep data + if (phrase_details[index].phrase_detail_function === 'pause') { + sleep.value = phrase_details[index].phrase_detail_data; + slider.value = phrase_details[index].phrase_detail_data; + } + //update the execute text + if (phrase_details[index].phrase_detail_function === 'execute') { + phrase_detail_text.value = phrase_details[index].phrase_detail_data; + } + // Manually trigger the change event to select the proper display + if (phrase_details[index].phrase_detail_function !== 'play-file') { + const changeEvent = new Event('change', { bubbles: true }); + select_action.dispatchEvent(changeEvent); + } + } + + remove_loading(); +} + +// +// Set the selected index on a dropdown box based on the value (key) +// +function select_by_value(selectElement, valueToFind) { + // Loop through the options of the select element + for (let i = 0; i < selectElement.options.length; i++) { + if (selectElement.options[i].value === valueToFind) { + selectElement.selectedIndex = i; // Set the selected index + return; // Exit the loop once found + } + } + console.warn('Value not found in select options'); +} + +// +// Set the selected index on a dropdown box based on the text +// +function select_by_text(selectElement, textToFind) { + for (let i = 0; i < selectElement.options.length; i++) { + if (selectElement.options[i].text === textToFind) { + selectElement.selectedIndex = i; + return; + } + } + console.warn('Text not found in select options'); +} + +// +// Add draggable functionality to rows +// +function add_draggable_rows() { + const tableBody = document.getElementById('structure'); + let draggedRow = null; + + // Add drag listeners only to the leftmost cell on the row + tableBody.querySelectorAll('tr').forEach(row => { + const dragHandleCell = row.cells[0]; // Assuming the first cell is the one left to the dropdown + + if (!dragHandleCell) return; + + // Enable dragging from this cell + dragHandleCell.setAttribute('draggable', 'true'); + + dragHandleCell.addEventListener('dragstart', (e) => { + draggedRow = row; + row.classList.add('dragging'); + }); + + dragHandleCell.addEventListener('dragend', () => { + if (draggedRow) { + draggedRow.classList.remove('dragging'); + draggedRow = null; + } + }); + + dragHandleCell.addEventListener('dragover', (e) => { + e.preventDefault(); + const targetRow = e.target.closest('tr'); + if (targetRow && targetRow !== draggedRow) { + const bounding = targetRow.getBoundingClientRect(); + const offset = e.clientY - bounding.top; + if (offset > bounding.height / 2) { + targetRow.parentNode.insertBefore(draggedRow, targetRow.nextSibling); + } else { + targetRow.parentNode.insertBefore(draggedRow, targetRow); + } + } + }); + dragHandleCell.addEventListener('dragend', () => { + if (draggedRow) { + draggedRow.classList.remove('dragging'); + draggedRow = null; + update_order(); + } + }); + }); + +} + +// +// Function to update the 'name' attribute based on row numbers +// +function update_order() { + const tableBody = document.getElementById('structure'); + const rows = tableBody.querySelectorAll('tr'); + + //iterate over all rows to renumber them + rows.forEach((row, index) => { + //set 'name' attribute and id + row.setAttribute('name', 'row_' + index); + row.id = 'row_' + index; + + //get the select boxes + const select_list = row.querySelectorAll('td select'); //action and recording select dropdown boxes + + //get the input boxes + const input_boxes = row.querySelectorAll('td input'); + //uuid + const phrase_detail_uuid = input_boxes[0]; + phrase_detail_uuid.removeAttribute('id'); + phrase_detail_uuid.id = 'phrase_detail_uuid[' + index + ']'; + phrase_detail_uuid.name = phrase_detail_uuid.id; + //execute action + const phrase_detail_text = input_boxes[1]; + temp_value = phrase_detail_text.value; + phrase_detail_text.removeAttribute('id'); + phrase_detail_text.id = 'phrase_detail_text[' + index + ']'; + phrase_detail_text.name = phrase_detail_text.id; + phrase_detail_text.value = temp_value; + //slider + const slider = input_boxes[2]; + slider.removeAttribute('id'); + slider.id = 'slider[' + index + ']'; + slider.name = slider.id; + //sleep value + const sleep = input_boxes[3]; + temp_value = sleep.value; + sleep.removeAttribute('id'); + sleep.id = 'sleep[' + index + ']'; + sleep.name = sleep.id; + sleep.value = temp_value; + + //play, pause, execute select box + const select_function = select_list[0]; + select_function.removeAttribute('id'); + select_function.id = 'phrase_detail_function[' + index + ']' + select_function.name = select_function.id; + + //recording select box + const select_data = select_list[1]; + select_data.removeAttribute('id'); + select_data.id = 'phrase_detail_data[' + index + ']' + select_data.name = select_data.id; + + }); +} + +// +// Ensure the order is updated when submitting the form +// +function submit_phrase() { + //ensure order is updated before submitting form + update_order(); + //submit form + const form = document.getElementById('frm').submit(); +} + +// +// Add a new row to the table +// +function add_row() { + const tbody = document.getElementById('structure'); + + // current index is the count subtract the hidden row + const index = tbody.childElementCount; + + const newRow = document.getElementById('empty_row').cloneNode(true); + //reset id + newRow.removeAttribute('id'); + newRow.id = 'row_' + index; + + //un-hide row + newRow.style.display = ''; + + //reset 'name' attribute + newRow.setAttribute('name', 'recording_' + index); + + //get the select boxes + const select_list = newRow.querySelectorAll('td select'); //action and recording select dropdown boxes + //play, pause, execute select box + const select_action = select_list[0]; + select_action.removeAttribute('id'); + select_action.id = 'phrase_detail_function[' + index + ']'; + select_action.name = 'phrase_detail_function[' + index + ']'; + //recording select box + const select_data = select_list[1]; + select_data.removeAttribute('id'); + select_data.id = 'phrase_detail_data[' + index + ']'; + select_data.name = 'phrase_detail_data[' + index + ']'; + //uuid field + const uuid_field = newRow.querySelector('input[name="empty_uuid"]'); + uuid_field.removeAttribute('id'); + uuid_field.id = 'phrase_detail_uuid[' + index +']'; + uuid_field.name = 'phrase_detail_uuid[' + index +']'; + const phrase_detail_text = newRow.querySelector('input[name="empty_phrase_detail_text"]'); + phrase_detail_text.removeAttribute('id'); + phrase_detail_text.id = 'phrase_detail_text[' + index + ']'; + phrase_detail_text.name = 'phrase_detail_text[' + index + ']'; + //slider + const slider = newRow.querySelector('input[name="range"]'); + slider.removeAttribute('id'); + slider.id = 'slider[' + index + ']'; + slider.name = 'slider[' + index + ']'; + //sleep + const sleep = newRow.querySelector('input[name="sleep"]'); + sleep.removeAttribute('id'); + sleep.id = 'sleep[' + index + ']'; + sleep.name = 'sleep[' + index + ']'; + sleep.value = slider.value; + + let changing = false; + + slider.addEventListener('mousemove', function () { + changing = true; + sleep.value = slider.value; + changing = false; + }); + + sleep.addEventListener('keyup', function() { + if (!changing) { + if (sleep.value.length > 0) + slider.value = sleep.value; + else { + slider.value = 0; + } + } + }) + + //add switchable select box to text input box + select_action.addEventListener('change', function () { + if (select_action.value === 'execute') { + //show text box + select_data.style.display = 'none'; + slider.style.display = 'none'; + sleep.style.display = 'none'; + phrase_detail_text.style.display = 'block'; + } else if (select_action.value === 'pause') { + //show the range bar + select_data.style.display = 'none'; + phrase_detail_text.style.display = 'none'; + slider.style.display = 'block'; + sleep.style.display = 'block'; + } else { + //show drop down + phrase_detail_text.style.display = 'none'; + slider.style.display = 'none'; + sleep.style.display = 'none'; + select_data.style.display = 'block'; + } + }); + + //add the row to the table body + tbody.appendChild(newRow); + + //reinitialize draggable functionality on the row + add_draggable_rows(); + return index; +} + +// +// Remove the last row in the table +// +function remove_row() { + const tbody = document.getElementById('structure'); + if (tbody && tbody.rows.length > 1) { + tbody.lastElementChild.remove(); + } +} diff --git a/app/phrases/resources/javascript/phrase_fetch.js b/app/phrases/resources/javascript/phrase_fetch.js new file mode 100644 index 0000000000..2e899040b0 --- /dev/null +++ b/app/phrases/resources/javascript/phrase_fetch.js @@ -0,0 +1,52 @@ +/* + * 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/ + * + * 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 Initial Developer of the Original Code is + * Mark J Crane + * Portions created by the Initial Developer are Copyright (C) 2008-2024 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark J Crane + * Tim Fry + */ + +async function fetchData() { + try { + const response = await fetch('phrase_responder.php', { + method: 'POST', // or 'GET' depending on your requirement + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ request: 'phrase_details', phrase_uuid: '86141ace-b218-4f07-b412-fe02d4fdde17' }) // If sending data with POST + }); + + const data = await response.text(); + const json = JSON.parse(data); + + const body = document.body; + const input = document.createElement('input'); + input.type = 'text'; + input.value = json.message; + body.appendChild(input); + + console.log(data); + } catch (error) { + console.error('Error:', error); + } +} + +fetchData(); diff --git a/app/phrases/resources/phrase_responder.php b/app/phrases/resources/phrase_responder.php new file mode 100644 index 0000000000..9ac9b76d2e --- /dev/null +++ b/app/phrases/resources/phrase_responder.php @@ -0,0 +1,204 @@ + + * Portions created by the Initial Developer are Copyright (C) 2008-2024 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark J Crane + * Tim Fry + */ + +require_once dirname(__DIR__, 3) . '/resources/require.php'; + +//check permissions +if (permission_exists('phrase_add') || permission_exists('phrase_edit')) { + //access granted +} +else { + echo "access denied"; + exit; +} + +// Disable output buffering and compression +ini_set('output_buffering', 'off'); +ini_set('zlib.output_compression', 'off'); +ini_set('implicit_flush', 1); +ob_implicit_flush(1); + +// Set headers to ensure immediate response +header('Content-Type: text/plain'); +header('Cache-Control: no-cache'); +header('Content-Encoding: none'); + +function fetch_recordings(database $database): array { + global $domain_uuid; + // guard against corrupt data + if (empty($domain_uuid) || !is_uuid($domain_uuid)) { + throw new Exception('Domain is invalid'); + } + // always return an array + $return_value = []; + $sql = "select recording_uuid, recording_name, recording_filename, domain_uuid from v_recordings "; + $sql .= "where domain_uuid = :domain_uuid "; + $sql .= "order by recording_name asc "; + $parameters['domain_uuid'] = $domain_uuid; + $recordings = $database->select($sql, $parameters, 'all'); + if (!empty($recordings)) { + $return_value = $recordings; + } + return $return_value; +} + +function fetch_sound_files(settings $settings) { + $return_value = []; + //get the switch sound files + $file = new file($settings); + $sound_files = $file->sounds(); + + //try finding display_name in the switch sound files + if (!empty($sound_files)) { + $return_value = $sound_files; + } + return $return_value; +} + +function fetch_phrase_details(settings $settings, string $phrase_uuid): array { + global $domain_uuid; + // guard against corrupt data + if (empty($domain_uuid) || !is_uuid($domain_uuid)) { + throw new Exception('Domain is invalid'); + } + if (empty($phrase_uuid) || !is_uuid($phrase_uuid)) { + throw new Exception('Phrase UUID is invalid'); + } + + $database = $settings->database(); + // set the return value to be an empty array + $return_value = []; + // get the phrase details + if (!empty($phrase_uuid)) { + $sql = "select * from v_phrase_details "; + $sql .= "where domain_uuid = :domain_uuid "; + $sql .= "and phrase_uuid = :phrase_uuid "; + $sql .= "order by phrase_detail_order asc "; + $parameters['domain_uuid'] = $domain_uuid; + $parameters['phrase_uuid'] = $phrase_uuid; + $phrase_details = $database->select($sql, $parameters, 'all'); + } + //existing details + if (!empty($phrase_details)) { + $recordings = fetch_recordings($database); + $sound_files = fetch_sound_files($settings); + //update the array to include the recording name for display in select box + foreach ($phrase_details as &$row) { + $row['display_name'] = ''; + $file = basename($row['phrase_detail_data']); + //get the display_name from recording name based on the file matched + foreach ($recordings as $key => $recordings_row) { + //match on filename first and then domain_uuid + if ($recordings_row['recording_filename'] === $file && $recordings_row['domain_uuid'] === $row['domain_uuid']) { + $row['display_name'] = $recordings[$key]['recording_name']; + break; + } + } + //check if display_name was not found in the recording names + if (strlen($row['display_name']) === 0) { + //try finding display_name in the switch sound files + if (!empty($sound_files)) { + //use optimized php function with strict comparison + $i = array_search($row['phrase_detail_data'], $sound_files, true); + //if found in the switch sound files + if ($i !== false) { + //set the display_name to the switch sound file name + $row['display_name'] = $sound_files[$i]; + } + } + } + } + $return_value = $phrase_details; + } + return $return_value; +} + +function fetch_domain_uuid(database $database): string { + $domain = $_SERVER['HTTP_HOST']; + $domain_uuid = ''; + $sql = 'select domain_uuid from v_domains where domain_name = :domain'; + $parameters = []; + $parameters['domain'] = $domain; + $result = $database->select($sql, $parameters, 'column'); + if (!empty($result)) { + $domain_uuid = $result; + } + return $domain_uuid; +} + +function send_message(string $json_data) { + echo $json_data . "\n"; + ob_flush(); + flush(); +} + +$config = config::load(); +$database = database::new(['config' => $config]); +$domain_uuid = fetch_domain_uuid($database); +$settings = new settings(['database' => $database, 'domain_uuid' => $domain_uuid]); + +// Set default response +$response = ['code' => 200, 'message' => '']; + +// Check if the request method is POST +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $message = ''; + // Get the raw POST data + $input = file_get_contents('php://input'); + + // Parse JSON data + $data = json_decode($input, true); // Decode JSON as associative array + + if (isset($data['request'])) { + try { + //check the data source requested + switch ($data['request']) { + case 'sound_files': + $message = fetch_sound_files($settings); + break; + case 'recordings': + $message = fetch_recordings($database); + break; + case 'phrase_details': + $phrase_uuid = $data['data']; + $message = fetch_phrase_details($settings, $phrase_uuid); + break; + } + } catch (Exception $e) { + $response['code'] = 500; + $message = $e->getMessage(); + } + } + //save the message + $response['message'] = $message; + //send the response + send_message(json_encode($response)); + exit(); +} + +exit(); diff --git a/app/phrases/resources/styles/phrase_edit.css b/app/phrases/resources/styles/phrase_edit.css new file mode 100644 index 0000000000..353b97cae8 --- /dev/null +++ b/app/phrases/resources/styles/phrase_edit.css @@ -0,0 +1,11 @@ +.draggable-row { + cursor: move; +} + +.dragging { + opacity: 0.5; +} + +.target-background { + background-color: #ffc107; +} diff --git a/resources/classes/file.php b/resources/classes/file.php index 26c3c8f9ec..41d7b3a5b4 100644 --- a/resources/classes/file.php +++ b/resources/classes/file.php @@ -2,8 +2,6 @@ /** * cache class provides an abstracted cache - * - * @method string glob */ class file { @@ -12,12 +10,17 @@ class file { */ public $recursive; public $files; + private $settings; /** * Called when the object is created + * @param settings $settings Settings object */ - public function __construct() { - //place holder + public function __construct($settings = null) { + if ($settings === null) { + $settings = new settings(); + } + $this->settings = $settings; } /** @@ -66,8 +69,8 @@ class file { if (!isset($voice)) { $voice = 'callie'; } //set the variables - if (!empty($_SESSION['switch']['sounds']['dir']) && file_exists($_SESSION['switch']['sounds']['dir'])) { - $dir = $_SESSION['switch']['sounds']['dir'].'/'.$language.'/'.$dialect.'/'.$voice; + if (!empty($this->settings->get('switch', 'sounds')) && file_exists($this->settings->get('switch', 'sounds'))) { + $dir = $this->settings->get('switch', 'sounds').'/'.$language.'/'.$dialect.'/'.$voice; $rate = '8000'; $files = $this->glob($dir.'/*/'.$rate, true); } @@ -93,5 +96,3 @@ class file { $files = $file->sounds(); print_r($files); */ - -?> diff --git a/resources/classes/settings.php b/resources/classes/settings.php index b190013169..f7eae6376f 100644 --- a/resources/classes/settings.php +++ b/resources/classes/settings.php @@ -110,6 +110,22 @@ class settings implements clear_cache { return $this->database; } + /** + * Returns the domain_uuid that was used to load the settings + * @return string domain_uuid or an empty string + */ + public function domain_uuid(): string { + return $this->domain_uuid; + } + + /** + * Returns the user_uuid that was used to load the settings + * @return string user_uuid or an empty string + */ + public function user_uuid(): string { + return $this->user_uuid; + } + /** * Reloads the settings from the database */