Commit ed724aac authored by Marina Glancy's avatar Marina Glancy
Browse files

MDL-43470 mod_feedback: select courses for mapping

- use course autocomplete element to map courses
- add a tab and navigation node
- clean up some html
- cover course mapping with behat tests
parent e44b55f5
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* AJAX helper for the tag management page.
*
* @module mod_feedback/feedback
* @package mod_feedback
* @copyright 2016 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since 3.1
*/
define(['jquery'], function($) {
return /** @alias module:mod_feedback/feedback */ {
/**
* Initialises course mapping page.
*
* @method initCourseMapping
*/
initCourseMapping: function (elementid) {
// Auto submit form on every change to element with id elementid.
$('body').on('change', elementid, function (e) {
var form = $(e.target).closest('form');
$.ajax(form.attr('action'), {
type: 'POST',
data: form.serialize()
});
if (typeof M.core_formchangechecker != 'undefined') {
M.core_formchangechecker.set_form_submitted();
}
});
}
};
});
......@@ -47,7 +47,6 @@ if (!feedback_can_view_analysis($feedback, $context)) {
$strfeedbacks = get_string("modulenameplural", "feedback");
$strfeedback = get_string("modulename", "feedback");
$PAGE->navbar->add(get_string('analysis', 'feedback'));
$PAGE->set_heading($course->fullname);
$PAGE->set_title($feedback->name);
echo $OUTPUT->header();
......@@ -57,9 +56,6 @@ echo $OUTPUT->heading(format_string($feedback->name));
require('tabs.php');
//print analysed items
echo $OUTPUT->box_start('generalbox boxaligncenter boxwidthwide');
//get the groupid
$myurl = $CFG->wwwroot.'/mod/feedback/analysis.php?id='.$cm->id.'&do_show=analysis';
$mygroupid = groups_get_activity_group($cm, true);
......@@ -127,7 +123,6 @@ if ($check_anonymously) {
'feedback', '', '', 3);
}
echo '</div>';
echo $OUTPUT->box_end();
echo $OUTPUT->footer();
......@@ -28,19 +28,16 @@ require_once("lib.php");
$current_tab = 'analysis';
$id = required_param('id', PARAM_INT); //the POST dominated the GET
$coursefilter = optional_param('coursefilter', '0', PARAM_INT);
$courseitemfilter = optional_param('courseitemfilter', '0', PARAM_INT);
$courseitemfiltertyp = optional_param('courseitemfiltertyp', '0', PARAM_ALPHANUM);
$searchcourse = optional_param('searchcourse', '', PARAM_RAW);
$courseid = optional_param('courseid', false, PARAM_INT);
$url = new moodle_url('/mod/feedback/analysis_course.php', array('id'=>$id));
navigation_node::override_active_url($url);
if ($courseid !== false) {
$url->param('courseid', $courseid);
}
if ($coursefilter !== '0') {
$url->param('coursefilter', $coursefilter);
}
if ($courseitemfilter !== '0') {
$url->param('courseitemfilter', $courseitemfilter);
}
......@@ -52,10 +49,6 @@ if ($searchcourse !== '') {
}
$PAGE->set_url($url);
if (($searchcourse OR $courseitemfilter OR $coursefilter) AND !confirm_sesskey()) {
print_error('invalidsesskey');
}
if (! $cm = get_coursemodule_from_id('feedback', $id)) {
print_error('invalidcoursemodule');
}
......@@ -83,21 +76,18 @@ $strfeedback = get_string("modulename", "feedback");
$PAGE->set_heading($course->fullname);
$PAGE->set_title($feedback->name);
echo $OUTPUT->header();
echo $OUTPUT->heading(format_string($feedback->name));
/// print the tabs
require('tabs.php');
//print the analysed items
echo $OUTPUT->box_start('generalbox boxaligncenter boxwidthwide');
if (has_capability('mod/feedback:viewreports', $context)) {
//button "export to excel"
echo $OUTPUT->container_start('mdl-align');
$aurl = new moodle_url('analysis_to_excel.php',
array('sesskey' => sesskey(),
'id' => $id,
'coursefilter' => $coursefilter));
echo $OUTPUT->container_start('form-buttons');
$aurl = new moodle_url('analysis_to_excel.php', array('sesskey' => sesskey(), 'id' => $id,
'coursefilter' => $courseid));
echo $OUTPUT->single_button($aurl, get_string('export_to_excel', 'feedback'));
echo $OUTPUT->container_end();
}
......@@ -106,7 +96,7 @@ if (has_capability('mod/feedback:viewreports', $context)) {
//lstgroupid is the choosen id
$mygroupid = false;
//get completed feedbacks
$completedscount = feedback_get_completeds_group_count($feedback, $mygroupid, $coursefilter);
$completedscount = feedback_get_completeds_group_count($feedback, $mygroupid, $courseid);
//show the count
echo '<b>'.get_string('completed_feedbacks', 'feedback').': '.$completedscount. '</b><br />';
......@@ -117,15 +107,8 @@ $items = $DB->get_records('feedback_item', $params, 'position');
//show the count
if (is_array($items)) {
echo '<b>'.get_string('questions', 'feedback').': ' .count($items). ' </b><hr />';
echo '<a href="analysis_course.php?id=' . $id . '&courseid='.$courseid.'">';
echo get_string('show_all', 'feedback');
echo '</a>';
} else {
$items=array();
}
echo '<form name="report" method="post" id="analysis-form">';
echo '<div class="mdl-align"><table width="80%" cellpadding="10">';
if ($courseitemfilter > 0) {
$avgvalue = 'avg(value)';
if ($DB->get_dbfamily() == 'postgres') { // TODO: this should be moved to standard sql DML function ;-)
......@@ -140,8 +123,9 @@ if ($courseitemfilter > 0) {
if ($courses = $DB->get_records_sql($sql, array($courseitemfiltertyp, $courseitemfilter))) {
$item = $DB->get_record('feedback_item', array('id'=>$courseitemfilter));
echo '<tr><th colspan="2">'.$item->name.'</th></tr>';
echo '<tr><td><table align="left">';
echo '<h4>'.$item->name.'</h4>';
echo '<div class="clearfix">';
echo '<table>';
echo '<tr><th>Course</th><th>Average</th></tr>';
$sep_dec = get_string('separator_decimal', 'feedback');
$sep_thous = get_string('separator_thousand', 'feedback');
......@@ -157,21 +141,21 @@ if ($courseitemfilter > 0) {
echo '</td>';
echo '</tr>';
}
echo '</table></td></tr>';
echo '</table>';
} else {
echo '<tr><td>'.get_string('noresults').'</td></tr>';
echo '<p>'.get_string('noresults').'</p>';
}
echo '<p><a href="analysis_course.php?id=' . $id . '">';
echo get_string('back');
echo '</a></p>';
} else {
echo '<div class="mdl-align">';
echo '<form name="report" method="get" id="analysis-form">';
echo html_writer::label(get_string('search_course', 'feedback') . ': ', 'searchcourse');
echo '<input id="searchcourse" type="text" name="searchcourse" value="'.s($searchcourse).'"/> ';
echo '<input type="submit" value="'.get_string('search').'"/>';
echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
echo '<input type="hidden" name="id" value="'.$id.'" />';
echo '<input type="hidden" name="courseitemfilter" value="'.$courseitemfilter.'" />';
echo '<input type="hidden" name="courseitemfiltertyp" value="'.$courseitemfiltertyp.'" />';
echo '<input type="hidden" name="courseid" value="'.$courseid.'" />';
echo html_writer::script('', $CFG->wwwroot.'/mod/feedback/feedback.js');
$sql = 'select DISTINCT c.id, c.shortname from {course} c, '.
'{feedback_value} fv, {feedback_item} fi '.
'where c.id = fv.course_id and fv.item = fi.id '.
......@@ -182,9 +166,11 @@ if ($courseitemfilter > 0) {
$params = array($feedback->id, "%$searchcourse%", "%$searchcourse%");
if ($courses = $DB->get_records_sql_menu($sql, $params)) {
echo ' '. html_writer::label(get_string('filter_by_course', 'feedback'), 'coursefilterid'). ': ';
echo html_writer::select($courses, 'coursefilter', $coursefilter,
if (!$courseid) {
$courses = array('' => get_string('choosedots')) + $courses;
}
echo ' '. html_writer::label(get_string('filter_by_course', 'feedback'). ': ', 'coursefilterid');
echo html_writer::select($courses, 'courseid', $courseid,
null, array('id'=>'coursefilterid', 'class' => 'autosubmit'));
$PAGE->requires->yui_module('moodle-core-formautosubmit',
......@@ -192,15 +178,22 @@ if ($courseitemfilter > 0) {
array(array('selectid' => 'coursefilterid', 'nothing' => false))
);
}
if ($courseid) {
echo ' <a href="analysis_course.php?id=' . $id . '">';
echo get_string('show_all', 'feedback');
echo '</a>';
}
echo '</form>';
echo '</div>';
echo '<hr />';
$itemnr = 0;
//print the items in an analysed form
echo '<tr><td>';
foreach ($items as $item) {
if ($item->hasvalue == 0) {
continue;
}
echo '<table width="100%" class="generalbox">';
echo '<table>';
//get the class from item-typ
$itemobj = feedback_get_item_class($item->typ);
$itemnr++;
......@@ -209,25 +202,17 @@ if ($courseitemfilter > 0) {
} else {
$printnr = '';
}
$itemobj->print_analysed($item, $printnr, $mygroupid, $coursefilter);
$itemobj->print_analysed($item, $printnr, $mygroupid, $courseid);
if (preg_match('/rated$/i', $item->typ)) {
$onclick = 'onclick="setcourseitemfilter'.
"(".$item->id.",'".$item->typ."');".
' return false;"';
$anker = '<a href="#" '.$onclick.'>'.
get_string('sort_by_course', 'feedback').
'</a>';
$url = new moodle_url('/mod/feedback/analysis_course.php', array('id' => $id,
'courseitemfilter' => $item->id, 'courseitemfiltertyp' => $item->typ));
$anker = html_writer::link($url, get_string('sort_by_course', 'feedback'));
echo '<tr><td colspan="2">'.$anker.'</td></tr>';
}
echo '</table>';
}
echo '</td></tr>';
}
echo '</table></div>';
echo '</form>';
echo $OUTPUT->box_end();
echo $OUTPUT->footer();
......@@ -15,48 +15,36 @@
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* drops records from feedback_sitecourse_map
* Contains class mod_feedback_course_map_form
*
* @author Andreas Grabs
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package mod_feedback
* @package mod_feedback
* @copyright 2016 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once("../../config.php");
require_once($CFG->dirroot.'/mod/feedback/lib.php');
$id = required_param('id', PARAM_INT);
$cmapid = required_param('cmapid', PARAM_INT);
$url = new moodle_url('/mod/feedback/unmapcourse.php', array('id'=>$id));
if ($cmapid !== '') {
$url->param('cmapid', $cmapid);
}
$PAGE->set_url($url);
if (! $cm = get_coursemodule_from_id('feedback', $id)) {
print_error('invalidcoursemodule');
}
if (! $course = $DB->get_record("course", array("id"=>$cm->course))) {
print_error('coursemisconf');
}
if (! $feedback = $DB->get_record("feedback", array("id"=>$cm->instance))) {
print_error('invalidcoursemodule');
}
$context = context_module::instance($cm->id);
require_capability('mod/feedback:mapcourse', $context);
// cleanup all lost entries after deleting courses or feedbacks
feedback_clean_up_sitecourse_map();
if ($DB->delete_records('feedback_sitecourse_map', array('id'=>$cmapid))) {
$mapurl = new moodle_url('/mod/feedback/mapcourse.php', array('id'=>$id));
redirect ($mapurl->out(false));
} else {
print_error('cannotunmap', 'feedback');
/**
* Form for mapping courses to the feedback
*
* @package mod_feedback
* @copyright 2016 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_feedback_course_map_form extends moodleform {
/**
* Definition of the form
*/
public function definition() {
global $PAGE;
$mform = $this->_form;
$mform->addElement('hidden', 'id');
$mform->setType('id', PARAM_INT);
$options = array('multiple' => true);
$mform->addElement('course', 'mappedcourses', get_string('courses'), $options);
// Automatically submit form in AJAX request every time 'mappedcourses' is modified.
$PAGE->requires->js_call_amd('mod_feedback/feedback', 'initCourseMapping', array('#id_mappedcourses'));
}
}
......@@ -137,11 +137,6 @@ if (isset($error)) {
}
$itemobj->show_editform();
if ($typ!='label') {
$PAGE->requires->js('/mod/feedback/feedback.js');
$PAGE->requires->js_function_call('set_item_focus', Array('id_itemname'));
}
/// Finish the page
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
......
function set_item_focus(itemid) {
var item = document.getElementById(itemid);
if(item){
item.focus();
}
}
function feedbackGo2delete(form) {
form.action = M.cfg.wwwroot+'/mod/feedback/delete_completed.php';
form.submit();
}
function setcourseitemfilter(item, item_typ) {
document.report.courseitemfilter.value = item;
document.report.courseitemfiltertyp.value = item_typ;
document.report.submit();
}
M.mod_feedback = {};
M.mod_feedback.init_sendmessage = function(Y) {
......
completed,mod_feedback
mapcourses_help,mod_feedback
......@@ -152,8 +152,6 @@ $string['mapcourse'] = 'Map feedback to courses';
$string['mapcourse_help'] = 'By default, feedback forms created on your homepage are available site-wide
and will appear in all courses using the feedback block. You can force the feedback form to appear by making it a sticky block or limit the courses in which a feedback form will appear by mapping it to specific courses.';
$string['mapcourses'] = 'Map feedback to courses';
$string['mapcourses_help'] = 'Once you have selected the relevant course(s) from your search,
you can associate them with this feedback using map course(s). Multiple courses may be selected by holding down the Apple or Ctrl key whilst clicking on the course names. A course may be disassociated from a feedback at any time.';
$string['mappedcourses'] = 'Mapped courses';
$string['max_args_exceeded'] = 'Max 6 arguments can be handled, too many arguments for';
$string['minimal'] = 'minimum';
......@@ -291,3 +289,6 @@ $string['viewcompleted_help'] = 'You may view completed feedback forms, searchab
Feedback responses may be exported to Excel.';
// Deprecated since Moodle 3.0.
$string['completed'] = 'completed';
// Deprecated since Moodle 3.1.
$string['mapcourses_help'] = 'Once you have selected the relevant course(s) from your search,
you can associate them with this feedback using map course(s). Multiple courses may be selected by holding down the Apple or Ctrl key whilst clicking on the course names. A course may be disassociated from a feedback at any time.';
......@@ -2851,16 +2851,15 @@ function feedback_get_feedbacks_from_sitecourse_map($courseid) {
}
/**
* gets the courses from table feedback_sitecourse_map.
* Gets the courses from table feedback_sitecourse_map
*
* @global object
* @param int $feedbackid
* @return array the course-records
*/
function feedback_get_courses_from_sitecourse_map($feedbackid) {
global $DB;
$sql = "SELECT f.id, f.courseid, c.fullname, c.shortname
$sql = "SELECT c.id, c.fullname, c.shortname
FROM {feedback_sitecourse_map} f, {course} c
WHERE c.id = f.courseid
AND f.feedbackid = ?
......@@ -2870,6 +2869,27 @@ function feedback_get_courses_from_sitecourse_map($feedbackid) {
}
/**
* Updates the course mapping for the feedback
*
* @param stdClass $feedback
* @param array $courses array of course ids
*/
function feedback_update_sitecourse_map($feedback, $courses) {
global $DB;
if (empty($courses)) {
$courses = array();
}
$currentmapping = $DB->get_fieldset_select('feedback_sitecourse_map', 'courseid', 'feedbackid=?', array($feedback->id));
foreach (array_diff($courses, $currentmapping) as $courseid) {
$DB->insert_record('feedback_sitecourse_map', array('feedbackid' => $feedback->id, 'courseid' => $courseid));
}
foreach (array_diff($currentmapping, $courses) as $courseid) {
$DB->delete_records('feedback_sitecourse_map', array('feedbackid' => $feedback->id, 'courseid' => $courseid));
}
// TODO MDL-53574 add events.
}
/**
* removes non existing courses or feedbacks from sitecourse_map.
* it shouldn't be called all too often
......@@ -3147,7 +3167,7 @@ function feedback_encode_target_url($url) {
function feedback_extend_settings_navigation(settings_navigation $settings,
navigation_node $feedbacknode) {
global $PAGE, $DB;
global $PAGE;
if (!$context = context_module::instance($PAGE->cm->id, IGNORE_MISSING)) {
print_error('badcontext');
......@@ -3176,20 +3196,22 @@ function feedback_extend_settings_navigation(settings_navigation $settings,
'do_show' => 'templates')));
}
if (has_capability('mod/feedback:mapcourse', $context) && $PAGE->course->id == SITEID) {
$feedbacknode->add(get_string('mappedcourses', 'feedback'),
new moodle_url('/mod/feedback/mapcourse.php',
array('id' => $PAGE->cm->id)));
}
if (has_capability('mod/feedback:viewreports', $context)) {
$feedback = $DB->get_record('feedback', array('id'=>$PAGE->cm->instance));
$feedback = $PAGE->activityrecord;
if ($feedback->course == SITEID) {
$feedbacknode->add(get_string('analysis', 'feedback'),
new moodle_url('/mod/feedback/analysis_course.php',
array('id' => $PAGE->cm->id,
'course' => $PAGE->course->id,
'do_show' => 'analysis')));
array('id' => $PAGE->cm->id)));
} else {
$feedbacknode->add(get_string('analysis', 'feedback'),
new moodle_url('/mod/feedback/analysis.php',
array('id' => $PAGE->cm->id,
'course' => $PAGE->course->id,
'do_show' => 'analysis')));
array('id' => $PAGE->cm->id)));
}
$feedbacknode->add(get_string('show_entries', 'feedback'),
......
......@@ -22,65 +22,32 @@
* @package mod_feedback
*/
require_once("../../config.php");
require_once("lib.php");
require_once(__DIR__ . "/../../config.php");
require_once($CFG->dirroot . "/mod/feedback/lib.php");
require_once("$CFG->libdir/tablelib.php");
$id = required_param('id', PARAM_INT); // Course Module ID, or
$searchcourse = optional_param('searchcourse', '', PARAM_NOTAGS);
$coursefilter = optional_param('coursefilter', '', PARAM_INT);
$courseid = optional_param('courseid', false, PARAM_INT);
$id = required_param('id', PARAM_INT); // Course Module ID.
$url = new moodle_url('/mod/feedback/mapcourse.php', array('id'=>$id));
if ($searchcourse !== '') {
$url->param('searchcourse', $searchcourse);
}
if ($coursefilter !== '') {
$url->param('coursefilter', $coursefilter);
}
if ($courseid !== false) {
$url->param('courseid', $courseid);
}
$PAGE->set_url($url);
if (($formdata = data_submitted()) AND !confirm_sesskey()) {
print_error('invalidsesskey');
}
$current_tab = 'mapcourse';
if (! $cm = get_coursemodule_from_id('feedback', $id)) {
print_error('invalidcoursemodule');
}
if (! $course = $DB->get_record("course", array("id"=>$cm->course))) {
print_error('coursemisconf');
}
if (! $feedback = $DB->get_record("feedback", array("id"=>$cm->instance))) {
print_error('invalidcoursemodule');
}
$context = context_module::instance($cm->id);
list($course, $cm) = get_course_and_cm_from_cmid($id, 'feedback');
require_login($course, true, $cm);
$feedback = $PAGE->activityrecord;
$context = context_module::instance($cm->id);
require_capability('mod/feedback:mapcourse', $context);
if ($coursefilter) {
$map = new stdClass;
$map->feedbackid = $feedback->id;
$map->courseid = $coursefilter;
// insert a map only if it does exists yet
$sql = "SELECT id, feedbackid
FROM {feedback_sitecourse_map}
WHERE feedbackid = ? AND courseid = ?";
if (!$DB->get_records_sql($sql, array($map->feedbackid, $map->courseid))) {
$DB->insert_record('feedback_sitecourse_map', $map);
}
$coursemap = array_keys(feedback_get_courses_from_sitecourse_map($feedback->id));
$form = new mod_feedback_course_map_form();
$form->set_data(array('id' => $cm->id, 'mappedcourses' => $coursemap));
if ($data = $form->get_data()) {
feedback_update_sitecourse_map($feedback, $data->mappedcourses);
}
/// Print the page header
// Print the page header.
$strfeedbacks = get_string("modulenameplural", "feedback");
$strfeedback = get_string("modulename", "feedback");
......@@ -91,64 +58,8 @@ echo $OUTPUT->heading(format_string($feedback->name));
require('tabs.php');
echo $OUTPUT->box(get_string('mapcourseinfo', 'feedback'), 'generalbox boxaligncenter boxwidthwide');
echo $OUTPUT->box_start('generalbox boxaligncenter boxwidthwide');
echo '<form method="post">';
echo '<input type="hidden" name="id" value="'.$id.'" />';
echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
$sql = "select c.id, c.shortname
from {course} c
where ".$DB->sql_like('c.shortname', '?', false)."
OR ".$DB->sql_like('c.fullname', '?', false);
$params = array("%{$searchcourse}%", "%{$searchcourse}%");
if (($courses = $DB->get_records_sql_menu($sql, $params)) && !empty($searchcourse)) {
echo ' '. html_writer::label(get_string('courses'), 'menucoursefilter', false). ': ';
echo html_writer::select($courses, 'coursefilter', $coursefilter);
echo '<input type="submit" value="'.get_string('mapcourse', 'feedback').'"/>';
echo $OUTPUT->help_icon('mapcourses', 'feedback');
echo '<input type="button" '.
'value="'.get_string('searchagain').'" '.
'onclick="document.location=\'mapcourse.php?id='.$id.'\'"/>';
echo '<input type="hidden" name="searchcourse" value="'.s($searchcourse).'"/>';
echo '<input type="hidden" name="feedbackid" value="'.$feedback->id.'"/>';
echo $OUTPUT->help_icon('searchcourses', 'feedback');
} else {
echo '<input type="text" name="searchcourse" value="'.s($searchcourse).'"/> ';
echo '<input type="submit" value="'.get_string('searchcourses').'"/>';
echo $OUTPUT->help_icon('searchcourses', 'feedback');
}
echo '</form>';
if ($coursemap = feedback_get_courses_from_sitecourse_map($feedback->id)) {
$table = new flexible_table('coursemaps');
$table->baseurl = $url;
$table->define_columns( array('course'));
$table->define_headers( array(get_string('mappedcourses', 'feedback')));
$table->setup();
$unmapurl = new moodle_url('/mod/feedback/unmapcourse.php');
foreach ($coursemap as $cmap) {