parent
436307390b
commit
9bae181917
@ -0,0 +1,256 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
use Chamilo\CoreBundle\Entity\ExtraField; |
||||
use Chamilo\CoreBundle\Entity\ExtraFieldValues; |
||||
use Chamilo\UserBundle\Entity\User; |
||||
|
||||
/** |
||||
* Class WhispeakAuthPlugin. |
||||
*/ |
||||
class WhispeakAuthPlugin extends Plugin |
||||
{ |
||||
const SETTING_ENABLE = 'enable'; |
||||
const SETTING_API_URL = 'api_url'; |
||||
const SETTING_TOKEN = 'token'; |
||||
|
||||
const EXTRAFIELD_AUTH_UID = 'whispeak_auth_uid'; |
||||
|
||||
/** |
||||
* StudentFollowUpPlugin constructor. |
||||
*/ |
||||
protected function __construct() |
||||
{ |
||||
parent::__construct( |
||||
'0.1', |
||||
'Angel Fernando Quiroz', |
||||
[ |
||||
self::SETTING_ENABLE => 'boolean', |
||||
self::SETTING_API_URL => 'text', |
||||
self::SETTING_TOKEN => 'text', |
||||
'<p>Add <code>$_configuration[\'whispeak_auth_enabled\'] = true;</code> '. |
||||
'in <code>configuration.php</code> file</p>' => 'html', |
||||
] |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* @return WhispeakAuthPlugin |
||||
*/ |
||||
public static function create() |
||||
{ |
||||
static $result = null; |
||||
|
||||
return $result ? $result : $result = new self(); |
||||
} |
||||
|
||||
public function install() |
||||
{ |
||||
UserManager::create_extra_field( |
||||
self::EXTRAFIELD_AUTH_UID, |
||||
\ExtraField::FIELD_TYPE_TEXT, |
||||
$this->get_lang('Whispeak uid'), |
||||
'' |
||||
); |
||||
} |
||||
|
||||
public function uninstall() |
||||
{ |
||||
$extraField = self::getAuthUidExtraField(); |
||||
|
||||
if (empty($extraField)) { |
||||
return; |
||||
} |
||||
|
||||
$em = Database::getManager(); |
||||
$em->remove($extraField); |
||||
$em->flush(); |
||||
} |
||||
|
||||
/** |
||||
* @return ExtraField |
||||
*/ |
||||
public static function getAuthUidExtraField() |
||||
{ |
||||
$em = Database::getManager(); |
||||
$efRepo = $em->getRepository('ChamiloCoreBundle:ExtraField'); |
||||
|
||||
/** @var ExtraField $extraField */ |
||||
$extraField = $efRepo->findOneBy( |
||||
[ |
||||
'variable' => self::EXTRAFIELD_AUTH_UID, |
||||
'extraFieldType' => ExtraField::USER_FIELD_TYPE, |
||||
] |
||||
); |
||||
|
||||
return $extraField; |
||||
} |
||||
|
||||
/** |
||||
* @param int $userId |
||||
* |
||||
* @return ExtraFieldValues |
||||
*/ |
||||
public static function getAuthUidValue($userId) |
||||
{ |
||||
$extraField = self::getAuthUidExtraField(); |
||||
$em = Database::getManager(); |
||||
$efvRepo = $em->getRepository('ChamiloCoreBundle:ExtraFieldValues'); |
||||
|
||||
/** @var ExtraFieldValues $value */ |
||||
$value = $efvRepo->findOneBy(['field' => $extraField, 'itemId' => $userId]); |
||||
|
||||
return $value; |
||||
} |
||||
|
||||
/** |
||||
* @param int $userId |
||||
* |
||||
* @return bool |
||||
*/ |
||||
public static function checkUserIsEnrolled($userId) |
||||
{ |
||||
$value = self::getAuthUidValue($userId); |
||||
|
||||
if (empty($value)) { |
||||
return false; |
||||
} |
||||
|
||||
return !empty($value->getValue()); |
||||
} |
||||
|
||||
/** |
||||
* @return string |
||||
*/ |
||||
public static function getEnrollmentUrl() |
||||
{ |
||||
return api_get_path(WEB_PLUGIN_PATH).'whispeakauth/enrollment.php'; |
||||
} |
||||
|
||||
/** |
||||
* @return string |
||||
*/ |
||||
private function getApiUrl() |
||||
{ |
||||
$url = $this->get(self::SETTING_API_URL); |
||||
|
||||
return trim($url, " \t\n\r \v/"); |
||||
} |
||||
|
||||
/** |
||||
* @param string $endPoint |
||||
* @param array $metadata |
||||
* @param User $user |
||||
* @param string $filePath |
||||
* |
||||
* @return array |
||||
*/ |
||||
private function sendRequest($endPoint, array $metadata, User $user, $filePath) |
||||
{ |
||||
$moderator = $user->getCreatorId() ?: $user->getId(); |
||||
$apiUrl = $this->getApiUrl()."/$endPoint"; |
||||
$headers = [ |
||||
//"Content-Type: application/x-www-form-urlencoded", |
||||
"Authorization: Bearer ".$this->get(self::SETTING_TOKEN), |
||||
]; |
||||
$post = [ |
||||
'metadata' => json_encode($metadata), |
||||
'moderator' => "moderator_$moderator", |
||||
'client' => base64_encode($user->getUserId()), |
||||
'voice' => new CURLFile($filePath), |
||||
]; |
||||
|
||||
$ch = curl_init(); |
||||
curl_setopt($ch, CURLOPT_URL, $apiUrl); |
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); |
||||
curl_setopt($ch, CURLOPT_POST, true); |
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $post); |
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); |
||||
$result = curl_exec($ch); |
||||
curl_close($ch); |
||||
|
||||
$result = json_decode($result, true); |
||||
|
||||
if (!empty($result['error'])) { |
||||
return null; |
||||
} |
||||
|
||||
return json_decode($result, true); |
||||
} |
||||
|
||||
/** |
||||
* @param User $user |
||||
* @param string $filePath |
||||
* |
||||
* @return array |
||||
*/ |
||||
public function requestEnrollment(User $user, $filePath) |
||||
{ |
||||
$metadata = [ |
||||
'motherTongue' => $user->getLanguage(), |
||||
'spokenTongue' => $user->getLanguage(), |
||||
'audioType' => 'pcm', |
||||
]; |
||||
|
||||
return $this->sendRequest( |
||||
'enrollment', |
||||
$metadata, |
||||
$user, |
||||
$filePath |
||||
); |
||||
} |
||||
|
||||
/** |
||||
* @param User $user |
||||
* @param string $uid |
||||
* |
||||
* @throws \Doctrine\ORM\OptimisticLockException |
||||
*/ |
||||
public function saveEnrollment(User $user, $uid) |
||||
{ |
||||
$em = Database::getManager(); |
||||
$value = self::getAuthUidValue($user->getId()); |
||||
|
||||
if (empty($value)) { |
||||
$ef = self::getAuthUidExtraField(); |
||||
$now = new DateTime('now', new DateTimeZone('UTC')); |
||||
|
||||
$value = new ExtraFieldValues(); |
||||
$value |
||||
->setField($ef) |
||||
->setItemId($user->getId()) |
||||
->setUpdatedAt($now); |
||||
} |
||||
|
||||
$value->setValue($uid); |
||||
|
||||
$em->persist($value); |
||||
$em->flush(); |
||||
} |
||||
|
||||
public function requestAuthentify(User $user, $filePath) |
||||
{ |
||||
$value = self::getAuthUidValue($user->getId()); |
||||
|
||||
if (empty($value)) { |
||||
return null; |
||||
} |
||||
|
||||
$metadata = [ |
||||
'uid' => $value->getValue(), |
||||
'audioType' => 'pcm', |
||||
]; |
||||
|
||||
return $this->sendRequest( |
||||
'authentify', |
||||
$metadata, |
||||
$user, |
||||
$filePath |
||||
); |
||||
} |
||||
|
||||
public function getAuthentifySampleText() |
||||
{ |
||||
return 'Hola hola hola'; |
||||
} |
||||
} |
@ -0,0 +1,118 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
use Chamilo\UserBundle\Entity\User; |
||||
use FFMpeg\FFMpeg; |
||||
use FFMpeg\Format\Audio\Wav; |
||||
|
||||
$cidReset = true; |
||||
|
||||
require_once __DIR__.'/../../../main/inc/global.inc.php'; |
||||
|
||||
$action = isset($_POST['action']) ? $_POST['action'] : 'enrollment'; |
||||
$isEnrollment = 'enrollment' === $action; |
||||
$isAuthentify = 'authentify' === $action; |
||||
|
||||
$isAllowed = true; |
||||
|
||||
if ($isEnrollment) { |
||||
api_block_anonymous_users(false); |
||||
|
||||
$isAllowed = !empty($_FILES['audio']); |
||||
} elseif ($isAuthentify) { |
||||
$isAllowed = !empty($_POST['username']) && !empty($_FILES['audio']); |
||||
} |
||||
|
||||
if (!$isAllowed) { |
||||
echo Display::return_message(get_lang('NotAllowed'), 'error'); |
||||
|
||||
exit; |
||||
} |
||||
|
||||
$plugin = WhispeakAuthPlugin::create(); |
||||
|
||||
if ($isAuthentify) { |
||||
$em = Database::getManager(); |
||||
/** @var User|null $user */ |
||||
$user = $em->getRepository('ChamiloUserBundle:User')->findOneBy(['username' => $_POST['username']]); |
||||
} else { |
||||
/** @var User $user */ |
||||
$user = api_get_user_entity(api_get_user_id()); |
||||
} |
||||
|
||||
if (empty($user)) { |
||||
echo Display::return_message(get_lang('NoUser'), 'error'); |
||||
|
||||
exit; |
||||
} |
||||
|
||||
$path = api_upload_file('whispeakauth', $_FILES['audio'], $user->getId()); |
||||
|
||||
if (false === $path) { |
||||
echo Display::return_message(get_lang('UploadError'), 'error'); |
||||
|
||||
exit; |
||||
} |
||||
|
||||
$originFullPath = api_get_path(SYS_UPLOAD_PATH).'whispeakauth'.$path['path_to_save']; |
||||
$directory = dirname($originFullPath); |
||||
$newFullPath = $directory.'/audio.wav'; |
||||
|
||||
try { |
||||
$ffmpeg = FFMpeg::create(); |
||||
|
||||
$audio = $ffmpeg->open($originFullPath); |
||||
$audio->save(new Wav(), $newFullPath); |
||||
} catch (Exception $exception) { |
||||
echo Display::return_message($exception->getMessage(), 'error'); |
||||
|
||||
exit; |
||||
} |
||||
|
||||
if ($isEnrollment) { |
||||
$result = $plugin->requestEnrollment($user, $newFullPath); |
||||
|
||||
if (empty($result)) { |
||||
echo Display::return_message($plugin->get_lang('EnrollmentFailed')); |
||||
|
||||
exit; |
||||
} |
||||
|
||||
$plugin->saveEnrollment($user, $result['uid']); |
||||
|
||||
echo Display::return_message($plugin->get_lang('EnrollmentSuccess'), 'success'); |
||||
|
||||
exit; |
||||
} |
||||
|
||||
if ($isAuthentify) { |
||||
$result = $plugin->requestAuthentify($user, $newFullPath); |
||||
|
||||
if (empty($result)) { |
||||
echo Display::return_message($plugin->get_lang('AuthentifyFailed')); |
||||
|
||||
exit; |
||||
} |
||||
|
||||
$success = (bool) $result['audio'][0]['result']; |
||||
|
||||
if (!$success) { |
||||
echo Display::return_message($plugin->get_lang('TryAgain')); |
||||
|
||||
exit; |
||||
} |
||||
|
||||
$loggedUser = [ |
||||
'user_id' => $user->getId(), |
||||
'status' => $user->getStatus(), |
||||
'uidReset' => true, |
||||
]; |
||||
|
||||
ChamiloSession::write('_user', $loggedUser); |
||||
Login::init_user($user->getId(), true); |
||||
|
||||
echo Display::return_message($plugin->get_lang('AuthentifySuccess'), 'success'); |
||||
echo '<script>window.location.href = "'.api_get_path(WEB_PATH).'";</script>'; |
||||
|
||||
exit; |
||||
} |
@ -0,0 +1,138 @@ |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
window.RecordAudio = (function () { |
||||
function useRecordRTC(rtcInfo) { |
||||
$(rtcInfo.blockId).show(); |
||||
|
||||
var mediaConstraints = {audio: true}, |
||||
localStream = null, |
||||
recordRTC = null, |
||||
btnStart = $(rtcInfo.btnStartId), |
||||
btnStop = $(rtcInfo.btnStopId), |
||||
btnSave = $(rtcInfo.btnSaveId), |
||||
tagAudio = $(rtcInfo.plyrPreviewId); |
||||
|
||||
function saveAudio() { |
||||
var recordedBlob = recordRTC.getBlob(); |
||||
|
||||
if (!recordedBlob) { |
||||
return; |
||||
} |
||||
|
||||
var btnSaveText = btnSave.html(); |
||||
var fileExtension = recordedBlob.type.split('/')[1]; |
||||
|
||||
var formData = new FormData(); |
||||
formData.append('audio', recordedBlob, 'audio.' + fileExtension); |
||||
|
||||
for (var prop in rtcInfo.data) { |
||||
if (!rtcInfo.data.hasOwnProperty(prop)) { |
||||
continue; |
||||
} |
||||
|
||||
formData.append(prop, rtcInfo.data[prop]); |
||||
} |
||||
|
||||
$.ajax({ |
||||
url: _p.web_plugin + 'whispeakauth/ajax/record_audio.php', |
||||
data: formData, |
||||
processData: false, |
||||
contentType: false, |
||||
type: 'POST', |
||||
beforeSend: function () { |
||||
btnStart.prop('disabled', true); |
||||
btnStop.prop('disabled', true); |
||||
btnSave.prop('disabled', true).text(btnSave.data('loadingtext')); |
||||
} |
||||
}).done(function (response) { |
||||
$('#messages-deck').append(response); |
||||
}).always(function () { |
||||
btnSave.prop('disabled', true).html(btnSaveText).parent().addClass('hidden'); |
||||
btnStop.prop('disabled', true).parent().addClass('hidden'); |
||||
btnStart.prop('disabled', false).parent().removeClass('hidden'); |
||||
}); |
||||
} |
||||
|
||||
btnStart.on('click', function () { |
||||
tagAudio.prop('src', ''); |
||||
|
||||
function successCallback(stream) { |
||||
localStream = stream; |
||||
|
||||
recordRTC = RecordRTC(stream, { |
||||
numberOfAudioChannels: 1, |
||||
type: 'audio' |
||||
}); |
||||
recordRTC.startRecording(); |
||||
|
||||
btnSave.prop('disabled', true).parent().addClass('hidden'); |
||||
btnStop.prop('disabled', false).parent().removeClass('hidden'); |
||||
btnStart.prop('disabled', true).parent().addClass('hidden'); |
||||
tagAudio.removeClass('show').parents('#audio-wrapper').addClass('hidden'); |
||||
} |
||||
|
||||
function errorCallback(error) { |
||||
alert(error.message); |
||||
} |
||||
|
||||
if (navigator.mediaDevices.getUserMedia) { |
||||
navigator.mediaDevices.getUserMedia(mediaConstraints) |
||||
.then(successCallback) |
||||
.catch(errorCallback); |
||||
|
||||
return; |
||||
} |
||||
|
||||
navigator.getUserMedia = navigator.getUserMedia || navigator.mozGetUserMedia || navigator.webkitGetUserMedia; |
||||
|
||||
if (navigator.getUserMedia) { |
||||
navigator.getUserMedia(mediaConstraints, successCallback, errorCallback); |
||||
} |
||||
}); |
||||
|
||||
btnStop.on('click', function () { |
||||
if (!recordRTC) { |
||||
return; |
||||
} |
||||
|
||||
recordRTC.stopRecording(function (audioURL) { |
||||
btnStart.prop('disabled', false).parent().removeClass('hidden'); |
||||
btnStop.prop('disabled', true).parent().addClass('hidden'); |
||||
btnSave.prop('disabled', false).parent().removeClass('hidden'); |
||||
|
||||
tagAudio |
||||
.prop('src', audioURL) |
||||
.parents('#audio-wrapper') |
||||
.removeClass('hidden') |
||||
.addClass('show'); |
||||
|
||||
localStream.getTracks()[0].stop(); |
||||
}); |
||||
}); |
||||
|
||||
btnSave.on('click', function () { |
||||
if (!recordRTC) { |
||||
return; |
||||
} |
||||
|
||||
saveAudio(); |
||||
}); |
||||
} |
||||
|
||||
return { |
||||
init: function (rtcInfo) { |
||||
$(rtcInfo.blockId).hide(); |
||||
|
||||
var userMediaEnabled = (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) || |
||||
!!navigator.webkitGetUserMedia || |
||||
!!navigator.mozGetUserMedia || |
||||
!!navigator.getUserMedia; |
||||
|
||||
if (!userMediaEnabled) { |
||||
return; |
||||
} |
||||
|
||||
useRecordRTC(rtcInfo); |
||||
} |
||||
} |
||||
})(); |
@ -0,0 +1,24 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
$cidReset = true; |
||||
|
||||
require_once __DIR__.'/../../main/inc/global.inc.php'; |
||||
|
||||
$plugin = WhispeakAuthPlugin::create(); |
||||
|
||||
$form = new FormValidator('enter_username', 'post', '#'); |
||||
$form->addText('username', get_lang('Username')); |
||||
|
||||
$htmlHeadXtra[] = api_get_js('rtc/RecordRTC.js'); |
||||
$htmlHeadXtra[] = api_get_js_simple(api_get_path(WEB_PLUGIN_PATH).'whispeakauth/assets/js/RecordAudio.js'); |
||||
|
||||
$template = new Template(); |
||||
$template->assign('form', $form->returnForm()); |
||||
$template->assign('sample_text', $plugin->getAuthentifySampleText()); |
||||
|
||||
$content = $template->fetch('whispeakauth/view/authentify_recorder.html.twig'); |
||||
|
||||
$template->assign('header', $plugin->get_title()); |
||||
$template->assign('content', $content); |
||||
$template->display_one_col_template(); |
@ -0,0 +1,24 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
$cidReset = true; |
||||
|
||||
require_once __DIR__.'/../../main/inc/global.inc.php'; |
||||
|
||||
api_block_anonymous_users(true); |
||||
|
||||
$plugin = WhispeakAuthPlugin::create(); |
||||
|
||||
$sampleText = $plugin->get_lang('EnrollmentSampleText'); |
||||
|
||||
$htmlHeadXtra[] = api_get_js('rtc/RecordRTC.js'); |
||||
$htmlHeadXtra[] = api_get_js_simple(api_get_path(WEB_PLUGIN_PATH).'whispeakauth/assets/js/RecordAudio.js'); |
||||
|
||||
$template = new Template(); |
||||
$template->assign('sample_text', $sampleText); |
||||
|
||||
$content = $template->fetch('whispeakauth/view/record_audio.html.twig'); |
||||
|
||||
$template->assign('header', $plugin->get_title()); |
||||
$template->assign('content', $content); |
||||
$template->display_one_col_template(); |
@ -0,0 +1,12 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
$plugin = WhispeakAuthPlugin::create(); |
||||
|
||||
echo Display::toolbarButton( |
||||
$plugin->get_lang('SpeechAuthentication'), |
||||
api_get_path(WEB_PLUGIN_PATH).'whispeakauth/authentify.php', |
||||
'sign-in', |
||||
'info', |
||||
['class' => 'btn-block'] |
||||
); |
@ -0,0 +1,4 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
WhispeakAuthPlugin::create()->install(); |
@ -0,0 +1,15 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
$strings['plugin_title'] = 'Speech authentication with Whispeak'; |
||||
|
||||
$strings['EnrollmentSampleText'] = 'The famous Mona Lisa painting was painted by Leonardo Da Vinci.'; |
||||
|
||||
$strings['RepeatThisPhrase'] = 'Repeat this phrase three times after allowing audio recording:'; |
||||
|
||||
$strings['SpeechAuthentication'] = 'Speech authentication'; |
||||
$strings['EnrollmentFailed'] = 'Enrollment failed.'; |
||||
$strings['EnrollmentSuccess'] = 'Enrollment success.'; |
||||
$strings['AuthentifyFailed'] = 'Login failed.'; |
||||
$strings['AuthentifySuccess'] = '¡Authentication success!'; |
||||
$strings['TryAgain'] = 'Try again'; |
@ -0,0 +1,8 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
$strings['plugin_title'] = 'Authenticación de voz con Whispeak'; |
||||
|
||||
$strings['EnrollmentSampleText'] = 'El famoso cuadro de Mona Lisa fue pintado por Leonardo Da Vinci.'; |
||||
|
||||
$strings['RepeatThisPhrase'] = 'Repita esta frase tres veces después de permitir la grabación de audio:'; |
@ -0,0 +1,4 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
$plugin_info = WhispeakAuthPlugin::create()->get_info(); |
@ -0,0 +1,4 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
WhispeakAuthPlugin::create()->uninstall(); |
@ -0,0 +1,38 @@ |
||||
{% extends 'whispeakauth/view/record_audio.html.twig' %} |
||||
|
||||
{% block intro %} |
||||
<form class="form-horizontal" action="#" method="post"> |
||||
<div class="form-group "> |
||||
<label for="enter_username_username" class="col-sm-4 control-label"> |
||||
{{ 'Username'|get_lang }} |
||||
</label> |
||||
<div class="col-sm-8"> |
||||
<input class="form-control" name="username" type="text" id="username"> |
||||
</div> |
||||
</div> |
||||
</form> |
||||
|
||||
<hr> |
||||
|
||||
{{ parent() }} |
||||
{% endblock %} |
||||
|
||||
{% block config_data %} |
||||
$('#username').on('change', function () { |
||||
$('#record-audio-recordrtc, #btn-start-record, #btn-stop-record, #btn-save-record').off('click', ''); |
||||
|
||||
RecordAudio.init( |
||||
{ |
||||
blockId: '#record-audio-recordrtc', |
||||
btnStartId: '#btn-start-record', |
||||
btnStopId: '#btn-stop-record', |
||||
btnSaveId: '#btn-save-record', |
||||
plyrPreviewId: '#record-preview', |
||||
data: { |
||||
action: 'authentify', |
||||
username: $('#username').val() |
||||
} |
||||
} |
||||
); |
||||
}); |
||||
{% endblock %} |
@ -0,0 +1,60 @@ |
||||
<div id="record-audio-recordrtc" class="row"> |
||||
<div class="col-sm-6"> |
||||
{% block intro %} |
||||
<p class="text-center">{{ 'RepeatThisPhrase'|get_plugin_lang('WhispeakAuthPlugin') }}</p> |
||||
<div class="well well-sm"> |
||||
<div class="row"> |
||||
<div class="col-sm-3 text-center"> |
||||
<span class="fa fa-microphone fa-5x fa-fw" aria-hidden="true"></span> |
||||
<span class="sr-only">{{ 'RecordAudio'|get_lang }}</span> |
||||
</div> |
||||
<div class="col-sm-9 text-center"> |
||||
<p class="lead">{{ sample_text }}</p> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
{% endblock %} |
||||
|
||||
<ul class="list-inline text-center"> |
||||
<li> |
||||
<button class="btn btn-primary" type="button" id="btn-start-record"> |
||||
<span class="fa fa-circle fa-fw" aria-hidden="true"></span> {{ 'StartRecordingAudio'|get_lang }} |
||||
</button> |
||||
</li> |
||||
<li class="hidden"> |
||||
<button class="btn btn-danger" type="button" id="btn-stop-record" disabled> |
||||
<span class="fa fa-square fa-fw" aria-hidden="true"></span> {{ 'StopRecordingAudio'|get_lang }} |
||||
</button> |
||||
</li> |
||||
<li class="hidden"> |
||||
<button class="btn btn-success" type="button" id="btn-save-record" |
||||
data-loadingtext="{{ 'Uploading'|get_lang }}" disabled> |
||||
<span class="fa fa-send fa-fw" aria-hidden="true"></span> {{ 'SaveRecordedAudio'|get_lang }} |
||||
</button> |
||||
</li> |
||||
</ul> |
||||
<p class="hidden" id="audio-wrapper"> |
||||
<audio class="center-block" controls id="record-preview"></audio> |
||||
</p> |
||||
</div> |
||||
<div class="col-sm-5 col-sm-offset-1" id="messages-deck"></div> |
||||
</div> |
||||
|
||||
<script> |
||||
$(document).on('ready', function () { |
||||
{% block config_data %} |
||||
RecordAudio.init( |
||||
{ |
||||
blockId: '#record-audio-recordrtc', |
||||
btnStartId: '#btn-start-record', |
||||
btnStopId: '#btn-stop-record', |
||||
btnSaveId: '#btn-save-record', |
||||
plyrPreviewId: '#record-preview', |
||||
data: { |
||||
action: 'enrollment' |
||||
} |
||||
} |
||||
); |
||||
{% endblock %} |
||||
}); |
||||
</script> |
Loading…
Reference in new issue