diff --git a/course/lib.php b/course/lib.php index ba5d976e1a317..4f2a597485ff0 100644 --- a/course/lib.php +++ b/course/lib.php @@ -3892,3 +3892,559 @@ function get_sorted_course_formats($enabledonly = false) { function course_get_url($courseorid, $section = null, $options = array()) { return course_get_format($courseorid)->get_view_url($section, $options); } + + +/** + * Add course module + * The function does not check user capabilities. + * The function creates course module, module instance, add the module to the correct section. + * It also trigger common action that need to be done after adding/updating a module. + * + * @param object $moduleinfo + * @param object $course the course of the module + * @param object $mform - this is required by an hack to deal with files during MODULENAME_add_instance() - + * TODO: get rid of this hack. Core lib function should not deal with mform. + * @return object the updated module info + */ +function add_moduleinfo($moduleinfo, $course, $mform = null) { + global $DB, $CFG; + + $moduleinfo->course = $course->id; + $moduleinfo = set_moduleinfo_defaults($moduleinfo); + + if (!empty($course->groupmodeforce) or !isset($moduleinfo->groupmode)) { + $moduleinfo->groupmode = 0; // do not set groupmode + } + + if (!course_allowed_module($course, $moduleinfo->modulename)) { + print_error('moduledisable', '', '', $moduleinfo->modulename); + } + + // first add course_module record because we need the context + $newcm = new stdClass(); + $newcm->course = $course->id; + $newcm->module = $moduleinfo->module; + $newcm->instance = 0; // not known yet, will be updated later (this is similar to restore code) + $newcm->visible = $moduleinfo->visible; + $newcm->groupmode = $moduleinfo->groupmode; + $newcm->groupingid = $moduleinfo->groupingid; + $newcm->groupmembersonly = $moduleinfo->groupmembersonly; + $completion = new completion_info($course); + if ($completion->is_enabled()) { + $newcm->completion = $moduleinfo->completion; + $newcm->completiongradeitemnumber = $moduleinfo->completiongradeitemnumber; + $newcm->completionview = $moduleinfo->completionview; + $newcm->completionexpected = $moduleinfo->completionexpected; + } + if(!empty($CFG->enableavailability)) { + $newcm->availablefrom = $moduleinfo->availablefrom; + $newcm->availableuntil = $moduleinfo->availableuntil; + $newcm->showavailability = $moduleinfo->showavailability; + } + if (isset($moduleinfo->showdescription)) { + $newcm->showdescription = $moduleinfo->showdescription; + } else { + $newcm->showdescription = 0; + } + + if (!$moduleinfo->coursemodule = add_course_module($newcm)) { + print_error('cannotaddcoursemodule'); + } + + if (plugin_supports('mod', $moduleinfo->modulename, FEATURE_MOD_INTRO, true)) { + $introeditor = $moduleinfo->introeditor; + unset($moduleinfo->introeditor); + $moduleinfo->intro = $introeditor['text']; + $moduleinfo->introformat = $introeditor['format']; + } + + $addinstancefunction = $moduleinfo->modulename."_add_instance"; + $returnfromfunc = $addinstancefunction($moduleinfo, $mform); + + if (!$returnfromfunc or !is_number($returnfromfunc)) { + // undo everything we can + $modcontext = context_module::instance($moduleinfo->coursemodule); + delete_context(CONTEXT_MODULE, $moduleinfo->coursemodule); + $DB->delete_records('course_modules', array('id'=>$moduleinfo->coursemodule)); + + if (!is_number($returnfromfunc)) { + print_error('invalidfunction', '', course_get_url($course, $cw->section)); + } else { + print_error('cannotaddnewmodule', '', course_get_url($course, $cw->section), $moduleinfo->modulename); + } + } + + $moduleinfo->instance = $returnfromfunc; + + $DB->set_field('course_modules', 'instance', $returnfromfunc, array('id'=>$moduleinfo->coursemodule)); + + // update embedded links and save files + $modcontext = context_module::instance($moduleinfo->coursemodule); + if (!empty($introeditor)) { + $moduleinfo->intro = file_save_draft_area_files($introeditor['itemid'], $modcontext->id, + 'mod_'.$moduleinfo->modulename, 'intro', 0, + array('subdirs'=>true), $introeditor['text']); + $DB->set_field($moduleinfo->modulename, 'intro', $moduleinfo->intro, array('id'=>$moduleinfo->instance)); + } + + // course_modules and course_sections each contain a reference + // to each other, so we have to update one of them twice. + $sectionid = course_add_cm_to_section($course, $moduleinfo->coursemodule, $moduleinfo->section); + + // make sure visibility is set correctly (in particular in calendar) + // note: allow them to set it even without moodle/course:activityvisibility + set_coursemodule_visible($moduleinfo->coursemodule, $moduleinfo->visible); + + if (isset($moduleinfo->cmidnumber)) { //label + // set cm idnumber - uniqueness is already verified by form validation + set_coursemodule_idnumber($moduleinfo->coursemodule, $moduleinfo->cmidnumber); + } + + // Set up conditions + if ($CFG->enableavailability) { + condition_info::update_cm_from_form((object)array('id'=>$moduleinfo->coursemodule), $moduleinfo, false); + } + + $eventname = 'mod_created'; + + add_to_log($course->id, "course", "add mod", + "../mod/$moduleinfo->modulename/view.php?id=$moduleinfo->coursemodule", + "$moduleinfo->modulename $moduleinfo->instance"); + add_to_log($course->id, $moduleinfo->modulename, "add", + "view.php?id=$moduleinfo->coursemodule", + "$moduleinfo->instance", $moduleinfo->coursemodule); + + edit_module_post_actions($moduleinfo, $course, 'mod_created'); + + return $moduleinfo; +} + + +/** + * Common create/update module module actions that need to be processed as soon as a module is created/updaded. + * For example: trigger event, create grade parent category, add outcomes + * rebuild caches, regrade, save plagiarism settings... + * + * @param object $moduleinfo the module info + * @param object $course the course of the module + * @param string $eventname the event name to trigger + */ +function edit_module_post_actions($moduleinfo, $course, $eventname) { + global $USER, $CFG; + + $modcontext = context_module::instance($moduleinfo->coursemodule); + + // Trigger mod_created/mod_updated event with information about this module. + $eventdata = new stdClass(); + $eventdata->modulename = $moduleinfo->modulename; + $eventdata->name = $moduleinfo->name; + $eventdata->cmid = $moduleinfo->coursemodule; + $eventdata->courseid = $course->id; + $eventdata->userid = $USER->id; + events_trigger($eventname, $eventdata); + + // sync idnumber with grade_item + if ($grade_item = grade_item::fetch(array('itemtype'=>'mod', 'itemmodule'=>$moduleinfo->modulename, + 'iteminstance'=>$moduleinfo->instance, 'itemnumber'=>0, 'courseid'=>$course->id))) { + if ($grade_item->idnumber != $moduleinfo->cmidnumber) { + $grade_item->idnumber = $moduleinfo->cmidnumber; + $grade_item->update(); + } + } + + $items = grade_item::fetch_all(array('itemtype'=>'mod', 'itemmodule'=>$moduleinfo->modulename, + 'iteminstance'=>$moduleinfo->instance, 'courseid'=>$course->id)); + + // create parent category if requested and move to correct parent category + if ($items and isset($moduleinfo->gradecat)) { + if ($moduleinfo->gradecat == -1) { + $grade_category = new grade_category(); + $grade_category->courseid = $course->id; + $grade_category->fullname = $moduleinfo->name; + $grade_category->insert(); + if ($grade_item) { + $parent = $grade_item->get_parent_category(); + $grade_category->set_parent($parent->id); + } + $moduleinfo->gradecat = $grade_category->id; + } + foreach ($items as $itemid=>$unused) { + $items[$itemid]->set_parent($moduleinfo->gradecat); + if ($itemid == $grade_item->id) { + // use updated grade_item + $grade_item = $items[$itemid]; + } + } + } + + // add outcomes if requested + if ($outcomes = grade_outcome::fetch_all_available($course->id)) { + $grade_items = array(); + + // Outcome grade_item.itemnumber start at 1000, there is nothing above outcomes + $max_itemnumber = 999; + if ($items) { + foreach($items as $item) { + if ($item->itemnumber > $max_itemnumber) { + $max_itemnumber = $item->itemnumber; + } + } + } + + foreach($outcomes as $outcome) { + $elname = 'outcome_'.$outcome->id; + + if (property_exists($moduleinfo, $elname) and $moduleinfo->$elname) { + // so we have a request for new outcome grade item? + if ($items) { + foreach($items as $item) { + if ($item->outcomeid == $outcome->id) { + //outcome aready exists + continue 2; + } + } + } + + $max_itemnumber++; + + $outcome_item = new grade_item(); + $outcome_item->courseid = $course->id; + $outcome_item->itemtype = 'mod'; + $outcome_item->itemmodule = $moduleinfo->modulename; + $outcome_item->iteminstance = $moduleinfo->instance; + $outcome_item->itemnumber = $max_itemnumber; + $outcome_item->itemname = $outcome->fullname; + $outcome_item->outcomeid = $outcome->id; + $outcome_item->gradetype = GRADE_TYPE_SCALE; + $outcome_item->scaleid = $outcome->scaleid; + $outcome_item->insert(); + + // move the new outcome into correct category and fix sortorder if needed + if ($grade_item) { + $outcome_item->set_parent($grade_item->categoryid); + $outcome_item->move_after_sortorder($grade_item->sortorder); + + } else if (isset($moduleinfo->gradecat)) { + $outcome_item->set_parent($moduleinfo->gradecat); + } + } + } + } + + if (plugin_supports('mod', $moduleinfo->modulename, FEATURE_ADVANCED_GRADING, false) + and has_capability('moodle/grade:managegradingforms', $modcontext)) { + require_once($CFG->dirroot.'/grade/grading/lib.php'); + $gradingman = get_grading_manager($modcontext, 'mod_'.$moduleinfo->modulename); + $showgradingmanagement = false; + foreach ($gradingman->get_available_areas() as $areaname => $aretitle) { + $formfield = 'advancedgradingmethod_'.$areaname; + if (isset($moduleinfo->{$formfield})) { + $gradingman->set_area($areaname); + $methodchanged = $gradingman->set_active_method($moduleinfo->{$formfield}); + if (empty($moduleinfo->{$formfield})) { + // going back to the simple direct grading is not a reason + // to open the management screen + $methodchanged = false; + } + $showgradingmanagement = $showgradingmanagement || $methodchanged; + } + } + } + + rebuild_course_cache($course->id); + grade_regrade_final_grades($course->id); + require_once($CFG->libdir.'/plagiarismlib.php'); + plagiarism_save_form_elements($moduleinfo); //save plagiarism settings +} + + +/** + * Set module info default values for the unset module attributs. + * + * @param object $moduleinfo the current known data of the module + * @return object the completed module info + */ +function set_moduleinfo_defaults($moduleinfo) { + global $DB; + + if (empty($moduleinfo->coursemodule)) { + // Add + $cm = null; + $moduleinfo->instance = ''; + $moduleinfo->coursemodule = ''; + } else { + // Update + $cm = get_coursemodule_from_id('', $moduleinfo->coursemodule, 0, false, MUST_EXIST); + $moduleinfo->instance = $cm->instance; + $moduleinfo->coursemodule = $cm->id; + } + + $moduleinfo->modulename = clean_param($moduleinfo->modulename, PARAM_PLUGIN); // For safety + + if (!isset($moduleinfo->groupingid)) { + $moduleinfo->groupingid = 0; + } + + if (!isset($moduleinfo->groupmembersonly)) { + $moduleinfo->groupmembersonly = 0; + } + + if (!isset($moduleinfo->name)) { //label + $moduleinfo->name = $moduleinfo->modulename; + } + + if (!isset($moduleinfo->completion)) { + $moduleinfo->completion = COMPLETION_DISABLED; + } + if (!isset($moduleinfo->completionview)) { + $moduleinfo->completionview = COMPLETION_VIEW_NOT_REQUIRED; + } + + // Convert the 'use grade' checkbox into a grade-item number: 0 if + // checked, null if not + if (isset($moduleinfo->completionusegrade) && $moduleinfo->completionusegrade) { + $moduleinfo->completiongradeitemnumber = 0; + } else { + $moduleinfo->completiongradeitemnumber = null; + } + + return $moduleinfo; +} + +/** + * Check that the user can add a module. Also returns some information like the module, context and course section infoi. + * The fucntion create the course section if it doesn't exist. + * + * @param object $course the course of the module + * @param object $modulename the module name + * @param object $section the section of the module + * @return array list containing module, context, course section. + */ +function can_add_moduleinfo($course, $modulename, $section) { + global $DB; + + $module = $DB->get_record('modules', array('name'=>$modulename), '*', MUST_EXIST); + + $context = context_course::instance($course->id); + require_capability('moodle/course:manageactivities', $context); + + course_create_sections_if_missing($course, $section); + $cw = get_fast_modinfo($course)->get_section_info($section); + + if (!course_allowed_module($course, $module->name)) { + print_error('moduledisable'); + } + + return array($module, $context, $cw); +} + +/** + * Check if user is allowed to update module info and returns related item/data to the + * module. + * + * @param object $cm course module + * @return array - list of course module, context, module, moduleinfo, and course section. + */ +function can_update_moduleinfo($cm) { + global $DB; + + // Check the $USER has the right capability + $context = context_module::instance($cm->id); + require_capability('moodle/course:manageactivities', $context); + + // Check module exists. + $module = $DB->get_record('modules', array('id'=>$cm->module), '*', MUST_EXIST); + + // Check the moduleinfo exists. + $data = $DB->get_record($module->name, array('id'=>$cm->instance), '*', MUST_EXIST); + + // Check the course section exists. + $cw = $DB->get_record('course_sections', array('id'=>$cm->section), '*', MUST_EXIST); + + return array($cm, $context, $module, $data, $cw); +} + + +/** + * Update the module info. + * This function doesn't check the user capabilities. It updates the course module and the module instance. + * Then execute common action to create/update module process (trigger event, rebuild cache, save plagiarism settings...). + * + * @param object $cm course module + * @param object $moduleinfo module info + * @param object $course course of the module + * @param object $mform - the mform is required by some specific module in the function MODULE_update_instance(). This is due to a hack in this function. + * TODO: remove the $mform from MODULE_update_instance() + * @return array list of course module and module info. + */ +function update_moduleinfo($cm, $moduleinfo, $course, $mform = null) { + global $DB, $CFG; + + $moduleinfo->course = $course->id; + $moduleinfo = set_moduleinfo_defaults($moduleinfo); + + if (!empty($course->groupmodeforce) or !isset($moduleinfo->groupmode)) { + $moduleinfo->groupmode = $cm->groupmode; // keep original + } + + // update course module first + $cm->groupmode = $moduleinfo->groupmode; + if (isset($moduleinfo->groupingid)) { + $cm->groupingid = $moduleinfo->groupingid; + } + if (isset($moduleinfo->groupmembersonly)) { + $cm->groupmembersonly = $moduleinfo->groupmembersonly; + } + + $completion = new completion_info($course); + if ($completion->is_enabled()) { + // Update completion settings + $cm->completion = $moduleinfo->completion; + $cm->completiongradeitemnumber = $moduleinfo->completiongradeitemnumber; + $cm->completionview = $moduleinfo->completionview; + $cm->completionexpected = $moduleinfo->completionexpected; + } + if (!empty($CFG->enableavailability)) { + $cm->availablefrom = $moduleinfo->availablefrom; + $cm->availableuntil = $moduleinfo->availableuntil; + $cm->showavailability = $moduleinfo->showavailability; + condition_info::update_cm_from_form($cm,$moduleinfo,true); + } + if (isset($moduleinfo->showdescription)) { + $cm->showdescription = $moduleinfo->showdescription; + } else { + $cm->showdescription = 0; + } + + $DB->update_record('course_modules', $cm); + + $modcontext = context_module::instance($moduleinfo->coursemodule); + + // update embedded links and save files + if (plugin_supports('mod', $moduleinfo->modulename, FEATURE_MOD_INTRO, true)) { + $moduleinfo->intro = file_save_draft_area_files($moduleinfo->introeditor['itemid'], $modcontext->id, + 'mod_'.$moduleinfo->modulename, 'intro', 0, + array('subdirs'=>true), $moduleinfo->introeditor['text']); + $moduleinfo->introformat = $moduleinfo->introeditor['format']; + unset($moduleinfo->introeditor); + } + $updateinstancefunction = $moduleinfo->modulename."_update_instance"; + if (!$updateinstancefunction($moduleinfo, $mform)) { + print_error('cannotupdatemod', '', course_get_url($course, $cw->section), $moduleinfo->modulename); + } + + // make sure visibility is set correctly (in particular in calendar) + if (has_capability('moodle/course:activityvisibility', $modcontext)) { + set_coursemodule_visible($moduleinfo->coursemodule, $moduleinfo->visible); + } + + if (isset($moduleinfo->cmidnumber)) { //label + // set cm idnumber - uniqueness is already verified by form validation + set_coursemodule_idnumber($moduleinfo->coursemodule, $moduleinfo->cmidnumber); + } + + // Now that module is fully updated, also update completion data if + // required (this will wipe all user completion data and recalculate it) + if ($completion->is_enabled() && !empty($moduleinfo->completionunlocked)) { + $completion->reset_all_state($cm); + } + + add_to_log($course->id, "course", "update mod", + "../mod/$moduleinfo->modulename/view.php?id=$moduleinfo->coursemodule", + "$moduleinfo->modulename $moduleinfo->instance"); + add_to_log($course->id, $moduleinfo->modulename, "update", + "view.php?id=$moduleinfo->coursemodule", + "$moduleinfo->instance", $moduleinfo->coursemodule); + + edit_module_post_actions($moduleinfo, $course, 'mod_updated'); + + return array($cm, $moduleinfo); +} + +/** + * Include once the module lib file. + * + * @param string $modulename module name of the lib to include + */ +function include_modulelib($modulename) { + global $CFG; + $modlib = "$CFG->dirroot/mod/$modulename/lib.php"; + if (file_exists($modlib)) { + include_once($modlib); + } else { + throw new moodle_exception('modulemissingcode', '', '', $modlib); + } +} + +/** + * Create a module. + * It includes: + * - capability checks and other checks + * - create the module from the module info + * + * @param object $module + * @return object the created module info + */ +function create_module($moduleinfo) { + global $DB; + // Check manadatory attributs. + $mandatoryfields = array('modulename', 'course', 'section', 'visible'); + if (plugin_supports('mod', $moduleinfo->modulename, FEATURE_MOD_INTRO, true)) { + $mandatoryfields[] = 'introeditor'; + } + foreach($mandatoryfields as $mandatoryfield) { + if (!isset($moduleinfo->{$mandatoryfield})) { + throw new moodle_exception('createmodulemissingattribut', '', '', $mandatoryfield); + } + } + + // Some additional checks (capability / existing instances) + $course = $DB->get_record('course', array('id'=>$moduleinfo->course), '*', MUST_EXIST); + list($module, $context, $cw) = can_add_moduleinfo($course, $moduleinfo->modulename, $moduleinfo->section); + + // Load module library. + include_modulelib($module->name); + + // Add the module. + $moduleinfo->module = $module->id; + $moduleinfo = add_moduleinfo($moduleinfo, $course, null); + + return $moduleinfo; +} + +/** + * Update a module. + * It includes: + * - capability and other checks) + * - update the module + * + * @param object $module + * @return object the updated module info + */ +function update_module($moduleinfo) { + global $DB; + + // Check the course module exists. + $cm = get_coursemodule_from_id('', $moduleinfo->coursemodule, 0, false, MUST_EXIST); + + // Check the course exists. + $course = $DB->get_record('course', array('id'=>$cm->course), '*', MUST_EXIST); + + // Some checks (capaibility / existing instances) + list($cm, $context, $module, $data, $cw) = can_update_moduleinfo($cm); + + // Load module library + include_modulelib($module->name); + + // Retrieve few information needed by update_moduleinfo + $moduleinfo->modulename = $cm->modname; + if (!isset($moduleinfo->scale)) { + $moduleinfo->scale = 0; + } + $moduleinfo->type = 'mod'; + + // Update the module + list($cm, $moduleinfo) = update_moduleinfo($cm, $moduleinfo, $course, null); + + return $moduleinfo; +} diff --git a/course/modedit.php b/course/modedit.php index c08108abe4aaa..3018b43128432 100644 --- a/course/modedit.php +++ b/course/modedit.php @@ -53,18 +53,9 @@ $PAGE->set_url($url); $course = $DB->get_record('course', array('id'=>$course), '*', MUST_EXIST); - $module = $DB->get_record('modules', array('name'=>$add), '*', MUST_EXIST); - require_login($course); - $context = context_course::instance($course->id); - require_capability('moodle/course:manageactivities', $context); - - course_create_sections_if_missing($course, $section); - $cw = get_fast_modinfo($course)->get_section_info($section); - if (!course_allowed_module($course, $module->name)) { - print_error('moduledisable'); - } + list($module, $context, $cw) = can_add_moduleinfo($course, $add, $section); $cm = null; @@ -128,16 +119,16 @@ $url->param('update', $update); $PAGE->set_url($url); + // Check the course module exists. $cm = get_coursemodule_from_id('', $update, 0, false, MUST_EXIST); + + // Check the course exists. $course = $DB->get_record('course', array('id'=>$cm->course), '*', MUST_EXIST); + // require_login require_login($course, false, $cm); // needed to setup proper $COURSE - $context = context_module::instance($cm->id); - require_capability('moodle/course:manageactivities', $context); - $module = $DB->get_record('modules', array('id'=>$cm->module), '*', MUST_EXIST); - $data = $data = $DB->get_record($module->name, array('id'=>$cm->instance), '*', MUST_EXIST); - $cw = $DB->get_record('course_sections', array('id'=>$cm->section), '*', MUST_EXIST); + list($cm, $context, $module, $data, $cw) = can_update_moduleinfo($cm); $data->coursemodule = $cm->id; $data->section = $cw->section; // The section number itself - relative!!! (section column in course_sections) @@ -250,12 +241,7 @@ print_error('noformdesc'); } -$modlib = "$CFG->dirroot/mod/$module->name/lib.php"; -if (file_exists($modlib)) { - include_once($modlib); -} else { - print_error('modulemissingcode', '', '', $modlib); -} +include_modulelib($module->name); $mformclassname = 'mod_'.$module->name.'_mod_form'; $mform = new $mformclassname($data, $cw->section, $cm, $course); @@ -268,363 +254,15 @@ redirect(course_get_url($course, $cw->section, array('sr' => $sectionreturn))); } } else if ($fromform = $mform->get_data()) { - if (empty($fromform->coursemodule)) { - // Add - $cm = null; - $course = $DB->get_record('course', array('id'=>$fromform->course), '*', MUST_EXIST); - $fromform->instance = ''; - $fromform->coursemodule = ''; - } else { - // Update - $cm = get_coursemodule_from_id('', $fromform->coursemodule, 0, false, MUST_EXIST); - $course = $DB->get_record('course', array('id'=>$cm->course), '*', MUST_EXIST); - $fromform->instance = $cm->instance; - $fromform->coursemodule = $cm->id; - } - - if (!empty($fromform->coursemodule)) { - $context = context_module::instance($fromform->coursemodule); - } else { - $context = context_course::instance($course->id); - } - - $fromform->course = $course->id; - $fromform->modulename = clean_param($fromform->modulename, PARAM_PLUGIN); // For safety - - $addinstancefunction = $fromform->modulename."_add_instance"; - $updateinstancefunction = $fromform->modulename."_update_instance"; - - if (!isset($fromform->groupingid)) { - $fromform->groupingid = 0; - } - - if (!isset($fromform->groupmembersonly)) { - $fromform->groupmembersonly = 0; - } - - if (!isset($fromform->name)) { //label - $fromform->name = $fromform->modulename; - } - - if (!isset($fromform->completion)) { - $fromform->completion = COMPLETION_DISABLED; - } - if (!isset($fromform->completionview)) { - $fromform->completionview = COMPLETION_VIEW_NOT_REQUIRED; - } - - // Convert the 'use grade' checkbox into a grade-item number: 0 if - // checked, null if not - if (isset($fromform->completionusegrade) && $fromform->completionusegrade) { - $fromform->completiongradeitemnumber = 0; - } else { - $fromform->completiongradeitemnumber = null; - } - - // the type of event to trigger (mod_created/mod_updated) - $eventname = ''; if (!empty($fromform->update)) { - - if (!empty($course->groupmodeforce) or !isset($fromform->groupmode)) { - $fromform->groupmode = $cm->groupmode; // keep original - } - - // update course module first - $cm->groupmode = $fromform->groupmode; - $cm->groupingid = $fromform->groupingid; - $cm->groupmembersonly = $fromform->groupmembersonly; - - $completion = new completion_info($course); - if ($completion->is_enabled()) { - // Update completion settings - $cm->completion = $fromform->completion; - $cm->completiongradeitemnumber = $fromform->completiongradeitemnumber; - $cm->completionview = $fromform->completionview; - $cm->completionexpected = $fromform->completionexpected; - } - if (!empty($CFG->enableavailability)) { - $cm->availablefrom = $fromform->availablefrom; - $cm->availableuntil = $fromform->availableuntil; - $cm->showavailability = $fromform->showavailability; - condition_info::update_cm_from_form($cm,$fromform,true); - } - if (isset($fromform->showdescription)) { - $cm->showdescription = $fromform->showdescription; - } else { - $cm->showdescription = 0; - } - - $DB->update_record('course_modules', $cm); - - $modcontext = context_module::instance($fromform->coursemodule); - - // update embedded links and save files - if (plugin_supports('mod', $fromform->modulename, FEATURE_MOD_INTRO, true)) { - $fromform->intro = file_save_draft_area_files($fromform->introeditor['itemid'], $modcontext->id, - 'mod_'.$fromform->modulename, 'intro', 0, - array('subdirs'=>true), $fromform->introeditor['text']); - $fromform->introformat = $fromform->introeditor['format']; - unset($fromform->introeditor); - } - - if (!$updateinstancefunction($fromform, $mform)) { - print_error('cannotupdatemod', '', course_get_url($course, $cw->section), $fromform->modulename); - } - - // make sure visibility is set correctly (in particular in calendar) - if (has_capability('moodle/course:activityvisibility', $modcontext)) { - set_coursemodule_visible($fromform->coursemodule, $fromform->visible); - } - - if (isset($fromform->cmidnumber)) { //label - // set cm idnumber - uniqueness is already verified by form validation - set_coursemodule_idnumber($fromform->coursemodule, $fromform->cmidnumber); - } - - // Now that module is fully updated, also update completion data if - // required (this will wipe all user completion data and recalculate it) - if ($completion->is_enabled() && !empty($fromform->completionunlocked)) { - $completion->reset_all_state($cm); - } - - $eventname = 'mod_updated'; - - add_to_log($course->id, "course", "update mod", - "../mod/$fromform->modulename/view.php?id=$fromform->coursemodule", - "$fromform->modulename $fromform->instance"); - add_to_log($course->id, $fromform->modulename, "update", - "view.php?id=$fromform->coursemodule", - "$fromform->instance", $fromform->coursemodule); - + list($cm, $fromform) = update_moduleinfo($cm, $fromform, $course, $mform); } else if (!empty($fromform->add)) { - - if (!empty($course->groupmodeforce) or !isset($fromform->groupmode)) { - $fromform->groupmode = 0; // do not set groupmode - } - - if (!course_allowed_module($course, $fromform->modulename)) { - print_error('moduledisable', '', '', $fromform->modulename); - } - - // first add course_module record because we need the context - $newcm = new stdClass(); - $newcm->course = $course->id; - $newcm->module = $fromform->module; - $newcm->instance = 0; // not known yet, will be updated later (this is similar to restore code) - $newcm->visible = $fromform->visible; - $newcm->groupmode = $fromform->groupmode; - $newcm->groupingid = $fromform->groupingid; - $newcm->groupmembersonly = $fromform->groupmembersonly; - $completion = new completion_info($course); - if ($completion->is_enabled()) { - $newcm->completion = $fromform->completion; - $newcm->completiongradeitemnumber = $fromform->completiongradeitemnumber; - $newcm->completionview = $fromform->completionview; - $newcm->completionexpected = $fromform->completionexpected; - } - if(!empty($CFG->enableavailability)) { - $newcm->availablefrom = $fromform->availablefrom; - $newcm->availableuntil = $fromform->availableuntil; - $newcm->showavailability = $fromform->showavailability; - } - if (isset($fromform->showdescription)) { - $newcm->showdescription = $fromform->showdescription; - } else { - $newcm->showdescription = 0; - } - - if (!$fromform->coursemodule = add_course_module($newcm)) { - print_error('cannotaddcoursemodule'); - } - - if (plugin_supports('mod', $fromform->modulename, FEATURE_MOD_INTRO, true)) { - $introeditor = $fromform->introeditor; - unset($fromform->introeditor); - $fromform->intro = $introeditor['text']; - $fromform->introformat = $introeditor['format']; - } - - $returnfromfunc = $addinstancefunction($fromform, $mform); - - if (!$returnfromfunc or !is_number($returnfromfunc)) { - // undo everything we can - $modcontext = context_module::instance($fromform->coursemodule); - delete_context(CONTEXT_MODULE, $fromform->coursemodule); - $DB->delete_records('course_modules', array('id'=>$fromform->coursemodule)); - - if (!is_number($returnfromfunc)) { - print_error('invalidfunction', '', course_get_url($course, $cw->section)); - } else { - print_error('cannotaddnewmodule', '', course_get_url($course, $cw->section), $fromform->modulename); - } - } - - $fromform->instance = $returnfromfunc; - - $DB->set_field('course_modules', 'instance', $returnfromfunc, array('id'=>$fromform->coursemodule)); - - // update embedded links and save files - $modcontext = context_module::instance($fromform->coursemodule); - if (!empty($introeditor)) { - $fromform->intro = file_save_draft_area_files($introeditor['itemid'], $modcontext->id, - 'mod_'.$fromform->modulename, 'intro', 0, - array('subdirs'=>true), $introeditor['text']); - $DB->set_field($fromform->modulename, 'intro', $fromform->intro, array('id'=>$fromform->instance)); - } - - // course_modules and course_sections each contain a reference - // to each other, so we have to update one of them twice. - $sectionid = course_add_cm_to_section($course, $fromform->coursemodule, $fromform->section); - - // make sure visibility is set correctly (in particular in calendar) - // note: allow them to set it even without moodle/course:activityvisibility - set_coursemodule_visible($fromform->coursemodule, $fromform->visible); - $DB->set_field('course_modules', 'visibleold', 1, array('id' => $fromform->coursemodule)); - - if (isset($fromform->cmidnumber)) { //label - // set cm idnumber - uniqueness is already verified by form validation - set_coursemodule_idnumber($fromform->coursemodule, $fromform->cmidnumber); - } - - // Set up conditions - if ($CFG->enableavailability) { - condition_info::update_cm_from_form((object)array('id'=>$fromform->coursemodule), $fromform, false); - } - - $eventname = 'mod_created'; - - add_to_log($course->id, "course", "add mod", - "../mod/$fromform->modulename/view.php?id=$fromform->coursemodule", - "$fromform->modulename $fromform->instance"); - add_to_log($course->id, $fromform->modulename, "add", - "view.php?id=$fromform->coursemodule", - "$fromform->instance", $fromform->coursemodule); + $fromform = add_moduleinfo($fromform, $course, $mform); } else { print_error('invaliddata'); } - // Trigger mod_created/mod_updated event with information about this module. - $eventdata = new stdClass(); - $eventdata->modulename = $fromform->modulename; - $eventdata->name = $fromform->name; - $eventdata->cmid = $fromform->coursemodule; - $eventdata->courseid = $course->id; - $eventdata->userid = $USER->id; - events_trigger($eventname, $eventdata); - - // sync idnumber with grade_item - if ($grade_item = grade_item::fetch(array('itemtype'=>'mod', 'itemmodule'=>$fromform->modulename, - 'iteminstance'=>$fromform->instance, 'itemnumber'=>0, 'courseid'=>$course->id))) { - if ($grade_item->idnumber != $fromform->cmidnumber) { - $grade_item->idnumber = $fromform->cmidnumber; - $grade_item->update(); - } - } - - $items = grade_item::fetch_all(array('itemtype'=>'mod', 'itemmodule'=>$fromform->modulename, - 'iteminstance'=>$fromform->instance, 'courseid'=>$course->id)); - - // create parent category if requested and move to correct parent category - if ($items and isset($fromform->gradecat)) { - if ($fromform->gradecat == -1) { - $grade_category = new grade_category(); - $grade_category->courseid = $course->id; - $grade_category->fullname = $fromform->name; - $grade_category->insert(); - if ($grade_item) { - $parent = $grade_item->get_parent_category(); - $grade_category->set_parent($parent->id); - } - $fromform->gradecat = $grade_category->id; - } - foreach ($items as $itemid=>$unused) { - $items[$itemid]->set_parent($fromform->gradecat); - if ($itemid == $grade_item->id) { - // use updated grade_item - $grade_item = $items[$itemid]; - } - } - } - - // add outcomes if requested - if ($outcomes = grade_outcome::fetch_all_available($course->id)) { - $grade_items = array(); - - // Outcome grade_item.itemnumber start at 1000, there is nothing above outcomes - $max_itemnumber = 999; - if ($items) { - foreach($items as $item) { - if ($item->itemnumber > $max_itemnumber) { - $max_itemnumber = $item->itemnumber; - } - } - } - - foreach($outcomes as $outcome) { - $elname = 'outcome_'.$outcome->id; - - if (property_exists($fromform, $elname) and $fromform->$elname) { - // so we have a request for new outcome grade item? - if ($items) { - foreach($items as $item) { - if ($item->outcomeid == $outcome->id) { - //outcome aready exists - continue 2; - } - } - } - - $max_itemnumber++; - - $outcome_item = new grade_item(); - $outcome_item->courseid = $course->id; - $outcome_item->itemtype = 'mod'; - $outcome_item->itemmodule = $fromform->modulename; - $outcome_item->iteminstance = $fromform->instance; - $outcome_item->itemnumber = $max_itemnumber; - $outcome_item->itemname = $outcome->fullname; - $outcome_item->outcomeid = $outcome->id; - $outcome_item->gradetype = GRADE_TYPE_SCALE; - $outcome_item->scaleid = $outcome->scaleid; - $outcome_item->insert(); - - // move the new outcome into correct category and fix sortorder if needed - if ($grade_item) { - $outcome_item->set_parent($grade_item->categoryid); - $outcome_item->move_after_sortorder($grade_item->sortorder); - - } else if (isset($fromform->gradecat)) { - $outcome_item->set_parent($fromform->gradecat); - } - } - } - } - - if (plugin_supports('mod', $fromform->modulename, FEATURE_ADVANCED_GRADING, false) - and has_capability('moodle/grade:managegradingforms', $modcontext)) { - require_once($CFG->dirroot.'/grade/grading/lib.php'); - $gradingman = get_grading_manager($modcontext, 'mod_'.$fromform->modulename); - $showgradingmanagement = false; - foreach ($gradingman->get_available_areas() as $areaname => $aretitle) { - $formfield = 'advancedgradingmethod_'.$areaname; - if (isset($fromform->{$formfield})) { - $gradingman->set_area($areaname); - $methodchanged = $gradingman->set_active_method($fromform->{$formfield}); - if (empty($fromform->{$formfield})) { - // going back to the simple direct grading is not a reason - // to open the management screen - $methodchanged = false; - } - $showgradingmanagement = $showgradingmanagement || $methodchanged; - } - } - } - - rebuild_course_cache($course->id); - grade_regrade_final_grades($course->id); - plagiarism_save_form_elements($fromform); //save plagiarism settings - if (isset($fromform->submitbutton)) { if (empty($showgradingmanagement)) { redirect("$CFG->wwwroot/mod/$module->name/view.php?id=$fromform->coursemodule"); diff --git a/course/tests/courselib_test.php b/course/tests/courselib_test.php index 0e6275e3ffcc2..587ea623da5e7 100644 --- a/course/tests/courselib_test.php +++ b/course/tests/courselib_test.php @@ -30,6 +30,236 @@ class courselib_testcase extends advanced_testcase { + /** + * Test create_module() + */ + public function test_create_module() { + global $DB, $CFG; + + $this->resetAfterTest(true); + + $this->setAdminUser(); + + require_once($CFG->dirroot.'/mod/forum/lib.php'); + + $course = $this->getDataGenerator()->create_course(array('numsections'=>1), + array('createsections'=>true)); + + $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id)); + + // FORUM MODULE + + $moduleinfo = new stdClass(); + + // Always mandatory generic values to any module + $moduleinfo->modulename = 'forum'; + $moduleinfo->section= 1; + $moduleinfo->course= $course->id; + $moduleinfo->groupingid= $grouping->id; + $moduleinfo->groupmembersonly= ''; + $moduleinfo->visible= true; + + // Sometimes optional generic values for some modules + $moduleinfo->name= 'My test forum'; + $moduleinfo->completion= ''; + $moduleinfo->completionview= ''; + $moduleinfo->completiongradeitemonly= ''; + $moduleinfo->completionexpected= false; + $moduleinfo->availablefrom= 0; + $moduleinfo->availableuntil= 0; + $moduleinfo->showavailability= false; + $moduleinfo->showdescription= true; + $moduleinfo->gradecat= ''; + $moduleinfo->groupmod = VISIBLEGROUPS; + $moduleinfo->cmidnumber= 'idnumber_XXX'; + + // Optional intro editor (depends of module) + $draftid_editor = 0; + file_prepare_draft_area($draftid_editor, null, null, null, null); + $moduleinfo->introeditor = array('text'=>'This is a forum', 'format'=>FORMAT_HTML, 'itemid'=>$draftid_editor); + + // Specific values to the Forum module + $moduleinfo->forcesubscribe = FORUM_INITIALSUBSCRIBE; + $moduleinfo->type = 'single'; + + // create the module + $forum = create_module($moduleinfo); + + // Retriev the module + + // Compare the module values + + // error_log(print_r($forum, true)); + + // ASSIGN MODULE + + $moduleinfo = new stdClass(); + + // Always mandatory generic values to any module + $moduleinfo->modulename = 'assign'; + $moduleinfo->section= 1; + $moduleinfo->course= $course->id; + $moduleinfo->groupingid= $grouping->id; + $moduleinfo->groupmembersonly= ''; + $moduleinfo->visible= true; + + // Sometimes optional generic values for some modules + $moduleinfo->name= 'My test assignment'; + $moduleinfo->completion= ''; + $moduleinfo->completionview= ''; + $moduleinfo->completiongradeitemonly= ''; + $moduleinfo->completionexpected= false; + $moduleinfo->availablefrom= 0; + $moduleinfo->availableuntil= 0; + $moduleinfo->showavailability= false; + $moduleinfo->showdescription= true; + $moduleinfo->gradecat= 1; + $moduleinfo->groupmod = VISIBLEGROUPS; + $moduleinfo->cmidnumber= 'idnumber_XXXY'; + + // Optional intro editor (depends of module) + // TODO: way to support file - see Damyon assignment solution + $draftid_editor = 0; + file_prepare_draft_area($draftid_editor, null, null, null, null); + $moduleinfo->introeditor = array('text'=>'This is a assignmenet', 'format'=>FORMAT_HTML, 'itemid'=>$draftid_editor); + + // Specific values to the Assign module + $moduleinfo->alwaysshowdescription = true; + $moduleinfo->submissiondrafts = true; + $moduleinfo->requiresubmissionstatement = true; + $moduleinfo->sendnotifications = true; + $moduleinfo->sendlatenotifications = true; + $moduleinfo->duedate = time()+7*24*3600; + $moduleinfo->cutoffdate = time()+7*24*3600; + $moduleinfo->allowsubmissionsfromdate = ''; + $moduleinfo->teamsubmission = true; + $moduleinfo->requireallteammemberssubmit = true; + $moduleinfo->teamsubmissiongroupingid = true; + $moduleinfo->blindmarking = true; + $moduleinfo->assignsubmission_onlinetext_enabled = true; + $moduleinfo->assignsubmission_file_enabled = true; + $moduleinfo->assignsubmission_file_maxfiles = 1; + $moduleinfo->assignsubmission_file_maxsizebytes = 1000000; + $moduleinfo->assignsubmission_comments_enabled = true; + $moduleinfo->assignfeedback_comments_enabled = true; + $moduleinfo->assignfeedback_offline_enabled = true; + $moduleinfo->assignfeedback_file_enabled = true; + + // Following is the advanced grading method area called 'submissions' for the 'assign' module : + $moduleinfo->grade = 100; + $moduleinfo->advancedgradingmethod_submissions = ''; + + // Plagiarism form values + // No plagiarism plugin installed by default. Use this space to make your own test. + + // create the module + $assign = create_module($moduleinfo); + + // Retrieve the module + + // Compare the module values + + // error_log(print_r($assign, true)); + + } + + /** + * Test update_module() + */ + public function test_update_module() { + global $DB, $CFG; + + $this->resetAfterTest(true); + + $this->setAdminUser(); + + require_once($CFG->dirroot.'/mod/forum/lib.php'); + + $course = $this->getDataGenerator()->create_course(array('numsections'=>2), + array('createsections'=>true)); + + $grouping = $this->getDataGenerator()->create_grouping(array('courseid' => $course->id)); + + + // UPDATE ASSIGN MODULE + $assigninfo = array('course' => $course->id); + $sectionnumber = 1; + $assign = $this->getDataGenerator()->create_module('assign', $assigninfo, array('section' => $sectionnumber)); + + // Retrieve course modulei + $cm = get_coursemodule_from_instance('assign', $assign->id); + + $moduleinfo = new stdClass(); + + // Always mandatory generic values to any module + $moduleinfo->coursemodule = $cm->id; + $moduleinfo->modulename = 'assign'; + $moduleinfo->section= 1; + $moduleinfo->course= $course->id; + $moduleinfo->groupingid= $grouping->id; + $moduleinfo->groupmembersonly= ''; + $moduleinfo->visible= true; + + // Sometimes optional generic values for some modules + $moduleinfo->name= 'My test assignment'; + $moduleinfo->completion= ''; + $moduleinfo->completionview= ''; + $moduleinfo->completiongradeitemonly= ''; + $moduleinfo->completionexpected= false; + $moduleinfo->availablefrom= 0; + $moduleinfo->availableuntil= 0; + $moduleinfo->showavailability= false; + $moduleinfo->showdescription= true; + $moduleinfo->gradecat= 1; + $moduleinfo->groupmod = VISIBLEGROUPS; + $moduleinfo->cmidnumber= 'idnumber_XXXY'; + + // Optional intro editor (depends of module) + // TODO: way to support file - see Damyon assignment solution + $draftid_editor = 0; + file_prepare_draft_area($draftid_editor, null, null, null, null); + $moduleinfo->introeditor = array('text'=>'This is a assignmenet', 'format'=>FORMAT_HTML, 'itemid'=>$draftid_editor); + + // Specific values to the Assign module + $moduleinfo->alwaysshowdescription = true; + $moduleinfo->submissiondrafts = true; + $moduleinfo->requiresubmissionstatement = true; + $moduleinfo->sendnotifications = true; + $moduleinfo->sendlatenotifications = true; + $moduleinfo->duedate = time()+7*24*3600; + $moduleinfo->cutoffdate = time()+7*24*3600; + $moduleinfo->allowsubmissionsfromdate = ''; + $moduleinfo->teamsubmission = true; + $moduleinfo->requireallteammemberssubmit = true; + $moduleinfo->teamsubmissiongroupingid = true; + $moduleinfo->blindmarking = true; + $moduleinfo->assignsubmission_onlinetext_enabled = true; + $moduleinfo->assignsubmission_file_enabled = true; + $moduleinfo->assignsubmission_file_maxfiles = 1; + $moduleinfo->assignsubmission_file_maxsizebytes = 1000000; + $moduleinfo->assignsubmission_comments_enabled = true; + $moduleinfo->assignfeedback_comments_enabled = true; + $moduleinfo->assignfeedback_offline_enabled = true; + $moduleinfo->assignfeedback_file_enabled = true; + + // Following is the advanced grading method area called 'submissions' for the 'assign' module : + $moduleinfo->grade = 100; + $moduleinfo->advancedgradingmethod_submissions = ''; + + // Plagiarism form values + // No plagiarism plugin installed by default. Use this space to make your own test. + + // update the module + $assign = update_module($moduleinfo); + + // Retrieve the module + + // Compare the module values + + // error_log(print_r($assign, true)); + } + + public function test_create_course() { global $DB; $this->resetAfterTest(true); diff --git a/mod/assign/lib.php b/mod/assign/lib.php index aad2a587d73f1..c57fe3bcaad20 100644 --- a/mod/assign/lib.php +++ b/mod/assign/lib.php @@ -131,10 +131,10 @@ function assign_reset_course_form_defaults($course) { * * This is done by calling the update_instance() method of the assignment type class * @param stdClass $data - * @param mod_assign_mod_form $form + * @param $form * @return object */ -function assign_update_instance(stdClass $data, mod_assign_mod_form $form) { +function assign_update_instance(stdClass $data, $form) { global $CFG; require_once($CFG->dirroot . '/mod/assign/locallib.php'); $context = context_module::instance($data->coursemodule); diff --git a/plagiarism/lib.php b/plagiarism/lib.php index b6179e983034c..b41d785e75436 100644 --- a/plagiarism/lib.php +++ b/plagiarism/lib.php @@ -31,6 +31,16 @@ //dummy class - all plugins should be based off this. class plagiarism_plugin { + + /** + * Return the list of form element names. + * + * @return array contains the form element names. + */ + public function get_configs() { + return array(); + } + /** * hook to allow plagiarism specific information to be displayed beside a submission * @param array $linkarraycontains all relevant information for the plugin to generate a link