mirror of
https://github.com/fusionpbx/fusionpbx.git
synced 2025-12-30 00:53:50 +00:00
Add call recordings to the transcribe queue
This commit is contained in:
@@ -138,7 +138,7 @@
|
||||
if ($transcribe_enabled && !empty($transcribe_engine) && !empty($call_recordings) && is_array($call_recordings)) {
|
||||
$transcriptions_exists = false;
|
||||
foreach ($call_recordings as $row) {
|
||||
if (!empty($row['call_recording_transcription'])) { $transcriptions_exists = true; }
|
||||
// if (!empty($row['call_recording_transcription'])) { $transcriptions_exists = true; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,8 +282,8 @@
|
||||
if (permission_exists('call_recording_download')) {
|
||||
echo button::create(['type'=>'button','title'=>$text['label-download'],'icon'=>$settings->get('theme', 'button_icon_download'),'link'=>'download.php?id='.urlencode($row['call_recording_uuid']).'&binary']);
|
||||
}
|
||||
if (permission_exists('call_recording_transcribe') && $transcribe_enabled && !empty($transcribe_engine) && $transcriptions_exists === true) {
|
||||
echo button::create(['type'=>'button','title'=>$text['label-transcription'],'icon'=>'quote-right','style'=>(empty($row['call_recording_transcription']) ? 'visibility:hidden;' : null),'onclick'=>"document.getElementById('transcription_".$row['call_recording_uuid']."').style.display = document.getElementById('transcription_".$row['call_recording_uuid']."').style.display == 'none' ? 'table-row' : 'none'; this.blur(); return false;"]);
|
||||
if (permission_exists('call_recording_transcribe') && $transcribe_enabled && !empty($transcribe_engine) && !empty($row['call_recording_transcription'])) {
|
||||
echo button::create(['type'=>'button','title'=>$text['label-transcription'],'icon'=>'quote-right','style'=>'','link'=>PROJECT_PATH.'/app/xml_cdr/xml_cdr_details.php?id='.urlencode($row['call_recording_uuid'])]);
|
||||
}
|
||||
}
|
||||
echo " </td>\n";
|
||||
@@ -297,15 +297,15 @@
|
||||
echo " </td>\n";
|
||||
}
|
||||
echo "</tr>\n";
|
||||
if (permission_exists('call_recording_transcribe') && $transcribe_enabled && !empty($transcribe_engine) && !empty($row['call_recording_transcription'])) {
|
||||
echo "<tr style='display: none;'><td></td></tr>\n"; // dummy row to maintain same background color for transcription row
|
||||
echo "<tr id='transcription_".$row['call_recording_uuid']."' class='list-row' style='display: none;'>\n";
|
||||
echo " <td style='padding: 10px 20px 15px 20px;' colspan='".$col_count."'>\n";
|
||||
echo " <strong style='display: inline-block; font-size: 90%; margin-bottom: 10px;'>".$text['label-transcription']."...</strong><br />\n";
|
||||
echo escape($row['call_recording_transcription'])."\n";
|
||||
echo " </td>\n";
|
||||
echo "</tr>\n";
|
||||
}
|
||||
// if (permission_exists('call_recording_transcribe') && $transcribe_enabled && !empty($transcribe_engine) && !empty($row['call_recording_transcription'])) {
|
||||
// echo "<tr style='display: none;'><td></td></tr>\n"; // dummy row to maintain same background color for transcription row
|
||||
// echo "<tr id='transcription_".$row['call_recording_uuid']."' class='list-row' style='display: none;'>\n";
|
||||
// echo " <td style='padding: 10px 20px 15px 20px;' colspan='".$col_count."'>\n";
|
||||
// echo " <strong style='display: inline-block; font-size: 90%; margin-bottom: 10px;'>".$text['label-transcription']."...</strong><br />\n";
|
||||
// echo escape($row['call_recording_transcription'])."\n";
|
||||
// echo " </td>\n";
|
||||
// echo "</tr>\n";
|
||||
// }
|
||||
$x++;
|
||||
}
|
||||
unset($call_recordings);
|
||||
|
||||
@@ -209,7 +209,7 @@ class call_recordings {
|
||||
if (permission_exists($this->name . '_view')) {
|
||||
//add multi-lingual support
|
||||
$language = new text;
|
||||
$text = $language->get();
|
||||
$text = $language->get();
|
||||
|
||||
//validate the token
|
||||
$token = new token;
|
||||
@@ -225,8 +225,6 @@ class call_recordings {
|
||||
|
||||
//transcribe multiple recordings
|
||||
if ($transcribe_enabled && !empty($transcribe_engine) && is_array($records) && @sizeof($records) != 0) {
|
||||
//add the transcribe object
|
||||
$transcribe = new transcribe($this->settings);
|
||||
|
||||
//build the array
|
||||
$x = 0;
|
||||
@@ -235,27 +233,30 @@ class call_recordings {
|
||||
if (!empty($record['checked']) && $record['checked'] == 'true' && is_uuid($record['uuid'])) {
|
||||
|
||||
//get the call recording file name and path
|
||||
$sql = "select call_recording_name, call_recording_path ";
|
||||
$sql .= "from view_call_recordings ";
|
||||
$sql .= "where call_recording_uuid = :call_recording_uuid ";
|
||||
$sql .= "and call_recording_transcription is null ";
|
||||
$sql = "select call_recording_name, call_recording_path ";
|
||||
$sql .= "from view_call_recordings ";
|
||||
$sql .= "where call_recording_uuid = :call_recording_uuid ";
|
||||
$sql .= "and call_recording_transcription is null ";
|
||||
$parameters['call_recording_uuid'] = $record['uuid'];
|
||||
$field = $this->database->select($sql, $parameters, 'row');
|
||||
$field = $this->database->select($sql, $parameters, 'row');
|
||||
if (
|
||||
is_array($field) &&
|
||||
@sizeof($field) != 0 &&
|
||||
file_exists($field['call_recording_path'] . '/' . $field['call_recording_name'])
|
||||
) {
|
||||
//audio to text - get the transcription from the audio file
|
||||
$transcribe->audio_path = $field['call_recording_path'];
|
||||
$transcribe->audio_filename = $field['call_recording_name'];
|
||||
$record_transcription = $transcribe->transcribe();
|
||||
|
||||
//build call recording data array
|
||||
if (!empty($record_transcription)) {
|
||||
$array['xml_cdr'][$x]['xml_cdr_uuid'] = $record['uuid'];
|
||||
$array['xml_cdr'][$x]['record_transcription'] = $record_transcription;
|
||||
}
|
||||
//add the recording to the transcribe queue
|
||||
$array['transcribe_queue'][$x]['transcribe_queue_uuid'] = uuid();
|
||||
$array['transcribe_queue'][$x]['domain_uuid'] = $_SESSION['domain_uuid'];
|
||||
$array['transcribe_queue'][$x]['hostname'] = gethostname();
|
||||
$array['transcribe_queue'][$x]['transcribe_status'] = 'pending';
|
||||
$array['transcribe_queue'][$x]['transcribe_application_name'] = 'call_recordings';
|
||||
$array['transcribe_queue'][$x]['transcribe_application_uuid'] = '56165644-598d-4ed8-be01-d960bcb8ffed';
|
||||
$array['transcribe_queue'][$x]['transcribe_audio_path'] = $field['call_recording_path'];
|
||||
$array['transcribe_queue'][$x]['transcribe_audio_name'] = $field['call_recording_name'];
|
||||
$array['transcribe_queue'][$x]['transcribe_target_table'] = 'xml_cdr';
|
||||
$array['transcribe_queue'][$x]['transcribe_target_key_name'] = 'xml_cdr_uuid';
|
||||
$array['transcribe_queue'][$x]['transcribe_target_key_uuid'] = $record['uuid'];
|
||||
$array['transcribe_queue'][$x]['transcribe_target_column_name'] = 'record_transcription';
|
||||
|
||||
//increment the id
|
||||
$x++;
|
||||
@@ -270,17 +271,16 @@ class call_recordings {
|
||||
|
||||
//add temporary permissions
|
||||
$p = permissions::new();
|
||||
$p->add('xml_cdr_edit', 'temp');
|
||||
$p->add('transcribe_queue_add', 'temp');
|
||||
|
||||
//remove record_path, record_name and record_length
|
||||
$this->database->app_name = 'xml_cdr';
|
||||
$this->database->app_uuid = '4a085c51-7635-ff03-f67b-86e834422848';
|
||||
$this->database->save($array, false);
|
||||
$message = $this->database->message;
|
||||
unset($array);
|
||||
|
||||
//remove the temporary permissions
|
||||
$p->delete('xml_cdr_edit', 'temp');
|
||||
$p->delete('transcribe_queue_add', 'temp');
|
||||
|
||||
//set message
|
||||
message::add($text['message-audio_transcribed']);
|
||||
@@ -341,29 +341,29 @@ class call_recordings {
|
||||
if (!empty($storage_type) && $storage_type == 'base64' && !empty($row['call_recording_base64'])) {
|
||||
$sql .= ", call_recording_base64 ";
|
||||
}
|
||||
$sql .= "from view_call_recordings ";
|
||||
$sql .= "where call_recording_uuid = :call_recording_uuid ";
|
||||
$sql .= "from view_call_recordings ";
|
||||
$sql .= "where call_recording_uuid = :call_recording_uuid ";
|
||||
$parameters['call_recording_uuid'] = $this->recording_uuid;
|
||||
$parameters['time_zone'] = $time_zone;
|
||||
$row = $this->database->select($sql, $parameters, 'row');
|
||||
$parameters['time_zone'] = $time_zone;
|
||||
$row = $this->database->select($sql, $parameters, 'row');
|
||||
if (is_array($row) && @sizeof($row) != 0) {
|
||||
$call_recording_uuid = $row['call_recording_uuid'];
|
||||
$caller_id_name = $row['caller_id_name'];
|
||||
$caller_id_number = $row['caller_id_number'];
|
||||
$caller_destination = $row['caller_destination'];
|
||||
$destination_number = $row['destination_number'];
|
||||
$call_recording_name = $row['call_recording_name'];
|
||||
$call_recording_path = $row['call_recording_path'];
|
||||
$call_recording_date = $row['call_recording_date'];
|
||||
$call_direction = $row['call_direction'];
|
||||
$call_recording_year = $row['call_recording_year'];
|
||||
$call_recording_month_name = $row['call_recording_month_name'];
|
||||
$call_recording_month_number = $row['call_recording_month_number'];
|
||||
$call_recording_day = $row['call_recording_day'];
|
||||
$call_recording_time = $row['call_recording_time'];
|
||||
$call_recording_uuid = $row['call_recording_uuid'];
|
||||
$caller_id_name = $row['caller_id_name'];
|
||||
$caller_id_number = $row['caller_id_number'];
|
||||
$caller_destination = $row['caller_destination'];
|
||||
$destination_number = $row['destination_number'];
|
||||
$call_recording_name = $row['call_recording_name'];
|
||||
$call_recording_path = $row['call_recording_path'];
|
||||
$call_recording_date = $row['call_recording_date'];
|
||||
$call_direction = $row['call_direction'];
|
||||
$call_recording_year = $row['call_recording_year'];
|
||||
$call_recording_month_name = $row['call_recording_month_name'];
|
||||
$call_recording_month_number = $row['call_recording_month_number'];
|
||||
$call_recording_day = $row['call_recording_day'];
|
||||
$call_recording_time = $row['call_recording_time'];
|
||||
$call_recording_date_formatted = $row['call_recording_date_formatted'];
|
||||
$call_recording_time_formatted = $row['call_recording_time_formatted'];
|
||||
$call_recording_base64 = $row['call_recording_base64'];
|
||||
$call_recording_base64 = $row['call_recording_base64'];
|
||||
if (!empty($storage_type) && $storage_type == 'base64' && !empty($row['call_recording_base64'])) {
|
||||
file_put_contents($call_recording_path . '/' . $call_recording_name, base64_decode($row['call_recording_base64']));
|
||||
}
|
||||
@@ -440,7 +440,7 @@ class call_recordings {
|
||||
|
||||
//add multi-lingual support
|
||||
$language = new text;
|
||||
$text = $language->get();
|
||||
$text = $language->get();
|
||||
|
||||
//validate the token
|
||||
$token = new token;
|
||||
@@ -483,26 +483,26 @@ class call_recordings {
|
||||
if (!empty($storage_type) && $storage_type == 'base64') {
|
||||
$sql .= ", call_recording_base64 ";
|
||||
}
|
||||
$sql .= "from view_call_recordings ";
|
||||
$sql .= "where call_recording_uuid in ('" . implode("','", $uuids) . "') ";
|
||||
$sql .= "from view_call_recordings ";
|
||||
$sql .= "where call_recording_uuid in ('" . implode("','", $uuids) . "') ";
|
||||
$parameters['time_zone'] = $time_zone;
|
||||
$rows = $this->database->select($sql, $parameters, 'all');
|
||||
$rows = $this->database->select($sql, $parameters, 'all');
|
||||
if (!empty($rows) && is_array($rows) && @sizeof($rows) != 0) {
|
||||
foreach ($rows as $row) {
|
||||
$call_recording_uuid = $row['call_recording_uuid'];
|
||||
$caller_id_name = $row['caller_id_name'];
|
||||
$caller_id_number = $row['caller_id_number'];
|
||||
$caller_destination = $row['caller_destination'];
|
||||
$destination_number = $row['destination_number'];
|
||||
$call_recording_name = $row['call_recording_name'];
|
||||
$call_recording_path = $row['call_recording_path'];
|
||||
$call_recording_date = $row['call_recording_date'];
|
||||
$call_direction = $row['call_direction'];
|
||||
$call_recording_year = $row['call_recording_year'];
|
||||
$call_recording_month_name = $row['call_recording_month_name'];
|
||||
$call_recording_month_number = $row['call_recording_month_number'];
|
||||
$call_recording_day = $row['call_recording_day'];
|
||||
$call_recording_time = $row['call_recording_time'];
|
||||
$call_recording_uuid = $row['call_recording_uuid'];
|
||||
$caller_id_name = $row['caller_id_name'];
|
||||
$caller_id_number = $row['caller_id_number'];
|
||||
$caller_destination = $row['caller_destination'];
|
||||
$destination_number = $row['destination_number'];
|
||||
$call_recording_name = $row['call_recording_name'];
|
||||
$call_recording_path = $row['call_recording_path'];
|
||||
$call_recording_date = $row['call_recording_date'];
|
||||
$call_direction = $row['call_direction'];
|
||||
$call_recording_year = $row['call_recording_year'];
|
||||
$call_recording_month_name = $row['call_recording_month_name'];
|
||||
$call_recording_month_number = $row['call_recording_month_number'];
|
||||
$call_recording_day = $row['call_recording_day'];
|
||||
$call_recording_time = $row['call_recording_time'];
|
||||
$call_recording_date_formatted = $row['call_recording_date_formatted'];
|
||||
$call_recording_time_formatted = $row['call_recording_time_formatted'];
|
||||
if (!empty($storage_type) && $storage_type == 'base64' && !empty($row['call_recording_base64'])) {
|
||||
|
||||
@@ -84,47 +84,43 @@
|
||||
unset($sql, $parameters, $row);
|
||||
|
||||
//transcribe, if enabled
|
||||
if (
|
||||
!empty($_GET['action']) &&
|
||||
$_GET['action'] == 'transcribe' &&
|
||||
$transcribe_enabled &&
|
||||
!empty($transcribe_engine) &&
|
||||
empty($record_transcription) &&
|
||||
!empty($record_path) &&
|
||||
!empty($record_name) &&
|
||||
file_exists($record_path.'/'.$record_name)
|
||||
) {
|
||||
//add the transcribe object
|
||||
$transcribe = new transcribe($settings);
|
||||
//audio to text - get the transcription from the audio file
|
||||
$transcribe->audio_path = $record_path;
|
||||
$transcribe->audio_filename = $record_name;
|
||||
$record_transcription = $transcribe->transcribe();
|
||||
//build call recording data array
|
||||
if (!empty($record_transcription)) {
|
||||
$array['xml_cdr'][0]['xml_cdr_uuid'] = $uuid;
|
||||
$array['xml_cdr'][0]['record_transcription'] = $record_transcription;
|
||||
}
|
||||
//update the checked rows
|
||||
if (is_array($array) && @sizeof($array) != 0) {
|
||||
if (!empty($_GET['action']) && $_GET['action'] == 'transcribe' &&
|
||||
$transcribe_enabled && !empty($transcribe_engine) &&
|
||||
!empty($record_path) && !empty($record_name) &&
|
||||
file_exists($record_path.'/'.$record_name)) {
|
||||
|
||||
//add the recording to the transcribe queue
|
||||
$array['transcribe_queue'][$x]['transcribe_queue_uuid'] = uuid();
|
||||
$array['transcribe_queue'][$x]['domain_uuid'] = $_SESSION['domain_uuid'];
|
||||
$array['transcribe_queue'][$x]['hostname'] = gethostname();
|
||||
$array['transcribe_queue'][$x]['transcribe_status'] = 'pending';
|
||||
$array['transcribe_queue'][$x]['transcribe_application_name'] = 'call_recordings';
|
||||
$array['transcribe_queue'][$x]['transcribe_application_uuid'] = '56165644-598d-4ed8-be01-d960bcb8ffed';
|
||||
$array['transcribe_queue'][$x]['transcribe_audio_path'] = $record_path;
|
||||
$array['transcribe_queue'][$x]['transcribe_audio_name'] = $record_name;
|
||||
$array['transcribe_queue'][$x]['transcribe_target_table'] = 'xml_cdr';
|
||||
$array['transcribe_queue'][$x]['transcribe_target_key_name'] = 'xml_cdr_uuid';
|
||||
$array['transcribe_queue'][$x]['transcribe_target_key_uuid'] = $uuid;
|
||||
$array['transcribe_queue'][$x]['transcribe_target_column_name'] = 'record_transcription';
|
||||
|
||||
//add the checked rows
|
||||
if (is_array($array) && @sizeof($array) != 0) {
|
||||
//add temporary permissions
|
||||
$p = permissions::new();
|
||||
$p->add('xml_cdr_edit', 'temp');
|
||||
$p = permissions::new();
|
||||
$p->add('transcribe_queue_add', 'temp');
|
||||
|
||||
//remove record_path, record_name and record_length
|
||||
$database->save($array, false);
|
||||
//$message = $database->message;
|
||||
unset($array);
|
||||
$database->save($array, false);
|
||||
unset($array);
|
||||
|
||||
//remove the temporary permissions
|
||||
$p->delete('xml_cdr_edit', 'temp');
|
||||
$p->delete('transcribe_queue_add', 'temp');
|
||||
|
||||
//set message
|
||||
message::add($text['message-audio_transcribed']);
|
||||
|
||||
message::add($text['message-audio_transcribed']);
|
||||
}
|
||||
//redirect
|
||||
|
||||
//redirect
|
||||
header('Location: '.$_SERVER['PHP_SELF'].'?id='.$uuid);
|
||||
exit;
|
||||
}
|
||||
@@ -368,6 +364,28 @@
|
||||
$summary_array['hangup_cause'] = escape($hangup_cause);
|
||||
}
|
||||
|
||||
//convert the transcription into a conversation
|
||||
function conversational_html($transcription) {
|
||||
$html = '';
|
||||
$previous_speaker = '';
|
||||
$i = 0;
|
||||
foreach ($transcription as $segment) {
|
||||
if ($previous_speaker != $segment['speaker']) {
|
||||
if ($i > 0) { $html .= "</div>\n"; }
|
||||
$speaker_class = $segment['speaker'] === 'A' ? 'message-bubble-em' : 'message-bubble-me';
|
||||
$html .= "<div class='message-bubble {$speaker_class}'>";
|
||||
}
|
||||
//$html .= " <span class='time'>[{$segment['start']} - {$segment['end']}]</span>";
|
||||
$html .= "".escape(trim($segment['text']))." ";
|
||||
if ($previous_speaker != $segment['speaker']) {
|
||||
$previous_speaker = $segment['speaker'];
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
$html .= "</div>\n";
|
||||
return $html;
|
||||
}
|
||||
|
||||
//get the header
|
||||
require_once "resources/header.php";
|
||||
|
||||
@@ -380,7 +398,7 @@
|
||||
if (permission_exists('xml_cdr_call_log') && $call_log_enabled && isset($log_content) && !empty($log_content)) {
|
||||
echo button::create(['type'=>'button','label'=>$text['button-call_log'],'icon'=>$settings->get('theme', 'button_icon_search'),'style'=>'margin-left: 15px;','link'=>'xml_cdr_log.php?id='.$uuid]);
|
||||
}
|
||||
if ($transcribe_enabled && !empty($transcribe_engine) && empty($record_transcription) && !empty($record_path) && !empty($record_name) && file_exists($record_path.'/'.$record_name)) {
|
||||
if ($transcribe_enabled && !empty($transcribe_engine) && !empty($record_path) && !empty($record_name) && file_exists($record_path.'/'.$record_name)) {
|
||||
echo button::create(['type'=>'button','label'=>$text['button-transcribe'],'icon'=>'quote-right','id'=>'btn_transcribe','name'=>'btn_transcribe','collapse'=>'hide-xs','style'=>'margin-left: 15px;','onclick'=>"window.location.href='?id=".$uuid."&action=transcribe';"]);
|
||||
}
|
||||
echo "</td>\n";
|
||||
@@ -583,7 +601,42 @@
|
||||
echo "<script>recording_load('".escape($xml_cdr_uuid)."');</script>\n";
|
||||
}
|
||||
|
||||
//css styles
|
||||
echo "<style>\n";
|
||||
|
||||
echo " .message-bubble {\n";
|
||||
echo " display: table;\n";
|
||||
echo " padding: 10px;\n";
|
||||
echo " border: 1px solid;\n";
|
||||
echo " margin-bottom: 10px;\n";
|
||||
echo " clear: both;\n";
|
||||
echo " }\n";
|
||||
|
||||
echo " .message-bubble-em {\n";
|
||||
echo " padding-right: 15px;\n";
|
||||
echo " border-radius: " . $settings->get('theme', 'message_bubble_em_border_radius', '0 20px 20px 20px') . ";\n";
|
||||
echo " border-color: " . $settings->get('theme', 'message_bubble_em_border_color', '#abefa0') . ";\n";
|
||||
echo " background: " . $settings->get('theme', 'message_bubble_em_background_color', '#daffd4') . ";\n";
|
||||
echo " background: linear-gradient(180deg, ".$settings->get('theme', 'message_bubble_em_border_color', '#abefa0') . " 0%, " . $settings->get('theme', 'message_bubble_em_background_color', '#daffd4') . " 15px);\n";
|
||||
echo " color: " . $settings->get('theme', 'message_bubble_em_text_color', '#000000') . ";\n";
|
||||
echo " }\n";
|
||||
|
||||
echo " .message-bubble-me {\n";
|
||||
echo " float: right;\n";
|
||||
echo " padding-left: 15px;\n";
|
||||
echo " border-radius: " . $settings->get('theme', 'message_bubble_em_border_radius', '20px 20px 0 20px') . ";\n";
|
||||
echo " border-color: " . $settings->get('theme', 'message_bubble_me_border_color', '#a3e1fd') . ";\n";
|
||||
echo " background: " . $settings->get('theme', 'message_bubble_me_background_color', '#cbf0ff') . ";\n";
|
||||
echo " background: linear-gradient(180deg, " . $settings->get('theme', 'message_bubble_me_background_color', '#cbf0ff') . " calc(100% - 15px), ".$settings->get('theme', 'message_bubble_me_border_color', '#a3e1fd') . " 100%);\n";
|
||||
echo " color: " . $settings->get('theme', 'message_bubble_me_text_color', '#000000') . ";\n";
|
||||
echo " }\n";
|
||||
|
||||
echo "</style>\n";
|
||||
|
||||
//transcription, if enabled
|
||||
$transcription_array = json_decode($record_transcription, true);
|
||||
$record_transcription = $transcription_array['segments'];
|
||||
$record_transcription_html = conversational_html($record_transcription);
|
||||
if ($transcribe_enabled == 'true' && !empty($transcribe_engine) && !empty($record_transcription)) {
|
||||
echo "<b>".$text['label-transcription']."</b><br>\n";
|
||||
echo "<div class='card'>\n";
|
||||
@@ -592,7 +645,7 @@
|
||||
echo " <th>".$text['label-text']."</th>\n";
|
||||
echo " </tr>\n";
|
||||
echo " <tr >\n";
|
||||
echo " <td valign='top' class='".$row_style[0]."'>".escape($record_transcription)."</td>\n";
|
||||
echo " <td valign='top' class='".$row_style[0]."'><div style='width: 80%; min-width: 200px; max-width: 800px;'>".$record_transcription_html."</div></td>\n";
|
||||
echo " </tr>\n";
|
||||
echo " </table>";
|
||||
echo "</div>\n";
|
||||
|
||||
Reference in New Issue
Block a user