commit
9f57df788f
After Width: | Height: | Size: 565 B |
@ -0,0 +1,37 @@ |
||||
body { |
||||
font-size: 25px; |
||||
line-height: 1.25em !important; |
||||
text-align: justify; |
||||
-webkit-touch-callout: none; |
||||
-webkit-user-select: none; |
||||
-khtml-user-select: none; |
||||
-moz-user-select: none; |
||||
-ms-user-select: none; |
||||
user-select: none; |
||||
} |
||||
|
||||
.text-highlight { |
||||
-webkit-transition: color .3s linear, text-shadow .3s linear; |
||||
-khtml-transition: color .3s linear, text-shadow .3s linear; |
||||
-moz-transition: color .3s linear, text-shadow .3s linear; |
||||
-ms-transition: color .3s linear, text-shadow .3s linear; |
||||
transition: color .3s linear, text-shadow .3s linear; |
||||
} |
||||
|
||||
.text-highlight.active { |
||||
color: red; |
||||
-webkit-text-shadow: 0px 0px 1px rgba(255, 0, 0, 1); |
||||
-khtml-text-shadow: 0px 0px 1px rgba(255, 0, 0, 1); |
||||
-moz-text-shadow: 0px 0px 1px rgba(255, 0, 0, 1); |
||||
-ms-text-shadow: 0px 0px 1px rgba(255, 0, 0, 1); |
||||
text-shadow: 0px 0px 1px rgba(255, 0, 0, 1); |
||||
-webkit-transition: color .3s linear, text-shadow .3s linear; |
||||
-khtml-transition: color .3s linear, text-shadow .3s linear; |
||||
-moz-transition: color .3s linear, text-shadow .3s linear; |
||||
-ms-transition: color .3s linear, text-shadow .3s linear; |
||||
transition: color .3s linear, text-shadow .3s linear; |
||||
} |
||||
|
||||
br { |
||||
margin-bottom: 1em; |
||||
} |
@ -0,0 +1,79 @@ |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
$(function () { |
||||
var parent$ = window.parent.$, |
||||
$player = parent$('audio#lp_audio_media_player_html5'); |
||||
|
||||
if ($player.length === 0) { |
||||
$player = parent$('audio#lp_audio_media_player'); |
||||
} |
||||
|
||||
var player = $player.get(0), |
||||
def = $.Deferred(); |
||||
|
||||
if (!$player.length) { |
||||
processText(wordsCount); |
||||
|
||||
return; |
||||
} |
||||
|
||||
player.preload = 'auto'; |
||||
|
||||
function processText(turns) { |
||||
var tagEnd = '</span> ', |
||||
tagStart = tagEnd + '<span class="text-highlight">', |
||||
wordsPerSecond = Math.ceil(wordsCount / turns); |
||||
|
||||
var indexes = Object.keys(words); |
||||
|
||||
var output = ''; |
||||
|
||||
for (var i = 0; i < turns; i++) { |
||||
var block = indexes.slice(i * wordsPerSecond, i * wordsPerSecond + wordsPerSecond), |
||||
index = block[0]; |
||||
|
||||
if (!index) { |
||||
continue; |
||||
} |
||||
|
||||
output += tagStart + words[index]; |
||||
|
||||
for (var j = 1; j < block.length; j++) { |
||||
index = block[j]; |
||||
output += ' ' + words[index]; |
||||
} |
||||
} |
||||
|
||||
output += tagEnd; |
||||
output = output.slice(tagEnd.length); |
||||
|
||||
$('.page-blank').html(output); |
||||
|
||||
def.resolve(output); |
||||
|
||||
return def.promise(); |
||||
} |
||||
|
||||
player.ontimeupdate = function () { |
||||
var block = Math.ceil(this.currentTime); |
||||
|
||||
$('.text-highlight') |
||||
.removeClass('active') |
||||
.filter(function (index) { |
||||
return index + 1 == block; |
||||
}) |
||||
.addClass('active'); |
||||
}; |
||||
player.onloadedmetadata = function () { |
||||
var turns = Math.ceil(this.duration); |
||||
|
||||
processText(turns) |
||||
.then(function (output) { |
||||
var to = window.setTimeout(function () { |
||||
player.play(); |
||||
|
||||
window.clearTimeout(to); |
||||
}, 1500); |
||||
}); |
||||
} |
||||
}); |
@ -0,0 +1,75 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
/** |
||||
* Print a read-out text inside a session |
||||
* |
||||
* @package chamilo.learnpath |
||||
*/ |
||||
|
||||
use Chamilo\CourseBundle\Entity\CDocument; |
||||
|
||||
$_in_course = true; |
||||
|
||||
require_once __DIR__.'/../inc/global.inc.php'; |
||||
|
||||
$current_course_tool = TOOL_LEARNPATH; |
||||
|
||||
api_protect_course_script(true); |
||||
|
||||
$id = isset($_GET['id']) ? intval($_GET['id']) : 0; |
||||
$lpId = isset($_GET['lp_id']) ? intval($_GET['lp_id']) : 0; |
||||
$courseInfo = api_get_course_info(); |
||||
$courseCode = $courseInfo['code']; |
||||
$courseId = $courseInfo['real_id']; |
||||
$userId = api_get_user_id(); |
||||
$sessionId = api_get_session_id(); |
||||
|
||||
$em = Database::getManager(); |
||||
$documentRepo = $em->getRepository('ChamiloCourseBundle:CDocument'); |
||||
|
||||
// This page can only be shown from inside a learning path |
||||
if (!$id && !$lpId) { |
||||
api_not_allowed(true); |
||||
exit; |
||||
} |
||||
|
||||
/** @var CDocument $document */ |
||||
$document = $documentRepo->findOneBy(['cId' => $courseId, 'iid' => $id]); |
||||
|
||||
if (empty($document)) { |
||||
// Try with normal id |
||||
/** @var CDocument $document */ |
||||
$document = $documentRepo->findOneBy(['cId' => $courseId, 'id' => $id]); |
||||
|
||||
if (empty($document)) { |
||||
Display::return_message(get_lang('FileNotFound'), 'error'); |
||||
exit; |
||||
} |
||||
} |
||||
|
||||
$documentPathInfo = pathinfo($document->getPath()); |
||||
$jplayer_supported_files = ['mp4', 'ogv', 'flv', 'm4v']; |
||||
$extension = isset($documentPathInfo['extension']) ? $documentPathInfo['extension'] : ''; |
||||
|
||||
$coursePath = api_get_path(SYS_COURSE_PATH).$courseInfo['directory']; |
||||
$documentPath = '/document'.$document->getPath(); |
||||
$documentText = file_get_contents($coursePath.$documentPath); |
||||
$documentText = api_remove_tags_with_space($documentText); |
||||
|
||||
$wordsInfo = preg_split('/ |\n/', $documentText, -1, PREG_SPLIT_OFFSET_CAPTURE); |
||||
$words = []; |
||||
|
||||
foreach ($wordsInfo as $wordInfo) { |
||||
$words[$wordInfo[1]] = nl2br($wordInfo[0]); |
||||
} |
||||
|
||||
$htmlHeadXtra[] = '<script> |
||||
var words = '.json_encode($words, JSON_OBJECT_AS_ARRAY).', |
||||
wordsCount = '.count($words).' |
||||
</script>'; |
||||
$htmlHeadXtra[] = api_get_js('readout_text/js/start.js'); |
||||
$htmlHeadXtra[] = api_get_css(api_get_path(WEB_LIBRARY_JS_PATH).'readout_text/css/start.css'); |
||||
|
||||
$template = new Template(strip_tags($document->getTitle())); |
||||
$template->display_blank_template(); |
@ -1,24 +1,45 @@ |
||||
IMS/LTI plugin |
||||
=== |
||||
|
||||
Version 1.0 (beta) |
||||
Version 1.1 (beta) |
||||
|
||||
Installation |
||||
------------ |
||||
1. Install the plugin from Plugin page |
||||
2. Enable the plugin from Plugin Settings page |
||||
3. Assign to the Administrator region |
||||
This plugin is meant to be later integrated into Chamilo (in a major version |
||||
release). |
||||
|
||||
IMS/LTI defines the possibility to integrate tools or content into Chamilo. |
||||
This plugin allows the integration of a new tool into courses, without (for now) |
||||
obtaining any data back from those tools. |
||||
It will gradually be developed to support IMS/LTI content items. |
||||
|
||||
As platform admin you can register external tools available for all courses. |
||||
You need set the tools settings in the IMS/LTI administration page. |
||||
Then the registered tools should be add in each course individually. |
||||
|
||||
As teacher you can register external tools available only for the current course. |
||||
You need follow the link in the IMS/LTI block located in the Course Settings tool. |
||||
Then select a previously tool registered or register a new external tool. |
||||
As teacher you can register external tools available only for the current |
||||
course. You need follow the link in the IMS/LTI block located in the Course |
||||
Settings tool. Then select a previously tool registered or register a new |
||||
external tool. |
||||
|
||||
This plugin is meant to be later integrated into Chamilo (in a major version release). |
||||
# Changelog |
||||
|
||||
IMS/LTI defines the possibility to integrate tools or content into Chamilo. |
||||
This plugin allows the integration of a new tool into courses, without (for now) obtaining any data back from those tools. |
||||
It will gradually be developed to support IMS/LTI content items. |
||||
**v1.1** |
||||
|
||||
* Support for Deep-Linking added. |
||||
|
||||
# Installation |
||||
|
||||
1. Install the plugin from Plugin page |
||||
2. Enable the plugin from Plugin Settings page |
||||
3. Assign to the Administrator region |
||||
|
||||
# Upgrading |
||||
|
||||
**To v1.1** |
||||
|
||||
Run this changes on database: |
||||
```sql |
||||
ALTER TABLE plugin_ims_lti_tool |
||||
ADD active_deep_linking TINYINT(1) DEFAULT '0' NOT NULL, |
||||
CHANGE id id INT AUTO_INCREMENT NOT NULL, |
||||
CHANGE launch_url launch_url VARCHAR(255) NOT NULL; |
||||
``` |
||||
|
@ -0,0 +1,60 @@ |
||||
<?php |
||||
/* For license terms, see /license.txt */ |
||||
|
||||
use Chamilo\CoreBundle\Entity\Course; |
||||
use Chamilo\PluginBundle\Entity\ImsLti\ImsLtiTool; |
||||
|
||||
require_once __DIR__.'/../../main/inc/global.inc.php'; |
||||
|
||||
api_protect_course_script(false); |
||||
api_block_anonymous_users(false); |
||||
|
||||
if (empty($_POST['content_items']) || empty($_POST['data'])) { |
||||
api_not_allowed(false); |
||||
} |
||||
|
||||
$toolId = str_replace('tool:', '', $_POST['data']); |
||||
|
||||
$plugin = ImsLtiPlugin::create(); |
||||
$em = Database::getManager(); |
||||
/** @var Course $course */ |
||||
$course = $em->find('ChamiloCoreBundle:Course', api_get_course_int_id()); |
||||
/** @var ImsLtiTool|null $ltiTool */ |
||||
$ltiTool = $em->find('ChamiloPluginBundle:ImsLti\ImsLtiTool', $toolId); |
||||
|
||||
if (!$ltiTool) { |
||||
api_not_allowed(false); |
||||
} |
||||
|
||||
$contentItems = json_decode($_POST['content_items'], true); |
||||
$contentItems = $contentItems['@graph']; |
||||
|
||||
foreach ($contentItems as $contentItem) { |
||||
if ('LtiLinkItem' === $contentItem['@type']) { |
||||
if ('application/vnd.ims.lti.v1.ltilink' === $contentItem['mediaType']) { |
||||
$plugin->saveItemAsLtiLink($contentItem, $ltiTool, $course); |
||||
|
||||
Display::addFlash( |
||||
Display::return_message($plugin->get_lang('ToolAdded'), 'success') |
||||
); |
||||
} |
||||
} |
||||
} |
||||
|
||||
$currentUrl = api_get_path(WEB_PLUGIN_PATH).'ims_lti/start.php?id='.$ltiTool->getId(); |
||||
?> |
||||
<!DOCTYPE html> |
||||
<html> |
||||
<head> |
||||
<meta charset="UTF-8"> |
||||
<meta name="viewport" |
||||
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> |
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge"> |
||||
<title>Document</title> |
||||
</head> |
||||
<body> |
||||
<script> |
||||
window.parent.location.href = '<?php echo $currentUrl ?>';
|
||||
</script> |
||||
</body> |
||||
</html> |
@ -1,6 +1,6 @@ |
||||
{% if tool.description %} |
||||
<p class="lead">{{ tool.description }}</p> |
||||
{% endif %} |
||||
<div class="embed-responsive embed-responsive-16by9"> |
||||
<div class="embed-responsive embed-responsive-4by3"> |
||||
<iframe src="{{ launch_url }}" class="plugin-ims-lti-iframe"></iframe> |
||||
</div> |
||||
|
@ -0,0 +1,7 @@ |
||||
# 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. |
@ -0,0 +1,289 @@ |
||||
<?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->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); |
||||
} |
||||
} |
@ -0,0 +1,136 @@ |
||||
<?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; |
||||
} |
||||
|
||||
$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; |
||||
} |
@ -0,0 +1,139 @@ |
||||
/* 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); |
||||
} |
||||
} |
||||
})(); |
@ -0,0 +1,26 @@ |
||||
<?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(); |
@ -0,0 +1,28 @@ |
||||
<?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(); |
@ -0,0 +1,14 @@ |
||||
<?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'] |
||||
); |
||||
} |
@ -0,0 +1,4 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
WhispeakAuthPlugin::create()->install(); |
@ -0,0 +1,33 @@ |
||||
<?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'; |
@ -0,0 +1,18 @@ |
||||
<?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'; |
@ -0,0 +1,33 @@ |
||||
<?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.'; |
@ -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,67 @@ |
||||
<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