upgrade
This commit is contained in:
366
plugin/whispeakauth/Controller/AuthenticationController.php
Normal file
366
plugin/whispeakauth/Controller/AuthenticationController.php
Normal file
@@ -0,0 +1,366 @@
|
||||
<?php
|
||||
/* For licensing terms, see /license.txt */
|
||||
|
||||
namespace Chamilo\PluginBundle\WhispeakAuth\Controller;
|
||||
|
||||
use Chamilo\PluginBundle\Entity\WhispeakAuth\LogEvent;
|
||||
use Chamilo\PluginBundle\WhispeakAuth\Request\ApiRequest;
|
||||
use Chamilo\UserBundle\Entity\User;
|
||||
use ChamiloSession;
|
||||
use Display;
|
||||
use Login;
|
||||
use WhispeakAuthPlugin;
|
||||
|
||||
/**
|
||||
* Class AuthenticationController.
|
||||
*
|
||||
* @package Chamilo\PluginBundle\WhispeakAuth\Controller
|
||||
*/
|
||||
class AuthenticationController extends BaseController
|
||||
{
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
if (!$this->plugin->toolIsEnabled()) {
|
||||
throw new \Exception(get_lang('NotAllowed'));
|
||||
}
|
||||
|
||||
/** @var array $lpQuestionInfo */
|
||||
$lpQuestionInfo = ChamiloSession::read(WhispeakAuthPlugin::SESSION_QUIZ_QUESTION, []);
|
||||
|
||||
if (ChamiloSession::read(WhispeakAuthPlugin::SESSION_AUTH_PASSWORD, false)) {
|
||||
ChamiloSession::erase(WhispeakAuthPlugin::SESSION_AUTH_PASSWORD);
|
||||
|
||||
if (empty($lpQuestionInfo)) {
|
||||
$message = $this->plugin->get_lang('MaxAttemptsReached')
|
||||
.'<br><strong>'.$this->plugin->get_lang('LoginWithUsernameAndPassword').'</strong>';
|
||||
|
||||
Display::addFlash(
|
||||
Display::return_message($message, 'warning')
|
||||
);
|
||||
}
|
||||
|
||||
header('Location: '.api_get_path(WEB_PLUGIN_PATH).'whispeakauth/authentify_password.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
/** @var array $lpItemInfo */
|
||||
$lpItemInfo = ChamiloSession::read(WhispeakAuthPlugin::SESSION_LP_ITEM, []);
|
||||
/** @var \learnpath $oLp */
|
||||
$oLp = ChamiloSession::read('oLP', null);
|
||||
/** @var \Exercise $objExercise */
|
||||
$objExercise = ChamiloSession::read('objExercise', null);
|
||||
|
||||
$isAuthOnLp = !empty($lpItemInfo) && !empty($oLp);
|
||||
$isAuthOnQuiz = !empty($lpQuestionInfo) && !empty($objExercise);
|
||||
$showFullPage = !$isAuthOnLp && !$isAuthOnQuiz;
|
||||
|
||||
$user = api_get_user_entity(
|
||||
ChamiloSession::read(WhispeakAuthPlugin::SESSION_2FA_USER, 0) ?: api_get_user_id()
|
||||
);
|
||||
|
||||
$showForm = !$user;
|
||||
|
||||
if ($user) {
|
||||
if (!WhispeakAuthPlugin::getAuthUidValue($user)) {
|
||||
$message = Display::return_message($this->plugin->get_lang('SpeechAuthNotEnrolled'), 'warning');
|
||||
|
||||
if (!empty($lpQuestionInfo) && empty($lpItemInfo)) {
|
||||
echo $message;
|
||||
} else {
|
||||
Display::addFlash($message);
|
||||
}
|
||||
|
||||
header('Location: '.api_get_path(WEB_PLUGIN_PATH).'whispeakauth/authentify_password.php');
|
||||
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($lpQuestionInfo) && empty($lpItemInfo)) {
|
||||
echo api_get_js('rtc/RecordRTC.js');
|
||||
echo api_get_js_simple(api_get_path(WEB_PLUGIN_PATH).'whispeakauth/assets/js/RecordAudio.js');
|
||||
}
|
||||
|
||||
$request = new ApiRequest();
|
||||
$response = $request->createAuthenticationSessionToken($user);
|
||||
|
||||
if (empty($response['text'])) {
|
||||
$varNumber = mt_rand(1, 6);
|
||||
$response['text'] = $this->plugin->get_lang("AuthentifySampleText$varNumber");
|
||||
}
|
||||
|
||||
ChamiloSession::write(WhispeakAuthPlugin::SESSION_SENTENCE_TEXT, $response['token']);
|
||||
|
||||
if (!empty($lpQuestionInfo) && empty($lpItemInfo)) {
|
||||
$template = new \Template('', $showFullPage, $showFullPage, false, true, false);
|
||||
$template->assign('show_form', $showForm);
|
||||
$template->assign('sample_text', $response['text']);
|
||||
|
||||
echo $template->fetch('whispeakauth/view/authentify_recorder.html.twig');
|
||||
exit;
|
||||
}
|
||||
|
||||
$this->displayPage(
|
||||
$showFullPage,
|
||||
[
|
||||
'show_form' => $showForm,
|
||||
'sample_text' => $response['text'],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function ajax()
|
||||
{
|
||||
$userId = api_get_user_id();
|
||||
$user2fa = ChamiloSession::read(WhispeakAuthPlugin::SESSION_2FA_USER, 0);
|
||||
|
||||
$result = [];
|
||||
|
||||
if (!empty($user2fa) || !empty($userId)) {
|
||||
$isAllowed = !empty($_FILES['audio']);
|
||||
} else {
|
||||
$isAllowed = !empty($_POST['username']) && !empty($_FILES['audio']);
|
||||
}
|
||||
|
||||
if (!$isAllowed || !$this->plugin->toolIsEnabled()) {
|
||||
throw new \Exception(get_lang('NotAllowed'));
|
||||
}
|
||||
|
||||
if (!empty($user2fa)) {
|
||||
$user = api_get_user_entity($user2fa);
|
||||
} elseif (!empty($userId)) {
|
||||
$user = api_get_user_entity($userId);
|
||||
} else {
|
||||
/** @var User|null $user */
|
||||
$user = \UserManager::getRepository()->findOneBy(['username' => $_POST['username']]);
|
||||
}
|
||||
|
||||
if (!$user) {
|
||||
throw new \Exception(get_lang('NotFound'));
|
||||
}
|
||||
|
||||
$audioFilePath = $this->uploadAudioFile($user);
|
||||
|
||||
$failedLogins = ChamiloSession::read(WhispeakAuthPlugin::SESSION_FAILED_LOGINS, 0);
|
||||
$maxAttempts = $this->plugin->getMaxAttempts();
|
||||
|
||||
if ($maxAttempts && $failedLogins >= $maxAttempts) {
|
||||
throw new \Exception($this->plugin->get_lang('MaxAttemptsReached'));
|
||||
}
|
||||
|
||||
$token = \ChamiloSession::read(\WhispeakAuthPlugin::SESSION_SENTENCE_TEXT);
|
||||
|
||||
\ChamiloSession::erase(\WhispeakAuthPlugin::SESSION_SENTENCE_TEXT);
|
||||
|
||||
/** @var array $lpItemInfo */
|
||||
$lpItemInfo = ChamiloSession::read(WhispeakAuthPlugin::SESSION_LP_ITEM, []);
|
||||
/** @var array $quizQuestionInfo */
|
||||
$quizQuestionInfo = ChamiloSession::read(WhispeakAuthPlugin::SESSION_QUIZ_QUESTION, []);
|
||||
|
||||
$success = true;
|
||||
|
||||
$request = new ApiRequest();
|
||||
|
||||
try {
|
||||
$request->performAuthentication($token, $user, $audioFilePath);
|
||||
|
||||
$message = $this->plugin->get_lang('AuthentifySuccess');
|
||||
} catch (\Exception $exception) {
|
||||
$message = $this->plugin->get_lang('AuthentifyFailed')
|
||||
.PHP_EOL
|
||||
.$exception->getMessage();
|
||||
|
||||
$success = false;
|
||||
}
|
||||
|
||||
if (!$success) {
|
||||
if (!empty($lpItemInfo)) {
|
||||
$this->plugin->addAttemptInLearningPath(
|
||||
LogEvent::STATUS_FAILED,
|
||||
$user->getId(),
|
||||
$lpItemInfo['lp_item'],
|
||||
$lpItemInfo['lp']
|
||||
);
|
||||
}
|
||||
|
||||
if (!empty($quizQuestionInfo)) {
|
||||
$this->plugin->addAttemptInQuiz(
|
||||
LogEvent::STATUS_FAILED,
|
||||
$user->getId(),
|
||||
$quizQuestionInfo['question'],
|
||||
$quizQuestionInfo['quiz']
|
||||
);
|
||||
}
|
||||
|
||||
if (empty($lpItemInfo) && empty($quizQuestionInfo)) {
|
||||
$this->plugin->addAuthenticationAttempt(LogEvent::STATUS_FAILED, $user->getId());
|
||||
}
|
||||
|
||||
$authTokenRequest = new ApiRequest();
|
||||
$authTokenResponse = $authTokenRequest->createAuthenticationSessionToken($user);
|
||||
|
||||
if (empty($authTokenResponse['text'])) {
|
||||
$varNumber = mt_rand(1, 6);
|
||||
$authTokenResponse['text'] = $this->plugin->get_lang("AuthentifySampleText$varNumber");
|
||||
}
|
||||
|
||||
$result['text'] = $authTokenResponse['text'];
|
||||
|
||||
ChamiloSession::write(WhispeakAuthPlugin::SESSION_SENTENCE_TEXT, $authTokenResponse['token']);
|
||||
|
||||
ChamiloSession::write(WhispeakAuthPlugin::SESSION_FAILED_LOGINS, ++$failedLogins);
|
||||
|
||||
if ($maxAttempts && $failedLogins >= $maxAttempts) {
|
||||
$message .= PHP_EOL
|
||||
.'<span data-reach-attempts="true">'.$this->plugin->get_lang('MaxAttemptsReached').'</span>'
|
||||
.PHP_EOL.PHP_EOL
|
||||
.'<strong>'
|
||||
.$this->plugin->get_lang('LoginWithUsernameAndPassword')
|
||||
.'</strong>';
|
||||
|
||||
if (!empty($user2fa)) {
|
||||
Display::addFlash(
|
||||
Display::return_message($message, 'warning', false)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$message .= PHP_EOL.$this->plugin->get_lang('TryAgain');
|
||||
|
||||
if ('true' === api_get_setting('allow_lostpassword')) {
|
||||
$message .= PHP_EOL
|
||||
.Display::url(
|
||||
get_lang('LostPassword'),
|
||||
api_get_path(WEB_CODE_PATH).'auth/lostPassword.php',
|
||||
['target' => $lpItemInfo ? '_top' : '_self']
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$result['resultHtml'] = Display::return_message(
|
||||
nl2br($message),
|
||||
$success ? 'success' : 'warning',
|
||||
false
|
||||
);
|
||||
|
||||
if (!$success && $maxAttempts && $failedLogins >= $maxAttempts) {
|
||||
ChamiloSession::erase(WhispeakAuthPlugin::SESSION_FAILED_LOGINS);
|
||||
|
||||
if (!empty($lpItemInfo)) {
|
||||
$result['resultHtml'] .= '<script>window.location.href = "'
|
||||
.api_get_path(WEB_PLUGIN_PATH)
|
||||
.'whispeakauth/authentify_password.php";</script>';
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
if (!empty($quizQuestionInfo)) {
|
||||
$url = api_get_path(WEB_CODE_PATH).'exercise/exercise_submit.php?'.$quizQuestionInfo['url_params'];
|
||||
|
||||
ChamiloSession::write(WhispeakAuthPlugin::SESSION_AUTH_PASSWORD, true);
|
||||
|
||||
$result['resultHtml'] .= "<script>window.location.href = '".$url."';</script>";
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
$result['resultHtml'] .= '<script>window.location.href = "'.api_get_path(WEB_PATH).'";</script>';
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
if ($success) {
|
||||
ChamiloSession::erase(WhispeakAuthPlugin::SESSION_SENTENCE_TEXT);
|
||||
ChamiloSession::erase(WhispeakAuthPlugin::SESSION_FAILED_LOGINS);
|
||||
|
||||
if (!empty($lpItemInfo)) {
|
||||
ChamiloSession::erase(WhispeakAuthPlugin::SESSION_LP_ITEM);
|
||||
ChamiloSession::erase(WhispeakAuthPlugin::SESSION_2FA_USER);
|
||||
|
||||
$this->plugin->addAttemptInLearningPath(
|
||||
LogEvent::STATUS_SUCCESS,
|
||||
$user->getId(),
|
||||
$lpItemInfo['lp_item'],
|
||||
$lpItemInfo['lp']
|
||||
);
|
||||
|
||||
$result['resultHtml'] .= '<script>window.location.href = "'.$lpItemInfo['src'].'";</script>';
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
if (!empty($quizQuestionInfo)) {
|
||||
$quizQuestionInfo['passed'] = true;
|
||||
$url = api_get_path(WEB_CODE_PATH).'exercise/exercise_submit.php?'.$quizQuestionInfo['url_params'];
|
||||
|
||||
ChamiloSession::write(WhispeakAuthPlugin::SESSION_QUIZ_QUESTION, $quizQuestionInfo);
|
||||
|
||||
$this->plugin->addAttemptInQuiz(
|
||||
LogEvent::STATUS_SUCCESS,
|
||||
$user->getId(),
|
||||
$quizQuestionInfo['question'],
|
||||
$quizQuestionInfo['quiz']
|
||||
);
|
||||
|
||||
$result['resultHtml'] .= '<script>window.location.href = "'.$url.'";</script>';
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
if (empty($lpItemInfo) && empty($quizQuestionInfo)) {
|
||||
$this->plugin->addAuthenticationAttempt(LogEvent::STATUS_SUCCESS, $user->getId());
|
||||
}
|
||||
|
||||
$loggedUser = [
|
||||
'user_id' => $user->getId(),
|
||||
'status' => $user->getStatus(),
|
||||
'uidReset' => true,
|
||||
];
|
||||
|
||||
if (empty($user2fa)) {
|
||||
ChamiloSession::write(WhispeakAuthPlugin::SESSION_2FA_USER, $user->getId());
|
||||
}
|
||||
|
||||
ChamiloSession::erase(WhispeakAuthPlugin::SESSION_FAILED_LOGINS);
|
||||
ChamiloSession::write('_user', $loggedUser);
|
||||
Login::init_user($user->getId(), true);
|
||||
|
||||
$result['resultHtml'] .= '<script>window.location.href = "'.api_get_path(WEB_PATH).'";</script>';
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function displayPage($isFullPage, array $variables)
|
||||
{
|
||||
global $htmlHeadXtra;
|
||||
|
||||
$htmlHeadXtra[] = api_get_js('rtc/RecordRTC.js');
|
||||
$htmlHeadXtra[] = api_get_js_simple(api_get_path(WEB_PLUGIN_PATH).'whispeakauth/assets/js/RecordAudio.js');
|
||||
|
||||
$pageTitle = $this->plugin->get_title();
|
||||
|
||||
$template = new \Template($pageTitle, $isFullPage, $isFullPage, !$isFullPage);
|
||||
|
||||
foreach ($variables as $key => $value) {
|
||||
$template->assign($key, $value);
|
||||
}
|
||||
|
||||
$pageContent = $template->fetch('whispeakauth/view/authentify_recorder.html.twig');
|
||||
|
||||
$template->assign('header', $pageTitle);
|
||||
$template->assign('content', $pageContent);
|
||||
$template->display_one_col_template();
|
||||
}
|
||||
}
|
||||
67
plugin/whispeakauth/Controller/BaseController.php
Normal file
67
plugin/whispeakauth/Controller/BaseController.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
/* For licensing terms, see /license.txt */
|
||||
|
||||
namespace Chamilo\PluginBundle\WhispeakAuth\Controller;
|
||||
|
||||
use Chamilo\UserBundle\Entity\User;
|
||||
use FFMpeg\FFMpeg;
|
||||
use FFMpeg\Format\Audio\Wav;
|
||||
|
||||
/**
|
||||
* Class BaseController.
|
||||
*
|
||||
* @package Chamilo\PluginBundle\WhispeakAuth\Controller
|
||||
*/
|
||||
abstract class BaseController
|
||||
{
|
||||
/**
|
||||
* @var \WhispeakAuthPlugin
|
||||
*/
|
||||
protected $plugin;
|
||||
|
||||
/**
|
||||
* BaseController constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->plugin = \WhispeakAuthPlugin::create();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $isFullPage
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
abstract protected function displayPage($isFullPage, array $variables);
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function uploadAudioFile(User $user)
|
||||
{
|
||||
$pluginName = $this->plugin->get_name();
|
||||
|
||||
$path = api_upload_file($pluginName, $_FILES['audio'], $user->getId());
|
||||
|
||||
if (false === $path) {
|
||||
throw new \Exception(get_lang('UploadError'));
|
||||
}
|
||||
|
||||
$fullPath = api_get_path(SYS_UPLOAD_PATH).$pluginName.$path['path_to_save'];
|
||||
$mimeType = mime_content_type($fullPath);
|
||||
|
||||
if ('wav' !== substr($mimeType, -3)) {
|
||||
$ffmpeg = FFMpeg::create();
|
||||
|
||||
$audioFile = $ffmpeg->open($fullPath);
|
||||
|
||||
$fullPath = dirname($fullPath).'/audio.wav';
|
||||
|
||||
$audioFile->save(new Wav(), $fullPath);
|
||||
}
|
||||
|
||||
return $fullPath;
|
||||
}
|
||||
}
|
||||
116
plugin/whispeakauth/Controller/EnrollmentController.php
Normal file
116
plugin/whispeakauth/Controller/EnrollmentController.php
Normal file
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
/* For licensing terms, see /license.txt */
|
||||
|
||||
namespace Chamilo\PluginBundle\WhispeakAuth\Controller;
|
||||
|
||||
use Chamilo\PluginBundle\WhispeakAuth\Request\ApiRequest;
|
||||
|
||||
/**
|
||||
* Class EnrollmentController.
|
||||
*
|
||||
* @package Chamilo\PluginBundle\WhispeakAuth\Controller
|
||||
*/
|
||||
class EnrollmentController extends BaseController
|
||||
{
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
if (!$this->plugin->toolIsEnabled()) {
|
||||
throw new \Exception(get_lang('NotAllowed'));
|
||||
}
|
||||
|
||||
$user = api_get_user_entity(api_get_user_id());
|
||||
|
||||
$userIsEnrolled = \WhispeakAuthPlugin::checkUserIsEnrolled($user->getId());
|
||||
|
||||
if ($userIsEnrolled) {
|
||||
throw new \Exception($this->plugin->get_lang('SpeechAuthAlreadyEnrolled'));
|
||||
}
|
||||
|
||||
$request = new ApiRequest();
|
||||
$response = $request->createEnrollmentSessionToken($user);
|
||||
|
||||
\ChamiloSession::write(\WhispeakAuthPlugin::SESSION_SENTENCE_TEXT, $response['token']);
|
||||
|
||||
$this->displayPage(
|
||||
true,
|
||||
[
|
||||
'action' => 'enrollment',
|
||||
'sample_text' => $response['text'],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function ajax()
|
||||
{
|
||||
$result = ['resultHtml' => ''];
|
||||
|
||||
if (!$this->plugin->toolIsEnabled() || empty($_FILES['audio'])) {
|
||||
throw new \Exception(get_lang('NotAllowed'));
|
||||
}
|
||||
|
||||
$user = api_get_user_entity(api_get_user_id());
|
||||
|
||||
$audioFilePath = $this->uploadAudioFile($user);
|
||||
|
||||
$token = \ChamiloSession::read(\WhispeakAuthPlugin::SESSION_SENTENCE_TEXT);
|
||||
|
||||
if (empty($token)) {
|
||||
throw new \Exception($this->plugin->get_lang('EnrollmentFailed'));
|
||||
}
|
||||
|
||||
$request = new ApiRequest();
|
||||
|
||||
try {
|
||||
$response = $request->createEnrollment($token, $audioFilePath, $user);
|
||||
} catch (\Exception $exception) {
|
||||
$enrollTokenRequest = new ApiRequest();
|
||||
$enrollTokenResponse = $enrollTokenRequest->createEnrollmentSessionToken($user);
|
||||
|
||||
\ChamiloSession::write(\WhispeakAuthPlugin::SESSION_SENTENCE_TEXT, $enrollTokenResponse['token']);
|
||||
|
||||
return [
|
||||
'resultHtml' => \Display::return_message($exception->getMessage(), 'error'),
|
||||
'text' => $enrollTokenResponse['text'],
|
||||
];
|
||||
}
|
||||
|
||||
\ChamiloSession::erase(\WhispeakAuthPlugin::SESSION_SENTENCE_TEXT);
|
||||
|
||||
$this->plugin->saveEnrollment($user, $response['speaker']);
|
||||
|
||||
$result['resultHtml'] .= \Display::return_message($this->plugin->get_lang('EnrollmentSuccess'), 'success');
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function displayPage($isFullPage, array $variables)
|
||||
{
|
||||
global $htmlHeadXtra;
|
||||
|
||||
$htmlHeadXtra[] = api_get_js('rtc/RecordRTC.js');
|
||||
$htmlHeadXtra[] = api_get_js_simple(api_get_path(WEB_PLUGIN_PATH).'whispeakauth/assets/js/RecordAudio.js');
|
||||
|
||||
$pageTitle = $this->plugin->get_lang('EnrollmentTitle');
|
||||
|
||||
$template = new \Template($pageTitle);
|
||||
|
||||
foreach ($variables as $key => $value) {
|
||||
$template->assign($key, $value);
|
||||
}
|
||||
|
||||
$pageContent = $template->fetch('whispeakauth/view/record_audio.html.twig');
|
||||
|
||||
$template->assign('header', $pageTitle);
|
||||
$template->assign('content', $pageContent);
|
||||
$template->display_one_col_template();
|
||||
}
|
||||
}
|
||||
145
plugin/whispeakauth/Entity/LogEvent.php
Normal file
145
plugin/whispeakauth/Entity/LogEvent.php
Normal file
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
/* For licensing terms, see /license.txt */
|
||||
|
||||
namespace Chamilo\PluginBundle\Entity\WhispeakAuth;
|
||||
|
||||
use Chamilo\UserBundle\Entity\User;
|
||||
use DateTime;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* Class LogEvent.
|
||||
*
|
||||
* @package Chamilo\PluginBundle\Entity\WhispeakAuth
|
||||
*
|
||||
* @ORM\Table(name="whispeak_log_event")
|
||||
* @ORM\Entity()
|
||||
* @ORM\InheritanceType("SINGLE_TABLE")
|
||||
* @ORM\DiscriminatorColumn(name="discr", type="string")
|
||||
* @ORM\DiscriminatorMap({
|
||||
* "log_event" = "Chamilo\PluginBundle\Entity\WhispeakAuth\LogEvent",
|
||||
* "log_event_lp" = "Chamilo\PluginBundle\Entity\WhispeakAuth\LogEventLp",
|
||||
* "log_event_quiz" = "Chamilo\PluginBundle\Entity\WhispeakAuth\LogEventQuiz"
|
||||
* })
|
||||
*/
|
||||
class LogEvent
|
||||
{
|
||||
public const STATUS_FAILED = 0;
|
||||
public const STATUS_SUCCESS = 1;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*
|
||||
* @ORM\Column(name="id", type="integer")
|
||||
* @ORM\Id()
|
||||
* @ORM\GeneratedValue()
|
||||
*/
|
||||
private $id;
|
||||
/**
|
||||
* @var DateTime
|
||||
*
|
||||
* @ORM\Column(name="datetime", type="datetime")
|
||||
*/
|
||||
private $datetime;
|
||||
/**
|
||||
* @var int
|
||||
*
|
||||
* @ORM\Column(name="action_status", type="smallint")
|
||||
*/
|
||||
private $actionStatus;
|
||||
/**
|
||||
* @var User
|
||||
*
|
||||
* @ORM\ManyToOne(targetEntity="Chamilo\UserBundle\Entity\User")
|
||||
* @ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=false)
|
||||
*/
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
*
|
||||
* @return LogEvent
|
||||
*/
|
||||
public function setId($id)
|
||||
{
|
||||
$this->id = $id;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DateTime
|
||||
*/
|
||||
public function getDatetime()
|
||||
{
|
||||
return $this->datetime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DateTime $datetime
|
||||
*
|
||||
* @return LogEvent
|
||||
*/
|
||||
public function setDatetime($datetime)
|
||||
{
|
||||
$this->datetime = $datetime;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getActionStatus()
|
||||
{
|
||||
return $this->actionStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $actionStatus
|
||||
*
|
||||
* @return LogEvent
|
||||
*/
|
||||
public function setActionStatus($actionStatus)
|
||||
{
|
||||
$this->actionStatus = $actionStatus;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return User
|
||||
*/
|
||||
public function getUser()
|
||||
{
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
*
|
||||
* @return LogEvent
|
||||
*/
|
||||
public function setUser($user)
|
||||
{
|
||||
$this->user = $user;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTypeString()
|
||||
{
|
||||
return '-';
|
||||
}
|
||||
}
|
||||
84
plugin/whispeakauth/Entity/LogEventLp.php
Normal file
84
plugin/whispeakauth/Entity/LogEventLp.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
/* For licensing terms, see /license.txt */
|
||||
|
||||
namespace Chamilo\PluginBundle\Entity\WhispeakAuth;
|
||||
|
||||
use Chamilo\CourseBundle\Entity\CLp;
|
||||
use Chamilo\CourseBundle\Entity\CLpItem;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* Class LogEventLp.
|
||||
*
|
||||
* @package Chamilo\PluginBundle\Entity\WhispeakAuth
|
||||
*
|
||||
* @ORM\Entity()
|
||||
*/
|
||||
class LogEventLp extends LogEvent
|
||||
{
|
||||
/**
|
||||
* @var CLpItem
|
||||
*
|
||||
* @ORM\ManyToOne(targetEntity="Chamilo\CourseBundle\Entity\CLpItem")
|
||||
* @ORM\JoinColumn(name="lp_item_id", referencedColumnName="iid")
|
||||
*/
|
||||
private $lpItem;
|
||||
/**
|
||||
* @var CLp
|
||||
*
|
||||
* @ORM\ManyToOne(targetEntity="Chamilo\CourseBundle\Entity\CLp")
|
||||
* @ORM\JoinColumn(name="lp_id", referencedColumnName="iid")
|
||||
*/
|
||||
private $lp;
|
||||
|
||||
/**
|
||||
* @return CLpItem
|
||||
*/
|
||||
public function getLpItem()
|
||||
{
|
||||
return $this->lpItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CLpItem $lpItem
|
||||
*
|
||||
* @return LogEventLp
|
||||
*/
|
||||
public function setLpItem($lpItem)
|
||||
{
|
||||
$this->lpItem = $lpItem;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CLp
|
||||
*/
|
||||
public function getLp()
|
||||
{
|
||||
return $this->lp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CLp $lp
|
||||
*
|
||||
* @return LogEventLp
|
||||
*/
|
||||
public function setLp($lp)
|
||||
{
|
||||
$this->lp = $lp;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTypeString()
|
||||
{
|
||||
$lpName = $this->lp->getName();
|
||||
$itemTitle = $this->getLpItem()->getTitle();
|
||||
|
||||
return "$lpName > $itemTitle";
|
||||
}
|
||||
}
|
||||
84
plugin/whispeakauth/Entity/LogEventQuiz.php
Normal file
84
plugin/whispeakauth/Entity/LogEventQuiz.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
/* For licensing terms, see /license.txt */
|
||||
|
||||
namespace Chamilo\PluginBundle\Entity\WhispeakAuth;
|
||||
|
||||
use Chamilo\CourseBundle\Entity\CQuiz;
|
||||
use Chamilo\CourseBundle\Entity\CQuizQuestion;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* Class LogEventQuiz.
|
||||
*
|
||||
* @package Chamilo\PluginBundle\Entity\WhispeakAuth
|
||||
*
|
||||
* @ORM\Entity()
|
||||
*/
|
||||
class LogEventQuiz extends LogEvent
|
||||
{
|
||||
/**
|
||||
* @var CQuizQuestion
|
||||
*
|
||||
* @ORM\ManyToOne(targetEntity="Chamilo\CourseBundle\Entity\CQuizQuestion")
|
||||
* @ORM\JoinColumn(name="question_id", referencedColumnName="iid")
|
||||
*/
|
||||
private $question;
|
||||
/**
|
||||
* @var CQuiz
|
||||
*
|
||||
* @ORM\ManyToOne(targetEntity="Chamilo\CourseBundle\Entity\CQuiz")
|
||||
* @ORM\JoinColumn(name="quiz_id", referencedColumnName="iid")
|
||||
*/
|
||||
private $quiz;
|
||||
|
||||
/**
|
||||
* @return CQuizQuestion
|
||||
*/
|
||||
public function getQuestion()
|
||||
{
|
||||
return $this->question;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CQuizQuestion $question
|
||||
*
|
||||
* @return LogEventQuiz
|
||||
*/
|
||||
public function setQuestion($question)
|
||||
{
|
||||
$this->question = $question;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CQuiz
|
||||
*/
|
||||
public function getQuiz()
|
||||
{
|
||||
return $this->quiz;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CQuiz $quiz
|
||||
*
|
||||
* @return LogEventQuiz
|
||||
*/
|
||||
public function setQuiz($quiz)
|
||||
{
|
||||
$this->quiz = $quiz;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTypeString()
|
||||
{
|
||||
$quiz = strip_tags($this->getQuiz()->getTitle());
|
||||
$question = strip_tags($this->getQuestion()->getQuestion());
|
||||
|
||||
return "$quiz > $question";
|
||||
}
|
||||
}
|
||||
23
plugin/whispeakauth/README.md
Normal file
23
plugin/whispeakauth/README.md
Normal file
@@ -0,0 +1,23 @@
|
||||
Speech authentication with Whispeak
|
||||
===================================
|
||||
|
||||
**Notice:**
|
||||
|
||||
This plugin requires the user to grant permission to use the microphone connected on the web browser. Currently,
|
||||
browsers are limiting this permission to be used only in a secure environment with HTTPS.
|
||||
**If your portal does not work with HTTPS, then Whispeak authentication may not work.**
|
||||
|
||||
Installation:
|
||||
-------------
|
||||
|
||||
*Prior to installing/uninstalling this plugin, you will need to make sure the src/Chamilo/PluginBundle/Entity folder is
|
||||
temporarily writeable by the web server. This might imply a manual change on your server (outside of the Chamilo
|
||||
interface).*
|
||||
|
||||
1. Install plugin in Chamilo.
|
||||
2. Set the plugin configuration enabling the plugin and (optionally) set the max attempts.
|
||||
3. Set the `login_bottom` region to the plugin.
|
||||
4. Add `$_configuration['whispeak_auth_enabled'] = true;` to `configuration.php` file.
|
||||
5. Optionally, you can add the `menu_administrator` region to se the user logged activities from Whispeak.
|
||||
|
||||
To have more information about whispeak or create an account to be able to use it on Chamilo you can go here <a href="https://whispeak.io/elearning/?source=chamilo">Whispeak</a>
|
||||
230
plugin/whispeakauth/Request/ApiRequest.php
Normal file
230
plugin/whispeakauth/Request/ApiRequest.php
Normal file
@@ -0,0 +1,230 @@
|
||||
<?php
|
||||
/* For licensing terms, see /license.txt */
|
||||
|
||||
namespace Chamilo\PluginBundle\WhispeakAuth\Request;
|
||||
|
||||
use Chamilo\UserBundle\Entity\User;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
|
||||
/**
|
||||
* Class ApiRequest.
|
||||
*
|
||||
* @package Chamilo\PluginBundle\WhispeakAuth\Request
|
||||
*/
|
||||
class ApiRequest
|
||||
{
|
||||
/**
|
||||
* @var \WhispeakAuthPlugin
|
||||
*/
|
||||
protected $plugin;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $apiKey;
|
||||
|
||||
/**
|
||||
* BaseController constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->plugin = \WhispeakAuthPlugin::create();
|
||||
$this->apiKey = $this->plugin->get(\WhispeakAuthPlugin::SETTING_TOKEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a session token to perform an enrollment.
|
||||
*
|
||||
* @throws \Exception
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function createEnrollmentSessionToken(User $user)
|
||||
{
|
||||
$apiKey = $this->plugin->get(\WhispeakAuthPlugin::SETTING_TOKEN);
|
||||
$langIso = api_get_language_isocode($user->getLanguage());
|
||||
|
||||
return $this->sendRequest(
|
||||
'get',
|
||||
'enroll',
|
||||
$apiKey,
|
||||
$langIso
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $token
|
||||
* @param string $audioFilePath
|
||||
*
|
||||
* @throws \Exception
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function createEnrollment($token, $audioFilePath, User $user)
|
||||
{
|
||||
$langIso = api_get_language_isocode($user->getLanguage());
|
||||
|
||||
return $this->sendRequest(
|
||||
'post',
|
||||
'enroll',
|
||||
$token,
|
||||
$langIso,
|
||||
[
|
||||
[
|
||||
'name' => 'file',
|
||||
'contents' => fopen($audioFilePath, 'r'),
|
||||
'filename' => basename($audioFilePath),
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function createAuthenticationSessionToken(User $user = null)
|
||||
{
|
||||
$apiKey = $this->plugin->get(\WhispeakAuthPlugin::SETTING_TOKEN);
|
||||
|
||||
$langIso = api_get_language_isocode($user ? $user->getLanguage() : null);
|
||||
|
||||
return $this->sendRequest(
|
||||
'get',
|
||||
'auth',
|
||||
$apiKey,
|
||||
$langIso
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function deleteEnrollment(User $user)
|
||||
{
|
||||
$apiKey = $this->plugin->get(\WhispeakAuthPlugin::SETTING_TOKEN);
|
||||
$langIso = api_get_language_isocode($user->getLanguage());
|
||||
$userAuthKey = \WhispeakAuthPlugin::getAuthUidValue($user->getId());
|
||||
|
||||
if (empty($userAuthKey) || empty($userAuthKey->getValue())) {
|
||||
throw new \Exception(get_plugin_lang('NoEnrollment', 'WhispeakAuthPlugin'));
|
||||
}
|
||||
|
||||
$queryData = ['speaker' => $userAuthKey->getValue()];
|
||||
|
||||
return $this->sendRequest(
|
||||
'delete',
|
||||
'enroll',
|
||||
$apiKey,
|
||||
$langIso,
|
||||
[],
|
||||
$queryData
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $token
|
||||
* @param string $audioFilePath
|
||||
*
|
||||
* @throws \Exception
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function performAuthentication($token, User $user, $audioFilePath)
|
||||
{
|
||||
$wsid = \WhispeakAuthPlugin::getAuthUidValue($user->getId());
|
||||
|
||||
if (empty($wsid)) {
|
||||
throw new \Exception($this->plugin->get_lang('SpeechAuthNotEnrolled'));
|
||||
}
|
||||
|
||||
$langIso = api_get_language_isocode($user ? $user->getLanguage() : null);
|
||||
|
||||
$this->sendRequest(
|
||||
'post',
|
||||
'auth',
|
||||
$token,
|
||||
$langIso,
|
||||
[
|
||||
[
|
||||
'name' => 'speaker',
|
||||
'contents' => $wsid->getValue(),
|
||||
],
|
||||
[
|
||||
'name' => 'file',
|
||||
'contents' => fopen($audioFilePath, 'r'),
|
||||
'filename' => basename($audioFilePath),
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $method
|
||||
* @param string $uri
|
||||
* @param string $authBearer
|
||||
* @param string $lang
|
||||
* @param array $queryParams
|
||||
*
|
||||
* @throws \GuzzleHttp\Exception\GuzzleException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function sendRequest($method, $uri, $authBearer, $lang, array $multipart = [], $queryParams = [])
|
||||
{
|
||||
$httpClient = new Client(['base_uri' => $this->plugin->getApiUrl()]);
|
||||
|
||||
$options = [];
|
||||
$options['headers'] = [
|
||||
'Authorization' => "Bearer $authBearer",
|
||||
'Accept-Language' => $lang,
|
||||
];
|
||||
|
||||
if ($queryParams) {
|
||||
$options['query'] = $queryParams;
|
||||
} else {
|
||||
$options['multipart'] = $multipart;
|
||||
}
|
||||
|
||||
try {
|
||||
$responseBody = $httpClient
|
||||
->request(
|
||||
$method,
|
||||
$uri,
|
||||
$options
|
||||
)
|
||||
->getBody()
|
||||
->getContents();
|
||||
|
||||
return json_decode($responseBody, true);
|
||||
} catch (RequestException $requestException) {
|
||||
if (!$requestException->hasResponse()) {
|
||||
throw new \Exception($requestException->getMessage());
|
||||
}
|
||||
|
||||
$responseBody = $requestException->getResponse()->getBody()->getContents();
|
||||
$json = json_decode($responseBody, true);
|
||||
|
||||
$message = '';
|
||||
|
||||
if (isset($json['asserts'])) {
|
||||
foreach ($json['asserts'] as $assert) {
|
||||
if ('invalid_' === substr($assert['value'], 0, 8)) {
|
||||
$message .= $assert['message'].PHP_EOL;
|
||||
}
|
||||
}
|
||||
} elseif (empty($json['message'])) {
|
||||
$message = $requestException->getMessage();
|
||||
} else {
|
||||
$message = is_array($json['message']) ? implode(PHP_EOL, $json['message']) : $json['message'];
|
||||
}
|
||||
|
||||
throw new \Exception($message);
|
||||
} catch (Exception $exception) {
|
||||
throw new \Exception($exception->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
804
plugin/whispeakauth/WhispeakAuthPlugin.php
Normal file
804
plugin/whispeakauth/WhispeakAuthPlugin.php
Normal file
@@ -0,0 +1,804 @@
|
||||
<?php
|
||||
/* For licensing terms, see /license.txt */
|
||||
|
||||
use Chamilo\CoreBundle\Entity\ExtraField;
|
||||
use Chamilo\CoreBundle\Entity\ExtraFieldValues;
|
||||
use Chamilo\PluginBundle\Entity\WhispeakAuth\LogEvent;
|
||||
use Chamilo\PluginBundle\Entity\WhispeakAuth\LogEventLp;
|
||||
use Chamilo\PluginBundle\Entity\WhispeakAuth\LogEventQuiz;
|
||||
use Chamilo\UserBundle\Entity\User;
|
||||
use Doctrine\ORM\Tools\SchemaTool;
|
||||
|
||||
/**
|
||||
* Class WhispeakAuthPlugin.
|
||||
*/
|
||||
class WhispeakAuthPlugin extends Plugin implements HookPluginInterface
|
||||
{
|
||||
public const SETTING_ENABLE = 'enable';
|
||||
public const SETTING_MAX_ATTEMPTS = 'max_attempts';
|
||||
public const SETTING_2FA = '2fa';
|
||||
public const SETTING_API_URL = 'api_url';
|
||||
public const SETTING_TOKEN = 'token';
|
||||
|
||||
public const EXTRAFIELD_AUTH_UID = 'whispeak_auth_uid';
|
||||
public const EXTRAFIELD_LP_ITEM = 'whispeak_lp_item';
|
||||
public const EXTRAFIELD_QUIZ_QUESTION = 'whispeak_quiz_question';
|
||||
|
||||
public const SESSION_FAILED_LOGINS = 'whispeak_failed_logins';
|
||||
public const SESSION_2FA_USER = 'whispeak_user_id';
|
||||
public const SESSION_LP_ITEM = 'whispeak_lp_item';
|
||||
public const SESSION_QUIZ_QUESTION = 'whispeak_quiz_question';
|
||||
public const SESSION_AUTH_PASSWORD = 'whispeak_auth_password';
|
||||
public const SESSION_SENTENCE_TEXT = 'whispeak_sentence_text';
|
||||
|
||||
/**
|
||||
* 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_MAX_ATTEMPTS => 'text',
|
||||
self::SETTING_2FA => 'boolean',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the admin URL for the plugin if Plugin::isAdminPlugin is true.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAdminUrl()
|
||||
{
|
||||
$webPath = api_get_path(WEB_PLUGIN_PATH).$this->get_name();
|
||||
|
||||
return "$webPath/admin.php";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WhispeakAuthPlugin
|
||||
*/
|
||||
public static function create()
|
||||
{
|
||||
static $result = null;
|
||||
|
||||
return $result ? $result : $result = new self();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Doctrine\ORM\Tools\ToolsException
|
||||
*/
|
||||
public function install()
|
||||
{
|
||||
$this->installExtraFields();
|
||||
$this->installEntities();
|
||||
$this->installHook();
|
||||
}
|
||||
|
||||
public function uninstall()
|
||||
{
|
||||
$this->uninstallHook();
|
||||
$this->uninstallExtraFields();
|
||||
$this->uninstallEntities();
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ExtraField
|
||||
*/
|
||||
public static function getLpItemExtraField()
|
||||
{
|
||||
$efRepo = Database::getManager()->getRepository('ChamiloCoreBundle:ExtraField');
|
||||
|
||||
/** @var ExtraField $extraField */
|
||||
$extraField = $efRepo->findOneBy(
|
||||
[
|
||||
'variable' => self::EXTRAFIELD_LP_ITEM,
|
||||
'extraFieldType' => ExtraField::LP_ITEM_FIELD_TYPE,
|
||||
]
|
||||
);
|
||||
|
||||
return $extraField;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ExtraField
|
||||
*/
|
||||
public static function getQuizQuestionExtraField()
|
||||
{
|
||||
$efRepo = Database::getManager()->getRepository('ChamiloCoreBundle:ExtraField');
|
||||
|
||||
/** @var ExtraField $extraField */
|
||||
$extraField = $efRepo->findOneBy(
|
||||
[
|
||||
'variable' => self::EXTRAFIELD_QUIZ_QUESTION,
|
||||
'extraFieldType' => ExtraField::QUESTION_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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the whispeak_lp_item value for a LP item ID.
|
||||
*
|
||||
* @param int $lpItemId
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
public static function getLpItemValue($lpItemId)
|
||||
{
|
||||
$efv = new ExtraFieldValue('lp_item');
|
||||
$value = $efv->get_values_by_handler_and_field_variable($lpItemId, self::EXTRAFIELD_LP_ITEM);
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $lpItemId
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isLpItemMarked($lpItemId)
|
||||
{
|
||||
if (!self::create()->isEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$value = self::getLpItemValue($lpItemId);
|
||||
|
||||
return !empty($value) && !empty($value['value']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the whispeak_quiz_question value for a quiz question ID.
|
||||
*
|
||||
* @param int $questionId
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
public static function getQuizQuestionValue($questionId)
|
||||
{
|
||||
$efv = new ExtraFieldValue('question');
|
||||
$value = $efv->get_values_by_handler_and_field_variable($questionId, self::EXTRAFIELD_QUIZ_QUESTION);
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $questionId
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isQuizQuestionMarked($questionId)
|
||||
{
|
||||
if (!self::create()->isEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$value = self::getQuizQuestionValue($questionId);
|
||||
|
||||
return !empty($value) && !empty($value['value']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $questionId
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function questionRequireAuthentify($questionId)
|
||||
{
|
||||
$isMarked = self::isQuizQuestionMarked($questionId);
|
||||
|
||||
if (!$isMarked) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$questionInfo = ChamiloSession::read(self::SESSION_QUIZ_QUESTION, []);
|
||||
|
||||
if (empty($questionInfo)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((int) $questionId !== $questionInfo['question']) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (false === $questionInfo['passed']) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 string $uid
|
||||
*
|
||||
* @throws \Doctrine\ORM\OptimisticLockException
|
||||
*/
|
||||
public function saveEnrollment(User $user, $uid)
|
||||
{
|
||||
$em = Database::getManager();
|
||||
$extraFieldValue = self::getAuthUidValue($user->getId());
|
||||
|
||||
if (empty($extraFieldValue)) {
|
||||
$extraField = self::getAuthUidExtraField();
|
||||
$now = new DateTime('now', new DateTimeZone('UTC'));
|
||||
|
||||
$extraFieldValue = new ExtraFieldValues();
|
||||
$extraFieldValue
|
||||
->setField($extraField)
|
||||
->setItemId($user->getId())
|
||||
->setUpdatedAt($now);
|
||||
}
|
||||
|
||||
$extraFieldValue->setValue($uid);
|
||||
|
||||
$em->persist($extraFieldValue);
|
||||
$em->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the max_attemtps option.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getMaxAttempts()
|
||||
{
|
||||
return (int) $this->get(self::SETTING_MAX_ATTEMPTS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Install hook when saving the plugin configuration.
|
||||
*
|
||||
* @return WhispeakAuthPlugin
|
||||
*/
|
||||
public function performActionsAfterConfigure()
|
||||
{
|
||||
$observer = WhispeakConditionalLoginHook::create();
|
||||
|
||||
if ('true' === $this->get(self::SETTING_2FA)) {
|
||||
HookConditionalLogin::create()->attach($observer);
|
||||
} else {
|
||||
HookConditionalLogin::create()->detach($observer);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will call the Hook management insertHook to add Hook observer from this plugin.
|
||||
*/
|
||||
public function installHook()
|
||||
{
|
||||
$observer = WhispeakMyStudentsLpTrackingHook::create();
|
||||
HookMyStudentsLpTracking::create()->attach($observer);
|
||||
|
||||
$observer = WhispeakMyStudentsQuizTrackingHook::create();
|
||||
HookMyStudentsQuizTracking::create()->attach($observer);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will call the Hook management deleteHook to disable Hook observer from this plugin.
|
||||
*/
|
||||
public function uninstallHook()
|
||||
{
|
||||
$observer = WhispeakConditionalLoginHook::create();
|
||||
HookConditionalLogin::create()->detach($observer);
|
||||
|
||||
$observer = WhispeakMyStudentsLpTrackingHook::create();
|
||||
HookMyStudentsLpTracking::create()->detach($observer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $userId
|
||||
*
|
||||
* @throws \Doctrine\ORM\OptimisticLockException
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function deleteEnrollment($userId)
|
||||
{
|
||||
$extraFieldValue = self::getAuthUidValue($userId);
|
||||
|
||||
if (empty($extraFieldValue)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$em = Database::getManager();
|
||||
$em->remove($extraFieldValue);
|
||||
$em->flush();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the WhispeakAuth plugin is installed and enabled.
|
||||
*
|
||||
* @param bool $checkEnabled Check if, additionnally to being installed, the plugin is enabled
|
||||
*/
|
||||
public function isEnabled(bool $checkEnabled = false): bool
|
||||
{
|
||||
return parent::isEnabled() && 'true' === api_get_plugin_setting('whispeakauth', self::SETTING_ENABLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $lpItemId
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isAllowedToSaveLpItem($lpItemId)
|
||||
{
|
||||
if (!self::isLpItemMarked($lpItemId)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$markedItem = ChamiloSession::read(self::SESSION_LP_ITEM, []);
|
||||
|
||||
if (empty($markedItem)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((int) $lpItemId !== (int) $markedItem['lp_item']) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a error message.
|
||||
*
|
||||
* @param string|null $error Optional. The message text
|
||||
*/
|
||||
public static function displayNotAllowedMessage($error = null)
|
||||
{
|
||||
$error = empty($error) ? get_lang('NotAllowed') : $error;
|
||||
|
||||
echo Display::return_message($error, 'error', false);
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $questionId
|
||||
*
|
||||
* @throws Exception
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function quizQuestionAuthentify($questionId, Exercise $exercise)
|
||||
{
|
||||
ChamiloSession::write(
|
||||
self::SESSION_QUIZ_QUESTION,
|
||||
[
|
||||
'quiz' => (int) $exercise->iid,
|
||||
'question' => (int) $questionId,
|
||||
'url_params' => $_SERVER['QUERY_STRING'],
|
||||
'passed' => false,
|
||||
]
|
||||
);
|
||||
|
||||
$template = new Template('', false, false, false, true, false, false);
|
||||
$template->assign('question', $questionId);
|
||||
$template->assign('exercise', $exercise->iid);
|
||||
$content = $template->fetch('whispeakauth/view/quiz_question.html.twig');
|
||||
|
||||
echo $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $status
|
||||
* @param int $userId
|
||||
* @param int $lpItemId
|
||||
* @param int $lpId
|
||||
*
|
||||
* @throws \Doctrine\ORM\ORMException
|
||||
* @throws \Doctrine\ORM\OptimisticLockException
|
||||
* @throws \Doctrine\ORM\TransactionRequiredException
|
||||
*
|
||||
* @return LogEventLp|null
|
||||
*/
|
||||
public function addAttemptInLearningPath($status, $userId, $lpItemId, $lpId)
|
||||
{
|
||||
$em = Database::getManager();
|
||||
|
||||
$user = api_get_user_entity($userId);
|
||||
$lpItem = $em->find('ChamiloCourseBundle:CLpItem', $lpItemId);
|
||||
$lp = $em->find('ChamiloCourseBundle:CLp', $lpId);
|
||||
|
||||
if (empty($lp) || empty($lpItem)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$logEvent = new LogEventLp();
|
||||
$logEvent
|
||||
->setLpItem($lpItem)
|
||||
->setLp($lp)
|
||||
->setUser($user)
|
||||
->setDatetime(
|
||||
api_get_utc_datetime(null, false, true)
|
||||
)
|
||||
->setActionStatus($status);
|
||||
|
||||
$em->persist($logEvent);
|
||||
$em->flush();
|
||||
|
||||
return $logEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $status
|
||||
* @param int $userId
|
||||
* @param int $questionId
|
||||
* @param int $quizId
|
||||
*
|
||||
* @throws \Doctrine\ORM\ORMException
|
||||
* @throws \Doctrine\ORM\OptimisticLockException
|
||||
* @throws \Doctrine\ORM\TransactionRequiredException
|
||||
*
|
||||
* @return LogEventQuiz|null
|
||||
*/
|
||||
public function addAttemptInQuiz($status, $userId, $questionId, $quizId)
|
||||
{
|
||||
$em = Database::getManager();
|
||||
|
||||
$user = api_get_user_entity($userId);
|
||||
$question = $em->find('ChamiloCourseBundle:CQuizQuestion', $questionId);
|
||||
$quiz = $em->find('ChamiloCourseBundle:CQuiz', $quizId);
|
||||
|
||||
if (empty($quiz) || empty($question)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$logEvent = new LogEventQuiz();
|
||||
$logEvent
|
||||
->setQuestion($question)
|
||||
->setQuiz($quiz)
|
||||
->setUser($user)
|
||||
->setDatetime(
|
||||
api_get_utc_datetime(null, false, true)
|
||||
)
|
||||
->setActionStatus($status);
|
||||
|
||||
$em->persist($logEvent);
|
||||
$em->flush();
|
||||
|
||||
return $logEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $status
|
||||
* @param int $userId
|
||||
*
|
||||
* @throws \Doctrine\ORM\ORMException
|
||||
* @throws \Doctrine\ORM\OptimisticLockException
|
||||
* @throws \Doctrine\ORM\TransactionRequiredException
|
||||
*
|
||||
* @return LogEvent|null
|
||||
*/
|
||||
public function addAuthenticationAttempt($status, $userId)
|
||||
{
|
||||
$em = Database::getManager();
|
||||
|
||||
$user = api_get_user_entity($userId);
|
||||
|
||||
$logEvent = new LogEvent();
|
||||
$logEvent
|
||||
->setUser($user)
|
||||
->setDatetime(
|
||||
api_get_utc_datetime(null, false, true)
|
||||
)
|
||||
->setActionStatus($status);
|
||||
|
||||
$em->persist($logEvent);
|
||||
$em->flush();
|
||||
|
||||
return $logEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $lpId
|
||||
* @param int $userId
|
||||
*
|
||||
* @throws \Doctrine\ORM\Query\QueryException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function countAllAttemptsInLearningPath($lpId, $userId)
|
||||
{
|
||||
$query = Database::getManager()
|
||||
->createQuery(
|
||||
'SELECT COUNT(log) AS c FROM ChamiloPluginBundle:WhispeakAuth\LogEventLp log
|
||||
WHERE log.lp = :lp AND log.user = :user'
|
||||
)
|
||||
->setParameters(['lp' => $lpId, 'user' => $userId]);
|
||||
|
||||
$totalCount = (int) $query->getSingleScalarResult();
|
||||
|
||||
return $totalCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $lpId
|
||||
* @param int $userId
|
||||
*
|
||||
* @throws \Doctrine\ORM\Query\QueryException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function countSuccessAttemptsInLearningPath($lpId, $userId)
|
||||
{
|
||||
$query = Database::getManager()
|
||||
->createQuery(
|
||||
'SELECT COUNT(log) AS c FROM ChamiloPluginBundle:WhispeakAuth\LogEventLp log
|
||||
WHERE log.lp = :lp AND log.user = :user AND log.actionStatus = :status'
|
||||
)
|
||||
->setParameters(['lp' => $lpId, 'user' => $userId, 'status' => LogEvent::STATUS_SUCCESS]);
|
||||
|
||||
$totalCount = (int) $query->getSingleScalarResult();
|
||||
|
||||
return $totalCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $quizId
|
||||
* @param int $userId
|
||||
*
|
||||
* @throws \Doctrine\ORM\Query\QueryException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function countAllAttemptsInQuiz($quizId, $userId)
|
||||
{
|
||||
$query = Database::getManager()
|
||||
->createQuery(
|
||||
'SELECT COUNT(log) AS c FROM ChamiloPluginBundle:WhispeakAuth\LogEventQuiz log
|
||||
WHERE log.quiz = :quiz AND log.user = :user'
|
||||
)
|
||||
->setParameters(['quiz' => $quizId, 'user' => $userId]);
|
||||
|
||||
$totalCount = (int) $query->getSingleScalarResult();
|
||||
|
||||
return $totalCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $quizId
|
||||
* @param int $userId
|
||||
*
|
||||
* @throws \Doctrine\ORM\Query\QueryException
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function countSuccessAttemptsInQuiz($quizId, $userId)
|
||||
{
|
||||
$query = Database::getManager()
|
||||
->createQuery(
|
||||
'SELECT COUNT(log) AS c FROM ChamiloPluginBundle:WhispeakAuth\LogEventQuiz log
|
||||
WHERE log.quiz = :quiz AND log.user = :user AND log.actionStatus = :status'
|
||||
)
|
||||
->setParameters(['quiz' => $quizId, 'user' => $userId, 'status' => LogEvent::STATUS_SUCCESS]);
|
||||
|
||||
$totalCount = (int) $query->getSingleScalarResult();
|
||||
|
||||
return $totalCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getApiUrl()
|
||||
{
|
||||
$url = $this->get(self::SETTING_API_URL);
|
||||
|
||||
return trim($url, " \t\n\r \v/").'/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Install extra fields for user, learning path and quiz question.
|
||||
*/
|
||||
private function installExtraFields()
|
||||
{
|
||||
UserManager::create_extra_field(
|
||||
self::EXTRAFIELD_AUTH_UID,
|
||||
\ExtraField::FIELD_TYPE_TEXT,
|
||||
$this->get_lang('Whispeak uid'),
|
||||
''
|
||||
);
|
||||
|
||||
LpItem::createExtraField(
|
||||
self::EXTRAFIELD_LP_ITEM,
|
||||
\ExtraField::FIELD_TYPE_CHECKBOX,
|
||||
$this->get_lang('MarkForSpeechAuthentication'),
|
||||
'0',
|
||||
true,
|
||||
true
|
||||
);
|
||||
|
||||
$extraField = new \ExtraField('question');
|
||||
$params = [
|
||||
'variable' => self::EXTRAFIELD_QUIZ_QUESTION,
|
||||
'field_type' => \ExtraField::FIELD_TYPE_CHECKBOX,
|
||||
'display_text' => $this->get_lang('MarkForSpeechAuthentication'),
|
||||
'default_value' => '0',
|
||||
'changeable' => true,
|
||||
'visible_to_self' => true,
|
||||
'visible_to_others' => false,
|
||||
];
|
||||
|
||||
$extraField->save($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Install the Doctrine's entities.
|
||||
*
|
||||
* @throws \Doctrine\ORM\Tools\ToolsException
|
||||
*/
|
||||
private function installEntities()
|
||||
{
|
||||
$em = Database::getManager();
|
||||
|
||||
if ($em->getConnection()->getSchemaManager()->tablesExist(['whispeak_log_event'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$schemaTool = new SchemaTool($em);
|
||||
$schemaTool->createSchema(
|
||||
[
|
||||
$em->getClassMetadata(LogEvent::class),
|
||||
$em->getClassMetadata(LogEventLp::class),
|
||||
$em->getClassMetadata(LogEventQuiz::class),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstall extra fields for user, learning path and quiz question.
|
||||
*/
|
||||
private function uninstallExtraFields()
|
||||
{
|
||||
$em = Database::getManager();
|
||||
|
||||
$authIdExtrafield = self::getAuthUidExtraField();
|
||||
|
||||
if (!empty($authIdExtrafield)) {
|
||||
$em
|
||||
->createQuery('DELETE FROM ChamiloCoreBundle:ExtraFieldValues efv WHERE efv.field = :field')
|
||||
->execute(['field' => $authIdExtrafield]);
|
||||
|
||||
$em->remove($authIdExtrafield);
|
||||
$em->flush();
|
||||
}
|
||||
|
||||
$lpItemExtrafield = self::getLpItemExtraField();
|
||||
|
||||
if (!empty($lpItemExtrafield)) {
|
||||
$em
|
||||
->createQuery('DELETE FROM ChamiloCoreBundle:ExtraFieldValues efv WHERE efv.field = :field')
|
||||
->execute(['field' => $lpItemExtrafield]);
|
||||
|
||||
$em->remove($lpItemExtrafield);
|
||||
$em->flush();
|
||||
}
|
||||
|
||||
$quizQuestionExtrafield = self::getQuizQuestionExtraField();
|
||||
|
||||
if (!empty($quizQuestionExtrafield)) {
|
||||
$em
|
||||
->createQuery('DELETE FROM ChamiloCoreBundle:ExtraFieldValues efv WHERE efv.field = :field')
|
||||
->execute(['field' => $quizQuestionExtrafield]);
|
||||
|
||||
$em->remove($quizQuestionExtrafield);
|
||||
$em->flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstall the Doctrine's entities.
|
||||
*/
|
||||
private function uninstallEntities()
|
||||
{
|
||||
$em = Database::getManager();
|
||||
|
||||
if (!$em->getConnection()->getSchemaManager()->tablesExist(['whispeak_log_event'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$schemaTool = new SchemaTool($em);
|
||||
$schemaTool->dropSchema(
|
||||
[
|
||||
$em->getClassMetadata(LogEvent::class),
|
||||
$em->getClassMetadata(LogEventLp::class),
|
||||
$em->getClassMetadata(LogEventQuiz::class),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
59
plugin/whispeakauth/WhispeakConditionalLoginHook.php
Normal file
59
plugin/whispeakauth/WhispeakConditionalLoginHook.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/* For licensing terms, see /license.txt */
|
||||
|
||||
/**
|
||||
* Class WhispeakConditionalLoginHook.
|
||||
*
|
||||
* Implements a Two-Factor Authentication with Whispeak.
|
||||
*/
|
||||
class WhispeakConditionalLoginHook extends HookObserver implements HookConditionalLoginObserverInterface
|
||||
{
|
||||
/**
|
||||
* WhispeakConditionalLoginHook constructor.
|
||||
*/
|
||||
protected function __construct()
|
||||
{
|
||||
parent::__construct(
|
||||
'plugin/whispeakauth/WhispeakAuthPlugin.php',
|
||||
'whispeakauth'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an associative array (callable, url) needed for Conditional Login.
|
||||
* <code>
|
||||
* [
|
||||
* 'conditional_function' => function (array $userInfo) {},
|
||||
* 'url' => '',
|
||||
* ]
|
||||
* </code>
|
||||
* conditional_function returns false to redirect to the url and returns true to continue with the classical login.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function hookConditionalLogin(HookConditionalLoginEventInterface $hook)
|
||||
{
|
||||
return [
|
||||
'conditional_function' => function (array $userInfo) {
|
||||
$isEnrolled = WhispeakAuthPlugin::checkUserIsEnrolled($userInfo['user_id']);
|
||||
|
||||
if (!$isEnrolled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$user2fa = (int) ChamiloSession::read(WhispeakAuthPlugin::SESSION_2FA_USER, 0);
|
||||
|
||||
if ($user2fa === (int) $userInfo['user_id']) {
|
||||
ChamiloSession::erase(WhispeakAuthPlugin::SESSION_2FA_USER);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ChamiloSession::write(WhispeakAuthPlugin::SESSION_2FA_USER, $userInfo['user_id']);
|
||||
|
||||
return false;
|
||||
},
|
||||
'url' => api_get_path(WEB_PLUGIN_PATH).$this->getPluginName().'/authentify.php',
|
||||
];
|
||||
}
|
||||
}
|
||||
67
plugin/whispeakauth/WhispeakMyStudentsLpTrackingHook.php
Normal file
67
plugin/whispeakauth/WhispeakMyStudentsLpTrackingHook.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
/* For licensing terms, see /license.txt */
|
||||
|
||||
/**
|
||||
* Class WhispeakMyStudentsLpTrackingHook.
|
||||
*/
|
||||
class WhispeakMyStudentsLpTrackingHook extends HookObserver implements HookMyStudentsLpTrackingObserverInterface
|
||||
{
|
||||
/**
|
||||
* WhispeakMyStudentsLpTrackingHook constructor.
|
||||
*/
|
||||
protected function __construct()
|
||||
{
|
||||
parent::__construct(
|
||||
'plugin/whispeakauth/WhispeakAuthPlugin.php',
|
||||
'whispeakauth'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function trackingHeader(HookMyStudentsLpTrackingEventInterface $hook)
|
||||
{
|
||||
if (false === WhispeakAuthPlugin::create()->isEnabled()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
'value' => WhispeakAuthPlugin::create()->get_lang('plugin_title'),
|
||||
'attrs' => ['class' => 'text-center'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Doctrine\ORM\Query\QueryException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function trackingContent(HookMyStudentsLpTrackingEventInterface $hook)
|
||||
{
|
||||
if (false === WhispeakAuthPlugin::create()->isEnabled()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$data = $hook->getEventData();
|
||||
|
||||
$totalCount = WhispeakAuthPlugin::countAllAttemptsInLearningPath($data['lp_id'], $data['student_id']);
|
||||
|
||||
if (0 === $totalCount) {
|
||||
return [
|
||||
'value' => '-',
|
||||
'attrs' => ['class' => 'text-center'],
|
||||
];
|
||||
}
|
||||
|
||||
$successCount = WhispeakAuthPlugin::countSuccessAttemptsInLearningPath($data['lp_id'], $data['student_id']);
|
||||
|
||||
$attrs = ['class' => 'text-center '];
|
||||
$attrs['class'] .= $successCount <= $totalCount / 2 ? 'text-danger' : 'text-success';
|
||||
|
||||
return [
|
||||
'value' => Display::tag('strong', "$successCount / $totalCount"),
|
||||
'attrs' => $attrs,
|
||||
];
|
||||
}
|
||||
}
|
||||
84
plugin/whispeakauth/WhispeakMyStudentsQuizTrackingHook.php
Normal file
84
plugin/whispeakauth/WhispeakMyStudentsQuizTrackingHook.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
/* For licensing terms, see /license.txt */
|
||||
|
||||
/**
|
||||
* Class WhispeakMyStudentsQuizTrackingHook.
|
||||
*/
|
||||
class WhispeakMyStudentsQuizTrackingHook extends HookObserver implements HookMyStudentsQuizTrackingObserverInterface
|
||||
{
|
||||
/**
|
||||
* WhispeakMyStudentsQuizTrackingHook constructor.
|
||||
*/
|
||||
protected function __construct()
|
||||
{
|
||||
parent::__construct(
|
||||
'plugin/whispeakauth/WhispeakAuthPlugin.php',
|
||||
'whispeakauth'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an associative array this value and attributes.
|
||||
* <code>
|
||||
* [
|
||||
* 'value' => 'Users online',
|
||||
* 'attrs' => ['class' => 'text-center'],
|
||||
* ]
|
||||
* </code>.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function trackingHeader(HookMyStudentsQuizTrackingEventInterface $hook)
|
||||
{
|
||||
if (false === WhispeakAuthPlugin::create()->isEnabled()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
'value' => WhispeakAuthPlugin::create()->get_lang('plugin_title'),
|
||||
'attrs' => [
|
||||
'class' => 'text-center',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an associative array this value and attributes.
|
||||
* <code>
|
||||
* [
|
||||
* 'value' => '5 connected users ',
|
||||
* 'attrs' => ['class' => 'text-center text-success'],
|
||||
* ]
|
||||
* </code>.
|
||||
*
|
||||
* @throws \Doctrine\ORM\Query\QueryException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function trackingContent(HookMyStudentsQuizTrackingEventInterface $hook)
|
||||
{
|
||||
if (false === WhispeakAuthPlugin::create()->isEnabled()) {
|
||||
return [];
|
||||
}
|
||||
$data = $hook->getEventData();
|
||||
|
||||
$totalCount = WhispeakAuthPlugin::countAllAttemptsInQuiz($data['quiz_id'], $data['student_id']);
|
||||
|
||||
if (0 === $totalCount) {
|
||||
return [
|
||||
'value' => '-',
|
||||
'attrs' => ['class' => 'text-center'],
|
||||
];
|
||||
}
|
||||
|
||||
$successCount = WhispeakAuthPlugin::countSuccessAttemptsInQuiz($data['quiz_id'], $data['student_id']);
|
||||
|
||||
$attrs = ['class' => 'text-center '];
|
||||
$attrs['class'] .= $successCount <= $totalCount / 2 ? 'text-danger' : 'text-success';
|
||||
|
||||
return [
|
||||
'value' => Display::tag('strong', "$successCount / $totalCount"),
|
||||
'attrs' => $attrs,
|
||||
];
|
||||
}
|
||||
}
|
||||
149
plugin/whispeakauth/admin.php
Normal file
149
plugin/whispeakauth/admin.php
Normal file
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
/* For licensing terms, see /license.txt */
|
||||
|
||||
use Chamilo\PluginBundle\Entity\WhispeakAuth\LogEvent;
|
||||
use Chamilo\PluginBundle\Entity\WhispeakAuth\LogEventLp;
|
||||
use Chamilo\PluginBundle\Entity\WhispeakAuth\LogEventQuiz;
|
||||
|
||||
$cidReset = true;
|
||||
|
||||
require_once __DIR__.'/../../main/inc/global.inc.php';
|
||||
|
||||
$plugin = WhispeakAuthPlugin::create();
|
||||
|
||||
api_protect_admin_script(true);
|
||||
|
||||
$plugin->protectTool();
|
||||
|
||||
$form = new FormValidator('frm_filter', 'GET');
|
||||
$form->addHeader($plugin->get_lang('ActionRegistryPerUser'));
|
||||
$slctUsers = $form->addSelectAjax(
|
||||
'users',
|
||||
get_lang('Users'),
|
||||
[],
|
||||
[
|
||||
'url' => api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_like',
|
||||
'id' => 'user_id',
|
||||
'multiple' => true,
|
||||
]
|
||||
);
|
||||
$form->addDatePicker('date', get_lang('Date'));
|
||||
$form->addButtonSearch(get_lang('Search'));
|
||||
$form->addRule('users', get_lang('ThisFieldIsRequired'), 'required');
|
||||
$form->addRule('date', get_lang('ThisFieldIsRequired'), 'required');
|
||||
|
||||
$results = [];
|
||||
|
||||
if ($form->validate()) {
|
||||
$formValues = $form->exportValues();
|
||||
$userIds = $formValues['users'] ?: [];
|
||||
/** @var \DateTime $date */
|
||||
$starDate = api_get_utc_datetime($formValues['date'], true, true);
|
||||
$endDate = clone $starDate;
|
||||
$endDate->modify('next day');
|
||||
|
||||
$em = Database::getManager();
|
||||
$repo = $em->getRepository('ChamiloPluginBundle:WhispeakAuth\LogEvent');
|
||||
|
||||
foreach ($userIds as $userId) {
|
||||
$qb = $em->createQueryBuilder();
|
||||
$results[$userId] = $qb
|
||||
->select('event')
|
||||
->from('ChamiloPluginBundle:WhispeakAuth\LogEvent', 'event')
|
||||
->where(
|
||||
$qb->expr()->gte('event.datetime', ':start_date')
|
||||
)
|
||||
->andWhere(
|
||||
$qb->expr()->lt('event.datetime', ':end_date')
|
||||
)
|
||||
->andWhere(
|
||||
$qb->expr()->eq('event.user', ':user')
|
||||
)
|
||||
->setParameters(
|
||||
[
|
||||
'start_date' => $starDate->format('Y-m-d H:i:s'),
|
||||
'end_date' => $endDate->format('Y-m-d H:i:s'),
|
||||
'user' => $userId,
|
||||
]
|
||||
)
|
||||
->getQuery()
|
||||
->getResult();
|
||||
}
|
||||
}
|
||||
|
||||
$pageContent = '';
|
||||
|
||||
/**
|
||||
* @var int $userId
|
||||
* @var array|LogEvent[] $logEvents
|
||||
*/
|
||||
foreach ($results as $userId => $logEvents) {
|
||||
if (empty($logEvents)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$user = $logEvents[0]->getUser();
|
||||
|
||||
$slctUsers->addOption($user->getCompleteNameWithUsername(), $user->getId());
|
||||
|
||||
$tableHeaders = [get_lang('DateTime'), get_lang('Type'), get_lang('Item'), get_lang('Result')];
|
||||
$tableData = [];
|
||||
|
||||
foreach ($logEvents as $i => $logEvent) {
|
||||
$type = '';
|
||||
|
||||
switch (get_class($logEvent)) {
|
||||
case LogEventQuiz::class:
|
||||
$type = get_lang('Question');
|
||||
break;
|
||||
case LogEventLp::class:
|
||||
$type = get_lang('LearningPath');
|
||||
break;
|
||||
}
|
||||
|
||||
$tableData[] = [
|
||||
api_convert_and_format_date($logEvent->getDatetime(), DATE_TIME_FORMAT_SHORT),
|
||||
$type,
|
||||
$logEvent->getTypeString(),
|
||||
$logEvent->getActionStatus() === LogEvent::STATUS_SUCCESS
|
||||
? Display::span(get_lang('Success'), ['class' => 'text-success'])
|
||||
: Display::span(get_lang('Failed'), ['class' => 'text-danger']),
|
||||
];
|
||||
}
|
||||
|
||||
$table = new HTML_Table(['class' => 'data_table table table-bordered table-hover table-striped table-condensed']);
|
||||
$table->setHeaders($tableHeaders);
|
||||
$table->setData($tableData);
|
||||
$table->updateColAttributes(0, ['class' => 'text-center']);
|
||||
$table->updateColAttributes(3, ['class' => 'text-center']);
|
||||
|
||||
$pageContent .= Display::page_subheader($user->getCompleteNameWithUsername(), null, 'h4');
|
||||
$pageContent .= $table->toHtml();
|
||||
}
|
||||
|
||||
$interbreadcrumb[] = [
|
||||
'name' => get_lang('Administration'),
|
||||
'url' => api_get_path(WEB_CODE_PATH).'admin/index.php',
|
||||
];
|
||||
|
||||
$actionsLeft = '';
|
||||
|
||||
if (!empty($results)) {
|
||||
$actionsLeft = Display::url(
|
||||
Display::return_icon('back.png', $plugin->get_lang('Back'), [], ICON_SIZE_MEDIUM),
|
||||
api_get_self()
|
||||
);
|
||||
}
|
||||
|
||||
$actionsRight = Display::url(
|
||||
Display::return_icon('delete_terms.png', $plugin->get_lang('Revocation'), [], ICON_SIZE_MEDIUM),
|
||||
'revocation.php'
|
||||
);
|
||||
|
||||
$template = new Template($plugin->get_title());
|
||||
$template->assign('actions', Display::toolbarAction('whispeak_admin', [$actionsLeft, $actionsRight]));
|
||||
$template->assign(
|
||||
'content',
|
||||
$form->returnForm().PHP_EOL.$pageContent
|
||||
);
|
||||
$template->display_one_col_template();
|
||||
140
plugin/whispeakauth/ajax/authentify_password.php
Normal file
140
plugin/whispeakauth/ajax/authentify_password.php
Normal file
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
/* For licensing terms, see /license.txt */
|
||||
|
||||
use Chamilo\PluginBundle\Entity\WhispeakAuth\LogEvent;
|
||||
|
||||
require_once __DIR__.'/../../../main/inc/global.inc.php';
|
||||
|
||||
api_block_anonymous_users(false);
|
||||
|
||||
$plugin = WhispeakAuthPlugin::create();
|
||||
|
||||
$plugin->protectTool(false);
|
||||
|
||||
$tokenIsValid = Security::check_token();
|
||||
|
||||
if (!$tokenIsValid) {
|
||||
WhispeakAuthPlugin::displayNotAllowedMessage();
|
||||
}
|
||||
|
||||
$maxAttempts = $plugin->getMaxAttempts();
|
||||
$failedLogins = ChamiloSession::read(WhispeakAuthPlugin::SESSION_FAILED_LOGINS, 0);
|
||||
|
||||
if ($maxAttempts && $failedLogins >= $maxAttempts) {
|
||||
echo Display::return_message($plugin->get_lang('MaxAttemptsReached'), 'warning');
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
$user = api_get_user_entity(api_get_user_id());
|
||||
$password = isset($_POST['password']) ? $_POST['password'] : null;
|
||||
|
||||
if (empty($password) || empty($user)) {
|
||||
WhispeakAuthPlugin::displayNotAllowedMessage();
|
||||
}
|
||||
|
||||
if (!in_array($user->getAuthSource(), [PLATFORM_AUTH_SOURCE, CAS_AUTH_SOURCE])) {
|
||||
WhispeakAuthPlugin::displayNotAllowedMessage();
|
||||
}
|
||||
|
||||
/** @var array $lpItemInfo */
|
||||
$lpItemInfo = ChamiloSession::read(WhispeakAuthPlugin::SESSION_LP_ITEM, []);
|
||||
/** @var array $quizQuestionInfo */
|
||||
$quizQuestionInfo = ChamiloSession::read(WhispeakAuthPlugin::SESSION_QUIZ_QUESTION, []);
|
||||
|
||||
$isValidPassword = UserManager::checkPassword($user->getPassword(), $password, $user->getSalt(), $user->getId());
|
||||
$isActive = $user->isActive();
|
||||
$isExpired = empty($user->getExpirationDate()) || $user->getExpirationDate() > api_get_utc_datetime(null, false, true);
|
||||
|
||||
$userPass = true;
|
||||
|
||||
if (!$isValidPassword || !$isActive || !$isExpired) {
|
||||
if (!empty($lpItemInfo)) {
|
||||
$plugin->addAttemptInLearningPath(
|
||||
LogEvent::STATUS_FAILED,
|
||||
$user->getId(),
|
||||
$lpItemInfo['lp_item'],
|
||||
$lpItemInfo['lp']
|
||||
);
|
||||
} elseif (!empty($quizQuestionInfo)) {
|
||||
$plugin->addAttemptInQuiz(
|
||||
LogEvent::STATUS_FAILED,
|
||||
$user->getId(),
|
||||
$quizQuestionInfo['question'],
|
||||
$quizQuestionInfo['quiz']
|
||||
);
|
||||
}
|
||||
|
||||
$userPass = false;
|
||||
|
||||
$message = $plugin->get_lang('AuthentifyFailed');
|
||||
|
||||
if (!$isActive) {
|
||||
$message .= PHP_EOL.get_lang('Account inactive');
|
||||
}
|
||||
|
||||
if (!$isExpired) {
|
||||
$message .= PHP_EOL.get_lang('AccountExpired');
|
||||
}
|
||||
|
||||
ChamiloSession::write(WhispeakAuthPlugin::SESSION_FAILED_LOGINS, ++$failedLogins);
|
||||
|
||||
if ($maxAttempts && $failedLogins >= $maxAttempts) {
|
||||
$message .= PHP_EOL.'<span data-reach-attempts="true">'.$plugin->get_lang('MaxAttemptsReached').'</span>';
|
||||
} else {
|
||||
$message .= PHP_EOL.$plugin->get_lang('TryAgain');
|
||||
}
|
||||
|
||||
echo Display::return_message($message, 'error', false);
|
||||
|
||||
if (!$maxAttempts ||
|
||||
($maxAttempts && $failedLogins >= $maxAttempts)
|
||||
) {
|
||||
$userPass = true;
|
||||
}
|
||||
} elseif ($isValidPassword) {
|
||||
ChamiloSession::erase(WhispeakAuthPlugin::SESSION_FAILED_LOGINS);
|
||||
ChamiloSession::erase(WhispeakAuthPlugin::SESSION_2FA_USER);
|
||||
|
||||
if (!empty($lpItemInfo)) {
|
||||
$plugin->addAttemptInLearningPath(
|
||||
LogEvent::STATUS_SUCCESS,
|
||||
$user->getId(),
|
||||
$lpItemInfo['lp_item'],
|
||||
$lpItemInfo['lp']
|
||||
);
|
||||
} elseif (!empty($quizQuestionInfo)) {
|
||||
$plugin->addAttemptInQuiz(
|
||||
LogEvent::STATUS_SUCCESS,
|
||||
$user->getId(),
|
||||
$quizQuestionInfo['question'],
|
||||
$quizQuestionInfo['quiz']
|
||||
);
|
||||
}
|
||||
|
||||
echo Display::return_message($plugin->get_lang('AuthentifySuccess'), 'success');
|
||||
}
|
||||
|
||||
if ($userPass) {
|
||||
$url = '';
|
||||
|
||||
ChamiloSession::erase(WhispeakAuthPlugin::SESSION_FAILED_LOGINS);
|
||||
ChamiloSession::erase(WhispeakAuthPlugin::SESSION_2FA_USER);
|
||||
|
||||
if ($lpItemInfo) {
|
||||
ChamiloSession::erase(WhispeakAuthPlugin::SESSION_LP_ITEM);
|
||||
|
||||
$url = $lpItemInfo['src'];
|
||||
} elseif ($quizQuestionInfo) {
|
||||
$quizQuestionInfo['passed'] = true;
|
||||
$url = api_get_path(WEB_CODE_PATH).'exercise/exercise_submit.php?'.$quizQuestionInfo['url_params'];
|
||||
|
||||
ChamiloSession::write(WhispeakAuthPlugin::SESSION_QUIZ_QUESTION, $quizQuestionInfo);
|
||||
}
|
||||
|
||||
if (!empty($url)) {
|
||||
echo '
|
||||
<script>window.location.href = "'.$url.'";</script>
|
||||
';
|
||||
}
|
||||
}
|
||||
48
plugin/whispeakauth/ajax/record_audio.php
Normal file
48
plugin/whispeakauth/ajax/record_audio.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
/* For licensing terms, see /license.txt */
|
||||
|
||||
use Chamilo\PluginBundle\WhispeakAuth\Controller\AuthenticationController;
|
||||
use Chamilo\PluginBundle\WhispeakAuth\Controller\EnrollmentController;
|
||||
|
||||
require_once __DIR__.'/../../../main/inc/global.inc.php';
|
||||
|
||||
$action = isset($_POST['action']) ? $_POST['action'] : 'enrollment';
|
||||
$isEnrollment = 'enrollment' === $action;
|
||||
$isAuthentify = 'authentify' === $action;
|
||||
|
||||
$isAllowed = false;
|
||||
|
||||
if ($isEnrollment) {
|
||||
api_block_anonymous_users(false);
|
||||
|
||||
$controller = new EnrollmentController();
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
try {
|
||||
echo json_encode($controller->ajax());
|
||||
} catch (Exception $exception) {
|
||||
echo json_encode(
|
||||
[
|
||||
'resultHtml' => Display::return_message($exception->getMessage(), 'error', false),
|
||||
]
|
||||
);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($isAuthentify) {
|
||||
$controller = new AuthenticationController();
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
try {
|
||||
echo json_encode($controller->ajax());
|
||||
} catch (Exception $exception) {
|
||||
echo json_encode(
|
||||
[
|
||||
'resultHtml' => Display::return_message($exception->getMessage(), 'error', false),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
191
plugin/whispeakauth/assets/js/RecordAudio.js
Normal file
191
plugin/whispeakauth/assets/js/RecordAudio.js
Normal file
@@ -0,0 +1,191 @@
|
||||
/* For licensing terms, see /license.txt */
|
||||
|
||||
window.RecordAudio = (function () {
|
||||
var timerInterval = 0,
|
||||
$txtTimer = null;
|
||||
|
||||
function startTimer() {
|
||||
stopTimer();
|
||||
|
||||
$txtTimer = $('#txt-timer');
|
||||
|
||||
$txtTimer.text('00:00').css('visibility', 'visible');
|
||||
|
||||
var timerData = {
|
||||
hour: 0,
|
||||
minute: 0,
|
||||
second: 0
|
||||
};
|
||||
|
||||
timerInterval = setInterval(function(){
|
||||
timerData.second++;
|
||||
|
||||
if (timerData.second >= 60) {
|
||||
timerData.second = 0;
|
||||
timerData.minute++;
|
||||
}
|
||||
|
||||
|
||||
$txtTimer.text(
|
||||
function () {
|
||||
var txtSeconds = timerData.minute < 10 ? '0' + timerData.minute : timerData.minute,
|
||||
txtMinutes = timerData.second < 10 ? '0' + timerData.second : timerData.second;
|
||||
|
||||
return txtSeconds + ':' + txtMinutes;
|
||||
}
|
||||
);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function stopTimer() {
|
||||
if (timerInterval) {
|
||||
clearInterval(timerInterval);
|
||||
}
|
||||
|
||||
if ($txtTimer) {
|
||||
$txtTimer.css('visibility', 'hidden');
|
||||
}
|
||||
}
|
||||
|
||||
function useRecordRTC(rtcInfo) {
|
||||
$(rtcInfo.blockId).show();
|
||||
|
||||
var mediaConstraints = {audio: true},
|
||||
localStream = null,
|
||||
recordRTC = null,
|
||||
btnStart = $(rtcInfo.btnStartId),
|
||||
btnStop = $(rtcInfo.btnStopId),
|
||||
tagAudio = $(rtcInfo.plyrPreviewId);
|
||||
|
||||
function saveAudio() {
|
||||
var recordedBlob = recordRTC.getBlob();
|
||||
|
||||
if (!recordedBlob) {
|
||||
return;
|
||||
}
|
||||
|
||||
var btnStopText = btnStop.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).text(btnStop.data('loadingtext'));
|
||||
}
|
||||
}).done(function (response) {
|
||||
if (response.text) {
|
||||
$('#txt-sample-text').text(response.text);
|
||||
}
|
||||
|
||||
$('#messages-deck').html(response.resultHtml);
|
||||
|
||||
if ($('#messages-deck > .alert.alert-success').length > 0) {
|
||||
tagAudio.parents('#audio-wrapper').addClass('hidden').removeClass('show');
|
||||
} else {
|
||||
tagAudio.parents('#audio-wrapper').removeClass('hidden').addClass('show');
|
||||
}
|
||||
|
||||
btnStop.prop('disabled', true).html(btnStopText).parent().addClass('hidden');
|
||||
|
||||
if ($('#messages-deck > .alert.alert-success').length > 0 ||
|
||||
$('#messages-deck > .alert.alert-warning [data-reach-attempts]').length > 0
|
||||
) {
|
||||
btnStart.prop('disabled', true);
|
||||
} else {
|
||||
btnStart.prop('disabled', false);
|
||||
}
|
||||
|
||||
btnStart.parent().removeClass('hidden');
|
||||
});
|
||||
}
|
||||
|
||||
btnStart.on('click', function () {
|
||||
tagAudio.prop('src', '');
|
||||
|
||||
function successCallback(stream) {
|
||||
localStream = stream;
|
||||
|
||||
recordRTC = RecordRTC(stream, {
|
||||
recorderType: RecordRTC.StereoAudioRecorder,
|
||||
type: 'audio',
|
||||
mimeType: 'audio/wav',
|
||||
numberOfAudioChannels: 2
|
||||
});
|
||||
recordRTC.startRecording();
|
||||
|
||||
btnStop.prop('disabled', false).parent().removeClass('hidden');
|
||||
btnStart.prop('disabled', true).parent().addClass('hidden');
|
||||
tagAudio.removeClass('show').parents('#audio-wrapper').addClass('hidden');
|
||||
|
||||
$('.fa-microphone').addClass('text-danger');
|
||||
|
||||
startTimer();
|
||||
}
|
||||
|
||||
function errorCallback(error) {
|
||||
alert(error);
|
||||
}
|
||||
|
||||
if(!!(navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia)) {
|
||||
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
|
||||
navigator.getUserMedia(mediaConstraints, successCallback, errorCallback);
|
||||
return;
|
||||
}
|
||||
|
||||
navigator.mediaDevices.getUserMedia(mediaConstraints)
|
||||
.then(successCallback)
|
||||
.catch(errorCallback);
|
||||
});
|
||||
|
||||
btnStop.on('click', function () {
|
||||
if (!recordRTC) {
|
||||
return;
|
||||
}
|
||||
|
||||
$('.fa-microphone').removeClass('text-danger');
|
||||
|
||||
stopTimer();
|
||||
|
||||
recordRTC.stopRecording(function (audioURL) {
|
||||
tagAudio.prop('src', audioURL);
|
||||
|
||||
localStream.getTracks()[0].stop();
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
})();
|
||||
17
plugin/whispeakauth/authentify.php
Normal file
17
plugin/whispeakauth/authentify.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
/* For licensing terms, see /license.txt */
|
||||
|
||||
use Chamilo\PluginBundle\WhispeakAuth\Controller\AuthenticationController;
|
||||
|
||||
require_once __DIR__.'/../../main/inc/global.inc.php';
|
||||
|
||||
$controller = new AuthenticationController();
|
||||
|
||||
try {
|
||||
$controller->index();
|
||||
} catch (Exception $exception) {
|
||||
api_not_allowed(
|
||||
true,
|
||||
Display::return_message($exception->getMessage(), 'warning')
|
||||
);
|
||||
}
|
||||
79
plugin/whispeakauth/authentify_password.php
Normal file
79
plugin/whispeakauth/authentify_password.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
/* For licensing terms, see /license.txt */
|
||||
|
||||
$cidReset = true;
|
||||
|
||||
require_once __DIR__.'/../../main/inc/global.inc.php';
|
||||
|
||||
$plugin = WhispeakAuthPlugin::create();
|
||||
|
||||
$plugin->protectTool();
|
||||
|
||||
$userId = ChamiloSession::read(WhispeakAuthPlugin::SESSION_2FA_USER, 0) ?: api_get_user_id();
|
||||
|
||||
/** @var array $lpItemInfo */
|
||||
$lpItemInfo = ChamiloSession::read(WhispeakAuthPlugin::SESSION_LP_ITEM, []);
|
||||
/** @var learnpath $oLp */
|
||||
$oLp = ChamiloSession::read('oLP', null);
|
||||
/** @var array $lpQuestionInfo */
|
||||
$lpQuestionInfo = ChamiloSession::read(WhispeakAuthPlugin::SESSION_QUIZ_QUESTION, []);
|
||||
/** @var Exercise $objExercise */
|
||||
$objExercise = ChamiloSession::read('objExercise', null);
|
||||
|
||||
$isAuthOnLp = !empty($lpItemInfo) && !empty($oLp);
|
||||
$isAuthOnQuiz = !empty($lpQuestionInfo) && !empty($objExercise);
|
||||
|
||||
$showFullPage = !$isAuthOnLp && !$isAuthOnQuiz;
|
||||
|
||||
if (empty($userId)) {
|
||||
api_not_allowed($showFullPage);
|
||||
}
|
||||
|
||||
if (!empty($lpQuestionInfo) && empty($lpItemInfo)) {
|
||||
echo Display::return_message(
|
||||
$plugin->get_lang('MaxAttemptsReached').'<br>'
|
||||
.'<strong>'.$plugin->get_lang('LoginWithUsernameAndPassword').'</strong>',
|
||||
'warning',
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
$form = new FormValidator(
|
||||
'form-login',
|
||||
'POST',
|
||||
api_get_path(WEB_PLUGIN_PATH).'whispeakauth/ajax/authentify_password.php',
|
||||
null,
|
||||
null,
|
||||
FormValidator::LAYOUT_BOX_NO_LABEL
|
||||
);
|
||||
$form->addElement(
|
||||
'password',
|
||||
'password',
|
||||
get_lang('Pass'),
|
||||
['id' => 'password', 'icon' => 'lock fa-fw', 'placeholder' => get_lang('Pass')]
|
||||
);
|
||||
$form->addHidden('sec_token', '');
|
||||
$form->setConstants(['sec_token' => Security::get_token()]);
|
||||
$form->addButton('submitAuth', get_lang('LoginEnter'), 'check', 'primary', 'default', 'btn-block');
|
||||
|
||||
$template = new Template(
|
||||
!$showFullPage ? '' : $plugin->get_title(),
|
||||
$showFullPage,
|
||||
$showFullPage,
|
||||
false,
|
||||
true,
|
||||
false
|
||||
);
|
||||
$template->assign('form', $form->returnForm());
|
||||
|
||||
$content = $template->fetch('whispeakauth/view/authentify_password.html.twig');
|
||||
|
||||
if (!empty($lpQuestionInfo) && empty($lpItemInfo)) {
|
||||
echo $content;
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
$template->assign('header', $plugin->get_title());
|
||||
$template->assign('content', $content);
|
||||
$template->display_one_col_template();
|
||||
21
plugin/whispeakauth/enrollment.php
Normal file
21
plugin/whispeakauth/enrollment.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
/* For licensing terms, see /license.txt */
|
||||
|
||||
use Chamilo\PluginBundle\WhispeakAuth\Controller\EnrollmentController;
|
||||
|
||||
$cidReset = true;
|
||||
|
||||
require_once __DIR__.'/../../main/inc/global.inc.php';
|
||||
|
||||
api_block_anonymous_users(true);
|
||||
|
||||
$controller = new EnrollmentController();
|
||||
|
||||
try {
|
||||
$controller->index();
|
||||
} catch (Exception $exception) {
|
||||
api_not_allowed(
|
||||
true,
|
||||
Display::return_message($exception->getMessage(), 'warning')
|
||||
);
|
||||
}
|
||||
14
plugin/whispeakauth/index.php
Normal file
14
plugin/whispeakauth/index.php
Normal file
@@ -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']
|
||||
);
|
||||
}
|
||||
4
plugin/whispeakauth/install.php
Normal file
4
plugin/whispeakauth/install.php
Normal file
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
/* For licensing terms, see /license.txt */
|
||||
|
||||
WhispeakAuthPlugin::create()->install();
|
||||
48
plugin/whispeakauth/lang/english.php
Normal file
48
plugin/whispeakauth/lang/english.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
/* For licensing terms, see /license.txt */
|
||||
|
||||
$strings['plugin_title'] = 'Speech authentication with Whispeak';
|
||||
$strings['plugin_comment'] = 'Allow speech authentication in Chamilo, which can integrate to the login or inside tests.';
|
||||
|
||||
$strings['enable'] = 'Enable';
|
||||
$strings['enable_help'] = 'Add <code>$_configuration[\'whispeak_auth_enabled\'] = true;</code> in the <code>configuration.php</code> file';
|
||||
$strings['api_url'] = 'API URL';
|
||||
$strings['api_url_help'] = 'http://api.whispeak.io:8080/v1/';
|
||||
$strings['token'] = 'API key';
|
||||
$strings['max_attempts'] = 'Max attempts';
|
||||
$strings['max_attempts_help'] = '(Optional) If the Whispeak authentication is failed x times, then ask and verify the password of the user';
|
||||
$strings['2fa'] = 'Two-Factor Authentication';
|
||||
$strings['2fa_help'] = 'Allows extend the login page with a Two-Factor Authentication process. After the classic login, the user must authenticate through Whispeak.';
|
||||
$strings['ActionRegistryPerUser'] = 'Action registry per user';
|
||||
|
||||
$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'] = 'Allow audio recording and then read this sentence out loud:';
|
||||
$strings['SpeechAuthAlreadyEnrolled'] = 'Speech authentication already enrolled previously.';
|
||||
$strings['SpeechAuthNotEnrolled'] = 'Speech authentication not 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';
|
||||
$strings['MaxAttemptsReached'] = 'You reached the maximum number of attempts allowed.';
|
||||
$strings['LoginWithUsernameAndPassword'] = 'You should login using the username and password.';
|
||||
$strings['YouNeedToIdentifyYourselfToAnswerThisQuestion'] = 'You need to identify yourself to answer this question.';
|
||||
$strings['IdentifyMe'] = 'Identify me';
|
||||
$strings['PleaseWaitWhileLoading'] = "Please wait while loading...";
|
||||
$strings['Quality'] = 'Quality';
|
||||
$strings['Failed'] = "Failed";
|
||||
$strings['ActivityId'] = "Activity ID";
|
||||
$strings['Success'] = "Success";
|
||||
$strings['MarkForSpeechAuthentication'] = 'Mark it for speech authentication';
|
||||
$strings['EnrollmentTitle'] = "Enrollment to generate voice print with Whispeak";
|
||||
$strings['Revocation'] = "Revocation";
|
||||
$strings['DeleteEnrollments'] = "Delete enrollments";
|
||||
$strings['NoEnrollment'] = "No enrollment.";
|
||||
$strings['EnrollmentDeleted'] = "Enrollment deleted";
|
||||
48
plugin/whispeakauth/lang/french.php
Normal file
48
plugin/whispeakauth/lang/french.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
/* For licensing terms, see /license.txt */
|
||||
|
||||
$strings['plugin_title'] = 'Authentification vocale avec Whispeak';
|
||||
$strings['plugin_comment'] = 'Autoriser l\'authentification de la voix dans Chamilo.';
|
||||
|
||||
$strings['enable'] = 'Activer';
|
||||
$strings['enable_help'] = '<p>Ajoutez <code>$_configuration[\'whispeak_auth_enabled\'] = true;</code> dans le fichier <code>configuration.php</code></p>';
|
||||
$strings['api_url'] = 'URL de l\'API';
|
||||
$strings['api_url_help'] = 'http://api.whispeak.io:8080/v1.1/';
|
||||
$strings['token'] = 'Clef API';
|
||||
$strings['max_attempts'] = 'Tentatives maximum';
|
||||
$strings['max_attempts_help'] = '(Optionnel) Si l\'authentification Whispeak échoue x fois, alors abandonner et demander le mot de passe de l\'utilisateur';
|
||||
$strings['2fa'] = 'Authentification à 2 facteurs (2FA)';
|
||||
$strings['2fa_help'] = 'Autoriser l\'extension du formulaire de login par une page d\'authentification forte. Après le login classique, l\'utilisateur/trice devra aussi s\'authentifier au travers de Whispeak.';
|
||||
$strings['ActionRegistryPerUser'] = 'Registre d\'actions par utilisateur';
|
||||
|
||||
$strings['EnrollmentSampleText'] = 'Le fameux chef-d\'oeuvre Mona Lisa a été peint par Léonardo da Vinci.';
|
||||
$strings['AuthentifySampleText1'] = 'Tomber comme des mouches.';
|
||||
$strings['AuthentifySampleText2'] = 'Restez vigilants.';
|
||||
$strings['AuthentifySampleText3'] = 'Le renard hurle à minuit.';
|
||||
$strings['AuthentifySampleText4'] = 'Errer dans la campagne.';
|
||||
$strings['AuthentifySampleText5'] = 'Sous l\'océan.';
|
||||
$strings['AuthentifySampleText6'] = 'Prendre la mouche.';
|
||||
$strings['RepeatThisPhrase'] = 'Autorisez l\'enregistrement audio puis lisez cette phrase à voix haute :';
|
||||
$strings['SpeechAuthAlreadyEnrolled'] = 'L\'authentification de voix a déjà réussi précédemment.';
|
||||
$strings['SpeechAuthNotEnrolled'] = 'L\'authentification de voix n\'a pas encore été enregistrée.';
|
||||
$strings['SpeechAuthentication'] = 'Authentification par la 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';
|
||||
$strings['MaxAttemptsReached'] = 'Vous avez atteint le nombre maximum de tentatives autorisées.';
|
||||
$strings['LoginWithUsernameAndPassword'] = 'Authentifiez-vous en utilisant votre mot de passe.';
|
||||
$strings['YouNeedToIdentifyYourselfToAnswerThisQuestion'] = 'Vous devez vous authentifier pour répondre à cette question.';
|
||||
$strings['IdentifyMe'] = 'M\'identifier';
|
||||
$strings['PleaseWaitWhileLoading'] = 'Connexion au serveur d\'authentification. Veuillez patienter...';
|
||||
$strings['Quality'] = 'Quality';
|
||||
$strings['Failed'] = "Failed";
|
||||
$strings['ActivityId'] = "Activity ID";
|
||||
$strings['Success'] = "Success";
|
||||
$strings['MarkForSpeechAuthentication'] = 'Cocher pour l\'authentification par la voix';
|
||||
$strings['EnrollmentTitle'] = "Enrôlement pour générer l'empreinte vocale avec Whispeak";
|
||||
$strings['Revocation'] = "Révocation";
|
||||
$strings['DeleteEnrollments'] = "Supprimer les inscriptions";
|
||||
$strings['NoEnrollment'] = "Aucune inscription";
|
||||
$strings['EnrollmentDeleted'] = "Inscription supprimée.";
|
||||
48
plugin/whispeakauth/lang/spanish.php
Normal file
48
plugin/whispeakauth/lang/spanish.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?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['enable_help'] = '<p>Agrega <code>$_configuration[\'whispeak_auth_enabled\'] = true;</code> al archivo <code>configuration.php</code></p>';
|
||||
$strings['api_url'] = 'URL del API';
|
||||
$strings['api_url_help'] = 'http://api.whispeak.io:8080/v1/';
|
||||
$strings['token'] = 'Llave del API';
|
||||
$strings['max_attempts'] = 'Máximo de intentos';
|
||||
$strings['max_attempts_help'] = '(Opcional) Si la autenticación de Whispeak falla x intentos, preguntar y verificar la contraseña del usuario';
|
||||
$strings['2fa'] = 'Autenticación en dos factores';
|
||||
$strings['2fa_help'] = 'Permite extender la página de inicio de sesión con un proceso de dos factores. Después del inicio de sesión clásico, el usuario deberá autenticarse a través de Whispeak.';
|
||||
$strings['ActionRegistryPerUser'] = 'Registro de acciones por usuario';
|
||||
|
||||
$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'] = 'Permita la grabación de audio y luego lea esta oración en voz alta:';
|
||||
$strings['SpeechAuthAlreadyEnrolled'] = 'Autenticación de voz registrada anteriormente.';
|
||||
$strings['SpeechAuthNotEnrolled'] = 'Autenticación de voz no registrada previamente.';
|
||||
$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.';
|
||||
$strings['MaxAttemptsReached'] = 'Ha alcanzado el número máximo de intentos permitidos.';
|
||||
$strings['LoginWithUsernameAndPassword'] = 'Debe iniciar sesión usando su nombre de usuario y contraseña.';
|
||||
$strings['YouNeedToIdentifyYourselfToAnswerThisQuestion'] = 'Necesita identificarse para responder esta pregunta.';
|
||||
$strings['IdentifyMe'] = 'Identificarme';
|
||||
$strings['PleaseWaitWhileLoading'] = "Por favor, espere mientras dure la carga...";
|
||||
$strings['Quality'] = 'Calidad';
|
||||
$strings['Failed'] = "Fallido";
|
||||
$strings['ActivityId'] = "Identificador de actividad";
|
||||
$strings['Success'] = "Satisfactotio";
|
||||
$strings['MarkForSpeechAuthentication'] = 'Marcarlo para autenticación con voz';
|
||||
$strings['EnrollmentTitle'] = "Inscripción para generar huella de voz con Whispeak";
|
||||
$strings['Revocation'] = "Revocación";
|
||||
$strings['DeleteEnrollments'] = "Eliminar inscripciones";
|
||||
$strings['NoEnrollment'] = "Sin inscripción.";
|
||||
$strings['EnrollmentDeleted'] = "Inscripción anulada.";
|
||||
4
plugin/whispeakauth/plugin.php
Normal file
4
plugin/whispeakauth/plugin.php
Normal file
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
/* For licensing terms, see /license.txt */
|
||||
|
||||
$plugin_info = WhispeakAuthPlugin::create()->get_info();
|
||||
94
plugin/whispeakauth/revocation.php
Normal file
94
plugin/whispeakauth/revocation.php
Normal file
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
/* For licensing terms, see /license.txt */
|
||||
|
||||
use Chamilo\PluginBundle\WhispeakAuth\Request\ApiRequest;
|
||||
|
||||
$cidReset = true;
|
||||
|
||||
require_once __DIR__.'/../../main/inc/global.inc.php';
|
||||
|
||||
$plugin = WhispeakAuthPlugin::create();
|
||||
|
||||
api_protect_admin_script(true);
|
||||
|
||||
$plugin->protectTool();
|
||||
|
||||
$pageContent = '';
|
||||
|
||||
$form = new FormValidator('frm_revocation', 'GET');
|
||||
$form->setAttribute('onsubmit', "return confirm('".addslashes(get_lang('AreYouSureToDelete'))."');");
|
||||
$slctUsers = $form->addSelectAjax(
|
||||
'users',
|
||||
get_lang('Users'),
|
||||
[],
|
||||
[
|
||||
'url' => api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_like',
|
||||
'id' => 'user_id',
|
||||
'multiple' => true,
|
||||
]
|
||||
);
|
||||
$form->addButton('asubmit', $plugin->get_lang('DeleteEnrollments'), 'times', 'danger');
|
||||
$form->addRule('users', get_lang('ThisFieldIsRequired'), 'required');
|
||||
|
||||
$userIds = [];
|
||||
|
||||
if ($form->validate()) {
|
||||
$formValues = $form->exportValues();
|
||||
$userIds = $formValues['users'] ?: [];
|
||||
|
||||
/** @var int $userId */
|
||||
foreach ($userIds as $userId) {
|
||||
$user = api_get_user_entity($userId);
|
||||
|
||||
if (null === $user) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$slctUsers->addOption($user->getCompleteNameWithUsername(), $user->getId());
|
||||
|
||||
$request = new ApiRequest();
|
||||
|
||||
$pageContent .= Display::page_subheader($user->getCompleteNameWithUsername(), null, 'h4');
|
||||
|
||||
try {
|
||||
$request->deleteEnrollment($user);
|
||||
|
||||
$response = WhispeakAuthPlugin::deleteEnrollment($user->getId());
|
||||
|
||||
$pageContent .= Display::return_message(
|
||||
$plugin->get_lang('EnrollmentDeleted'),
|
||||
'success'
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
$pageContent .= Display::return_message(
|
||||
$e->getMessage(),
|
||||
'error'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$interbreadcrumb[] = [
|
||||
'name' => get_lang('Administration'),
|
||||
'url' => api_get_path(WEB_CODE_PATH).'admin/index.php',
|
||||
];
|
||||
$interbreadcrumb[] = [
|
||||
'name' => $plugin->get_title(),
|
||||
'url' => 'admin.php',
|
||||
];
|
||||
|
||||
$actionsLeft = Display::url(
|
||||
Display::return_icon('back.png', $plugin->get_lang('Back'), [], ICON_SIZE_MEDIUM),
|
||||
'admin.php'
|
||||
);
|
||||
|
||||
$pageTitle = $plugin->get_lang('Revocation');
|
||||
|
||||
$template = new Template($pageTitle);
|
||||
$template->assign('actions', Display::toolbarAction('whispeak_admin', [$actionsLeft]));
|
||||
$template->assign('header', $pageTitle);
|
||||
$template->assign(
|
||||
'content',
|
||||
$form->returnForm().PHP_EOL.$pageContent
|
||||
);
|
||||
$template->display_one_col_template();
|
||||
4
plugin/whispeakauth/uninstall.php
Normal file
4
plugin/whispeakauth/uninstall.php
Normal file
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
/* For licensing terms, see /license.txt */
|
||||
|
||||
WhispeakAuthPlugin::create()->uninstall();
|
||||
44
plugin/whispeakauth/view/authentify_password.html.twig
Normal file
44
plugin/whispeakauth/view/authentify_password.html.twig
Normal file
@@ -0,0 +1,44 @@
|
||||
<div class="row">
|
||||
<div class="col-sm-6 col-sm-offset-3 col-md-4 col-md-offset-4">
|
||||
{{ form }}
|
||||
|
||||
<br>
|
||||
|
||||
<div id="frm-login-result"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(function () {
|
||||
$('#form-login').on('submit', function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
var formData = new FormData(this),
|
||||
self = $(this);
|
||||
self.children().prop('disabled', true);
|
||||
self.find(':submit em.fa').get(0).className = 'fa fa-spinner fa-spin';
|
||||
|
||||
$
|
||||
.ajax({
|
||||
type: 'POST',
|
||||
url: this.action,
|
||||
data: formData,
|
||||
processData: false,
|
||||
contentType: false
|
||||
})
|
||||
.then(function (response) {
|
||||
$('#frm-login-result').html(response);
|
||||
self.children().prop('disabled', false);
|
||||
self.find(':submit em.fa').get(0).className = 'fa fa-check';
|
||||
|
||||
if ($('#frm-login-result > .alert.alert-success').length > 0 ||
|
||||
$('#frm-login-result > .alert.alert-danger [data-reach-attempts]').length > 0
|
||||
) {
|
||||
self.find(':submit').prop('disabled', true);
|
||||
}
|
||||
});
|
||||
|
||||
this.reset();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
44
plugin/whispeakauth/view/authentify_recorder.html.twig
Normal file
44
plugin/whispeakauth/view/authentify_recorder.html.twig
Normal file
@@ -0,0 +1,44 @@
|
||||
{% extends 'whispeakauth/view/record_audio.html.twig' %}
|
||||
|
||||
{% block intro %}
|
||||
{% if show_form %}
|
||||
<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>
|
||||
{% endif %}
|
||||
|
||||
{{ parent() }}
|
||||
{% endblock %}
|
||||
|
||||
{% block config_data %}
|
||||
{% if show_form %}
|
||||
$('#username').on('change', function () {
|
||||
$('#record-audio-recordrtc, #btn-start-record, #btn-stop-record, #btn-save-record').off('click', '');
|
||||
{% endif %}
|
||||
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()
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
{% if show_form %}
|
||||
});
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
45
plugin/whispeakauth/view/quiz_question.html.twig
Normal file
45
plugin/whispeakauth/view/quiz_question.html.twig
Normal file
@@ -0,0 +1,45 @@
|
||||
<div class="text-center">
|
||||
<p>{{ 'YouNeedToIdentifyYourselfToAnswerThisQuestion'|get_plugin_lang('WhispeakAuthPlugin') }}</p>
|
||||
|
||||
<button type="button" class="btn btn-info" id="whispeak-question-{{ question }}" data-loading-text="{{ 'PleaseWaitWhileLoading'|get_plugin_lang('WhispeakAuthPlugin')|escape('html') }}">
|
||||
{{ 'IdentifyMe'|get_plugin_lang('WhispeakAuthPlugin') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(function () {
|
||||
function loadAuth() {
|
||||
var $btnTrigger = $('#whispeak-question-{{ question }}'),
|
||||
originalText = $btnTrigger.text(),
|
||||
$modal = $('#global-modal'),
|
||||
$modalTitle = $modal.find('.modal-title'),
|
||||
$modalBody = $modal.find('.modal-body'),
|
||||
$originalLoadingText = $btnTrigger.data('loading-text');
|
||||
|
||||
$btnTrigger.text($originalLoadingText).prop('disabled', true);
|
||||
|
||||
$modalTitle.text($originalLoadingText);
|
||||
$modalBody.html('');
|
||||
$modal.modal('show');
|
||||
|
||||
$
|
||||
.ajax({
|
||||
url: _p.web_plugin + 'whispeakauth/authentify.php'
|
||||
})
|
||||
.then(function (response) {
|
||||
$modalBody.html(response);
|
||||
|
||||
$modalTitle.text('{{ 'plugin_title'|get_plugin_lang('WhispeakAuthPlugin') }}');
|
||||
$btnTrigger.text(originalText).prop('disabled', false);
|
||||
});
|
||||
}
|
||||
|
||||
$('#whispeak-question-{{ question }}').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
loadAuth();
|
||||
});
|
||||
|
||||
loadAuth();
|
||||
});
|
||||
</script>
|
||||
59
plugin/whispeakauth/view/record_audio.html.twig
Normal file
59
plugin/whispeakauth/view/record_audio.html.twig
Normal file
@@ -0,0 +1,59 @@
|
||||
<div id="record-audio-recordrtc" class="row">
|
||||
<div class="col-sm-8 col-sm-offset-2 col-md-6 col-md-offset-0">
|
||||
{% 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 id="txt-timer" class="text-danger h3 show"></span>
|
||||
</div>
|
||||
<div class="col-sm-9 text-center">
|
||||
<p class="lead" id="txt-sample-text">{{ sample_text }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
<div class="text-center">
|
||||
<p>
|
||||
<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>
|
||||
</p>
|
||||
<p class="hidden">
|
||||
<button class="btn btn-danger" type="button" id="btn-stop-record" disabled
|
||||
data-loadingtext="{{ 'Uploading'|get_lang }}">
|
||||
<span class="fa fa-square fa-fw" aria-hidden="true"></span> {{ 'StopRecordingAudio'|get_lang }}
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-8 col-sm-offset-2 col-md-5 col-md-offset-1">
|
||||
<hr class="visible-sm">
|
||||
<div id="messages-deck"></div>
|
||||
<div class="hidden" id="audio-wrapper">
|
||||
<p>
|
||||
<audio class="center-block" controls id="record-preview"></audio>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(function () {
|
||||
{% block config_data %}
|
||||
var data = {action: 'enrollment', license: 0 };
|
||||
|
||||
RecordAudio.init(
|
||||
{
|
||||
blockId: '#record-audio-recordrtc',
|
||||
btnStartId: '#btn-start-record',
|
||||
btnStopId: '#btn-stop-record',
|
||||
plyrPreviewId: '#record-preview',
|
||||
data: data
|
||||
}
|
||||
);
|
||||
{% endblock %}
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user