parent
							
								
									a5d5a5b5d8
								
							
						
					
					
						commit
						2efd2af702
					
				@ -1,7 +0,0 @@ | 
				
			||||
# Speech authentication with Whispeak | 
				
			||||
 | 
				
			||||
Instructions: | 
				
			||||
1. Install plugin in Chamilo. | 
				
			||||
2. Set the plugin configuration with the token and API url. And enable the plugin. | 
				
			||||
3. Set the `login_bottom` region to the plugin.  | 
				
			||||
4. Add `$_configuration['whispeak_auth_enabled'] = true;` to `configuration.php` file. | 
				
			||||
@ -1,293 +0,0 @@ | 
				
			||||
<?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 SETTING_INSTRUCTION = 'instruction'; | 
				
			||||
 | 
				
			||||
    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', | 
				
			||||
                self::SETTING_INSTRUCTION => '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->createQuery('DELETE FROM ChamiloCoreBundle:ExtraFieldValues efv WHERE efv.field = :field') | 
				
			||||
            ->execute(['field' => $extraField]); | 
				
			||||
 | 
				
			||||
        $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'; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * @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 | 
				
			||||
        ); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * @return string | 
				
			||||
     */ | 
				
			||||
    public function getAuthentifySampleText() | 
				
			||||
    { | 
				
			||||
        $phrases = []; | 
				
			||||
 | 
				
			||||
        for ($i = 1; $i <= 6; $i++) { | 
				
			||||
            $phrases[] = $this->get_lang("AuthentifySampleText$i"); | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        $rand = array_rand($phrases, 1); | 
				
			||||
 | 
				
			||||
        return $phrases[$rand]; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * @return bool | 
				
			||||
     */ | 
				
			||||
    public function toolIsEnabled() | 
				
			||||
    { | 
				
			||||
        return 'true' === $this->get(self::SETTING_ENABLE); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * Access not allowed when tool is not enabled. | 
				
			||||
     * | 
				
			||||
     * @param bool $printHeaders Optional. Print headers. | 
				
			||||
     */ | 
				
			||||
    public function protectTool($printHeaders = true) | 
				
			||||
    { | 
				
			||||
        if ($this->toolIsEnabled()) { | 
				
			||||
            return; | 
				
			||||
        } | 
				
			||||
 | 
				
			||||
        api_not_allowed($printHeaders); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    /** | 
				
			||||
     * @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); | 
				
			||||
    } | 
				
			||||
} | 
				
			||||
@ -1,136 +0,0 @@ | 
				
			||||
<?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(); | 
				
			||||
 | 
				
			||||
$plugin->protectTool(false); | 
				
			||||
 | 
				
			||||
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; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
$newFullPath = $originFullPath = api_get_path(SYS_UPLOAD_PATH).'whispeakauth'.$path['path_to_save']; | 
				
			||||
$fileType = mime_content_type($originFullPath); | 
				
			||||
 | 
				
			||||
if ('wav' !== substr($fileType, -3)) { | 
				
			||||
    $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; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    $reliability = (int) $result['reliability']; | 
				
			||||
 | 
				
			||||
    if ($reliability <= 0) { | 
				
			||||
        echo Display::return_message($plugin->get_lang('EnrollmentSignature0'), 'error'); | 
				
			||||
 | 
				
			||||
        exit; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    $plugin->saveEnrollment($user, $result['uid']); | 
				
			||||
 | 
				
			||||
    $message = '<strong>'.$plugin->get_lang('EnrollmentSuccess').'</strong>'; | 
				
			||||
    $message .= PHP_EOL; | 
				
			||||
    $message .= $plugin->get_lang("EnrollmentSignature$reliability"); | 
				
			||||
 | 
				
			||||
    echo Display::return_message($message, 'success', false); | 
				
			||||
 | 
				
			||||
    exit; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
if ($isAuthentify) { | 
				
			||||
    $result = $plugin->requestAuthentify($user, $newFullPath); | 
				
			||||
 | 
				
			||||
    if (empty($result)) { | 
				
			||||
        echo Display::return_message($plugin->get_lang('AuthentifyFailed'), 'error'); | 
				
			||||
 | 
				
			||||
        exit; | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    $success = (bool) $result['audio'][0]['result']; | 
				
			||||
 | 
				
			||||
    if (!$success) { | 
				
			||||
        echo Display::return_message($plugin->get_lang('TryAgain'), 'warning'); | 
				
			||||
 | 
				
			||||
        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; | 
				
			||||
} | 
				
			||||
@ -1,139 +0,0 @@ | 
				
			||||
/* 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, { | 
				
			||||
                    recorderType: StereoAudioRecorder, | 
				
			||||
                    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); | 
				
			||||
        } | 
				
			||||
    } | 
				
			||||
})(); | 
				
			||||
@ -1,26 +0,0 @@ | 
				
			||||
<?php | 
				
			||||
/* For licensing terms, see /license.txt */ | 
				
			||||
 | 
				
			||||
$cidReset = true; | 
				
			||||
 | 
				
			||||
require_once __DIR__.'/../../main/inc/global.inc.php'; | 
				
			||||
 | 
				
			||||
$plugin = WhispeakAuthPlugin::create(); | 
				
			||||
 | 
				
			||||
$plugin->protectTool(); | 
				
			||||
 | 
				
			||||
$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(); | 
				
			||||
@ -1,28 +0,0 @@ | 
				
			||||
<?php | 
				
			||||
/* For licensing terms, see /license.txt */ | 
				
			||||
 | 
				
			||||
$cidReset = true; | 
				
			||||
 | 
				
			||||
require_once __DIR__.'/../../main/inc/global.inc.php'; | 
				
			||||
 | 
				
			||||
api_block_anonymous_users(true); | 
				
			||||
 | 
				
			||||
$userId = api_get_user_id(); | 
				
			||||
$plugin = WhispeakAuthPlugin::create(); | 
				
			||||
 | 
				
			||||
$plugin->protectTool(); | 
				
			||||
 | 
				
			||||
$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('is_authenticated', WhispeakAuthPlugin::checkUserIsEnrolled($userId)); | 
				
			||||
$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(); | 
				
			||||
@ -1,14 +0,0 @@ | 
				
			||||
<?php | 
				
			||||
/* For licensing terms, see /license.txt */ | 
				
			||||
 | 
				
			||||
$plugin = WhispeakAuthPlugin::create(); | 
				
			||||
 | 
				
			||||
if ($plugin->toolIsEnabled()) { | 
				
			||||
    echo Display::toolbarButton( | 
				
			||||
        $plugin->get_lang('SpeechAuthentication'), | 
				
			||||
        api_get_path(WEB_PLUGIN_PATH).'whispeakauth/authentify.php', | 
				
			||||
        'sign-in', | 
				
			||||
        'info', | 
				
			||||
        ['class' => 'btn-block'] | 
				
			||||
    ); | 
				
			||||
} | 
				
			||||
@ -1,4 +0,0 @@ | 
				
			||||
<?php | 
				
			||||
/* For licensing terms, see /license.txt */ | 
				
			||||
 | 
				
			||||
WhispeakAuthPlugin::create()->install(); | 
				
			||||
@ -1,33 +0,0 @@ | 
				
			||||
<?php | 
				
			||||
/* For licensing terms, see /license.txt */ | 
				
			||||
 | 
				
			||||
$strings['plugin_title'] = 'Speech authentication with Whispeak'; | 
				
			||||
$strings['plugin_comment'] = 'Allow speech authentication in Chamilo.'; | 
				
			||||
 | 
				
			||||
$strings['enable'] = 'Enable'; | 
				
			||||
$strings['api_url'] = 'API URL'; | 
				
			||||
$strings['api_url_help'] = 'http://api.whispeak.io:8080/v1/'; | 
				
			||||
$strings['token'] = 'API key'; | 
				
			||||
$strings['instruction'] = '<p>Add <code>$_configuration[\'whispeak_auth_enabled\'] = true;</code>'. | 
				
			||||
    'in the <code>configuration.php</code> file</p>'; | 
				
			||||
 | 
				
			||||
$strings['EnrollmentSampleText'] = 'The famous Mona Lisa painting was painted by Leonardo Da Vinci.'; | 
				
			||||
$strings['AuthentifySampleText1'] = 'Dropping Like Flies.'; | 
				
			||||
$strings['AuthentifySampleText2'] = 'Keep Your Eyes Peeled.'; | 
				
			||||
$strings['AuthentifySampleText3'] = 'The fox screams at midnight.'; | 
				
			||||
$strings['AuthentifySampleText4'] = 'Go Out On a Limb.'; | 
				
			||||
$strings['AuthentifySampleText5'] = 'Under the Water.'; | 
				
			||||
$strings['AuthentifySampleText6'] = 'Barking Up The Wrong Tree.'; | 
				
			||||
 | 
				
			||||
$strings['RepeatThisPhrase'] = 'Repeat this phrase three times after allowing audio recording:'; | 
				
			||||
$strings['EnrollmentSignature0'] = 'Unsustainable signature requires a new enrollment.'; | 
				
			||||
$strings['EnrollmentSignature1'] = 'Passable signature, advice to make a new enrollment.'; | 
				
			||||
$strings['EnrollmentSignature2'] = 'Correct signature.'; | 
				
			||||
$strings['EnrollmentSignature3'] = 'Good signature.'; | 
				
			||||
$strings['SpeechAuthAlreadyEnrolled'] = 'Speech authentication already enrolled previously.'; | 
				
			||||
$strings['SpeechAuthentication'] = 'Speech authentication'; | 
				
			||||
$strings['EnrollmentFailed'] = 'Enrollment failed.'; | 
				
			||||
$strings['EnrollmentSuccess'] = 'Enrollment success.'; | 
				
			||||
$strings['AuthentifyFailed'] = 'Login failed.'; | 
				
			||||
$strings['AuthentifySuccess'] = 'Authentication success!'; | 
				
			||||
$strings['TryAgain'] = 'Try again'; | 
				
			||||
@ -1,18 +0,0 @@ | 
				
			||||
<?php | 
				
			||||
/* For licensing terms, see /license.txt */ | 
				
			||||
 | 
				
			||||
$strings['plugin_title'] = 'Authentification vocale avec Whispeak'; | 
				
			||||
 | 
				
			||||
$strings['EnrollmentSampleText'] = 'Le fameux chef-d\'oeuvre Mona Lisa a été peint par Léonardo da Vinci.'; | 
				
			||||
 | 
				
			||||
$strings['RepeatThisPhrase'] = 'Autorisez l\'enregistrement audio puis répétez cette phrase trois fois:'; | 
				
			||||
$strings['EnrollmentSignature0'] = 'Signature non viable, nécessite un nouvel enrôlement'; | 
				
			||||
$strings['EnrollmentSignature1'] = 'Signature passable, conseil de faire un nouvel enrôlement.'; | 
				
			||||
$strings['EnrollmentSignature2'] = 'Signature correcte.'; | 
				
			||||
$strings['EnrollmentSignature3'] = 'Signature bonne.'; | 
				
			||||
$strings['SpeechAuthentication'] = 'Authentification de voix'; | 
				
			||||
$strings['EnrollmentFailed'] = 'Échec à l\'inscription.'; | 
				
			||||
$strings['EnrollmentSuccess'] = 'Inscription réussie.'; | 
				
			||||
$strings['AuthentifyFailed'] = 'Échec de l\'authentification.'; | 
				
			||||
$strings['AuthentifySuccess'] = 'Authentification réussie!'; | 
				
			||||
$strings['TryAgain'] = 'Essayez encore'; | 
				
			||||
@ -1,33 +0,0 @@ | 
				
			||||
<?php | 
				
			||||
/* For licensing terms, see /license.txt */ | 
				
			||||
 | 
				
			||||
$strings['plugin_title'] = 'Authenticación de voz con Whispeak'; | 
				
			||||
$strings['plugin_comment'] = 'Permitir autenticación de voz en Chamilo.'; | 
				
			||||
 | 
				
			||||
$strings['enable'] = 'Habilitar'; | 
				
			||||
$strings['api_url'] = 'URL del API'; | 
				
			||||
$strings['api_url_help'] = 'http://api.whispeak.io:8080/v1/'; | 
				
			||||
$strings['token'] = 'Llave del API'; | 
				
			||||
$strings['instruction'] = '<p>Agrega <code>$_configuration[\'whispeak_auth_enabled\'] = true;</code>'. | 
				
			||||
    'al archivo <code>configuration.php</code></p>'; | 
				
			||||
 | 
				
			||||
$strings['EnrollmentSampleText'] = 'El famoso cuadro de Mona Lisa fue pintado por Leonardo Da Vinci.'; | 
				
			||||
$strings['AuthentifySampleText1'] = 'Cayendo como moscas.'; | 
				
			||||
$strings['AuthentifySampleText2'] = 'Mantén tus ojos abiertos.'; | 
				
			||||
$strings['AuthentifySampleText3'] = 'El zorro grita a medianoche.'; | 
				
			||||
$strings['AuthentifySampleText4'] = 'Ir por las ramas.'; | 
				
			||||
$strings['AuthentifySampleText5'] = 'Debajo del agua.'; | 
				
			||||
$strings['AuthentifySampleText6'] = 'Ladrando al árbol equivocado.'; | 
				
			||||
 | 
				
			||||
$strings['RepeatThisPhrase'] = 'Repita esta frase tres veces después de permitir la grabación de audio:'; | 
				
			||||
$strings['EnrollmentSignature0'] = 'Firma insostenible, requiere una nueva inscripción.'; | 
				
			||||
$strings['EnrollmentSignature1'] = 'Firma aceptable, pero se aconseja hacer una nueva inscripción.'; | 
				
			||||
$strings['EnrollmentSignature2'] = 'Firma correcta.'; | 
				
			||||
$strings['EnrollmentSignature3'] = 'Buena firma.'; | 
				
			||||
$strings['SpeechAuthAlreadyEnrolled'] = 'Autenticación de voz registrada anteriormente.'; | 
				
			||||
$strings['SpeechAuthentication'] = 'Atenticación con voz'; | 
				
			||||
$strings['EnrollmentFailed'] = 'Inscripción fallida.'; | 
				
			||||
$strings['EnrollmentSuccess'] = 'Inscripción correcta.'; | 
				
			||||
$strings['AuthentifyFailed'] = 'Inicio de sesión fallido.'; | 
				
			||||
$strings['AuthentifySuccess'] = '¡Autenticación correcta!'; | 
				
			||||
$strings['TryAgain'] = 'Intente de nuevo.'; | 
				
			||||
@ -1,4 +0,0 @@ | 
				
			||||
<?php | 
				
			||||
/* For licensing terms, see /license.txt */ | 
				
			||||
 | 
				
			||||
$plugin_info = WhispeakAuthPlugin::create()->get_info(); | 
				
			||||
@ -1,4 +0,0 @@ | 
				
			||||
<?php | 
				
			||||
/* For licensing terms, see /license.txt */ | 
				
			||||
 | 
				
			||||
WhispeakAuthPlugin::create()->uninstall(); | 
				
			||||
@ -1,38 +0,0 @@ | 
				
			||||
{% 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 %} | 
				
			||||
@ -1,67 +0,0 @@ | 
				
			||||
<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"> | 
				
			||||
        {% if is_authenticated %} | 
				
			||||
            <div class="alert alert-info"> | 
				
			||||
                <span class="fa fa-info-circle" aria-hidden="true"></span> | 
				
			||||
                <strong>{{ 'SpeechAuthAlreadyEnrolled'|get_plugin_lang('WhispeakAuthPlugin') }}</strong> | 
				
			||||
            </div> | 
				
			||||
        {% endif %} | 
				
			||||
    </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