1 |
efrain |
1 |
<?php
|
|
|
2 |
// This file is part of Moodle - http://moodle.org/
|
|
|
3 |
//
|
|
|
4 |
// Moodle is free software: you can redistribute it and/or modify
|
|
|
5 |
// it under the terms of the GNU General Public License as published by
|
|
|
6 |
// the Free Software Foundation, either version 3 of the License, or
|
|
|
7 |
// (at your option) any later version.
|
|
|
8 |
//
|
|
|
9 |
// Moodle is distributed in the hope that it will be useful,
|
|
|
10 |
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
11 |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
12 |
// GNU General Public License for more details.
|
|
|
13 |
//
|
|
|
14 |
// You should have received a copy of the GNU General Public License
|
|
|
15 |
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
|
16 |
|
|
|
17 |
/**
|
|
|
18 |
* The Gradebook setup page.
|
|
|
19 |
*
|
|
|
20 |
* @package core_grades
|
|
|
21 |
* @copyright 2008 Nicolas Connault
|
|
|
22 |
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
23 |
*/
|
|
|
24 |
|
|
|
25 |
define('NO_OUTPUT_BUFFERING', true); // The progress bar may be used here.
|
|
|
26 |
|
|
|
27 |
require_once '../../../config.php';
|
|
|
28 |
require_once $CFG->dirroot.'/grade/lib.php';
|
|
|
29 |
require_once $CFG->dirroot.'/grade/report/lib.php'; // for preferences
|
|
|
30 |
require_once $CFG->dirroot.'/grade/edit/tree/lib.php';
|
|
|
31 |
|
|
|
32 |
$courseid = required_param('id', PARAM_INT);
|
|
|
33 |
$action = optional_param('action', 0, PARAM_ALPHA);
|
|
|
34 |
$eid = optional_param('eid', 0, PARAM_ALPHANUM);
|
|
|
35 |
$weightsadjusted = optional_param('weightsadjusted', 0, PARAM_INT);
|
|
|
36 |
|
|
|
37 |
$url = new moodle_url('/grade/edit/tree/index.php', array('id' => $courseid));
|
|
|
38 |
$PAGE->set_url($url);
|
|
|
39 |
$PAGE->set_pagelayout('admin');
|
|
|
40 |
|
|
|
41 |
/// Make sure they can even access this course
|
|
|
42 |
if (!$course = $DB->get_record('course', array('id' => $courseid))) {
|
|
|
43 |
throw new \moodle_exception('invalidcourseid');
|
|
|
44 |
}
|
|
|
45 |
|
|
|
46 |
require_login($course);
|
|
|
47 |
$context = context_course::instance($course->id);
|
|
|
48 |
require_capability('moodle/grade:manage', $context);
|
|
|
49 |
|
|
|
50 |
$PAGE->requires->js_call_amd('core_grades/edittree_index', 'init', [$courseid, $USER->id]);
|
|
|
51 |
$PAGE->requires->js_call_amd('core_grades/gradebooksetup_forms', 'init');
|
|
|
52 |
|
|
|
53 |
$decsep = get_string('decsep', 'langconfig');
|
|
|
54 |
// This setting indicates if we should use algorithm prior to MDL-49257 fix for calculating extra credit weights.
|
|
|
55 |
$gradebookcalculationfreeze = (int) get_config('core', 'gradebook_calculations_freeze_' . $courseid);
|
|
|
56 |
$oldextracreditcalculation = $gradebookcalculationfreeze && ($gradebookcalculationfreeze <= 20150619);
|
|
|
57 |
$PAGE->requires->js_call_amd('core_grades/edittree_weights', 'init', [$decsep, $oldextracreditcalculation]);
|
|
|
58 |
|
|
|
59 |
/// return tracking object
|
|
|
60 |
$gpr = new grade_plugin_return(array('type'=>'edit', 'plugin'=>'tree', 'courseid'=>$courseid));
|
|
|
61 |
$returnurl = $gpr->get_return_url(null);
|
|
|
62 |
|
|
|
63 |
// get the grading tree object
|
|
|
64 |
// note: total must be first for moving to work correctly, if you want it last moving code must be rewritten!
|
|
|
65 |
$gtree = new grade_tree($courseid, false, false);
|
|
|
66 |
|
|
|
67 |
if (empty($eid)) {
|
|
|
68 |
$element = null;
|
|
|
69 |
$object = null;
|
|
|
70 |
|
|
|
71 |
} else {
|
|
|
72 |
if (!$element = $gtree->locate_element($eid)) {
|
|
|
73 |
throw new \moodle_exception('invalidelementid', '', $returnurl);
|
|
|
74 |
}
|
|
|
75 |
$object = $element['object'];
|
|
|
76 |
}
|
|
|
77 |
|
|
|
78 |
$switch = grade_get_setting($course->id, 'aggregationposition', $CFG->grade_aggregationposition);
|
|
|
79 |
|
|
|
80 |
$strgrades = get_string('grades');
|
|
|
81 |
$strgraderreport = get_string('graderreport', 'grades');
|
|
|
82 |
|
|
|
83 |
$moving = false;
|
|
|
84 |
$movingeid = false;
|
|
|
85 |
|
|
|
86 |
if ($action == 'moveselect') {
|
|
|
87 |
if ($eid and confirm_sesskey()) {
|
|
|
88 |
$movingeid = $eid;
|
|
|
89 |
$moving=true;
|
|
|
90 |
}
|
|
|
91 |
}
|
|
|
92 |
|
|
|
93 |
$gradeedittree = new grade_edit_tree($gtree, $movingeid, $gpr);
|
|
|
94 |
|
|
|
95 |
switch ($action) {
|
|
|
96 |
case 'duplicate':
|
|
|
97 |
if ($eid and confirm_sesskey()) {
|
|
|
98 |
if (!$el = $gtree->locate_element($eid)) {
|
|
|
99 |
throw new \moodle_exception('invalidelementid', '', $returnurl);
|
|
|
100 |
}
|
|
|
101 |
|
|
|
102 |
$object->duplicate();
|
|
|
103 |
redirect($returnurl);
|
|
|
104 |
}
|
|
|
105 |
break;
|
|
|
106 |
|
|
|
107 |
case 'delete':
|
|
|
108 |
if ($eid && confirm_sesskey()) {
|
|
|
109 |
if (!$gradeedittree->element_deletable($element)) {
|
|
|
110 |
// no deleting of external activities - they would be recreated anyway!
|
|
|
111 |
// exception is activity without grading or misconfigured activities
|
|
|
112 |
break;
|
|
|
113 |
}
|
|
|
114 |
$confirm = optional_param('confirm', 0, PARAM_BOOL);
|
|
|
115 |
|
|
|
116 |
if ($confirm) {
|
|
|
117 |
$object->delete('grade/report/grader/category');
|
|
|
118 |
redirect($returnurl);
|
|
|
119 |
|
|
|
120 |
}
|
|
|
121 |
}
|
|
|
122 |
break;
|
|
|
123 |
|
|
|
124 |
case 'autosort':
|
|
|
125 |
//TODO: implement autosorting based on order of mods on course page, categories first, manual items last
|
|
|
126 |
break;
|
|
|
127 |
|
|
|
128 |
case 'move':
|
|
|
129 |
if ($eid and confirm_sesskey()) {
|
|
|
130 |
$moveafter = required_param('moveafter', PARAM_ALPHANUM);
|
|
|
131 |
$first = optional_param('first', false, PARAM_BOOL); // If First is set to 1, it means the target is the first child of the category $moveafter
|
|
|
132 |
|
|
|
133 |
if(!$after_el = $gtree->locate_element($moveafter)) {
|
|
|
134 |
throw new \moodle_exception('invalidelementid', '', $returnurl);
|
|
|
135 |
}
|
|
|
136 |
|
|
|
137 |
$after = $after_el['object'];
|
|
|
138 |
$sortorder = $after->get_sortorder();
|
|
|
139 |
|
|
|
140 |
if (!$first) {
|
|
|
141 |
$parent = $after->get_parent_category();
|
|
|
142 |
$object->set_parent($parent->id);
|
|
|
143 |
} else {
|
|
|
144 |
$object->set_parent($after->id);
|
|
|
145 |
}
|
|
|
146 |
|
|
|
147 |
$object->move_after_sortorder($sortorder);
|
|
|
148 |
|
|
|
149 |
redirect($returnurl);
|
|
|
150 |
}
|
|
|
151 |
break;
|
|
|
152 |
|
|
|
153 |
default:
|
|
|
154 |
break;
|
|
|
155 |
}
|
|
|
156 |
|
|
|
157 |
// If we go straight to the db to update an element we need to recreate the tree as
|
|
|
158 |
// $gradeedittree has already been constructed.
|
|
|
159 |
// Ideally we could do the updates through $gradeedittree to avoid recreating it.
|
|
|
160 |
$recreatetree = false;
|
|
|
161 |
|
|
|
162 |
if ($data = data_submitted() and confirm_sesskey()) {
|
|
|
163 |
// Perform bulk actions first
|
|
|
164 |
if (!empty($data->bulkmove)) {
|
|
|
165 |
$elements = array();
|
|
|
166 |
|
|
|
167 |
foreach ($data as $key => $value) {
|
|
|
168 |
if (preg_match('/select_(ig[0-9]*)/', $key, $matches)) {
|
|
|
169 |
$elements[] = $matches[1];
|
|
|
170 |
}
|
|
|
171 |
}
|
|
|
172 |
|
|
|
173 |
$gradeedittree->move_elements($elements, $returnurl);
|
|
|
174 |
}
|
|
|
175 |
|
|
|
176 |
// Update weights (extra credits) on categories and items.
|
|
|
177 |
foreach ($data as $key => $value) {
|
|
|
178 |
if (preg_match('/^weight_([0-9]+)$/', $key, $matches)) {
|
|
|
179 |
$aid = $matches[1];
|
|
|
180 |
|
|
|
181 |
$value = unformat_float($value);
|
|
|
182 |
$value = clean_param($value, PARAM_FLOAT);
|
|
|
183 |
|
|
|
184 |
$grade_item = grade_item::fetch(array('id' => $aid, 'courseid' => $courseid));
|
|
|
185 |
|
|
|
186 |
// Convert weight to aggregation coef2.
|
|
|
187 |
$aggcoef = $grade_item->get_coefstring();
|
|
|
188 |
if ($aggcoef == 'aggregationcoefextraweightsum') {
|
|
|
189 |
// The field 'weight' should only be sent when the checkbox 'weighoverride' is checked,
|
|
|
190 |
// so there is not need to set weightoverride here, it is done below.
|
|
|
191 |
$value = $value / 100.0;
|
|
|
192 |
$grade_item->aggregationcoef2 = $value;
|
|
|
193 |
} else if ($aggcoef == 'aggregationcoefweight' || $aggcoef == 'aggregationcoefextraweight') {
|
|
|
194 |
$grade_item->aggregationcoef = $value;
|
|
|
195 |
}
|
|
|
196 |
|
|
|
197 |
$grade_item->update();
|
|
|
198 |
|
|
|
199 |
$recreatetree = true;
|
|
|
200 |
|
|
|
201 |
// Grade item checkbox inputs.
|
|
|
202 |
} elseif (preg_match('/^(weightoverride)_([0-9]+)$/', $key, $matches)) {
|
|
|
203 |
$param = $matches[1];
|
|
|
204 |
$aid = $matches[2];
|
|
|
205 |
$value = clean_param($value, PARAM_BOOL);
|
|
|
206 |
|
|
|
207 |
$grade_item = grade_item::fetch(array('id' => $aid, 'courseid' => $courseid));
|
|
|
208 |
$grade_item->$param = $value;
|
|
|
209 |
|
|
|
210 |
$grade_item->update();
|
|
|
211 |
|
|
|
212 |
$recreatetree = true;
|
|
|
213 |
}
|
|
|
214 |
}
|
|
|
215 |
}
|
|
|
216 |
|
|
|
217 |
$originalweights = grade_helper::fetch_all_natural_weights_for_course($courseid);
|
|
|
218 |
|
|
|
219 |
/**
|
|
|
220 |
* Callback function to adjust the URL if weights changed after the
|
|
|
221 |
* regrade.
|
|
|
222 |
*
|
|
|
223 |
* @param int $courseid The course ID
|
|
|
224 |
* @param array $originalweights The weights before the regrade
|
|
|
225 |
* @param int $weightsadjusted Whether weights have been adjusted
|
|
|
226 |
* @return moodle_url A URL to redirect to after regrading when a progress bar is displayed.
|
|
|
227 |
*/
|
|
|
228 |
$grade_edit_tree_index_checkweights = function() use ($courseid, $originalweights, &$weightsadjusted) {
|
|
|
229 |
global $PAGE;
|
|
|
230 |
|
|
|
231 |
$alteredweights = grade_helper::fetch_all_natural_weights_for_course($courseid);
|
|
|
232 |
if (array_diff($originalweights, $alteredweights)) {
|
|
|
233 |
$weightsadjusted = 1;
|
|
|
234 |
return new moodle_url($PAGE->url, array('weightsadjusted' => $weightsadjusted));
|
|
|
235 |
}
|
|
|
236 |
return $PAGE->url;
|
|
|
237 |
};
|
|
|
238 |
|
|
|
239 |
if (grade_regrade_final_grades_if_required($course, $grade_edit_tree_index_checkweights)) {
|
|
|
240 |
$recreatetree = true;
|
|
|
241 |
}
|
|
|
242 |
|
|
|
243 |
$actionbar = new \core_grades\output\gradebook_setup_action_bar($context);
|
|
|
244 |
print_grade_page_head($courseid, 'settings', 'setup', false,
|
|
|
245 |
false, false, true, null, null, null, $actionbar);
|
|
|
246 |
|
|
|
247 |
// Print Table of categories and items
|
|
|
248 |
echo $OUTPUT->box_start('gradetreebox generalbox');
|
|
|
249 |
|
|
|
250 |
// Did we update something in the db and thus invalidate $gradeedittree?
|
|
|
251 |
if ($recreatetree) {
|
|
|
252 |
$gradeedittree = new grade_edit_tree($gtree, $movingeid, $gpr);
|
|
|
253 |
}
|
|
|
254 |
|
|
|
255 |
$tpldata = (object) [
|
|
|
256 |
'actionurl' => $returnurl,
|
|
|
257 |
'sesskey' => sesskey(),
|
|
|
258 |
'movingmodeenabled' => $moving,
|
|
|
259 |
'courseid' => $courseid
|
|
|
260 |
];
|
|
|
261 |
|
|
|
262 |
// Check to see if we have a normalisation message to send.
|
|
|
263 |
if ($weightsadjusted) {
|
|
|
264 |
$notification = new \core\output\notification(get_string('weightsadjusted', 'grades'), \core\output\notification::NOTIFY_INFO);
|
|
|
265 |
$tpldata->notification = $notification->export_for_template($OUTPUT);
|
|
|
266 |
}
|
|
|
267 |
|
|
|
268 |
$tpldata->table = html_writer::table($gradeedittree->table);
|
|
|
269 |
|
|
|
270 |
// If not in moving mode and there is more than one grade category, then initialise the bulk action module.
|
|
|
271 |
if (!$moving && count($gradeedittree->categories) > 1) {
|
|
|
272 |
$PAGE->requires->js_call_amd('core_grades/bulkactions/edit/tree/bulk_actions', 'init', [$courseid]);
|
|
|
273 |
}
|
|
|
274 |
|
|
|
275 |
$footercontent = $OUTPUT->render_from_template('core_grades/edit_tree_sticky_footer', $tpldata);
|
|
|
276 |
$stickyfooter = new core\output\sticky_footer($footercontent);
|
|
|
277 |
$tpldata->stickyfooter = $OUTPUT->render($stickyfooter);
|
|
|
278 |
|
|
|
279 |
echo $OUTPUT->render_from_template('core_grades/edit_tree', $tpldata);
|
|
|
280 |
|
|
|
281 |
echo $OUTPUT->box_end();
|
|
|
282 |
|
|
|
283 |
$PAGE->requires->js_call_amd('core_form/changechecker', 'watchFormById', ['gradetreeform']);
|
|
|
284 |
|
|
|
285 |
echo $OUTPUT->footer();
|
|
|
286 |
die;
|