ÿØÿà JFIF    ÿÛ „  ( %!1"%)+...383-7(-.+  -%%--------+------------/----------------------------ÿÀ  ¶" ÿÄ     ÿÄ @    !1AQaq‘¡"±ÁÑð2’BRráS‚ñ3b¢CT²Ò #cÿÄ    ÿÄ '   1!AQ2a"‘±#ÿÚ   ? ò(/p‘—–¢·,M|¯G»R£üešT. ôŸEoCFñ‡ 4ÔkåóxR|µÁ¨ÓDà!Ç´µ˜‡p ÍQ‹§!þž^õWsŒy  åÁ§$á«%ºq˜™JyyË0y«^Ϗ -3ÃÏËÈ£Æ\P’ä„s&•‹Üs üô'àWt¹pñCp$bÚF`·7.^)¾!)ÒÙ˜–ÅÏš^h¾ø³·ìe±ŒaeÖ—Í,e=B}Jéߦ$$½—šÙÜCC¯¶è8β–Åkèwx LÉngT±æ¹£Ócœ¿Í/©Éÿ ‰ÚCÒ­mq;Tt¶–}0"zÖPcd1š­¤tˆ„ÐÓâqú[™öUCŠ2޿د­É(¸ßúçšmW,Ò ºf‹Ån™ÄLêÁ8üÁkÁ€L¥ÍuJ1œôrÂSŒÓöt!7EUíïb)AËi¬â-ÙHk8òÁiXYwÅ)O^"ÇØriÛô{»wRUKÙÏÚlÓ¬€‚’שT ] ªb—ÊîKC^×M“xùäñz¬zJ× {4[¹M'IÆeH1LCWÕ]œûº WDæà°OÅ‚¹c²ÞuA–rL`HšpCez‡V’— Ž‚Ë„–ñ­'µR0ÕÖ=À:±G»C\nÆÉ5*¢ †3šhQ4ýÒi$Õ1"Ü]¢p-q3zcQ$ªö¨¥æf›‘»´ÝÚHá„e²JÇ–iÊ:·à¬Øc5Åk»Laª*wi [0Ówk ¦a¦0Õ²ÅÅ‚T¸’³q:Æ<¹ú Ò˜Æ}2VE•Ѐp¡Ô3Úº …h³^LJºçì¥Ánó|˜ÖˆÎˆ+« ¦dø‘Mº–d\ªDðÛ„Ì×LÝ——Ð9Ç|•FèçVm™¨€™3õS–9Äå¢<̉Ìm¢±`·º˜&Z²ž Ë®D«¶»6™‰©­HÈ Àá«h­i³04æ’hxœi3ï¨(ÓL·†‹¶ËpŒ ƒÜÒ? :õdsÀ –3æÓ·Üjâ¬9—]8n¼&]I F£¼…ì–—Œ0v°LǐÅfÛ2TÖ’aµ˜N@ÖBSÏ^à´4-±°b×nÈÈœÆD=KdŒø…ÄÈK,›ÖR§fjü$^ÔÎ!„6^ s:Üun\ôkCÞò÷<—Lúl Í›G¹ÀPá¼Nc¡šÒ…ÙWͳœ±vÊáЄ×9‹P‰cEØ‘™›µŸP<×g1 º©Ø¬wiÁ^l5× I/';Ë+´Z‡ÓÄFÕp[Èð’ HÄ ó 9¬Dl4“é±Ï”V=^Hð;žLädÉa‚°Ë,#/Ž~²BÔÛ QãñQtIeórVB,ÏÂf‹4#Μc‹É3ZIÖ Œ¢¥·üxa‡Ãçè„jŠ[5 Ä!U?,3žÎ×…ðdÓ÷háŠA‰ìÄ4÷ÃÜZÃE{‰®+W\ZÍE[‰»µk»MÝ­`¢©†˜ÃV‹-f¢¡†¢a«eŠ%«Y¨©q%jâd,Ôq`) ¤¦…šˆ ¦E>0Ùb²t–Ÿ  ÷jsςҒKÈcø(é›;k(’#ôŒQ ™C> õ³M9óºÆµ¤çâ;uÀ)5>RÔ¸fÓ~Ȧ—’-$fe«"FâUˆÊwµPÏu w+"åÉó¨6s™Û9(À¨ù©hE°½†n`m~“@©›p!Vkhµ Á1šbEqqÊ@ºèl›2 –Yã%‘‚hÛ{a¶‡ÅÞM²¬çL34®Çl]®‹ŽØŒm'RÜe35æöˆ­Á­ cP9šeÁköo´L³Ì=p8]”‡B¶,šº|ɏdzXˆØk*ÇÚ{#ñ‰pã(€¶‡9ý'šÜ³½¯kƒ†Â’ëSOƒ•Å®H6+a¢µˆ‹Y¨b˜b+XˆÖ!°u!¢"µˆˆn2ˆ Å0Ä`Å Ô®c(ÓÜGº•Ä»Œ âWî%q.áÐÄ÷î%umÍ£+ÜQ,Vn¦-GphU¸¢X­&,Gt ·K¢ÅÅ·¥RÄÊÉbKn N­R2g$VµÕó Qr#–ÓZUÏð0ÎtÙÀœz ëöo“Îk¯n„T°"SÞ+³/…€½"ižãO%Í%&Δâ‘ËØ´IˆÓ°Ë‰ÞM3.™yçÁzit›`4C`k¢¼á!álÄï sÂ[×!¤l¾æo"ƒIÔ'@ªu)3›ô®“@Z à¾] üœ'‡4¸PÞkÁ-qÄH‰ÓÍBd´ÿ —¹°ÄA&ÊUC„öz‚‚RæŠtf¬>˜ÑidšEçÑL÷áý¡V±h§­c¿¥¨ƒ@g¼…Ýö^ÞËc.ž„æ“I[ ª+·zÐÒÚ,½Ð»¶€C›7jk$๖€Ž¾ÅÙpp¶ÍþéΤ ½ØÎŒq ñpæžÅ¥#Øâ49ÃÂK^MYV“,¶Éz_òæ:…;Õ«¦I3âJ£¤û5$7Èxün\â]Êe¯(Ÿ&Ž‹¶2<6Ćf× æ¢ƱqWı[(ÙÂŽç‚T7¨@ÈÉÍß‚ïÙ"&3TYlGށ†)†#5ŠA©^T2ÆÁ†)©†©†¤yGXÁ†©"†)©¼ÅV WS†#©]R–r‹q5ÕbâWþBh¯u+ªÅÄ×ü„nÑ^êkªÁj‰jeœWˆÕÕ`µDµ2Ì+Ä µDµµ5Ô{¤Þ0RF’H÷AÛ<ÊÁ§ ƈæ0Ñ£ê4 öÚµ[¬/1²€â<-¬šö²r×0o×w ´xÐÒË„ g01¬„åEM蛇Ã\6µE›‘fÙO k÷CºnÓ1`iîÒ² áÂ!ÑLÄÿ ³¬\õ*:M=Ç»c‹2[‰ØsÕLò\ŒK L^™üÄN\¶#» Ç\……ßïIœÉ$¸ÎñÍ®ÔHÇ Ûí/´>Li”„†g^$ 7NÍÑÑ2M2ªNg=k½Ð}Ÿl6‡ —JWˆ”ÞéT·+µ¦üÂFè¢Vs6ýÆ4»2+2IÝ9ô9®ÇGÙÇ1+Ä8;*Õ§ž½D*îÑWÚçá3Ma¤·ˆ¥t}š²Ü…Q+Æüµ8HíS”üxGéÁˆ¦Ël„éÈ 5 µÄƒ6äÜ(kIš¯S†fÖ&¸®×v|Äyˆ*禌Éù@7-®Ä[]ÎÖ¿ê`º…=:…žJVnݳvðÖKE¼‚ »mà ÷f®:Ë ™–4rð¡4` ܹçž~‹C=œ…¾Ïj|VDtK.ƒ"9 Ï ­–éXá³6WÒïIL-Ї Ç PK\scale/edit_form.phpnu[. /** * Edit form for grade scales * * @package core_grades * @copyright 2007 Petr Skoda * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ if (!defined('MOODLE_INTERNAL')) { die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page } require_once $CFG->libdir.'/formslib.php'; class edit_scale_form extends moodleform { function definition() { global $CFG; $mform =& $this->_form; // visible elements $mform->addElement('header', 'general', get_string('scale')); $mform->addElement('text', 'name', get_string('name'), 'size="40"'); $mform->addRule('name', get_string('required'), 'required', null, 'client'); $mform->setType('name', PARAM_TEXT); $mform->addElement('advcheckbox', 'standard', get_string('scalestandard')); $mform->addHelpButton('standard', 'scalestandard'); $mform->addElement('static', 'used', get_string('used')); $mform->addElement('textarea', 'scale', get_string('scale'), array('cols'=>50, 'rows'=>2)); $mform->addHelpButton('scale', 'scale'); $mform->addRule('scale', get_string('required'), 'required', null, 'client'); $mform->setType('scale', PARAM_TEXT); $mform->addElement('editor', 'description_editor', get_string('description'), null, $this->_customdata['editoroptions']); // hidden params $mform->addElement('hidden', 'id', 0); $mform->setType('id', PARAM_INT); $mform->addElement('hidden', 'courseid', 0); $mform->setType('courseid', PARAM_INT); /// add return tracking info $gpr = $this->_customdata['gpr']; $gpr->add_mform_elements($mform); //------------------------------------------------------------------------------- // buttons $this->add_action_buttons(); } /// tweak the form - depending on existing data function definition_after_data() { global $CFG; $mform =& $this->_form; $courseid = $mform->getElementValue('courseid'); if ($id = $mform->getElementValue('id')) { $scale = grade_scale::fetch(array('id'=>$id)); $used = $scale->is_used(); if ($used) { $mform->hardFreeze('scale'); } if (empty($courseid)) { $mform->hardFreeze('standard'); } else if (!has_capability('moodle/course:managescales', context_system::instance())) { //if they dont have managescales at system level the shouldnt be allowed to make scales standard (or not standard) $mform->hardFreeze('standard'); } else if ($used and !empty($scale->courseid)) { $mform->hardFreeze('standard'); } $usedstr = $scale->is_used() ? get_string('yes') : get_string('no'); $used_el =& $mform->getElement('used'); $used_el->setValue($usedstr); } else { $mform->removeElement('used'); if (empty($courseid) or !has_capability('moodle/course:managescales', context_system::instance())) { $mform->hardFreeze('standard'); } } } /// perform extra validation before submission function validation($data, $files) { global $CFG, $COURSE, $DB; $errors = parent::validation($data, $files); // we can not allow 2 scales with the same exact scale as this creates // problems for backup/restore $old = grade_scale::fetch(array('id'=>$data['id'])); if (array_key_exists('standard', $data)) { if (empty($data['standard'])) { $courseid = $COURSE->id; } else { $courseid = 0; } } else { $courseid = $old->courseid; } if (array_key_exists('scale', $data)) { $scalearray = explode(',', $data['scale']); $scalearray = array_map('trim', $scalearray); $scaleoptioncount = count($scalearray); if (count($scalearray) < 1) { $errors['scale'] = get_string('badlyformattedscale', 'grades'); } else { $thescale = implode(',',$scalearray); //this check strips out whitespace from the scale we're validating but not from those already in the DB $count = $DB->count_records_select('scale', "courseid=:courseid AND ".$DB->sql_compare_text('scale', core_text::strlen($thescale)).'=:scale', array('courseid'=>$courseid, 'scale'=>$thescale)); if ($count) { //if this is a new scale but we found a duplice in the DB //or we found a duplicate in another course report the error if (empty($old->id) or $old->courseid != $courseid) { $errors['scale'] = get_string('duplicatescale', 'grades'); } else if ($old->scale !== $thescale and $old->scale !== $data['scale']) { //if the old scale from DB is different but we found a duplicate then we're trying to modify a scale to be a duplicate $errors['scale'] = get_string('duplicatescale', 'grades'); } } } } return $errors; } } PK\m-q&&scale/edit.phpnu[. /** * Edit page for grade scales * * @package core_grades * @copyright 2007 Petr Skoda * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ require_once '../../../config.php'; require_once $CFG->dirroot.'/grade/lib.php'; require_once $CFG->dirroot.'/grade/report/lib.php'; require_once 'edit_form.php'; $courseid = optional_param('courseid', 0, PARAM_INT); $id = optional_param('id', 0, PARAM_INT); $PAGE->set_url('/grade/edit/scale/edit.php', array('id' => $id, 'courseid' => $courseid)); $PAGE->set_pagelayout('admin'); navigation_node::override_active_url(new moodle_url('/grade/edit/scale/index.php', array('id' => $courseid))); $systemcontext = context_system::instance(); // a bit complex access control :-O if ($id) { /// editing existing scale if (!$scale_rec = $DB->get_record('scale', array('id' => $id))) { throw new \moodle_exception('invalidscaleid'); } if ($scale_rec->courseid) { $scale_rec->standard = 0; if (!$course = $DB->get_record('course', array('id' => $scale_rec->courseid))) { throw new \moodle_exception('invalidcourseid'); } require_login($course); $context = context_course::instance($course->id); require_capability('moodle/course:managescales', $context); $courseid = $course->id; } else { if ($courseid) { if (!$course = $DB->get_record('course', array('id' => $courseid))) { throw new \moodle_exception('invalidcourseid'); } } $scale_rec->standard = 1; $scale_rec->courseid = $courseid; require_login($courseid); require_capability('moodle/course:managescales', $systemcontext); } } else if ($courseid){ /// adding new scale from course if (!$course = $DB->get_record('course', array('id' => $courseid))) { throw new \moodle_exception('invalidcourseid'); } $scale_rec = new stdClass(); $scale_rec->standard = 0; $scale_rec->courseid = $courseid; require_login($course); $context = context_course::instance($course->id); require_capability('moodle/course:managescales', $context); } else { /// adding new scale from admin section $scale_rec = new stdClass(); $scale_rec->standard = 1; $scale_rec->courseid = 0; require_login(); require_capability('moodle/course:managescales', $systemcontext); } if (!$courseid) { require_once $CFG->libdir.'/adminlib.php'; admin_externalpage_setup('scales'); $PAGE->set_primary_active_tab('siteadminnode'); } // default return url $gpr = new grade_plugin_return(); $returnurl = $gpr->get_return_url('index.php?id='.$courseid); $editoroptions = array( 'maxfiles' => EDITOR_UNLIMITED_FILES, 'maxbytes' => $CFG->maxbytes, 'trusttext' => false, 'noclean' => true, 'context' => $systemcontext ); if (!empty($scale_rec->id)) { $editoroptions['subdirs'] = file_area_contains_subdirs($systemcontext, 'grade', 'scale', $scale_rec->id); $scale_rec = file_prepare_standard_editor($scale_rec, 'description', $editoroptions, $systemcontext, 'grade', 'scale', $scale_rec->id); } else { $editoroptions['subdirs'] = false; $scale_rec = file_prepare_standard_editor($scale_rec, 'description', $editoroptions, $systemcontext, 'grade', 'scale', null); } $mform = new edit_scale_form(null, compact('gpr', 'editoroptions')); $mform->set_data($scale_rec); if ($mform->is_cancelled()) { redirect($returnurl); } else if ($data = $mform->get_data()) { $scale = new grade_scale(array('id'=>$id)); $data->userid = $USER->id; if (empty($scale->id)) { $data->description = $data->description_editor['text']; $data->descriptionformat = $data->description_editor['format']; grade_scale::set_properties($scale, $data); if (!has_capability('moodle/grade:manage', $systemcontext)) { $data->standard = 0; } $scale->courseid = !empty($data->standard) ? 0 : $courseid; $scale->insert(); $data = file_postupdate_standard_editor($data, 'description', $editoroptions, $systemcontext, 'grade', 'scale', $scale->id); $DB->set_field($scale->table, 'description', $data->description, array('id'=>$scale->id)); } else { $data = file_postupdate_standard_editor($data, 'description', $editoroptions, $systemcontext, 'grade', 'scale', $id); grade_scale::set_properties($scale, $data); if (isset($data->standard)) { $scale->courseid = !empty($data->standard) ? 0 : $courseid; } else { unset($scale->courseid); // keep previous } $scale->update(); } redirect($returnurl); } $heading = $id ? get_string('editscale', 'grades') : get_string('addscale', 'grades'); $PAGE->navbar->add($heading); print_grade_page_head($COURSE->id, 'scale', null, $heading, false, false, false); $mform->display(); echo $OUTPUT->footer(); PK\a  scale/index.phpnu[. /** * A page for managing custom and standard scales * * @package core_grades * @copyright 2007 Petr Skoda * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ require_once '../../../config.php'; require_once $CFG->dirroot.'/grade/lib.php'; require_once $CFG->libdir.'/gradelib.php'; $courseid = optional_param('id', 0, PARAM_INT); $action = optional_param('action', '', PARAM_ALPHA); $PAGE->set_url('/grade/edit/scale/index.php', array('id' => $courseid)); /// Make sure they can even access this course if ($courseid) { if (!$course = $DB->get_record('course', array('id' => $courseid))) { throw new \moodle_exception('invalidcourseid'); } require_login($course); $context = context_course::instance($course->id); require_capability('moodle/course:managescales', $context); $PAGE->set_pagelayout('admin'); } else { require_once $CFG->libdir.'/adminlib.php'; admin_externalpage_setup('scales'); $context = context_system::instance(); $PAGE->set_primary_active_tab('siteadminnode'); } /// return tracking object $gpr = new grade_plugin_return(array('type'=>'edit', 'plugin'=>'scale', 'courseid'=>$courseid)); $strscale = get_string('scale'); $strstandardscale = get_string('scalesstandard'); $strcustomscales = get_string('scalescustom'); $strname = get_string('name'); $strdelete = get_string('delete'); $stredit = get_string('edit'); $strused = get_string('used'); $stredit = get_string('edit'); switch ($action) { case 'delete': if (!confirm_sesskey()) { break; } $scaleid = required_param('scaleid', PARAM_INT); if (!$scale = grade_scale::fetch(array('id'=>$scaleid))) { break; } if (empty($scale->courseid)) { require_capability('moodle/course:managescales', context_system::instance()); } else if ($scale->courseid != $courseid) { throw new \moodle_exception('invalidcourseid'); } if (!$scale->can_delete()) { break; } $deleteconfirmed = optional_param('deleteconfirmed', 0, PARAM_BOOL); if (!$deleteconfirmed) { if ($courseid) { $PAGE->navbar->add(get_string('scales'), new moodle_url('/grade/edit/scale/index.php', ['id' => $courseid])); } $strdeletescale = get_string('deletescale', 'grades'); $PAGE->navbar->add($strdeletescale); $PAGE->set_title($strdeletescale); $PAGE->set_heading($COURSE->fullname); echo $OUTPUT->header(); $confirmurl = new moodle_url('index.php', array( 'id' => $courseid, 'scaleid' => $scale->id, 'action'=> 'delete', 'sesskey' => sesskey(), 'deleteconfirmed'=> 1)); echo $OUTPUT->confirm(get_string('scaleconfirmdelete', 'grades', $scale->get_name()), $confirmurl, "index.php?id={$courseid}"); echo $OUTPUT->footer(); die; } else { $scale->delete(); } break; } if (!$courseid) { echo $OUTPUT->header(); } $table = new html_table(); $table2 = new html_table(); $heading = ''; if ($courseid and $scales = grade_scale::fetch_all_local($courseid)) { $heading = $strcustomscales; $data = array(); foreach($scales as $scale) { $line = array(); $line[] = $scale->get_name() .'
'.str_replace(",", ", ", $scale->scale).'
'; $used = $scale->is_used(); $line[] = $used ? get_string('yes') : get_string('no'); $buttons = ""; $buttons .= grade_button('edit', $courseid, $scale); if (!$used) { $buttons .= grade_button('delete', $courseid, $scale); } $line[] = $buttons; $data[] = $line; } $table->head = array($strscale, $strused, $stredit); $table->size = array('70%', '20%', '10%'); $table->align = array('left', 'center', 'center'); $table->attributes['class'] = 'scaletable localscales generaltable table table-hover'; $table->data = $data; } if ($scales = grade_scale::fetch_all_global()) { $heading = $strstandardscale; $data = array(); foreach($scales as $scale) { $line = array(); $line[] = $scale->get_name().'
'.str_replace(",", ", ", $scale->scale).'
'; $used = $scale->is_used(); $line[] = $used ? get_string('yes') : get_string('no'); $buttons = ""; if (has_capability('moodle/course:managescales', context_system::instance())) { $buttons .= grade_button('edit', $courseid, $scale); } if (!$used and has_capability('moodle/course:managescales', context_system::instance())) { $buttons .= grade_button('delete', $courseid, $scale); } $line[] = $buttons; $data[] = $line; } $table2->head = array($strscale, $strused, $stredit); $table->attributes['class'] = 'scaletable globalscales generaltable table table-hover'; $table2->size = array('70%', '20%', '10%'); $table2->align = array('left', 'center', 'center'); $table2->data = $data; } $actionbar = new \core_grades\output\scales_action_bar($context); if ($courseid) { print_grade_page_head($courseid, 'scale', 'scale', false, false, false, true, null, null, null, $actionbar); } else { $renderer = $PAGE->get_renderer('core_grades'); echo $renderer->render_action_bar($actionbar); echo $OUTPUT->heading(get_string('scales', 'core')); } $hascustomscales = !empty($table->data); $hasstandardscales = !empty($table2->data); // If there are custom scales available in this context, output the custom scales table and a heading. if ($hascustomscales) { echo $OUTPUT->heading($strcustomscales, 3, 'main mt-3'); echo html_writer::table($table); } // If there are standard scales available in this context, output the standard scales table and a heading. if ($hasstandardscales) { echo $OUTPUT->heading($strstandardscale, 3, 'main mt-3'); echo html_writer::table($table2); } // If the are no available scales, display a notification. if (!$hascustomscales && !$hasstandardscales) { echo $OUTPUT->notification(get_string('noexistingscales', 'grades'), 'info', false); } echo $OUTPUT->footer(); PK\FLLsettings/form.phpnu[. /** * A form for editing course grade settings * * @package core_grades * @copyright 2007 Petr Skoda * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ if (!defined('MOODLE_INTERNAL')) { die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page } require_once($CFG->libdir.'/formslib.php'); /** * First implementation of the preferences in the form of a moodleform. * TODO add "reset to site defaults" button */ class course_settings_form extends moodleform { function definition() { global $USER, $CFG; $mform =& $this->_form; $systemcontext = context_system::instance(); $can_view_admin_links = false; if (has_capability('moodle/grade:manage', $systemcontext)) { $can_view_admin_links = true; } // General settings $strchangedefaults = get_string('changedefaults', 'grades'); $mform->addElement('header', 'general', get_string('generalsettings', 'grades')); if ($can_view_admin_links) { $link = '' . $strchangedefaults . ''; $mform->addElement('static', 'generalsettingslink', null, $link); } $options = array(-1 => get_string('default', 'grades'), GRADE_REPORT_AGGREGATION_POSITION_FIRST => get_string('positionfirst', 'grades'), GRADE_REPORT_AGGREGATION_POSITION_LAST => get_string('positionlast', 'grades')); $default_gradedisplaytype = $CFG->grade_aggregationposition; foreach ($options as $key=>$option) { if ($key == $default_gradedisplaytype) { $options[-1] = get_string('defaultprev', 'grades', $option); break; } } $mform->addElement('select', 'aggregationposition', get_string('aggregationposition', 'grades'), $options); $mform->addHelpButton('aggregationposition', 'aggregationposition', 'grades'); if ($CFG->grade_minmaxtouse == GRADE_MIN_MAX_FROM_GRADE_ITEM) { $default = get_string('gradeitemminmax', 'grades'); } else if ($CFG->grade_minmaxtouse == GRADE_MIN_MAX_FROM_GRADE_GRADE) { $default = get_string('gradegrademinmax', 'grades'); } else { throw new coding_exception('Invalid $CFG->grade_minmaxtouse value.'); } $options = array( -1 => get_string('defaultprev', 'grades', $default), GRADE_MIN_MAX_FROM_GRADE_ITEM => get_string('gradeitemminmax', 'grades'), GRADE_MIN_MAX_FROM_GRADE_GRADE => get_string('gradegrademinmax', 'grades') ); $mform->addElement('select', 'minmaxtouse', get_string('minmaxtouse', 'grades'), $options); $mform->addHelpButton('minmaxtouse', 'minmaxtouse', 'grades'); // Grade item settings $mform->addElement('header', 'grade_item_settings', get_string('gradeitemsettings', 'grades')); $mform->setExpanded('grade_item_settings'); if ($can_view_admin_links) { $link = '' . $strchangedefaults . ''; $mform->addElement('static', 'gradeitemsettingslink', null, $link); } $options = array(-1 => get_string('default', 'grades'), GRADE_DISPLAY_TYPE_REAL => get_string('real', 'grades'), GRADE_DISPLAY_TYPE_REAL_PERCENTAGE => get_string('realpercentage', 'grades'), GRADE_DISPLAY_TYPE_REAL_LETTER => get_string('realletter', 'grades'), GRADE_DISPLAY_TYPE_PERCENTAGE => get_string('percentage', 'grades'), GRADE_DISPLAY_TYPE_PERCENTAGE_REAL => get_string('percentagereal', 'grades'), GRADE_DISPLAY_TYPE_PERCENTAGE_LETTER => get_string('percentageletter', 'grades'), GRADE_DISPLAY_TYPE_LETTER => get_string('letter', 'grades'), GRADE_DISPLAY_TYPE_LETTER_REAL => get_string('letterreal', 'grades'), GRADE_DISPLAY_TYPE_LETTER_PERCENTAGE => get_string('letterpercentage', 'grades')); $default_gradedisplaytype = $CFG->grade_displaytype; foreach ($options as $key=>$option) { if ($key == $default_gradedisplaytype) { $options[-1] = get_string('defaultprev', 'grades', $option); break; } } $mform->addElement('select', 'displaytype', get_string('gradedisplaytype', 'grades'), $options); $mform->addHelpButton('displaytype', 'gradedisplaytype', 'grades'); $mform->setDefault('displaytype', -1); $options = array(-1=> get_string('defaultprev', 'grades', $CFG->grade_decimalpoints), 0=>0, 1=>1, 2=>2, 3=>3, 4=>4, 5=>5); $mform->addElement('select', 'decimalpoints', get_string('decimalpoints', 'grades'), $options); $mform->addHelpButton('decimalpoints', 'decimalpoints', 'grades'); // add setting options for plugins $types = array('report', 'export', 'import'); foreach($types as $type) { foreach (core_component::get_plugin_list('grade'.$type) as $plugin => $plugindir) { // Include all the settings commands for this plugin if there are any if (file_exists($plugindir.'/lib.php')) { require_once($plugindir.'/lib.php'); $functionname = 'grade_'.$type.'_'.$plugin.'_settings_definition'; if (function_exists($functionname)) { $mform->addElement('header', 'grade_'.$type.$plugin, get_string('pluginname', 'grade'.$type.'_'.$plugin, NULL)); $mform->setExpanded('grade_'.$type.$plugin); if ($can_view_admin_links) { $link = '' . $strchangedefaults . ''; $mform->addElement('static', 'gradeitemsettingslink', null, $link); } $functionname($mform); } } } } $mform->addElement('hidden', 'id'); $mform->setType('id', PARAM_INT); $this->add_action_buttons(false); } } PK\QZv settings/index.phpnu[. /** * A page for editing course grade settings * * @package core_grades * @copyright 2007 Petr Skoda * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ require_once '../../../config.php'; require_once $CFG->dirroot.'/grade/lib.php'; require_once $CFG->libdir.'/gradelib.php'; require_once 'form.php'; $courseid = optional_param('id', SITEID, PARAM_INT); $PAGE->set_url('/grade/edit/settings/index.php', array('id'=>$courseid)); $PAGE->set_pagelayout('admin'); if (!$course = $DB->get_record('course', array('id' => $courseid))) { throw new \moodle_exception('invalidcourseid'); } require_login($course); $context = context_course::instance($course->id); require_capability('moodle/grade:manage', $context); $gpr = new grade_plugin_return(array('type'=>'edit', 'plugin'=>'settings', 'courseid'=>$courseid)); $strgrades = get_string('grades'); $pagename = get_string('coursesettings', 'grades'); $mform = new course_settings_form(); $settings = grade_get_settings($course->id); $mform->set_data($settings); if ($data = $mform->get_data()) { $data = (array)$data; $general = array('displaytype', 'decimalpoints', 'aggregationposition', 'minmaxtouse'); foreach ($data as $key=>$value) { if (!in_array($key, $general) and strpos($key, 'report_') !== 0 and strpos($key, 'import_') !== 0 and strpos($key, 'export_') !== 0) { continue; } if ($value == -1) { $value = null; } grade_set_setting($course->id, $key, $value); $previousvalue = isset($settings->{$key}) ? $settings->{$key} : null; if ($key == 'minmaxtouse' && $previousvalue != $value) { // The min max has changed, we need to regrade the grades. grade_force_full_regrading($courseid); } } } print_grade_page_head($courseid, 'settings', 'coursesettings'); // The settings could have been changed due to a notice shown in print_grade_page_head, we need to refresh them. $settings = grade_get_settings($course->id); $mform->set_data($settings); echo $OUTPUT->box_start('generalbox boxaligncenter boxwidthnormal centerpara'); echo get_string('coursesettingsexplanation', 'grades'); echo $OUTPUT->box_end(); $mform->display(); echo $OUTPUT->footer(); PK\" letter/edit_form.phpnu[. /** * A moodleform for editing grade letters * * @package core_grades * @copyright 2007 Petr Skoda * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ if (!defined('MOODLE_INTERNAL')) { die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page } require_once $CFG->libdir.'/formslib.php'; class edit_letter_form extends moodleform { public function definition() { $mform =& $this->_form; [ 'lettercount' => $lettercount, 'admin' => $admin, ] = $this->_customdata; $mform->addElement('header', 'gradeletters', get_string('gradeletters', 'grades')); // Only show "override site defaults" checkbox if editing the course grade letters if (!$admin) { $mform->addElement('checkbox', 'override', get_string('overridesitedefaultgradedisplaytype', 'grades')); $mform->addHelpButton('override', 'overridesitedefaultgradedisplaytype', 'grades'); } $gradeletter = get_string('gradeletter', 'grades'); $gradeboundary = get_string('gradeboundary', 'grades'); // The fields to create the grade letter/boundary. $elements = []; $elements[] = $mform->createElement('text', 'gradeletter', "{$gradeletter} {no}"); $elements[] = $mform->createElement('static', '', '', '≥'); $elements[] = $mform->createElement('float', 'gradeboundary', "{$gradeboundary} {no}"); $elements[] = $mform->createElement('static', '', '', '%'); // Element options/rules, fields should be disabled unless "Override" is checked for course grade letters. $options = []; $options['gradeletter']['type'] = PARAM_TEXT; if (!$admin) { $options['gradeletter']['disabledif'] = ['override', 'notchecked']; $options['gradeboundary']['disabledif'] = ['override', 'notchecked']; } // Create our repeatable elements, each one a group comprised of the fields defined previously. $this->repeat_elements([ $mform->createElement('group', 'gradeentry', "{$gradeletter} {no}", $elements, [' '], false) ], $lettercount, $options, 'gradeentrycount', 'gradeentryadd', 3); // Add a help icon to first element group, if it exists. if ($mform->elementExists('gradeentry[0]')) { $mform->addHelpButton('gradeentry[0]', 'gradeletter', 'grades'); } $mform->addElement('hidden', 'id'); $mform->setType('id', PARAM_INT); $this->add_action_buttons(); } } PK\+letter/tabs.phpnu[. /** * Prints navigation tabs for viewing and editing grade letters * * @package core_grades * @copyright 2007 Petr Skoda * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ $row = $tabs = array(); $row[] = new tabobject('lettersview', $CFG->wwwroot.'/grade/edit/letter/index.php?id='.$COURSE->id, get_string('letters', 'grades')); if (has_capability('moodle/grade:manageletters', $context)) { $row[] = new tabobject('lettersedit', $CFG->wwwroot.'/grade/edit/letter/edit.php?id='.$context->id, get_string('edit')); } $tabs[] = $row; echo '
'; print_tabs($tabs, $currenttab); echo '
'; PK\{8$&$&letter/index.phpnu[. /** * List of grade letters. * * @package core_grades * @copyright 2008 Nicolas Connault * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ require_once '../../../config.php'; require_once $CFG->dirroot.'/grade/lib.php'; require_once $CFG->libdir.'/gradelib.php'; $contextid = optional_param('id', SYSCONTEXTID, PARAM_INT); $action = optional_param('action', '', PARAM_ALPHA); $edit = optional_param('edit', false, PARAM_BOOL); //are we editing? $url = new moodle_url('/grade/edit/letter/index.php', array('id' => $contextid)); list($context, $course, $cm) = get_context_info_array($contextid); $contextid = null;//now we have a context object throw away the $contextid from the params //if viewing if (!$edit) { if (!has_capability('moodle/grade:manage', $context) and !has_capability('moodle/grade:manageletters', $context)) { throw new \moodle_exception('nopermissiontoviewletergrade'); } } else {//else we're editing require_capability('moodle/grade:manageletters', $context); navigation_node::override_active_url($url); $url->param('edit', 1); $PAGE->navbar->add(get_string('editgradeletters', 'grades'), $url); } $PAGE->set_url($url); $returnurl = null; $editparam = null; if ($context->contextlevel == CONTEXT_SYSTEM or $context->contextlevel == CONTEXT_COURSECAT) { require_once $CFG->libdir.'/adminlib.php'; admin_externalpage_setup('letters'); $admin = true; $returnurl = "$CFG->wwwroot/grade/edit/letter/index.php"; $editparam = '?edit=1'; $PAGE->set_primary_active_tab('siteadminnode'); } else if ($context->contextlevel == CONTEXT_COURSE) { $PAGE->set_pagelayout('standard');//calling this here to make blocks display require_login($context->instanceid, false, $cm); $admin = false; $returnurl = $CFG->wwwroot.'/grade/edit/letter/index.php?id='.$context->id; $editparam = '&edit=1'; $gpr = new grade_plugin_return(array('type'=>'edit', 'plugin'=>'letter', 'courseid'=>$course->id)); } else { throw new \moodle_exception('invalidcourselevel'); } $strgrades = get_string('grades'); $pagename = get_string('letters', 'grades'); $letters = grade_get_letters($context); $override = $DB->record_exists('grade_letters', array('contextid' => $context->id)); //if were viewing the letters if (!$edit) { $heading = get_string('gradeletters', 'grades'); $actionbar = new \core_grades\output\grade_letters_action_bar($context); if ($admin) { echo $OUTPUT->header(); $renderer = $PAGE->get_renderer('core_grades'); echo $renderer->render_action_bar($actionbar); echo $OUTPUT->heading($heading); } else { print_grade_page_head($course->id, 'letter', 'view', false, false, false, true, null, null, null, $actionbar); } $data = array(); $max = 100; foreach($letters as $boundary=>$letter) { $line = array(); $line[] = format_float($max,2).' %'; $line[] = format_float($boundary,2).' %'; $line[] = format_string($letter); $data[] = $line; $max = $boundary - 0.01; } if (!empty($override)) { echo $OUTPUT->notification(get_string('gradeletteroverridden', 'grades'), 'notifymessage'); } $table = new html_table(); $table->id = 'grade-letters-view'; $table->head = array(get_string('max', 'grades'), get_string('min', 'grades'), get_string('letter', 'grades')); $table->size = array('30%', '30%', '40%'); $table->align = array('left', 'left', 'left'); $table->width = '30%'; $table->data = $data; $table->tablealign = 'center'; echo html_writer::table($table); } else { //else we're editing require_once('edit_form.php'); $data = new stdClass(); $data->id = $context->id; $i = 0; foreach ($letters as $boundary=>$letter) { $data->gradeletter[$i] = $letter; $data->gradeboundary[$i] = $boundary; $i++; } $data->override = $override; // Count number of letters, used to build the repeated elements of the form. $lettercount = count($letters); $mform = new edit_letter_form($returnurl.$editparam, ['lettercount' => $lettercount, 'admin' => $admin]); $mform->set_data($data); if ($mform->is_cancelled()) { redirect($returnurl); } else if ($data = $mform->get_data()) { // Make sure we are updating the cache. $cache = cache::make('core', 'grade_letters'); if (!$admin and empty($data->override)) { $records = $DB->get_records('grade_letters', array('contextid' => $context->id)); foreach ($records as $record) { $DB->delete_records('grade_letters', array('id' => $record->id)); // Trigger the letter grade deleted event. $event = \core\event\grade_letter_deleted::create(array( 'objectid' => $record->id, 'context' => $context, )); $event->trigger(); } // Make sure we clear the cache for this context. $cache->delete($context->id); redirect($returnurl); } $letters = array(); for ($i = 0; $i < $data->gradeentrycount; $i++) { $letter = $data->gradeletter[$i]; if ($letter === '') { continue; } $boundary = floatval($data->gradeboundary[$i]); if ($boundary < 0 || $boundary > 100) { continue; // Skip if out of range. } // The keys need to be strings so floats are not truncated. $letters[number_format($boundary, 5)] = $letter; } $pool = array(); if ($records = $DB->get_records('grade_letters', array('contextid' => $context->id), 'lowerboundary ASC')) { foreach ($records as $r) { // Will re-use the lowerboundary to avoid duplicate during the update process. $pool[number_format($r->lowerboundary, 5)] = $r; } } foreach ($letters as $boundary => $letter) { $record = new stdClass(); $record->letter = $letter; $record->lowerboundary = $boundary; $record->contextid = $context->id; if (isset($pool[$boundary])) { // Re-use the existing boundary to avoid key constraint. if ($letter != $pool[$boundary]->letter) { // The letter has been assigned to another boundary, we update it. $record->id = $pool[$boundary]->id; $DB->update_record('grade_letters', $record); // Trigger the letter grade updated event. $event = \core\event\grade_letter_updated::create(array( 'objectid' => $record->id, 'context' => $context, )); $event->trigger(); } unset($pool[$boundary]); // Remove the letter from the pool. } else if ($candidate = array_pop($pool)) { // The boundary is new, we update a random record from the pool. $record->id = $candidate->id; $DB->update_record('grade_letters', $record); // Trigger the letter grade updated event. $event = \core\event\grade_letter_updated::create(array( 'objectid' => $record->id, 'context' => $context, )); $event->trigger(); } else { // No records were found, this must be a new letter. $newid = $DB->insert_record('grade_letters', $record); // Trigger the letter grade added event. $event = \core\event\grade_letter_created::create(array( 'objectid' => $newid, 'context' => $context, )); $event->trigger(); } } // Cache the changed letters. if (!empty($letters)) { // For some reason, the cache saves it in the order in which they were entered // but we really want to order them in descending order so we sort it here. krsort($letters); $cache->set($context->id, $letters); } // Delete the unused records. foreach($pool as $leftover) { $DB->delete_records('grade_letters', array('id' => $leftover->id)); // Trigger the letter grade deleted event. $event = \core\event\grade_letter_deleted::create(array( 'objectid' => $leftover->id, 'context' => $context, )); $event->trigger(); } redirect($returnurl); } print_grade_page_head($COURSE->id, 'letter', 'edit', get_string('editgradeletters', 'grades'), false, false, false); $mform->display(); } echo $OUTPUT->footer(); PK\Iͯ outcome/course_form.htmlnu[


PK\DD D outcome/import_outcomes_form.phpnu[. /** * A form to allow importing outcomes from a file * * @package core_grades * @copyright 2008 Moodle Pty Ltd (http://moodle.com) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ if (!defined('MOODLE_INTERNAL')) { die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page } require_once($CFG->dirroot.'/lib/formslib.php'); class import_outcomes_form extends moodleform { public function definition() { global $PAGE, $USER; $mform =& $this->_form; $mform->addElement('hidden', 'action', 'upload'); $mform->setType('action', PARAM_ALPHANUMEXT); $mform->addElement('hidden', 'courseid', $PAGE->course->id); $mform->setType('courseid', PARAM_INT); $scope = array(); if (($PAGE->course->id > 1) && has_capability('moodle/grade:manage', context_system::instance())) { $mform->addElement('radio', 'scope', get_string('importcustom', 'grades'), null, 'custom'); $mform->addElement('radio', 'scope', get_string('importstandard', 'grades'), null, 'global'); $mform->setDefault('scope', 'custom'); } $mform->addElement('filepicker', 'userfile', get_string('importoutcomes', 'grades')); $mform->addRule('userfile', get_string('required'), 'required', null, 'server'); $mform->addHelpButton('userfile', 'importoutcomes', 'grades'); $buttonarray = [ $mform->createElement('submit', 'save', get_string('uploadthisfile')), $mform->createElement('cancel') ]; $mform->addGroup($buttonarray, 'buttonar', '', ' ', false); } } PK\G^outcome/edit_form.phpnu[. /** * Edit form for grade outcomes * * @package core_grades * @copyright 2007 Petr Skoda * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ if (!defined('MOODLE_INTERNAL')) { die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page } require_once $CFG->libdir.'/formslib.php'; class edit_outcome_form extends moodleform { public function definition() { global $CFG, $COURSE; $mform =& $this->_form; // visible elements $mform->addElement('header', 'general', get_string('outcomes', 'grades')); $mform->addElement('text', 'fullname', get_string('outcomefullname', 'grades'), 'size="40"'); $mform->addRule('fullname', get_string('required'), 'required'); $mform->setType('fullname', PARAM_TEXT); $mform->addElement('text', 'shortname', get_string('outcomeshortname', 'grades'), 'size="20"'); $mform->addRule('shortname', get_string('required'), 'required'); $mform->setType('shortname', PARAM_NOTAGS); $mform->addElement('advcheckbox', 'standard', get_string('outcomestandard', 'grades')); $mform->addHelpButton('standard', 'outcomestandard', 'grades'); $options = array(); $mform->addElement('selectwithlink', 'scaleid', get_string('scale'), $options, null, array('link' => $CFG->wwwroot.'/grade/edit/scale/edit.php?courseid='.$COURSE->id, 'label' => get_string('scalescustomcreate'))); $mform->addHelpButton('scaleid', 'typescale', 'grades'); $mform->addRule('scaleid', get_string('required'), 'required'); $mform->addElement('editor', 'description_editor', get_string('description'), null, $this->_customdata['editoroptions']); // hidden params $mform->addElement('hidden', 'id', 0); $mform->setType('id', PARAM_INT); $mform->addElement('hidden', 'courseid', 0); $mform->setType('courseid', PARAM_INT); /// add return tracking info $gpr = $this->_customdata['gpr']; $gpr->add_mform_elements($mform); //------------------------------------------------------------------------------- // buttons $this->add_action_buttons(); } /// tweak the form - depending on existing data function definition_after_data() { global $CFG; $mform =& $this->_form; // first load proper scales if ($courseid = $mform->getElementValue('courseid')) { $options = array(); if ($scales = grade_scale::fetch_all_local($courseid)) { $options[-1] = '--'.get_string('scalescustom'); foreach($scales as $scale) { $options[$scale->id] = $scale->get_name(); } } if ($scales = grade_scale::fetch_all_global()) { $options[-2] = '--'.get_string('scalesstandard'); foreach($scales as $scale) { $options[$scale->id] = $scale->get_name(); } } $scale_el =& $mform->getElement('scaleid'); $scale_el->load($options); } else { $options = array(); if ($scales = grade_scale::fetch_all_global()) { foreach($scales as $scale) { $options[$scale->id] = $scale->get_name(); } } $scale_el =& $mform->getElement('scaleid'); $scale_el->load($options); } if ($id = $mform->getElementValue('id')) { $outcome = grade_outcome::fetch(array('id'=>$id)); $itemcount = $outcome->get_item_uses_count(); $coursecount = $outcome->get_course_uses_count(); if ($itemcount) { $mform->hardFreeze('scaleid'); } if (empty($courseid)) { $mform->hardFreeze('standard'); } else if (!has_capability('moodle/grade:manage', context_system::instance())) { $mform->hardFreeze('standard'); } else if ($coursecount and empty($outcome->courseid)) { $mform->hardFreeze('standard'); } } else { if (empty($courseid) or !has_capability('moodle/grade:manage', context_system::instance())) { $mform->hardFreeze('standard'); } } } /// perform extra validation before submission function validation($data, $files) { $errors = parent::validation($data, $files); if ($data['scaleid'] < 1) { $errors['scaleid'] = get_string('required'); } if (!empty($data['standard']) and $scale = grade_scale::fetch(array('id'=>$data['scaleid']))) { if (!empty($scale->courseid)) { //TODO: localize $errors['scaleid'] = 'Can not use custom scale in global outcome!'; } } return $errors; } } PK\?D:outcome/export.phpnu[. /** * Exports selected outcomes in CSV format * * @package core_grades * @copyright 2008 Moodle Pty Ltd (http://moodle.com) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ require_once '../../../config.php'; require_once $CFG->dirroot.'/grade/lib.php'; require_once $CFG->libdir.'/gradelib.php'; $courseid = optional_param('id', 0, PARAM_INT); $action = optional_param('action', '', PARAM_ALPHA); /// Make sure they can even access this course if ($courseid) { if (!$course = $DB->get_record('course', array('id' => $courseid))) { throw new \moodle_exception('invalidcourseid'); } require_login($course); $context = context_course::instance($course->id); require_capability('moodle/grade:manage', $context); if (empty($CFG->enableoutcomes)) { redirect('../../index.php?id='.$courseid); } } else { require_once $CFG->libdir.'/adminlib.php'; admin_externalpage_setup('outcomes'); } require_sesskey(); header("Content-Type: text/csv; charset=utf-8"); // TODO: make the filename more useful, include a date, a specific name, something... header('Content-Disposition: attachment; filename=outcomes.csv'); // sending header with clear names, to make 'what is what' as easy as possible to understand $header = array('outcome_name', 'outcome_shortname', 'outcome_description', 'scale_name', 'scale_items', 'scale_description'); echo format_csv($header, ';', '"'); $outcomes = array(); if ( $courseid ) { $outcomes = array_merge(grade_outcome::fetch_all_global(), grade_outcome::fetch_all_local($courseid)); } else { $outcomes = grade_outcome::fetch_all_global(); } foreach($outcomes as $outcome) { $line = array(); $line[] = $outcome->get_name(); $line[] = $outcome->get_shortname(); $line[] = $outcome->get_description(); $scale = $outcome->load_scale(); $line[] = $scale->get_name(); $line[] = $scale->compact_items(); $line[] = $scale->get_description(); echo format_csv($line, ';', '"'); } /** * Formats and returns a line of data, in CSV format. This code * is from http://au2.php.net/manual/en/function.fputcsv.php#77866 * * @param string[] $fields data to be exported * @param string $delimiter char to be used to separate fields * @param string $enclosure char used to enclose strings that contains newlines, spaces, tabs or the delimiter char itself * @returns string one line of csv data */ function format_csv($fields = array(), $delimiter = ';', $enclosure = '"') { $str = ''; $escape_char = '\\'; foreach ($fields as $value) { if (strpos($value, $delimiter) !== false || strpos($value, $enclosure) !== false || strpos($value, "\n") !== false || strpos($value, "\r") !== false || strpos($value, "\t") !== false || strpos($value, ' ') !== false) { $str2 = $enclosure; $escaped = 0; $len = strlen($value); for ($i=0;$i<$len;$i++) { if ($value[$i] == $escape_char) { $escaped = 1; } else if (!$escaped && $value[$i] == $enclosure) { $str2 .= $enclosure; } else { $escaped = 0; } $str2 .= $value[$i]; } $str2 .= $enclosure; $str .= $str2.$delimiter; } else { $str .= $value.$delimiter; } } $str = substr($str,0,-1); $str .= "\n"; return $str; } PK\>!!outcome/tabs.phpnu[. /** * Prints navigation tabs for viewing and editing grade outcomes * * @package core_grades * @copyright 2009 Petr Skoda * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ $row = $tabs = array(); $context = context_course::instance($courseid); $row[] = new tabobject('courseoutcomes', $CFG->wwwroot.'/grade/edit/outcome/course.php?id='.$courseid, get_string('outcomescourse', 'grades')); if (has_capability('moodle/grade:manage', $context)) { $row[] = new tabobject('outcomes', $CFG->wwwroot.'/grade/edit/outcome/index.php?id='.$courseid, get_string('editoutcomes', 'grades')); } $tabs[] = $row; echo '
'; print_tabs($tabs, $currenttab); echo '
'; PK\ ~outcome/course.phpnu[. /** * A page for selecting outcomes for use in a course * * @package core_grades * @copyright 2007 Petr Skoda * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ require_once '../../../config.php'; require_once $CFG->dirroot.'/grade/lib.php'; require_once $CFG->libdir.'/gradelib.php'; $courseid = required_param('id', PARAM_INT); $PAGE->set_url('/grade/edit/outcome/course.php', array('id'=>$courseid)); $course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST); /// Make sure they can even access this course require_login($course); $context = context_course::instance($course->id); require_capability('moodle/course:update', $context); /// return tracking object $gpr = new grade_plugin_return(array('type'=>'edit', 'plugin'=>'outcomes', 'courseid'=>$courseid)); // first of all fix the state of outcomes_course table $standardoutcomes = grade_outcome::fetch_all_global(); $co_custom = grade_outcome::fetch_all_local($courseid); $co_standard_used = array(); $co_standard_notused = array(); if ($courseused = $DB->get_records('grade_outcomes_courses', array('courseid' => $courseid), '', 'outcomeid')) { $courseused = array_keys($courseused); } else { $courseused = array(); } // fix wrong entries in outcomes_courses foreach ($courseused as $oid) { if (!array_key_exists($oid, $standardoutcomes) and !array_key_exists($oid, $co_custom)) { $DB->delete_records('grade_outcomes_courses', array('outcomeid' => $oid, 'courseid' => $courseid)); } } // fix local custom outcomes missing in outcomes_course foreach($co_custom as $oid=>$outcome) { if (!in_array($oid, $courseused)) { $courseused[$oid] = $oid; $goc = new stdClass(); $goc->courseid = $courseid; $goc->outcomeid = $oid; $DB->insert_record('grade_outcomes_courses', $goc); } } // now check all used standard outcomes are in outcomes_course too $params = array($courseid); $sql = "SELECT DISTINCT outcomeid FROM {grade_items} WHERE courseid=? and outcomeid IS NOT NULL"; if ($realused = $DB->get_records_sql($sql, $params)) { $realused = array_keys($realused); foreach ($realused as $oid) { if (array_key_exists($oid, $standardoutcomes)) { $co_standard_used[$oid] = $standardoutcomes[$oid]; unset($standardoutcomes[$oid]); if (!in_array($oid, $courseused)) { $courseused[$oid] = $oid; $goc = new stdClass(); $goc->courseid = $courseid; $goc->outcomeid = $oid; $DB->insert_record('grade_outcomes_courses', $goc); } } } } // find all unused standard course outcomes - candidates for removal foreach ($standardoutcomes as $oid=>$outcome) { if (in_array($oid, $courseused)) { $co_standard_notused[$oid] = $standardoutcomes[$oid]; unset($standardoutcomes[$oid]); } } /// form processing if ($data = data_submitted() and confirm_sesskey()) { require_capability('moodle/grade:manageoutcomes', $context); if (!empty($data->add) && !empty($data->addoutcomes)) { /// add all selected to course list foreach ($data->addoutcomes as $add) { $add = clean_param($add, PARAM_INT); if (!array_key_exists($add, $standardoutcomes)) { continue; } $goc = new stdClass(); $goc->courseid = $courseid; $goc->outcomeid = $add; $DB->insert_record('grade_outcomes_courses', $goc); } } else if (!empty($data->remove) && !empty($data->removeoutcomes)) { /// remove all selected from course outcomes list foreach ($data->removeoutcomes as $remove) { $remove = clean_param($remove, PARAM_INT); if (!array_key_exists($remove, $co_standard_notused)) { continue; } $DB->delete_records('grade_outcomes_courses', array('courseid' => $courseid, 'outcomeid' => $remove)); } } redirect('course.php?id='.$courseid); // we must redirect to get fresh data } $actionbar = new \core_grades\output\course_outcomes_action_bar($context); // Print header. print_grade_page_head($COURSE->id, 'outcome', 'course', false, false, false, true, null, null, null, $actionbar); require('course_form.html'); echo $OUTPUT->footer(); PK\-outcome/edit.phpnu[. /** * Edit page for grade outcomes. * * @package core_grades * @copyright 2008 Nicolas Connault * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ require_once '../../../config.php'; require_once $CFG->dirroot.'/grade/lib.php'; require_once $CFG->dirroot.'/grade/report/lib.php'; require_once 'edit_form.php'; $courseid = optional_param('courseid', 0, PARAM_INT); $id = optional_param('id', 0, PARAM_INT); $url = new moodle_url('/grade/edit/outcome/edit.php'); if ($courseid !== 0) { $url->param('courseid', $courseid); } if ($id !== 0) { $url->param('id', $id); } $PAGE->set_url($url); $PAGE->set_pagelayout('admin'); $systemcontext = context_system::instance(); $heading = get_string('addoutcome', 'grades'); // a bit complex access control :-O if ($id) { $heading = get_string('editoutcome', 'grades'); /// editing existing outcome if (!$outcome_rec = $DB->get_record('grade_outcomes', array('id' => $id))) { throw new \moodle_exception('invalidoutcome'); } if ($outcome_rec->courseid) { $outcome_rec->standard = 0; if (!$course = $DB->get_record('course', array('id' => $outcome_rec->courseid))) { throw new \moodle_exception('invalidcourseid'); } require_login($course); $context = context_course::instance($course->id); require_capability('moodle/grade:manage', $context); $courseid = $course->id; } else { if ($courseid) { if (!$course = $DB->get_record('course', array('id' => $courseid))) { throw new \moodle_exception('invalidcourseid'); } } $outcome_rec->standard = 1; $outcome_rec->courseid = $courseid; require_login(); require_capability('moodle/grade:manage', $systemcontext); $PAGE->set_context($systemcontext); } } else if ($courseid){ /// adding new outcome from course $course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST); require_login($course); $context = context_course::instance($course->id); require_capability('moodle/grade:manage', $context); $outcome_rec = new stdClass(); $outcome_rec->standard = 0; $outcome_rec->courseid = $courseid; } else { require_login(); require_capability('moodle/grade:manage', $systemcontext); $PAGE->set_context($systemcontext); /// adding new outcome from admin section $outcome_rec = new stdClass(); $outcome_rec->standard = 1; $outcome_rec->courseid = 0; } if (!$courseid) { require_once $CFG->libdir.'/adminlib.php'; admin_externalpage_setup('outcomes'); $PAGE->set_primary_active_tab('siteadminnode'); } else { navigation_node::override_active_url(new moodle_url('/grade/edit/outcome/course.php', ['id' => $courseid])); $PAGE->navbar->add(get_string('manageoutcomes', 'grades'), new moodle_url('/grade/edit/outcome/index.php', ['id' => $courseid])); } // default return url $gpr = new grade_plugin_return(); $returnurl = $gpr->get_return_url('index.php?id='.$courseid); $editoroptions = array( 'maxfiles' => EDITOR_UNLIMITED_FILES, 'maxbytes' => $CFG->maxbytes, 'trusttext' => false, 'noclean' => true, 'context' => $systemcontext ); if (!empty($outcome_rec->id)) { $editoroptions['subdirs'] = file_area_contains_subdirs($systemcontext, 'grade', 'outcome', $outcome_rec->id); $outcome_rec = file_prepare_standard_editor($outcome_rec, 'description', $editoroptions, $systemcontext, 'grade', 'outcome', $outcome_rec->id); } else { $editoroptions['subdirs'] = false; $outcome_rec = file_prepare_standard_editor($outcome_rec, 'description', $editoroptions, $systemcontext, 'grade', 'outcome', null); } $mform = new edit_outcome_form(null, compact('gpr', 'editoroptions')); $mform->set_data($outcome_rec); if ($mform->is_cancelled()) { redirect($returnurl); } else if ($data = $mform->get_data()) { $outcome = new grade_outcome(array('id'=>$id)); $data->usermodified = $USER->id; if (empty($outcome->id)) { $data->description = $data->description_editor['text']; grade_outcome::set_properties($outcome, $data); if (!has_capability('moodle/grade:manage', $systemcontext)) { $data->standard = 0; } $outcome->courseid = !empty($data->standard) ? null : $courseid; if (empty($outcome->courseid)) { $outcome->courseid = null; } $outcome->insert(); $data = file_postupdate_standard_editor($data, 'description', $editoroptions, $systemcontext, 'grade', 'outcome', $outcome->id); $DB->set_field($outcome->table, 'description', $data->description, array('id'=>$outcome->id)); } else { $data = file_postupdate_standard_editor($data, 'description', $editoroptions, $systemcontext, 'grade', 'outcome', $id); grade_outcome::set_properties($outcome, $data); if (isset($data->standard)) { $outcome->courseid = !empty($data->standard) ? null : $courseid; } else { unset($outcome->courseid); // keep previous } $outcome->update(); } redirect($returnurl); } $PAGE->navbar->add($heading, $url); print_grade_page_head($courseid ?: SITEID, 'outcome', 'edit', $heading, false, false, false); if (!grade_scale::fetch_all_local($courseid) && !grade_scale::fetch_all_global()) { echo $OUTPUT->confirm(get_string('noscales', 'grades'), $CFG->wwwroot.'/grade/edit/scale/edit.php?courseid='.$courseid, $returnurl); echo $OUTPUT->footer(); die(); } $mform->display(); echo $OUTPUT->footer(); PK\)%%outcome/index.phpnu[. /** * Listing page for grade outcomes. * * @package core_grades * @copyright 2008 Nicolas Connault * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ require_once(__DIR__.'/../../../config.php'); require_once($CFG->dirroot.'/grade/lib.php'); require_once($CFG->libdir.'/gradelib.php'); $courseid = optional_param('id', 0, PARAM_INT); $action = optional_param('action', '', PARAM_ALPHA); $url = new moodle_url('/grade/edit/outcome/index.php', ['id' => $courseid]); $PAGE->set_url($url); $PAGE->set_pagelayout('admin'); /// Make sure they can even access this course if ($courseid) { $course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST); require_login($course); $context = context_course::instance($course->id); require_capability('moodle/grade:manageoutcomes', $context); if (empty($CFG->enableoutcomes)) { redirect('../../index.php?id='.$courseid); } // This page doesn't exist on the navigation so map it to another navigation_node::override_active_url(new moodle_url('/grade/edit/outcome/course.php', array('id'=>$courseid))); $PAGE->navbar->add(get_string('manageoutcomes', 'grades'), $url); } else { if (empty($CFG->enableoutcomes)) { redirect('../../../'); } require_once $CFG->libdir.'/adminlib.php'; admin_externalpage_setup('outcomes'); $context = context_system::instance(); $PAGE->set_primary_active_tab('siteadminnode'); } /// return tracking object $gpr = new grade_plugin_return(array('type'=>'edit', 'plugin'=>'outcome', 'courseid'=>$courseid)); $strshortname = get_string('outcomeshortname', 'grades'); $strfullname = get_string('outcomefullname', 'grades'); $strscale = get_string('scale'); $strstandardoutcome = get_string('outcomesstandard', 'grades'); $strcustomoutcomes = get_string('outcomescustom', 'grades'); $strcreatenewoutcome = get_string('outcomecreate', 'grades'); $stritems = get_string('items', 'grades'); $strcourses = get_string('courses'); $stredit = get_string('edit'); switch ($action) { case 'delete': if (!confirm_sesskey()) { break; } $outcomeid = required_param('outcomeid', PARAM_INT); if (!$outcome = grade_outcome::fetch(array('id'=>$outcomeid))) { break; } if (empty($outcome->courseid)) { require_capability('moodle/grade:manage', context_system::instance()); } else if ($outcome->courseid != $courseid) { throw new \moodle_exception('invalidcourseid'); } if (!$outcome->can_delete()) { break; } $deleteconfirmed = optional_param('deleteconfirmed', 0, PARAM_BOOL); if(!$deleteconfirmed){ $PAGE->set_title(get_string('outcomedelete', 'grades')); $PAGE->navbar->add(get_string('outcomedelete', 'grades')); echo $OUTPUT->header(); $confirmurl = new moodle_url('index.php', array( 'id' => $courseid, 'outcomeid' => $outcome->id, 'action'=> 'delete', 'sesskey' => sesskey(), 'deleteconfirmed'=> 1)); echo $OUTPUT->confirm(get_string('outcomeconfirmdelete', 'grades', $outcome->fullname), $confirmurl, "index.php?id={$courseid}"); echo $OUTPUT->footer(); die; }else{ $outcome->delete(); } break; } $systemcontext = context_system::instance(); $caneditsystemscales = has_capability('moodle/course:managescales', $systemcontext); if ($courseid) { $caneditcoursescales = has_capability('moodle/course:managescales', $context); } else { $caneditcoursescales = $caneditsystemscales; } $outcomes_tables = array(); $heading = get_string('outcomes', 'grades'); if ($courseid and $outcomes = grade_outcome::fetch_all_local($courseid)) { $return = $OUTPUT->heading($strcustomoutcomes, 3, 'main mt-3'); $data = array(); foreach($outcomes as $outcome) { $line = array(); $line[] = $outcome->get_name(); $line[] = $outcome->get_shortname(); $scale = $outcome->load_scale(); if (empty($scale->id)) { // hopefully never happens $line[] = $scale->get_name(); debugging("Found a scale with no ID ({$scale->get_name()}) while outputting course outcomes", DEBUG_DEVELOPER); } else { if (empty($scale->courseid)) { $caneditthisscale = $caneditsystemscales; } else if ($scale->courseid == $courseid) { $caneditthisscale = $caneditcoursescales; } else { $context = context_course::instance($scale->courseid); $caneditthisscale = has_capability('moodle/course:managescales', $context); } if ($caneditthisscale) { $line[] = grade_print_scale_link($courseid, $scale, $gpr); } else { $line[] = $scale->get_name(); } } $line[] = $outcome->get_item_uses_count(); $buttons = grade_button('edit', $courseid, $outcome); if ($outcome->can_delete()) { $buttons .= grade_button('delete', $courseid, $outcome); } $line[] = $buttons; $data[] = $line; } $table = new html_table(); $table->head = array($strfullname, $strshortname, $strscale, $stritems, $stredit); $table->size = array('30%', '20%', '20%', '20%', '10%' ); $table->align = array('left', 'left', 'left', 'center', 'center'); $table->width = '90%'; $table->data = $data; $return .= html_writer::table($table); $outcomes_tables[] = $return; } if ($outcomes = grade_outcome::fetch_all_global()) { $return = $OUTPUT->heading($strstandardoutcome, 3, 'main mt-3'); $data = array(); foreach($outcomes as $outcome) { $line = array(); $line[] = $outcome->get_name(); $line[] = $outcome->get_shortname(); $scale = $outcome->load_scale(); if (empty($scale->id)) { // hopefully never happens $line[] = $scale->get_name(); debugging("Found a scale with no ID ({$scale->get_name()}) while outputting global outcomes", DEBUG_DEVELOPER); } else { if (empty($scale->courseid)) { $caneditthisscale = $caneditsystemscales; } else if ($scale->courseid == $courseid) { $caneditthisscale = $caneditcoursescales; } else { $context = context_course::instance($scale->courseid); $caneditthisscale = has_capability('moodle/course:managescales', $context); } if ($caneditthisscale) { $line[] = grade_print_scale_link($courseid, $scale, $gpr); } else { $line[] = $scale->get_name(); } } $line[] = $outcome->get_course_uses_count(); $line[] = $outcome->get_item_uses_count(); $buttons = ""; if (has_capability('moodle/grade:manage', context_system::instance())) { $buttons .= grade_button('edit', $courseid, $outcome); } if (has_capability('moodle/grade:manage', context_system::instance()) and $outcome->can_delete()) { $buttons .= grade_button('delete', $courseid, $outcome); } $line[] = $buttons; $data[] = $line; } $table = new html_table(); $table->head = array($strfullname, $strshortname, $strscale, $strcourses, $stritems, $stredit); $table->size = array('30%', '20%', '20%', '10%', '10%', '10%'); $table->align = array('left', 'left', 'left', 'center', 'center', 'center'); $table->width = '90%'; $table->data = $data; $return .= html_writer::table($table); $outcomes_tables[] = $return; } $actionbar = new \core_grades\output\manage_outcomes_action_bar($context, !empty($outcomes_tables)); print_grade_page_head($courseid ?: SITEID, 'outcome', 'edit', $heading, false, false, true, null, null, null, $actionbar); // If there are existing outcomes, output the outcome tables. if (!empty($outcomes_tables)) { foreach ($outcomes_tables as $table) { echo $table; } } else { echo $OUTPUT->notification(get_string('noexistingoutcomes', 'grades'), 'info', false); } echo $OUTPUT->footer(); /** * Local shortcut function for creating a link to a scale. * @param int $courseid The Course ID * @param grade_scale $scale The Scale to link to * @param grade_plugin_return $gpr An object used to identify the page we just came from * @return string html */ function grade_print_scale_link($courseid, $scale, $gpr) { global $CFG, $OUTPUT; $url = new moodle_url('/grade/edit/scale/edit.php', array('courseid' => $courseid, 'id' => $scale->id)); $url = $gpr->add_url_params($url); return html_writer::link($url, $scale->get_name()); } PK\9$))outcome/import.phpnu[. /** * Import outcomes from a file * * @package core_grades * @copyright 2008 Moodle Pty Ltd (http://moodle.com) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ require_once(__DIR__.'/../../../config.php'); require_once($CFG->dirroot.'/lib/formslib.php'); require_once($CFG->dirroot.'/grade/lib.php'); require_once($CFG->libdir.'/gradelib.php'); require_once('import_outcomes_form.php'); $courseid = optional_param('courseid', 0, PARAM_INT); $action = optional_param('action', '', PARAM_ALPHA); $scope = optional_param('scope', 'custom', PARAM_ALPHA); $url = new moodle_url('/grade/edit/outcome/import.php', array('courseid' => $courseid)); $PAGE->set_url($url); $PAGE->set_pagelayout('admin'); /// Make sure they can even access this course if ($courseid) { if (!$course = $DB->get_record('course', array('id' => $courseid))) { throw new \moodle_exception('invalidcourseid'); } require_login($course); $context = context_course::instance($course->id); if (empty($CFG->enableoutcomes)) { redirect('../../index.php?id='.$courseid); } navigation_node::override_active_url(new moodle_url('/grade/edit/outcome/course.php', ['id' => $courseid])); $PAGE->navbar->add(get_string('manageoutcomes', 'grades'), new moodle_url('/grade/edit/outcome/index.php', ['id' => $courseid])); $PAGE->navbar->add(get_string('importoutcomes', 'grades'), new moodle_url('/grade/edit/outcome/import.php', ['courseid' => $courseid])); } else { require_once $CFG->libdir.'/adminlib.php'; admin_externalpage_setup('outcomes'); $context = context_system::instance(); } require_capability('moodle/grade:manageoutcomes', $context); $upload_form = new import_outcomes_form(); if ($upload_form->is_cancelled()) { redirect(new moodle_url('/grade/edit/outcome/index.php', ['id' => $courseid])); die; } print_grade_page_head($courseid, 'outcome', 'import', get_string('importoutcomes', 'grades'), false, false, false); if (!$upload_form->get_data()) { // Display the import form. $upload_form->display(); echo $OUTPUT->footer(); die; } $imported_file = $CFG->tempdir . '/outcomeimport/importedfile_'.time().'.csv'; make_temp_directory('outcomeimport'); // copying imported file if (!$upload_form->save_file('userfile', $imported_file, true)) { redirect('import.php'. ($courseid ? "?courseid=$courseid" : ''), get_string('importfilemissing', 'grades')); } /// which scope are we importing the outcomes in? if (isset($courseid) && ($scope == 'custom')) { // custom scale $local_scope = true; } elseif (($scope == 'global') && has_capability('moodle/grade:manage', context_system::instance())) { // global scale $local_scope = false; } else { // shouldn't happen .. user might be trying to access this script without the right permissions. redirect('index.php', get_string('importerror', 'grades')); } // open the file, start importing data if ($handle = fopen($imported_file, 'r')) { $line = 0; // will keep track of current line, to give better error messages. $file_headers = ''; // $csv_data needs to have at least these columns, the value is the default position in the data file. $headers = array('outcome_name' => 0, 'outcome_shortname' => 1, 'scale_name' => 3, 'scale_items' => 4); $optional_headers = array('outcome_description'=>2, 'scale_description' => 5); $imported_headers = array(); // will later be initialized with the values found in the file $fatal_error = false; $errormessage = ''; // data should be separated by a ';'. *NOT* by a comma! TODO: version 2.0 // or whenever we can depend on PHP5, set the second parameter (8192) to 0 (unlimited line length) : the database can store over 128k per line. while ( $csv_data = fgetcsv($handle, 8192, ';', '"', '\\')) { // if the line is over 8k, it won't work... $line++; // be tolerant on input, as fgetcsv returns "an array comprising a single null field" on blank lines if ($csv_data == array(null)) { continue; } // on first run, grab and analyse the header if ($file_headers == '') { $file_headers = array_flip($csv_data); // save the header line ... TODO: use the header line to let import work with columns in arbitrary order $error = false; foreach($headers as $key => $value) { // sanity check #1: make sure the file contains all the mandatory headers if (!array_key_exists($key, $file_headers)) { $error = true; break; } } if ($error) { $fatal_error = true; $errormessage = get_string('importoutcomenofile', 'grades', $line); break; } foreach(array_merge($headers, $optional_headers) as $header => $position) { // match given columns to expected columns *into* $headers $imported_headers[$header] = $file_headers[$header]; } continue; // we don't import headers } // sanity check #2: every line must have the same number of columns as there are // headers. If not, processing stops. if ( count($csv_data) != count($file_headers) ) { $fatal_error = true; $errormessage = get_string('importoutcomenofile', 'grades', $line); break; } // sanity check #3: all required fields must be present on the current line. foreach ($headers as $header => $position) { if ($csv_data[$imported_headers[$header]] == '') { $fatal_error = true; $errormessage = get_string('importoutcomenofile', 'grades', $line); break; } } // MDL-17273 errors in csv are not preventing import from happening. We break from the while loop here if ($fatal_error) { break; } $params = array($csv_data[$imported_headers['outcome_shortname']]); $wheresql = 'shortname = ? '; if ($local_scope) { $params[] = $courseid; $wheresql .= ' AND courseid = ?'; } else { $wheresql .= ' AND courseid IS NULL'; } $outcome = $DB->get_records_select('grade_outcomes', $wheresql, $params); if ($outcome) { // already exists, print a message and skip. echo $OUTPUT->notification(get_string('importskippedoutcome', 'grades', $csv_data[$imported_headers['outcome_shortname']]), 'info', false); continue; } // new outcome will be added, search for compatible existing scale... $params = array($csv_data[$imported_headers['scale_name']], $csv_data[$imported_headers['scale_items']], $courseid); $wheresql = 'name = ? AND scale = ? AND (courseid = ? OR courseid = 0)'; $scale = $DB->get_records_select('scale', $wheresql, $params); if ($scale) { // already exists in the right scope: use it. $scale_id = key($scale); } else { if (!has_capability('moodle/course:managescales', $context)) { echo $OUTPUT->notification(get_string('importskippedoutcome', 'grades', $csv_data[$imported_headers['outcome_shortname']]), 'warning', false); continue; } else { // scale doesn't exists : create it. $scale_data = array('name' => $csv_data[$imported_headers['scale_name']], 'scale' => $csv_data[$imported_headers['scale_items']], 'description' => $csv_data[$imported_headers['scale_description']], 'userid' => $USER->id); if ($local_scope) { $scale_data['courseid'] = $courseid; } else { $scale_data['courseid'] = 0; // 'global' : scale use '0', outcomes use null } $scale = new grade_scale($scale_data); $scale_id = $scale->insert(); } } // add outcome $outcome_data = array('shortname' => $csv_data[$imported_headers['outcome_shortname']], 'fullname' => $csv_data[$imported_headers['outcome_name']], 'scaleid' => $scale_id, 'description' => $csv_data[$imported_headers['outcome_description']], 'usermodified' => $USER->id); if ($local_scope) { $outcome_data['courseid'] = $courseid; } else { $outcome_data['courseid'] = null; // 'global' : scale use '0', outcomes use null } $outcome = new grade_outcome($outcome_data); $outcome_id = $outcome->insert(); $outcome_success_strings = new StdClass(); $outcome_success_strings->name = $outcome_data['fullname']; $outcome_success_strings->id = $outcome_id; echo $OUTPUT->notification(get_string('importoutcomesuccess', 'grades', $outcome_success_strings), 'success', false); } if ($fatal_error) { echo $OUTPUT->notification($errormessage, 'error', false); echo $OUTPUT->single_button(new moodle_url('/grade/edit/outcome/import.php', ['courseid' => $courseid]), get_string('back'), 'get'); } else { echo $OUTPUT->single_button(new moodle_url('/grade/edit/outcome/index.php', ['id' => $courseid]), get_string('continue'), 'get'); } } else { echo $OUTPUT->box(get_string('importoutcomenofile', 'grades', 0)); } // finish fclose($handle); // delete temp file unlink($imported_file); echo $OUTPUT->footer(); PK\FKD((tree/grade.phpnu[. /** * Edit a user's grade for a particular activity * * @package core_grades * @copyright 2007 Petr Skoda * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ require_once '../../../config.php'; require_once $CFG->dirroot.'/grade/lib.php'; require_once $CFG->dirroot.'/grade/report/lib.php'; require_once 'grade_form.php'; $courseid = required_param('courseid', PARAM_INT); $id = optional_param('id', 0, PARAM_INT); $itemid = optional_param('itemid', 0, PARAM_INT); $userid = optional_param('userid', 0, PARAM_INT); $url = new moodle_url('/grade/edit/tree/grade.php', array('courseid'=>$courseid)); if ($id !== 0) { $url->param('id', $id); } if ($itemid !== 0) { $url->param('itemid', $itemid); } if ($userid !== 0) { $url->param('userid', $userid); } $PAGE->set_url($url); if (!$course = $DB->get_record('course', array('id' => $courseid))) { throw new \moodle_exception('invalidcourseid'); } $PAGE->set_pagelayout('incourse'); require_login($course); $context = context_course::instance($course->id); if (!has_capability('moodle/grade:manage', $context)) { require_capability('moodle/grade:edit', $context); } // default return url $gpr = new grade_plugin_return(); $returnurl = $gpr->get_return_url($CFG->wwwroot.'/grade/report/index.php?id='.$course->id); // security checks! if (!empty($id)) { if (!$grade = $DB->get_record('grade_grades', array('id' => $id))) { throw new \moodle_exception('invalidgroupid'); } if (!empty($itemid) and $itemid != $grade->itemid) { throw new \moodle_exception('invaliditemid'); } $itemid = $grade->itemid; if (!empty($userid) and $userid != $grade->userid) { throw new \moodle_exception('invaliduser'); } $userid = $grade->userid; unset($grade); } else if (empty($userid) or empty($itemid)) { throw new \moodle_exception('missinguseranditemid'); } if (!$grade_item = grade_item::fetch(array('id'=>$itemid, 'courseid'=>$courseid))) { throw new \moodle_exception('cannotfindgradeitem'); } // now verify grading user has access to all groups or is member of the same group when separate groups used in course if (groups_get_course_groupmode($COURSE) == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) { if ($groups = groups_get_all_groups($COURSE->id, $userid)) { $ok = false; foreach ($groups as $group) { if (groups_is_member($group->id, $USER->id)) { $ok = true; } } if (!$ok) { throw new \moodle_exception('cannotgradeuser'); } } else { throw new \moodle_exception('cannotgradeuser'); } } $mform = new edit_grade_form(null, array('grade_item'=>$grade_item, 'gpr'=>$gpr)); if ($grade = $DB->get_record('grade_grades', array('itemid' => $grade_item->id, 'userid' => $userid))) { // always clean existing feedback - grading should not have XSS risk if (empty($grade->feedback)) { $grade->feedback = ''; } else { $options = new stdClass(); $options->filter = false; $options->noclean = false; $options->para = false; $grade->feedback = format_text($grade->feedback, $grade->feedbackformat, $options); } $grade->feedbackformat = FORMAT_HTML; $grade->locked = $grade->locked > 0 ? 1:0; $grade->overridden = $grade->overridden > 0 ? 1:0; $grade->excluded = $grade->excluded > 0 ? 1:0; if ($grade->hidden > 1) { $grade->hiddenuntil = $grade->hidden; $grade->hidden = 1; } else { $grade->hiddenuntil = 0; } if ($grade_item->is_hidden()) { $grade->hidden = 1; } if ($grade_item->is_locked()) { $grade->locked = 1; } // normalize the final grade value if ($grade_item->gradetype == GRADE_TYPE_SCALE) { if (empty($grade->finalgrade)) { $grade->finalgrade = -1; } else { $grade->finalgrade = (int)$grade->finalgrade; } } else if ($grade_item->gradetype == GRADE_TYPE_VALUE) { $grade->finalgrade = format_float($grade->finalgrade, $grade_item->get_decimals()); } $grade->oldgrade = $grade->finalgrade; $grade->oldfeedback = $grade->feedback; $grade->feedback = array('text'=>$grade->feedback, 'format'=>$grade->feedbackformat); $mform->set_data($grade); } else { $grade = new stdClass(); $grade->feedback = array('text'=>'', 'format'=>FORMAT_HTML); $mform->set_data(array('itemid'=>$itemid, 'userid'=>$userid, 'locked'=>$grade_item->locked, 'locktime'=>$grade_item->locktime)); } if ($mform->is_cancelled()) { redirect($returnurl); // form processing } else if ($data = $mform->get_data(false)) { if (isset($data->feedback) && is_array($data->feedback)) { $data->feedbackformat = $data->feedback['format']; $data->feedback = $data->feedback['text'] ?? null; } $old_grade_grade = new grade_grade(array('userid'=>$data->userid, 'itemid'=>$grade_item->id), true); //might not exist yet // fix no grade for scales if (!isset($data->finalgrade) or $data->finalgrade == $data->oldgrade) { $data->finalgrade = $old_grade_grade->finalgrade; } else if ($grade_item->gradetype == GRADE_TYPE_SCALE) { if ($data->finalgrade < 1) { $data->finalgrade = NULL; } } else if ($grade_item->gradetype == GRADE_TYPE_VALUE) { $data->finalgrade = unformat_float($data->finalgrade); } else { //this should not happen $data->finalgrade = $old_grade_grade->finalgrade; } // the overriding of feedback is tricky - we have to care about external items only if (!property_exists($data, 'feedback') or $data->feedback == $data->oldfeedback) { $data->feedback = $old_grade_grade->feedback; $data->feedbackformat = $old_grade_grade->feedbackformat; } // update final grade or feedback // when we set override grade the first time, it happens here $grade_item->update_final_grade($data->userid, $data->finalgrade, 'editgrade', $data->feedback, $data->feedbackformat); $grade_grade = new grade_grade(array('userid'=>$data->userid, 'itemid'=>$grade_item->id), true); $grade_grade->grade_item =& $grade_item; // no db fetching if (has_capability('moodle/grade:manage', $context) or has_capability('moodle/grade:edit', $context)) { // change overridden flag if (!isset($data->overridden)) { $data->overridden = 0; // checkbox unticked } $grade_grade->set_overridden($data->overridden); } if (has_capability('moodle/grade:manage', $context) or has_capability('moodle/grade:hide', $context)) { $hidden = empty($data->hidden) ? 0: $data->hidden; $hiddenuntil = empty($data->hiddenuntil) ? 0: $data->hiddenuntil; if ($grade_item->is_hidden()) { if ($old_grade_grade->hidden == 1 and $hiddenuntil == 0) { //nothing to do - grade was originally hidden, we want to keep it that way } else { $grade_grade->set_hidden($hiddenuntil); } } else { if ($hiddenuntil) { $grade_grade->set_hidden($hiddenuntil); } else { $grade_grade->set_hidden($hidden); // checkbox data might be undefined } } } if (isset($data->locked) and !$grade_item->is_locked()) { if (($old_grade_grade->locked or $old_grade_grade->locktime) and (!has_capability('moodle/grade:manage', $context) and !has_capability('moodle/grade:unlock', $context))) { //ignore data } else if ((!$old_grade_grade->locked and !$old_grade_grade->locktime) and (!has_capability('moodle/grade:manage', $context) and !has_capability('moodle/grade:lock', $context))) { //ignore data } else { $grade_grade->set_locktime($data->locktime); //set_lock may reset locktime $grade_grade->set_locked($data->locked, false, true); // reload grade in case it was regraded from activity $grade_grade = new grade_grade(array('userid'=>$data->userid, 'itemid'=>$grade_item->id), true); $grade_grade->grade_item =& $grade_item; // no db fetching } } if (isset($data->excluded) and has_capability('moodle/grade:manage', $context)) { $grade_grade->set_excluded($data->excluded); } // detect cases when we need to do full regrading if ($old_grade_grade->excluded != $grade_grade->excluded) { $parent = $grade_item->get_parent_category(); $parent->force_regrading(); } else if ($old_grade_grade->overridden != $grade_grade->overridden and empty($grade_grade->overridden)) { // only when unoverridding $grade_item->force_regrading(); } else if ($old_grade_grade->locktime != $grade_grade->locktime) { $grade_item->force_regrading(); } redirect($returnurl); } $strgrades = get_string('grades'); $strgraderreport = get_string('graderreport', 'grades'); $strgradeedit = get_string('editgrade', 'grades'); $struser = get_string('user'); grade_build_nav(__FILE__, $strgradeedit, array('courseid' => $courseid)); /*********** BEGIN OUTPUT *************/ $PAGE->set_title($strgrades . ': ' . $strgraderreport . ': ' . $strgradeedit); $PAGE->set_heading($course->fullname); echo $OUTPUT->header(); echo $OUTPUT->heading($strgradeedit); echo $OUTPUT->box_start(); // Form if in edit or add modes $mform->display(); echo $OUTPUT->box_end(); echo $OUTPUT->footer(); die; PK\ .tree/action.phpnu[. /** * Performs actions on grade items and categories like hiding and locking * * @package core_grades * @copyright 2007 Petr Skoda * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ require_once '../../../config.php'; require_once $CFG->dirroot.'/grade/lib.php'; $courseid = required_param('id', PARAM_INT); $action = required_param('action', PARAM_ALPHA); $eid = required_param('eid', PARAM_ALPHANUM); $PAGE->set_url('/grade/edit/tree/action.php', array('id'=>$courseid, 'action'=>$action, 'eid'=>$eid)); /// Make sure they can even access this course if (!$course = $DB->get_record('course', array('id' => $courseid))) { throw new \moodle_exception('invalidcourseid'); } require_login($course); $context = context_course::instance($course->id); // default return url $gpr = new grade_plugin_return(); $returnurl = $gpr->get_return_url($CFG->wwwroot.'/grade/edit/tree/index.php?id='.$course->id); // get the grading tree object $gtree = new grade_tree($courseid, false, false); // what are we working with? if (!$element = $gtree->locate_element($eid)) { throw new \moodle_exception('invalidelementid', '', $returnurl); } $object = $element['object']; $type = $element['type']; switch ($action) { case 'hide': if ($eid and confirm_sesskey()) { if (!has_capability('moodle/grade:manage', $context) and !has_capability('moodle/grade:hide', $context)) { throw new \moodle_exception('nopermissiontohide', '', $returnurl); } if ($type == 'grade' and empty($object->id)) { $object->insert(); } if (!$object->can_control_visibility()) { throw new \moodle_exception('componentcontrolsvisibility', 'grades', $returnurl); } $object->set_hidden(1, true); } break; case 'show': if ($eid and confirm_sesskey()) { if (!has_capability('moodle/grade:manage', $context) and !has_capability('moodle/grade:hide', $context)) { throw new \moodle_exception('nopermissiontoshow', '', $returnurl); } if ($type == 'grade' and empty($object->id)) { $object->insert(); } if (!$object->can_control_visibility()) { throw new \moodle_exception('componentcontrolsvisibility', 'grades', $returnurl); } $object->set_hidden(0, true); } break; case 'lock': if ($eid and confirm_sesskey()) { if (!has_capability('moodle/grade:manage', $context) and !has_capability('moodle/grade:lock', $context)) { throw new \moodle_exception('nopermissiontolock', '', $returnurl); } if ($type == 'grade' and empty($object->id)) { $object->insert(); } $object->set_locked(1, false, true); } break; case 'unlock': if ($eid and confirm_sesskey()) { if (!has_capability('moodle/grade:manage', $context) and !has_capability('moodle/grade:unlock', $context)) { throw new \moodle_exception('nopermissiontounlock', '', $returnurl); } if ($type == 'grade' and empty($object->id)) { $object->insert(); } $object->set_locked(0, false, true); } break; case 'resetweights': if ($eid && confirm_sesskey()) { // This is specific to category items with natural weight as an aggregation method, and can // only be done by someone who can manage the grades. if ($type != 'category' || $object->aggregation != GRADE_AGGREGATE_SUM || !has_capability('moodle/grade:manage', $context)) { throw new \moodle_exception('nopermissiontoresetweights', 'grades', $returnurl); } // Remove the weightoverride flag from the children. $children = $object->get_children(); foreach ($children as $item) { if ($item['type'] == 'category') { $gradeitem = $item['object']->load_grade_item(); } else { $gradeitem = $item['object']; } if ($gradeitem->weightoverride == false) { continue; } $gradeitem->weightoverride = false; $gradeitem->update(); } // Force regrading. $object->force_regrading(); } } redirect($returnurl); //redirect($returnurl, 'debug delay', 5); PK\ַtree/calculation_form.phpnu[. /** * A moodleform to allow the editing of a calculated grade item * * @package core_grades * @copyright 2007 Petr Skoda * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ if (!defined('MOODLE_INTERNAL')) { die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page } require_once $CFG->libdir.'/formslib.php'; class edit_calculation_form extends moodleform { public $available; public $noidnumbers; function definition() { global $COURSE; $mform =& $this->_form; $itemid = $this->_customdata['itemid']; $this->available = grade_item::fetch_all(array('courseid'=>$COURSE->id)); $this->noidnumbers = array(); // All items that have no idnumbers are added to a separate section of the form (hidden by default), // enabling the user to assign idnumbers to these grade_items. foreach ($this->available as $item) { if (empty($item->idnumber)) { $this->noidnumbers[$item->id] = $item; unset($this->available[$item->id]); } if ($item->id == $itemid) { // Do not include the current grade_item in the available section unset($this->available[$item->id]); } } /// visible elements $mform->addElement('header', 'general', get_string('gradeitem', 'grades')); $mform->addElement('static', 'itemname', get_string('itemname', 'grades')); $mform->addElement('textarea', 'calculation', get_string('calculation', 'grades'), 'cols="60" rows="5"'); $mform->addHelpButton('calculation', 'calculation', 'grades'); $mform->setForceLtr('calculation'); /// hidden params $mform->addElement('hidden', 'id', 0); $mform->setType('id', PARAM_INT); $mform->addElement('hidden', 'courseid', 0); $mform->setType('courseid', PARAM_INT); $mform->addElement('hidden', 'section', 0); $mform->setType('section', PARAM_ALPHA); $mform->setDefault('section', 'calculation'); /// add return tracking info $gpr = $this->_customdata['gpr']; $gpr->add_mform_elements($mform); $this->add_action_buttons(); } function definition_after_data() { global $CFG, $COURSE; $mform =& $this->_form; } /// perform extra validation before submission function validation($data, $files) { $errors = parent::validation($data, $files); $mform =& $this->_form; // check the calculation formula if ($data['calculation'] != '') { $grade_item = grade_item::fetch(array('id'=>$data['id'], 'courseid'=>$data['courseid'])); $calculation = calc_formula::unlocalize(stripslashes($data['calculation'])); $result = $grade_item->validate_formula($calculation); if ($result !== true) { $errors['calculation'] = $result; } } return $errors; } } PK\::tree/category.phpnu[. /** * Edit the grade options for an individual grade category * * @package core_grades * @copyright 2007 Petr Skoda * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ use core_grades\form\add_category; require_once('../../../config.php'); require_once($CFG->dirroot.'/grade/lib.php'); require_once($CFG->dirroot.'/grade/edit/tree/lib.php'); require_once($CFG->dirroot.'/grade/report/lib.php'); require_once('category_form.php'); $courseid = required_param('courseid', PARAM_INT); $id = optional_param('id', 0, PARAM_INT); // grade_category->id $url = new moodle_url('/grade/edit/tree/category.php', array('courseid'=>$courseid)); if ($id !== 0) { $url->param('id', $id); } $PAGE->set_url($url); $PAGE->set_pagelayout('admin'); navigation_node::override_active_url(new moodle_url('/grade/edit/tree/index.php', array('id'=>$courseid))); if (!$course = $DB->get_record('course', array('id' => $courseid))) { throw new \moodle_exception('invalidcourseid'); } require_login($course); $context = context_course::instance($course->id); require_capability('moodle/grade:manage', $context); // default return url $gpr = new grade_plugin_return(); $returnurl = $gpr->get_return_url('index.php?id='.$course->id); $heading = get_string('categoryedit', 'grades'); if ($id) { if (!$grade_category = grade_category::fetch(array('id'=>$id, 'courseid'=>$course->id))) { throw new \moodle_exception('invalidcategory'); } $grade_category->apply_forced_settings(); $category = $grade_category->get_record_data(); // set parent $category->parentcategory = $grade_category->parent; $grade_item = $grade_category->load_grade_item(); // nomalize coef values if needed $parent_category = $grade_category->get_parent_category(); foreach ($grade_item->get_record_data() as $key => $value) { $category->{"grade_item_$key"} = $value; } $decimalpoints = $grade_item->get_decimals(); $category->grade_item_grademax = format_float($category->grade_item_grademax, $decimalpoints); $category->grade_item_grademin = format_float($category->grade_item_grademin, $decimalpoints); $category->grade_item_gradepass = format_float($category->grade_item_gradepass, $decimalpoints); $category->grade_item_multfactor = format_float($category->grade_item_multfactor, 4); $category->grade_item_plusfactor = format_float($category->grade_item_plusfactor, 4); $category->grade_item_aggregationcoef2 = format_float($category->grade_item_aggregationcoef2 * 100.0, 4); if (!$parent_category) { // keep as is } else if ($parent_category->aggregation == GRADE_AGGREGATE_SUM or $parent_category->aggregation == GRADE_AGGREGATE_WEIGHTED_MEAN2) { $category->grade_item_aggregationcoef = $category->grade_item_aggregationcoef == 0 ? 0 : 1; } else { $category->grade_item_aggregationcoef = format_float($category->grade_item_aggregationcoef, 4); } // Check to see if the gradebook is frozen. This allows grades to not be altered at all until a user verifies that they // wish to update the grades. $gradebookcalculationsfreeze = get_config('core', 'gradebook_calculations_freeze_' . $courseid); // Stick with the original code if the grade book is frozen. if ($gradebookcalculationsfreeze && (int)$gradebookcalculationsfreeze <= 20150627) { if ($category->aggregation == GRADE_AGGREGATE_SUM) { // Input fields for grademin and grademax are disabled for the "Natural" category, // this means they will be ignored if user does not change aggregation method. // But if user does change aggregation method the default values should be used. $category->grademax = 100; $category->grade_item_grademax = 100; $category->grademin = 0; $category->grade_item_grademin = 0; } } else { if ($category->aggregation == GRADE_AGGREGATE_SUM && !$grade_item->is_calculated()) { // Input fields for grademin and grademax are disabled for the "Natural" category, // this means they will be ignored if user does not change aggregation method. // But if user does change aggregation method the default values should be used. // This does not apply to calculated category totals. $category->grademax = 100; $category->grade_item_grademax = 100; $category->grademin = 0; $category->grade_item_grademin = 0; } } } else { $heading = get_string('newcategory', 'grades'); $grade_category = new grade_category(array('courseid'=>$courseid), false); $grade_category->apply_default_settings(); $grade_category->apply_forced_settings(); $category = $grade_category->get_record_data(); $grade_item = new grade_item(array('courseid'=>$courseid, 'itemtype'=>'manual'), false); foreach ($grade_item->get_record_data() as $key => $value) { $category->{"grade_item_$key"} = $value; } } $mform = new edit_category_form(null, array('current'=>$category, 'gpr'=>$gpr)); $simpleform = new add_category(null, ['category' => $grade_category->id, 'courseid' => $courseid, 'gpr' => $gpr]); // Data has been carried over from the dynamic form. if ($simpledata = $simpleform->get_submitted_data()) { $mform->set_data($simpledata); } if ($mform->is_cancelled()) { redirect($returnurl); } else if ($data = $mform->get_data()) { grade_edit_tree::update_gradecategory($grade_category, $data); redirect($returnurl); } $PAGE->navbar->add($heading); print_grade_page_head($courseid, 'settings', null, $heading, false, false, false); $mform->display(); echo $OUTPUT->footer(); die; PK\iHWW tree/lib.phpnu[. /** * A library of classes used by the grade edit pages * * @package core_grades * @copyright 2009 Nicolas Connault * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class grade_edit_tree { public $columns = array(); /** * @var grade_tree $gtree @see grade/lib.php */ public $gtree; /** * @var grade_plugin_return @see grade/lib.php */ public $gpr; /** * @var string $moving The eid of the category or item being moved */ public $moving; public $deepest_level; public $uses_weight = false; public $table; public $categories = array(); /** * Constructor */ public function __construct($gtree, $moving, $gpr) { global $USER, $OUTPUT, $COURSE; $this->gtree = $gtree; $this->moving = $moving; $this->gpr = $gpr; $this->deepest_level = $this->get_deepest_level($this->gtree->top_element); $this->columns = array(grade_edit_tree_column::factory('name', array('deepest_level' => $this->deepest_level))); if ($this->uses_weight) { $this->columns[] = grade_edit_tree_column::factory('weight', array('adv' => 'weight')); } $this->columns[] = grade_edit_tree_column::factory('range'); // This is not a setting... How do we deal with it? $this->columns[] = grade_edit_tree_column::factory('status'); $this->columns[] = grade_edit_tree_column::factory('actions'); $this->table = new html_table(); $this->table->id = "grade_edit_tree_table"; $this->table->attributes['class'] = 'generaltable simple setup-grades'; if ($this->moving) { $this->table->attributes['class'] .= ' moving'; } foreach ($this->columns as $column) { if (!($this->moving && $column->hide_when_moving)) { $this->table->head[] = $column->get_header_cell(); } } $rowcount = 0; $this->table->data = $this->build_html_tree($this->gtree->top_element, true, array(), 0, $rowcount); } /** * Recursive function for building the table holding the grade categories and items, * with CSS indentation and styles. * * @param array $element The current tree element being rendered * @param boolean $totals Whether or not to print category grade items (category totals) * @param array $parents An array of parent categories for the current element (used for indentation and row classes) * * @return string HTML */ public function build_html_tree($element, $totals, $parents, $level, &$row_count) { global $CFG, $COURSE, $PAGE, $OUTPUT; $object = $element['object']; $eid = $element['eid']; $name = grade_helper::get_element_header($element, true, false, true, false, true); $icon = grade_helper::get_element_icon($element); $type = grade_helper::get_element_type_string($element); $strippedname = grade_helper::get_element_header($element, false, false, false); $is_category_item = false; if ($element['type'] == 'categoryitem' || $element['type'] == 'courseitem') { $is_category_item = true; } $rowclasses = array(); foreach ($parents as $parent_eid) { $rowclasses[] = $parent_eid; } $moveaction = ''; $actions = $this->gtree->get_cell_action_menu($element, 'setup', $this->gpr); if ($element['type'] == 'item' or ($element['type'] == 'category' and $element['depth'] > 1)) { $aurl = new moodle_url('index.php', array('id' => $COURSE->id, 'action' => 'moveselect', 'eid' => $eid, 'sesskey' => sesskey())); $moveaction .= $OUTPUT->action_icon($aurl, new pix_icon('t/move', get_string('move'))); } $returnrows = array(); $root = false; $id = required_param('id', PARAM_INT); /// prepare move target if needed $last = ''; /// print the list items now if ($this->moving == $eid) { // do not diplay children $cell = new html_table_cell(); $cell->colspan = 12; $cell->attributes['class'] = $element['type'] . ' moving column-name level' . ($level + 1) . ' level' . ($level % 2 ? 'even' : 'odd'); $cell->text = $name.' ('.get_string('move').')'; // Create a row that represents the available area to move a grade item or a category into. $movingarea = new html_table_row(); // Obtain all parent category identifiers for this item and add them to its class list. This information // will be used when collapsing or expanding grade categories to properly show or hide this area. $parentcategories = array_merge($rowclasses, [$eid]); $movingarea->attributes = [ 'class' => implode(' ', $parentcategories), 'data-hidden' => 'false' ]; $movingarea->cells[] = $cell; return [$movingarea]; } if ($element['type'] == 'category') { $level++; $this->categories[$object->id] = $strippedname; $category = grade_category::fetch(array('id' => $object->id)); $category->load_grade_item(); // Add aggregation coef input if not a course item and if parent category has correct aggregation type // Before we print the category's row, we must find out how many rows will appear below it (for the filler cell's rowspan) $aggregation_position = grade_get_setting($COURSE->id, 'aggregationposition', $CFG->grade_aggregationposition); $category_total_data = null; // Used if aggregationposition is set to "last", so we can print it last $html_children = array(); // Take into consideration that a category item always has an empty row (spacer) below. $row_count = 1; foreach($element['children'] as $child_el) { $moveto = null; if (empty($child_el['object']->itemtype)) { $child_el['object']->itemtype = false; } if (($child_el['object']->itemtype == 'course' || $child_el['object']->itemtype == 'category') && !$totals) { continue; } $child_eid = $child_el['eid']; $first = ''; if ($child_el['object']->itemtype == 'course' || $child_el['object']->itemtype == 'category') { $first = array('first' => 1); $child_eid = $eid; } if ($this->moving && $this->moving != $child_eid) { $strmove = get_string('move'); $actions = $moveaction = ''; // no action icons when moving $aurl = new moodle_url('index.php', array('id' => $COURSE->id, 'action' => 'move', 'eid' => $this->moving, 'moveafter' => $child_eid, 'sesskey' => sesskey())); if ($first) { $aurl->params($first); } $cell = new html_table_cell(); $cell->colspan = 12; $cell->attributes['class'] = 'movehere level' . ($level + 1) . ' level' . ($level % 2 ? 'even' : 'odd'); $cell->text = html_writer::link($aurl, html_writer::empty_tag('hr'), ['title' => get_string('movehere'), 'class' => 'movehere']); // Create a row that represents the available area to move a grade item or a category into. $moveto = new html_table_row(); // Obtain all parent category identifiers for this item and add them to its class list. This information // will be used when collapsing or expanding grade categories to properly show or hide this area. $parentcategories = array_merge($rowclasses, [$eid]); $moveto->attributes['class'] = implode(' ', $parentcategories); $moveto->attributes['data-hidden'] = 'false'; $moveto->cells[] = $cell; } $newparents = $parents; $newparents[] = $eid; $row_count++; $child_row_count = 0; // If moving, do not print course and category totals, but still print the moveto target box if ($this->moving && ($child_el['object']->itemtype == 'course' || $child_el['object']->itemtype == 'category')) { $html_children[] = $moveto; } elseif ($child_el['object']->itemtype == 'course' || $child_el['object']->itemtype == 'category') { // We don't build the item yet because we first need to know the deepest level of categories (for category/name colspans) $category_total_item = $this->build_html_tree($child_el, $totals, $newparents, $level, $child_row_count); if (!$aggregation_position) { $html_children = array_merge($html_children, $category_total_item); } } else { $html_children = array_merge($html_children, $this->build_html_tree($child_el, $totals, $newparents, $level, $child_row_count)); if (!empty($moveto)) { $html_children[] = $moveto; } if ($this->moving) { $row_count++; } } $row_count += $child_row_count; // If the child is a category, increment row_count by one more (for the extra coloured row) if ($child_el['type'] == 'category') { $row_count++; } } // Print category total at the end if aggregation position is "last" (1) if (!empty($category_total_item) && $aggregation_position) { $html_children = array_merge($html_children, $category_total_item); } // Determine if we are at the root if (isset($element['object']->grade_item) && $element['object']->grade_item->is_course_item()) { $root = true; } $levelclass = "level$level level" . ($level % 2 ? 'odd' : 'even'); $courseclass = ''; if ($level == 1) { $courseclass = 'coursecategory'; } $categoryrow = new html_table_row(); $categoryrow->id = 'grade-item-' . $eid; $categoryrow->attributes['class'] = $courseclass . ' category '; $categoryrow->attributes['data-category'] = $eid; if (!empty($parent_eid)) { $categoryrow->attributes['data-parent-category'] = $parent_eid; } $categoryrow->attributes['data-aggregation'] = $category->aggregation; $categoryrow->attributes['data-grademax'] = $category->grade_item->grademax; $categoryrow->attributes['data-aggregationcoef'] = floatval($category->grade_item->aggregationcoef); $categoryrow->attributes['data-itemid'] = $category->grade_item->id; $categoryrow->attributes['data-hidden'] = 'false'; foreach ($rowclasses as $class) { $categoryrow->attributes['class'] .= ' ' . $class; } foreach ($this->columns as $column) { if (!($this->moving && $column->hide_when_moving)) { $categoryrow->cells[] = $column->get_category_cell($category, $levelclass, [ 'id' => $id, 'name' => $name, 'level' => $level, 'actions' => $actions, 'moveaction' => $moveaction, 'eid' => $eid, ]); } } $emptyrow = new html_table_row(); // Obtain all parent category identifiers for this item and add them to its class list. This information // will be used when collapsing or expanding grade categories to properly show or hide this area. $parentcategories = array_merge($rowclasses, [$eid]); $emptyrow->attributes['class'] = 'spacer ' . implode(' ', $parentcategories); $emptyrow->attributes['data-hidden'] = 'false'; $emptyrow->attributes['aria-hidden'] = 'true'; $headercell = new html_table_cell(); $headercell->header = true; $headercell->scope = 'row'; $headercell->attributes['class'] = 'cell column-rowspan rowspan'; $headercell->attributes['aria-hidden'] = 'true'; $headercell->rowspan = $row_count; $emptyrow->cells[] = $headercell; $returnrows = array_merge([$categoryrow, $emptyrow], $html_children); // Print a coloured row to show the end of the category across the table $endcell = new html_table_cell(); $endcell->colspan = (19 - $level); $endcell->attributes['class'] = 'emptyrow colspan ' . $levelclass; $endcell->attributes['aria-hidden'] = 'true'; $returnrows[] = new html_table_row(array($endcell)); } else { // Dealing with a grade item $item = grade_item::fetch(array('id' => $object->id)); $element['type'] = 'item'; $element['object'] = $item; $categoryitemclass = ''; if ($item->itemtype == 'category') { $categoryitemclass = 'categoryitem'; } if ($item->itemtype == 'course') { $categoryitemclass = 'courseitem'; } $gradeitemrow = new html_table_row(); $gradeitemrow->id = 'grade-item-' . $eid; $gradeitemrow->attributes['class'] = $categoryitemclass . ' item '; $gradeitemrow->attributes['data-itemid'] = $object->id; $gradeitemrow->attributes['data-hidden'] = 'false'; // If this item is a course or category aggregation, add a data attribute that stores the identifier of // the related category or course. This attribute is used when collapsing a grade category to fetch the // max grade from the aggregation and display it in the grade category row when the category items are // collapsed and the aggregated max grade is not visible. if (!empty($categoryitemclass)) { $gradeitemrow->attributes['data-aggregationforcategory'] = $parent_eid; } else { $gradeitemrow->attributes['data-parent-category'] = $parent_eid; $gradeitemrow->attributes['data-grademax'] = $object->grademax; $gradeitemrow->attributes['data-aggregationcoef'] = floatval($object->aggregationcoef); } foreach ($rowclasses as $class) { $gradeitemrow->attributes['class'] .= ' ' . $class; } foreach ($this->columns as $column) { if (!($this->moving && $column->hide_when_moving)) { $gradeitemrow->cells[] = $column->get_item_cell( $item, [ 'id' => $id, 'name' => $name, 'level' => $level, 'actions' => $actions, 'element' => $element, 'eid' => $eid, 'moveaction' => $moveaction, 'itemtype' => $object->itemtype, 'icon' => $icon, 'type' => $type, ] ); } } $returnrows[] = $gradeitemrow; } return $returnrows; } /** * Given a grade_item object, returns a labelled input if an aggregation coefficient (weight or extra credit) applies to it. * @param grade_item $item * @return string HTML */ static function get_weight_input($item) { global $OUTPUT; if (!is_object($item) || get_class($item) !== 'grade_item') { throw new Exception('grade_edit_tree::get_weight_input($item) was given a variable that is not of the required type (grade_item object)'); return false; } if ($item->is_course_item()) { return ''; } $parent_category = $item->get_parent_category(); $parent_category->apply_forced_settings(); $aggcoef = $item->get_coefstring(); $itemname = $item->itemname; if ($item->is_category_item()) { // Remember, the parent category of a category item is the category itself. $itemname = $parent_category->get_name(); } $str = ''; if ($aggcoef == 'aggregationcoefweight' || $aggcoef == 'aggregationcoef' || $aggcoef == 'aggregationcoefextraweight') { return $OUTPUT->render_from_template('core_grades/weight_field', [ 'id' => $item->id, 'itemname' => $itemname, 'value' => self::format_number($item->aggregationcoef) ]); } else if ($aggcoef == 'aggregationcoefextraweightsum') { $tpldata = [ 'id' => $item->id, 'itemname' => $itemname, 'value' => self::format_number($item->aggregationcoef2 * 100.0), 'checked' => $item->weightoverride, 'disabled' => !$item->weightoverride ]; $str .= $OUTPUT->render_from_template('core_grades/weight_override_field', $tpldata); } return $str; } // Trims trailing zeros. // Used on the 'Gradebook setup' page for grade items settings like aggregation co-efficient. // Grader report has its own decimal place settings so they are handled elsewhere. static function format_number($number) { $formatted = rtrim(format_float($number, 4),'0'); if (substr($formatted, -1)==get_string('decsep', 'langconfig')) { //if last char is the decimal point $formatted .= '0'; } return $formatted; } /** * Given an element of the grade tree, returns whether it is deletable or not (only manual grade items are deletable) * * @param array $element * @return bool */ public static function element_deletable($element) { global $COURSE; if ($element['type'] != 'item') { return true; } $grade_item = $element['object']; if ($grade_item->itemtype != 'mod' or $grade_item->is_outcome_item() or $grade_item->gradetype == GRADE_TYPE_NONE) { return true; } $modinfo = get_fast_modinfo($COURSE); if (!isset($modinfo->instances[$grade_item->itemmodule][$grade_item->iteminstance])) { // module does not exist return true; } return false; } /** * Given an element of the grade tree, returns whether it is duplicatable or not (only manual grade items are duplicatable) * * @param array $element * @return bool */ public static function element_duplicatable($element) { if ($element['type'] != 'item') { return false; } $gradeitem = $element['object']; if ($gradeitem->itemtype != 'mod') { return true; } return false; } /** * Given the grade tree and an array of element ids (e.g. c15, i42), and expecting the 'moveafter' URL param, * moves the selected items to the requested location. Then redirects the user to the given $returnurl * * @param object $gtree The grade tree (a recursive representation of the grade categories and grade items) * @param array $eids * @param string $returnurl */ function move_elements($eids, $returnurl) { $moveafter = required_param('moveafter', PARAM_INT); if (!is_array($eids)) { $eids = array($eids); } if(!$after_el = $this->gtree->locate_element("cg$moveafter")) { throw new \moodle_exception('invalidelementid', '', $returnurl); } $after = $after_el['object']; $parent = $after; $sortorder = $after->get_sortorder(); foreach ($eids as $eid) { if (!$element = $this->gtree->locate_element($eid)) { throw new \moodle_exception('invalidelementid', '', $returnurl); } $object = $element['object']; $object->set_parent($parent->id); $object->move_after_sortorder($sortorder); $sortorder++; } redirect($returnurl, '', 0); } /** * Recurses through the entire grade tree to find and return the maximum depth of the tree. * This should be run only once from the root element (course category), and is used for the * indentation of the Name column's cells (colspan) * * @param array $element An array of values representing a grade tree's element (all grade items in this case) * @param int $level The level of the current recursion * @param int $deepest_level A value passed to each subsequent level of recursion and incremented if $level > $deepest_level * @return int Deepest level */ function get_deepest_level($element, $level=0, $deepest_level=1) { $object = $element['object']; $level++; $coefstring = $element['object']->get_coefstring(); if ($element['type'] == 'category') { if ($coefstring == 'aggregationcoefweight' || $coefstring == 'aggregationcoefextraweightsum' || $coefstring == 'aggregationcoefextraweight') { $this->uses_weight = true; } foreach($element['children'] as $child_el) { if ($level > $deepest_level) { $deepest_level = $level; } $deepest_level = $this->get_deepest_level($child_el, $level, $deepest_level); } $category = grade_category::fetch(array('id' => $object->id)); $item = $category->get_grade_item(); if ($item->gradetype == GRADE_TYPE_NONE) { // Add 1 more level for grade category that has no total. $deepest_level++; } } return $deepest_level; } /** * Updates the provided gradecategory item with the provided data. * * @param grade_category $gradecategory The category to update. * @param stdClass $data the data to update the category with. * @return void */ public static function update_gradecategory(grade_category $gradecategory, stdClass $data) { // If no fullname is entered for a course category, put ? in the DB. if (!isset($data->fullname) || $data->fullname == '') { $data->fullname = '?'; } if (!isset($data->aggregateonlygraded)) { $data->aggregateonlygraded = 0; } if (!isset($data->aggregateoutcomes)) { $data->aggregateoutcomes = 0; } grade_category::set_properties($gradecategory, $data); // CATEGORY. if (empty($gradecategory->id)) { $gradecategory->insert(); } else { $gradecategory->update(); } // GRADE ITEM. // Grade item data saved with prefix "grade_item_". $itemdata = new stdClass(); foreach ($data as $k => $v) { if (preg_match('/grade_item_(.*)/', $k, $matches)) { $itemdata->{$matches[1]} = $v; } } if (!isset($itemdata->aggregationcoef)) { $itemdata->aggregationcoef = 0; } if (!isset($itemdata->gradepass) || $itemdata->gradepass == '') { $itemdata->gradepass = 0; } if (!isset($itemdata->grademax) || $itemdata->grademax == '') { $itemdata->grademax = 0; } if (!isset($itemdata->grademin) || $itemdata->grademin == '') { $itemdata->grademin = 0; } $hidden = empty($itemdata->hidden) ? 0 : $itemdata->hidden; $hiddenuntil = empty($itemdata->hiddenuntil) ? 0 : $itemdata->hiddenuntil; unset($itemdata->hidden); unset($itemdata->hiddenuntil); $locked = empty($itemdata->locked) ? 0 : $itemdata->locked; $locktime = empty($itemdata->locktime) ? 0 : $itemdata->locktime; unset($itemdata->locked); unset($itemdata->locktime); $convert = array('grademax', 'grademin', 'gradepass', 'multfactor', 'plusfactor', 'aggregationcoef', 'aggregationcoef2'); foreach ($convert as $param) { if (property_exists($itemdata, $param)) { $itemdata->$param = unformat_float($itemdata->$param); } } if (isset($itemdata->aggregationcoef2)) { $itemdata->aggregationcoef2 = $itemdata->aggregationcoef2 / 100.0; } // When creating a new category, a number of grade item fields are filled out automatically, and are required. // If the user leaves these fields empty during creation of a category, we let the default values take effect. // Otherwise, we let the user-entered grade item values take effect. $gradeitem = $gradecategory->load_grade_item(); $gradeitemcopy = fullclone($gradeitem); grade_item::set_properties($gradeitem, $itemdata); if (empty($gradeitem->id)) { $gradeitem->id = $gradeitemcopy->id; } if (empty($gradeitem->grademax) && $gradeitem->grademax != '0') { $gradeitem->grademax = $gradeitemcopy->grademax; } if (empty($gradeitem->grademin) && $gradeitem->grademin != '0') { $gradeitem->grademin = $gradeitemcopy->grademin; } if (empty($gradeitem->gradepass) && $gradeitem->gradepass != '0') { $gradeitem->gradepass = $gradeitemcopy->gradepass; } if (empty($gradeitem->aggregationcoef) && $gradeitem->aggregationcoef != '0') { $gradeitem->aggregationcoef = $gradeitemcopy->aggregationcoef; } // Handle null decimals value - must be done before update! if (!property_exists($itemdata, 'decimals') or $itemdata->decimals < 0) { $gradeitem->decimals = null; } // Change weightoverride flag. Check if the value is set, because it is not when the checkbox is not ticked. $itemdata->weightoverride = isset($itemdata->weightoverride) ? $itemdata->weightoverride : 0; if ($gradeitem->weightoverride != $itemdata->weightoverride && $gradecategory->aggregation == GRADE_AGGREGATE_SUM) { // If we are using natural weight and the weight has been un-overriden, force parent category to recalculate weights. $gradecategory->force_regrading(); } $gradeitem->weightoverride = $itemdata->weightoverride; $gradeitem->outcomeid = null; // This means we want to rescale overridden grades as well. if (!empty($data->grade_item_rescalegrades) && $data->grade_item_rescalegrades == 'yes') { $gradeitem->markasoverriddenwhengraded = false; $gradeitem->rescale_grades_keep_percentage($gradeitemcopy->grademin, $gradeitemcopy->grademax, $gradeitem->grademin, $gradeitem->grademax, 'gradebook'); } // Only update the category's 'hidden' status if it has changed. Leaving a category as 'unhidden' (checkbox left // unmarked) and submitting the form without this conditional check will result in displaying any grade items that // are in the category, including those that were previously 'hidden'. if (($gradecategory->get_hidden() != $hiddenuntil) || ($gradecategory->get_hidden() != $hidden)) { if ($hiddenuntil) { $gradecategory->set_hidden($hiddenuntil, true); } else { $gradecategory->set_hidden($hidden, true); } } $gradeitem->set_locktime($locktime); // Locktime first - it might be removed when unlocking. $gradeitem->set_locked($locked, false, true); $gradeitem->update(); // We don't need to insert it, it's already created when the category is created. // Set parent if needed. if (isset($data->parentcategory)) { $gradecategory->set_parent($data->parentcategory, 'gradebook'); } } } /** * Class grade_edit_tree_column * * @package core_grades * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ abstract class grade_edit_tree_column { public $forced; public $hidden; public $forced_hidden; public $advanced_hidden; public $hide_when_moving = true; /** * html_table_cell object used as a template for header cells in all categories. * It must be cloned before being used. * @var html_table_cell $headercell */ public $headercell; /** * html_table_cell object used as a template for category cells in all categories. * It must be cloned before being used. * @var html_table_cell $categorycell */ public $categorycell; /** * html_table_cell object used as a template for item cells in all categories. * It must be cloned before being used. * @var html_table_cell $itemcell */ public $itemcell; public static function factory($name, $params=array()) { $class_name = "grade_edit_tree_column_$name"; if (class_exists($class_name)) { return new $class_name($params); } } abstract public function get_header_cell(); public function get_category_cell($category, $levelclass, $params) { $cell = clone($this->categorycell); $cell->attributes['class'] .= ' ' . $levelclass; return $cell; } public function get_item_cell($item, $params) { $cell = clone($this->itemcell); if (isset($params['level'])) { $level = $params['level'] + (($item->itemtype == 'category' || $item->itemtype == 'course') ? 0 : 1); $cell->attributes['class'] .= ' level' . $level; $cell->attributes['class'] .= ' level' . ($level % 2 ? 'odd' : 'even'); } return $cell; } public function __construct() { $this->headercell = new html_table_cell(); $this->headercell->header = true; $this->headercell->attributes['class'] = 'header'; $this->categorycell = new html_table_cell(); $this->categorycell->attributes['class'] = 'cell'; $this->itemcell = new html_table_cell(); $this->itemcell->attributes['class'] = 'cell'; if (preg_match('/^grade_edit_tree_column_(\w*)$/', get_class($this), $matches)) { $this->headercell->attributes['class'] .= ' column-' . $matches[1]; $this->categorycell->attributes['class'] .= ' column-' . $matches[1]; $this->itemcell->attributes['class'] .= ' column-' . $matches[1]; } } } /** * Class grade_edit_tree_column_name * * @package core_grades * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class grade_edit_tree_column_name extends grade_edit_tree_column { public $forced = false; public $hidden = false; public $forced_hidden = false; public $advanced_hidden = false; public $deepest_level = 1; public $hide_when_moving = false; public function __construct($params) { if (empty($params['deepest_level'])) { throw new Exception('Tried to instantiate a grade_edit_tree_column_name object without the "deepest_level" param!'); } $this->deepest_level = $params['deepest_level']; parent::__construct(); } public function get_header_cell() { $headercell = clone($this->headercell); $headercell->colspan = $this->deepest_level + 1; $headercell->text = get_string('name'); return $headercell; } public function get_category_cell($category, $levelclass, $params) { global $OUTPUT; if (empty($params['name']) || empty($params['level'])) { throw new Exception('Array key (name or level) missing from 3rd param of grade_edit_tree_column_name::get_category_cell($category, $levelclass, $params)'); } $visibilitytoggle = $OUTPUT->render_from_template('core_grades/grade_category_visibility_toggle', [ 'category' => $params['eid'] ]); $mastercheckbox = ''; if ($this->deepest_level > 1) { if (empty($params['eid'])) { throw new Exception('Array key (eid) missing from 3rd param of ' . 'grade_edit_tree_column_name::get_category_cell($category, $levelclass, $params)'); } // Get toggle group for this master checkbox. $togglegroup = $this->get_checkbox_togglegroup($category); // Set label for this master checkbox. $masterlabel = $params['level'] === 1 ? get_string('all') : $params['name']; // Build the master checkbox. $mastercheckbox = new \core\output\checkbox_toggleall($togglegroup, true, [ 'id' => 'select_category_' . $category->id, 'name' => $togglegroup, 'value' => 1, 'classes' => 'itemselect ignoredirty', 'label' => $masterlabel, // Consistent label to prevent the select column from resizing. 'selectall' => $masterlabel, 'deselectall' => $masterlabel, 'labelclasses' => 'accesshide', ]); $mastercheckbox = $OUTPUT->render($mastercheckbox); } $moveaction = isset($params['moveaction']) ? $params['moveaction'] : ''; $categorycell = parent::get_category_cell($category, $levelclass, $params); $categorycell->colspan = ($this->deepest_level + 2) - $params['level']; $rowtitle = html_writer::div($params['name'], 'rowtitle'); $categorycell->text = html_writer::div($mastercheckbox . $visibilitytoggle . $moveaction . $rowtitle, 'fw-bold'); return $categorycell; } public function get_item_cell($item, $params) { if (empty($params['element']) || empty($params['name']) || empty($params['level'])) { throw new Exception('Array key (name, level or element) missing from 2nd param of grade_edit_tree_column_name::get_item_cell($item, $params)'); } $itemicon = \html_writer::div($params['icon'], 'me-1'); $itemtype = \html_writer::span($params['type'], 'd-block text-uppercase small dimmed_text'); $itemtitle = html_writer::div($params['name'], 'rowtitle'); $content = \html_writer::div($itemtype . $itemtitle); $moveaction = isset($params['moveaction']) ? $params['moveaction'] : ''; $itemcell = parent::get_item_cell($item, $params); $itemcell->colspan = ($this->deepest_level + 1) - $params['level']; $checkbox = ''; if (($this->deepest_level > 1) && ($params['itemtype'] != 'course') && ($params['itemtype'] != 'category')) { global $OUTPUT; $label = get_string('select', 'grades', $params['name']); if (empty($params['itemtype']) || empty($params['eid'])) { throw new \moodle_exception('missingitemtypeoreid', 'core_grades'); } // Fetch the grade item's category. $category = $item->get_parent_category(); $togglegroup = $this->get_checkbox_togglegroup($category); $checkboxid = 'select_' . $params['eid']; $checkbox = new \core\output\checkbox_toggleall($togglegroup, false, [ 'id' => $checkboxid, 'name' => $checkboxid, 'label' => $label, 'labelclasses' => 'accesshide', 'classes' => 'itemselect ignoredirty', ]); $checkbox = $OUTPUT->render($checkbox); } $itemcell->text = \html_writer::div($checkbox . $moveaction . $itemicon . $content, "{$params['itemtype']} d-flex align-items-center"); return $itemcell; } /** * Generates a toggle group name for a bulk-action checkbox based on the given grade category. * * @param grade_category $category The grade category. * @return string */ protected function get_checkbox_togglegroup(grade_category $category): string { $levels = []; $categories = explode('/', $category->path); foreach ($categories as $categoryid) { $level = 'category' . $categoryid; if (!in_array($level, $levels)) { $levels[] = 'category' . $categoryid; } } $togglegroup = implode(' ', $levels); return $togglegroup; } } /** * Class grade_edit_tree_column_weight * * @package core_grades * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class grade_edit_tree_column_weight extends grade_edit_tree_column { public function get_header_cell() { global $OUTPUT; $headercell = clone($this->headercell); $headercell->text = get_string('weights', 'grades').$OUTPUT->help_icon('aggregationcoefweight', 'grades'); return $headercell; } public function get_category_cell($category, $levelclass, $params) { $item = $category->get_grade_item(); $categorycell = parent::get_category_cell($category, $levelclass, $params); $categorycell->text = grade_edit_tree::get_weight_input($item); return $categorycell; } public function get_item_cell($item, $params) { global $CFG; if (empty($params['element'])) { throw new Exception('Array key (element) missing from 2nd param of grade_edit_tree_column_weightorextracredit::get_item_cell($item, $params)'); } $itemcell = parent::get_item_cell($item, $params); $itemcell->text = ' '; $object = $params['element']['object']; if (!in_array($object->itemtype, array('courseitem', 'categoryitem', 'category')) && !in_array($object->gradetype, array(GRADE_TYPE_NONE, GRADE_TYPE_TEXT)) && (!$object->is_outcome_item() || $object->load_parent_category()->aggregateoutcomes) && ($object->gradetype != GRADE_TYPE_SCALE || !empty($CFG->grade_includescalesinaggregation))) { $itemcell->text = grade_edit_tree::get_weight_input($item); } return $itemcell; } } /** * Class grade_edit_tree_column_range * * @package core_grades * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class grade_edit_tree_column_range extends grade_edit_tree_column { public function get_header_cell() { $headercell = clone($this->headercell); $headercell->text = get_string('maxgrade', 'grades'); return $headercell; } public function get_category_cell($category, $levelclass, $params) { $categorycell = parent::get_category_cell($category, $levelclass, $params); $categorycell->text = ''; return $categorycell; } public function get_item_cell($item, $params) { global $DB, $OUTPUT; // If the parent aggregation is Natural, we should show the number, even for scales, as that value is used... // ...in the computation. For text grades, the grademax is not used, so we can still show the no value string. $parentcat = $item->get_parent_category(); if ($item->gradetype == GRADE_TYPE_TEXT) { $grademax = ' - '; } else if ($item->gradetype == GRADE_TYPE_SCALE) { $scale = $DB->get_record('scale', array('id' => $item->scaleid)); $scale_items = null; if (empty($scale)) { //if the item is using a scale that's been removed $scale_items = array(); } else { $scale_items = explode(',', $scale->scale); } if ($parentcat->aggregation == GRADE_AGGREGATE_SUM) { $grademax = end($scale_items) . ' (' . format_float($item->grademax, $item->get_decimals()) . ')'; } else { $grademax = end($scale_items) . ' (' . count($scale_items) . ')'; } } else { $grademax = format_float($item->grademax, $item->get_decimals()); } $isextracredit = false; if ($item->aggregationcoef > 0) { // For category grade items, we need the grandparent category. // The parent is just category the grade item represents. if ($item->is_category_item()) { $grandparentcat = $parentcat->get_parent_category(); if ($grandparentcat->is_extracredit_used()) { $isextracredit = true; } } else if ($parentcat->is_extracredit_used()) { $isextracredit = true; } } if ($isextracredit) { $grademax .= ' ' . html_writer::tag('abbr', get_string('aggregationcoefextrasumabbr', 'grades'), array('title' => get_string('aggregationcoefextrasum', 'grades'))); } $itemcell = parent::get_item_cell($item, $params); $itemcell->text = $grademax; return $itemcell; } } /** * Class grade_edit_tree_column_status * * @package core_grades * @copyright 2023 Ilya Tregubov * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class grade_edit_tree_column_status extends grade_edit_tree_column { /** * Get status column header cell * @return html_table_cell status column header cell */ public function get_header_cell() { $headercell = clone($this->headercell); $headercell->text = get_string('status'); return $headercell; } /** * Get category cell in status column * * @param grade_category $category grade category * @param string $levelclass Category level info * @param array $params Params (category id, action performed etc) * @return html_table_cell category cell in status columns */ public function get_category_cell($category, $levelclass, $params) { global $OUTPUT, $gtree; $category->load_grade_item(); $categorycell = parent::get_category_cell($category, $levelclass, $params); $element = []; $element['object'] = $category; $categorycell->text = $gtree->set_grade_status_icons($element); $context = new stdClass(); if ($category->grade_item->is_calculated()) { $context->calculatedgrade = get_string('calculatedgrade', 'grades'); } else { // Aggregation type. $aggrstrings = grade_helper::get_aggregation_strings(); $context->aggregation = $aggrstrings[$category->aggregation]; // Include/exclude empty grades. if ($category->aggregateonlygraded) { $context->aggregateonlygraded = $category->aggregateonlygraded; } // Aggregate outcomes. if ($category->aggregateoutcomes) { $context->aggregateoutcomes = $category->aggregateoutcomes; } // Drop the lowest. if ($category->droplow) { $context->droplow = $category->droplow; } // Keep the highest. if ($category->keephigh) { $context->keephigh = $category->keephigh; } } $categorycell->text .= $OUTPUT->render_from_template('core_grades/category_settings', $context); return $categorycell; } /** * Get category cell in status column * * @param grade_item $item grade item * @param array $params Params * @return html_table_cell item cell in status columns */ public function get_item_cell($item, $params) { global $gtree; $element = []; $element['object'] = $item; $itemcell = parent::get_item_cell($item, $params); $itemcell->text = $gtree->set_grade_status_icons($element); return $itemcell; } } /** * Class grade_edit_tree_column_actions * * @package core_grades * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class grade_edit_tree_column_actions extends grade_edit_tree_column { public function __construct($params) { parent::__construct(); } public function get_header_cell() { $headercell = clone($this->headercell); $headercell->text = get_string('actions'); return $headercell; } public function get_category_cell($category, $levelclass, $params) { if (empty($params['actions'])) { throw new Exception('Array key (actions) missing from 3rd param of grade_edit_tree_column_actions::get_category_actions($category, $levelclass, $params)'); } $categorycell = parent::get_category_cell($category, $levelclass, $params); $categorycell->text = $params['actions']; return $categorycell; } public function get_item_cell($item, $params) { if (empty($params['actions'])) { throw new Exception('Array key (actions) missing from 2nd param of grade_edit_tree_column_actions::get_item_cell($item, $params)'); } $itemcell = parent::get_item_cell($item, $params); $itemcell->text = $params['actions']; return $itemcell; } } PK\Wn--tree/outcomeitem_form.phpnu[. /** * A moodleform to allow the creation and editing of outcome grade items * * @package core_grades * @copyright 2007 Petr Skoda * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ if (!defined('MOODLE_INTERNAL')) { die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page } require_once $CFG->libdir.'/formslib.php'; class edit_outcomeitem_form extends moodleform { function definition() { global $COURSE, $CFG; $mform =& $this->_form; /// visible elements $mform->addElement('header', 'general', get_string('gradeoutcomeitem', 'grades')); $mform->addElement('text', 'itemname', get_string('itemname', 'grades')); $mform->addRule('itemname', get_string('required'), 'required', null, 'client'); $mform->setType('itemname', PARAM_TEXT); $mform->addElement('text', 'iteminfo', get_string('iteminfo', 'grades')); $mform->addHelpButton('iteminfo', 'iteminfo', 'grades'); $mform->setType('iteminfo', PARAM_TEXT); $mform->addElement('text', 'idnumber', get_string('idnumbermod')); $mform->addHelpButton('idnumber', 'idnumbermod'); $mform->setType('idnumber', PARAM_RAW); // allow setting of outcomes on module items too $options = array(); if ($outcomes = grade_outcome::fetch_all_available($COURSE->id)) { foreach ($outcomes as $outcome) { $options[$outcome->id] = $outcome->get_name(); } } $mform->addElement('selectwithlink', 'outcomeid', get_string('outcome', 'grades'), $options, null, array('link' => $CFG->wwwroot.'/grade/edit/outcome/course.php?id='.$COURSE->id, 'label' => get_string('outcomeassigntocourse', 'grades'))); $mform->addHelpButton('outcomeid', 'outcome', 'grades'); $mform->addRule('outcomeid', get_string('required'), 'required'); $options = array(0=>get_string('none')); if ($coursemods = get_course_mods($COURSE->id)) { foreach ($coursemods as $coursemod) { if ($mod = get_coursemodule_from_id($coursemod->modname, $coursemod->id)) { $options[$coursemod->id] = format_string($mod->name); } } } $mform->addElement('select', 'cmid', get_string('linkedactivity', 'grades'), $options); $mform->addHelpButton('cmid', 'linkedactivity', 'grades'); $mform->setDefault('cmid', 0); /// hiding /// advcheckbox is not compatible with disabledIf !! $mform->addElement('checkbox', 'hidden', get_string('hidden', 'grades')); $mform->addHelpButton('hidden', 'hidden', 'grades'); $mform->addElement('date_time_selector', 'hiddenuntil', get_string('hiddenuntil', 'grades'), array('optional'=>true)); $mform->disabledIf('hidden', 'hiddenuntil[enabled]', 'checked'); //locking $mform->addElement('advcheckbox', 'locked', get_string('locked', 'grades')); $mform->addHelpButton('locked', 'locked', 'grades'); $mform->addElement('date_time_selector', 'locktime', get_string('locktime', 'grades'), array('optional'=>true)); /// parent category related settings $mform->addElement('header', 'headerparent', get_string('parentcategory', 'grades')); $mform->addElement('advcheckbox', 'weightoverride', get_string('adjustedweight', 'grades')); $mform->addHelpButton('weightoverride', 'weightoverride', 'grades'); $mform->addElement('text', 'aggregationcoef2', get_string('weight', 'grades')); $mform->addHelpButton('aggregationcoef2', 'weight', 'grades'); $mform->setType('aggregationcoef2', PARAM_RAW); $mform->disabledIf('aggregationcoef2', 'weightoverride'); $options = array(); $default = ''; $coefstring = ''; $categories = grade_category::fetch_all(array('courseid'=>$COURSE->id)); foreach ($categories as $cat) { $cat->apply_forced_settings(); $options[$cat->id] = $cat->get_name(); if ($cat->is_course_category()) { $default = $cat->id; } if ($cat->is_aggregationcoef_used()) { if ($cat->aggregation == GRADE_AGGREGATE_WEIGHTED_MEAN) { $coefstring = ($coefstring=='' or $coefstring=='aggregationcoefweight') ? 'aggregationcoefweight' : 'aggregationcoef'; } else if ($cat->aggregation == GRADE_AGGREGATE_WEIGHTED_MEAN2) { $coefstring = ($coefstring=='' or $coefstring=='aggregationcoefextrasum') ? 'aggregationcoefextrasum' : 'aggregationcoef'; } else if ($cat->aggregation == GRADE_AGGREGATE_EXTRACREDIT_MEAN) { $coefstring = ($coefstring=='' or $coefstring=='aggregationcoefextraweight') ? 'aggregationcoefextraweight' : 'aggregationcoef'; } else if ($cat->aggregation == GRADE_AGGREGATE_SUM) { $coefstring = ($coefstring=='' or $coefstring=='aggregationcoefextrasum') ? 'aggregationcoefextrasum' : 'aggregationcoef'; } else { $coefstring = 'aggregationcoef'; } } else { $mform->disabledIf('aggregationcoef', 'parentcategory', 'eq', $cat->id); } } if (count($categories) > 1) { $mform->addElement('select', 'parentcategory', get_string('gradecategory', 'grades'), $options); $mform->disabledIf('parentcategory', 'cmid', 'noteq', 0); } if ($coefstring !== '') { if ($coefstring == 'aggregationcoefextrasum' || $coefstring == 'aggregationcoefextraweightsum') { // advcheckbox is not compatible with disabledIf! $coefstring = 'aggregationcoefextrasum'; $mform->addElement('checkbox', 'aggregationcoef', get_string($coefstring, 'grades')); } else { $mform->addElement('text', 'aggregationcoef', get_string($coefstring, 'grades')); } $mform->addHelpButton('aggregationcoef', $coefstring, 'grades'); } /// hidden params $mform->addElement('hidden', 'id', 0); $mform->setType('id', PARAM_INT); $mform->addElement('hidden', 'courseid', $COURSE->id); $mform->setType('courseid', PARAM_INT); /// add return tracking info $gpr = $this->_customdata['gpr']; $gpr->add_mform_elements($mform); //------------------------------------------------------------------------------- // buttons $this->add_action_buttons(); } /// tweak the form - depending on existing data function definition_after_data() { global $CFG, $COURSE; $mform =& $this->_form; if ($id = $mform->getElementValue('id')) { $grade_item = grade_item::fetch(array('id'=>$id)); //remove the aggregation coef element if not needed if ($grade_item->is_course_item()) { if ($mform->elementExists('parentcategory')) { $mform->removeElement('parentcategory'); } if ($mform->elementExists('aggregationcoef')) { $mform->removeElement('aggregationcoef'); } } else { // if we wanted to change parent of existing item - we would have to verify there are no circular references in parents!!! if ($mform->elementExists('parentcategory')) { $mform->hardFreeze('parentcategory'); } if ($grade_item->is_category_item()) { $category = $grade_item->get_item_category(); $parent_category = $category->get_parent_category(); } else { $parent_category = $grade_item->get_parent_category(); } $parent_category->apply_forced_settings(); if (!$parent_category->is_aggregationcoef_used() || !$parent_category->aggregateoutcomes) { if ($mform->elementExists('aggregationcoef')) { $mform->removeElement('aggregationcoef'); } } else { //fix label if needed $agg_el =& $mform->getElement('aggregationcoef'); $aggcoef = ''; if ($parent_category->aggregation == GRADE_AGGREGATE_WEIGHTED_MEAN) { $aggcoef = 'aggregationcoefweight'; } else if ($parent_category->aggregation == GRADE_AGGREGATE_WEIGHTED_MEAN2) { $aggcoef = 'aggregationcoefextrasum'; } else if ($parent_category->aggregation == GRADE_AGGREGATE_EXTRACREDIT_MEAN) { $aggcoef = 'aggregationcoefextraweight'; } else if ($parent_category->aggregation == GRADE_AGGREGATE_SUM) { $aggcoef = 'aggregationcoefextrasum'; } if ($aggcoef !== '') { $agg_el->setLabel(get_string($aggcoef, 'grades')); $mform->addHelpButton('aggregationcoef', $aggcoef, 'grades'); } } // Remove the natural weighting fields for other aggregations, // or when the category does not aggregate outcomes. if ($parent_category->aggregation != GRADE_AGGREGATE_SUM || !$parent_category->aggregateoutcomes) { if ($mform->elementExists('weightoverride')) { $mform->removeElement('weightoverride'); } if ($mform->elementExists('aggregationcoef2')) { $mform->removeElement('aggregationcoef2'); } } } } // no parent header for course category if (!$mform->elementExists('aggregationcoef') and !$mform->elementExists('parentcategory')) { $mform->removeElement('headerparent'); } } /// perform extra validation before submission function validation($data, $files) { global $COURSE; $errors = parent::validation($data, $files); if (array_key_exists('idnumber', $data)) { if ($data['id']) { $grade_item = new grade_item(array('id'=>$data['id'], 'courseid'=>$data['courseid'])); } else { $grade_item = null; } if (!grade_verify_idnumber($data['idnumber'], $COURSE->id, $grade_item, null)) { $errors['idnumber'] = get_string('idnumbertaken'); } } return $errors; } } PK\es"s"tree/grade_form.phpnu[. /** * A moodleform to allow the editing of a user's grade for a particular activity * * @package core_grades * @copyright 2007 Petr Skoda * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ if (!defined('MOODLE_INTERNAL')) { die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page } require_once $CFG->libdir.'/formslib.php'; class edit_grade_form extends moodleform { function definition() { global $CFG, $COURSE, $DB; $mform =& $this->_form; $grade_item = $this->_customdata['grade_item']; $gpr = $this->_customdata['gpr']; if ($grade_item->is_course_item()) { $grade_category = null; } else if ($grade_item->is_category_item()) { $grade_category = $grade_item->get_item_category(); $grade_category = $grade_category->get_parent_category(); } else { $grade_category = $grade_item->get_parent_category(); } /// information fields $mform->addElement('static', 'user', get_string('user')); $mform->addElement('static', 'itemname', get_string('itemname', 'grades')); $mform->addElement('checkbox', 'overridden', get_string('overridden', 'grades')); $mform->addHelpButton('overridden', 'overridden', 'grades'); /// actual grade - numeric or scale if ($grade_item->gradetype == GRADE_TYPE_VALUE) { // numeric grade $mform->addElement('text', 'finalgrade', get_string('finalgrade', 'grades')); $mform->setType('finalgrade', PARAM_RAW); $mform->addHelpButton('finalgrade', 'finalgrade', 'grades'); $mform->disabledIf('finalgrade', 'overridden', 'notchecked'); } else if ($grade_item->gradetype == GRADE_TYPE_SCALE) { // scale grade $scaleopt = array(); if (empty($grade_item->outcomeid)) { $scaleopt[-1] = get_string('nograde'); } else { $scaleopt[-1] = get_string('nooutcome', 'grades'); } $i = 1; if ($scale = $DB->get_record('scale', array('id' => $grade_item->scaleid))) { foreach (explode(",", $scale->scale) as $option) { $scaleopt[$i] = $option; $i++; } } $mform->addElement('select', 'finalgrade', get_string('finalgrade', 'grades'), $scaleopt); $mform->addHelpButton('finalgrade', 'finalgrade', 'grades'); $mform->disabledIf('finalgrade', 'overridden', 'notchecked'); } $mform->addElement('advcheckbox', 'excluded', get_string('excluded', 'grades')); $mform->addHelpButton('excluded', 'excluded', 'grades'); /// hiding /// advcheckbox is not compatible with disabledIf !! $mform->addElement('checkbox', 'hidden', get_string('hidden', 'grades')); $mform->addHelpButton('hidden', 'hidden', 'grades'); $mform->addElement('date_time_selector', 'hiddenuntil', get_string('hiddenuntil', 'grades'), array('optional'=>true)); $mform->disabledIf('hidden', 'hiddenuntil[enabled]', 'checked'); /// locking $mform->addElement('advcheckbox', 'locked', get_string('locked', 'grades')); $mform->addHelpButton('locked', 'locked', 'grades'); $mform->addElement('date_time_selector', 'locktime', get_string('locktime', 'grades'), array('optional'=>true)); $mform->disabledIf('locktime', 'gradetype', 'eq', GRADE_TYPE_NONE); // Feedback format is automatically converted to html if user has enabled editor $feedbackoptions = array('maxfiles'=>0, 'maxbytes'=>0); //TODO: no files here for now, if ever gets implemented use component 'grade' and filearea 'feedback' $mform->addElement('editor', 'feedback', get_string('feedback', 'grades'), null, $feedbackoptions); $mform->addHelpButton('feedback', 'feedback', 'grades'); $mform->setType('text', PARAM_RAW); // to be cleaned before display, no XSS risk $mform->disabledIf('feedback', 'overridden'); // hidden params $mform->addElement('hidden', 'oldgrade'); $mform->setType('oldgrade', PARAM_RAW); $mform->addElement('hidden', 'oldfeedback'); $mform->setType('oldfeedback', PARAM_RAW); $mform->addElement('hidden', 'id', 0); $mform->setType('id', PARAM_INT); $mform->addElement('hidden', 'itemid', 0); $mform->setType('itemid', PARAM_INT); $mform->addElement('hidden', 'userid', 0); $mform->setType('userid', PARAM_INT); $mform->addElement('hidden', 'courseid', $COURSE->id); $mform->setType('courseid', PARAM_INT); /// add return tracking info $gpr->add_mform_elements($mform); //------------------------------------------------------------------------------- // buttons $this->add_action_buttons(); } function definition_after_data() { global $CFG, $COURSE, $DB; $context = context_course::instance($COURSE->id); $mform =& $this->_form; $grade_item = $this->_customdata['grade_item']; // fill in user name if user still exists $userid = $mform->getElementValue('userid'); if ($user = $DB->get_record('user', array('id' => $userid))) { $username = ''.fullname($user).''; $user_el =& $mform->getElement('user'); $user_el->setValue($username); } // add activity name + link if ($grade_item->itemtype == 'mod') { $cm = get_coursemodule_from_instance($grade_item->itemmodule, $grade_item->iteminstance, $grade_item->courseid); $itemname = ''.$grade_item->get_name().''; } else { $itemname = $grade_item->get_name(); } $itemname_el =& $mform->getElement('itemname'); $itemname_el->setValue($itemname); // access control - disable not allowed elements if (!has_capability('moodle/grade:manage', $context)) { $mform->hardFreeze('excluded'); } if (!has_capability('moodle/grade:manage', $context) and !has_capability('moodle/grade:hide', $context)) { $mform->hardFreeze('hidden'); $mform->hardFreeze('hiddenuntil'); } $old_grade_grade = new grade_grade(array('itemid'=>$grade_item->id, 'userid'=>$userid)); $gradeitemoverridable = $grade_item->is_overridable_item(); if (!$gradeitemoverridable) { $mform->removeElement('overridden'); } if ($grade_item->is_hidden()) { $mform->hardFreeze('hidden'); } if ($old_grade_grade->is_locked()) { if ($grade_item->is_locked()) { $mform->hardFreeze('locked'); $mform->hardFreeze('locktime'); } if ($gradeitemoverridable) { $mform->hardFreeze('overridden'); } $mform->hardFreeze('finalgrade'); $mform->hardFreeze('feedback'); } else { if (empty($old_grade_grade->id)) { $old_grade_grade->locked = $grade_item->locked; $old_grade_grade->locktime = $grade_item->locktime; } if (($old_grade_grade->locked or $old_grade_grade->locktime) and (!has_capability('moodle/grade:manage', $context) and !has_capability('moodle/grade:unlock', $context))) { $mform->hardFreeze('locked'); $mform->hardFreeze('locktime'); } else if ((!$old_grade_grade->locked and !$old_grade_grade->locktime) and (!has_capability('moodle/grade:manage', $context) and !has_capability('moodle/grade:lock', $context))) { $mform->hardFreeze('locked'); $mform->hardFreeze('locktime'); } } } } PK\݆ tree/item.phpnu[. /** * Edit the grade options for an individual grade item * * @package core_grades * @copyright 2007 Petr Skoda * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ use core_grades\form\add_item; require_once '../../../config.php'; require_once $CFG->dirroot.'/grade/lib.php'; require_once $CFG->dirroot.'/grade/report/lib.php'; require_once 'item_form.php'; $courseid = required_param('courseid', PARAM_INT); $id = optional_param('id', 0, PARAM_INT); $url = new moodle_url('/grade/edit/tree/item.php', array('courseid'=>$courseid)); if ($id !== 0) { $url->param('id', $id); } $PAGE->set_url($url); $PAGE->set_pagelayout('admin'); navigation_node::override_active_url(new moodle_url('/grade/edit/tree/index.php', array('id'=>$courseid))); if (!$course = $DB->get_record('course', array('id' => $courseid))) { throw new \moodle_exception('invalidcourseid'); } require_login($course); $context = context_course::instance($course->id); require_capability('moodle/grade:manage', $context); // default return url $gpr = new grade_plugin_return(); $returnurl = $gpr->get_return_url('index.php?id='.$course->id); $heading = get_string('itemsedit', 'grades'); if ($grade_item = grade_item::fetch(array('id'=>$id, 'courseid'=>$courseid))) { // redirect if outcomeid present if (!empty($grade_item->outcomeid) && !empty($CFG->enableoutcomes)) { $url = new moodle_url('/grade/edit/tree/outcomeitem.php', ['id' => $id, 'courseid' => $courseid]); redirect($gpr->add_url_params($url)); } if ($grade_item->is_course_item() or $grade_item->is_category_item()) { $grade_category = $grade_item->get_item_category(); $url = new moodle_url('/grade/edit/tree/category.php', ['id' => $grade_category->id, 'courseid' => $courseid]); redirect($gpr->add_url_params($url)); } $item = $grade_item->get_record_data(); $parent_category = $grade_item->get_parent_category(); $item->parentcategory = $parent_category->id; } else { $heading = get_string('newitem', 'grades'); $grade_item = new grade_item(array('courseid'=>$courseid, 'itemtype'=>'manual'), false); $item = $grade_item->get_record_data(); $parent_category = grade_category::fetch_course_category($courseid); $item->parentcategory = $parent_category->id; } $decimalpoints = $grade_item->get_decimals(); if ($item->hidden > 1) { $item->hiddenuntil = $item->hidden; $item->hidden = 0; } else { $item->hiddenuntil = 0; } $item->locked = !empty($item->locked); $item->grademax = format_float($item->grademax, $decimalpoints); $item->grademin = format_float($item->grademin, $decimalpoints); $item->gradepass = format_float($item->gradepass, $decimalpoints); $item->multfactor = format_float($item->multfactor, 4); $item->plusfactor = format_float($item->plusfactor, 4); if ($parent_category->aggregation == GRADE_AGGREGATE_SUM or $parent_category->aggregation == GRADE_AGGREGATE_WEIGHTED_MEAN2) { $item->aggregationcoef = $item->aggregationcoef == 0 ? 0 : 1; } else { $item->aggregationcoef = format_float($item->aggregationcoef, 4); } if ($parent_category->aggregation == GRADE_AGGREGATE_SUM) { $item->aggregationcoef2 = format_float($item->aggregationcoef2 * 100.0); } $item->cancontrolvisibility = $grade_item->can_control_visibility(); $mform = new edit_item_form(null, array('current'=>$item, 'gpr'=>$gpr)); $simpleform = new add_item(null, ['itemid' => $grade_item->id, 'courseid' => $courseid, 'gpr' => $gpr]); // Data has been carried over from the dynamic form. if ($simpledata = $simpleform->get_submitted_data()) { $mform->set_data($simpledata); } if ($mform->is_cancelled()) { redirect($returnurl); } else if ($data = $mform->get_data()) { // This is a new item, and the category chosen is different than the default category. if (empty($grade_item->id) && isset($data->parentcategory) && $parent_category->id != $data->parentcategory) { $parent_category = grade_category::fetch(array('id' => $data->parentcategory)); } // If unset, give the aggregation values a default based on parent aggregation method. $defaults = grade_category::get_default_aggregation_coefficient_values($parent_category->aggregation); if (!isset($data->aggregationcoef) || $data->aggregationcoef == '') { $data->aggregationcoef = $defaults['aggregationcoef']; } if (!isset($data->weightoverride)) { $data->weightoverride = $defaults['weightoverride']; } if (!isset($data->gradepass) || $data->gradepass == '') { $data->gradepass = 0; } if (!isset($data->grademin) || $data->grademin == '') { $data->grademin = 0; } $hide = empty($data->hiddenuntil) ? 0 : $data->hiddenuntil; if (!$hide) { $hide = empty($data->hidden) ? 0 : $data->hidden; } unset($data->hidden); unset($data->hiddenuntil); $locked = empty($data->locked) ? 0: $data->locked; $locktime = empty($data->locktime) ? 0: $data->locktime; unset($data->locked); unset($data->locktime); $convert = array('grademax', 'grademin', 'gradepass', 'multfactor', 'plusfactor', 'aggregationcoef', 'aggregationcoef2'); foreach ($convert as $param) { if (property_exists($data, $param)) { $data->$param = unformat_float($data->$param); } } if (isset($data->aggregationcoef2) && $parent_category->aggregation == GRADE_AGGREGATE_SUM) { $data->aggregationcoef2 = $data->aggregationcoef2 / 100.0; } else { $data->aggregationcoef2 = $defaults['aggregationcoef2']; } $gradeitem = new grade_item(array('id' => $id, 'courseid' => $courseid)); $oldmin = $gradeitem->grademin; $oldmax = $gradeitem->grademax; grade_item::set_properties($gradeitem, $data); $gradeitem->outcomeid = null; // Handle null decimals value if (!property_exists($data, 'decimals') or $data->decimals < 0) { $gradeitem->decimals = null; } if (empty($gradeitem->id)) { $gradeitem->itemtype = 'manual'; // All new items to be manual only. $gradeitem->insert(); // set parent if needed if (isset($data->parentcategory)) { $gradeitem->set_parent($data->parentcategory, false); } } else { $gradeitem->update(); if (!empty($data->rescalegrades) && $data->rescalegrades == 'yes') { $newmin = $gradeitem->grademin; $newmax = $gradeitem->grademax; $gradeitem->rescale_grades_keep_percentage($oldmin, $oldmax, $newmin, $newmax, 'gradebook'); } } if ($item->cancontrolvisibility) { // Update hiding flag. $gradeitem->set_hidden($hide, true); } $gradeitem->set_locktime($locktime); // Locktime first - it might be removed when unlocking. $gradeitem->set_locked($locked, false, true); redirect($returnurl); } $PAGE->navbar->add($heading); print_grade_page_head($courseid, 'settings', null, $heading, false, false, false); $mform->display(); echo $OUTPUT->footer(); PK\M=3!3!tree/calculation.phpnu[. /** * Edit a calculated grade item * * @package core_grades * @copyright 2007 Petr Skoda * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ require_once '../../../config.php'; require_once $CFG->dirroot.'/grade/lib.php'; require_once $CFG->libdir.'/mathslib.php'; require_once 'calculation_form.php'; $courseid = required_param('courseid', PARAM_INT); $id = required_param('id', PARAM_INT); $section = optional_param('section', 'calculation', PARAM_ALPHA); $idnumbers = optional_param_array('idnumbers', null, PARAM_RAW); $url = new moodle_url('/grade/edit/tree/calculation.php', array('id'=>$id, 'courseid'=>$courseid)); if ($section !== 'calculation') { $url->param('section', $section); } $PAGE->set_url($url); if (!$course = $DB->get_record('course', array('id' => $courseid))) { throw new \moodle_exception('invalidcourseid'); } require_login($course); $context = context_course::instance($course->id); require_capability('moodle/grade:manage', $context); $PAGE->set_pagelayout('admin'); navigation_node::override_active_url(new moodle_url('/grade/edit/tree/index.php', array('id'=>$course->id))); // default return url $gpr = new grade_plugin_return(); $returnurl = $gpr->get_return_url($CFG->wwwroot.'/grade/report/index.php?id='.$course->id); if (!$grade_item = grade_item::fetch(array('id'=>$id, 'courseid'=>$course->id))) { throw new \moodle_exception('invaliditemid'); } // activity items and items without grade can not have calculation if ($grade_item->is_external_item() or ($grade_item->gradetype != GRADE_TYPE_VALUE and $grade_item->gradetype != GRADE_TYPE_SCALE)) { redirect($returnurl, get_string('errornocalculationallowed', 'grades')); } $mform = new edit_calculation_form(null, array('gpr'=>$gpr, 'itemid' => $grade_item->id)); if ($mform->is_cancelled()) { redirect($returnurl); } $calculation = calc_formula::localize($grade_item->calculation); $calculation = grade_item::denormalize_formula($calculation, $grade_item->courseid); $mform->set_data(array('courseid'=>$grade_item->courseid, 'calculation'=>$calculation, 'id'=>$grade_item->id, 'itemname'=>$grade_item->itemname)); $errors = array(); if ($data = $mform->get_data()) { $calculation = calc_formula::unlocalize($data->calculation); $grade_item->set_calculation($calculation); redirect($returnurl); } elseif (!empty($section) AND $section='idnumbers' AND !empty($idnumbers)) { // Handle idnumbers separately (non-mform) //first validate and store the new idnumbers foreach ($idnumbers as $giid => $value) { if ($gi = grade_item::fetch(array('id' => $giid))) { if ($gi->itemtype == 'mod') { $cm = get_coursemodule_from_instance($gi->itemmodule, $gi->iteminstance, $gi->courseid); } else { $cm = null; } if (!grade_verify_idnumber($value, $COURSE->id, $gi, $cm)) { $errors[$giid] = get_string('idnumbertaken'); continue; } if (empty($gi->idnumber) and !$gi->add_idnumber($idnumbers[$gi->id])) { $errors[$giid] = get_string('error'); continue; } } else { $errors[$giid] = 'Could not fetch the grade_item with id=' . $giid; } } } $gtree = new grade_tree($course->id, false, false); $strgrades = get_string('grades'); $strgraderreport = get_string('graderreport', 'grades'); $strcalculationedit = get_string('editcalculation', 'grades'); $PAGE->navbar->add($strcalculationedit); print_grade_page_head($courseid, 'settings', null, $strcalculationedit, false, false, false); $mform->display(); // Now show the gradetree with the idnumbers add/edit form echo '
'.get_string('idnumbers', 'grades').'
'; echo $OUTPUT->footer(); die(); /** * Simplified version of the print_grade_tree() recursive function found in grade/edit/tree/index.php * Only prints a tree with a basic icon for each element, and an edit field for * items without an idnumber. * @param object $gtree * @param object $element * @param int $current_itemid The itemid of this page: should be excluded from the tree * @param array $errors An array of idnumbers => error * @return string */ function get_grade_tree(&$gtree, $element, $current_itemid=null, $errors=null) { global $CFG; $object = $element['object']; $eid = $element['eid']; $type = $element['type']; $grade_item = $object->get_grade_item(); $name = $object->get_name(); $return_string = ''; //TODO: improve outcome visualisation if ($type == 'item' and !empty($object->outcomeid)) { $name = $name.' ('.get_string('outcome', 'grades').')'; } $idnumber = $object->get_idnumber(); // Don't show idnumber or input field for current item if given to function. Highlight the item instead. if ($type != 'category') { if (is_null($current_itemid) OR $grade_item->id != $current_itemid) { if ($idnumber) { $name .= ": [[$idnumber]]"; } else { $closingdiv = ''; if (!empty($errors[$grade_item->id])) { $name .= '
' . $errors[$grade_item->id].'
'."\n"; $closingdiv = "
\n"; } $name .= ''; $name .= '' . "\n"; $name .= $closingdiv; } } else { $name = "$name"; } } $icon = grade_helper::get_element_icon($element, true); $last = ''; $catcourseitem = ($element['type'] == 'courseitem' or $element['type'] == 'categoryitem'); if ($type != 'category') { $return_string .= '
  • '.$icon.$name.'
  • ' . "\n"; } else { $return_string .= '
  • '.$icon.$name . "\n"; $return_string .= '
      '."\n"; $last = null; foreach($element['children'] as $child_el) { $return_string .= get_grade_tree($gtree, $child_el, $current_itemid, $errors); } if ($last) { $return_string .= get_grade_tree($gtree, $last, $current_itemid, $errors); } $return_string .= '
  • '."\n"; } return $return_string; } PK\KS44m4mtree/category_form.phpnu[. /** * A moodleform to edit the grade options for an individual grade category * * @package core_grades * @copyright 2007 Petr Skoda * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ if (!defined('MOODLE_INTERNAL')) { die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page } require_once $CFG->libdir.'/formslib.php'; class edit_category_form extends moodleform { private $aggregation_options = array(); function definition() { global $CFG, $COURSE, $DB, $OUTPUT; $mform =& $this->_form; $category = $this->_customdata['current']; $this->aggregation_options = grade_helper::get_aggregation_strings(); // visible elements $mform->addElement('header', 'headercategory', get_string('gradecategory', 'grades')); $mform->addElement('text', 'fullname', get_string('categoryname', 'grades')); $mform->setType('fullname', PARAM_TEXT); $mform->addRule('fullname', null, 'required', null, 'client'); $mform->addElement('select', 'aggregation', get_string('aggregation', 'grades'), $this->aggregation_options); $mform->addHelpButton('aggregation', 'aggregation', 'grades'); $mform->addElement('checkbox', 'aggregateonlygraded', get_string('aggregateonlygraded', 'grades')); $mform->addHelpButton('aggregateonlygraded', 'aggregateonlygraded', 'grades'); if (empty($CFG->enableoutcomes)) { $mform->addElement('hidden', 'aggregateoutcomes'); $mform->setType('aggregateoutcomes', PARAM_INT); } else { $mform->addElement('checkbox', 'aggregateoutcomes', get_string('aggregateoutcomes', 'grades')); $mform->addHelpButton('aggregateoutcomes', 'aggregateoutcomes', 'grades'); } $mform->addElement('text', 'keephigh', get_string('keephigh', 'grades'), 'size="3"'); $mform->setType('keephigh', PARAM_INT); $mform->addHelpButton('keephigh', 'keephigh', 'grades'); $mform->addElement('text', 'droplow', get_string('droplow', 'grades'), 'size="3"'); $mform->setType('droplow', PARAM_INT); $mform->addHelpButton('droplow', 'droplow', 'grades'); $mform->disabledIf('droplow', 'keephigh', 'noteq', 0); $mform->disabledIf('keephigh', 'droplow', 'noteq', 0); $mform->disabledIf('droplow', 'keephigh', 'noteq', 0); // Grade item settings // Displayed as Category total to avoid confusion between grade items requiring marking and category totals $mform->addElement('header', 'general', get_string('categorytotal', 'grades')); $mform->setExpanded('general'); $mform->addElement('text', 'grade_item_itemname', get_string('categorytotalname', 'grades')); $mform->setType('grade_item_itemname', PARAM_TEXT); $mform->addElement('text', 'grade_item_iteminfo', get_string('iteminfo', 'grades')); $mform->addHelpButton('grade_item_iteminfo', 'iteminfo', 'grades'); $mform->setType('grade_item_iteminfo', PARAM_TEXT); $mform->addElement('text', 'grade_item_idnumber', get_string('idnumbermod')); $mform->addHelpButton('grade_item_idnumber', 'idnumbermod'); $mform->setType('grade_item_idnumber', PARAM_RAW); if (!empty($category->id)) { $gradecategory = grade_category::fetch(array('id' => $category->id)); $gradeitem = $gradecategory->load_grade_item(); // If grades exist set a message so the user knows why they can not alter the grade type or scale. // We could never change the grade type for external items, so only need to show this for manual grade items. if ($gradeitem->has_overridden_grades()) { // Set a message so the user knows why the can not alter the grade type or scale. if ($gradeitem->gradetype == GRADE_TYPE_SCALE) { $gradesexistmsg = get_string('modgradecategorycantchangegradetyporscalemsg', 'grades'); } else { $gradesexistmsg = get_string('modgradecategorycantchangegradetypemsg', 'grades'); } $notification = new \core\output\notification($gradesexistmsg, \core\output\notification::NOTIFY_INFO); $notification->set_show_closebutton(false); $mform->addElement('static', 'gradesexistmsg', '', $OUTPUT->render($notification)); } } $options = array(GRADE_TYPE_NONE=>get_string('typenone', 'grades'), GRADE_TYPE_VALUE=>get_string('typevalue', 'grades'), GRADE_TYPE_SCALE=>get_string('typescale', 'grades'), GRADE_TYPE_TEXT=>get_string('typetext', 'grades')); $mform->addElement('select', 'grade_item_gradetype', get_string('gradetype', 'grades'), $options); $mform->addHelpButton('grade_item_gradetype', 'gradetype', 'grades'); $mform->setDefault('grade_item_gradetype', GRADE_TYPE_VALUE); $mform->disabledIf('grade_item_gradetype', 'aggregation', 'eq', GRADE_AGGREGATE_SUM); //$mform->addElement('text', 'calculation', get_string('calculation', 'grades')); //$mform->disabledIf('calculation', 'gradetype', 'eq', GRADE_TYPE_TEXT); //$mform->disabledIf('calculation', 'gradetype', 'eq', GRADE_TYPE_NONE); $options = array(0=>get_string('usenoscale', 'grades')); if ($scales = grade_scale::fetch_all_local($COURSE->id)) { foreach ($scales as $scale) { $options[$scale->id] = $scale->get_name(); } } if ($scales = grade_scale::fetch_all_global()) { foreach ($scales as $scale) { $options[$scale->id] = $scale->get_name(); } } // ugly BC hack - it was possible to use custom scale from other courses :-( if (!empty($category->grade_item_scaleid) and !isset($options[$category->grade_item_scaleid])) { if ($scale = grade_scale::fetch(array('id'=>$category->grade_item_scaleid))) { $options[$scale->id] = $scale->get_name().' '.get_string('incorrectcustomscale', 'grades'); } } $mform->addElement('select', 'grade_item_scaleid', get_string('scale'), $options); $mform->addHelpButton('grade_item_scaleid', 'typescale', 'grades'); $mform->disabledIf('grade_item_scaleid', 'grade_item_gradetype', 'noteq', GRADE_TYPE_SCALE); $mform->disabledIf('grade_item_scaleid', 'aggregation', 'eq', GRADE_AGGREGATE_SUM); $choices = array(); $choices[''] = get_string('choose'); $choices['no'] = get_string('no'); $choices['yes'] = get_string('yes'); $mform->addElement('select', 'grade_item_rescalegrades', get_string('modgradecategoryrescalegrades', 'grades'), $choices); $mform->addHelpButton('grade_item_rescalegrades', 'modgradecategoryrescalegrades', 'grades'); $mform->disabledIf('grade_item_rescalegrades', 'grade_item_gradetype', 'noteq', GRADE_TYPE_VALUE); $mform->addElement('float', 'grade_item_grademax', get_string('grademax', 'grades')); $mform->addHelpButton('grade_item_grademax', 'grademax', 'grades'); $mform->disabledIf('grade_item_grademax', 'grade_item_gradetype', 'noteq', GRADE_TYPE_VALUE); $mform->disabledIf('grade_item_grademax', 'aggregation', 'eq', GRADE_AGGREGATE_SUM); if ((bool) get_config('moodle', 'grade_report_showmin')) { $mform->addElement('float', 'grade_item_grademin', get_string('grademin', 'grades')); $mform->addHelpButton('grade_item_grademin', 'grademin', 'grades'); $mform->disabledIf('grade_item_grademin', 'grade_item_gradetype', 'noteq', GRADE_TYPE_VALUE); $mform->disabledIf('grade_item_grademin', 'aggregation', 'eq', GRADE_AGGREGATE_SUM); } $mform->addElement('float', 'grade_item_gradepass', get_string('gradepass', 'grades')); $mform->addHelpButton('grade_item_gradepass', 'gradepass', 'grades'); $mform->disabledIf('grade_item_gradepass', 'grade_item_gradetype', 'eq', GRADE_TYPE_NONE); $mform->disabledIf('grade_item_gradepass', 'grade_item_gradetype', 'eq', GRADE_TYPE_TEXT); /// grade display prefs $default_gradedisplaytype = grade_get_setting($COURSE->id, 'displaytype', $CFG->grade_displaytype); $options = array(GRADE_DISPLAY_TYPE_DEFAULT => get_string('default', 'grades'), GRADE_DISPLAY_TYPE_REAL => get_string('real', 'grades'), GRADE_DISPLAY_TYPE_PERCENTAGE => get_string('percentage', 'grades'), GRADE_DISPLAY_TYPE_LETTER => get_string('letter', 'grades'), GRADE_DISPLAY_TYPE_REAL_PERCENTAGE => get_string('realpercentage', 'grades'), GRADE_DISPLAY_TYPE_REAL_LETTER => get_string('realletter', 'grades'), GRADE_DISPLAY_TYPE_LETTER_REAL => get_string('letterreal', 'grades'), GRADE_DISPLAY_TYPE_LETTER_PERCENTAGE => get_string('letterpercentage', 'grades'), GRADE_DISPLAY_TYPE_PERCENTAGE_LETTER => get_string('percentageletter', 'grades'), GRADE_DISPLAY_TYPE_PERCENTAGE_REAL => get_string('percentagereal', 'grades') ); asort($options); foreach ($options as $key=>$option) { if ($key == $default_gradedisplaytype) { $options[GRADE_DISPLAY_TYPE_DEFAULT] = get_string('defaultprev', 'grades', $option); break; } } $mform->addElement('select', 'grade_item_display', get_string('gradedisplaytype', 'grades'), $options); $mform->addHelpButton('grade_item_display', 'gradedisplaytype', 'grades'); $mform->disabledIf('grade_item_display', 'grade_item_gradetype', 'in', array(GRADE_TYPE_TEXT, GRADE_TYPE_NONE)); $default_gradedecimals = grade_get_setting($COURSE->id, 'decimalpoints', $CFG->grade_decimalpoints); $options = array(-1=>get_string('defaultprev', 'grades', $default_gradedecimals), 0=>0, 1=>1, 2=>2, 3=>3, 4=>4, 5=>5); $mform->addElement('select', 'grade_item_decimals', get_string('decimalpoints', 'grades'), $options); $mform->addHelpButton('grade_item_decimals', 'decimalpoints', 'grades'); $mform->setDefault('grade_item_decimals', -1); $mform->disabledIf('grade_item_decimals', 'grade_item_display', 'eq', GRADE_DISPLAY_TYPE_LETTER); $mform->disabledIf('grade_item_decimals', 'grade_item_gradetype', 'in', array(GRADE_TYPE_TEXT, GRADE_TYPE_NONE)); if ($default_gradedisplaytype == GRADE_DISPLAY_TYPE_LETTER) { $mform->disabledIf('grade_item_decimals', 'grade_item_display', "eq", GRADE_DISPLAY_TYPE_DEFAULT); } /// hiding // advcheckbox is not compatible with disabledIf! $mform->addElement('checkbox', 'grade_item_hidden', get_string('hidden', 'grades')); $mform->addHelpButton('grade_item_hidden', 'hidden', 'grades'); $mform->addElement('date_time_selector', 'grade_item_hiddenuntil', get_string('hiddenuntil', 'grades'), array('optional'=>true)); $mform->disabledIf('grade_item_hidden', 'grade_item_hiddenuntil[enabled]', 'checked'); /// locking $mform->addElement('checkbox', 'grade_item_locked', get_string('locked', 'grades')); $mform->addHelpButton('grade_item_locked', 'locked', 'grades'); $mform->addElement('date_time_selector', 'grade_item_locktime', get_string('locktime', 'grades'), array('optional'=>true)); $mform->disabledIf('grade_item_locktime', 'grade_item_gradetype', 'eq', GRADE_TYPE_NONE); /// parent category related settings $mform->addElement('header', 'headerparent', get_string('parentcategory', 'grades')); $mform->setExpanded('headerparent'); $mform->addElement('advcheckbox', 'grade_item_weightoverride', get_string('adjustedweight', 'grades')); $mform->addHelpButton('grade_item_weightoverride', 'weightoverride', 'grades'); $mform->addElement('float', 'grade_item_aggregationcoef2', get_string('weight', 'grades')); $mform->addHelpButton('grade_item_aggregationcoef2', 'weight', 'grades'); $mform->disabledIf('grade_item_aggregationcoef2', 'grade_item_weightoverride'); $options = array(); $default = -1; $categories = grade_category::fetch_all(array('courseid'=>$COURSE->id)); foreach ($categories as $cat) { $cat->apply_forced_settings(); $options[$cat->id] = $cat->get_name(); if ($cat->is_course_category()) { $default = $cat->id; } } if (count($categories) > 1) { $mform->addElement('select', 'parentcategory', get_string('parentcategory', 'grades'), $options); $mform->setDefault('parentcategory', $default); $mform->addElement('static', 'currentparentaggregation', get_string('currentparentaggregation', 'grades')); } // hidden params $mform->addElement('hidden', 'id', 0); $mform->setType('id', PARAM_INT); $mform->addElement('hidden', 'courseid', 0); $mform->setType('courseid', PARAM_INT); /// add return tracking info $gpr = $this->_customdata['gpr']; $gpr->add_mform_elements($mform); //------------------------------------------------------------------------------- // buttons $this->add_action_buttons(); //------------------------------------------------------------------------------- $this->set_data($category); } function definition_after_data() { global $CFG, $COURSE; $mform =& $this->_form; $somecat = new grade_category(); foreach ($somecat->forceable as $property) { if ((int)$CFG->{"grade_{$property}_flag"} & 1) { if ($mform->elementExists($property)) { if (empty($CFG->grade_hideforcedsettings)) { $mform->hardFreeze($property); } else { if ($mform->elementExists($property)) { $mform->removeElement($property); } } } } } if ($CFG->grade_droplow > 0) { if ($mform->elementExists('keephigh')) { $mform->removeElement('keephigh'); } } else if ($CFG->grade_keephigh > 0) { if ($mform->elementExists('droplow')) { $mform->removeElement('droplow'); } } if ($id = $mform->getElementValue('id')) { $grade_category = grade_category::fetch(array('id'=>$id)); $grade_item = $grade_category->load_grade_item(); // remove agg coef if not used if ($grade_category->is_course_category()) { if ($mform->elementExists('parentcategory')) { $mform->removeElement('parentcategory'); } if ($mform->elementExists('currentparentaggregation')) { $mform->removeElement('currentparentaggregation'); } } else { // if we wanted to change parent of existing category - we would have to verify there are no circular references in parents!!! if ($mform->elementExists('parentcategory')) { $mform->hardFreeze('parentcategory'); } $parent_cat = $grade_category->get_parent_category(); $mform->setDefault('currentparentaggregation', $this->aggregation_options[$parent_cat->aggregation]); } // Prevent the user from using drop lowest/keep highest when the aggregation method cannot handle it. if (!$grade_category->can_apply_limit_rules()) { if ($mform->elementExists('keephigh')) { $mform->setConstant('keephigh', 0); $mform->hardFreeze('keephigh'); } if ($mform->elementExists('droplow')) { $mform->setConstant('droplow', 0); $mform->hardFreeze('droplow'); } } if ($grade_item->is_calculated()) { // following elements are ignored when calculation formula used if ($mform->elementExists('aggregation')) { $mform->removeElement('aggregation'); } if ($mform->elementExists('keephigh')) { $mform->removeElement('keephigh'); } if ($mform->elementExists('droplow')) { $mform->removeElement('droplow'); } if ($mform->elementExists('aggregateonlygraded')) { $mform->removeElement('aggregateonlygraded'); } if ($mform->elementExists('aggregateoutcomes')) { $mform->removeElement('aggregateoutcomes'); } } // If it is a course category, remove the "required" rule from the "fullname" element if ($grade_category->is_course_category()) { unset($mform->_rules['fullname']); $key = array_search('fullname', $mform->_required); unset($mform->_required[$key]); } // If it is a course category and its fullname is ?, show an empty field if ($grade_category->is_course_category() && $mform->getElementValue('fullname') == '?') { $mform->setDefault('fullname', ''); } // remove unwanted aggregation options if ($mform->elementExists('aggregation')) { $allaggoptions = array_keys($this->aggregation_options); $agg_el =& $mform->getElement('aggregation'); $visible = explode(',', $CFG->grade_aggregations_visible); if (!is_null($grade_category->aggregation)) { // current type is always visible $visible[] = $grade_category->aggregation; } foreach ($allaggoptions as $type) { if (!in_array($type, $visible)) { $agg_el->removeOption($type); } } } } else { // adding new category if ($mform->elementExists('currentparentaggregation')) { $mform->removeElement('currentparentaggregation'); } // remove unwanted aggregation options if ($mform->elementExists('aggregation')) { $allaggoptions = array_keys($this->aggregation_options); $agg_el =& $mform->getElement('aggregation'); $visible = explode(',', $CFG->grade_aggregations_visible); foreach ($allaggoptions as $type) { if (!in_array($type, $visible)) { $agg_el->removeOption($type); } } } $mform->removeElement('grade_item_rescalegrades'); } // no parent header for course category if (!$mform->elementExists('parentcategory')) { $mform->removeElement('headerparent'); } /// GRADE ITEM if ($id = $mform->getElementValue('id')) { $grade_category = grade_category::fetch(array('id'=>$id)); $grade_item = $grade_category->load_grade_item(); // Load appropriate "hidden"/"hidden until" defaults. if ($grade_item->is_hiddenuntil()) { $mform->setDefault('grade_item_hiddenuntil', $grade_item->get_hidden()); } else { $mform->setDefault('grade_item_hidden', $grade_item->get_hidden()); } if ($grade_item->is_outcome_item()) { // we have to prevent incompatible modifications of outcomes if outcomes disabled $mform->removeElement('grade_item_grademax'); if ($mform->elementExists('grade_item_grademin')) { $mform->removeElement('grade_item_grademin'); } $mform->removeElement('grade_item_gradetype'); $mform->removeElement('grade_item_display'); $mform->removeElement('grade_item_decimals'); $mform->hardFreeze('grade_item_scaleid'); // Only show the option to rescale grades on a category if its corresponding grade_item has overridden grade_grades. } else if ($grade_item->has_overridden_grades()) { // Can't change the grade type or the scale if there are grades. $mform->hardFreeze('grade_item_gradetype, grade_item_scaleid'); // If we are using scles then remove the unnecessary rescale and grade fields. if ($grade_item->gradetype == GRADE_TYPE_SCALE) { $mform->removeElement('grade_item_rescalegrades'); $mform->removeElement('grade_item_grademax'); if ($mform->elementExists('grade_item_grademin')) { $mform->removeElement('grade_item_grademin'); } } else { // Not using scale, so remove it. $mform->removeElement('grade_item_scaleid'); $mform->disabledIf('grade_item_grademax', 'grade_item_rescalegrades', 'eq', ''); $mform->disabledIf('grade_item_grademin', 'grade_item_rescalegrades', 'eq', ''); } } else { // Remove the rescale element if there are no grades. $mform->removeElement('grade_item_rescalegrades'); } //remove the aggregation coef element if not needed if ($grade_item->is_course_item()) { if ($mform->elementExists('grade_item_aggregationcoef')) { $mform->removeElement('grade_item_aggregationcoef'); } if ($mform->elementExists('grade_item_weightoverride')) { $mform->removeElement('grade_item_weightoverride'); } if ($mform->elementExists('grade_item_aggregationcoef2')) { $mform->removeElement('grade_item_aggregationcoef2'); } } else { if ($grade_item->is_category_item()) { $category = $grade_item->get_item_category(); $parent_category = $category->get_parent_category(); } else { $parent_category = $grade_item->get_parent_category(); } $parent_category->apply_forced_settings(); if (!$parent_category->is_aggregationcoef_used()) { if ($mform->elementExists('grade_item_aggregationcoef')) { $mform->removeElement('grade_item_aggregationcoef'); } } else { $coefstring = $grade_item->get_coefstring(); if ($coefstring == 'aggregationcoefextrasum' || $coefstring == 'aggregationcoefextraweightsum') { // advcheckbox is not compatible with disabledIf! $coefstring = 'aggregationcoefextrasum'; $element =& $mform->createElement('checkbox', 'grade_item_aggregationcoef', get_string($coefstring, 'grades')); } else { $element =& $mform->createElement('text', 'grade_item_aggregationcoef', get_string($coefstring, 'grades')); } $mform->insertElementBefore($element, 'parentcategory'); $mform->addHelpButton('grade_item_aggregationcoef', $coefstring, 'grades'); } // Remove fields used by natural weighting if the parent category is not using natural weighting. // Or if the item is a scale and scales are not used in aggregation. if ($parent_category->aggregation != GRADE_AGGREGATE_SUM || (empty($CFG->grade_includescalesinaggregation) && $grade_item->gradetype == GRADE_TYPE_SCALE)) { if ($mform->elementExists('grade_item_weightoverride')) { $mform->removeElement('grade_item_weightoverride'); } if ($mform->elementExists('grade_item_aggregationcoef2')) { $mform->removeElement('grade_item_aggregationcoef2'); } } } } } function validation($data, $files) { global $COURSE; $gradeitem = false; if ($data['id']) { $gradecategory = grade_category::fetch(array('id' => $data['id'])); $gradeitem = $gradecategory->load_grade_item(); } $errors = parent::validation($data, $files); if (array_key_exists('grade_item_gradetype', $data) and $data['grade_item_gradetype'] == GRADE_TYPE_SCALE) { if (empty($data['grade_item_scaleid'])) { $errors['grade_item_scaleid'] = get_string('missingscale', 'grades'); } } // We need to make all the validations related with grademax and grademin // with them being correct floats, keeping the originals unmodified for // later validations / showing the form back... // TODO: Note that once MDL-73994 is fixed we'll have to re-visit this and // adapt the code below to the new values arriving here, without forgetting // the special case of empties and nulls. $grademax = isset($data['grade_item_grademax']) ? unformat_float($data['grade_item_grademax']) : null; $grademin = isset($data['grade_item_grademin']) ? unformat_float($data['grade_item_grademin']) : null; if (!is_null($grademin) and !is_null($grademax)) { if (($grademax != 0 OR $grademin != 0) AND ($grademax == $grademin OR $grademax < $grademin)) { $errors['grade_item_grademin'] = get_string('incorrectminmax', 'grades'); $errors['grade_item_grademax'] = get_string('incorrectminmax', 'grades'); } } if ($data['id'] && $gradeitem->has_overridden_grades()) { if ($gradeitem->gradetype == GRADE_TYPE_VALUE) { if (grade_floats_different($grademin, $gradeitem->grademin) || grade_floats_different($grademax, $gradeitem->grademax)) { if (empty($data['grade_item_rescalegrades'])) { $errors['grade_item_rescalegrades'] = get_string('mustchooserescaleyesorno', 'grades'); } } } } return $errors; } } PK\G(G(tree/index.phpnu[. /** * The Gradebook setup page. * * @package core_grades * @copyright 2008 Nicolas Connault * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ define('NO_OUTPUT_BUFFERING', true); // The progress bar may be used here. require_once '../../../config.php'; require_once $CFG->dirroot.'/grade/lib.php'; require_once $CFG->dirroot.'/grade/report/lib.php'; // for preferences require_once $CFG->dirroot.'/grade/edit/tree/lib.php'; $courseid = required_param('id', PARAM_INT); $action = optional_param('action', 0, PARAM_ALPHA); $eid = optional_param('eid', 0, PARAM_ALPHANUM); $weightsadjusted = optional_param('weightsadjusted', 0, PARAM_INT); $url = new moodle_url('/grade/edit/tree/index.php', array('id' => $courseid)); $PAGE->set_url($url); $PAGE->set_pagelayout('admin'); /// Make sure they can even access this course if (!$course = $DB->get_record('course', array('id' => $courseid))) { throw new \moodle_exception('invalidcourseid'); } require_login($course); $context = context_course::instance($course->id); require_capability('moodle/grade:manage', $context); $PAGE->requires->js_call_amd('core_grades/edittree_index', 'init', [$courseid, $USER->id]); $PAGE->requires->js_call_amd('core_grades/gradebooksetup_forms', 'init'); $decsep = get_string('decsep', 'langconfig'); // This setting indicates if we should use algorithm prior to MDL-49257 fix for calculating extra credit weights. $gradebookcalculationfreeze = (int) get_config('core', 'gradebook_calculations_freeze_' . $courseid); $oldextracreditcalculation = $gradebookcalculationfreeze && ($gradebookcalculationfreeze <= 20150619); $PAGE->requires->js_call_amd('core_grades/edittree_weights', 'init', [$decsep, $oldextracreditcalculation]); /// return tracking object $gpr = new grade_plugin_return(array('type'=>'edit', 'plugin'=>'tree', 'courseid'=>$courseid)); $returnurl = $gpr->get_return_url(null); // get the grading tree object // note: total must be first for moving to work correctly, if you want it last moving code must be rewritten! $gtree = new grade_tree($courseid, false, false); if (empty($eid)) { $element = null; $object = null; } else { if (!$element = $gtree->locate_element($eid)) { throw new \moodle_exception('invalidelementid', '', $returnurl); } $object = $element['object']; } $switch = grade_get_setting($course->id, 'aggregationposition', $CFG->grade_aggregationposition); $strgrades = get_string('grades'); $strgraderreport = get_string('graderreport', 'grades'); $moving = false; $movingeid = false; if ($action == 'moveselect') { if ($eid and confirm_sesskey()) { $movingeid = $eid; $moving=true; } } $gradeedittree = new grade_edit_tree($gtree, $movingeid, $gpr); switch ($action) { case 'duplicate': if ($eid and confirm_sesskey()) { if (!$el = $gtree->locate_element($eid)) { throw new \moodle_exception('invalidelementid', '', $returnurl); } $object->duplicate(); redirect($returnurl); } break; case 'delete': if ($eid && confirm_sesskey()) { if (!$gradeedittree->element_deletable($element)) { // no deleting of external activities - they would be recreated anyway! // exception is activity without grading or misconfigured activities break; } $confirm = optional_param('confirm', 0, PARAM_BOOL); if ($confirm) { $object->delete('grade/report/grader/category'); redirect($returnurl); } } break; case 'autosort': //TODO: implement autosorting based on order of mods on course page, categories first, manual items last break; case 'move': if ($eid and confirm_sesskey()) { $moveafter = required_param('moveafter', PARAM_ALPHANUM); $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 if(!$after_el = $gtree->locate_element($moveafter)) { throw new \moodle_exception('invalidelementid', '', $returnurl); } $after = $after_el['object']; $sortorder = $after->get_sortorder(); if (!$first) { $parent = $after->get_parent_category(); $object->set_parent($parent->id); } else { $object->set_parent($after->id); } $object->move_after_sortorder($sortorder); redirect($returnurl); } break; default: break; } // If we go straight to the db to update an element we need to recreate the tree as // $gradeedittree has already been constructed. // Ideally we could do the updates through $gradeedittree to avoid recreating it. $recreatetree = false; if ($data = data_submitted() and confirm_sesskey()) { // Perform bulk actions first if (!empty($data->bulkmove)) { $elements = array(); foreach ($data as $key => $value) { if (preg_match('/select_(ig[0-9]*)/', $key, $matches)) { $elements[] = $matches[1]; } } $gradeedittree->move_elements($elements, $returnurl); } // Update weights (extra credits) on categories and items. foreach ($data as $key => $value) { if (preg_match('/^weight_([0-9]+)$/', $key, $matches)) { $aid = $matches[1]; $value = unformat_float($value); $value = clean_param($value, PARAM_FLOAT); $grade_item = grade_item::fetch(array('id' => $aid, 'courseid' => $courseid)); // Convert weight to aggregation coef2. $aggcoef = $grade_item->get_coefstring(); if ($aggcoef == 'aggregationcoefextraweightsum') { // The field 'weight' should only be sent when the checkbox 'weighoverride' is checked, // so there is not need to set weightoverride here, it is done below. $value = $value / 100.0; $grade_item->aggregationcoef2 = $value; } else if ($aggcoef == 'aggregationcoefweight' || $aggcoef == 'aggregationcoefextraweight') { $grade_item->aggregationcoef = $value; } $grade_item->update(); $recreatetree = true; // Grade item checkbox inputs. } elseif (preg_match('/^(weightoverride)_([0-9]+)$/', $key, $matches)) { $param = $matches[1]; $aid = $matches[2]; $value = clean_param($value, PARAM_BOOL); $grade_item = grade_item::fetch(array('id' => $aid, 'courseid' => $courseid)); $grade_item->$param = $value; $grade_item->update(); $recreatetree = true; } } } $originalweights = grade_helper::fetch_all_natural_weights_for_course($courseid); /** * Callback function to adjust the URL if weights changed after the * regrade. * * @param int $courseid The course ID * @param array $originalweights The weights before the regrade * @param int $weightsadjusted Whether weights have been adjusted * @return moodle_url A URL to redirect to after regrading when a progress bar is displayed. */ $grade_edit_tree_index_checkweights = function() use ($courseid, $originalweights, &$weightsadjusted) { global $PAGE; $alteredweights = grade_helper::fetch_all_natural_weights_for_course($courseid); if (array_diff($originalweights, $alteredweights)) { $weightsadjusted = 1; return new moodle_url($PAGE->url, array('weightsadjusted' => $weightsadjusted)); } return $PAGE->url; }; if (grade_regrade_final_grades_if_required($course, $grade_edit_tree_index_checkweights)) { $recreatetree = true; } $actionbar = new \core_grades\output\gradebook_setup_action_bar($context); print_grade_page_head($courseid, 'settings', 'setup', false, false, false, true, null, null, null, $actionbar); // Print Table of categories and items echo $OUTPUT->box_start('gradetreebox generalbox'); // Did we update something in the db and thus invalidate $gradeedittree? if ($recreatetree) { $gradeedittree = new grade_edit_tree($gtree, $movingeid, $gpr); } $tpldata = (object) [ 'actionurl' => $returnurl, 'sesskey' => sesskey(), 'movingmodeenabled' => $moving, 'courseid' => $courseid ]; // Check to see if we have a normalisation message to send. if ($weightsadjusted) { $notification = new \core\output\notification(get_string('weightsadjusted', 'grades'), \core\output\notification::NOTIFY_INFO); $tpldata->notification = $notification->export_for_template($OUTPUT); } $tpldata->table = html_writer::table($gradeedittree->table); // If not in moving mode and there is more than one grade category, then initialise the bulk action module. if (!$moving && count($gradeedittree->categories) > 1) { $PAGE->requires->js_call_amd('core_grades/bulkactions/edit/tree/bulk_actions', 'init', [$courseid]); } $footercontent = $OUTPUT->render_from_template('core_grades/edit_tree_sticky_footer', $tpldata); $stickyfooter = new core\output\sticky_footer($footercontent); $tpldata->stickyfooter = $OUTPUT->render($stickyfooter); echo $OUTPUT->render_from_template('core_grades/edit_tree', $tpldata); echo $OUTPUT->box_end(); $PAGE->requires->js_call_amd('core_form/changechecker', 'watchFormById', ['gradetreeform']); echo $OUTPUT->footer(); die; PK\)ZZtree/item_form.phpnu[. /** * A moodleform allowing the editing of the grade options for an individual grade item * * @package core_grades * @copyright 2007 Petr Skoda * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ if (!defined('MOODLE_INTERNAL')) { die('Direct access to this script is forbidden.'); /// It must be included from a Moodle page } require_once $CFG->libdir.'/formslib.php'; class edit_item_form extends moodleform { private $displayoptions; function definition() { global $COURSE, $CFG, $DB; $mform =& $this->_form; $item = $this->_customdata['current']; /// visible elements $mform->addElement('header', 'general', get_string('gradeitem', 'grades')); $mform->addElement('text', 'itemname', get_string('itemname', 'grades')); $mform->setType('itemname', PARAM_TEXT); $mform->addElement('text', 'iteminfo', get_string('iteminfo', 'grades')); $mform->addHelpButton('iteminfo', 'iteminfo', 'grades'); $mform->setType('iteminfo', PARAM_TEXT); $mform->addElement('text', 'idnumber', get_string('idnumbermod')); $mform->addHelpButton('idnumber', 'idnumbermod'); $mform->setType('idnumber', PARAM_RAW); if (!empty($item->id)) { $gradeitem = new grade_item(array('id' => $item->id, 'courseid' => $item->courseid)); // If grades exist set a message so the user knows why they can not alter the grade type or scale. // We could never change the grade type for external items, so only need to show this for manual grade items. if ($gradeitem->has_grades() && !$gradeitem->is_external_item()) { // Set a message so the user knows why they can not alter the grade type or scale. if ($gradeitem->gradetype == GRADE_TYPE_SCALE) { $gradesexistmsg = get_string('modgradecantchangegradetyporscalemsg', 'grades'); } else { $gradesexistmsg = get_string('modgradecantchangegradetypemsg', 'grades'); } $gradesexisthtml = '
    ' . $gradesexistmsg . '
    '; $mform->addElement('static', 'gradesexistmsg', '', $gradesexisthtml); } } // Manual grade items cannot have grade type GRADE_TYPE_NONE. $options = array(GRADE_TYPE_VALUE => get_string('typevalue', 'grades'), GRADE_TYPE_SCALE => get_string('typescale', 'grades'), GRADE_TYPE_TEXT => get_string('typetext', 'grades')); $mform->addElement('select', 'gradetype', get_string('gradetype', 'grades'), $options); $mform->addHelpButton('gradetype', 'gradetype', 'grades'); $mform->setDefault('gradetype', GRADE_TYPE_VALUE); //$mform->addElement('text', 'calculation', get_string('calculation', 'grades')); //$mform->disabledIf('calculation', 'gradetype', 'eq', GRADE_TYPE_TEXT); //$mform->disabledIf('calculation', 'gradetype', 'eq', GRADE_TYPE_NONE); $options = array(0=>get_string('usenoscale', 'grades')); if ($scales = grade_scale::fetch_all_local($COURSE->id)) { foreach ($scales as $scale) { $options[$scale->id] = $scale->get_name(); } } if ($scales = grade_scale::fetch_all_global()) { foreach ($scales as $scale) { $options[$scale->id] = $scale->get_name(); } } // ugly BC hack - it was possible to use custom scale from other courses :-( if (!empty($item->scaleid) and !isset($options[$item->scaleid])) { if ($scale = grade_scale::fetch(array('id'=>$item->scaleid))) { $options[$scale->id] = $scale->get_name().get_string('incorrectcustomscale', 'grades'); } } $mform->addElement('select', 'scaleid', get_string('scale'), $options); $mform->addHelpButton('scaleid', 'typescale', 'grades'); $mform->disabledIf('scaleid', 'gradetype', 'noteq', GRADE_TYPE_SCALE); $choices = array(); $choices[''] = get_string('choose'); $choices['no'] = get_string('no'); $choices['yes'] = get_string('yes'); $mform->addElement('select', 'rescalegrades', get_string('modgraderescalegrades', 'grades'), $choices); $mform->addHelpButton('rescalegrades', 'modgraderescalegrades', 'grades'); $mform->disabledIf('rescalegrades', 'gradetype', 'noteq', GRADE_TYPE_VALUE); $mform->addElement('float', 'grademax', get_string('grademax', 'grades')); $mform->addHelpButton('grademax', 'grademax', 'grades'); $mform->disabledIf('grademax', 'gradetype', 'noteq', GRADE_TYPE_VALUE); if ((bool) get_config('moodle', 'grade_report_showmin')) { $mform->addElement('float', 'grademin', get_string('grademin', 'grades')); $mform->addHelpButton('grademin', 'grademin', 'grades'); $mform->disabledIf('grademin', 'gradetype', 'noteq', GRADE_TYPE_VALUE); } $mform->addElement('float', 'gradepass', get_string('gradepass', 'grades')); $mform->addHelpButton('gradepass', 'gradepass', 'grades'); $mform->disabledIf('gradepass', 'gradetype', 'eq', GRADE_TYPE_NONE); $mform->disabledIf('gradepass', 'gradetype', 'eq', GRADE_TYPE_TEXT); $mform->addElement('float', 'multfactor', get_string('multfactor', 'grades')); $mform->addHelpButton('multfactor', 'multfactor', 'grades'); $mform->disabledIf('multfactor', 'gradetype', 'eq', GRADE_TYPE_NONE); $mform->disabledIf('multfactor', 'gradetype', 'eq', GRADE_TYPE_TEXT); $mform->addElement('float', 'plusfactor', get_string('plusfactor', 'grades')); $mform->addHelpButton('plusfactor', 'plusfactor', 'grades'); $mform->disabledIf('plusfactor', 'gradetype', 'eq', GRADE_TYPE_NONE); $mform->disabledIf('plusfactor', 'gradetype', 'eq', GRADE_TYPE_TEXT); /// grade display prefs $default_gradedisplaytype = grade_get_setting($COURSE->id, 'displaytype', $CFG->grade_displaytype); $options = array(GRADE_DISPLAY_TYPE_DEFAULT => get_string('default', 'grades'), GRADE_DISPLAY_TYPE_REAL => get_string('real', 'grades'), GRADE_DISPLAY_TYPE_PERCENTAGE => get_string('percentage', 'grades'), GRADE_DISPLAY_TYPE_LETTER => get_string('letter', 'grades'), GRADE_DISPLAY_TYPE_REAL_PERCENTAGE => get_string('realpercentage', 'grades'), GRADE_DISPLAY_TYPE_REAL_LETTER => get_string('realletter', 'grades'), GRADE_DISPLAY_TYPE_LETTER_REAL => get_string('letterreal', 'grades'), GRADE_DISPLAY_TYPE_LETTER_PERCENTAGE => get_string('letterpercentage', 'grades'), GRADE_DISPLAY_TYPE_PERCENTAGE_LETTER => get_string('percentageletter', 'grades'), GRADE_DISPLAY_TYPE_PERCENTAGE_REAL => get_string('percentagereal', 'grades') ); asort($options); foreach ($options as $key=>$option) { if ($key == $default_gradedisplaytype) { $options[GRADE_DISPLAY_TYPE_DEFAULT] = get_string('defaultprev', 'grades', $option); break; } } $mform->addElement('select', 'display', get_string('gradedisplaytype', 'grades'), $options); $mform->addHelpButton('display', 'gradedisplaytype', 'grades'); $mform->disabledIf('display', 'gradetype', 'eq', GRADE_TYPE_TEXT); $default_gradedecimals = grade_get_setting($COURSE->id, 'decimalpoints', $CFG->grade_decimalpoints); $options = array(-1=>get_string('defaultprev', 'grades', $default_gradedecimals), 0=>0, 1=>1, 2=>2, 3=>3, 4=>4, 5=>5); $mform->addElement('select', 'decimals', get_string('decimalpoints', 'grades'), $options); $mform->addHelpButton('decimals', 'decimalpoints', 'grades'); $mform->setDefault('decimals', -1); $mform->disabledIf('decimals', 'display', 'eq', GRADE_DISPLAY_TYPE_LETTER); if ($default_gradedisplaytype == GRADE_DISPLAY_TYPE_LETTER) { $mform->disabledIf('decimals', 'display', "eq", GRADE_DISPLAY_TYPE_DEFAULT); } $mform->disabledIf('decimals', 'gradetype', 'eq', GRADE_TYPE_TEXT); /// hiding if ($item->cancontrolvisibility) { $mform->addElement('advcheckbox', 'hidden', get_string('hidden', 'grades'), '', [], [0, 1]); $mform->addElement('date_time_selector', 'hiddenuntil', get_string('hiddenuntil', 'grades'), array('optional'=>true)); $mform->disabledIf('hidden', 'hiddenuntil[enabled]', 'checked'); } else { $mform->addElement('static', 'hidden', get_string('hidden', 'grades'), get_string('componentcontrolsvisibility', 'grades')); // Unset hidden to avoid data override. unset($item->hidden); } $mform->addHelpButton('hidden', 'hidden', 'grades'); /// locking $mform->addElement('advcheckbox', 'locked', get_string('locked', 'grades')); $mform->addHelpButton('locked', 'locked', 'grades'); $mform->addElement('date_time_selector', 'locktime', get_string('locktime', 'grades'), array('optional'=>true)); $mform->disabledIf('locktime', 'gradetype', 'eq', GRADE_TYPE_NONE); /// parent category related settings $mform->addElement('header', 'headerparent', get_string('parentcategory', 'grades')); $mform->addElement('advcheckbox', 'weightoverride', get_string('adjustedweight', 'grades')); $mform->addHelpButton('weightoverride', 'weightoverride', 'grades'); $mform->disabledIf('weightoverride', 'gradetype', 'eq', GRADE_TYPE_NONE); $mform->disabledIf('weightoverride', 'gradetype', 'eq', GRADE_TYPE_TEXT); $mform->addElement('float', 'aggregationcoef2', get_string('weight', 'grades')); $mform->addHelpButton('aggregationcoef2', 'weight', 'grades'); $mform->disabledIf('aggregationcoef2', 'weightoverride'); $mform->disabledIf('aggregationcoef2', 'gradetype', 'eq', GRADE_TYPE_NONE); $mform->disabledIf('aggregationcoef2', 'gradetype', 'eq', GRADE_TYPE_TEXT); $options = array(); $coefstring = ''; $categories = grade_category::fetch_all(array('courseid'=>$COURSE->id)); foreach ($categories as $cat) { $cat->apply_forced_settings(); $options[$cat->id] = $cat->get_name(); } if (count($categories) > 1) { $mform->addElement('select', 'parentcategory', get_string('gradecategory', 'grades'), $options); } /// hidden params $mform->addElement('hidden', 'id', 0); $mform->setType('id', PARAM_INT); $mform->addElement('hidden', 'courseid', $COURSE->id); $mform->setType('courseid', PARAM_INT); $mform->addElement('hidden', 'itemtype', 'manual'); // all new items are manual only $mform->setType('itemtype', PARAM_ALPHA); /// add return tracking info $gpr = $this->_customdata['gpr']; $gpr->add_mform_elements($mform); //------------------------------------------------------------------------------- // buttons $this->add_action_buttons(); //------------------------------------------------------------------------------- $this->set_data($item); } /// tweak the form - depending on existing data function definition_after_data() { global $CFG, $COURSE; $mform =& $this->_form; if ($id = $mform->getElementValue('id')) { $gradeitem = grade_item::fetch(array('id' => $id)); $parentcategory = $gradeitem->get_parent_category(); } else { // If we do not have an id, we are creating a new grade item. $gradeitem = new grade_item(array('courseid' => $COURSE->id, 'itemtype' => 'manual'), false); // Assign the course category to this grade item. $parentcategory = grade_category::fetch_course_category($COURSE->id); $gradeitem->parent_category = $parentcategory; } if (!$gradeitem->is_raw_used()) { $mform->removeElement('plusfactor'); $mform->removeElement('multfactor'); } if ($gradeitem->is_outcome_item()) { // We have to prevent incompatible modifications of outcomes if outcomes disabled. $mform->removeElement('grademax'); if ($mform->elementExists('grademin')) { $mform->removeElement('grademin'); } $mform->removeElement('gradetype'); $mform->removeElement('display'); $mform->removeElement('decimals'); $mform->hardFreeze('scaleid'); } else { if ($gradeitem->is_external_item()) { // Following items are set up from modules and should not be overrided by user. if ($mform->elementExists('grademin')) { // The site setting grade_report_showmin may have prevented grademin being added to the form. $mform->hardFreeze('grademin'); } $mform->hardFreeze('itemname,gradetype,grademax,scaleid'); if ($gradeitem->itemnumber == 0) { // The idnumber of grade itemnumber 0 is synced with course_modules. $mform->hardFreeze('idnumber'); } // For external items we can not change the grade type, even if no grades exist, so if it is set to // scale, then remove the grademax and grademin fields from the form - no point displaying them. if ($gradeitem->gradetype == GRADE_TYPE_SCALE) { $mform->removeElement('grademax'); if ($mform->elementExists('grademin')) { $mform->removeElement('grademin'); } } else { // Not using scale, so remove it. $mform->removeElement('scaleid'); } // Always remove the rescale grades element if it's an external item. $mform->removeElement('rescalegrades'); } else if ($gradeitem->has_grades()) { // Can't change the grade type or the scale if there are grades. $mform->hardFreeze('gradetype, scaleid'); // If we are using scales then remove the unnecessary rescale and grade fields. if ($gradeitem->gradetype == GRADE_TYPE_SCALE) { $mform->removeElement('rescalegrades'); $mform->removeElement('grademax'); if ($mform->elementExists('grademin')) { $mform->removeElement('grademin'); } } else { // Remove the scale field. $mform->removeElement('scaleid'); // Set the maximum grade to disabled unless a grade is chosen. $mform->disabledIf('grademax', 'rescalegrades', 'eq', ''); } } else { // Remove the rescale element if there are no grades. $mform->removeElement('rescalegrades'); } } // If we wanted to change parent of existing item - we would have to verify there are no circular references in parents!!! if ($id && $mform->elementExists('parentcategory')) { $mform->hardFreeze('parentcategory'); } $parentcategory->apply_forced_settings(); if (!$parentcategory->is_aggregationcoef_used()) { if ($mform->elementExists('aggregationcoef')) { $mform->removeElement('aggregationcoef'); } } else { $coefstring = $gradeitem->get_coefstring(); if ($coefstring !== '') { if ($coefstring == 'aggregationcoefextrasum' || $coefstring == 'aggregationcoefextraweightsum') { // The advcheckbox is not compatible with disabledIf! $coefstring = 'aggregationcoefextrasum'; $element =& $mform->createElement('checkbox', 'aggregationcoef', get_string($coefstring, 'grades')); } else { $element =& $mform->createElement('text', 'aggregationcoef', get_string($coefstring, 'grades')); } if ($mform->elementExists('parentcategory')) { $mform->insertElementBefore($element, 'parentcategory'); } else { $mform->insertElementBefore($element, 'id'); } $mform->addHelpButton('aggregationcoef', $coefstring, 'grades'); } $mform->disabledIf('aggregationcoef', 'gradetype', 'eq', GRADE_TYPE_NONE); $mform->disabledIf('aggregationcoef', 'gradetype', 'eq', GRADE_TYPE_TEXT); $mform->disabledIf('aggregationcoef', 'parentcategory', 'eq', $parentcategory->id); } // Remove fields used by natural weighting if the parent category is not using natural weighting. // Or if the item is a scale and scales are not used in aggregation. if ($parentcategory->aggregation != GRADE_AGGREGATE_SUM || (empty($CFG->grade_includescalesinaggregation) && $gradeitem->gradetype == GRADE_TYPE_SCALE)) { if ($mform->elementExists('weightoverride')) { $mform->removeElement('weightoverride'); } if ($mform->elementExists('aggregationcoef2')) { $mform->removeElement('aggregationcoef2'); } } if ($category = $gradeitem->get_item_category()) { if ($category->aggregation == GRADE_AGGREGATE_SUM) { if ($mform->elementExists('gradetype')) { $mform->hardFreeze('gradetype'); } if ($mform->elementExists('grademin')) { $mform->hardFreeze('grademin'); } if ($mform->elementExists('grademax')) { $mform->hardFreeze('grademax'); } if ($mform->elementExists('scaleid')) { $mform->removeElement('scaleid'); } } } // no parent header for course category if (!$mform->elementExists('aggregationcoef') and !$mform->elementExists('parentcategory')) { $mform->removeElement('headerparent'); } } /// perform extra validation before submission function validation($data, $files) { global $COURSE; $grade_item = false; if ($data['id']) { $grade_item = new grade_item(array('id' => $data['id'], 'courseid' => $data['courseid'])); } $errors = parent::validation($data, $files); if (array_key_exists('idnumber', $data)) { if ($grade_item) { if ($grade_item->itemtype == 'mod') { $cm = get_coursemodule_from_instance($grade_item->itemmodule, $grade_item->iteminstance, $grade_item->courseid); } else { $cm = null; } } else { $grade_item = null; $cm = null; } if (!grade_verify_idnumber($data['idnumber'], $COURSE->id, $grade_item, $cm)) { $errors['idnumber'] = get_string('idnumbertaken'); } } if (array_key_exists('gradetype', $data) and $data['gradetype'] == GRADE_TYPE_SCALE) { if (empty($data['scaleid'])) { $errors['scaleid'] = get_string('missingscale', 'grades'); } } // We need to make all the validations related with grademax and grademin // with them being correct floats, keeping the originals unmodified for // later validations / showing the form back... // TODO: Note that once MDL-73994 is fixed we'll have to re-visit this and // adapt the code below to the new values arriving here, without forgetting // the special case of empties and nulls. $grademax = isset($data['grademax']) ? unformat_float($data['grademax']) : null; $grademin = isset($data['grademin']) ? unformat_float($data['grademin']) : null; if (!is_null($grademin) and !is_null($grademax)) { if ($grademax == $grademin or $grademax < $grademin) { $errors['grademin'] = get_string('incorrectminmax', 'grades'); $errors['grademax'] = get_string('incorrectminmax', 'grades'); } } // We do not want the user to be able to change the grade type or scale for this item if grades exist. if ($grade_item && $grade_item->has_grades()) { // Check that grade type is set - should never not be set unless form has been modified. if (!isset($data['gradetype'])) { $errors['gradetype'] = get_string('modgradecantchangegradetype', 'grades'); } else if ($data['gradetype'] !== $grade_item->gradetype) { // Check if we are changing the grade type. $errors['gradetype'] = get_string('modgradecantchangegradetype', 'grades'); } else if ($data['gradetype'] == GRADE_TYPE_SCALE) { // Check if we are changing the scale - can't do this when grades exist. if (isset($data['scaleid']) && ($data['scaleid'] !== $grade_item->scaleid)) { $errors['scaleid'] = get_string('modgradecantchangescale', 'grades'); } } } if ($grade_item) { if ($grade_item->gradetype == GRADE_TYPE_VALUE) { if ((((bool) get_config('moodle', 'grade_report_showmin')) && grade_floats_different($grademin, $grade_item->grademin)) || grade_floats_different($grademax, $grade_item->grademax)) { if ($grade_item->has_grades() && empty($data['rescalegrades'])) { $errors['rescalegrades'] = get_string('mustchooserescaleyesorno', 'grades'); } } } } return $errors; } } PK\NL##tree/outcomeitem.phpnu[. /** * A page to create or edit outcome grade items * * @package core_grades * @copyright 2007 Petr Skoda * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ use core_grades\form\add_outcome; require_once '../../../config.php'; require_once $CFG->dirroot.'/grade/lib.php'; require_once $CFG->dirroot.'/grade/report/lib.php'; require_once 'outcomeitem_form.php'; $courseid = required_param('courseid', PARAM_INT); $id = optional_param('id', 0, PARAM_INT); $url = new moodle_url('/grade/edit/tree/outcomeitem.php', array('courseid'=>$courseid)); if ($id !== 0) { $url->param('id', $id); } $PAGE->set_url($url); $PAGE->set_pagelayout('admin'); navigation_node::override_active_url(new moodle_url('/grade/edit/tree/index.php', array('id'=>$courseid))); if (!$course = $DB->get_record('course', array('id' => $courseid))) { throw new \moodle_exception('invalidcourseid'); } require_login($course); $context = context_course::instance($course->id); require_capability('moodle/grade:manage', $context); // default return url $gpr = new grade_plugin_return(); $returnurl = $gpr->get_return_url('index.php?id='.$course->id); $mform = new edit_outcomeitem_form(null, array('gpr'=>$gpr)); if ($mform->is_cancelled() || empty($CFG->enableoutcomes)) { redirect($returnurl); } $heading = get_string('outcomeitemsedit', 'grades'); if ($grade_item = grade_item::fetch(array('id'=>$id, 'courseid'=>$courseid))) { // redirect if outcomeid present if (empty($grade_item->outcomeid)) { $url = new moodle_url('/grade/edit/tree/item.php', ['id' => $id, 'courseid' => $courseid]); redirect($gpr->add_url_params($url)); } $item = $grade_item->get_record_data(); $parent_category = $grade_item->get_parent_category(); $item->parentcategory = $parent_category->id; if ($item->itemtype == 'mod') { $cm = get_coursemodule_from_instance($item->itemmodule, $item->iteminstance, $item->courseid); $item->cmid = $cm->id; } else { $item->cmid = 0; } } else { $heading = get_string('newoutcomeitem', 'grades'); $grade_item = new grade_item(array('courseid'=>$courseid, 'itemtype'=>'manual'), false); $item = $grade_item->get_record_data(); $item->cmid = 0; $parent_category = grade_category::fetch_course_category($courseid); $item->parentcategory = $parent_category->id; } $decimalpoints = $grade_item->get_decimals(); if ($item->hidden > 1) { $item->hiddenuntil = $item->hidden; $item->hidden = 0; } else { $item->hiddenuntil = 0; } $item->locked = !empty($item->locked); $item->gradepass = format_float($item->gradepass, $decimalpoints); if (empty($parent_category)) { $item->aggregationcoef = 0; } else if ($parent_category->aggregation == GRADE_AGGREGATE_SUM) { $item->aggregationcoef = $item->aggregationcoef > 0 ? 1 : 0; $item->aggregationcoef2 = format_float($item->aggregationcoef2 * 100.0); } else { $item->aggregationcoef = format_float($item->aggregationcoef, 4); } $mform->set_data($item); $simpleform = new add_outcome(null, ['itemid' => $grade_item->id, 'courseid' => $courseid, 'gpr' => $gpr]); // Data has been carried over from the dynamic form. if ($simpledata = $simpleform->get_submitted_data()) { $mform->set_data($simpledata); } if ($data = $mform->get_data()) { // This is a new item, and the category chosen is different than the default category. if (empty($grade_item->id) && isset($data->parentcategory) && $parent_category->id != $data->parentcategory) { $parent_category = grade_category::fetch(array('id' => $data->parentcategory)); } // If unset, give the aggregation values a default based on parent aggregation method. $defaults = grade_category::get_default_aggregation_coefficient_values($parent_category->aggregation); if (!isset($data->aggregationcoef) || $data->aggregationcoef == '') { $data->aggregationcoef = $defaults['aggregationcoef']; } if (!isset($data->weightoverride)) { $data->weightoverride = $defaults['weightoverride']; } if (property_exists($data, 'calculation')) { $data->calculation = grade_item::normalize_formula($data->calculation, $course->id); } $hidden = empty($data->hidden) ? 0: $data->hidden; $hiddenuntil = empty($data->hiddenuntil) ? 0: $data->hiddenuntil; unset($data->hidden); unset($data->hiddenuntil); $locked = empty($data->locked) ? 0: $data->locked; $locktime = empty($data->locktime) ? 0: $data->locktime; unset($data->locked); unset($data->locktime); $convert = array('gradepass', 'aggregationcoef', 'aggregationcoef2'); foreach ($convert as $param) { if (property_exists($data, $param)) { $data->$param = unformat_float($data->$param); } } if (isset($data->aggregationcoef2) && $parent_category->aggregation == GRADE_AGGREGATE_SUM) { $data->aggregationcoef2 = $data->aggregationcoef2 / 100.0; } else { $data->aggregationcoef2 = $defaults['aggregationcoef2']; } $grade_item = new grade_item(array('id'=>$id, 'courseid'=>$courseid)); grade_item::set_properties($grade_item, $data); // fix activity links if (empty($data->cmid)) { // manual item $grade_item->itemtype = 'manual'; $grade_item->itemmodule = null; $grade_item->iteminstance = null; $grade_item->itemnumber = 0; } else { $params = array($data->cmid); $module = $DB->get_record_sql("SELECT cm.*, m.name as modname FROM {modules} m, {course_modules} cm WHERE cm.id = ? AND cm.module = m.id ", $params); $grade_item->itemtype = 'mod'; $grade_item->itemmodule = $module->modname; $grade_item->iteminstance = $module->instance; if ($items = grade_item::fetch_all(array('itemtype'=>'mod', 'itemmodule'=>$grade_item->itemmodule, 'iteminstance'=>$grade_item->iteminstance, 'courseid'=>$COURSE->id))) { if (!empty($grade_item->id) and in_array($grade_item, $items)) { //no change needed } else { $max = 999; foreach($items as $item) { if (empty($item->outcomeid)) { continue; } if ($item->itemnumber > $max) { $max = $item->itemnumber; } } $grade_item->itemnumber = $max + 1; } } else { $grade_item->itemnumber = 1000; } } // fix scale used $outcome = grade_outcome::fetch(array('id'=>$data->outcomeid)); $grade_item->gradetype = GRADE_TYPE_SCALE; $grade_item->scaleid = $outcome->scaleid; //TODO: we might recalculate existing outcome grades when changing scale if (empty($grade_item->id)) { $grade_item->insert(); // move next to activity if adding linked outcome if ($grade_item->itemtype == 'mod') { if ($item = grade_item::fetch(array('itemtype'=>'mod', 'itemmodule'=>$grade_item->itemmodule, 'iteminstance'=>$grade_item->iteminstance, 'itemnumber'=>0, 'courseid'=>$COURSE->id))) { $grade_item->set_parent($item->categoryid); $grade_item->move_after_sortorder($item->sortorder); } } else { // set parent if needed if (isset($data->parentcategory)) { $grade_item->set_parent($data->parentcategory, false); } } } else { $grade_item->update(); } // update hiding flag if ($hiddenuntil) { $grade_item->set_hidden($hiddenuntil, false); } else { $grade_item->set_hidden($hidden, false); } $grade_item->set_locktime($locktime); // locktime first - it might be removed when unlocking $grade_item->set_locked($locked, false, true); redirect($returnurl); } $PAGE->navbar->add($heading); print_grade_page_head($courseid, 'settings', null, $heading, false, false, false); if (!grade_outcome::fetch_all_available($COURSE->id)) { echo $OUTPUT->confirm(get_string('nooutcomes', 'grades'), $CFG->wwwroot.'/grade/edit/outcome/course.php?id='.$courseid, $returnurl); echo $OUTPUT->footer(); die(); } $mform->display(); echo $OUTPUT->footer(); PKg\?tree/move_options_tree.jsnu[// 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 . /** * Keyboard navigation and aria-tree compatibility for the grade move options. * * @module core_grades/bulkactions/edit/tree/move_options_tree * @copyright 2023 Mihail Geshoski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ import Tree from 'core/tree'; import {getList} from 'core/normalise'; /** @constant {Object} The object containing the relevant selectors. */ const Selectors = { moveOptionsTree: '#destination-selector [role="tree"]', moveOption: '#destination-selector [role="treeitem"]', toggleGroupLink: '#destination-selector .collapse-list-link', }; export default class MoveOptionsTree extends Tree { /** @property {function|null} afterSelectMoveOptionCallback Callback function to run after selecting a move option. */ afterSelectMoveOptionCallback = null; /** @property {HTMLElement|null} selectedMoveOption The selected move option. */ selectedMoveOption = null; /** * The class constructor. * * @param {function|null} afterSelectMoveOptionCallback Callback function used to define actions that should be run * after selecting a move option. * @returns {void} */ constructor(afterSelectMoveOptionCallback) { super(Selectors.moveOptionsTree); this.afterSelectMoveOptionCallback = afterSelectMoveOptionCallback; } /** * Handle a key down event. * * @method handleKeyDown * @param {Event} e The event. */ handleKeyDown(e) { // If the user presses enter or space, select the item. if (e.keyCode === this.keys.enter || e.keyCode === this.keys.space) { this.selectMoveOption(e.target); } else { // Otherwise, let the default behaviour happen. super.handleKeyDown(e); } } /** * Handle an item click. * * @param {Event} event The click event. * @param {jQuery} item The item clicked. * @returns {void} */ handleItemClick(event, item) { const isToggleGroupLink = event.target.closest(Selectors.toggleGroupLink); // If the click is on the toggle group (chevron) link, let the default behaviour happen. if (isToggleGroupLink) { super.handleItemClick(event, item); return; } // If the click is on the item itself, select it. this.selectMoveOption(getList(item)[0]); } /** * Select a move option. * * @method selectMoveOption * @param {HTMLElement} moveOption The move option to select. */ selectMoveOption(moveOption) { // Create the cache of the visible items. this.refreshVisibleItemsCache(); // Deselect all the move options. document.querySelectorAll(Selectors.moveOption).forEach(item => { item.dataset.selected = "false"; }); // Select and set the focus on the specified move option. moveOption.dataset.selected = "true"; this.selectedMoveOption = moveOption; moveOption.focus(); // Call the callback function if it is defined. if (typeof this.afterSelectMoveOptionCallback === 'function') { this.afterSelectMoveOptionCallback(); } } } PKg\Q tree/bulk_actions.jsnu[// 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 . import BulkActions from "core/bulkactions/bulk_actions"; import GradebookEditTreeBulkMove from "core_grades/bulkactions/edit/tree/move"; /** * Class for defining the bulk actions area in the gradebook setup page. * * @module core_grades/bulkactions/edit/tree/bulk_actions * @copyright 2023 Mihail Geshoski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ const Selectors = { selectBulkItemCheckbox: 'input[type="checkbox"].itemselect' }; export default class GradebookEditTreeBulkActions extends BulkActions { /** @property {int|null} courseID The course ID. */ courseID = null; /** * Returns the instance of the class. * * @param {int} courseID * @returns {GradebookEditTreeBulkActions} */ static init(courseID) { return new this(courseID); } /** * The class constructor. * * @param {int} courseID The course ID. * @returns {void} */ constructor(courseID) { super(); this.courseID = courseID; } /** * Returns the array of the relevant bulk action objects for the gradebook setup page. * * @method getBulkActions * @returns {Array} */ getBulkActions() { return [ new GradebookEditTreeBulkMove(this.courseID) ]; } /** * Returns the array of selected items. * * @method getSelectedItems * @returns {Array} */ getSelectedItems() { return document.querySelectorAll(`${Selectors.selectBulkItemCheckbox}:checked`); } /** * Adds the listener for the item select change event. * * @method registerItemSelectChangeEvent * @param {function} eventHandler The event handler function. * @returns {void} */ registerItemSelectChangeEvent(eventHandler) { const itemSelectCheckboxes = document.querySelectorAll(Selectors.selectBulkItemCheckbox); itemSelectCheckboxes.forEach((checkbox) => { checkbox.addEventListener('change', eventHandler.bind(this)); }); } /** * Defines the action for deselecting a selected item. * * @method deselectItem * @param {HTMLElement} selectedItem The selected element. * @returns {void} */ deselectItem(selectedItem) { selectedItem.checked = false; } } PKg\ tree/move.jsnu[// 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 . /** * Class that defines the bulk move action in the gradebook setup page. * * @module core_grades/bulkactions/edit/tree/move * @copyright 2023 Mihail Geshoski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ import BulkAction from 'core/bulkactions/bulk_action'; import {get_string as getString} from 'core/str'; import ModalSaveCancel from 'core/modal_save_cancel'; import Templates from 'core/templates'; import Ajax from 'core/ajax'; import ModalEvents from 'core/modal_events'; import MoveOptionsTree from 'core_grades/bulkactions/edit/tree/move_options_tree'; /** @constant {Object} The object containing the relevant selectors. */ const Selectors = { editTreeForm: '#gradetreeform', bulkMoveInput: 'input[name="bulkmove"]', bulkMoveAfterInput: 'input[name="moveafter"]' }; export default class GradebookEditTreeBulkMove extends BulkAction { /** @property {int|null} courseId The course ID. */ courseId = null; /** @property {MoveOptionsTree|null} moveOptionsTree The move options tree object. */ moveOptionsTree = null; /** @property {string|null} gradeTree The grade tree structure. */ gradeTree = null; /** * The class constructor. * * @param {int} courseId The course ID. * @returns {void} */ constructor(courseId) { super(); this.courseId = courseId; } /** * Defines the selector of the element that triggers the bulk move action. * * @returns {string} The bulk move action trigger selector. */ getBulkActionTriggerSelector() { return '[data-type="bulkactions"] [data-action="move"]'; } /** * Defines the behavior once the bulk move action is triggered. * * @method executeBulkAction * @returns {void} */ async triggerBulkAction() { const modal = await this.showModal(); this.registerCustomListenerEvents(modal); } /** * Renders the bulk move action trigger element. * * @method renderBulkActionTrigger * @param {boolean} showInDropdown Whether the action is displayed under a 'More' dropdown or as a separate button. * @param {number} index The index of the action. * @returns {Promise} The bulk move action trigger promise */ async renderBulkActionTrigger(showInDropdown, index) { return Templates.render('core_grades/bulkactions/edit/tree/bulk_move_trigger', { showindropdown: showInDropdown, isfirst: index === 0, }); } /** * Register custom event listeners. * * @method registerCustomClickListenerEvents * @param {Object} modal The modal object. * @returns {void} */ async registerCustomListenerEvents(modal) { await modal.getBody(); // Initialize the move options tree once the modal is shown. modal.getRoot().on(ModalEvents.shown, () => { this.moveOptionsTree = new MoveOptionsTree(() => { // Enable the 'Move' action button once something is selected. modal.setButtonDisabled('save', false); }); }); // Destroy the modal once it is hidden. modal.getRoot().on(ModalEvents.hidden, () => { modal.destroy(); }); // Define the move action event. modal.getRoot().on(ModalEvents.save, () => { // Make sure that a move option is selected. if (this.moveOptionsTree && this.moveOptionsTree.selectedMoveOption) { // Set the relevant form values. document.querySelector(Selectors.bulkMoveInput).value = 1; document.querySelector(Selectors.bulkMoveAfterInput).value = this.moveOptionsTree.selectedMoveOption.dataset.id; // Submit the form. document.querySelector(Selectors.editTreeForm).submit(); } }); } /** * Fetch the grade tree structure for the current course. * * @method fetchGradeTree * @returns {Promise} The grade tree promise */ fetchGradeTree() { const request = { methodname: 'core_grades_get_grade_tree', args: { courseid: this.courseId, }, }; return Ajax.call([request])[0]; } /** * Renders the bulk move modal body. * * @method renderModalBody * @returns {Promise} The modal body promise */ async renderModalBody() { // We need to fetch the grade tree structure only once. if (this.gradeTree === null) { this.gradeTree = await this.fetchGradeTree(); } return Templates.render('core_grades/bulkactions/edit/tree/bulk_move_grade_tree', JSON.parse(this.gradeTree)); } /** * Show the bulk move modal. * * @method showModal * @returns {Promise} The modal promise */ async showModal() { const modal = await ModalSaveCancel.create({ title: await getString('movesitems', 'grades'), body: await this.renderModalBody(), buttons: { save: await getString('move') }, large: true, }); // Disable the 'Move' action button until something is selected. modal.setButtonDisabled('save', true); modal.show(); return modal; } } PKr\u-VVtree/bulk_actions.min.js.mapnu[{"version":3,"file":"bulk_actions.min.js","sources":["../../../../src/bulkactions/edit/tree/bulk_actions.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\nimport BulkActions from \"core/bulkactions/bulk_actions\";\nimport GradebookEditTreeBulkMove from \"core_grades/bulkactions/edit/tree/move\";\n\n/**\n * Class for defining the bulk actions area in the gradebook setup page.\n *\n * @module core_grades/bulkactions/edit/tree/bulk_actions\n * @copyright 2023 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nconst Selectors = {\n selectBulkItemCheckbox: 'input[type=\"checkbox\"].itemselect'\n};\n\nexport default class GradebookEditTreeBulkActions extends BulkActions {\n\n /** @property {int|null} courseID The course ID. */\n courseID = null;\n\n /**\n * Returns the instance of the class.\n *\n * @param {int} courseID\n * @returns {GradebookEditTreeBulkActions}\n */\n static init(courseID) {\n return new this(courseID);\n }\n\n /**\n * The class constructor.\n *\n * @param {int} courseID The course ID.\n * @returns {void}\n */\n constructor(courseID) {\n super();\n this.courseID = courseID;\n }\n\n /**\n * Returns the array of the relevant bulk action objects for the gradebook setup page.\n *\n * @method getBulkActions\n * @returns {Array}\n */\n getBulkActions() {\n return [\n new GradebookEditTreeBulkMove(this.courseID)\n ];\n }\n\n /**\n * Returns the array of selected items.\n *\n * @method getSelectedItems\n * @returns {Array}\n */\n getSelectedItems() {\n return document.querySelectorAll(`${Selectors.selectBulkItemCheckbox}:checked`);\n }\n\n /**\n * Adds the listener for the item select change event.\n *\n * @method registerItemSelectChangeEvent\n * @param {function} eventHandler The event handler function.\n * @returns {void}\n */\n registerItemSelectChangeEvent(eventHandler) {\n const itemSelectCheckboxes = document.querySelectorAll(Selectors.selectBulkItemCheckbox);\n itemSelectCheckboxes.forEach((checkbox) => {\n checkbox.addEventListener('change', eventHandler.bind(this));\n });\n }\n\n /**\n * Defines the action for deselecting a selected item.\n *\n * @method deselectItem\n * @param {HTMLElement} selectedItem The selected element.\n * @returns {void}\n */\n deselectItem(selectedItem) {\n selectedItem.checked = false;\n }\n}\n"],"names":["Selectors","GradebookEditTreeBulkActions","BulkActions","courseID","this","constructor","getBulkActions","GradebookEditTreeBulkMove","getSelectedItems","document","querySelectorAll","registerItemSelectChangeEvent","eventHandler","forEach","checkbox","addEventListener","bind","deselectItem","selectedItem","checked"],"mappings":";;;;;;;;MA0BMA,iCACsB,0CAGPC,qCAAqCC,kCAW1CC,iBACD,IAAIC,KAAKD,UASpBE,YAAYF,0CAlBD,6IAoBFA,SAAWA,SASpBG,uBACW,CACH,IAAIC,cAA0BH,KAAKD,WAU3CK,0BACWC,SAASC,2BAAoBV,8CAUxCW,8BAA8BC,cACGH,SAASC,iBAAiBV,kCAClCa,SAASC,WAC1BA,SAASC,iBAAiB,SAAUH,aAAaI,KAAKZ,UAW9Da,aAAaC,cACTA,aAAaC,SAAU"}PKr\[L!tree/move_options_tree.min.js.mapnu[{"version":3,"file":"move_options_tree.min.js","sources":["../../../../src/bulkactions/edit/tree/move_options_tree.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Keyboard navigation and aria-tree compatibility for the grade move options.\n *\n * @module core_grades/bulkactions/edit/tree/move_options_tree\n * @copyright 2023 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Tree from 'core/tree';\nimport {getList} from 'core/normalise';\n\n/** @constant {Object} The object containing the relevant selectors. */\nconst Selectors = {\n moveOptionsTree: '#destination-selector [role=\"tree\"]',\n moveOption: '#destination-selector [role=\"treeitem\"]',\n toggleGroupLink: '#destination-selector .collapse-list-link',\n};\n\nexport default class MoveOptionsTree extends Tree {\n\n /** @property {function|null} afterSelectMoveOptionCallback Callback function to run after selecting a move option. */\n afterSelectMoveOptionCallback = null;\n\n /** @property {HTMLElement|null} selectedMoveOption The selected move option. */\n selectedMoveOption = null;\n\n /**\n * The class constructor.\n *\n * @param {function|null} afterSelectMoveOptionCallback Callback function used to define actions that should be run\n * after selecting a move option.\n * @returns {void}\n */\n constructor(afterSelectMoveOptionCallback) {\n super(Selectors.moveOptionsTree);\n this.afterSelectMoveOptionCallback = afterSelectMoveOptionCallback;\n }\n\n /**\n * Handle a key down event.\n *\n * @method handleKeyDown\n * @param {Event} e The event.\n */\n handleKeyDown(e) {\n // If the user presses enter or space, select the item.\n if (e.keyCode === this.keys.enter || e.keyCode === this.keys.space) {\n this.selectMoveOption(e.target);\n } else { // Otherwise, let the default behaviour happen.\n super.handleKeyDown(e);\n }\n }\n\n /**\n * Handle an item click.\n *\n * @param {Event} event The click event.\n * @param {jQuery} item The item clicked.\n * @returns {void}\n */\n handleItemClick(event, item) {\n const isToggleGroupLink = event.target.closest(Selectors.toggleGroupLink);\n // If the click is on the toggle group (chevron) link, let the default behaviour happen.\n if (isToggleGroupLink) {\n super.handleItemClick(event, item);\n return;\n }\n // If the click is on the item itself, select it.\n this.selectMoveOption(getList(item)[0]);\n }\n\n /**\n * Select a move option.\n *\n * @method selectMoveOption\n * @param {HTMLElement} moveOption The move option to select.\n */\n selectMoveOption(moveOption) {\n // Create the cache of the visible items.\n this.refreshVisibleItemsCache();\n // Deselect all the move options.\n document.querySelectorAll(Selectors.moveOption).forEach(item => {\n item.dataset.selected = \"false\";\n });\n // Select and set the focus on the specified move option.\n moveOption.dataset.selected = \"true\";\n this.selectedMoveOption = moveOption;\n moveOption.focus();\n // Call the callback function if it is defined.\n if (typeof this.afterSelectMoveOptionCallback === 'function') {\n this.afterSelectMoveOptionCallback();\n }\n }\n}\n"],"names":["Selectors","MoveOptionsTree","Tree","constructor","afterSelectMoveOptionCallback","handleKeyDown","e","keyCode","this","keys","enter","space","selectMoveOption","target","handleItemClick","event","item","closest","moveOption","refreshVisibleItemsCache","document","querySelectorAll","forEach","dataset","selected","selectedMoveOption","focus"],"mappings":"icA2BMA,0BACe,sCADfA,qBAEU,0CAFVA,0BAGe,kDAGAC,wBAAwBC,cAezCC,YAAYC,qCACFJ,gFAbsB,gDAGX,WAWZI,8BAAgCA,8BASzCC,cAAcC,GAENA,EAAEC,UAAYC,KAAKC,KAAKC,OAASJ,EAAEC,UAAYC,KAAKC,KAAKE,WACpDC,iBAAiBN,EAAEO,cAElBR,cAAcC,GAW5BQ,gBAAgBC,MAAOC,MACOD,MAAMF,OAAOI,QAAQjB,iCAGrCc,gBAAgBC,MAAOC,WAI5BJ,kBAAiB,sBAAQI,MAAM,IASxCJ,iBAAiBM,iBAERC,2BAELC,SAASC,iBAAiBrB,sBAAsBsB,SAAQN,OACpDA,KAAKO,QAAQC,SAAW,WAG5BN,WAAWK,QAAQC,SAAW,YACzBC,mBAAqBP,WAC1BA,WAAWQ,QAEuC,mBAAvClB,KAAKJ,oCACPA"}PKr\]^1\\tree/move.min.js.mapnu[{"version":3,"file":"move.min.js","sources":["../../../../src/bulkactions/edit/tree/move.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Class that defines the bulk move action in the gradebook setup page.\n *\n * @module core_grades/bulkactions/edit/tree/move\n * @copyright 2023 Mihail Geshoski \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport BulkAction from 'core/bulkactions/bulk_action';\nimport {get_string as getString} from 'core/str';\nimport ModalSaveCancel from 'core/modal_save_cancel';\nimport Templates from 'core/templates';\nimport Ajax from 'core/ajax';\nimport ModalEvents from 'core/modal_events';\nimport MoveOptionsTree from 'core_grades/bulkactions/edit/tree/move_options_tree';\n\n/** @constant {Object} The object containing the relevant selectors. */\nconst Selectors = {\n editTreeForm: '#gradetreeform',\n bulkMoveInput: 'input[name=\"bulkmove\"]',\n bulkMoveAfterInput: 'input[name=\"moveafter\"]'\n};\n\nexport default class GradebookEditTreeBulkMove extends BulkAction {\n\n /** @property {int|null} courseId The course ID. */\n courseId = null;\n\n /** @property {MoveOptionsTree|null} moveOptionsTree The move options tree object. */\n moveOptionsTree = null;\n\n /** @property {string|null} gradeTree The grade tree structure. */\n gradeTree = null;\n\n /**\n * The class constructor.\n *\n * @param {int} courseId The course ID.\n * @returns {void}\n */\n constructor(courseId) {\n super();\n this.courseId = courseId;\n }\n\n /**\n * Defines the selector of the element that triggers the bulk move action.\n *\n * @returns {string} The bulk move action trigger selector.\n */\n getBulkActionTriggerSelector() {\n return '[data-type=\"bulkactions\"] [data-action=\"move\"]';\n }\n\n /**\n * Defines the behavior once the bulk move action is triggered.\n *\n * @method executeBulkAction\n * @returns {void}\n */\n async triggerBulkAction() {\n const modal = await this.showModal();\n this.registerCustomListenerEvents(modal);\n }\n\n /**\n * Renders the bulk move action trigger element.\n *\n * @method renderBulkActionTrigger\n * @param {boolean} showInDropdown Whether the action is displayed under a 'More' dropdown or as a separate button.\n * @param {number} index The index of the action.\n * @returns {Promise} The bulk move action trigger promise\n */\n async renderBulkActionTrigger(showInDropdown, index) {\n return Templates.render('core_grades/bulkactions/edit/tree/bulk_move_trigger', {\n showindropdown: showInDropdown,\n isfirst: index === 0,\n });\n }\n\n /**\n * Register custom event listeners.\n *\n * @method registerCustomClickListenerEvents\n * @param {Object} modal The modal object.\n * @returns {void}\n */\n async registerCustomListenerEvents(modal) {\n await modal.getBody();\n // Initialize the move options tree once the modal is shown.\n modal.getRoot().on(ModalEvents.shown, () => {\n this.moveOptionsTree = new MoveOptionsTree(() => {\n // Enable the 'Move' action button once something is selected.\n modal.setButtonDisabled('save', false);\n });\n });\n // Destroy the modal once it is hidden.\n modal.getRoot().on(ModalEvents.hidden, () => {\n modal.destroy();\n });\n // Define the move action event.\n modal.getRoot().on(ModalEvents.save, () => {\n // Make sure that a move option is selected.\n if (this.moveOptionsTree && this.moveOptionsTree.selectedMoveOption) {\n // Set the relevant form values.\n document.querySelector(Selectors.bulkMoveInput).value = 1;\n document.querySelector(Selectors.bulkMoveAfterInput).value = this.moveOptionsTree.selectedMoveOption.dataset.id;\n // Submit the form.\n document.querySelector(Selectors.editTreeForm).submit();\n }\n });\n }\n\n /**\n * Fetch the grade tree structure for the current course.\n *\n * @method fetchGradeTree\n * @returns {Promise} The grade tree promise\n */\n fetchGradeTree() {\n const request = {\n methodname: 'core_grades_get_grade_tree',\n args: {\n courseid: this.courseId,\n },\n };\n return Ajax.call([request])[0];\n }\n\n /**\n * Renders the bulk move modal body.\n *\n * @method renderModalBody\n * @returns {Promise} The modal body promise\n */\n async renderModalBody() {\n // We need to fetch the grade tree structure only once.\n if (this.gradeTree === null) {\n this.gradeTree = await this.fetchGradeTree();\n }\n\n return Templates.render('core_grades/bulkactions/edit/tree/bulk_move_grade_tree',\n JSON.parse(this.gradeTree));\n }\n\n /**\n * Show the bulk move modal.\n *\n * @method showModal\n * @returns {Promise} The modal promise\n */\n async showModal() {\n const modal = await ModalSaveCancel.create({\n title: await getString('movesitems', 'grades'),\n body: await this.renderModalBody(),\n buttons: {\n save: await getString('move')\n },\n large: true,\n });\n // Disable the 'Move' action button until something is selected.\n modal.setButtonDisabled('save', true);\n modal.show();\n\n return modal;\n }\n}\n"],"names":["Selectors","GradebookEditTreeBulkMove","BulkAction","constructor","courseId","getBulkActionTriggerSelector","modal","this","showModal","registerCustomListenerEvents","showInDropdown","index","Templates","render","showindropdown","isfirst","getBody","getRoot","on","ModalEvents","shown","moveOptionsTree","MoveOptionsTree","setButtonDisabled","hidden","destroy","save","selectedMoveOption","document","querySelector","value","dataset","id","submit","fetchGradeTree","request","methodname","args","courseid","Ajax","call","gradeTree","JSON","parse","ModalSaveCancel","create","title","body","renderModalBody","buttons","large","show"],"mappings":"i9BAgCMA,uBACY,iBADZA,wBAEa,yBAFbA,6BAGkB,gCAGHC,kCAAkCC,qBAiBnDC,YAAYC,kDAdD,6CAGO,uCAGN,WAUHA,SAAWA,SAQpBC,qCACW,iFAUDC,YAAcC,KAAKC,iBACpBC,6BAA6BH,qCAWRI,eAAgBC,cACnCC,mBAAUC,OAAO,sDAAuD,CAC3EC,eAAgBJ,eAChBK,QAAmB,IAAVJ,2CAWkBL,aACzBA,MAAMU,UAEZV,MAAMW,UAAUC,GAAGC,sBAAYC,OAAO,UAC7BC,gBAAkB,IAAIC,4BAAgB,KAEvChB,MAAMiB,kBAAkB,QAAQ,SAIxCjB,MAAMW,UAAUC,GAAGC,sBAAYK,QAAQ,KACnClB,MAAMmB,aAGVnB,MAAMW,UAAUC,GAAGC,sBAAYO,MAAM,KAE7BnB,KAAKc,iBAAmBd,KAAKc,gBAAgBM,qBAE7CC,SAASC,cAAc7B,yBAAyB8B,MAAQ,EACxDF,SAASC,cAAc7B,8BAA8B8B,MAAQvB,KAAKc,gBAAgBM,mBAAmBI,QAAQC,GAE7GJ,SAASC,cAAc7B,wBAAwBiC,aAW3DC,uBACUC,QAAU,CACZC,WAAY,6BACZC,KAAM,CACFC,SAAU/B,KAAKH,kBAGhBmC,cAAKC,KAAK,CAACL,UAAU,kCAWL,OAAnB5B,KAAKkC,iBACAA,gBAAkBlC,KAAK2B,kBAGzBtB,mBAAUC,OAAO,yDACpB6B,KAAKC,MAAMpC,KAAKkC,oCAUdnC,YAAcsC,2BAAgBC,OAAO,CACvCC,YAAa,mBAAU,aAAc,UACrCC,WAAYxC,KAAKyC,kBACjBC,QAAS,CACLvB,WAAY,mBAAU,SAE1BwB,OAAO,WAGX5C,MAAMiB,kBAAkB,QAAQ,GAChCjB,MAAM6C,OAEC7C"}PKr\T,kktree/bulk_actions.min.jsnu[define("core_grades/bulkactions/edit/tree/bulk_actions",["exports","core/bulkactions/bulk_actions","core_grades/bulkactions/edit/tree/move"],(function(_exports,_bulk_actions,_move){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_bulk_actions=_interopRequireDefault(_bulk_actions),_move=_interopRequireDefault(_move); /** * Class for defining the bulk actions area in the gradebook setup page. * * @module core_grades/bulkactions/edit/tree/bulk_actions * @copyright 2023 Mihail Geshoski * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ const Selectors_selectBulkItemCheckbox='input[type="checkbox"].itemselect';class GradebookEditTreeBulkActions extends _bulk_actions.default{static init(courseID){return new this(courseID)}constructor(courseID){var obj,key,value;super(),value=null,(key="courseID")in(obj=this)?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,this.courseID=courseID}getBulkActions(){return[new _move.default(this.courseID)]}getSelectedItems(){return document.querySelectorAll("".concat(Selectors_selectBulkItemCheckbox,":checked"))}registerItemSelectChangeEvent(eventHandler){document.querySelectorAll(Selectors_selectBulkItemCheckbox).forEach((checkbox=>{checkbox.addEventListener("change",eventHandler.bind(this))}))}deselectItem(selectedItem){selectedItem.checked=!1}}return _exports.default=GradebookEditTreeBulkActions,_exports.default})); //# sourceMappingURL=bulk_actions.min.js.mapPKr\ܠ# # tree/move.min.jsnu[define("core_grades/bulkactions/edit/tree/move",["exports","core/bulkactions/bulk_action","core/str","core/modal_save_cancel","core/templates","core/ajax","core/modal_events","core_grades/bulkactions/edit/tree/move_options_tree"],(function(_exports,_bulk_action,_str,_modal_save_cancel,_templates,_ajax,_modal_events,_move_options_tree){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_bulk_action=_interopRequireDefault(_bulk_action),_modal_save_cancel=_interopRequireDefault(_modal_save_cancel),_templates=_interopRequireDefault(_templates),_ajax=_interopRequireDefault(_ajax),_modal_events=_interopRequireDefault(_modal_events),_move_options_tree=_interopRequireDefault(_move_options_tree);const Selectors_editTreeForm="#gradetreeform",Selectors_bulkMoveInput='input[name="bulkmove"]',Selectors_bulkMoveAfterInput='input[name="moveafter"]';class GradebookEditTreeBulkMove extends _bulk_action.default{constructor(courseId){super(),_defineProperty(this,"courseId",null),_defineProperty(this,"moveOptionsTree",null),_defineProperty(this,"gradeTree",null),this.courseId=courseId}getBulkActionTriggerSelector(){return'[data-type="bulkactions"] [data-action="move"]'}async triggerBulkAction(){const modal=await this.showModal();this.registerCustomListenerEvents(modal)}async renderBulkActionTrigger(showInDropdown,index){return _templates.default.render("core_grades/bulkactions/edit/tree/bulk_move_trigger",{showindropdown:showInDropdown,isfirst:0===index})}async registerCustomListenerEvents(modal){await modal.getBody(),modal.getRoot().on(_modal_events.default.shown,(()=>{this.moveOptionsTree=new _move_options_tree.default((()=>{modal.setButtonDisabled("save",!1)}))})),modal.getRoot().on(_modal_events.default.hidden,(()=>{modal.destroy()})),modal.getRoot().on(_modal_events.default.save,(()=>{this.moveOptionsTree&&this.moveOptionsTree.selectedMoveOption&&(document.querySelector(Selectors_bulkMoveInput).value=1,document.querySelector(Selectors_bulkMoveAfterInput).value=this.moveOptionsTree.selectedMoveOption.dataset.id,document.querySelector(Selectors_editTreeForm).submit())}))}fetchGradeTree(){const request={methodname:"core_grades_get_grade_tree",args:{courseid:this.courseId}};return _ajax.default.call([request])[0]}async renderModalBody(){return null===this.gradeTree&&(this.gradeTree=await this.fetchGradeTree()),_templates.default.render("core_grades/bulkactions/edit/tree/bulk_move_grade_tree",JSON.parse(this.gradeTree))}async showModal(){const modal=await _modal_save_cancel.default.create({title:await(0,_str.get_string)("movesitems","grades"),body:await this.renderModalBody(),buttons:{save:await(0,_str.get_string)("move")},large:!0});return modal.setButtonDisabled("save",!0),modal.show(),modal}}return _exports.default=GradebookEditTreeBulkMove,_exports.default})); //# sourceMappingURL=move.min.js.mapPKr\UMtree/move_options_tree.min.jsnu[define("core_grades/bulkactions/edit/tree/move_options_tree",["exports","core/tree","core/normalise"],(function(_exports,_tree,_normalise){var obj;function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_tree=(obj=_tree)&&obj.__esModule?obj:{default:obj};const Selectors_moveOptionsTree='#destination-selector [role="tree"]',Selectors_moveOption='#destination-selector [role="treeitem"]',Selectors_toggleGroupLink="#destination-selector .collapse-list-link";class MoveOptionsTree extends _tree.default{constructor(afterSelectMoveOptionCallback){super(Selectors_moveOptionsTree),_defineProperty(this,"afterSelectMoveOptionCallback",null),_defineProperty(this,"selectedMoveOption",null),this.afterSelectMoveOptionCallback=afterSelectMoveOptionCallback}handleKeyDown(e){e.keyCode===this.keys.enter||e.keyCode===this.keys.space?this.selectMoveOption(e.target):super.handleKeyDown(e)}handleItemClick(event,item){event.target.closest(Selectors_toggleGroupLink)?super.handleItemClick(event,item):this.selectMoveOption((0,_normalise.getList)(item)[0])}selectMoveOption(moveOption){this.refreshVisibleItemsCache(),document.querySelectorAll(Selectors_moveOption).forEach((item=>{item.dataset.selected="false"})),moveOption.dataset.selected="true",this.selectedMoveOption=moveOption,moveOption.focus(),"function"==typeof this.afterSelectMoveOptionCallback&&this.afterSelectMoveOptionCallback()}}return _exports.default=MoveOptionsTree,_exports.default})); //# sourceMappingURL=move_options_tree.min.js.mapPK\scale/edit_form.phpnu[PK\m-q&& scale/edit.phpnu[PK\a  o.scale/index.phpnu[PK\FLLJsettings/form.phpnu[PK\QZv Egsettings/index.phpnu[PK\" osletter/edit_form.phpnu[PK\+letter/tabs.phpnu[PK\{8$&$&letter/index.phpnu[PK\Iͯ outcome/course_form.htmlnu[PK\DD D outcome/import_outcomes_form.phpnu[PK\G^outcome/edit_form.phpnu[PK\?D:outcome/export.phpnu[PK\>!!outcome/tabs.phpnu[PK\ ~3outcome/course.phpnu[PK\-ioutcome/edit.phpnu[PK\)%%voutcome/index.phpnu[PK\9$))Coutcome/import.phpnu[PK\FKD((ltree/grade.phpnu[PK\ .tree/action.phpnu[PK\ַªtree/calculation_form.phpnu[PK\::tree/category.phpnu[PK\iHWW  tree/lib.phpnu[PK\Wn--tree/outcomeitem_form.phpnu[PK\es"s"tree/grade_form.phpnu[PK\݆ tree/item.phpnu[PK\M=3!3!otree/calculation.phpnu[PK\KS44m4mtree/category_form.phpnu[PK\G(G(`tree/index.phpnu[PK\)ZZtree/item_form.phpnu[PK\NL## tree/outcomeitem.phpnu[PKg\?,0tree/move_options_tree.jsnu[PKg\Q @tree/bulk_actions.jsnu[PKg\ ULtree/move.jsnu[PKr\u-VVdtree/bulk_actions.min.js.mapnu[PKr\[L!#ttree/move_options_tree.min.js.mapnu[PKr\]^1\\\tree/move.min.js.mapnu[PKr\T,kktree/bulk_actions.min.jsnu[PKr\ܠ# # tree/move.min.jsnu[PKr\UMtree/move_options_tree.min.jsnu[PK''