parent
9ff9f0fa6c
commit
dbd33061c1
File diff suppressed because one or more lines are too long
@ -0,0 +1,278 @@ |
|||||||
|
<?php |
||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
use Chamilo\CoreBundle\Entity\TrackEAttempt; |
||||||
|
|
||||||
|
/** |
||||||
|
* Class QuestionOptionsEvaluationPlugin. |
||||||
|
*/ |
||||||
|
class QuestionOptionsEvaluationPlugin extends Plugin |
||||||
|
{ |
||||||
|
const SETTING_ENABLE = 'enable'; |
||||||
|
const SETTING_MAX_SCORE = 'exercise_max_score'; |
||||||
|
|
||||||
|
const EXTRAFIELD_FORMULA = 'quiz_evaluation_formula'; |
||||||
|
|
||||||
|
/** |
||||||
|
* QuestionValuationPlugin constructor. |
||||||
|
*/ |
||||||
|
protected function __construct() |
||||||
|
{ |
||||||
|
$version = '1.0'; |
||||||
|
$author = 'Angel Fernando Quiroz Campos'; |
||||||
|
|
||||||
|
parent::__construct( |
||||||
|
$version, |
||||||
|
$author, |
||||||
|
[ |
||||||
|
self::SETTING_ENABLE => 'boolean', |
||||||
|
self::SETTING_MAX_SCORE => 'text', |
||||||
|
] |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return QuestionOptionsEvaluationPlugin|null |
||||||
|
*/ |
||||||
|
public static function create() |
||||||
|
{ |
||||||
|
static $result = null; |
||||||
|
|
||||||
|
return $result ? $result : $result = new self(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param int $exerciseId |
||||||
|
* @param int $iconSize |
||||||
|
* |
||||||
|
* @return string |
||||||
|
*/ |
||||||
|
public static function filterModify($exerciseId, $iconSize = ICON_SIZE_SMALL) |
||||||
|
{ |
||||||
|
$directory = basename(__DIR__); |
||||||
|
$title = get_plugin_lang('plugin_title', self::class); |
||||||
|
$enabled = api_get_plugin_setting('questionoptionsevaluation', 'enable'); |
||||||
|
|
||||||
|
if ('true' !== $enabled) { |
||||||
|
return ''; |
||||||
|
} |
||||||
|
|
||||||
|
return Display::url( |
||||||
|
Display::return_icon('options_evaluation.png', $title, [], $iconSize), |
||||||
|
api_get_path(WEB_PATH)."plugin/$directory/evaluation.php?exercise=$exerciseId", |
||||||
|
[ |
||||||
|
'class' => 'ajax', |
||||||
|
'data-size' => 'md', |
||||||
|
'data-title' => get_plugin_lang('plugin_title', self::class), |
||||||
|
] |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
public function install() |
||||||
|
{ |
||||||
|
$this->createExtraField(); |
||||||
|
} |
||||||
|
|
||||||
|
public function uninstall() |
||||||
|
{ |
||||||
|
$this->removeExtraField(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return Plugin |
||||||
|
*/ |
||||||
|
public function performActionsAfterConfigure() |
||||||
|
{ |
||||||
|
return $this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param int $formula |
||||||
|
* @param Exercise $exercise |
||||||
|
*/ |
||||||
|
public function saveFormulaForExercise($formula, Exercise $exercise) |
||||||
|
{ |
||||||
|
$this->recalculateQuestionScore($formula, $exercise); |
||||||
|
|
||||||
|
$extraFieldValue = new ExtraFieldValue('quiz'); |
||||||
|
$extraFieldValue->save( |
||||||
|
[ |
||||||
|
'item_id' => $exercise->iId, |
||||||
|
'variable' => self::EXTRAFIELD_FORMULA, |
||||||
|
'value' => $formula, |
||||||
|
] |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param int $exerciseId |
||||||
|
* |
||||||
|
* @return int |
||||||
|
*/ |
||||||
|
public function getFormulaForExercise($exerciseId) |
||||||
|
{ |
||||||
|
$extraFieldValue = new ExtraFieldValue('quiz'); |
||||||
|
$value = $extraFieldValue->get_values_by_handler_and_field_variable( |
||||||
|
$exerciseId, |
||||||
|
self::EXTRAFIELD_FORMULA |
||||||
|
); |
||||||
|
|
||||||
|
if (empty($value)) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
return (int) $value['value']; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return int |
||||||
|
*/ |
||||||
|
public function getMaxScore() |
||||||
|
{ |
||||||
|
$max = $this->get(self::SETTING_MAX_SCORE); |
||||||
|
|
||||||
|
if (!empty($max)) { |
||||||
|
return (int) $max; |
||||||
|
} |
||||||
|
|
||||||
|
return 10; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param int $trackId |
||||||
|
* @param int $formula |
||||||
|
* |
||||||
|
* @throws \Doctrine\ORM\ORMException |
||||||
|
* @throws \Doctrine\ORM\OptimisticLockException |
||||||
|
* @throws \Doctrine\ORM\TransactionRequiredException |
||||||
|
* |
||||||
|
* @return float|int |
||||||
|
*/ |
||||||
|
public function getResultWithFormula($trackId, $formula) |
||||||
|
{ |
||||||
|
$em = Database::getManager(); |
||||||
|
|
||||||
|
$eTrack = $em->find('ChamiloCoreBundle:TrackEExercises', $trackId); |
||||||
|
|
||||||
|
$qTracks = $em |
||||||
|
->createQuery( |
||||||
|
'SELECT a FROM ChamiloCoreBundle:TrackEAttempt a |
||||||
|
WHERE a.exeId = :id AND a.userId = :user AND a.cId = :course AND a.sessionId = :session' |
||||||
|
) |
||||||
|
->setParameters( |
||||||
|
[ |
||||||
|
'id' => $eTrack->getExeId(), |
||||||
|
'course' => $eTrack->getCId(), |
||||||
|
'session' => $eTrack->getSessionId(), |
||||||
|
'user' => $eTrack->getExeUserId(), |
||||||
|
] |
||||||
|
) |
||||||
|
->getResult(); |
||||||
|
|
||||||
|
$counts = ['correct' => 0, 'incorrect' => 0]; |
||||||
|
|
||||||
|
/** @var TrackEAttempt $qTrack */ |
||||||
|
foreach ($qTracks as $qTrack) { |
||||||
|
if ($qTrack->getMarks() > 0) { |
||||||
|
$counts['correct']++; |
||||||
|
} elseif ($qTrack->getMarks() < 0) { |
||||||
|
$counts['incorrect']++; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
switch ($formula) { |
||||||
|
case 1: |
||||||
|
$result = $counts['correct'] - $counts['incorrect']; |
||||||
|
break; |
||||||
|
case 2: |
||||||
|
$result = $counts['correct'] - $counts['incorrect'] / 2; |
||||||
|
break; |
||||||
|
case 3: |
||||||
|
$result = $counts['correct'] - $counts['incorrect'] / 3; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
$score = ($result / count($qTracks)) * $this->getMaxScore(); |
||||||
|
|
||||||
|
return $score >= 0 ? $score : 0; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param int $formula |
||||||
|
* @param Exercise $exercise |
||||||
|
*/ |
||||||
|
private function recalculateQuestionScore($formula, Exercise $exercise) |
||||||
|
{ |
||||||
|
$tblQuestion = Database::get_course_table(TABLE_QUIZ_QUESTION); |
||||||
|
$tblAnswer = Database::get_course_table(TABLE_QUIZ_ANSWER); |
||||||
|
|
||||||
|
foreach ($exercise->questionList as $questionId) { |
||||||
|
$question = Question::read($questionId, $exercise->course, false); |
||||||
|
if (!in_array($question->selectType(), [UNIQUE_ANSWER, MULTIPLE_ANSWER])) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
$questionAnswers = new Answer($questionId, $exercise->course_id, $exercise); |
||||||
|
$counts = array_count_values($questionAnswers->correct); |
||||||
|
|
||||||
|
$questionPonderation = 0; |
||||||
|
|
||||||
|
foreach ($questionAnswers->correct as $i => $isCorrect) { |
||||||
|
if (!isset($questionAnswers->iid[$i])) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
$iid = $questionAnswers->iid[$i]; |
||||||
|
|
||||||
|
if ($question->selectType() == MULTIPLE_ANSWER || 0 === $formula) { |
||||||
|
$ponderation = 1 == $isCorrect ? 1 / $counts[1] : -1 / $counts[0]; |
||||||
|
} else { |
||||||
|
$ponderation = 1 == $isCorrect ? 1 : -1 / $formula; |
||||||
|
} |
||||||
|
|
||||||
|
if ($ponderation > 0) { |
||||||
|
$questionPonderation += $ponderation; |
||||||
|
} |
||||||
|
|
||||||
|
//error_log("question: $questionId -- i: $i -- w: $ponderation"); |
||||||
|
Database::query("UPDATE $tblAnswer SET ponderation = $ponderation WHERE iid = $iid"); |
||||||
|
} |
||||||
|
|
||||||
|
Database::query("UPDATE $tblQuestion SET ponderation = $questionPonderation WHERE iid = {$question->iid}"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates an extrafield. |
||||||
|
*/ |
||||||
|
private function createExtraField() |
||||||
|
{ |
||||||
|
$extraField = new ExtraField('quiz'); |
||||||
|
|
||||||
|
if (false === $extraField->get_handler_field_info_by_field_variable(self::EXTRAFIELD_FORMULA)) { |
||||||
|
$extraField |
||||||
|
->save( |
||||||
|
[ |
||||||
|
'variable' => self::EXTRAFIELD_FORMULA, |
||||||
|
'field_type' => ExtraField::FIELD_TYPE_TEXT, |
||||||
|
'display_text' => $this->get_lang('EvaluationFormula'), |
||||||
|
'visible_to_self' => false, |
||||||
|
'changeable' => false, |
||||||
|
] |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Removes the extrafield . |
||||||
|
*/ |
||||||
|
private function removeExtraField() |
||||||
|
{ |
||||||
|
$extraField = new ExtraField('quiz'); |
||||||
|
$value = $extraField->get_handler_field_info_by_field_variable(self::EXTRAFIELD_FORMULA); |
||||||
|
|
||||||
|
if (false !== $value) { |
||||||
|
$extraField->delete($value['id']); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,20 @@ |
|||||||
|
# Questions Options Evaluation |
||||||
|
|
||||||
|
Allow recalulate the options score in questions: |
||||||
|
|
||||||
|
* Successes - Failures |
||||||
|
* Successes - Failures / 2 |
||||||
|
* Successes - Failures / 3 |
||||||
|
* Recalculate question scores |
||||||
|
|
||||||
|
**Setup instructions** |
||||||
|
|
||||||
|
- Install plugin |
||||||
|
- Set enabled in configuration |
||||||
|
- Edit `configuration.php` file |
||||||
|
```php |
||||||
|
$_configuration['exercise_additional_teacher_modify_actions'] = [ |
||||||
|
// ... |
||||||
|
'questionoptionsevaluation' => ['QuestionOptionsEvaluationPlugin', 'filterModify'] |
||||||
|
]; |
||||||
|
``` |
||||||
@ -0,0 +1,74 @@ |
|||||||
|
<?php |
||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
require_once __DIR__.'/../../main/inc/global.inc.php'; |
||||||
|
|
||||||
|
api_protect_teacher_script(); |
||||||
|
api_protect_course_script(); |
||||||
|
|
||||||
|
$exerciseId = isset($_REQUEST['exercise']) ? (int) $_REQUEST['exercise'] : 0; |
||||||
|
|
||||||
|
if (empty($exerciseId)) { |
||||||
|
echo Display::return_message(get_lang('NotAllowed'), 'error'); |
||||||
|
|
||||||
|
exit; |
||||||
|
} |
||||||
|
|
||||||
|
$exercise = new Exercise(); |
||||||
|
|
||||||
|
if (!$exercise->read($exerciseId, false)) { |
||||||
|
echo Display::return_message(get_lang('ExerciseNotFound'), 'error'); |
||||||
|
|
||||||
|
exit; |
||||||
|
} |
||||||
|
|
||||||
|
$plugin = QuestionOptionsEvaluationPlugin::create(); |
||||||
|
|
||||||
|
if ($plugin->get('enable') !== 'true') { |
||||||
|
echo Display::return_message(get_lang('NotAllowed'), 'error'); |
||||||
|
|
||||||
|
exit; |
||||||
|
} |
||||||
|
|
||||||
|
$formEvaluation = new FormValidator('evaluation'); |
||||||
|
$formEvaluation |
||||||
|
->addRadio( |
||||||
|
'formula', |
||||||
|
$plugin->get_lang('EvaluationFormula'), |
||||||
|
[ |
||||||
|
-1 => $plugin->get_lang('NoFormula'), |
||||||
|
0 => $plugin->get_lang('RecalculateQuestionScores'), |
||||||
|
1 => $plugin->get_lang('Formula1'), |
||||||
|
2 => $plugin->get_lang('Formula2'), |
||||||
|
3 => $plugin->get_lang('Formula3'), |
||||||
|
] |
||||||
|
) |
||||||
|
->setColumnsSize([4, 7, 1]); |
||||||
|
$formEvaluation->addButtonSave(get_lang('Save'))->setColumnsSize([4, 7, 1]); |
||||||
|
$formEvaluation->addHidden('exercise', $exerciseId); |
||||||
|
|
||||||
|
if ($formEvaluation->validate()) { |
||||||
|
$exercise->read($exerciseId, true); |
||||||
|
$values = $formEvaluation->exportValues(); |
||||||
|
$formula = isset($values['formula']) ? (int) $values['formula'] : 0; |
||||||
|
$plugin->saveFormulaForExercise($formula, $exercise); |
||||||
|
Display::addFlash( |
||||||
|
Display::return_message( |
||||||
|
sprintf($plugin->get_lang('FormulaSavedForExerciseX'), $exercise->selectTitle(true)), |
||||||
|
'success' |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
header( |
||||||
|
'Location: '.api_get_path(WEB_CODE_PATH).'exercise/exercise.php?'.api_get_cidreq()."&exerciseId=$exerciseId" |
||||||
|
); |
||||||
|
exit; |
||||||
|
} |
||||||
|
|
||||||
|
$formEvaluation->setDefaults(['formula' => $plugin->getFormulaForExercise($exercise->iId)]); |
||||||
|
|
||||||
|
echo Display::return_message( |
||||||
|
$plugin->get_lang('QuizQuestionsScoreRulesTitleConfirm'), |
||||||
|
'warning' |
||||||
|
); |
||||||
|
$formEvaluation->display(); |
||||||
@ -0,0 +1,4 @@ |
|||||||
|
<?php |
||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
QuestionOptionsEvaluationPlugin::create()->install(); |
||||||
@ -0,0 +1,19 @@ |
|||||||
|
<?php |
||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
$strings['plugin_title'] = 'Question options evaluation'; |
||||||
|
$strings['plugin_comment'] = 'Allow recalulate the options score in questions'; |
||||||
|
|
||||||
|
$strings['enable'] = 'Enable'; |
||||||
|
$strings['exercise_max_score'] = 'Max score in exercises'; |
||||||
|
$strings['exercise_max_score_help'] = 'Default is 10.'; |
||||||
|
|
||||||
|
$strings['QuizQuestionsScoreRulesTitleConfirm'] = 'Changing the evaluation formula generates changes for each question in the exercise and prevents undoing this change below. Are you sure you want to proceed?'; |
||||||
|
$strings['EvaluationFormula'] = 'Evaluation formula'; |
||||||
|
$strings['NoFormula'] = 'No formula'; |
||||||
|
$strings['Formula1'] = 'Successes - Failures'; |
||||||
|
$strings['Formula2'] = 'Successes - Failures / 2'; |
||||||
|
$strings['Formula3'] = 'Successes - Failures / 3'; |
||||||
|
$strings['QuestionsEvaluated'] = 'Questions evaluated'; |
||||||
|
$strings['RecalculateQuestionScores'] = 'Recalculate question scores'; |
||||||
|
$strings['FormulaSavedForExerciseX'] = 'Formula saved for exercise "%s".'; |
||||||
@ -0,0 +1,19 @@ |
|||||||
|
<?php |
||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
$strings['plugin_title'] = 'Evaluación para opciones de pregunta'; |
||||||
|
$strings['plugin_comment'] = 'Permite recalcular los puntajes de opciones en preguntas'; |
||||||
|
|
||||||
|
$strings['enable'] = 'Habilitar'; |
||||||
|
$strings['exercise_max_score'] = 'Puntuación máxima por ejercicio'; |
||||||
|
$strings['exercise_max_score_help'] = 'Por defecto es 10.'; |
||||||
|
|
||||||
|
$strings['QuizQuestionsScoreRulesTitleConfirm'] = 'Cambiar la fórmula de evaluación genera cambios para cada pregunta del ejercicio e impide deshacer este cambio a continuación. ¿Está seguro que desea proceder?'; |
||||||
|
$strings['EvaluationFormula'] = 'Fórmula de evaluación'; |
||||||
|
$strings['NoFormula'] = 'Sin formula'; |
||||||
|
$strings['Formula1'] = 'Aciertos - Fallos'; |
||||||
|
$strings['Formula2'] = 'Aciertos - Fallos / 2'; |
||||||
|
$strings['Formula3'] = 'Aciertos - Fallos / 3'; |
||||||
|
$strings['QuestionsEvaluated'] = 'Preguntas evaluadas'; |
||||||
|
$strings['RecalculateQuestionScores'] = 'Recalcular puntuaciones de preguntas'; |
||||||
|
$strings['FormulaSavedForExerciseX'] = 'Formula guardada para el ejercicio "%s".'; |
||||||
@ -0,0 +1,4 @@ |
|||||||
|
<?php |
||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
$plugin_info = QuestionOptionsEvaluationPlugin::create()->get_info(); |
||||||
@ -0,0 +1,4 @@ |
|||||||
|
<?php |
||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
QuestionOptionsEvaluationPlugin::create()->uninstall(); |
||||||
@ -0,0 +1,42 @@ |
|||||||
|
# Survey Export CSV |
||||||
|
|
||||||
|
Exports survey results to a CSV file with a very specific format. |
||||||
|
|
||||||
|
This plugin will add a new action button in the surveys list, allowing the |
||||||
|
teacher to export the survey in a CSV format meant at exchanging with external |
||||||
|
analysis tools. |
||||||
|
|
||||||
|
The CSV format looks this way: |
||||||
|
|
||||||
|
``` |
||||||
|
DATID;P01;P02;P03;P04;P05;P06;P07;P08;DATOBS |
||||||
|
1;"1";"2";"26";"10";"2";"2";"2";"4";"2" |
||||||
|
2;"1";"2";"32";"10";"6";"4";"4";"5";"2" |
||||||
|
3;"2";"3";"27";"8";"5";"5";"2";"5";"1" |
||||||
|
4;"1";"3";"33";"11";"1";"4";"1";"6";"1" |
||||||
|
``` |
||||||
|
|
||||||
|
Where: |
||||||
|
- DATID represents a sequential ID for the participants (not related to |
||||||
|
their internal user ID) |
||||||
|
- P01,P02,... represent the sequential ID of each question inside the survey |
||||||
|
- DATOBS represents the free answer of the user to an open remarks form at |
||||||
|
the end of the survey |
||||||
|
|
||||||
|
**Setup instructions** |
||||||
|
|
||||||
|
- Install plugin |
||||||
|
- Set enabled in configuration |
||||||
|
- Edit `configuration.php` file |
||||||
|
```php |
||||||
|
$_configuration['survey_additional_teacher_modify_actions'] = [ |
||||||
|
// ... |
||||||
|
'SurveyExportCSVPlugin' => ['SurveyExportCsvPlugin', 'filterModify'], |
||||||
|
]; |
||||||
|
``` |
||||||
|
If you have large surveys with large numbers of users answering them, you |
||||||
|
might want to ensure your c_survey_answer table is properly indexed. If not, |
||||||
|
use the following SQL statement to modify that: |
||||||
|
```sql |
||||||
|
alter table c_survey_answer add index idx_c_survey_answerucsq (user, c_id, survey_id, question_id); |
||||||
|
``` |
||||||
@ -0,0 +1,95 @@ |
|||||||
|
<?php |
||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
/** |
||||||
|
* Class SurveyExportCsvPlugin. |
||||||
|
*/ |
||||||
|
class SurveyExportCsvPlugin extends Plugin |
||||||
|
{ |
||||||
|
/** |
||||||
|
* SurveyExportCsvPlugin constructor. |
||||||
|
*/ |
||||||
|
protected function __construct() |
||||||
|
{ |
||||||
|
$settings = [ |
||||||
|
'enabled' => 'boolean', |
||||||
|
'export_incomplete' => 'boolean', |
||||||
|
]; |
||||||
|
|
||||||
|
parent::__construct('0.1', 'Angel Fernando Quiroz Campos', $settings); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return SurveyExportCsvPlugin|null |
||||||
|
*/ |
||||||
|
public static function create() |
||||||
|
{ |
||||||
|
static $result = null; |
||||||
|
|
||||||
|
return $result ? $result : $result = new self(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Installation process. |
||||||
|
*/ |
||||||
|
public function install() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Uninstallation process. |
||||||
|
*/ |
||||||
|
public function uninstall() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param array $params |
||||||
|
* |
||||||
|
* @return string |
||||||
|
*/ |
||||||
|
public static function filterModify($params) |
||||||
|
{ |
||||||
|
$enabled = api_get_plugin_setting('surveyexportcsv', 'enabled'); |
||||||
|
|
||||||
|
if ($enabled !== 'true') { |
||||||
|
return ''; |
||||||
|
} |
||||||
|
|
||||||
|
$surveyId = isset($params['survey_id']) ? (int) $params['survey_id'] : 0; |
||||||
|
$iconSize = isset($params['icon_size']) ? $params['icon_size'] : ICON_SIZE_SMALL; |
||||||
|
|
||||||
|
if (empty($surveyId)) { |
||||||
|
return ''; |
||||||
|
} |
||||||
|
|
||||||
|
return Display::url( |
||||||
|
Display::return_icon('export_csv.png', get_lang('ExportAsCSV'), [], $iconSize), |
||||||
|
api_get_path(WEB_PLUGIN_PATH).'surveyexportcsv/export.php?survey='.$surveyId.'&'.api_get_cidreq() |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Create tools for all courses. |
||||||
|
*/ |
||||||
|
private function createLinkToCourseTools() |
||||||
|
{ |
||||||
|
$result = Database::getManager() |
||||||
|
->createQuery('SELECT c.id FROM ChamiloCoreBundle:Course c') |
||||||
|
->getResult(); |
||||||
|
|
||||||
|
foreach ($result as $item) { |
||||||
|
$this->createLinkToCourseTool($this->get_name().':teacher', $item['id'], 'survey.png'); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Remove all course tools created by plugin. |
||||||
|
*/ |
||||||
|
private function removeLinkToCourseTools() |
||||||
|
{ |
||||||
|
Database::getManager() |
||||||
|
->createQuery('DELETE FROM ChamiloCourseBundle:CTool t WHERE t.link LIKE :link AND t.category = :category') |
||||||
|
->execute(['link' => 'surveyexportcsv/start.php%', 'category' => 'plugin']); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,215 @@ |
|||||||
|
<?php |
||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
use Chamilo\CourseBundle\Entity\CSurveyAnswer; |
||||||
|
use Chamilo\CourseBundle\Entity\CSurveyQuestionOption; |
||||||
|
|
||||||
|
require_once __DIR__.'/../../main/inc/global.inc.php'; |
||||||
|
|
||||||
|
api_protect_course_script(true); |
||||||
|
api_protect_teacher_script(); |
||||||
|
|
||||||
|
$surveyId = isset($_GET['survey']) ? (int) $_GET['survey'] : 0; |
||||||
|
$surveyData = SurveyManager::get_survey($surveyId); |
||||||
|
$courseId = api_get_course_int_id(); |
||||||
|
|
||||||
|
if (empty($surveyData)) { |
||||||
|
api_not_allowed(true); |
||||||
|
} |
||||||
|
|
||||||
|
$plugin = SurveyExportCsvPlugin::create(); |
||||||
|
$allowExportIncomplete = 'true' === $plugin->get('export_incomplete'); |
||||||
|
|
||||||
|
if ($plugin->get('enabled') !== 'true') { |
||||||
|
api_not_allowed(true); |
||||||
|
} |
||||||
|
|
||||||
|
$questionsData = SurveyManager::get_questions($surveyId, $courseId); |
||||||
|
// Sort questions by their "sort" field |
||||||
|
$questionsData = array_filter( |
||||||
|
$questionsData, |
||||||
|
function ($questionData) { |
||||||
|
return in_array($questionData['type'], ['yesno', 'multiplechoice', 'open']); |
||||||
|
} |
||||||
|
); |
||||||
|
$numberOfQuestions = count($questionsData); |
||||||
|
|
||||||
|
usort( |
||||||
|
$questionsData, |
||||||
|
function ($qL, $qR) { |
||||||
|
if ($qL['sort'] == $qR['sort']) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
return $qL['sort'] < $qR['sort'] ? -1 : 1; |
||||||
|
} |
||||||
|
); |
||||||
|
|
||||||
|
$content = []; |
||||||
|
$content[] = firstRow($questionsData); |
||||||
|
|
||||||
|
$surveyAnswers = getSurveyAnswers($courseId, $surveyId); |
||||||
|
|
||||||
|
// Process answers |
||||||
|
$i = 1; |
||||||
|
foreach ($surveyAnswers as $answer) { |
||||||
|
$row = otherRow($questionsData, $answer['user'], $courseId); |
||||||
|
|
||||||
|
if (!$allowExportIncomplete && count($row) < $numberOfQuestions) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
array_unshift($row, $i); |
||||||
|
|
||||||
|
$content[] = $row; |
||||||
|
$i++; |
||||||
|
} |
||||||
|
|
||||||
|
// Generate file |
||||||
|
$fileName = md5($surveyId.time()); |
||||||
|
|
||||||
|
Export::arrayToCsv($content, $fileName, false, "'"); |
||||||
|
|
||||||
|
/** |
||||||
|
* Generate the first row for file. |
||||||
|
* |
||||||
|
* @param $questions |
||||||
|
* |
||||||
|
* @return array |
||||||
|
*/ |
||||||
|
function firstRow($questions) |
||||||
|
{ |
||||||
|
array_pop($questions); |
||||||
|
$positions = array_keys($questions); |
||||||
|
|
||||||
|
$row = ['DATID']; |
||||||
|
|
||||||
|
foreach ($positions as $position) { |
||||||
|
$row[] = sprintf("P%02d", $position + 1); |
||||||
|
} |
||||||
|
|
||||||
|
$row[] = 'DATOBS'; |
||||||
|
|
||||||
|
return $row; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get unique answer for surveys by users. |
||||||
|
* |
||||||
|
* @param int $courseId |
||||||
|
* @param int $surveyId |
||||||
|
* |
||||||
|
* @return array |
||||||
|
*/ |
||||||
|
function getSurveyAnswers($courseId, $surveyId) |
||||||
|
{ |
||||||
|
$surveyAnswers = Database::getManager() |
||||||
|
->createQuery( |
||||||
|
'SELECT sa.user, MIN(sa.iid) AS id FROM ChamiloCourseBundle:CSurveyAnswer sa |
||||||
|
WHERE sa.cId = :course AND sa.surveyId = :survey |
||||||
|
GROUP BY sa.user ORDER BY id ASC' |
||||||
|
) |
||||||
|
->setParameters(['course' => $courseId, 'survey' => $surveyId]) |
||||||
|
->getResult(); |
||||||
|
|
||||||
|
return $surveyAnswers; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param string $user |
||||||
|
* @param int $courseId |
||||||
|
* @param int $surveyId |
||||||
|
* @param int $questionId |
||||||
|
* |
||||||
|
* @return array |
||||||
|
*/ |
||||||
|
function getQuestionOptions($user, $courseId, $surveyId, $questionId) |
||||||
|
{ |
||||||
|
$options = Database::getManager() |
||||||
|
->createQuery( |
||||||
|
'SELECT sqo FROM ChamiloCourseBundle:CSurveyQuestionOption sqo |
||||||
|
INNER JOIN ChamiloCourseBundle:CSurveyAnswer sa |
||||||
|
WITH |
||||||
|
sqo.cId = sa.cId |
||||||
|
AND sqo.questionId = sa.questionId |
||||||
|
AND sqo.surveyId = sa.surveyId |
||||||
|
AND sqo.iid = sa.optionId |
||||||
|
WHERE sa.user = :user AND sa.cId = :course AND sa.surveyId = :survey AND sa.questionId = :question' |
||||||
|
) |
||||||
|
->setParameters( |
||||||
|
[ |
||||||
|
'user' => $user, |
||||||
|
'course' => $courseId, |
||||||
|
'survey' => $surveyId, |
||||||
|
'question' => $questionId, |
||||||
|
] |
||||||
|
) |
||||||
|
->getResult(); |
||||||
|
|
||||||
|
return $options; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param int $questionId |
||||||
|
* @param int $surveyId |
||||||
|
* @param int $courseId |
||||||
|
* @param string $user |
||||||
|
* |
||||||
|
* @throws \Doctrine\ORM\NonUniqueResultException |
||||||
|
* |
||||||
|
* @return CSurveyAnswer|null |
||||||
|
*/ |
||||||
|
function getOpenAnswer($questionId, $surveyId, $courseId, $user) |
||||||
|
{ |
||||||
|
$answer = Database::getManager() |
||||||
|
->createQuery( |
||||||
|
'SELECT sa FROM ChamiloCourseBundle:CSurveyAnswer sa |
||||||
|
WHERE sa.cId = :course AND sa.surveyId = :survey AND sa.questionId = :question AND sa.user = :user' |
||||||
|
) |
||||||
|
->setParameters(['course' => $courseId, 'survey' => $surveyId, 'question' => $questionId, 'user' => $user]) |
||||||
|
->getOneOrNullResult(); |
||||||
|
|
||||||
|
return $answer; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generate the content rows for file. |
||||||
|
* |
||||||
|
* @param array $questions |
||||||
|
* @param string $user |
||||||
|
* @param int $courseId |
||||||
|
* |
||||||
|
* @throws \Doctrine\ORM\NonUniqueResultException |
||||||
|
* |
||||||
|
* @return array |
||||||
|
*/ |
||||||
|
function otherRow($questions, $user, $courseId) |
||||||
|
{ |
||||||
|
$row = []; |
||||||
|
|
||||||
|
foreach ($questions as $question) { |
||||||
|
if ('open' === $question['type']) { |
||||||
|
$answer = getOpenAnswer($question['question_id'], $question['survey_id'], $courseId, $user); |
||||||
|
|
||||||
|
if ($answer) { |
||||||
|
$row[] = Security::remove_XSS($answer->getOptionId()); |
||||||
|
} |
||||||
|
} else { |
||||||
|
$options = getQuestionOptions( |
||||||
|
$user, |
||||||
|
$courseId, |
||||||
|
$question['survey_id'], |
||||||
|
$question['question_id'] |
||||||
|
); |
||||||
|
/** @var CSurveyQuestionOption|null $option */ |
||||||
|
$option = end($options); |
||||||
|
|
||||||
|
if ($option) { |
||||||
|
$value = $option->getSort(); |
||||||
|
$row[] = '"'.$value.'"'; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return $row; |
||||||
|
} |
||||||
@ -0,0 +1,4 @@ |
|||||||
|
<?php |
||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
SurveyExportCsvPlugin::create()->install(); |
||||||
@ -0,0 +1,9 @@ |
|||||||
|
<?php |
||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
$strings['plugin_title'] = "Survey Export CSV"; |
||||||
|
$strings['plugin_comment'] = "Export surveys results to CSV file"; |
||||||
|
|
||||||
|
$strings['enabled'] = 'Enabled'; |
||||||
|
$strings['export_incomplete'] = 'Export incomplete'; |
||||||
|
$strings['export_incomplete_help'] = 'Allow export incomplete surveys. Disabled by default.'; |
||||||
@ -0,0 +1,9 @@ |
|||||||
|
<?php |
||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
$strings['plugin_title'] = "Survey Export CSV"; |
||||||
|
$strings['plugin_comment'] = "Exportar resultados de encuestas en archivo CSV."; |
||||||
|
|
||||||
|
$strings['enabled'] = 'Habilitado'; |
||||||
|
$strings['export_incomplete'] = 'Exportar incompletos'; |
||||||
|
$strings['export_incomplete_help'] = 'Permitir exportar encuestas incompletas. Deshabilitado por defecto.'; |
||||||
@ -0,0 +1,4 @@ |
|||||||
|
<?php |
||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
$plugin_info = SurveyExportCsvPlugin::create()->get_info(); |
||||||
@ -0,0 +1,50 @@ |
|||||||
|
<?php |
||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
require_once __DIR__.'/../../main/inc/global.inc.php'; |
||||||
|
|
||||||
|
api_protect_course_script(true); |
||||||
|
api_protect_teacher_script(); |
||||||
|
|
||||||
|
$plugin = SurveyExportCsvPlugin::create(); |
||||||
|
|
||||||
|
$courseCode = api_get_course_id(); |
||||||
|
|
||||||
|
// Create a sortable table with survey-data |
||||||
|
$table = new SortableTable( |
||||||
|
'surveys', |
||||||
|
['SurveyUtil', 'get_number_of_surveys'], |
||||||
|
['SurveyUtil', 'get_survey_data'], |
||||||
|
2 |
||||||
|
); |
||||||
|
$table->set_additional_parameters(['cidReq' => $courseCode]); |
||||||
|
$table->set_header(0, '', false); |
||||||
|
$table->setHideColumn(0); |
||||||
|
$table->set_header(1, get_lang('SurveyName')); |
||||||
|
$table->set_header(2, get_lang('SurveyCode'), true, ['class' => 'text-center'], ['class' => 'text-center']); |
||||||
|
$table->set_header(3, get_lang('NumberOfQuestions'), true, ['class' => 'text-right'], ['class' => 'text-right']); |
||||||
|
$table->set_header(4, get_lang('Author')); |
||||||
|
$table->set_header(5, get_lang('AvailableFrom'), true, ['class' => 'text-center'], ['class' => 'text-center']); |
||||||
|
$table->set_header(6, get_lang('AvailableUntil'), true, ['class' => 'text-center'], ['class' => 'text-center']); |
||||||
|
$table->set_header(7, get_lang('Invite'), true, ['class' => 'text-right'], ['class' => 'text-right']); |
||||||
|
$table->set_header(8, get_lang('Anonymous'), true, ['class' => 'text-center'], ['class' => 'text-center']); |
||||||
|
$table->set_column_filter(8, ['SurveyUtil', 'anonymous_filter']); |
||||||
|
|
||||||
|
if (api_get_configuration_value('allow_mandatory_survey')) { |
||||||
|
$table->set_header(9, get_lang('IsMandatory'), true, ['class' => 'text-center'], ['class' => 'text-center']); |
||||||
|
$table->set_header(10, get_lang('Export'), false, ['class' => 'text-center'], ['class' => 'text-center']); |
||||||
|
$table->set_column_filter(10, ['SurveyExportCsvPlugin', 'filterModify']); |
||||||
|
} else { |
||||||
|
$table->set_header(9, get_lang('Export'), false, ['class' => 'text-center'], ['class' => 'text-center']); |
||||||
|
$table->set_column_filter(9, ['SurveyExportCsvPlugin', 'filterModify']); |
||||||
|
} |
||||||
|
|
||||||
|
$pageTitle = $plugin->get_title(); |
||||||
|
|
||||||
|
$template = new Template($pageTitle); |
||||||
|
|
||||||
|
$content = $table->return_table(); |
||||||
|
|
||||||
|
$template->assign('header', $pageTitle); |
||||||
|
$template->assign('content', $content); |
||||||
|
$template->display_one_col_template(); |
||||||
@ -0,0 +1,4 @@ |
|||||||
|
<?php |
||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
SurveyExportCsvPlugin::create()->uninstall(); |
||||||
@ -0,0 +1,10 @@ |
|||||||
|
Speech authentication with Whispeak |
||||||
|
=================================== |
||||||
|
|
||||||
|
Instructions: |
||||||
|
------------- |
||||||
|
|
||||||
|
1. Install plugin in Chamilo. |
||||||
|
2. Set the plugin configuration with the token and API url. And enable the plugin. |
||||||
|
3. Set the `login_bottom` region to the plugin. |
||||||
|
4. Add `$_configuration['whispeak_auth_enabled'] = true;` to `configuration.php` file. |
||||||
@ -0,0 +1,293 @@ |
|||||||
|
<?php |
||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
use Chamilo\CoreBundle\Entity\ExtraField; |
||||||
|
use Chamilo\CoreBundle\Entity\ExtraFieldValues; |
||||||
|
use Chamilo\UserBundle\Entity\User; |
||||||
|
|
||||||
|
/** |
||||||
|
* Class WhispeakAuthPlugin. |
||||||
|
*/ |
||||||
|
class WhispeakAuthPlugin extends Plugin |
||||||
|
{ |
||||||
|
const SETTING_ENABLE = 'enable'; |
||||||
|
const SETTING_API_URL = 'api_url'; |
||||||
|
const SETTING_TOKEN = 'token'; |
||||||
|
const SETTING_INSTRUCTION = 'instruction'; |
||||||
|
|
||||||
|
const EXTRAFIELD_AUTH_UID = 'whispeak_auth_uid'; |
||||||
|
|
||||||
|
/** |
||||||
|
* StudentFollowUpPlugin constructor. |
||||||
|
*/ |
||||||
|
protected function __construct() |
||||||
|
{ |
||||||
|
parent::__construct( |
||||||
|
'0.1', |
||||||
|
'Angel Fernando Quiroz', |
||||||
|
[ |
||||||
|
self::SETTING_ENABLE => 'boolean', |
||||||
|
self::SETTING_API_URL => 'text', |
||||||
|
self::SETTING_TOKEN => 'text', |
||||||
|
self::SETTING_INSTRUCTION => 'html', |
||||||
|
] |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return WhispeakAuthPlugin |
||||||
|
*/ |
||||||
|
public static function create() |
||||||
|
{ |
||||||
|
static $result = null; |
||||||
|
|
||||||
|
return $result ? $result : $result = new self(); |
||||||
|
} |
||||||
|
|
||||||
|
public function install() |
||||||
|
{ |
||||||
|
UserManager::create_extra_field( |
||||||
|
self::EXTRAFIELD_AUTH_UID, |
||||||
|
\ExtraField::FIELD_TYPE_TEXT, |
||||||
|
$this->get_lang('Whispeak uid'), |
||||||
|
'' |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
public function uninstall() |
||||||
|
{ |
||||||
|
$extraField = self::getAuthUidExtraField(); |
||||||
|
|
||||||
|
if (empty($extraField)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
$em = Database::getManager(); |
||||||
|
|
||||||
|
$em->createQuery('DELETE FROM ChamiloCoreBundle:ExtraFieldValues efv WHERE efv.field = :field') |
||||||
|
->execute(['field' => $extraField]); |
||||||
|
|
||||||
|
$em->remove($extraField); |
||||||
|
$em->flush(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return ExtraField |
||||||
|
*/ |
||||||
|
public static function getAuthUidExtraField() |
||||||
|
{ |
||||||
|
$em = Database::getManager(); |
||||||
|
$efRepo = $em->getRepository('ChamiloCoreBundle:ExtraField'); |
||||||
|
|
||||||
|
/** @var ExtraField $extraField */ |
||||||
|
$extraField = $efRepo->findOneBy( |
||||||
|
[ |
||||||
|
'variable' => self::EXTRAFIELD_AUTH_UID, |
||||||
|
'extraFieldType' => ExtraField::USER_FIELD_TYPE, |
||||||
|
] |
||||||
|
); |
||||||
|
|
||||||
|
return $extraField; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param int $userId |
||||||
|
* |
||||||
|
* @return ExtraFieldValues |
||||||
|
*/ |
||||||
|
public static function getAuthUidValue($userId) |
||||||
|
{ |
||||||
|
$extraField = self::getAuthUidExtraField(); |
||||||
|
$em = Database::getManager(); |
||||||
|
$efvRepo = $em->getRepository('ChamiloCoreBundle:ExtraFieldValues'); |
||||||
|
|
||||||
|
/** @var ExtraFieldValues $value */ |
||||||
|
$value = $efvRepo->findOneBy(['field' => $extraField, 'itemId' => $userId]); |
||||||
|
|
||||||
|
return $value; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param int $userId |
||||||
|
* |
||||||
|
* @return bool |
||||||
|
*/ |
||||||
|
public static function checkUserIsEnrolled($userId) |
||||||
|
{ |
||||||
|
$value = self::getAuthUidValue($userId); |
||||||
|
|
||||||
|
if (empty($value)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
return !empty($value->getValue()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return string |
||||||
|
*/ |
||||||
|
public static function getEnrollmentUrl() |
||||||
|
{ |
||||||
|
return api_get_path(WEB_PLUGIN_PATH).'whispeakauth/enrollment.php'; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param User $user |
||||||
|
* @param string $filePath |
||||||
|
* |
||||||
|
* @return array |
||||||
|
*/ |
||||||
|
public function requestEnrollment(User $user, $filePath) |
||||||
|
{ |
||||||
|
$metadata = [ |
||||||
|
'motherTongue' => $user->getLanguage(), |
||||||
|
'spokenTongue' => $user->getLanguage(), |
||||||
|
'audioType' => 'pcm', |
||||||
|
]; |
||||||
|
|
||||||
|
return $this->sendRequest( |
||||||
|
'enrollment', |
||||||
|
$metadata, |
||||||
|
$user, |
||||||
|
$filePath |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param User $user |
||||||
|
* @param string $uid |
||||||
|
* |
||||||
|
* @throws \Doctrine\ORM\OptimisticLockException |
||||||
|
*/ |
||||||
|
public function saveEnrollment(User $user, $uid) |
||||||
|
{ |
||||||
|
$em = Database::getManager(); |
||||||
|
$value = self::getAuthUidValue($user->getId()); |
||||||
|
|
||||||
|
if (empty($value)) { |
||||||
|
$ef = self::getAuthUidExtraField(); |
||||||
|
$now = new DateTime('now', new DateTimeZone('UTC')); |
||||||
|
|
||||||
|
$value = new ExtraFieldValues(); |
||||||
|
$value |
||||||
|
->setField($ef) |
||||||
|
->setItemId($user->getId()) |
||||||
|
->setUpdatedAt($now); |
||||||
|
} |
||||||
|
|
||||||
|
$value->setValue($uid); |
||||||
|
|
||||||
|
$em->persist($value); |
||||||
|
$em->flush(); |
||||||
|
} |
||||||
|
|
||||||
|
public function requestAuthentify(User $user, $filePath) |
||||||
|
{ |
||||||
|
$value = self::getAuthUidValue($user->getId()); |
||||||
|
|
||||||
|
if (empty($value)) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
$metadata = [ |
||||||
|
'uid' => $value->getValue(), |
||||||
|
'audioType' => 'pcm', |
||||||
|
]; |
||||||
|
|
||||||
|
return $this->sendRequest( |
||||||
|
'authentify', |
||||||
|
$metadata, |
||||||
|
$user, |
||||||
|
$filePath |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return string |
||||||
|
*/ |
||||||
|
public function getAuthentifySampleText() |
||||||
|
{ |
||||||
|
$phrases = []; |
||||||
|
|
||||||
|
for ($i = 1; $i <= 6; $i++) { |
||||||
|
$phrases[] = $this->get_lang("AuthentifySampleText$i"); |
||||||
|
} |
||||||
|
|
||||||
|
$rand = array_rand($phrases, 1); |
||||||
|
|
||||||
|
return $phrases[$rand]; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return bool |
||||||
|
*/ |
||||||
|
public function toolIsEnabled() |
||||||
|
{ |
||||||
|
return 'true' === $this->get(self::SETTING_ENABLE); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Access not allowed when tool is not enabled. |
||||||
|
* |
||||||
|
* @param bool $printHeaders Optional. Print headers. |
||||||
|
*/ |
||||||
|
public function protectTool($printHeaders = true) |
||||||
|
{ |
||||||
|
if ($this->toolIsEnabled()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
api_not_allowed($printHeaders); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return string |
||||||
|
*/ |
||||||
|
private function getApiUrl() |
||||||
|
{ |
||||||
|
$url = $this->get(self::SETTING_API_URL); |
||||||
|
|
||||||
|
return trim($url, " \t\n\r \v/"); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @param string $endPoint |
||||||
|
* @param array $metadata |
||||||
|
* @param User $user |
||||||
|
* @param string $filePath |
||||||
|
* |
||||||
|
* @return array |
||||||
|
*/ |
||||||
|
private function sendRequest($endPoint, array $metadata, User $user, $filePath) |
||||||
|
{ |
||||||
|
$moderator = $user->getCreatorId() ?: $user->getId(); |
||||||
|
$apiUrl = $this->getApiUrl()."/$endPoint"; |
||||||
|
$headers = [ |
||||||
|
//"Content-Type: application/x-www-form-urlencoded", |
||||||
|
"Authorization: Bearer ".$this->get(self::SETTING_TOKEN), |
||||||
|
]; |
||||||
|
$post = [ |
||||||
|
'metadata' => json_encode($metadata), |
||||||
|
'moderator' => "moderator_$moderator", |
||||||
|
'client' => base64_encode($user->getUserId()), |
||||||
|
'voice' => new CURLFile($filePath), |
||||||
|
]; |
||||||
|
|
||||||
|
$ch = curl_init(); |
||||||
|
curl_setopt($ch, CURLOPT_URL, $apiUrl); |
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); |
||||||
|
curl_setopt($ch, CURLOPT_POST, true); |
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $post); |
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); |
||||||
|
$result = curl_exec($ch); |
||||||
|
curl_close($ch); |
||||||
|
|
||||||
|
$result = json_decode($result, true); |
||||||
|
|
||||||
|
if (!empty($result['error'])) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
return json_decode($result, true); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,136 @@ |
|||||||
|
<?php |
||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
use Chamilo\UserBundle\Entity\User; |
||||||
|
use FFMpeg\FFMpeg; |
||||||
|
use FFMpeg\Format\Audio\Wav; |
||||||
|
|
||||||
|
$cidReset = true; |
||||||
|
|
||||||
|
require_once __DIR__.'/../../../main/inc/global.inc.php'; |
||||||
|
|
||||||
|
$action = isset($_POST['action']) ? $_POST['action'] : 'enrollment'; |
||||||
|
$isEnrollment = 'enrollment' === $action; |
||||||
|
$isAuthentify = 'authentify' === $action; |
||||||
|
|
||||||
|
$isAllowed = true; |
||||||
|
|
||||||
|
if ($isEnrollment) { |
||||||
|
api_block_anonymous_users(false); |
||||||
|
|
||||||
|
$isAllowed = !empty($_FILES['audio']); |
||||||
|
} elseif ($isAuthentify) { |
||||||
|
$isAllowed = !empty($_POST['username']) && !empty($_FILES['audio']); |
||||||
|
} |
||||||
|
|
||||||
|
if (!$isAllowed) { |
||||||
|
echo Display::return_message(get_lang('NotAllowed'), 'error'); |
||||||
|
|
||||||
|
exit; |
||||||
|
} |
||||||
|
|
||||||
|
$plugin = WhispeakAuthPlugin::create(); |
||||||
|
|
||||||
|
$plugin->protectTool(false); |
||||||
|
|
||||||
|
if ($isAuthentify) { |
||||||
|
$em = Database::getManager(); |
||||||
|
/** @var User|null $user */ |
||||||
|
$user = $em->getRepository('ChamiloUserBundle:User')->findOneBy(['username' => $_POST['username']]); |
||||||
|
} else { |
||||||
|
/** @var User $user */ |
||||||
|
$user = api_get_user_entity(api_get_user_id()); |
||||||
|
} |
||||||
|
|
||||||
|
if (empty($user)) { |
||||||
|
echo Display::return_message(get_lang('NoUser'), 'error'); |
||||||
|
|
||||||
|
exit; |
||||||
|
} |
||||||
|
|
||||||
|
$path = api_upload_file('whispeakauth', $_FILES['audio'], $user->getId()); |
||||||
|
|
||||||
|
if (false === $path) { |
||||||
|
echo Display::return_message(get_lang('UploadError'), 'error'); |
||||||
|
|
||||||
|
exit; |
||||||
|
} |
||||||
|
|
||||||
|
$newFullPath = $originFullPath = api_get_path(SYS_UPLOAD_PATH).'whispeakauth'.$path['path_to_save']; |
||||||
|
$fileType = mime_content_type($originFullPath); |
||||||
|
|
||||||
|
if ('wav' !== substr($fileType, -3)) { |
||||||
|
$directory = dirname($originFullPath); |
||||||
|
$newFullPath = $directory.'/audio.wav'; |
||||||
|
|
||||||
|
try { |
||||||
|
$ffmpeg = FFMpeg::create(); |
||||||
|
|
||||||
|
$audio = $ffmpeg->open($originFullPath); |
||||||
|
$audio->save(new Wav(), $newFullPath); |
||||||
|
} catch (Exception $exception) { |
||||||
|
echo Display::return_message($exception->getMessage(), 'error'); |
||||||
|
|
||||||
|
exit; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if ($isEnrollment) { |
||||||
|
$result = $plugin->requestEnrollment($user, $newFullPath); |
||||||
|
|
||||||
|
if (empty($result)) { |
||||||
|
echo Display::return_message($plugin->get_lang('EnrollmentFailed')); |
||||||
|
|
||||||
|
exit; |
||||||
|
} |
||||||
|
|
||||||
|
$reliability = (int) $result['reliability']; |
||||||
|
|
||||||
|
if ($reliability <= 0) { |
||||||
|
echo Display::return_message($plugin->get_lang('EnrollmentSignature0'), 'error'); |
||||||
|
|
||||||
|
exit; |
||||||
|
} |
||||||
|
|
||||||
|
$plugin->saveEnrollment($user, $result['uid']); |
||||||
|
|
||||||
|
$message = '<strong>'.$plugin->get_lang('EnrollmentSuccess').'</strong>'; |
||||||
|
$message .= PHP_EOL; |
||||||
|
$message .= $plugin->get_lang("EnrollmentSignature$reliability"); |
||||||
|
|
||||||
|
echo Display::return_message($message, 'success', false); |
||||||
|
|
||||||
|
exit; |
||||||
|
} |
||||||
|
|
||||||
|
if ($isAuthentify) { |
||||||
|
$result = $plugin->requestAuthentify($user, $newFullPath); |
||||||
|
|
||||||
|
if (empty($result)) { |
||||||
|
echo Display::return_message($plugin->get_lang('AuthentifyFailed'), 'error'); |
||||||
|
|
||||||
|
exit; |
||||||
|
} |
||||||
|
|
||||||
|
$success = (bool) $result['audio'][0]['result']; |
||||||
|
|
||||||
|
if (!$success) { |
||||||
|
echo Display::return_message($plugin->get_lang('TryAgain'), 'warning'); |
||||||
|
|
||||||
|
exit; |
||||||
|
} |
||||||
|
|
||||||
|
$loggedUser = [ |
||||||
|
'user_id' => $user->getId(), |
||||||
|
'status' => $user->getStatus(), |
||||||
|
'uidReset' => true, |
||||||
|
]; |
||||||
|
|
||||||
|
ChamiloSession::write('_user', $loggedUser); |
||||||
|
Login::init_user($user->getId(), true); |
||||||
|
|
||||||
|
echo Display::return_message($plugin->get_lang('AuthentifySuccess'), 'success'); |
||||||
|
echo '<script>window.location.href = "'.api_get_path(WEB_PATH).'";</script>'; |
||||||
|
|
||||||
|
exit; |
||||||
|
} |
||||||
@ -0,0 +1,139 @@ |
|||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
window.RecordAudio = (function () { |
||||||
|
function useRecordRTC(rtcInfo) { |
||||||
|
$(rtcInfo.blockId).show(); |
||||||
|
|
||||||
|
var mediaConstraints = {audio: true}, |
||||||
|
localStream = null, |
||||||
|
recordRTC = null, |
||||||
|
btnStart = $(rtcInfo.btnStartId), |
||||||
|
btnStop = $(rtcInfo.btnStopId), |
||||||
|
btnSave = $(rtcInfo.btnSaveId), |
||||||
|
tagAudio = $(rtcInfo.plyrPreviewId); |
||||||
|
|
||||||
|
function saveAudio() { |
||||||
|
var recordedBlob = recordRTC.getBlob(); |
||||||
|
|
||||||
|
if (!recordedBlob) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
var btnSaveText = btnSave.html(); |
||||||
|
var fileExtension = recordedBlob.type.split('/')[1]; |
||||||
|
|
||||||
|
var formData = new FormData(); |
||||||
|
formData.append('audio', recordedBlob, 'audio.' + fileExtension); |
||||||
|
|
||||||
|
for (var prop in rtcInfo.data) { |
||||||
|
if (!rtcInfo.data.hasOwnProperty(prop)) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
formData.append(prop, rtcInfo.data[prop]); |
||||||
|
} |
||||||
|
|
||||||
|
$.ajax({ |
||||||
|
url: _p.web_plugin + 'whispeakauth/ajax/record_audio.php', |
||||||
|
data: formData, |
||||||
|
processData: false, |
||||||
|
contentType: false, |
||||||
|
type: 'POST', |
||||||
|
beforeSend: function () { |
||||||
|
btnStart.prop('disabled', true); |
||||||
|
btnStop.prop('disabled', true); |
||||||
|
btnSave.prop('disabled', true).text(btnSave.data('loadingtext')); |
||||||
|
} |
||||||
|
}).done(function (response) { |
||||||
|
$('#messages-deck').append(response); |
||||||
|
}).always(function () { |
||||||
|
btnSave.prop('disabled', true).html(btnSaveText).parent().addClass('hidden'); |
||||||
|
btnStop.prop('disabled', true).parent().addClass('hidden'); |
||||||
|
btnStart.prop('disabled', false).parent().removeClass('hidden'); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
btnStart.on('click', function () { |
||||||
|
tagAudio.prop('src', ''); |
||||||
|
|
||||||
|
function successCallback(stream) { |
||||||
|
localStream = stream; |
||||||
|
|
||||||
|
recordRTC = RecordRTC(stream, { |
||||||
|
recorderType: StereoAudioRecorder, |
||||||
|
numberOfAudioChannels: 1, |
||||||
|
type: 'audio' |
||||||
|
}); |
||||||
|
recordRTC.startRecording(); |
||||||
|
|
||||||
|
btnSave.prop('disabled', true).parent().addClass('hidden'); |
||||||
|
btnStop.prop('disabled', false).parent().removeClass('hidden'); |
||||||
|
btnStart.prop('disabled', true).parent().addClass('hidden'); |
||||||
|
tagAudio.removeClass('show').parents('#audio-wrapper').addClass('hidden'); |
||||||
|
} |
||||||
|
|
||||||
|
function errorCallback(error) { |
||||||
|
alert(error.message); |
||||||
|
} |
||||||
|
|
||||||
|
if (navigator.mediaDevices.getUserMedia) { |
||||||
|
navigator.mediaDevices.getUserMedia(mediaConstraints) |
||||||
|
.then(successCallback) |
||||||
|
.catch(errorCallback); |
||||||
|
|
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
navigator.getUserMedia = navigator.getUserMedia || navigator.mozGetUserMedia || navigator.webkitGetUserMedia; |
||||||
|
|
||||||
|
if (navigator.getUserMedia) { |
||||||
|
navigator.getUserMedia(mediaConstraints, successCallback, errorCallback); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
btnStop.on('click', function () { |
||||||
|
if (!recordRTC) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
recordRTC.stopRecording(function (audioURL) { |
||||||
|
btnStart.prop('disabled', false).parent().removeClass('hidden'); |
||||||
|
btnStop.prop('disabled', true).parent().addClass('hidden'); |
||||||
|
btnSave.prop('disabled', false).parent().removeClass('hidden'); |
||||||
|
|
||||||
|
tagAudio |
||||||
|
.prop('src', audioURL) |
||||||
|
.parents('#audio-wrapper') |
||||||
|
.removeClass('hidden') |
||||||
|
.addClass('show'); |
||||||
|
|
||||||
|
localStream.getTracks()[0].stop(); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
btnSave.on('click', function () { |
||||||
|
if (!recordRTC) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
saveAudio(); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
return { |
||||||
|
init: function (rtcInfo) { |
||||||
|
$(rtcInfo.blockId).hide(); |
||||||
|
|
||||||
|
var userMediaEnabled = (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) || |
||||||
|
!!navigator.webkitGetUserMedia || |
||||||
|
!!navigator.mozGetUserMedia || |
||||||
|
!!navigator.getUserMedia; |
||||||
|
|
||||||
|
if (!userMediaEnabled) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
useRecordRTC(rtcInfo); |
||||||
|
} |
||||||
|
} |
||||||
|
})(); |
||||||
@ -0,0 +1,26 @@ |
|||||||
|
<?php |
||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
$cidReset = true; |
||||||
|
|
||||||
|
require_once __DIR__.'/../../main/inc/global.inc.php'; |
||||||
|
|
||||||
|
$plugin = WhispeakAuthPlugin::create(); |
||||||
|
|
||||||
|
$plugin->protectTool(); |
||||||
|
|
||||||
|
$form = new FormValidator('enter_username', 'post', '#'); |
||||||
|
$form->addText('username', get_lang('Username')); |
||||||
|
|
||||||
|
$htmlHeadXtra[] = api_get_js('rtc/RecordRTC.js'); |
||||||
|
$htmlHeadXtra[] = api_get_js_simple(api_get_path(WEB_PLUGIN_PATH).'whispeakauth/assets/js/RecordAudio.js'); |
||||||
|
|
||||||
|
$template = new Template(); |
||||||
|
$template->assign('form', $form->returnForm()); |
||||||
|
$template->assign('sample_text', $plugin->getAuthentifySampleText()); |
||||||
|
|
||||||
|
$content = $template->fetch('whispeakauth/view/authentify_recorder.html.twig'); |
||||||
|
|
||||||
|
$template->assign('header', $plugin->get_title()); |
||||||
|
$template->assign('content', $content); |
||||||
|
$template->display_one_col_template(); |
||||||
@ -0,0 +1,28 @@ |
|||||||
|
<?php |
||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
$cidReset = true; |
||||||
|
|
||||||
|
require_once __DIR__.'/../../main/inc/global.inc.php'; |
||||||
|
|
||||||
|
api_block_anonymous_users(true); |
||||||
|
|
||||||
|
$userId = api_get_user_id(); |
||||||
|
$plugin = WhispeakAuthPlugin::create(); |
||||||
|
|
||||||
|
$plugin->protectTool(); |
||||||
|
|
||||||
|
$sampleText = $plugin->get_lang('EnrollmentSampleText'); |
||||||
|
|
||||||
|
$htmlHeadXtra[] = api_get_js('rtc/RecordRTC.js'); |
||||||
|
$htmlHeadXtra[] = api_get_js_simple(api_get_path(WEB_PLUGIN_PATH).'whispeakauth/assets/js/RecordAudio.js'); |
||||||
|
|
||||||
|
$template = new Template(); |
||||||
|
$template->assign('is_authenticated', WhispeakAuthPlugin::checkUserIsEnrolled($userId)); |
||||||
|
$template->assign('sample_text', $sampleText); |
||||||
|
|
||||||
|
$content = $template->fetch('whispeakauth/view/record_audio.html.twig'); |
||||||
|
|
||||||
|
$template->assign('header', $plugin->get_title()); |
||||||
|
$template->assign('content', $content); |
||||||
|
$template->display_one_col_template(); |
||||||
@ -0,0 +1,14 @@ |
|||||||
|
<?php |
||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
$plugin = WhispeakAuthPlugin::create(); |
||||||
|
|
||||||
|
if ($plugin->toolIsEnabled()) { |
||||||
|
echo Display::toolbarButton( |
||||||
|
$plugin->get_lang('SpeechAuthentication'), |
||||||
|
api_get_path(WEB_PLUGIN_PATH).'whispeakauth/authentify.php', |
||||||
|
'sign-in', |
||||||
|
'info', |
||||||
|
['class' => 'btn-block'] |
||||||
|
); |
||||||
|
} |
||||||
@ -0,0 +1,4 @@ |
|||||||
|
<?php |
||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
WhispeakAuthPlugin::create()->install(); |
||||||
@ -0,0 +1,33 @@ |
|||||||
|
<?php |
||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
$strings['plugin_title'] = 'Speech authentication with Whispeak'; |
||||||
|
$strings['plugin_comment'] = 'Allow speech authentication in Chamilo.'; |
||||||
|
|
||||||
|
$strings['enable'] = 'Enable'; |
||||||
|
$strings['api_url'] = 'API URL'; |
||||||
|
$strings['api_url_help'] = 'http://api.whispeak.io:8080/v1/'; |
||||||
|
$strings['token'] = 'API key'; |
||||||
|
$strings['instruction'] = '<p>Add <code>$_configuration[\'whispeak_auth_enabled\'] = true;</code>'. |
||||||
|
'in the <code>configuration.php</code> file</p>'; |
||||||
|
|
||||||
|
$strings['EnrollmentSampleText'] = 'The famous Mona Lisa painting was painted by Leonardo Da Vinci.'; |
||||||
|
$strings['AuthentifySampleText1'] = 'Dropping Like Flies.'; |
||||||
|
$strings['AuthentifySampleText2'] = 'Keep Your Eyes Peeled.'; |
||||||
|
$strings['AuthentifySampleText3'] = 'The fox screams at midnight.'; |
||||||
|
$strings['AuthentifySampleText4'] = 'Go Out On a Limb.'; |
||||||
|
$strings['AuthentifySampleText5'] = 'Under the Water.'; |
||||||
|
$strings['AuthentifySampleText6'] = 'Barking Up The Wrong Tree.'; |
||||||
|
|
||||||
|
$strings['RepeatThisPhrase'] = 'Repeat this phrase three times after allowing audio recording:'; |
||||||
|
$strings['EnrollmentSignature0'] = 'Unsustainable signature requires a new enrollment.'; |
||||||
|
$strings['EnrollmentSignature1'] = 'Passable signature, advice to make a new enrollment.'; |
||||||
|
$strings['EnrollmentSignature2'] = 'Correct signature.'; |
||||||
|
$strings['EnrollmentSignature3'] = 'Good signature.'; |
||||||
|
$strings['SpeechAuthAlreadyEnrolled'] = 'Speech authentication already enrolled previously.'; |
||||||
|
$strings['SpeechAuthentication'] = 'Speech authentication'; |
||||||
|
$strings['EnrollmentFailed'] = 'Enrollment failed.'; |
||||||
|
$strings['EnrollmentSuccess'] = 'Enrollment success.'; |
||||||
|
$strings['AuthentifyFailed'] = 'Login failed.'; |
||||||
|
$strings['AuthentifySuccess'] = 'Authentication success!'; |
||||||
|
$strings['TryAgain'] = 'Try again'; |
||||||
@ -0,0 +1,18 @@ |
|||||||
|
<?php |
||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
$strings['plugin_title'] = 'Authentification vocale avec Whispeak'; |
||||||
|
|
||||||
|
$strings['EnrollmentSampleText'] = 'Le fameux chef-d\'oeuvre Mona Lisa a été peint par Léonardo da Vinci.'; |
||||||
|
|
||||||
|
$strings['RepeatThisPhrase'] = 'Autorisez l\'enregistrement audio puis répétez cette phrase trois fois:'; |
||||||
|
$strings['EnrollmentSignature0'] = 'Signature non viable, nécessite un nouvel enrôlement'; |
||||||
|
$strings['EnrollmentSignature1'] = 'Signature passable, conseil de faire un nouvel enrôlement.'; |
||||||
|
$strings['EnrollmentSignature2'] = 'Signature correcte.'; |
||||||
|
$strings['EnrollmentSignature3'] = 'Signature bonne.'; |
||||||
|
$strings['SpeechAuthentication'] = 'Authentification de voix'; |
||||||
|
$strings['EnrollmentFailed'] = 'Échec à l\'inscription.'; |
||||||
|
$strings['EnrollmentSuccess'] = 'Inscription réussie.'; |
||||||
|
$strings['AuthentifyFailed'] = 'Échec de l\'authentification.'; |
||||||
|
$strings['AuthentifySuccess'] = 'Authentification réussie!'; |
||||||
|
$strings['TryAgain'] = 'Essayez encore'; |
||||||
@ -0,0 +1,33 @@ |
|||||||
|
<?php |
||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
$strings['plugin_title'] = 'Authenticación de voz con Whispeak'; |
||||||
|
$strings['plugin_comment'] = 'Permitir autenticación de voz en Chamilo.'; |
||||||
|
|
||||||
|
$strings['enable'] = 'Habilitar'; |
||||||
|
$strings['api_url'] = 'URL del API'; |
||||||
|
$strings['api_url_help'] = 'http://api.whispeak.io:8080/v1/'; |
||||||
|
$strings['token'] = 'Llave del API'; |
||||||
|
$strings['instruction'] = '<p>Agrega <code>$_configuration[\'whispeak_auth_enabled\'] = true;</code>'. |
||||||
|
'al archivo <code>configuration.php</code></p>'; |
||||||
|
|
||||||
|
$strings['EnrollmentSampleText'] = 'El famoso cuadro de Mona Lisa fue pintado por Leonardo Da Vinci.'; |
||||||
|
$strings['AuthentifySampleText1'] = 'Cayendo como moscas.'; |
||||||
|
$strings['AuthentifySampleText2'] = 'Mantén tus ojos abiertos.'; |
||||||
|
$strings['AuthentifySampleText3'] = 'El zorro grita a medianoche.'; |
||||||
|
$strings['AuthentifySampleText4'] = 'Ir por las ramas.'; |
||||||
|
$strings['AuthentifySampleText5'] = 'Debajo del agua.'; |
||||||
|
$strings['AuthentifySampleText6'] = 'Ladrando al árbol equivocado.'; |
||||||
|
|
||||||
|
$strings['RepeatThisPhrase'] = 'Repita esta frase tres veces después de permitir la grabación de audio:'; |
||||||
|
$strings['EnrollmentSignature0'] = 'Firma insostenible, requiere una nueva inscripción.'; |
||||||
|
$strings['EnrollmentSignature1'] = 'Firma aceptable, pero se aconseja hacer una nueva inscripción.'; |
||||||
|
$strings['EnrollmentSignature2'] = 'Firma correcta.'; |
||||||
|
$strings['EnrollmentSignature3'] = 'Buena firma.'; |
||||||
|
$strings['SpeechAuthAlreadyEnrolled'] = 'Autenticación de voz registrada anteriormente.'; |
||||||
|
$strings['SpeechAuthentication'] = 'Atenticación con voz'; |
||||||
|
$strings['EnrollmentFailed'] = 'Inscripción fallida.'; |
||||||
|
$strings['EnrollmentSuccess'] = 'Inscripción correcta.'; |
||||||
|
$strings['AuthentifyFailed'] = 'Inicio de sesión fallido.'; |
||||||
|
$strings['AuthentifySuccess'] = '¡Autenticación correcta!'; |
||||||
|
$strings['TryAgain'] = 'Intente de nuevo.'; |
||||||
@ -0,0 +1,4 @@ |
|||||||
|
<?php |
||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
$plugin_info = WhispeakAuthPlugin::create()->get_info(); |
||||||
@ -0,0 +1,4 @@ |
|||||||
|
<?php |
||||||
|
/* For licensing terms, see /license.txt */ |
||||||
|
|
||||||
|
WhispeakAuthPlugin::create()->uninstall(); |
||||||
@ -0,0 +1,38 @@ |
|||||||
|
{% extends 'whispeakauth/view/record_audio.html.twig' %} |
||||||
|
|
||||||
|
{% block intro %} |
||||||
|
<form class="form-horizontal" action="#" method="post"> |
||||||
|
<div class="form-group "> |
||||||
|
<label for="enter_username_username" class="col-sm-4 control-label"> |
||||||
|
{{ 'Username'|get_lang }} |
||||||
|
</label> |
||||||
|
<div class="col-sm-8"> |
||||||
|
<input class="form-control" name="username" type="text" id="username"> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</form> |
||||||
|
|
||||||
|
<hr> |
||||||
|
|
||||||
|
{{ parent() }} |
||||||
|
{% endblock %} |
||||||
|
|
||||||
|
{% block config_data %} |
||||||
|
$('#username').on('change', function () { |
||||||
|
$('#record-audio-recordrtc, #btn-start-record, #btn-stop-record, #btn-save-record').off('click', ''); |
||||||
|
|
||||||
|
RecordAudio.init( |
||||||
|
{ |
||||||
|
blockId: '#record-audio-recordrtc', |
||||||
|
btnStartId: '#btn-start-record', |
||||||
|
btnStopId: '#btn-stop-record', |
||||||
|
btnSaveId: '#btn-save-record', |
||||||
|
plyrPreviewId: '#record-preview', |
||||||
|
data: { |
||||||
|
action: 'authentify', |
||||||
|
username: $('#username').val() |
||||||
|
} |
||||||
|
} |
||||||
|
); |
||||||
|
}); |
||||||
|
{% endblock %} |
||||||
@ -0,0 +1,67 @@ |
|||||||
|
<div id="record-audio-recordrtc" class="row"> |
||||||
|
<div class="col-sm-6"> |
||||||
|
{% block intro %} |
||||||
|
<p class="text-center">{{ 'RepeatThisPhrase'|get_plugin_lang('WhispeakAuthPlugin') }}</p> |
||||||
|
<div class="well well-sm"> |
||||||
|
<div class="row"> |
||||||
|
<div class="col-sm-3 text-center"> |
||||||
|
<span class="fa fa-microphone fa-5x fa-fw" aria-hidden="true"></span> |
||||||
|
<span class="sr-only">{{ 'RecordAudio'|get_lang }}</span> |
||||||
|
</div> |
||||||
|
<div class="col-sm-9 text-center"> |
||||||
|
<p class="lead">{{ sample_text }}</p> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
{% endblock %} |
||||||
|
|
||||||
|
<ul class="list-inline text-center"> |
||||||
|
<li> |
||||||
|
<button class="btn btn-primary" type="button" id="btn-start-record"> |
||||||
|
<span class="fa fa-circle fa-fw" aria-hidden="true"></span> {{ 'StartRecordingAudio'|get_lang }} |
||||||
|
</button> |
||||||
|
</li> |
||||||
|
<li class="hidden"> |
||||||
|
<button class="btn btn-danger" type="button" id="btn-stop-record" disabled> |
||||||
|
<span class="fa fa-square fa-fw" aria-hidden="true"></span> {{ 'StopRecordingAudio'|get_lang }} |
||||||
|
</button> |
||||||
|
</li> |
||||||
|
<li class="hidden"> |
||||||
|
<button class="btn btn-success" type="button" id="btn-save-record" |
||||||
|
data-loadingtext="{{ 'Uploading'|get_lang }}" disabled> |
||||||
|
<span class="fa fa-send fa-fw" aria-hidden="true"></span> {{ 'SaveRecordedAudio'|get_lang }} |
||||||
|
</button> |
||||||
|
</li> |
||||||
|
</ul> |
||||||
|
<p class="hidden" id="audio-wrapper"> |
||||||
|
<audio class="center-block" controls id="record-preview"></audio> |
||||||
|
</p> |
||||||
|
</div> |
||||||
|
<div class="col-sm-5 col-sm-offset-1" id="messages-deck"> |
||||||
|
{% if is_authenticated %} |
||||||
|
<div class="alert alert-info"> |
||||||
|
<span class="fa fa-info-circle" aria-hidden="true"></span> |
||||||
|
<strong>{{ 'SpeechAuthAlreadyEnrolled'|get_plugin_lang('WhispeakAuthPlugin') }}</strong> |
||||||
|
</div> |
||||||
|
{% endif %} |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<script> |
||||||
|
$(function () { |
||||||
|
{% block config_data %} |
||||||
|
RecordAudio.init( |
||||||
|
{ |
||||||
|
blockId: '#record-audio-recordrtc', |
||||||
|
btnStartId: '#btn-start-record', |
||||||
|
btnStopId: '#btn-stop-record', |
||||||
|
btnSaveId: '#btn-save-record', |
||||||
|
plyrPreviewId: '#record-preview', |
||||||
|
data: { |
||||||
|
action: 'enrollment' |
||||||
|
} |
||||||
|
} |
||||||
|
); |
||||||
|
{% endblock %} |
||||||
|
}); |
||||||
|
</script> |
||||||
Loading…
Reference in new issue