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