public function get_description($full, $not, \core_availability\info $info) { // Get name for module. $modinfo = $info->get_modinfo(); if (!isset($this->expectedpublicprivate)) { $modname = get_string('missing', 'availability_publicprivate'); } // // Work out which lang string to use. // if ($not) { // // Convert NOT strings to use the equivalent where possible. // switch ($this->expectedpublicprivate) { // case COMPLETION_INCOMPLETE: // $str = 'requires_' . self::get_lang_string_keyword(COMPLETION_COMPLETE); // break; // case COMPLETION_COMPLETE: // $str = 'requires_' . self::get_lang_string_keyword(COMPLETION_INCOMPLETE); // break; // default: // // The other two cases do not have direct opposites. // $str = 'requires_not_' . self::get_lang_string_keyword($this->expectedpublicprivate); // break; // } // } else { // $str = 'requires_' . self::get_lang_string_keyword($this->expectedpublicprivate); // } if ($this->expectedpublicprivate === PUBLICPRIVATE_PRIVATE) { $str = 'requires_private'; } else { $str = 'requires_public'; } return get_string($str, 'availability_publicprivate'); }
/** * Determines whether a particular item is currently available * according to this availability condition. * * @param bool $not Set true if we are inverting the condition. * @param info $info Item we're checking. * @param bool $grabthelot Performance hint: if true, caches information * required for all course-modules, to make the front page and similar * pages work more quickly (works only for current user). * @param int $userid User ID to check availability for. * @return bool True if available. */ public function is_available($not, \core_availability\info $info, $grabthelot, $userid) { $available = false; if ($this->requiredlvl > 0) { $currentlvl = $this->get_user_level($info->get_course()->id, $userid); if ($currentlvl >= $this->requiredlvl) { $available = true; } else { $available = false; } if ($not) { $available = !$available; } } return $available; }
public function get_description($full, $not, \core_availability\info $info) { $course = $info->get_course(); // String depends on type of requirement. We are coy about // the actual numbers, in case grades aren't released to // students. if (is_null($this->min) && is_null($this->max)) { $string = 'any'; } else { if (is_null($this->max)) { $string = 'min'; } else { if (is_null($this->min)) { $string = 'max'; } else { $string = 'range'; } } } if ($not) { // The specific strings don't make as much sense with 'not'. if ($string === 'any') { $string = 'notany'; } else { $string = 'notgeneral'; } } $name = self::get_cached_grade_name($course->id, $this->gradeitemid); return get_string('requires_' . $string, 'availability_grade', $name); }
public function update_after_restore($restoreid, $courseid, \base_logger $logger, $name) { // Update the date, if restoring with changed date. $dateoffset = \core_availability\info::get_restore_date_offset($restoreid); if ($dateoffset) { $this->time += $dateoffset; return true; } return false; }
/** * Tests the is_available and get_description functions. */ public function test_usage() { global $CFG, $DB; require_once $CFG->dirroot . '/mod/assign/locallib.php'; $this->resetAfterTest(); // Create course with completion turned on. $CFG->enablecompletion = true; $CFG->enableavailability = true; $generator = $this->getDataGenerator(); $course = $generator->create_course(array('enablecompletion' => 1)); $user = $generator->create_user(); $generator->enrol_user($user->id, $course->id); $this->setUser($user); // Create a Page with manual completion for basic checks. $page = $generator->get_plugin_generator('mod_page')->create_instance(array('course' => $course->id, 'name' => 'Page!', 'completion' => COMPLETION_TRACKING_MANUAL)); // Create an assignment - we need to have something that can be graded // so as to test the PASS/FAIL states. Set it up to be completed based // on its grade item. $assignrow = $this->getDataGenerator()->create_module('assign', array('course' => $course->id, 'name' => 'Assign!', 'completion' => COMPLETION_TRACKING_AUTOMATIC)); $DB->set_field('course_modules', 'completiongradeitemnumber', 0, array('id' => $assignrow->cmid)); $assign = new assign(context_module::instance($assignrow->cmid), false, false); // Get basic details. $modinfo = get_fast_modinfo($course); $pagecm = $modinfo->get_cm($page->cmid); $assigncm = $assign->get_course_module(); $info = new \core_availability\mock_info($course, $user->id); // COMPLETE state (false), positive and NOT. $cond = new condition((object) array('cm' => (int) $pagecm->id, 'e' => COMPLETION_COMPLETE)); $this->assertFalse($cond->is_available(false, $info, true, $user->id)); $information = $cond->get_description(false, false, $info); $information = \core_availability\info::format_info($information, $course); $this->assertRegExp('~Page!.*is marked complete~', $information); $this->assertTrue($cond->is_available(true, $info, true, $user->id)); // INCOMPLETE state (true). $cond = new condition((object) array('cm' => (int) $pagecm->id, 'e' => COMPLETION_INCOMPLETE)); $this->assertTrue($cond->is_available(false, $info, true, $user->id)); $this->assertFalse($cond->is_available(true, $info, true, $user->id)); $information = $cond->get_description(false, true, $info); $information = \core_availability\info::format_info($information, $course); $this->assertRegExp('~Page!.*is marked complete~', $information); // Mark page complete. $completion = new completion_info($course); $completion->update_state($pagecm, COMPLETION_COMPLETE); // COMPLETE state (true). $cond = new condition((object) array('cm' => (int) $pagecm->id, 'e' => COMPLETION_COMPLETE)); $this->assertTrue($cond->is_available(false, $info, true, $user->id)); $this->assertFalse($cond->is_available(true, $info, true, $user->id)); $information = $cond->get_description(false, true, $info); $information = \core_availability\info::format_info($information, $course); $this->assertRegExp('~Page!.*is incomplete~', $information); // INCOMPLETE state (false). $cond = new condition((object) array('cm' => (int) $pagecm->id, 'e' => COMPLETION_INCOMPLETE)); $this->assertFalse($cond->is_available(false, $info, true, $user->id)); $information = $cond->get_description(false, false, $info); $information = \core_availability\info::format_info($information, $course); $this->assertRegExp('~Page!.*is incomplete~', $information); $this->assertTrue($cond->is_available(true, $info, true, $user->id)); // We are going to need the grade item so that we can get pass/fails. $gradeitem = $assign->get_grade_item(); grade_object::set_properties($gradeitem, array('gradepass' => 50.0)); $gradeitem->update(); // With no grade, it should return true for INCOMPLETE and false for // the other three. $cond = new condition((object) array('cm' => (int) $assigncm->id, 'e' => COMPLETION_INCOMPLETE)); $this->assertTrue($cond->is_available(false, $info, true, $user->id)); $this->assertFalse($cond->is_available(true, $info, true, $user->id)); $cond = new condition((object) array('cm' => (int) $assigncm->id, 'e' => COMPLETION_COMPLETE)); $this->assertFalse($cond->is_available(false, $info, true, $user->id)); $this->assertTrue($cond->is_available(true, $info, true, $user->id)); // Check $information for COMPLETE_PASS and _FAIL as we haven't yet. $cond = new condition((object) array('cm' => (int) $assigncm->id, 'e' => COMPLETION_COMPLETE_PASS)); $this->assertFalse($cond->is_available(false, $info, true, $user->id)); $information = $cond->get_description(false, false, $info); $information = \core_availability\info::format_info($information, $course); $this->assertRegExp('~Assign!.*is complete and passed~', $information); $this->assertTrue($cond->is_available(true, $info, true, $user->id)); $cond = new condition((object) array('cm' => (int) $assigncm->id, 'e' => COMPLETION_COMPLETE_FAIL)); $this->assertFalse($cond->is_available(false, $info, true, $user->id)); $information = $cond->get_description(false, false, $info); $information = \core_availability\info::format_info($information, $course); $this->assertRegExp('~Assign!.*is complete and failed~', $information); $this->assertTrue($cond->is_available(true, $info, true, $user->id)); // Change the grade to be complete and failed. self::set_grade($assignrow, $user->id, 40); $cond = new condition((object) array('cm' => (int) $assigncm->id, 'e' => COMPLETION_INCOMPLETE)); $this->assertFalse($cond->is_available(false, $info, true, $user->id)); $this->assertTrue($cond->is_available(true, $info, true, $user->id)); $cond = new condition((object) array('cm' => (int) $assigncm->id, 'e' => COMPLETION_COMPLETE)); $this->assertTrue($cond->is_available(false, $info, true, $user->id)); $this->assertFalse($cond->is_available(true, $info, true, $user->id)); $cond = new condition((object) array('cm' => (int) $assigncm->id, 'e' => COMPLETION_COMPLETE_PASS)); $this->assertFalse($cond->is_available(false, $info, true, $user->id)); $information = $cond->get_description(false, false, $info); $information = \core_availability\info::format_info($information, $course); $this->assertRegExp('~Assign!.*is complete and passed~', $information); $this->assertTrue($cond->is_available(true, $info, true, $user->id)); $cond = new condition((object) array('cm' => (int) $assigncm->id, 'e' => COMPLETION_COMPLETE_FAIL)); $this->assertTrue($cond->is_available(false, $info, true, $user->id)); $this->assertFalse($cond->is_available(true, $info, true, $user->id)); $information = $cond->get_description(false, true, $info); $information = \core_availability\info::format_info($information, $course); $this->assertRegExp('~Assign!.*is not complete and failed~', $information); // Now change it to pass. self::set_grade($assignrow, $user->id, 60); $cond = new condition((object) array('cm' => (int) $assigncm->id, 'e' => COMPLETION_INCOMPLETE)); $this->assertFalse($cond->is_available(false, $info, true, $user->id)); $this->assertTrue($cond->is_available(true, $info, true, $user->id)); $cond = new condition((object) array('cm' => (int) $assigncm->id, 'e' => COMPLETION_COMPLETE)); $this->assertTrue($cond->is_available(false, $info, true, $user->id)); $this->assertFalse($cond->is_available(true, $info, true, $user->id)); $cond = new condition((object) array('cm' => (int) $assigncm->id, 'e' => COMPLETION_COMPLETE_PASS)); $this->assertTrue($cond->is_available(false, $info, true, $user->id)); $this->assertFalse($cond->is_available(true, $info, true, $user->id)); $information = $cond->get_description(false, true, $info); $information = \core_availability\info::format_info($information, $course); $this->assertRegExp('~Assign!.*is not complete and passed~', $information); $cond = new condition((object) array('cm' => (int) $assigncm->id, 'e' => COMPLETION_COMPLETE_FAIL)); $this->assertFalse($cond->is_available(false, $info, true, $user->id)); $information = $cond->get_description(false, false, $info); $information = \core_availability\info::format_info($information, $course); $this->assertRegExp('~Assign!.*is complete and failed~', $information); $this->assertTrue($cond->is_available(true, $info, true, $user->id)); // Simulate deletion of an activity by using an invalid cmid. These // conditions always fail, regardless of NOT flag or INCOMPLETE. $cond = new condition((object) array('cm' => $assigncm->id + 100, 'e' => COMPLETION_COMPLETE)); $this->assertFalse($cond->is_available(false, $info, true, $user->id)); $information = $cond->get_description(false, false, $info); $information = \core_availability\info::format_info($information, $course); $this->assertRegExp('~(Missing activity).*is marked complete~', $information); $this->assertFalse($cond->is_available(true, $info, true, $user->id)); $cond = new condition((object) array('cm' => $assigncm->id + 100, 'e' => COMPLETION_INCOMPLETE)); $this->assertFalse($cond->is_available(false, $info, true, $user->id)); }
/** * Obtains a string describing this restriction (whether or not * it actually applies). * * @param bool $full Set true if this is the 'full information' view * @param bool $not Set true if we are inverting the condition * @param info $info Item we're checking * @return string Information string (for admin) about all restrictions on * this item */ public function get_description($full, $not, \core_availability\info $info) { global $USER; $logmanager = get_log_manager(); if (!($readers = $logmanager->get_readers('core\\log\\sql_reader'))) { // Should be using 2.8, use old class. $readers = $logmanager->get_readers('core\\log\\sql_select_reader'); } $reader = array_pop($readers); $context = $info->get_context(); $viewscount = $reader->get_events_select_count('contextid = :context AND userid = :userid AND crud = :crud', array('context' => $context->id, 'userid' => $USER->id, 'crud' => 'r')); $a = new \stdclass(); $a->viewslimit = $this->viewslimit; $a->viewscount = $viewscount; if ($not) { return get_string('eithernotdescription', 'availability_maxviews', $a); } else { return get_string('eitherdescription', 'availability_maxviews', $a); } }
/** * Process the legacy availability fields table record. This table does not * exist in Moodle 2.7+ but we still support restore. * * @param stdClass $data Record data */ protected function process_availability_field($data) { global $DB; $data = (object) $data; // Mark it is as passed by default $passed = true; $customfieldid = null; // If a customfield has been used in order to pass we must be able to match an existing // customfield by name (data->customfield) and type (data->customfieldtype) if (!empty($data->customfield) xor !empty($data->customfieldtype)) { // xor is sort of uncommon. If either customfield is null or customfieldtype is null BUT not both. // If one is null but the other isn't something clearly went wrong and we'll skip this condition. $passed = false; } else { if (!empty($data->customfield)) { $params = array('shortname' => $data->customfield, 'datatype' => $data->customfieldtype); $customfieldid = $DB->get_field('user_info_field', 'id', $params); $passed = $customfieldid !== false; } } if ($passed) { // Create the object to insert into the database $availfield = new stdClass(); $availfield->coursemoduleid = $this->task->get_moduleid(); // Lets add the availability cmid $availfield->userfield = $data->userfield; $availfield->customfieldid = $customfieldid; $availfield->operator = $data->operator; $availfield->value = $data->value; // Get showavailability option. $showrec = restore_dbops::get_backup_ids_record($this->get_restoreid(), 'module_showavailability', $availfield->coursemoduleid); if (!$showrec) { // Should not happen. throw new coding_exception('No matching showavailability record'); } $show = $showrec->info->showavailability; // The $availfieldobject is now in the format used in the old // system. Interpret this and convert to new system. $currentvalue = $DB->get_field('course_modules', 'availability', array('id' => $availfield->coursemoduleid), MUST_EXIST); $newvalue = \core_availability\info::add_legacy_availability_field_condition($currentvalue, $availfield, $show); $DB->set_field('course_modules', 'availability', $newvalue, array('id' => $availfield->coursemoduleid)); } }
public function get_description($full, $not, \core_availability\info $info) { // Get name for module. $modinfo = $info->get_modinfo(); if (!array_key_exists($this->cmid, $modinfo->cms)) { $modname = get_string('missing', 'availability_completion'); } else { $modname = '<AVAILABILITY_CMNAME_' . $modinfo->cms[$this->cmid]->id . '/>'; } // Work out which lang string to use. if ($not) { // Convert NOT strings to use the equivalent where possible. switch ($this->expectedcompletion) { case COMPLETION_INCOMPLETE: $str = 'requires_' . self::get_lang_string_keyword(COMPLETION_COMPLETE); break; case COMPLETION_COMPLETE: $str = 'requires_' . self::get_lang_string_keyword(COMPLETION_INCOMPLETE); break; default: // The other two cases do not have direct opposites. $str = 'requires_not_' . self::get_lang_string_keyword($this->expectedcompletion); break; } } else { $str = 'requires_' . self::get_lang_string_keyword($this->expectedcompletion); } return get_string($str, 'availability_completion', $modname); }
/** * Renders HTML to show course module availability information (for someone who isn't allowed * to see the activity itself, or for staff) * * @param cm_info $mod * @param array $displayoptions * @return string */ public function course_section_cm_availability(cm_info $mod, $displayoptions = array()) { global $CFG; if (!$mod->uservisible) { // this is a student who is not allowed to see the module but might be allowed // to see availability info (i.e. "Available from ...") if (!empty($mod->availableinfo)) { $formattedinfo = \core_availability\info::format_info($mod->availableinfo, $mod->get_course()); $output = html_writer::tag('div', $formattedinfo, array('class' => 'availabilityinfo')); } return $output; } // this is a teacher who is allowed to see module but still should see the // information that module is not available to all/some students $modcontext = context_module::instance($mod->id); $canviewhidden = has_capability('moodle/course:viewhiddenactivities', $modcontext); if ($canviewhidden && !empty($CFG->enableavailability)) { // Don't add availability information if user is not editing and activity is hidden. if ($mod->visible || $this->page->user_is_editing()) { $hidinfoclass = ''; if (!$mod->visible) { $hidinfoclass = 'hide'; } $ci = new \core_availability\info_module($mod); $fullinfo = $ci->get_full_information(); if ($fullinfo) { $formattedinfo = \core_availability\info::format_info($fullinfo, $mod->get_course()); return html_writer::div($formattedinfo, 'availabilityinfo ' . $hidinfoclass); } } } return ''; }
public function filter_user_list(array $users, $not, \core_availability\info $info, \core_availability\capability_checker $checker) { global $CFG, $DB; require_once $CFG->libdir . '/grouplib.php'; $course = $info->get_course(); // List users for this course who match the condition. if ($this->groupid) { $groupusers = groups_get_members($this->groupid, 'u.id', 'u.id ASC'); } else { $groupusers = $DB->get_records_sql("\n SELECT DISTINCT gm.userid\n FROM {groups} g\n JOIN {groups_members} gm ON gm.groupid = g.id\n WHERE g.courseid = ?", array($course->id)); } // List users who have access all groups. $aagusers = $checker->get_users_by_capability('moodle/site:accessallgroups'); // Filter the user list. $result = array(); foreach ($users as $id => $user) { // Always include users with access all groups. if (array_key_exists($id, $aagusers)) { $result[$id] = $user; continue; } // Other users are included or not based on group membership. $allow = array_key_exists($id, $groupusers); if ($not) { $allow = !$allow; } if ($allow) { $result[$id] = $user; } } return $result; }
public function get_description($full, $not, \core_availability\info $info) { global $DB; $context = \context_course::instance($info->get_course()->id); $role = $DB->get_record('role', array('id' => $this->roleid)); if (!$role) { $missing = get_string('missing', 'availability_role'); return get_string($not ? 'requires_notrole' : 'requires_role', 'availability_role', $missing); } else { $name = role_get_name($role, $context); return get_string($not ? 'requires_notrole' : 'requires_role', 'availability_role', $name); } }
public function __construct($course = null) { global $SITE; if (!$course) { $course = $SITE; } parent::__construct($course, true, null); }
/** * Renders HTML to show course module availability information * * @param cm_info $mod * @param array $displayoptions * @return string */ public function course_section_cm_availability(cm_info $mod, $displayoptions = array()) { // If we have available info, always spit it out. if (!empty($mod->availableinfo)) { $availinfo = $mod->availableinfo; } else { $ci = new \core_availability\info_module($mod); $availinfo = $ci->get_full_information(); } if ($availinfo) { $formattedinfo = \core_availability\info::format_info($availinfo, $mod->get_course()); return html_writer::div($formattedinfo, 'availabilityinfo'); } return ''; }
public function get_description($full, $not, \core_availability\info $info) { $course = $info->get_course(); // Display the fieldname into current lang. if ($this->customfield) { // Is a custom profile field (will use multilang). $customfields = self::get_custom_profile_fields(); if (array_key_exists($this->customfield, $customfields)) { $translatedfieldname = $customfields[$this->customfield]->name; } else { $translatedfieldname = get_string('missing', 'availability_profile', $this->customfield); } } else { $translatedfieldname = get_user_field_name($this->standardfield); } $context = \context_course::instance($course->id); $a = new \stdClass(); $a->field = format_string($translatedfieldname, true, array('context' => $context)); $a->value = s($this->value); if ($not) { // When doing NOT strings, we replace the operator with its inverse. // Some of them don't have inverses, so for those we use a new // identifier which is only used for this lang string. switch ($this->operator) { case self::OP_CONTAINS: $opname = self::OP_DOES_NOT_CONTAIN; break; case self::OP_DOES_NOT_CONTAIN: $opname = self::OP_CONTAINS; break; case self::OP_ENDS_WITH: $opname = 'notendswith'; break; case self::OP_IS_EMPTY: $opname = self::OP_IS_NOT_EMPTY; break; case self::OP_IS_EQUAL_TO: $opname = 'notisequalto'; break; case self::OP_IS_NOT_EMPTY: $opname = self::OP_IS_EMPTY; break; case self::OP_STARTS_WITH: $opname = 'notstartswith'; break; default: throw new \coding_exception('Unexpected operator: ' . $this->operator); } } else { $opname = $this->operator; } return get_string('requires_' . $opname, 'availability_profile', $a); }
protected function process_module($data) { global $CFG, $DB; $data = (object) $data; $oldid = $data->id; $this->task->set_old_moduleversion($data->version); // Get the current course module data. $newitemid = $this->task->get_moduleid(); $params = array('id' => $newitemid); $cmdata = $DB->get_record('course_modules', $params, '*', MUST_EXIST); // Group mode and Grouping. $cmdata->groupmode = $data->groupmode; $cmdata->groupingid = $this->get_mappingid('grouping', $data->groupingid); // Idnumber uniqueness. if (!grade_verify_idnumber($data->idnumber, $this->get_courseid())) { $data->idnumber = ''; } $cmdata->idnumber = $data->idnumber; // Completion. if (!empty($CFG->enablecompletion)) { $cmdata->completion = $data->completion; $cmdata->completiongradeitemnumber = $data->completiongradeitemnumber; $cmdata->completionview = $data->completionview; $cmdata->completionexpected = $this->apply_date_offset($data->completionexpected); } // Availability. if (empty($CFG->enableavailability)) { $data->availability = null; } if (empty($data->availability)) { // If there are legacy availablility data fields (and no new format data), // convert the old fields. $data->availability = \core_availability\info::convert_legacy_fields($data, false); } else { if (!empty($data->groupmembersonly)) { // There is current availability data, but it still has groupmembersonly // as well (2.7 backups), convert just that part. require_once $CFG->dirroot . '/lib/db/upgradelib.php'; $data->availability = upgrade_group_members_only($data->groupingid, $data->availability); } } $cmdata->availability = $data->availability; // Backups that did not include showdescription, set it to default 0 // (this is not totally necessary as it has a db default, but just to // be explicit). if (!isset($data->showdescription)) { $data->showdescription = 0; } $cmdata->showdescription = $data->showdescription; // Course_module record ready, update it. $DB->update_record('course_modules', $cmdata); // Save mapping. $this->set_mapping('course_module', $oldid, $newitemid); // Set the new course_module id in the task. $this->task->set_moduleid($newitemid); // We can now create the context safely. $ctxid = context_module::instance($newitemid)->id; // Set the new context id in the task. $this->task->set_contextid($ctxid); // If there is the legacy showavailability data, store this for later use. // (This data is not present when restoring 'new' backups.) if (isset($cmdata->showavailability)) { // Cache the showavailability flag using the backup_ids data field. restore_dbops::set_backup_ids_record($this->get_restoreid(), 'module_showavailability', $newitemid, 0, null, (object) array('showavailability' => $cmdata->showavailability)); } }
/** * If section is not visible, display the message about that ('Not available * until...', that sort of thing). Otherwise, returns blank. * * For users with the ability to view hidden sections, it shows the * information even though you can view the section and also may include * slightly fuller information (so that teachers can tell when sections * are going to be unavailable etc). This logic is the same as for * activities. * * @param stdClass $section The course_section entry from DB * @param bool $canviewhidden True if user can view hidden sections * @return string HTML to output */ protected function section_availability_message($section, $canviewhidden) { global $CFG; $o = ''; if (!$section->uservisible) { // Note: We only get to this function if availableinfo is non-empty, // so there is definitely something to print. $formattedinfo = \core_availability\info::format_info($section->availableinfo, $section->course); $o .= html_writer::div($formattedinfo, 'availabilityinfo'); } else { if ($canviewhidden && !empty($CFG->enableavailability) && $section->visible) { $ci = new \core_availability\info_section($section); $fullinfo = $ci->get_full_information(); if ($fullinfo) { $formattedinfo = \core_availability\info::format_info($fullinfo, $section->course); $o .= html_writer::div($formattedinfo, 'availabilityinfo'); } } } return $o; }
public function get_user_list_sql($not, \core_availability\info $info, $onlyactive) { global $DB; // Build suitable SQL depending on custom or standard field. if ($this->customfield) { $customfields = self::get_custom_profile_fields(); if (!array_key_exists($this->customfield, $customfields)) { // If the field isn't found, nobody matches. return array('SELECT id FROM {user} WHERE 0 = 1', array()); } $customfield = $customfields[$this->customfield]; $mainparams = array(); $tablesql = "LEFT JOIN {user_info_data} uid ON uid.fieldid = " . self::unique_sql_parameter($mainparams, $customfield->id) . " AND uid.userid = userids.id"; list($condition, $conditionparams) = $this->get_condition_sql('uid.data'); $mainparams = array_merge($mainparams, $conditionparams); // If default is true, then allow that too. if ($this->is_field_condition_met($this->operator, $customfield->defaultdata, $this->value)) { $where = "((uid.data IS NOT NULL AND {$condition}) OR (uid.data IS NULL))"; } else { $where = "(uid.data IS NOT NULL AND {$condition})"; } } else { $tablesql = "JOIN {user} u ON u.id = userids.id"; list($where, $mainparams) = $this->get_condition_sql('u.' . $this->standardfield); } // Handle NOT. if ($not) { $where = 'NOT (' . $where . ')'; } // Get enrolled user SQL and combine with this query. list($enrolsql, $enrolparams) = get_enrolled_sql($info->get_context(), '', 0, $onlyactive); $sql = "SELECT userids.id\n FROM ({$enrolsql}) userids\n {$tablesql}\n WHERE {$where}"; $params = array_merge($enrolparams, $mainparams); return array($sql, $params); }
public function get_user_list_sql($not, \core_availability\info $info, $onlyactive) { global $DB; // Get enrolled users with access all groups. These always are allowed. list($aagsql, $aagparams) = get_enrolled_sql($info->get_context(), 'moodle/site:accessallgroups', 0, $onlyactive); // Get all enrolled users. list($enrolsql, $enrolparams) = get_enrolled_sql($info->get_context(), '', 0, $onlyactive); // Condition for specified or any group. $matchparams = array(); $matchsql = "SELECT 1\n FROM {groups_members} gm\n JOIN {groupings_groups} gg ON gg.groupid = gm.groupid\n WHERE gm.userid = userids.id\n AND gg.groupingid = " . self::unique_sql_parameter($matchparams, $this->get_grouping_id($info)); // Overall query combines all this. $condition = $not ? 'NOT' : ''; $sql = "SELECT userids.id\n FROM ({$enrolsql}) userids\n WHERE (userids.id IN ({$aagsql})) OR {$condition} EXISTS ({$matchsql})"; return array($sql, array_merge($enrolparams, $aagparams, $matchparams)); }
/** * Obtains SQL that returns a list of enrolled users that has been filtered * by the conditions applied in the availability API, similar to calling * get_enrolled_users and then filter_user_list. As for filter_user_list, * this ONLY filteres out users with conditions that are marked as applying * to user lists. For example, group conditions are included but date * conditions are not included. * * The returned SQL is a query that returns a list of user IDs. It does not * include brackets, so you neeed to add these to make it into a subquery. * You would normally use it in an SQL phrase like "WHERE u.id IN ($sql)". * * The SQL will be complex and may be slow. It uses named parameters (sorry, * I know they are annoying, but it was unavoidable here). * * If there are no conditions, the returned result is array('', array()). * * @param bool $not True if this condition is applying in negative mode * @param \core_availability\info $info Item we're checking * @param bool $onlyactive If true, only returns active enrolments * @return array Array with two elements: SQL subquery and parameters array * @throws \coding_exception If called on a condition that doesn't apply to user lists */ public function get_user_list_sql($not, \core_availability\info $info, $onlyactive) { if (!$this->is_applied_to_user_lists()) { throw new \coding_exception('Not implemented (do not call unless ' . 'is_applied_to_user_lists is true)'); } // Handle situation where plugin does not implement this, by returning a // default (all enrolled users). This ensures compatibility with 2.7 // plugins and behaviour. Plugins should be updated to support this // new function (if they return true to is_applied_to_user_lists). debugging('Availability plugins that return true to is_applied_to_user_lists ' . 'should also now implement get_user_list_sql: ' . get_class($this), DEBUG_DEVELOPER); return get_enrolled_sql($info->get_context(), '', 0, $onlyactive); }
public function get_description($full, $not, \core_availability\info $info) { global $DB; $course = $info->get_course(); // Need to get the name for the grouping. Unfortunately this requires // a database query. To save queries, get all groupings for course at // once in a static cache. $groupingid = $this->get_grouping_id($info); if (!array_key_exists($groupingid, self::$groupingnames)) { $coursegroupings = $DB->get_records('groupings', array('courseid' => $course->id), '', 'id, name'); foreach ($coursegroupings as $rec) { self::$groupingnames[$rec->id] = $rec->name; } } // If it still doesn't exist, it must have been misplaced. if (!array_key_exists($groupingid, self::$groupingnames)) { $name = get_string('missing', 'availability_grouping'); } else { $context = \context_course::instance($course->id); $name = format_string(self::$groupingnames[$groupingid], true, array('context' => $context)); } return get_string($not ? 'requires_notgrouping' : 'requires_grouping', 'availability_grouping', $name); }
/** * This function converts all of the base settings for an instance of * the old setaskment to the new format. Then it calls each of the plugins * to see if they can help upgrade this setaskment. * @param int $oldsetaskmentid (don't rely on the old setaskment type even being installed) * @param string $log This string gets appended to during the conversion process * @return bool true or false */ public function upgrade_setaskment($oldsetaskmentid, &$log) { global $DB, $CFG, $USER; // Steps to upgrade an setaskment. core_php_time_limit::raise(ASSIGN_MAX_UPGRADE_TIME_SECS); // Get the module details. $oldmodule = $DB->get_record('modules', array('name' => 'setaskment'), '*', MUST_EXIST); $params = array('module' => $oldmodule->id, 'instance' => $oldsetaskmentid); $oldcoursemodule = $DB->get_record('course_modules', $params, '*', MUST_EXIST); $oldcontext = context_module::instance($oldcoursemodule->id); // We used to check for admin capability, but since Moodle 2.7 this is called // during restore of a mod_setaskment module. // Also note that we do not check for any mod_setaskment capabilities, because they can // be removed so that users don't add new instances of the broken old thing. if (!has_capability('mod/setask:addinstance', $oldcontext)) { $log = get_string('couldnotcreatenewsetaskmentinstance', 'mod_setask'); return false; } // First insert an setask instance to get the id. $oldsetaskment = $DB->get_record('setaskment', array('id' => $oldsetaskmentid), '*', MUST_EXIST); $oldversion = get_config('setaskment_' . $oldsetaskment->setaskmenttype, 'version'); $data = new stdClass(); $data->course = $oldsetaskment->course; $data->name = $oldsetaskment->name; $data->intro = $oldsetaskment->intro; $data->introformat = $oldsetaskment->introformat; $data->alwaysshowdescription = 1; $data->sendnotifications = $oldsetaskment->emailteachers; $data->sendlatenotifications = $oldsetaskment->emailteachers; $data->duedate = $oldsetaskment->timedue; $data->allowsubmissionsfromdate = $oldsetaskment->timeavailable; $data->grade = $oldsetaskment->grade; $data->submissiondrafts = $oldsetaskment->resubmit; $data->requiresubmissionstatement = 0; $data->markingworkflow = 0; $data->markingallocation = 0; $data->cutoffdate = 0; // New way to specify no late submissions. if ($oldsetaskment->preventlate) { $data->cutoffdate = $data->duedate; } $data->teamsubmission = 0; $data->requireallteammemberssubmit = 0; $data->teamsubmissiongroupingid = 0; $data->blindmarking = 0; $data->attemptreopenmethod = 'none'; $data->maxattempts = ASSIGN_UNLIMITED_ATTEMPTS; $newsetaskment = new setask(null, null, null); if (!$newsetaskment->add_instance($data, false)) { $log = get_string('couldnotcreatenewsetaskmentinstance', 'mod_setask'); return false; } // Now create a new coursemodule from the old one. $newmodule = $DB->get_record('modules', array('name' => 'setask'), '*', MUST_EXIST); $newcoursemodule = $this->duplicate_course_module($oldcoursemodule, $newmodule->id, $newsetaskment->get_instance()->id); if (!$newcoursemodule) { $log = get_string('couldnotcreatenewcoursemodule', 'mod_setask'); return false; } // Convert the base database tables (setaskment, submission, grade). // These are used to store information in case a rollback is required. $gradingarea = null; $gradingdefinitions = null; $gradeidmap = array(); $completiondone = false; $gradesdone = false; // From this point we want to rollback on failure. $rollback = false; try { $newsetaskment->set_context(context_module::instance($newcoursemodule->id)); // The course module has now been created - time to update the core tables. // Copy intro files. $newsetaskment->copy_area_files_for_upgrade($oldcontext->id, 'mod_setaskment', 'intro', 0, $newsetaskment->get_context()->id, 'mod_setask', 'intro', 0); // Get the plugins to do their bit. foreach ($newsetaskment->get_submission_plugins() as $plugin) { if ($plugin->can_upgrade($oldsetaskment->setaskmenttype, $oldversion)) { $plugin->enable(); if (!$plugin->upgrade_settings($oldcontext, $oldsetaskment, $log)) { $rollback = true; } } else { $plugin->disable(); } } foreach ($newsetaskment->get_feedback_plugins() as $plugin) { if ($plugin->can_upgrade($oldsetaskment->setaskmenttype, $oldversion)) { $plugin->enable(); if (!$plugin->upgrade_settings($oldcontext, $oldsetaskment, $log)) { $rollback = true; } } else { $plugin->disable(); } } // See if there is advanced grading upgrades required. $gradingarea = $DB->get_record('grading_areas', array('contextid' => $oldcontext->id, 'areaname' => 'submission'), '*', IGNORE_MISSING); if ($gradingarea) { $params = array('id' => $gradingarea->id, 'contextid' => $newsetaskment->get_context()->id, 'component' => 'mod_setask', 'areaname' => 'submissions'); $DB->update_record('grading_areas', $params); $gradingdefinitions = $DB->get_records('grading_definitions', array('areaid' => $gradingarea->id)); } // Upgrade availability data. \core_availability\info::update_dependency_id_across_course($newcoursemodule->course, 'course_modules', $oldcoursemodule->id, $newcoursemodule->id); // Upgrade completion data. $DB->set_field('course_modules_completion', 'coursemoduleid', $newcoursemodule->id, array('coursemoduleid' => $oldcoursemodule->id)); $allcriteria = $DB->get_records('course_completion_criteria', array('moduleinstance' => $oldcoursemodule->id)); foreach ($allcriteria as $criteria) { $criteria->module = 'setask'; $criteria->moduleinstance = $newcoursemodule->id; $DB->update_record('course_completion_criteria', $criteria); } $completiondone = true; // Migrate log entries so we don't lose them. $logparams = array('cmid' => $oldcoursemodule->id, 'course' => $oldcoursemodule->course); $DB->set_field('log', 'module', 'setask', $logparams); $DB->set_field('log', 'cmid', $newcoursemodule->id, $logparams); // Copy all the submission data (and get plugins to do their bit). $oldsubmissions = $DB->get_records('setaskment_submissions', array('setaskment' => $oldsetaskmentid)); foreach ($oldsubmissions as $oldsubmission) { $submission = new stdClass(); $submission->setaskment = $newsetaskment->get_instance()->id; $submission->userid = $oldsubmission->userid; $submission->timecreated = $oldsubmission->timecreated; $submission->timemodified = $oldsubmission->timemodified; $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED; // Because in mod_setaskment there could only be one submission per student, it is always the latest. $submission->latest = 1; $submission->id = $DB->insert_record('setask_submission', $submission); if (!$submission->id) { $log .= get_string('couldnotinsertsubmission', 'mod_setask', $submission->userid); $rollback = true; } foreach ($newsetaskment->get_submission_plugins() as $plugin) { if ($plugin->can_upgrade($oldsetaskment->setaskmenttype, $oldversion)) { if (!$plugin->upgrade($oldcontext, $oldsetaskment, $oldsubmission, $submission, $log)) { $rollback = true; } } } if ($oldsubmission->timemarked) { // Submission has been graded - create a grade record. $grade = new stdClass(); $grade->setaskment = $newsetaskment->get_instance()->id; $grade->userid = $oldsubmission->userid; $grade->grader = $oldsubmission->teacher; $grade->timemodified = $oldsubmission->timemarked; $grade->timecreated = $oldsubmission->timecreated; $grade->grade = $oldsubmission->grade; if ($oldsubmission->mailed) { // The mailed flag goes in the flags table. $flags = new stdClass(); $flags->userid = $oldsubmission->userid; $flags->setaskment = $newsetaskment->get_instance()->id; $flags->mailed = 1; $DB->insert_record('setask_user_flags', $flags); } $grade->id = $DB->insert_record('setask_grades', $grade); if (!$grade->id) { $log .= get_string('couldnotinsertgrade', 'mod_setask', $grade->userid); $rollback = true; } // Copy any grading instances. if ($gradingarea) { $gradeidmap[$grade->id] = $oldsubmission->id; foreach ($gradingdefinitions as $definition) { $params = array('definitionid' => $definition->id, 'itemid' => $oldsubmission->id); $DB->set_field('grading_instances', 'itemid', $grade->id, $params); } } foreach ($newsetaskment->get_feedback_plugins() as $plugin) { if ($plugin->can_upgrade($oldsetaskment->setaskmenttype, $oldversion)) { if (!$plugin->upgrade($oldcontext, $oldsetaskment, $oldsubmission, $grade, $log)) { $rollback = true; } } } } } $newsetaskment->update_calendar($newcoursemodule->id); // Reassociate grade_items from the old setaskment instance to the new setask instance. // This includes outcome linked grade_items. $params = array('setask', $newsetaskment->get_instance()->id, 'setaskment', $oldsetaskment->id); $sql = 'UPDATE {grade_items} SET itemmodule = ?, iteminstance = ? WHERE itemmodule = ? AND iteminstance = ?'; $DB->execute($sql, $params); // Create a mapping record to map urls from the old to the new setaskment. $mapping = new stdClass(); $mapping->oldcmid = $oldcoursemodule->id; $mapping->oldinstance = $oldsetaskment->id; $mapping->newcmid = $newcoursemodule->id; $mapping->newinstance = $newsetaskment->get_instance()->id; $mapping->timecreated = time(); $DB->insert_record('setaskment_upgrade', $mapping); $gradesdone = true; } catch (Exception $exception) { $rollback = true; $log .= get_string('conversionexception', 'mod_setask', $exception->getMessage()); } if ($rollback) { // Roll back the grades changes. if ($gradesdone) { // Reassociate grade_items from the new setask instance to the old setaskment instance. $params = array('setaskment', $oldsetaskment->id, 'setask', $newsetaskment->get_instance()->id); $sql = 'UPDATE {grade_items} SET itemmodule = ?, iteminstance = ? WHERE itemmodule = ? AND iteminstance = ?'; $DB->execute($sql, $params); } // Roll back the completion changes. if ($completiondone) { $DB->set_field('course_modules_completion', 'coursemoduleid', $oldcoursemodule->id, array('coursemoduleid' => $newcoursemodule->id)); $allcriteria = $DB->get_records('course_completion_criteria', array('moduleinstance' => $newcoursemodule->id)); foreach ($allcriteria as $criteria) { $criteria->module = 'setaskment'; $criteria->moduleinstance = $oldcoursemodule->id; $DB->update_record('course_completion_criteria', $criteria); } } // Roll back the log changes. $logparams = array('cmid' => $newcoursemodule->id, 'course' => $newcoursemodule->course); $DB->set_field('log', 'module', 'setaskment', $logparams); $DB->set_field('log', 'cmid', $oldcoursemodule->id, $logparams); // Roll back the advanced grading update. if ($gradingarea) { foreach ($gradeidmap as $newgradeid => $oldsubmissionid) { foreach ($gradingdefinitions as $definition) { $DB->set_field('grading_instances', 'itemid', $oldsubmissionid, array('definitionid' => $definition->id, 'itemid' => $newgradeid)); } } $params = array('id' => $gradingarea->id, 'contextid' => $oldcontext->id, 'component' => 'mod_setaskment', 'areaname' => 'submission'); $DB->update_record('grading_areas', $params); } $newsetaskment->delete_instance(); return false; } // Delete the old setaskment (use object delete). $cm = get_coursemodule_from_id('', $oldcoursemodule->id, $oldcoursemodule->course); if ($cm) { course_delete_module($cm->id); } rebuild_course_cache($oldcoursemodule->course); return true; }
/** * Obtains a string describing this restriction (whether or not * it actually applies). Used to obtain information that is displayed to * students if the activity is not available to them, and for staff to see * what conditions are. * * The $full parameter can be used to distinguish between 'staff' cases * (when displaying all information about the activity) and 'student' cases * (when displaying only conditions they don't meet). * * If implementations require a course or modinfo, they should use * the get methods in $info. * * The special string <AVAILABILITY_CMNAME_123/> can be returned, where * 123 is any number. It will be replaced with the correctly-formatted * name for that activity. * * @param bool $full Set true if this is the 'full information' view * @param bool $not Set true if we are inverting the condition * @param info $info Item we're checking * @return string Information string (for admin) about all restrictions on * this item */ public function get_description($full, $not, info $info) { global $USER, $PAGE; static $jsadded = false; if (!$info instanceof info_module) { return ''; // Should only be possible against activities, not sections. } $cm = $info->get_course_module(); if ($not) { $str = get_string('requires_nopassword', 'availability_password'); } else { $str = get_string('requires_password', 'availability_password'); } if (!$full || !$this->is_available($not, $info, false, $USER->id)) { $url = new \moodle_url('/availability/condition/password/index.php', array('id' => $cm->id)); $str = \html_writer::link($url, $str, array('class' => 'availability_password-popup')); if (!$jsadded) { $PAGE->requires->strings_for_js(['enterpassword', 'wrongpassword', 'passwordintro'], 'availability_password'); $PAGE->requires->strings_for_js(['submit', 'cancel'], 'core'); $jsadded = true; $PAGE->requires->yui_module('moodle-availability_password-popup', 'M.availability_password.popup.init'); } } return $str; }
/** * Tests the add_legacy_availability_field_condition function used in restore. */ public function test_add_legacy_availability_field_condition() { // User field, normal operator. $rec = (object) array('userfield' => 'email', 'shortname' => null, 'operator' => 'contains', 'value' => '@'); $this->assertEquals('{"op":"&","showc":[true],"c":[' . '{"type":"profile","op":"contains","sf":"email","v":"@"}]}', info::add_legacy_availability_field_condition(null, $rec, true)); // User field, non-value operator. $rec = (object) array('userfield' => 'email', 'shortname' => null, 'operator' => 'isempty', 'value' => ''); $this->assertEquals('{"op":"&","showc":[true],"c":[' . '{"type":"profile","op":"isempty","sf":"email"}]}', info::add_legacy_availability_field_condition(null, $rec, true)); // Custom field. $rec = (object) array('userfield' => null, 'shortname' => 'frogtype', 'operator' => 'isempty', 'value' => ''); $this->assertEquals('{"op":"&","showc":[true],"c":[' . '{"type":"profile","op":"isempty","cf":"frogtype"}]}', info::add_legacy_availability_field_condition(null, $rec, true)); }
public function get_user_list_sql($not, \core_availability\info $info, $onlyactive) { global $DB; // The data for this condition is not really stored in the database, // so we return SQL that contains the hard-coded user list. list($enrolsql, $enrolparams) = get_enrolled_sql($info->get_context(), '', 0, $onlyactive); $condition = $not ? 'NOT' : ''; list($matchsql, $matchparams) = $DB->get_in_or_equal($this->filter, SQL_PARAMS_NAMED); $sql = "SELECT userids.id\n FROM ({$enrolsql}) userids\n WHERE {$condition} (userids.id {$matchsql})"; return array($sql, array_merge($enrolparams, $matchparams)); }