diff --git a/CHANGELOG.md b/CHANGELOG.md index 98c8f6ae880..2e7dd7aaf47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,17 @@ Todas las modificaciones importantes del proyecto se documentarán en este archivo. +## [2.0.4] — 2026-07-28 + +### Cambiado +- El bloque para personal autorizado muestra un lanzador compacto hacia el panel administrativo, sin métricas ni estadísticas expuestas en la página del curso. +- Se estandarizó el acceso a resultados individuales, la exportación y los textos de permisos en español e inglés. + +### Seguridad +- Se añadieron las capacidades explícitas `viewstudentdata` y `deletestudentdata`; la consulta, exportación y eliminación de respuestas quedaron separadas. +- Los roles docentes ya no reciben acceso automático a datos sensibles. +- Se retiró la capacidad heredada e inactiva `viewreports`. + ## [2.0.3] — 2026-01-18 - Se eliminaron las comprobaciones redundantes de administrador (`is_siteadmin()`) en varias vistas clave, mejorando la detección correcta de roles locales (profesores vs estudiantes) y el sistema de permisos basado en capacidades. - Se agrega negrilla en el titulo del bloque para mejorar la visibilidad. diff --git a/admin_view.php b/admin_view.php index 2bc9edadb1c..8a0f2adfa31 100644 --- a/admin_view.php +++ b/admin_view.php @@ -33,7 +33,7 @@ } // Friendly redirect for unauthorized users -if (!has_capability('block/learning_style:viewreports', $context)) { +if (!has_capability('block/learning_style:viewstudentdata', $context)) { redirect(new moodle_url('/course/view.php', array('id' => $courseid))); } @@ -48,13 +48,17 @@ $PAGE->set_url($admin_url); // Handle Delete Action +if ($action === 'delete' && $userid && !has_capability('block/learning_style:deletestudentdata', $context)) { + redirect($admin_url); +} + if ($action === 'delete' && $userid && confirm_sesskey()) { $confirm = optional_param('confirm', 0, PARAM_INT); if ($confirm) { // Privacy check $targetuser = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST); if (!is_enrolled($context, $targetuser, 'block/learning_style:take_test', true) - || has_capability('block/learning_style:viewreports', $context, $userid)) { + || has_capability('block/learning_style:viewstudentdata', $context, $userid)) { redirect(new moodle_url('/course/view.php', array('id' => $courseid))); } @@ -64,7 +68,7 @@ } $title = get_string('admin_title', 'block_learning_style'); -$PAGE->set_pagelayout('standard'); +$PAGE->set_pagelayout('incourse'); $PAGE->set_title($title . " : " . $course->fullname); $PAGE->set_heading($title . " : " . $course->fullname); $PAGE->requires->css('/blocks/learning_style/styles.css'); @@ -78,7 +82,8 @@ 'admin_url' => $admin_url->out(false), 'export_url' => (new moodle_url('/blocks/learning_style/download_results.php', ['courseid' => $courseid, 'sesskey' => sesskey()]))->out(false), 'course_url' => (new moodle_url('/course/view.php', ['id' => $courseid]))->out(false), - 'search_term' => $search + 'search_term' => $search, + 'can_delete' => has_capability('block/learning_style:deletestudentdata', $context), ]; $user = $DB->get_record('user', array('id' => $userid), 'firstname, lastname'); @@ -259,6 +264,7 @@ 'is_completed' => ($p->is_completed == 1), 'completion_date' => userdate($p->updated_at, get_string('strftimedatetimeshort')), 'view_url' => (new moodle_url('/blocks/learning_style/view_individual.php', ['courseid' => $courseid, 'userid' => $p->user]))->out(false), + 'can_delete' => has_capability('block/learning_style:deletestudentdata', $context), 'delete_url' => (new moodle_url('/blocks/learning_style/admin_view.php', ['courseid' => $courseid, 'action' => 'delete', 'userid' => $p->user, 'sesskey' => sesskey()]))->out(false) ]; diff --git a/block_learning_style.php b/block_learning_style.php index f421c5bdd9e..1bc34f3ccaf 100644 --- a/block_learning_style.php +++ b/block_learning_style.php @@ -131,7 +131,7 @@ public function get_content() { * Check if user can view dashboard */ private function can_view_dashboard($context, $userid = null) { - return has_capability('block/learning_style:viewreports', $context, $userid); + return has_capability('block/learning_style:viewstudentdata', $context, $userid); } /** @@ -251,70 +251,15 @@ private function render_results($entry) { * Generate content for teachers/admins */ private function get_teacher_content($context) { - global $CFG, $COURSE, $DB, $OUTPUT; - - // Header with learning style icon for teacher/admin view. - $icon = $this->get_learning_style_icon('4em', '', true); - - // Assets for embedded dashboard. - $this->page->requires->css('/blocks/learning_style/dashboard/css/style.css'); - $this->page->requires->js('/blocks/learning_style/dashboard/js/main.js'); - - // Render dashboard HTML fragment. - $embedded_file = $CFG->dirroot . '/blocks/learning_style/dashboard/embedded.php'; - $dashboard_html = ''; - - if (file_exists($embedded_file)) { - ob_start(); - $courseid = $COURSE->id; - include_once($embedded_file); - $dashboard_html = ob_get_clean(); - } else { - $dashboard_html = '
' . get_string('dashboard_not_found', 'block_learning_style') . '
'; - } - - $template_data = [ - 'icon' => $icon, - 'dashboard_html' => $dashboard_html, - 'show_buttons' => false - ]; - - // Agregar botones al final (después del dashboard) para profesores/administradores - $is_teacher = $this->can_view_dashboard($context); - if ($is_teacher) { - $template_data['show_buttons'] = true; - $template_data['admin_url'] = (new moodle_url('/blocks/learning_style/admin_view.php', array('courseid' => $COURSE->id)))->out(); - - // Determinar si hay tests completados en este curso (solo estudiantes) - $student_users = get_enrolled_users($context, 'block/learning_style:take_test', 0, 'u.id'); - $student_ids = array_keys($student_users); - - // Defensive: exclude any teacher/manager-type user even if misconfigured. - $filtered_student_ids = array(); - foreach ($student_ids as $candidateid) { - $candidateid = (int)$candidateid; - - if ($this->can_view_dashboard($context, $candidateid)) { - continue; - } - $filtered_student_ids[] = $candidateid; - } - $student_ids = $filtered_student_ids; - - $has_completed = false; - if (!empty($student_ids)) { - list($insql, $params) = $DB->get_in_or_equal($student_ids, SQL_PARAMS_NAMED, 'user'); - $completed_count = $DB->count_records_sql("SELECT COUNT(*) FROM {learning_style} WHERE user $insql AND is_completed = 1", $params); - $has_completed = ($completed_count > 0); - } - - $template_data['show_download'] = $has_completed; - if ($has_completed) { - $template_data['download_url'] = (new moodle_url('/blocks/learning_style/download_results.php', array('courseid' => $COURSE->id, 'sesskey' => sesskey())))->out(false); - } - } + global $COURSE, $OUTPUT; - return $OUTPUT->render_from_template('block_learning_style/teacher_dashboard', $template_data); + return $OUTPUT->render_from_template('block_learning_style/admin_launcher', [ + 'icon_html' => $this->get_learning_style_icon('4em', '', true), + 'security_label' => get_string('sensitive_data', 'block_learning_style'), + 'title' => get_string('management_title', 'block_learning_style'), + 'admin_url' => (new moodle_url('/blocks/learning_style/admin_view.php', ['courseid' => $COURSE->id]))->out(false), + 'button_label' => get_string('open_admin_panel', 'block_learning_style'), + ]); } /** diff --git a/dashboard/metrics.php b/dashboard/metrics.php index 9b80e0cc44c..bdc19f310a9 100644 --- a/dashboard/metrics.php +++ b/dashboard/metrics.php @@ -16,9 +16,7 @@ $context = context_course::instance($courseid); -$canview = has_capability('block/learning_style:viewreports', $context) - || has_capability('moodle/course:viewhiddensections', $context) - || is_siteadmin(); +$canview = has_capability('block/learning_style:viewstudentdata', $context); if (!$canview) { http_response_code(403); diff --git a/dashboard/view.php b/dashboard/view.php index c71635c135d..7e0a175d6f2 100644 --- a/dashboard/view.php +++ b/dashboard/view.php @@ -13,9 +13,7 @@ $context = context_course::instance($courseid); // Check permissions -$canview = has_capability('block/learning_style:viewreports', $context) - || has_capability('moodle/course:viewhiddensections', $context) - || is_siteadmin(); +$canview = has_capability('block/learning_style:viewstudentdata', $context); if (!$canview) { $redirecturl = new moodle_url('/course/view.php', array('id' => $courseid)); diff --git a/db/access.php b/db/access.php index 2cc81451758..fa5ff95b9b2 100644 --- a/db/access.php +++ b/db/access.php @@ -23,16 +23,6 @@ 'clonepermissionsfrom' => 'moodle/site:manageblocks' ), - 'block/learning_style:viewreports' => array( - 'captype' => 'read', - 'contextlevel' => CONTEXT_COURSE, - 'archetypes' => array( - 'teacher' => CAP_ALLOW, - 'editingteacher' => CAP_ALLOW, - 'manager' => CAP_ALLOW - ) - ), - 'block/learning_style:take_test' => array( 'captype' => 'write', 'contextlevel' => CONTEXT_COURSE, @@ -40,4 +30,17 @@ 'student' => CAP_ALLOW ) ), + + // Datos personales: no se asignan automáticamente a roles docentes. + 'block/learning_style:viewstudentdata' => array( + 'riskbitmask' => RISK_PERSONAL, + 'captype' => 'read', + 'contextlevel' => CONTEXT_COURSE + ), + + 'block/learning_style:deletestudentdata' => array( + 'riskbitmask' => RISK_DATALOSS | RISK_PERSONAL, + 'captype' => 'write', + 'contextlevel' => CONTEXT_COURSE + ), ); diff --git a/download_results.php b/download_results.php index 83a15482b65..0f062f62c95 100644 --- a/download_results.php +++ b/download_results.php @@ -34,7 +34,7 @@ function block_learning_style_csv_safe($value): string { // Requerir inicio de sesión require_login($course, false); -require_capability('block/learning_style:viewreports', $context); +require_capability('block/learning_style:viewstudentdata', $context); // Obtener solo estudiantes (usuarios que pueden tomar el test) $enrolled_users = get_enrolled_users($context, 'block/learning_style:take_test', 0, 'u.id'); @@ -45,7 +45,7 @@ function block_learning_style_csv_safe($value): string { foreach ($enrolled_ids as $candidateid) { $candidateid = (int)$candidateid; - if (has_capability('block/learning_style:viewreports', $context, $candidateid)) { + if (has_capability('block/learning_style:viewstudentdata', $context, $candidateid)) { continue; } $student_ids[] = $candidateid; diff --git a/lang/en/block_learning_style.php b/lang/en/block_learning_style.php index af20dafbd9a..23c454b0cd0 100644 --- a/lang/en/block_learning_style.php +++ b/lang/en/block_learning_style.php @@ -1,4 +1,9 @@ +