From 09a171ec1ed05d923f244281d7adc23d0e560a91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ses=C3=B3stris=20Vieira?= Date: Wed, 22 Sep 2021 10:11:31 -0300 Subject: [PATCH] Initial commit --- README.md | 20 +- .../backup_palestra_activity_task.class.php | 54 +++ backup/moodle2/backup_palestra_stepslib.php | 62 +++ classes/event/course_module_viewed.php | 39 ++ classes/task/grade_participation_task.php | 71 ++++ db/access.php | 52 +++ db/install.xml | 42 ++ db/tasks.php | 38 ++ db/upgrade.php | 28 ++ grade.php | 115 ++++++ index.php | 95 +++++ lang/en/palestra.php | 56 +++ lang/pt_br/palestra.php | 56 +++ lib.php | 365 ++++++++++++++++++ mod_form.php | 79 ++++ pix/download.jpeg | Bin 0 -> 6126 bytes pix/icon.png | Bin 0 -> 2896 bytes presence.php | 36 ++ presences.php | 81 ++++ settings.php | 40 ++ version.php | 30 ++ view.php | 150 +++++++ 22 files changed, 1508 insertions(+), 1 deletion(-) create mode 100644 backup/moodle2/backup_palestra_activity_task.class.php create mode 100644 backup/moodle2/backup_palestra_stepslib.php create mode 100644 classes/event/course_module_viewed.php create mode 100644 classes/task/grade_participation_task.php create mode 100644 db/access.php create mode 100644 db/install.xml create mode 100755 db/tasks.php create mode 100644 db/upgrade.php create mode 100644 grade.php create mode 100644 index.php create mode 100644 lang/en/palestra.php create mode 100644 lang/pt_br/palestra.php create mode 100644 lib.php create mode 100644 mod_form.php create mode 100644 pix/download.jpeg create mode 100644 pix/icon.png create mode 100644 presence.php create mode 100644 presences.php create mode 100755 settings.php create mode 100644 version.php create mode 100644 view.php diff --git a/README.md b/README.md index ebb5497..371e522 100644 --- a/README.md +++ b/README.md @@ -1 +1,19 @@ -# moodle-mod_palestra \ No newline at end of file +# Palestra moodle plugin +This module allows creating **Youtube stream palestra** sessions fully integrated in Moodle. Youtube streams can embeded with or without youtube chat. + +The time spended in palestra will be computed as grade in gradebook. + +## Some module configuration options: +- **Youtube stream code**: Set the Youtube stream code be used here. This code + is the final characters in a Youtube URL. + e.g.: To the youtube url https://youtu.be/3ORsUGVNxGs the code is 3ORsUGVNxGs. +- **Include youtube chat**: Indicates whether Youtube chat should be included on + the page. +- **Chat disposition**: Choose how you want to display the Youtube chat. Options + are Side by side or stacked. +- **Start date**: The moment from which attendance must be registered. +- **Duration**: Attendance duration in minutes. +- **Grade after end of palestra**: Continue grading participation even after the + end of palestra transmission. +- **Check interval**: Indicates the time interval (in minutes) that the user's + browser sends a 'sign of life' to the moodle server. \ No newline at end of file diff --git a/backup/moodle2/backup_palestra_activity_task.class.php b/backup/moodle2/backup_palestra_activity_task.class.php new file mode 100644 index 0000000..35257ae --- /dev/null +++ b/backup/moodle2/backup_palestra_activity_task.class.php @@ -0,0 +1,54 @@ +. + +/** + * Defines {@link backup_palestra_activity_task} class + * + * @package mod_palestra + * @category backup + * @copyright 2021 Interlegis (https://www.interlegis.leg.br) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->dirroot . '/mod/palestra/backup/moodle2/backup_palestra_stepslib.php'); + +class backup_palestra_activity_task extends backup_activity_task { + protected function define_my_settings() { + } + + protected function define_my_steps() { + $this->add_step(new backup_palestra_activity_structure_step('palestra_structure', 'palestra.xml')); + } + + static public function encode_content_links($content) { + global $CFG; + + $base = preg_quote($CFG->wwwroot,"/"); + + // Link to the list of palestras + $search = "/(" . $base . "\/mod\/palestra\/index.php\?id\=)([0-9]+)/"; + $content = preg_replace($search, '$@PALESTRAINDEX*$2@$', $content); + + //Link to palestra view by moduleid + $search = "/(" . $base . "\/mod\/palestra\/view.php\?id\=)([0-9]+)/"; + $content= preg_replace($search, '$@PALESTRAVIEWBYID*$2@$', $content); + + return $content; + } +} diff --git a/backup/moodle2/backup_palestra_stepslib.php b/backup/moodle2/backup_palestra_stepslib.php new file mode 100644 index 0000000..c0f2f5e --- /dev/null +++ b/backup/moodle2/backup_palestra_stepslib.php @@ -0,0 +1,62 @@ +. + +/** + * Defines all the backup steps that will be used by {@link backup_palestra_activity_task} + * + * @package mod_palestra + * @category backup + * @copyright 2021 Interlegis (https://www.interlegis.leg.br) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +/** + * Defines the complete palestra structure for backup, with file and id annotations + * + */ +class backup_palestra_activity_structure_step extends backup_activity_structure_step { + + /** + * Defines the structure of the 'palestra' element inside the palestra.xml file + * + * @return backup_nested_element + */ + protected function define_structure() { + $userinfo = $this->get_setting_value('userinfo'); + $palestra = new backup_nested_element('palestra', array('id'), array( + 'name', 'intro', 'introformat', 'youtubecode', 'includechat', + 'grade', 'duration', 'checkinterval', 'timemodified')); + + $presence = new backup_nested_element('palestra_presence', array('id'), + array('palestraid', 'userid', 'starttime', 'lastcheck')); + + $palestra->add_child($presence); + + $palestra->set_source_table('palestra', array('id' => backup::VAR_ACTIVITYID)); + $presence->set_source_sql(" + SELECT * + FROM {palestra_presence} + WHERE palestraid = ?", + array(backup::VAR_PARENTID)); + + $presence->annotate_ids('user', 'userid'); + + return $this->prepare_activity_structure($palestra); + } +} diff --git a/classes/event/course_module_viewed.php b/classes/event/course_module_viewed.php new file mode 100644 index 0000000..733d823 --- /dev/null +++ b/classes/event/course_module_viewed.php @@ -0,0 +1,39 @@ +. + +/** + * The mod_palestra course module viewed event. + * + * @package mod_palestra + * @copyright 2021 Interlegis (https://www.interlegis.leg.br) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_palestra\event; +defined('MOODLE_INTERNAL') || die(); + +class course_module_viewed extends \core\event\course_module_viewed { + protected function init() { + $this->data['crud'] = 'r'; + $this->data['edulevel'] = self::LEVEL_PARTICIPATING; + $this->data['objecttable'] = 'palestra'; + } + + public static function get_objectid_mapping() { + return array('db' => 'palestra', 'restore' => 'palestra'); + } +} + diff --git a/classes/task/grade_participation_task.php b/classes/task/grade_participation_task.php new file mode 100644 index 0000000..6dae565 --- /dev/null +++ b/classes/task/grade_participation_task.php @@ -0,0 +1,71 @@ +. + +/** + * A scheduled task for workshop cron. + * + * @package mod_workshop + * @copyright 2021 Interlegis (https://www.interlegis.leg.br) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +namespace mod_palestra\task; + +use stdClass; + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->libdir.'/gradelib.php'); +require_once($CFG->dirroot.'/mod/palestra/lib.php'); +require_once($CFG->dirroot.'/course/modlib.php'); + +class grade_participation_task extends \core\task\scheduled_task { + public function get_name() { + return get_string('grade_participation_task', 'mod_palestra'); + } + + public function execute() { + global $DB; + + $now = time(); + //#TODO: gradeafter + $palestras = $DB->get_records_sql(" + select p.id, p.course, p.name, p.startdate, p.duration, p.checkinterval + from {palestra} p + inner join {palestra_presence} pp on pp.palestraid = p.id + inner join {course} c on c.id = p.course + where + (p.needsupdate = 1) and + (c.visible = 1) and + ((c.startdate <= ?) and (c.enddate = 0 or c.enddate >= ?)) + and ((p.startdate + (p.duration*60)) < ?) + group by p.id, p.course, p.name, p.startdate, p.duration, p.checkinterval + having max(pp.lastcheck) < ? - p.checkinterval*60 + + ", [$now, $now, $now, $now]); + + mtrace(count($palestras)." palestras to grade..."); + + foreach ($palestras as $palestra) { + mtrace("\tGrading participation on palestra {$palestra->name}..."); + $grading_info = grade_get_grades($palestra->course, 'mod', 'palestra', $palestra->id); + $cm = get_coursemodule_from_instance('palestra', $palestra->id, $palestra->course, false, MUST_EXIST); + $palestra->grade = $grading_info->items[0]->grademax; + $palestra->cmidnumber = $cm->id; + palestra_update_grades($palestra); + mtrace("\tParticipation on palestra {$palestra->name} graded!"); + } + } +} \ No newline at end of file diff --git a/db/access.php b/db/access.php new file mode 100644 index 0000000..6f51f7f --- /dev/null +++ b/db/access.php @@ -0,0 +1,52 @@ +. + +defined('MOODLE_INTERNAL') || die(); + +$capabilities = array( + 'mod/palestra:addinstance' => array( + 'riskbitmask' => RISK_XSS, + 'captype' => 'write', + 'contextlevel' => CONTEXT_COURSE, + 'archetypes' => array( + 'editingteacher' => CAP_ALLOW, + 'manager' => CAP_ALLOW + ), + 'clonepermissionsfrom' => 'moodle/course:manageactivities' + ), + + 'mod/palestra:view' => array( + 'captype' => 'read', + 'contextlevel' => CONTEXT_MODULE, + 'archetypes' => array( + 'student' => CAP_ALLOW, + 'teacher' => CAP_ALLOW, + 'editingteacher' => CAP_ALLOW, + 'manager' => CAP_ALLOW + ) + ), + + 'mod/palestra:viewpresences' => array( + 'captype' => 'read', + 'contextlevel' => CONTEXT_MODULE, + 'archetypes' => array( + 'student' => CAP_PROHIBIT, + 'teacher' => CAP_ALLOW, + 'editingteacher' => CAP_ALLOW, + 'manager' => CAP_ALLOW + ) + ), +); \ No newline at end of file diff --git a/db/install.xml b/db/install.xml new file mode 100644 index 0000000..560ffb7 --- /dev/null +++ b/db/install.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + +
+
+
\ No newline at end of file diff --git a/db/tasks.php b/db/tasks.php new file mode 100755 index 0000000..c34126e --- /dev/null +++ b/db/tasks.php @@ -0,0 +1,38 @@ +. + +/** + * Definition of Forum scheduled tasks. + * + * @package mod_palestra + * @category task + * @copyright 2021 Interlegis (https://www.interlegis.leg.br) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$tasks = array( + array( + 'classname' => 'mod_palestra\task\grade_participation_task', + 'blocking' => 0, + 'minute' => '*', + 'hour' => '*', + 'day' => '*', + 'month' => '*', + 'dayofweek' => '*' + ) +); diff --git a/db/upgrade.php b/db/upgrade.php new file mode 100644 index 0000000..f708ad7 --- /dev/null +++ b/db/upgrade.php @@ -0,0 +1,28 @@ +. + +/** + * @package mod_palestra + * @copyright 2021 Interlegis (https://www.interlegis.leg.br) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die; + +function xmldb_palestra_upgrade($oldversion) { + global $CFG; + return true; +} diff --git a/grade.php b/grade.php new file mode 100644 index 0000000..63920e1 --- /dev/null +++ b/grade.php @@ -0,0 +1,115 @@ +. + +/** + * This page is the entry page into the quiz UI. Displays information about the + * quiz to students and teachers, and lets students see their previous attempts. + * + * @package mod_palestra + * @category grade + * @copyright 2021 Interlegis (https://www.interlegis.leg.br) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once(__DIR__ . '/../../config.php'); +require_once("lib.php"); + +$id = required_param('id', PARAM_INT); +$userid = optional_param('userid', 0, PARAM_INT); + +$cm = get_coursemodule_from_id('palestra', $id, 0, false, MUST_EXIST); +$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST); +$palestra = $DB->get_record('palestra', array('id' => $cm->instance), '*', MUST_EXIST); + +require_login($course, false, $cm); + +if (!$userid && !has_capability('mod/palestra:viewpresences', $PAGE->cm->context)) { + $userid = $USER->id; +} + +if ($userid) { + $userlist = array($userid); + $where = 'p.userid = '.$userid; +} else { + $userlist = $DB->get_fieldset_sql('select distinct userid from {palestra_presence} where palestraid = ? and userid not in ('.$CFG->siteadmins.')', [$palestra->id]); + $where = 'p.userid not in ('.$CFG->siteadmins.')'; +} + +$presences = $DB->get_records_sql( + " + select p.*, ".get_all_user_name_fields(true,'u')." + from {palestra_presence} p + inner join {user} u on u.id = p.userid + where p.palestraid = ? and + ".$where, [$palestra->id] +); + +$grading_info = grade_get_grades($course->id, 'mod', 'palestra', $palestra->id, $userlist); + +$PAGE->set_url('/mod/lesson/grade.php', array('id'=>$cm->id)); +$PAGE->set_title($course->shortname.': '.$palestra->name); +$PAGE->set_heading($course->fullname); +$PAGE->requires->jquery(); + +echo $OUTPUT->header(); +echo $OUTPUT->heading(get_string('presence_report_title', 'palestra', ['name'=>format_string($palestra->name)]), 2); + +echo " + + + + + + + + +"; + +$lastuser = null; +$stay_total = 0; + +foreach ($presences as $presence) { + if ($lastuser != $presence->userid) { + echo ""; + if ($lastuser) { + $total_row = get_string('total_row', 'palestra', ['name'=>$fullname, 'stay'=>$stay_total, 'grade'=>$grading_info->items[0]->grades[$lastuser]->str_long_grade]); + echo ""; + } + $fullname = fullname($presence); + echo " + + + "; + $lastuser = $presence->userid; + $stay_total = 0; + } + $stay = round(($presence->lastcheck - $presence->starttime) / 60); + $stay_total = $stay_total + $stay; + echo " + + + + "; +} +echo ""; +if ($lastuser) { + $total_row = get_string('total_row', 'palestra', ['name'=>$fullname, 'stay'=>$stay_total, 'grade'=>$grading_info->items[0]->grades[$lastuser]->str_long_grade]); + echo ""; +} +echo "
".get_string('start', 'palestra')."".get_string('last_signal', 'palestra')."".get_string('stay_time', 'palestra')." +
{$total_row}
{$fullname}
".userdate($presence->starttime, '%d/%m/%Y %H:%M:%S')."".userdate($presence->lastcheck, '%d/%m/%Y %H:%M:%S')."{$stay}
{$total_row}
"; + +echo $OUTPUT->footer(); \ No newline at end of file diff --git a/index.php b/index.php new file mode 100644 index 0000000..63c1797 --- /dev/null +++ b/index.php @@ -0,0 +1,95 @@ +. + +/** + * List of all palestras in course + * + * @package mod_palestra + * @copyright 2021 Interlegis (https://www.interlegis.leg.br) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require('../../config.php'); + +$id = required_param('id', PARAM_INT); // course id + +$course = $DB->get_record('course', array('id'=>$id), '*', MUST_EXIST); + +require_course_login($course, true); +$PAGE->set_pagelayout('incourse'); + +$strpalestra = get_string('modulename', 'palestra'); +$strpalestras = get_string('modulenameplural', 'palestra'); +$strname = get_string('name'); +$strintro = get_string('moduleintro'); +$strlastmodified = get_string('lastmodified'); + +$PAGE->set_url('/mod/palestra/index.php', array('id' => $course->id)); +$PAGE->set_title($course->shortname.': '.$strpalestras); +$PAGE->set_heading($course->fullname); +$PAGE->navbar->add($strpalestras); +echo $OUTPUT->header(); +echo $OUTPUT->heading($strpalestras); +if (!$palestras = get_all_instances_in_course('palestra', $course)) { + notice(get_string('thereareno', 'moodle', $strpalestras), "$CFG->wwwroot/course/view.php?id=$course->id"); + exit; +} + +$usesections = course_format_uses_sections($course->format); + +$table = new html_table(); +$table->attributes['class'] = 'generaltable mod_index'; + +if ($usesections) { + $strsectionname = get_string('sectionname', 'format_'.$course->format); + $table->head = array ($strsectionname, $strname, $strintro); + $table->align = array ('center', 'left', 'left'); +} else { + $table->head = array ($strlastmodified, $strname, $strintro); + $table->align = array ('left', 'left', 'left'); +} + +$modinfo = get_fast_modinfo($course); +$currentsection = ''; +foreach ($palestras as $palestra) { + $cm = $modinfo->cms[$palestra->coursemodule]; + if ($usesections) { + $printsection = ''; + if ($palestra->section !== $currentsection) { + if ($palestra->section) { + $printsection = get_section_name($course, $palestra->section); + } + if ($currentsection !== '') { + $table->data[] = 'hr'; + } + $currentsection = $palestra->section; + } + } else { + $printsection = ''.userdate($palestra->timemodified).""; + } + + $class = $palestra->visible ? '' : 'class="dimmed"'; // hidden modules are dimmed + + $table->data[] = array ( + $printsection, + "id\">".format_string($palestra->name)."", + format_module_intro('palestra', $palestra, $cm->id)); +} + +echo html_writer::table($table); + +echo $OUTPUT->footer(); diff --git a/lang/en/palestra.php b/lang/en/palestra.php new file mode 100644 index 0000000..97691bd --- /dev/null +++ b/lang/en/palestra.php @@ -0,0 +1,56 @@ +. + +/** + * Strings for component 'palestra', language 'en' + * + * @package mod_palestra + * @copyright 2021 Interlegis (https://www.interlegis.leg.br) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +$string['pluginname'] = 'Palestra'; +$string['modulename'] = 'Palestra'; +$string['modulenameplural'] = 'Palestras'; +$string['pluginadministration'] = 'Palestra administration'; +$string['crontask'] = 'Schedule for palestra participation grade calculation'; +$string['contentheader'] = 'Palestra settings'; +$string['youtubecode'] = 'Youtube stream code'; +$string['startdate'] = 'Start date'; +$string['startdate_help'] = 'The moment from which attendance must be registered'; +$string['duration'] = 'Duration'; +$string['duration_help'] = 'Attendance duration in minutes'; +$string['checkinterval'] = 'Check interval'; +$string['checkinterval_help'] = "Indicates the time interval (in minutes) that the user's browser sends a 'sign of life' to the moodle server. This is used to calculate the user's time spent on the 'palestra' and, consequently, their participation grade. A lower value promotes better accuracy, but can overload the server, especially if you have many concurrent users. A higher value can decrease precision, but it makes the server lighter. The default value is 10 minutes. Zero indicates that no live signals will be sent and simple access to the Palestra will correspond to 100% participation grade."; +$string['includechat'] = 'Include youtube chat'; +$string['chatdisposition'] = 'Chat disposition'; +$string['chatdisposition_sidebyside'] = 'Side by side'; +$string['chatdisposition_stacked'] = 'Stacked'; +$string['includechat_help'] = 'If checked, the associated youtube chat will be displayed together the video area'; +$string['presence_unregistered'] = 'Your presence is not being registered. Try to leave the palestra and enter again.'; +$string['grade_participation_task'] = 'Grades the Palestra Participacion'; +$string['out_of_date_alert'] = 'Your presence is not being counted because the activity has already ended. You can continue watching the content.'; +$string['gradeafter'] = 'Grade after end of palestra'; +$string['gradeafter_help'] = 'Continue grading participation even after the end of palestra transmission.'; +$string['presence_report_title'] = 'Attendance report at {$a->name}'; +$string['start'] = 'Start time'; +$string['last_signal'] = 'Last live signal'; +$string['stay_time'] = 'Stay time (minutes)'; +$string['total_row'] = 'Total for {$a->name}: stay {$a->stay} minutes, grading {$a->grade}.'; +$string['view_presences'] = 'View present users'; +$string['presence_list_title'] = 'List of present users'; +$string['presence_total'] = '{$a} present users'; \ No newline at end of file diff --git a/lang/pt_br/palestra.php b/lang/pt_br/palestra.php new file mode 100644 index 0000000..c001ffe --- /dev/null +++ b/lang/pt_br/palestra.php @@ -0,0 +1,56 @@ +. + +/** + * Strings for component 'palestra', language 'en' + * + * @package mod_palestra + * @copyright 2021 Interlegis (https://www.interlegis.leg.br) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +$string['pluginname'] = 'Palestra'; +$string['modulename'] = 'Palestra'; +$string['modulenameplural'] = 'Palestras'; +$string['pluginadministration'] = 'Administração de palestra'; +$string['crontask'] = 'Cronograma para cálculo da nota de participação da palestra'; +$string['contentheader'] = 'Configurações da palestra'; +$string['youtubecode'] = 'Código de stream do Youtube'; +$string['startdate'] = 'Data de início'; +$string['startdate_help'] = 'O momento em que a presença deve começar a ser registrada'; +$string['duration'] = 'Duração'; +$string['duration_help'] = 'Duração da palestra em minutos'; +$string['checkinterval'] = 'Intervalo de verificação'; +$string['checkinterval_help'] = "Indica o intervalo de tempo (em minutos) que o browser do usário envia um 'sinal de vida' para o servidor moodle. Isto é usado para calcular o tempo que o usuário permaneceu na palestra e, consequentemente, sua nota de participação. Um valor baixo promove melhor precisão, mas pode sobrecarregar o servidor, especialmente se há muitos usuários simultâneos. Um valor alto pode reduzir a precisão, mas deixa o servidor mais leve. O valor default é 10 minutos. Zero indica que nenhum sinal de vida será enviado e o simples accesso à Palestra corresponderá a 100% de nota de participação."; +$string['includechat'] = 'Incluir chat do youtube'; +$string['chatdisposition'] = 'Posição do chat'; +$string['chatdisposition_sidebyside'] = 'Lado a lado'; +$string['chatdisposition_stacked'] = 'Empilhado'; +$string['includechat_help'] = 'Se marcado, o chat do youtube associado será mostrado próximo à área do vídeo'; +$string['presence_unregistered'] = 'Sua presença não estã sendo registrada. Tente sair da palestra e entrar novamente.'; +$string['grade_participation_task'] = 'Pontua a participação na palestra'; +$string['out_of_date_alert'] = 'Sua presença não está sendo computada porque a atividade já terminou. Você pode continuar assistindo ao conteúdo.'; +$string['gradeafter'] = 'Pontuar após o término da palestra'; +$string['gradeafter_help'] = 'Continua pontuando a participação mesmo após o término da transmissão da palestra.'; +$string['presence_report_title'] = 'Relatório de presença em {$a->name}'; +$string['start'] = 'Início'; +$string['last_signal'] = 'Último sinal de vida'; +$string['stay_time'] = 'Tempo de permanência (minutos)'; +$string['total_row'] = 'Total para {$a->name}: Permanência de {$a->stay} minutos, {$a->grade} pontos alcançados.'; +$string['view_presences'] = 'Visualizar usuários presentes'; +$string['presence_list_title'] = 'Lista de usuários presentes'; +$string['presence_total'] = '{$a} usuários presentes'; \ No newline at end of file diff --git a/lib.php b/lib.php new file mode 100644 index 0000000..9f061aa --- /dev/null +++ b/lib.php @@ -0,0 +1,365 @@ +. + +/** + * @package mod_palestra + * @copyright 2021 Interlegis (https://www.interlegis.leg.br) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die; + +function palestra_supports($feature) { + switch($feature) { + case FEATURE_MOD_ARCHETYPE: return MOD_ARCHETYPE_ASSIGNMENT; + case FEATURE_GROUPS: return false; + case FEATURE_GROUPINGS: return false; + case FEATURE_MOD_INTRO: return true; + case FEATURE_COMPLETION_TRACKS_VIEWS: return true; + case FEATURE_COMPLETION_HAS_RULES: return true; + case FEATURE_GRADE_HAS_GRADE: return true; + case FEATURE_BACKUP_MOODLE2: return true; + case FEATURE_SHOW_DESCRIPTION: return true; + + default: return null; + } +} + +function palestra_reset_userdata($data) { + + // Any changes to the list of dates that needs to be rolled should be same during course restore and course reset. + // See MDL-9367. + + return array(); +} + +function palestra_get_view_actions() { + return array('view','view all'); +} + +function palestra_get_post_actions() { + return array('update', 'add'); +} + +function palestra_add_instance($palestra, $mform = null) { + global $CFG, $DB; + require_once("$CFG->libdir/resourcelib.php"); + + $cmid = $palestra->coursemodule; + + $palestra->timemodified = time(); + $palestra->needsupdate = 1; + + $palestra->id = $DB->insert_record('palestra', $palestra); + + $DB->set_field('course_modules', 'instance', $palestra->id, array('id'=>$cmid)); + $context = context_module::instance($cmid); + + palestra_grade_item_update($palestra); + + $completiontimeexpected = !empty($palestra->completionexpected) ? $palestra->completionexpected : null; + \core_completion\api::update_completion_date_event($cmid, 'palestra', $palestra->id, $completiontimeexpected); + + return $palestra->id; +} + +function palestra_update_instance($palestra, $mform) { + global $CFG, $DB; + require_once("$CFG->libdir/resourcelib.php"); + + $cmid = $palestra->coursemodule; + $palestra->timemodified = time(); + $palestra->needsupdate = 1; + $palestra->id = $palestra->instance; + + $DB->update_record('palestra', $palestra); + + palestra_grade_item_update($palestra); + + $completiontimeexpected = !empty($palestra->completionexpected) ? $palestra->completionexpected : null; + \core_completion\api::update_completion_date_event($cmid, 'palestra', $palestra->id, $completiontimeexpected); + + return true; +} + +function palestra_delete_instance($id) { + global $DB; + + if (!$palestra = $DB->get_record('palestra', array('id'=>$id))) { + return false; + } + + $cm = get_coursemodule_from_instance('palestra', $id); + \core_completion\api::update_completion_date_event($cm->id, 'palestra', $id, null); + + $DB->delete_records('palestra', array('id'=>$palestra->id)); + $DB->delete_records('palestra_presence', array('palestraid'=>$palestra->id)); + + return true; +} + +function palestra_get_coursemodule_info($coursemodule) { + global $CFG, $DB; + require_once("$CFG->libdir/resourcelib.php"); + + if (!$palestra = $DB->get_record('palestra', array('id'=>$coursemodule->instance))) { + return NULL; + } + + $info = new cached_cm_info(); + $info->name = $palestra->name; + + if ($coursemodule->showdescription) { + // Convert intro to html. Do not filter cached version, filters run at display time. + $info->content = format_module_intro('palestra', $palestra, $coursemodule->id, false); + } + + return $info; +} + +function palestra_view($palestra, $course, $cm, $context) { + $params = array( + 'context' => $context, + 'objectid' => $palestra->id + ); + + $event = \mod_palestra\event\course_module_viewed::create($params); + $event->add_record_snapshot('course_modules', $cm); + $event->add_record_snapshot('course', $course); + $event->add_record_snapshot('palestra', $palestra); + $event->trigger(); + + // Completion. + $completion = new completion_info($course); + $completion->set_module_viewed($cm); +} + +function mod_palestra_core_calendar_provide_event_action(calendar_event $event, + \core_calendar\action_factory $factory, $userid = 0) { + global $USER; + + if (empty($userid)) { + $userid = $USER->id; + } + + $cm = get_fast_modinfo($event->courseid, $userid)->instances['palestra'][$event->instance]; + + $completion = new \completion_info($cm->get_course()); + + $completiondata = $completion->get_data($cm, false, $userid); + + if ($completiondata->completionstate != COMPLETION_INCOMPLETE) { + return null; + } + + return $factory->create_instance( + get_string('view'), + new \moodle_url('/mod/palestra/view.php', ['id' => $cm->id]), + 1, + true + ); +} + +function palestra_grade_item_update($palestra, $grades=NULL) { + global $CFG; + if (!function_exists('grade_update')) { //workaround for buggy PHP versions + require_once($CFG->libdir.'/gradelib.php'); + } + + $params = array('itemname'=>$palestra->name, 'idnumber'=>$palestra->cmidnumber, 'needsupdate'=>1); + + if ($palestra->grade == 0) { + $params['gradetype'] = GRADE_TYPE_NONE; + + } else if ($palestra->grade > 0) { + $params['gradetype'] = GRADE_TYPE_VALUE; + $params['grademax'] = $palestra->grade; + $params['grademin'] = 0; + + } else if ($palestra->grade < 0) { + $params['gradetype'] = GRADE_TYPE_SCALE; + $params['scaleid'] = -$palestra->grade; + } + + if ($grades === 'reset') { + $params['reset'] = true; + $grades = NULL; + } + + return grade_update('mod/palestra', $palestra->course, 'mod', 'palestra', $palestra->id, 0, $grades, $params); +} + +function palestra_update_grades($palestra, $userid=0, $nullifnone=true) { + global $CFG, $DB; + require_once($CFG->libdir.'/gradelib.php'); + + if ($userid) { + $presences = $DB->get_records("palestra_presence", array("palestraid"=>$palestra->id, "userid"=>$userid), 'userid, starttime, lastcheck'); + } else { + $presences = $DB->get_records("palestra_presence", array("palestraid"=>$palestra->id), 'userid, starttime, lastcheck'); + } + + $audiences = array(); + $minimal = $palestra->checkinterval * 60; + $user = null; + foreach($presences as $presence) { + # Compute only after palestra starts + if ($presence->starttime < $palestra->startdate) { + $presence->starttime = $palestra->startdate; + } + if ($user != $presence->userid) { + if ($user) { + $audiences[$user] = (isset($audiences[$user])?$audiences[$user]:0) + (max($end - $start, $minimal)/60); + } + $user = $presence->userid; + $start = $presence->starttime; + $end = $presence->lastcheck; + continue; + } + if ($presence->starttime < ($start + $minimal)) { + $end = $presence->lastcheck; + } else { + $audiences[$user] = (isset($audiences[$user])?$audiences[$user]:0) + (max($end - $start, $minimal)/60); + $start = $presence->starttime; + $end = $presence->lastcheck; + } + } + + if ($user) { + $audiences[$user] = (isset($audiences[$user])?$audiences[$user]:0) + (max($end - $start, $minimal)/60); + } + + $grades = array(); + foreach($audiences as $userid=>$presence_time) { + $presence_rate = $presence_time / $palestra->duration; + if ($presence_rate > 1.0) { + $presence_rate = 1.0; + } + $grade = new stdclass(); + $grade->userid = $userid; + $grade->rawgrade = grade_floatval($palestra->grade * $presence_rate); + $grade->datesubmitted = $palestra->startdate; + $grade->dategraded = $palestra->startdate; + $grades[$userid] = $grade; + } + palestra_grade_item_update($palestra, $grades); + $DB->set_field('palestra', 'needsupdate', 0, array('id'=>$palestra->id)); +} + +function palestra_reset_gradebook($courseid, $type='') { + global $CFG, $DB; + + $wheresql = ''; + $params = array($courseid); + if ($type) { + $wheresql = "AND p.type=?"; + $params[] = $type; + } + + $sql = "SELECT p.*, cm.idnumber as cmidnumber, p.course as courseid + FROM {palestra} p, {course_modules} cm, {modules} m + WHERE m.name='palestra' AND m.id=cm.module AND cm.instance=p.id AND p.course=? $wheresql"; + + if ($palestras = $DB->get_records_sql($sql, $params)) { + foreach ($palestras as $palestra) { + palestra_grade_item_update($palestra, 'reset'); + } + } +} + +function palestra_get_user_grades($palestra, $userid = 0) { + global $CFG; + + require_once($CFG->dirroot.'/rating/lib.php'); + + $ratingoptions = new stdClass; + $ratingoptions->component = 'mod_palestra'; + $ratingoptions->ratingarea = 'post'; + + //need these to work backwards to get a context id. Is there a better way to get contextid from a module instance? + $ratingoptions->modulename = 'palestra'; + $ratingoptions->moduleid = $palestra->id; + $ratingoptions->userid = $userid; + $ratingoptions->aggregationmethod = $palestra->assessed; + $ratingoptions->scaleid = $palestra->scale; + $ratingoptions->itemtable = 'palestra_posts'; + $ratingoptions->itemtableusercolumn = 'userid'; + + $rm = new rating_manager(); + return $rm->get_user_grades($ratingoptions); +} + +function palestra_extend_settings_navigation($settings, $palestranode) { + global $PAGE, $CFG; + // We want to add these new nodes after the Edit settings node, and before the + // Locally assigned roles node. Of course, both of those are controlled by capabilities. + $keys = $palestranode->get_children_key_list(); + $beforekey = null; + $i = array_search('modedit', $keys); + if ($i === false and array_key_exists(0, $keys)) { + $beforekey = $keys[0]; + } else if (array_key_exists($i + 1, $keys)) { + $beforekey = $keys[$i + 1]; + } + + if (has_capability('mod/palestra:viewpresences', $PAGE->cm->context)) { + $url = new moodle_url('/mod/palestra/presences.php', array('cmid'=>$PAGE->cm->id)); + $node = navigation_node::create(get_string('view_presences', 'palestra'), + $url, + navigation_node::TYPE_SETTING, null, 'mod_palestra_viewpresences'); + $palestranode->add_node($node, $beforekey); + } + + // if (has_capability('mod/quiz:manage', $PAGE->cm->context)) { + // $node = navigation_node::create(get_string('editquiz', 'quiz'), + // new moodle_url('/mod/quiz/edit.php', array('cmid'=>$PAGE->cm->id)), + // navigation_node::TYPE_SETTING, null, 'mod_quiz_edit', + // new pix_icon('t/edit', '')); + // $quiznode->add_node($node, $beforekey); + // } + + // if (has_capability('mod/quiz:preview', $PAGE->cm->context)) { + // $url = new moodle_url('/mod/quiz/startattempt.php', + // array('cmid'=>$PAGE->cm->id, 'sesskey'=>sesskey())); + // $node = navigation_node::create(get_string('preview', 'quiz'), $url, + // navigation_node::TYPE_SETTING, null, 'mod_quiz_preview', + // new pix_icon('i/preview', '')); + // $quiznode->add_node($node, $beforekey); + // } + + // if (has_any_capability(array('mod/quiz:viewreports', 'mod/quiz:grade'), $PAGE->cm->context)) { + // require_once($CFG->dirroot . '/mod/quiz/report/reportlib.php'); + // $reportlist = quiz_report_list($PAGE->cm->context); + + // $url = new moodle_url('/mod/quiz/report.php', + // array('id' => $PAGE->cm->id, 'mode' => reset($reportlist))); + // $reportnode = $quiznode->add_node(navigation_node::create(get_string('results', 'quiz'), $url, + // navigation_node::TYPE_SETTING, + // null, null, new pix_icon('i/report', '')), $beforekey); + + // foreach ($reportlist as $report) { + // $url = new moodle_url('/mod/quiz/report.php', + // array('id' => $PAGE->cm->id, 'mode' => $report)); + // $reportnode->add_node(navigation_node::create(get_string($report, 'quiz_'.$report), $url, + // navigation_node::TYPE_SETTING, + // null, 'quiz_report_' . $report, new pix_icon('i/item', ''))); + // } + // } + + // question_extend_settings_navigation($quiznode, $PAGE->cm->context)->trim_if_empty(); +} + diff --git a/mod_form.php b/mod_form.php new file mode 100644 index 0000000..a2bdbfa --- /dev/null +++ b/mod_form.php @@ -0,0 +1,79 @@ +. + +/** + * palestra configuration form + * + * @package mod_palestra + * @copyright 2021 Interlegis (https://www.interlegis.leg.br) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die; + +require_once($CFG->dirroot.'/course/moodleform_mod.php'); +require_once($CFG->libdir.'/filelib.php'); + +class mod_palestra_mod_form extends moodleform_mod { + function definition() { + global $CFG, $DB; + $mform = $this->_form; + $config = get_config('palestra'); + + $mform->addElement('header', 'general', get_string('general', 'form')); + $mform->addElement('text', 'name', get_string('name'), array('size'=>'48')); + if (!empty($CFG->formatstringstriptags)) { + $mform->setType('name', PARAM_TEXT); + } else { + $mform->setType('name', PARAM_CLEANHTML); + } + $mform->addRule('name', null, 'required', null, 'client'); + $mform->addRule('name', get_string('maximumchars', '', 255), 'maxlength', 255, 'client'); + $this->standard_intro_elements(); + + $mform->addElement('header', 'contentsection', get_string('contentheader', 'palestra')); + $mform->addElement('text', 'youtubecode', get_string('youtubecode', 'palestra'), array('size'=>'48')); + $mform->setType('youtubecode', PARAM_TEXT); + $mform->addRule('youtubecode', null, 'required', null, 'client'); + $mform->addRule('youtubecode', get_string('maximumchars', '', 255), 'maxlength', 255, 'client'); + $mform->addElement('advcheckbox', 'includechat', get_string('includechat', 'palestra')); + $mform->addElement('select', 'chatdisposition', get_string('chatdisposition', 'palestra'), array('S'=>get_string('chatdisposition_sidebyside', 'palestra'), 'T'=>get_string('chatdisposition_stacked', 'palestra'))); + $mform->disabledIf('chatdisposition', 'includechat', 'notchecked', '1'); + $mform->setDefault('includechat', $config->includechat); + $mform->addHelpButton('includechat', 'includechat', 'palestra'); + $mform->addElement('date_time_selector', 'startdate', get_string('startdate', 'palestra'), array('size'=>'10')); + $mform->setType('startdate', PARAM_INT); + $mform->addRule('startdate', null, 'required', null, 'client'); + $mform->addHelpButton('startdate', 'startdate', 'palestra'); + $mform->addElement('text', 'duration', get_string('duration', 'palestra'), array('size'=>'10')); + $mform->setType('duration', PARAM_INT); + $mform->addRule('duration', null, 'required', null, 'client'); + $mform->addHelpButton('duration', 'duration', 'palestra'); + $mform->addElement('checkbox', 'gradeafter', get_string('gradeafter', 'palestra')); + $mform->setDefault('gradeafter', $config->gradeafter); + $mform->addHelpButton('gradeafter', 'gradeafter', 'palestra'); + $mform->addElement('text', 'checkinterval', get_string('checkinterval', 'palestra'), array('size'=>'10')); + $mform->setType('checkinterval', PARAM_INT); + $mform->addRule('checkinterval', null, 'required', null, 'client'); + $mform->setDefault('checkinterval', $config->checkinterval); + $mform->addHelpButton('checkinterval', 'checkinterval', 'palestra'); + + $this->standard_grading_coursemodule_elements(); + $this->standard_coursemodule_elements(); + $this->add_action_buttons(); + } +} \ No newline at end of file diff --git a/pix/download.jpeg b/pix/download.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..e362275aca329fadb010e3a94431a10a0bfee613 GIT binary patch literal 6126 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. + +/** + * Page module version information + * + * @package mod_palestra + * @copyright 2021 Interlegis (https://www.interlegis.leg.br) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require('../../config.php'); +require_once($CFG->dirroot.'/mod/palestra/lib.php'); + +$id = optional_param('id', null, PARAM_INT); + +if (!$id) { + echo get_string('presence_unregistered', 'palestra'); +} else { + $DB->set_field('palestra_presence', 'lastcheck', time(), array('id'=>$id)); + echo "done"; +} \ No newline at end of file diff --git a/presences.php b/presences.php new file mode 100644 index 0000000..b57087b --- /dev/null +++ b/presences.php @@ -0,0 +1,81 @@ +. + +/** + * This page is the entry page into the quiz UI. Displays information about the + * quiz to students and teachers, and lets students see their previous attempts. + * + * @package mod_palestra + * @copyright 2021 Interlegis (https://www.interlegis.leg.br) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once(__DIR__ . '/../../config.php'); +require_once("lib.php"); + +$cmid = required_param('cmid', PARAM_INT); + +$cm = get_coursemodule_from_id('palestra', $cmid, 0, false, MUST_EXIST); +$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST); +$palestra = $DB->get_record('palestra', array('id' => $cm->instance), '*', MUST_EXIST); + +require_login($course, false, $cm); + +if (!has_capability('mod/palestra:viewpresences', $PAGE->cm->context)) { + redirect("view.php?id=$cm->id"); +} + +$presences = $DB->get_records_sql( + " + select distinct u.* + from {palestra_presence} p + inner join {user} u on u.id = p.userid + where + p.palestraid = ? and + p.userid not in (".$CFG->siteadmins.") + ", [$palestra->id] +); + +$PAGE->set_url('/mod/palestra/presences.php', array('id'=>$cm->id)); +$PAGE->set_title($course->shortname.': '.$palestra->name); +$PAGE->set_heading($course->fullname); +$PAGE->requires->jquery(); + +$pagetitle = get_string('presence_list_title', 'palestra', ['name'=>format_string($palestra->name)]); + +echo $OUTPUT->header(); +echo $OUTPUT->heading($pagetitle, 2); + +echo " + + + + + + + + + + +"; + +foreach ($presences as $presence) { + echo ""; +} + +echo "
".get_string('presence_total', 'palestra', count($presences))."
".get_string('fullname')."
".fullname($presence)."
"; + +echo $OUTPUT->footer(); \ No newline at end of file diff --git a/settings.php b/settings.php new file mode 100755 index 0000000..a065674 --- /dev/null +++ b/settings.php @@ -0,0 +1,40 @@ +. + +/** + * Page module admin settings and defaults + * + * @package mod_palestra + * @copyright 2021 Interlegis (https://www.interlegis.leg.br) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die; + +if ($ADMIN->fulltree) { + require_once("$CFG->libdir/resourcelib.php"); + + $displayoptions = resourcelib_get_displayoptions(array(RESOURCELIB_DISPLAY_OPEN, RESOURCELIB_DISPLAY_POPUP)); + $defaultdisplayoptions = array(RESOURCELIB_DISPLAY_OPEN); + + $settings->add(new admin_setting_configcheckbox('palestra/includechat', + get_string('includechat', 'palestra'), get_string('includechat_help', 'palestra'), 1)); + $settings->add(new admin_setting_configtext('palestra/checkinterval', + get_string('checkinterval', 'palestra'), get_string('checkinterval_help', 'palestra'), 10, PARAM_INT)); + $settings->add(new admin_setting_configcheckbox('palestra/gradeafter', + get_string('gradeafter', 'palestra'), get_string('gradeafter_help', 'palestra'), 0)); +} diff --git a/version.php b/version.php new file mode 100644 index 0000000..aecb1c0 --- /dev/null +++ b/version.php @@ -0,0 +1,30 @@ +. + +/** + * Palestra module version information + * + * @package mod_palestra + * @copyright 2021 Interlegis (https://www.interlegis.leg.br) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$plugin->version = 2021092200;// The current module version (Date: YYYYMMDDXX) +$plugin->requires = 2019051100;// Requires this Moodle version +$plugin->component = 'mod_palestra';// Full name of the plugin (used for diagnostics) +$plugin->cron = 0; diff --git a/view.php b/view.php new file mode 100644 index 0000000..8829c16 --- /dev/null +++ b/view.php @@ -0,0 +1,150 @@ +. + +/** + * Page module version information + * + * @package mod_palestra + * @copyright 2021 Interlegis (https://www.interlegis.leg.br) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require('../../config.php'); +require_once($CFG->dirroot.'/mod/palestra/lib.php'); +require_once($CFG->libdir.'/completionlib.php'); + +$id = optional_param('id', 0, PARAM_INT); // Course Module ID +$p = optional_param('p', 0, PARAM_INT); // Palestra instance ID + +if ($p) { + if (!$palestra = $DB->get_record('palestra', array('id'=>$p))) { + print_error('invalidaccessparameter'); + } + $cm = get_coursemodule_from_instance('palestra', $palestra->id, $palestra->course, false, MUST_EXIST); +} else { + if (!$cm = get_coursemodule_from_id('palestra', $id)) { + print_error('invalidcoursemodule'); + } + $palestra = $DB->get_record('palestra', array('id'=>$cm->instance), '*', MUST_EXIST); +} + +$course = $DB->get_record('course', array('id'=>$cm->course), '*', MUST_EXIST); + +require_course_login($course, true, $cm); +$context = context_module::instance($cm->id); +require_capability('mod/palestra:view', $context); + +// Completion and trigger events. +palestra_view($palestra, $course, $cm, $context); + +$PAGE->set_url('/mod/palestra/view.php', array('id' => $cm->id)); +$PAGE->set_title($course->shortname.': '.$palestra->name); +$PAGE->set_heading($course->fullname); +$PAGE->set_activity_record($palestra); +$PAGE->requires->jquery(); + +echo $OUTPUT->header(); +echo $OUTPUT->heading(format_string($palestra->name), 2); + +if (trim(strip_tags($palestra->intro))) { + echo $OUTPUT->box_start('mod_introbox', 'pageintro'); + echo format_module_intro('palestra', $palestra, $cm->id); + echo $OUTPUT->box_end(); +} + +echo $OUTPUT->box("", "generalbox center clearfix text-center"); + +$stream = ""; + +if ($palestra->includechat) { + $parse = parse_url($CFG->wwwroot); + $domain = $parse['host']; + $chat = ""; + if ($palestra->chatdisposition =='S') { + echo '
'; + echo $OUTPUT->box($stream, "col-8"); + echo $OUTPUT->box($chat, "col-4"); + echo "
"; + } else { + echo $OUTPUT->box($stream, "generalbox center clearfix"); + echo $OUTPUT->box($chat, "generalbox center clearfix"); + } +} + +$now = time(); + +// Presence will only be registered if user accessed before the end +// of palestra (startdate+duration), plus a lag margin (duration*1.5) +// or if gradeafter is checked as True +if (($now < ($palestra->startdate + ($palestra->duration*1.5*60))) + or ($palestra->gradeafter == 1)) { + $presence = array('palestraid'=>$palestra->id, 'userid'=>$USER->id, 'starttime'=>$now, 'lastcheck'=>$now); + if ($palestra->checkinterval == 0) { + // Just accessing the palestra grants 100% of grade + $presence['lastcheck'] += $palestra->duration * 60; + } + $presence['id'] = $DB->insert_record('palestra_presence', $presence); + $DB->set_field('palestra', 'needsupdate', '1', ['id'=>$palestra->id]); + + $interval = $palestra->checkinterval * 60 * 1000; + $alert = get_string('presence_unregistered', 'palestra'); + + if ($interval > 0) { + echo " + "; + } +} else { + $out_of_date_alert = get_string('out_of_date_alert', 'palestra'); + echo " + "; +} +echo $OUTPUT->footer();