/** * Initialise the iterator * @return boolean success */ public function init() { global $CFG, $DB; $this->close(); grade_regrade_final_grades($this->course->id); $course_item = grade_item::fetch_course_item($this->course->id); if ($course_item->needsupdate) { // can not calculate all final grades - sorry return false; } $coursecontext = get_context_instance(CONTEXT_COURSE, $this->course->id); $relatedcontexts = get_related_contexts_string($coursecontext); list($gradebookroles_sql, $params) = $DB->get_in_or_equal(explode(',', $CFG->gradebookroles), SQL_PARAMS_NAMED, 'grbr'); //limit to users with an active enrolment list($enrolledsql, $enrolledparams) = get_enrolled_sql($coursecontext); $params = array_merge($params, $enrolledparams); if ($this->groupid) { $groupsql = "INNER JOIN {groups_members} gm ON gm.userid = u.id"; $groupwheresql = "AND gm.groupid = :groupid"; // $params contents: gradebookroles $params['groupid'] = $this->groupid; } else { $groupsql = ""; $groupwheresql = ""; } if (empty($this->sortfield1)) { // we must do some sorting even if not specified $ofields = ", u.id AS usrt"; $order = "usrt ASC"; } else { $ofields = ", u.{$this->sortfield1} AS usrt1"; $order = "usrt1 {$this->sortorder1}"; if (!empty($this->sortfield2)) { $ofields .= ", u.{$this->sortfield2} AS usrt2"; $order .= ", usrt2 {$this->sortorder2}"; } if ($this->sortfield1 != 'id' and $this->sortfield2 != 'id') { // user order MUST be the same in both queries, // must include the only unique user->id if not already present $ofields .= ", u.id AS usrt"; $order .= ", usrt ASC"; } } // $params contents: gradebookroles and groupid (for $groupwheresql) $users_sql = "SELECT u.* {$ofields}\n FROM {user} u\n JOIN ({$enrolledsql}) je ON je.id = u.id\n {$groupsql}\n JOIN (\n SELECT DISTINCT ra.userid\n FROM {role_assignments} ra\n WHERE ra.roleid {$gradebookroles_sql}\n AND ra.contextid {$relatedcontexts}\n ) rainner ON rainner.userid = u.id\n WHERE u.deleted = 0\n {$groupwheresql}\n ORDER BY {$order}"; $this->users_rs = $DB->get_recordset_sql($users_sql, $params); if (!empty($this->grade_items)) { $itemids = array_keys($this->grade_items); list($itemidsql, $grades_params) = $DB->get_in_or_equal($itemids, SQL_PARAMS_NAMED, 'items'); $params = array_merge($params, $grades_params); // $params contents: gradebookroles, enrolledparams, groupid (for $groupwheresql) and itemids $grades_sql = "SELECT g.* {$ofields}\n FROM {grade_grades} g\n JOIN {user} u ON g.userid = u.id\n JOIN ({$enrolledsql}) je ON je.id = u.id\n {$groupsql}\n JOIN (\n SELECT DISTINCT ra.userid\n FROM {role_assignments} ra\n WHERE ra.roleid {$gradebookroles_sql}\n AND ra.contextid {$relatedcontexts}\n ) rainner ON rainner.userid = u.id\n WHERE u.deleted = 0\n AND g.itemid {$itemidsql}\n {$groupwheresql}\n ORDER BY {$order}, g.itemid ASC"; $this->grades_rs = $DB->get_recordset_sql($grades_sql, $params); } else { $this->grades_rs = false; } return true; }
/** * Get list of course participants. * * @param int $courseid * @param text $withcapability * @param int $groupid * @param bool $onlyactive * @return array of course participants */ public static function get_enrolled_users($courseid, $withcapability = null, $groupid = null, $onlyactive = false) { global $DB, $CFG, $USER; // Do basic automatic PARAM checks on incoming data, using params description // If any problems are found then exceptions are thrown with helpful error messages $params = self::validate_parameters(self::get_enrolled_users_parameters(), array('courseid' => $courseid, 'withcapability' => $withcapability, 'groupid' => $groupid, 'onlyactive' => $onlyactive)); $coursecontext = get_context_instance(CONTEXT_COURSE, $params['courseid']); if ($courseid == SITEID) { $context = get_context_instance(CONTEXT_SYSTEM); } else { $context = $coursecontext; } try { self::validate_context($context); } catch (Exception $e) { $exceptionparam = new stdClass(); $exceptionparam->message = $e->getMessage(); $exceptionparam->courseid = $params['courseid']; throw new moodle_exception(get_string('errorcoursecontextnotvalid', 'webservice', $exceptionparam)); } if ($courseid == SITEID) { require_capability('moodle/site:viewparticipants', $context); } else { require_capability('moodle/course:viewparticipants', $context); } if ($withcapability) { require_capability('moodle/role:review', $coursecontext); } if ($groupid && groups_is_member($groupid)) { require_capability('moodle/site:accessallgroups', $coursecontext); } if ($onlyactive) { require_capability('moodle/course:enrolreview', $coursecontext); } list($sqlparams, $params) = get_enrolled_sql($coursecontext, $withcapability, $groupid, $onlyactive); $sql = "SELECT ue.userid, e.courseid, u.firstname, u.lastname, u.username, c.id as usercontextid\n FROM {user_enrolments} ue\n JOIN {enrol} e ON (e.id = ue.enrolid)\n JOIN {user} u ON (ue.userid = u.id)\n JOIN {context} c ON (u.id = c.instanceid AND contextlevel = " . CONTEXT_USER . ")\n WHERE e.courseid = :courseid AND ue.userid IN ({$sqlparams})\n GROUP BY ue.userid, e.courseid, u.firstname, u.lastname, u.username, c.id"; $params['courseid'] = $courseid; $enrolledusers = $DB->get_records_sql($sql, $params); $result = array(); $isadmin = is_siteadmin($USER); $canviewfullnames = has_capability('moodle/site:viewfullnames', $context); foreach ($enrolledusers as $enrolleduser) { $profilimgurl = moodle_url::make_pluginfile_url($enrolleduser->usercontextid, 'user', 'icon', NULL, '/', 'f1'); $profilimgurlsmall = moodle_url::make_pluginfile_url($enrolleduser->usercontextid, 'user', 'icon', NULL, '/', 'f2'); $resultuser = array('courseid' => $enrolleduser->courseid, 'userid' => $enrolleduser->userid, 'fullname' => fullname($enrolleduser), 'profileimgurl' => $profilimgurl->out(false), 'profileimgurlsmall' => $profilimgurlsmall->out(false)); // check if we can return username if ($isadmin) { $resultuser['username'] = $enrolleduser->username; } // check if we can return first and last name if ($isadmin or $canviewfullnames) { $resultuser['firstname'] = $enrolleduser->firstname; $resultuser['lastname'] = $enrolleduser->lastname; } $result[] = $resultuser; } return $result; }
public function find_users($search) { global $DB, $USER; $mailmaxusers = (isset($CFG->maxusersperpage) ? $CFG->maxusersperpage : $this->maxusersperpage); $context = context_course::instance($this->courseid); $mailsamerole = has_capability('local/mail:mailsamerole', $context); $userroleids = local_mail_get_user_roleids($USER->id, $context); list($wherecondition, $params) = $this->search_sql($search, 'u'); list($enrolledsql, $enrolledparams) = get_enrolled_sql($context, '', $this->groupid, true); list($parentsql, $parentparams) = $DB->get_in_or_equal($context->get_parent_context_ids(true), SQL_PARAMS_NAMED); $params = array_merge($params, $enrolledparams, $parentparams); $params['courseid'] = $this->courseid; if (!$mailsamerole) { list($relctxsql, $reldctxparams) = $DB->get_in_or_equal($context->get_parent_context_ids(true), SQL_PARAMS_NAMED, 'relctx'); list($samerolesql, $sameroleparams) = $DB->get_in_or_equal($userroleids, SQL_PARAMS_NAMED, 'samerole' , false); $wherecondition .= " AND u.id IN (SELECT userid FROM {role_assignments} WHERE roleid $samerolesql AND contextid $relctxsql)"; $params = array_merge($params, $sameroleparams, $reldctxparams); } $fields = 'SELECT r.id AS roleid, ' . 'r.shortname AS roleshortname, ' . 'r.name AS rolename, ' . 'u.id AS userid, ' . $this->required_fields_sql('u'); $countfields = 'SELECT COUNT(1)'; $sql = ' FROM {user} u JOIN (' . $enrolledsql . ') e ON e.id = u.id' . ' LEFT JOIN {role_assignments} ra ON (ra.userid = u.id AND ra.contextid ' . $parentsql . ')' . ' LEFT JOIN {role} r ON r.id = ra.roleid' . ' WHERE ' . $wherecondition; $order = ' ORDER BY r.sortorder, u.lastname ASC, u.firstname ASC'; if (!$this->is_validating()) { $count = $DB->count_records_sql($countfields . $sql, $params); if ($count > $mailmaxusers) { return $this->too_many_results($search, $count); } } $rs = $DB->get_recordset_sql($fields . $sql . $order, $params); $roles = groups_calculate_role_people($rs, $context); return $this->convert_array_format($roles, $search); }
/** * Finds all subscribed users * * @param string $search * @return array */ public function find_users($search) { global $DB; list($wherecondition, $params) = $this->search_sql($search, 'u'); $params['forumid'] = $this->forumid; // only active enrolled or everybody on the frontpage list($esql, $eparams) = get_enrolled_sql($this->context, '', $this->currentgroup, true); $fields = $this->required_fields_sql('u'); list($sort, $sortparams) = users_order_by_sql('u', $search, $this->accesscontext); $params = array_merge($params, $eparams, $sortparams); $subscribers = $DB->get_records_sql("SELECT {$fields}\n FROM {user} u\n JOIN ({$esql}) je ON je.id = u.id\n JOIN {forum_subscriptions} s ON s.userid = u.id\n WHERE {$wherecondition} AND s.forum = :forumid\n ORDER BY {$sort}", $params); return array(get_string("existingsubscribers", 'forum') => $subscribers); }
/** * Get list of course participants. * * @param int $courseid * @param text $withcapability * @param int $groupid * @param bool $onlyactive * @return array of course participants */ public static function get_enrolled_users($courseid, $withcapability, $groupid, $onlyactive) { global $DB; // Do basic automatic PARAM checks on incoming data, using params description // If any problems are found then exceptions are thrown with helpful error messages $params = self::validate_parameters(self::get_enrolled_users_parameters(), array('courseid' => $courseid, 'withcapability' => $withcapability, 'groupid' => $groupid, 'onlyactive' => $onlyactive)); $coursecontext = get_context_instance(CONTEXT_COURSE, $params['courseid']); if ($courseid == SITEID) { $context = get_context_instance(CONTEXT_SYSTEM); } else { $context = $coursecontext; } try { self::validate_context($context); } catch (Exception $e) { $exceptionparam = new stdClass(); $exceptionparam->message = $e->getMessage(); $exceptionparam->courseid = $params['courseid']; throw new moodle_exception(get_string('errorcoursecontextnotvalid', 'webservice', $exceptionparam)); } if ($courseid == SITEID) { require_capability('moodle/site:viewparticipants', $context); } else { require_capability('moodle/course:viewparticipants', $context); } if ($withcapability) { require_capability('moodle/role:review', $coursecontext); } if ($groupid) { if (groups_is_member($groupid)) { require_capability('moodle/site:accessallgroups', $coursecontext); } } if ($onlyactive) { require_capability('moodle/course:enrolreview', $coursecontext); } list($sql, $params) = get_enrolled_sql($coursecontext, $withcapability, $groupid, $onlyactive); $sql = "SELECT DISTINCT ue.userid, e.courseid\n FROM {user_enrolments} ue\n JOIN {enrol} e ON (e.id = ue.enrolid)\n WHERE e.courseid = :courseid AND ue.userid IN ({$sql})"; $params['courseid'] = $courseid; $enrolledusers = $DB->get_records_sql($sql, $params); $result = array(); foreach ($enrolledusers as $enrolleduser) { $result[] = array('courseid' => $enrolleduser->courseid, 'userid' => $enrolleduser->userid); } return $result; }
/** * Finds all subscribed users * * @param string $search * @return array */ public function find_users($search) { global $DB; list($wherecondition, $params) = $this->search_sql($search, 'u'); $params['twfid'] = $this->twfid; // only active enrolled or everybody on the frontpage list($esql, $eparams) = get_enrolled_sql($this->context, '', $this->currentgroup, true); $fields = $this->required_fields_sql('u'); list($sort, $sortparams) = users_order_by_sql('u', $search, $this->accesscontext); $params = array_merge($params, $eparams, $sortparams); $subscribers = $DB->get_records_sql("SELECT {$fields}\n FROM {user} u\n JOIN ({$esql}) je ON je.id = u.id\n JOIN {twf_subscriptions} s ON s.userid = u.id\n WHERE {$wherecondition} AND s.twf = :twfid\n ORDER BY {$sort}", $params); $cm = get_coursemodule_from_instance('twf', $this->twfid); $modinfo = get_fast_modinfo($cm->course); $info = new \core_availability\info_module($modinfo->get_cm($cm->id)); $subscribers = $info->filter_user_list($subscribers); return array(get_string("existingsubscribers", 'twf') => $subscribers); }
/** * Handles searching for user in a particular course in the message area. * * @param int $userid The user id doing the searching * @param int $courseid The id of the course we are searching in * @param string $search The string the user is searching * @param int $limitfrom * @param int $limitnum * @return array */ public static function search_users_in_course($userid, $courseid, $search, $limitfrom = 0, $limitnum = 0) { global $DB; // Get all the users in the course. list($esql, $params) = get_enrolled_sql(\context_course::instance($courseid), '', 0, true); $sql = "SELECT u.*, mc.blocked\n FROM {user} u\n JOIN ({$esql}) je\n ON je.id = u.id\n LEFT JOIN {message_contacts} mc\n ON (mc.contactid = u.id AND mc.userid = :userid)\n WHERE u.deleted = 0"; // Add more conditions. $fullname = $DB->sql_fullname(); $sql .= " AND u.id != :userid2\n AND " . $DB->sql_like($fullname, ':search', false) . "\n ORDER BY " . $DB->sql_fullname(); $params = array_merge(array('userid' => $userid, 'userid2' => $userid, 'search' => '%' . $search . '%'), $params); // Convert all the user records into contacts. $contacts = array(); if ($users = $DB->get_records_sql($sql, $params, $limitfrom, $limitnum)) { foreach ($users as $user) { $contacts[] = helper::create_contact($user); } } return $contacts; }
function getUserList($currentgroup = '', $courseId, $contextlevel, $context, $limitUsers = 15) { global $USER, $CFG, $DB, $OUTPUT; $groupmembers = ""; $groupselect = ""; $params = array(); //Add this to the SQL to show only group users if ($currentgroup !== NULL) { $groupmembers = ", {groups_members} gm"; $groupselect = "AND u.id = gm.userid AND gm.groupid = :currentgroup"; $params['currentgroup'] = $currentgroup; } $userfields = user_picture::fields('u', array('username')); if ($courseId == SITEID or $contextlevel < CONTEXT_COURSE) { // Site-level //Only show if is admin if (!checkIfUserIsAdmin()) { return ''; } $sql = "SELECT {$userfields}, ul.lastip, MAX(ul.timemodified) AS lastaccess\n FROM {user} u {$groupmembers} ,{sessions} ul\n WHERE u.id = ul.userid AND u.deleted = 0\n {$groupselect}\n GROUP BY {$userfields}\n ORDER BY ul.timemodified DESC "; } else { // Course level - show only enrolled users for now //Only show if is teacher or admin if (!checkIfUserIsTeacher($courseId)) { return ''; } list($esqljoin, $eparams) = get_enrolled_sql($context); $params = array_merge($params, $eparams); $sql = "SELECT {$userfields}, ul.lastip, MAX(ul.timemodified) AS lastaccess\n FROM {sessions} ul {$groupmembers}, {user} u\n JOIN ({$esqljoin}) euj ON euj.id = u.id\n WHERE u.id = ul.userid\n AND u.deleted = 0\n {$groupselect}\n GROUP BY {$userfields}\n ORDER BY lastaccess DESC"; $params['courseid'] = $courseId; } if ($users = $DB->get_records_sql($sql, $params, 0, $limitUsers)) { // We'll just take the most recent 50 maximum foreach ($users as $user) { $users[$user->id]->fullname = fullname($user); } } else { $users = array(); } return $users; }
public function find_users($search) { global $DB; $context = context_course::instance($this->courseid); list($wherecondition, $params) = $this->search_sql($search, 'u'); list($enrolledsql, $enrolledparams) = get_enrolled_sql($context, '', $this->groupid, true); $params = array_merge($params, $enrolledparams); $params['courseid'] = $this->courseid; $fields = 'SELECT r.id AS roleid, ' . 'r.shortname AS roleshortname, ' . 'r.name AS rolename, ' . 'u.id AS userid, ' . $this->required_fields_sql('u'); $countfields = 'SELECT COUNT(1)'; $sql = ' FROM {user} u JOIN (' . $enrolledsql . ') e ON e.id = u.id' . ' LEFT JOIN {role_assignments} ra ON (ra.userid = u.id AND ra.contextid ' . get_related_contexts_string($context) . ')' . ' LEFT JOIN {role} r ON r.id = ra.roleid' . ' WHERE ' . $wherecondition; $order = ' ORDER BY r.sortorder, u.lastname ASC, u.firstname ASC'; if (!$this->is_validating()) { $count = $DB->count_records_sql($countfields . $sql, $params); if ($count > 100) { return $this->too_many_results($search, $count); } } $rs = $DB->get_recordset_sql($fields . $sql . $order, $params); $roles = groups_calculate_role_people($rs, $context); return $this->convert_array_format($roles, $search); }
function forumngfeature_usage_show_mostreaders($params, $forum = null) { global $DB, $PAGE; $cloneid = empty($params['clone']) ? 0 : $params['clone']; if ($forum == null) { if (empty($params['id'])) { throw new moodle_exception('Missing forum id param'); } $forum = mod_forumng::get_from_cmid($params['id'], $cloneid); } $groupwhere = ''; $groupparams = array(); $groupid = 0; if (!empty($params['group']) && $params['group'] != mod_forumng::NO_GROUPS && $params['group'] != mod_forumng::ALL_GROUPS) { $groupwhere = 'AND (fd.groupid = :groupid OR fd.groupid IS NULL)'; $groupid = $params['group']; $groupparams = array('groupid' => $groupid); } if (has_capability('mod/forumng:viewreadinfo', $forum->get_context())) { if (!$PAGE->has_set_url()) { // Set context when called via ajax. $PAGE->set_context($forum->get_context()); } $renderer = $PAGE->get_renderer('forumngfeature_usage'); // Only get enrolled users - speeds up query significantly on large forums. list($sql, $params) = get_enrolled_sql($forum->get_context(), '', $groupid, true); // View discussions read. $readers = $DB->get_recordset_sql("\n SELECT COUNT(fr.userid) AS count, fr.discussionid\n FROM {forumng_discussions} fd\n RIGHT JOIN (\n\t\t SELECT discussionid, userid\n\t\t FROM (\n\t\t SELECT * FROM {forumng_read}\n\t\t UNION ALL\n\t\t SELECT frp.id, frp.userid, fp.discussionid, frp.time\n FROM {forumng_posts} fp\n RIGHT JOIN {forumng_read_posts} frp ON fp.id = frp.postid\n WHERE fp.deleted = 0 AND fp.oldversion = 0\n ) frp GROUP BY discussionid, userid\n ) fr ON fr.discussionid = fd.id\n WHERE fd.forumngid = :courseid\n AND fd.deleted = 0\n {$groupwhere}\n AND fr.userid IN({$sql})\n GROUP BY fr.discussionid\n ORDER BY count desc, fr.discussionid desc", array_merge(array('courseid' => $forum->get_id()), $groupparams, $params), 0, 5); $readerlist = array(); foreach ($readers as $discuss) { $discussion = mod_forumng_discussion::get_from_id($discuss->discussionid, $cloneid); list($content, $user) = $renderer->render_usage_discussion_info($forum, $discussion); $readerlist[] = $renderer->render_usage_list_item($forum, $discuss->count, $user, $content); } return $renderer->render_usage_list($readerlist, 'mostreaders', false); } }
/** * Returns unread messages count and courses * @return array */ public static function count_unread_and_courses_messages($userid = '0') { global $DB, $USER; //Parameter validation //REQUIRED $params = self::validate_parameters(self::count_unread_messages_parameters(), array('userid' => $userid)); //Context validation //OPTIONAL but in most web service it should present $context = get_context_instance(CONTEXT_USER, $USER->id); self::validate_context($context); //Capability checking //OPTIONAL but in most web service it should present if (!has_capability('moodle/user:viewdetails', $context)) { throw new moodle_exception('cannotviewprofile'); } $messagecount = $DB->count_records_select('message', 'useridto=' . (int) $userid); $courses = enrol_get_users_courses($params['userid'], true, 'id, shortname, fullname, idnumber, visible'); $result = array('messagecount' => $messagecount, 'courses' => array()); foreach ($courses as $course) { $context = context_course::instance($course->id, IGNORE_MISSING); try { self::validate_context($context); } catch (Exception $e) { // current user can not access this course, sorry we can not disclose who is enrolled in this course! continue; } if ($userid != $USER->id and !has_capability('moodle/course:viewparticipants', $context)) { // we need capability to view participants continue; } list($enrolledsqlselect, $enrolledparams) = get_enrolled_sql($context); $enrolledsql = "SELECT COUNT('x') FROM ({$enrolledsqlselect}) enrolleduserids"; $enrolledusercount = $DB->count_records_sql($enrolledsql, $enrolledparams); $result['courses'][] = array('id' => $course->id, 'shortname' => $course->shortname, 'fullname' => $course->fullname, 'idnumber' => $course->idnumber, 'visible' => $course->visible, 'enrolledusercount' => $enrolledusercount); } return $result; }
public function find_users($search) { global $DB; list($enrolsql, $eparams) = get_enrolled_sql($this->context); // Now we have to go to the database. list($wherecondition, $params) = $this->search_sql($search, 'u'); $params = array_merge($params, $eparams); if ($wherecondition) { $wherecondition = ' AND ' . $wherecondition; } $fields = 'SELECT ' . $this->required_fields_sql('u'); $countfields = 'SELECT COUNT(u.id)'; $sql = " FROM ({$enrolsql}) enrolled_users_view\n JOIN {user} u ON u.id = enrolled_users_view.id\n LEFT JOIN {role_assignments} ra ON (ra.userid = enrolled_users_view.id AND\n ra.roleid = :roleid AND ra.contextid = :contextid)\n WHERE ra.id IS NULL\n {$wherecondition}"; $params['contextid'] = $this->context->id; $params['roleid'] = $this->roleid; list($sort, $sortparams) = users_order_by_sql('u', $search, $this->accesscontext); $order = ' ORDER BY ' . $sort; // Check to see if there are too many to show sensibly. if (!$this->is_validating()) { $potentialmemberscount = $DB->count_records_sql($countfields . $sql, $params); if ($potentialmemberscount > $this->maxusersperpage) { return $this->too_many_results($search, $potentialmemberscount); } } // If not, show them. $availableusers = $DB->get_records_sql($fields . $sql . $order, array_merge($params, $sortparams)); if (empty($availableusers)) { return array(); } if ($search) { $groupname = get_string('potusersmatching', 'core_role', $search); } else { $groupname = get_string('potusers', 'core_role'); } return array($groupname => $availableusers); }
/** * Finds all subscribed users * * @param string $search * @return array */ public function find_users($search) { global $DB; list($wherecondition, $params) = $this->search_sql($search, 'u'); $params['forumid'] = $this->forumid; // only active enrolled or everybody on the frontpage list($esql, $eparams) = get_enrolled_sql($this->context, '', $this->currentgroup, true); $params = array_merge($params, $eparams); $fields = $this->required_fields_sql('u'); $subscribers = $DB->get_records_sql("SELECT $fields FROM {user} u JOIN ($esql) je ON je.id = u.id JOIN {forum_subscriptions} s ON s.userid = u.id WHERE $wherecondition AND s.forum = :forumid ORDER BY u.lastname ASC, u.firstname ASC", $params); return array(get_string("existingsubscribers", 'forum') => $subscribers); }
/** * Return array of users whose progress is tracked in this course. * * Optionally supply a search's where clause, group id, sorting, paging. * * @param string $where Where clause sql, referring to 'u.' fields (optional) * @param array $whereparams Where clause params (optional) * @param int $groupid Group ID to restrict to (optional) * @param string $sort Order by clause (optional) * @param int $limitfrom Result start (optional) * @param int $limitnum Result max size (optional) * @param context $extracontext If set, includes extra user information fields * as appropriate to display for current user in this context * @return array Array of user objects with standard user fields */ public function get_tracked_users($where = '', $whereparams = array(), $groupid = 0, $sort = '', $limitfrom = '', $limitnum = '', context $extracontext = null) { global $DB; list($enrolledsql, $params) = get_enrolled_sql(context_course::instance($this->course->id), 'moodle/course:isincompletionreports', $groupid, true); $sql = 'SELECT u.id, u.firstname, u.lastname, u.idnumber'; if ($extracontext) { $sql .= get_extra_user_fields_sql($extracontext, 'u', '', array('idnumber')); } $sql .= ' FROM (' . $enrolledsql . ') eu JOIN {user} u ON u.id = eu.id'; if ($where) { $sql .= " AND {$where}"; $params = array_merge($params, $whereparams); } if ($sort) { $sql .= " ORDER BY {$sort}"; } return $DB->get_records_sql($sql, $params, $limitfrom, $limitnum); }
/** * This function returns an array of grades that were included in the import, * but where the user does not currently have a graded role on the course. These grades * are still stored in the database, but will not be visible in the gradebook unless * this user subsequently enrols on the course in a graded roles. * * The returned objects have fields user firstname, lastname and useridnumber, and gradeidnumber. * * @param integer $importcode import batch identifier * @param integer $courseid the course we are importing to. * @return mixed and array of user objects, or false if none. */ function get_unenrolled_users_in_import($importcode, $courseid) { global $CFG, $DB; $coursecontext = context_course::instance($courseid); // We want to query both the current context and parent contexts. list($relatedctxsql, $relatedctxparams) = $DB->get_in_or_equal($coursecontext->get_parent_context_ids(true), SQL_PARAMS_NAMED, 'relatedctx'); // Users with a gradeable role. list($gradebookrolessql, $gradebookrolesparams) = $DB->get_in_or_equal(explode(',', $CFG->gradebookroles), SQL_PARAMS_NAMED, 'grbr'); // Enrolled users. $context = context_course::instance($courseid); list($enrolledsql, $enrolledparams) = get_enrolled_sql($context); list($sort, $sortparams) = users_order_by_sql('u'); $sql = "SELECT giv.id, u.firstname, u.lastname, u.idnumber AS useridnumber,\n COALESCE(gi.idnumber, gin.itemname) AS gradeidnumber\n FROM {grade_import_values} giv\n JOIN {user} u\n ON giv.userid = u.id\n LEFT JOIN {grade_items} gi\n ON gi.id = giv.itemid\n LEFT JOIN {grade_import_newitem} gin\n ON gin.id = giv.newgradeitem\n LEFT JOIN ({$enrolledsql}) je\n ON je.id = u.id\n LEFT JOIN {role_assignments} ra\n ON (giv.userid = ra.userid AND ra.roleid {$gradebookrolessql} AND ra.contextid {$relatedctxsql})\n WHERE giv.importcode = :importcode\n AND (ra.id IS NULL OR je.id IS NULL)\n ORDER BY gradeidnumber, {$sort}"; $params = array_merge($gradebookrolesparams, $enrolledparams, $sortparams, $relatedctxparams); $params['importcode'] = $importcode; return $DB->get_records_sql($sql, $params); }
/** * Load a count of submissions with a specified status. * * @param string $status The submission status - should match one of the constants * @return int number of matching submissions */ public function count_submissions_with_status($status) { global $DB; $currentgroup = groups_get_activity_group($this->get_course_module(), true); list($esql, $params) = get_enrolled_sql($this->get_context(), 'mod/assign:submit', $currentgroup, true); $params['assignid'] = $this->get_instance()->id; $params['assignid2'] = $this->get_instance()->id; $params['submissionstatus'] = $status; if ($this->get_instance()->teamsubmission) { $groupsstr = ''; if ($currentgroup != 0) { // If there is an active group we should only display the current group users groups. $participants = $this->list_participants($currentgroup, true); $groups = groups_get_all_groups($this->get_course()->id, array_keys($participants), $this->get_instance()->teamsubmissiongroupingid, 'DISTINCT g.id, g.name'); list($groupssql, $groupsparams) = $DB->get_in_or_equal(array_keys($groups), SQL_PARAMS_NAMED); $groupsstr = 's.groupid ' . $groupssql . ' AND'; $params = $params + $groupsparams; } $sql = 'SELECT COUNT(s.groupid) FROM {assign_submission} s WHERE s.latest = 1 AND s.assignment = :assignid AND s.timemodified IS NOT NULL AND s.userid = :groupuserid AND ' . $groupsstr . ' s.status = :submissionstatus'; $params['groupuserid'] = 0; } else { $sql = 'SELECT COUNT(s.userid) FROM {assign_submission} s JOIN(' . $esql . ') e ON e.id = s.userid WHERE s.latest = 1 AND s.assignment = :assignid AND s.timemodified IS NOT NULL AND s.status = :submissionstatus'; } return $DB->count_records_sql($sql, $params); }
/** * Returns SQL to fetch all enrolled users with the given capability in the current workshop * * The returned array consists of string $sql and the $params array. Note that the $sql can be * empty if a grouping is selected and it has no groups. * * The list is automatically restricted according to any availability restrictions * that apply to user lists (e.g. group, grouping restrictions). * * @param string $capability the name of the capability * @param bool $musthavesubmission ff true, return only users who have already submitted * @param int $groupid 0 means ignore groups, any other value limits the result by group id * @return array of (string)sql, (array)params */ protected function get_users_with_capability_sql($capability, $musthavesubmission, $groupid) { global $CFG; /** @var int static counter used to generate unique parameter holders */ static $inc = 0; $inc++; // If the caller requests all groups and we are using a selected grouping, // recursively call this function for each group in the grouping (this is // needed because get_enrolled_sql only supports a single group). if (empty($groupid) and $this->cm->groupingid) { $groupingid = $this->cm->groupingid; $groupinggroupids = array_keys(groups_get_all_groups($this->cm->course, 0, $this->cm->groupingid, 'g.id')); $sql = array(); $params = array(); foreach ($groupinggroupids as $groupinggroupid) { if ($groupinggroupid > 0) { // just in case in order not to fall into the endless loop list($gsql, $gparams) = $this->get_users_with_capability_sql($capability, $musthavesubmission, $groupinggroupid); $sql[] = $gsql; $params = array_merge($params, $gparams); } } $sql = implode(PHP_EOL . " UNION " . PHP_EOL, $sql); return array($sql, $params); } list($esql, $params) = get_enrolled_sql($this->context, $capability, $groupid, true); $userfields = user_picture::fields('u'); $sql = "SELECT {$userfields}\n FROM {user} u\n JOIN ({$esql}) je ON (je.id = u.id AND u.deleted = 0) "; if ($musthavesubmission) { $sql .= " JOIN {workshop_submissions} ws ON (ws.authorid = u.id AND ws.example = 0 AND ws.workshopid = :workshopid{$inc}) "; $params['workshopid' . $inc] = $this->id; } // If the activity is restricted so that only certain users should appear // in user lists, integrate this into the same SQL. $info = new \core_availability\info_module($this->cm); list($listsql, $listparams) = $info->get_user_list_sql(false); if ($listsql) { $sql .= " JOIN ({$listsql}) restricted ON restricted.id = u.id "; $params = array_merge($params, $listparams); } return array($sql, $params); }
public function find_users($search) { global $DB; // Get list of allowed roles. $context = context_course::instance($this->courseid); if ($validroleids = groups_get_possible_roles($context)) { list($roleids, $roleparams) = $DB->get_in_or_equal($validroleids, SQL_PARAMS_NAMED, 'r'); } else { $roleids = " = -1"; $roleparams = array(); } // Get the search condition. list($searchcondition, $searchparams) = $this->search_sql($search, 'u'); // Build the SQL list($enrolsql, $enrolparams) = get_enrolled_sql($context); $fields = "SELECT r.id AS roleid, u.id AS userid, " . $this->required_fields_sql('u') . ", (SELECT count(igm.groupid) FROM {groups_members} igm JOIN {groups} ig ON igm.groupid = ig.id WHERE igm.userid = u.id AND ig.courseid = :courseid) AS numgroups"; $sql = " FROM {user} u JOIN ($enrolsql) e ON e.id = u.id LEFT JOIN {role_assignments} ra ON (ra.userid = u.id AND ra.contextid " . get_related_contexts_string($context) . " AND ra.roleid $roleids) LEFT JOIN {role} r ON r.id = ra.roleid LEFT JOIN {groups_members} gm ON (gm.userid = u.id AND gm.groupid = :groupid) WHERE u.deleted = 0 AND gm.id IS NULL AND $searchcondition"; list($sort, $sortparams) = users_order_by_sql('u', $search, $this->accesscontext); $orderby = ' ORDER BY ' . $sort; $params = array_merge($searchparams, $roleparams, $enrolparams); $params['courseid'] = $this->courseid; $params['groupid'] = $this->groupid; if (!$this->is_validating()) { $potentialmemberscount = $DB->count_records_sql("SELECT COUNT(DISTINCT u.id) $sql", $params); if ($potentialmemberscount > group_non_members_selector::MAX_USERS_PER_PAGE) { return $this->too_many_results($search, $potentialmemberscount); } } $rs = $DB->get_recordset_sql("$fields $sql $orderby", array_merge($params, $sortparams)); $roles = groups_calculate_role_people($rs, $context); //don't hold onto user IDs if we're doing validation if (empty($this->validatinguserids) ) { if($roles) { foreach($roles as $k=>$v) { if($v) { foreach($v->users as $uid=>$userobject) { $this->potentialmembersids[] = $uid; } } } } } return $this->convert_array_format($roles, $search); }
$table->define_baseurl($baseurl->out()); if (!isset($hiddenfields['lastaccess'])) { $table->sortable(true, 'lastaccess', SORT_DESC); } else { $table->sortable(true, 'firstname', SORT_ASC); } $table->no_sorting('roles'); $table->no_sorting('groups'); $table->no_sorting('groupings'); $table->no_sorting('select'); $table->set_attribute('cellspacing', '0'); $table->set_attribute('id', 'participants'); $table->set_attribute('class', 'generaltable generalbox'); $table->set_control_variables(array(TABLE_VAR_SORT => 'ssort', TABLE_VAR_HIDE => 'shide', TABLE_VAR_SHOW => 'sshow', TABLE_VAR_IFIRST => 'sifirst', TABLE_VAR_ILAST => 'silast', TABLE_VAR_PAGE => 'spage')); $table->setup(); list($esql, $params) = get_enrolled_sql($context, null, $currentgroup, true); $joins = array("FROM {user} u"); $wheres = array(); $userfields = array('username', 'email', 'city', 'country', 'lang', 'timezone', 'maildisplay'); $mainuserfields = user_picture::fields('u', $userfields); $extrasql = get_extra_user_fields_sql($context, 'u', '', $userfields); if ($isfrontpage) { $select = "SELECT {$mainuserfields}, u.lastaccess{$extrasql}"; $joins[] = "JOIN ({$esql}) e ON e.id = u.id"; // Everybody on the frontpage usually. if ($accesssince) { $wheres[] = get_user_lastaccess_sql($accesssince); } } else { $select = "SELECT {$mainuserfields}, COALESCE(ul.timeaccess, 0) AS lastaccess{$extrasql}"; $joins[] = "JOIN ({$esql}) e ON e.id = u.id";
/** * Builds the grade item averages. */ function calculate_averages() { global $USER, $DB; if ($this->showaverage) { // This settings are actually grader report settings (not user report) // however we're using them as having two separate but identical settings the // user would have to keep in sync would be annoying. $averagesdisplaytype = $this->get_pref('averagesdisplaytype'); $averagesdecimalpoints = $this->get_pref('averagesdecimalpoints'); $meanselection = $this->get_pref('meanselection'); $shownumberofgrades = $this->get_pref('shownumberofgrades'); $avghtml = ''; $groupsql = $this->groupsql; $groupwheresql = $this->groupwheresql; $totalcount = $this->get_numusers(false); // We want to query both the current context and parent contexts. list($relatedctxsql, $relatedctxparams) = $DB->get_in_or_equal($this->context->get_parent_context_ids(true), SQL_PARAMS_NAMED, 'relatedctx'); // Limit to users with a gradeable role ie students. list($gradebookrolessql, $gradebookrolesparams) = $DB->get_in_or_equal(explode(',', $this->gradebookroles), SQL_PARAMS_NAMED, 'grbr0'); // Limit to users with an active enrolment. list($enrolledsql, $enrolledparams) = get_enrolled_sql($this->context); $params = array_merge($this->groupwheresql_params, $gradebookrolesparams, $enrolledparams, $relatedctxparams); $params['courseid'] = $this->courseid; // find sums of all grade items in course $sql = "SELECT gg.itemid, SUM(gg.finalgrade) AS sum\n FROM {grade_items} gi\n JOIN {grade_grades} gg ON gg.itemid = gi.id\n JOIN {user} u ON u.id = gg.userid\n JOIN ({$enrolledsql}) je ON je.id = gg.userid\n JOIN (\n SELECT DISTINCT ra.userid\n FROM {role_assignments} ra\n WHERE ra.roleid {$gradebookrolessql}\n AND ra.contextid {$relatedctxsql}\n ) rainner ON rainner.userid = u.id\n {$groupsql}\n WHERE gi.courseid = :courseid\n AND u.deleted = 0\n AND gg.finalgrade IS NOT NULL\n AND gg.hidden = 0\n {$groupwheresql}\n GROUP BY gg.itemid"; $sum_array = array(); $sums = $DB->get_recordset_sql($sql, $params); foreach ($sums as $itemid => $csum) { $sum_array[$itemid] = $csum->sum; } $sums->close(); $columncount = 0; // Empty grades must be evaluated as grademin, NOT always 0 // This query returns a count of ungraded grades (NULL finalgrade OR no matching record in grade_grades table) // No join condition when joining grade_items and user to get a grade item row for every user // Then left join with grade_grades and look for rows with null final grade (which includes grade items with no grade_grade) $sql = "SELECT gi.id, COUNT(u.id) AS count\n FROM {grade_items} gi\n JOIN {user} u ON u.deleted = 0\n JOIN ({$enrolledsql}) je ON je.id = u.id\n JOIN (\n SELECT DISTINCT ra.userid\n FROM {role_assignments} ra\n WHERE ra.roleid {$gradebookrolessql}\n AND ra.contextid {$relatedctxsql}\n ) rainner ON rainner.userid = u.id\n LEFT JOIN {grade_grades} gg\n ON (gg.itemid = gi.id AND gg.userid = u.id AND gg.finalgrade IS NOT NULL AND gg.hidden = 0)\n {$groupsql}\n WHERE gi.courseid = :courseid\n AND gg.finalgrade IS NULL\n {$groupwheresql}\n GROUP BY gi.id"; $ungraded_counts = $DB->get_records_sql($sql, $params); foreach ($this->gtree->items as $itemid => $unused) { if (!empty($this->gtree->items[$itemid]->avg)) { continue; } $item = $this->gtree->items[$itemid]; if ($item->needsupdate) { $avghtml .= '<td class="cell c' . $columncount++ . '"><span class="gradingerror">' . get_string('error') . '</span></td>'; continue; } if (empty($sum_array[$item->id])) { $sum_array[$item->id] = 0; } if (empty($ungraded_counts[$itemid])) { $ungraded_count = 0; } else { $ungraded_count = $ungraded_counts[$itemid]->count; } //do they want the averages to include all grade items if ($meanselection == GRADE_REPORT_MEAN_GRADED) { $mean_count = $totalcount - $ungraded_count; } else { // Bump up the sum by the number of ungraded items * grademin $sum_array[$item->id] += $ungraded_count * $item->grademin; $mean_count = $totalcount; } // Determine which display type to use for this average if (!empty($USER->gradeediting) && $USER->gradeediting[$this->courseid]) { $displaytype = GRADE_DISPLAY_TYPE_REAL; } else { if ($averagesdisplaytype == GRADE_REPORT_PREFERENCE_INHERIT) { // no ==0 here, please resave the report and user preferences $displaytype = $item->get_displaytype(); } else { $displaytype = $averagesdisplaytype; } } // Override grade_item setting if a display preference (not inherit) was set for the averages if ($averagesdecimalpoints == GRADE_REPORT_PREFERENCE_INHERIT) { $decimalpoints = $item->get_decimals(); } else { $decimalpoints = $averagesdecimalpoints; } if (empty($sum_array[$item->id]) || $mean_count == 0) { $this->gtree->items[$itemid]->avg = '-'; } else { $sum = $sum_array[$item->id]; $avgradeval = $sum / $mean_count; $gradehtml = grade_format_gradevalue($avgradeval, $item, true, $displaytype, $decimalpoints); $numberofgrades = ''; if ($shownumberofgrades) { $numberofgrades = " ({$mean_count})"; } $this->gtree->items[$itemid]->avg = $gradehtml . $numberofgrades; } } } }
/** * Load a count of submissions with a specified status. * * @param string $status The submission status - should match one of the constants * @return int number of matching submissions */ public function count_submissions_with_status($status) { global $DB; $currentgroup = groups_get_activity_group($this->get_course_module(), true); list($esql, $params) = get_enrolled_sql($this->get_context(), 'mod/assign:submit', $currentgroup, true); $params['assignid'] = $this->get_instance()->id; $params['assignid2'] = $this->get_instance()->id; $params['submissionstatus'] = $status; if ($this->get_instance()->teamsubmission) { $maxattemptsql = 'SELECT mxs.groupid, MAX(mxs.attemptnumber) AS maxattempt FROM {assign_submission} mxs WHERE mxs.assignment = :assignid2 GROUP BY mxs.groupid'; $sql = 'SELECT COUNT(s.groupid) FROM {assign_submission} s JOIN(' . $maxattemptsql . ') smx ON s.groupid = smx.groupid WHERE s.attemptnumber = smx.maxattempt AND s.assignment = :assignid AND s.timemodified IS NOT NULL AND s.userid = :groupuserid AND s.status = :submissionstatus'; $params['groupuserid'] = 0; } else { $maxattemptsql = 'SELECT mxs.userid, MAX(mxs.attemptnumber) AS maxattempt FROM {assign_submission} mxs WHERE mxs.assignment = :assignid2 GROUP BY mxs.userid'; $sql = 'SELECT COUNT(s.userid) FROM {assign_submission} s JOIN(' . $esql . ') e ON e.id = s.userid JOIN(' . $maxattemptsql . ') smx ON s.userid = smx.userid WHERE s.attemptnumber = smx.maxattempt AND s.assignment = :assignid AND s.timemodified IS NOT NULL AND s.status = :submissionstatus'; } return $DB->count_records_sql($sql, $params); }
/** * Given context and array of users, returns array of user ids whose enrolment status is suspended, * or enrolment has expired or not started. * * @param context $context context in which user enrolment is checked. * @return array list of suspended user id's. */ function get_suspended_userids($context){ global $DB; // Get all enrolled users. list($sql, $params) = get_enrolled_sql($context); $users = $DB->get_records_sql($sql, $params); // Get active enrolled users. list($sql, $params) = get_enrolled_sql($context, null, null, true); $activeusers = $DB->get_records_sql($sql, $params); $susers = array(); if (sizeof($activeusers) != sizeof($users)) { foreach ($users as $userid => $user) { if (!array_key_exists($userid, $activeusers)) { $susers[$userid] = $userid; } } } return $susers; }
/** * Display all the submissions ready for grading * * @global object * @global object * @global object * @global object * @param string $message * @return bool|void */ function display_submissions($message='') { global $CFG, $DB, $USER, $DB, $OUTPUT, $PAGE; require_once($CFG->libdir.'/gradelib.php'); /* first we check to see if the form has just been submitted * to request user_preference updates */ $filters = array(self::FILTER_ALL => get_string('all'), self::FILTER_SUBMITTED => get_string('submitted', 'assignment'), self::FILTER_REQUIRE_GRADING => get_string('requiregrading', 'assignment')); $updatepref = optional_param('updatepref', 0, PARAM_INT); if (isset($_POST['updatepref'])){ $perpage = optional_param('perpage', 10, PARAM_INT); $perpage = ($perpage <= 0) ? 10 : $perpage ; $filter = optional_param('filter', 0, PARAM_INT); set_user_preference('assignment_perpage', $perpage); set_user_preference('assignment_quickgrade', optional_param('quickgrade', 0, PARAM_BOOL)); set_user_preference('assignment_filter', $filter); } /* next we get perpage and quickgrade (allow quick grade) params * from database */ $perpage = get_user_preferences('assignment_perpage', 10); $quickgrade = get_user_preferences('assignment_quickgrade', 0); $filter = get_user_preferences('assignment_filter', 0); $grading_info = grade_get_grades($this->course->id, 'mod', 'assignment', $this->assignment->id); if (!empty($CFG->enableoutcomes) and !empty($grading_info->outcomes)) { $uses_outcomes = true; } else { $uses_outcomes = false; } $page = optional_param('page', 0, PARAM_INT); $strsaveallfeedback = get_string('saveallfeedback', 'assignment'); /// Some shortcuts to make the code read better $course = $this->course; $assignment = $this->assignment; $cm = $this->cm; $tabindex = 1; //tabindex for quick grading tabbing; Not working for dropdowns yet add_to_log($course->id, 'assignment', 'view submission', 'submissions.php?id='.$this->cm->id, $this->assignment->id, $this->cm->id); $PAGE->set_title(format_string($this->assignment->name,true)); $PAGE->set_heading($this->course->fullname); echo $OUTPUT->header(); echo '<div class="usersubmissions">'; //hook to allow plagiarism plugins to update status/print links. plagiarism_update_status($this->course, $this->cm); /// Print quickgrade form around the table if ($quickgrade) { $formattrs = array(); $formattrs['action'] = new moodle_url('/mod/assignment/submissions.php'); $formattrs['id'] = 'fastg'; $formattrs['method'] = 'post'; echo html_writer::start_tag('form', $formattrs); echo html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'id', 'value'=> $this->cm->id)); echo html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'mode', 'value'=> 'fastgrade')); echo html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'page', 'value'=> $page)); echo html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'sesskey', 'value'=> sesskey())); } $course_context = get_context_instance(CONTEXT_COURSE, $course->id); if (has_capability('gradereport/grader:view', $course_context) && has_capability('moodle/grade:viewall', $course_context)) { echo '<div class="allcoursegrades"><a href="' . $CFG->wwwroot . '/grade/report/grader/index.php?id=' . $course->id . '">' . get_string('seeallcoursegrades', 'grades') . '</a></div>'; } if (!empty($message)) { echo $message; // display messages here if any } $context = get_context_instance(CONTEXT_MODULE, $cm->id); /// Check to see if groups are being used in this assignment /// find out current groups mode $groupmode = groups_get_activity_groupmode($cm); $currentgroup = groups_get_activity_group($cm, true); groups_print_activity_menu($cm, $CFG->wwwroot . '/mod/assignment/submissions.php?id=' . $this->cm->id); /// Get all ppl that are allowed to submit assignments list($esql, $params) = get_enrolled_sql($context, 'mod/assignment:view', $currentgroup); if ($filter == self::FILTER_ALL) { $sql = "SELECT u.id FROM {user} u ". "LEFT JOIN ($esql) eu ON eu.id=u.id ". "WHERE u.deleted = 0 AND eu.id=u.id "; } else { $wherefilter = ''; if($filter == self::FILTER_SUBMITTED) { $wherefilter = ' AND s.timemodified > 0'; } else if($filter == self::FILTER_REQUIRE_GRADING) { $wherefilter = ' AND s.timemarked < s.timemodified '; } $sql = "SELECT u.id FROM {user} u ". "LEFT JOIN ($esql) eu ON eu.id=u.id ". "LEFT JOIN {assignment_submissions} s ON (u.id = s.userid) " . "WHERE u.deleted = 0 AND eu.id=u.id ". 'AND s.assignment = '. $this->assignment->id . $wherefilter; } $users = $DB->get_records_sql($sql, $params); if (!empty($users)) { $users = array_keys($users); } // if groupmembersonly used, remove users who are not in any group if ($users and !empty($CFG->enablegroupmembersonly) and $cm->groupmembersonly) { if ($groupingusers = groups_get_grouping_members($cm->groupingid, 'u.id', 'u.id')) { $users = array_intersect($users, array_keys($groupingusers)); } } $tablecolumns = array('picture', 'fullname', 'grade', 'submissioncomment', 'timemodified', 'timemarked', 'status', 'finalgrade'); if ($uses_outcomes) { $tablecolumns[] = 'outcome'; // no sorting based on outcomes column } $tableheaders = array('', get_string('fullname'), get_string('grade'), get_string('comment', 'assignment'), get_string('lastmodified').' ('.get_string('submission', 'assignment').')', get_string('lastmodified').' ('.get_string('grade').')', get_string('status'), get_string('finalgrade', 'grades')); if ($uses_outcomes) { $tableheaders[] = get_string('outcome', 'grades'); } require_once($CFG->libdir.'/tablelib.php'); $table = new flexible_table('mod-assignment-submissions'); $table->define_columns($tablecolumns); $table->define_headers($tableheaders); $table->define_baseurl($CFG->wwwroot.'/mod/assignment/submissions.php?id='.$this->cm->id.'&currentgroup='.$currentgroup); $table->sortable(true, 'lastname');//sorted by lastname by default $table->collapsible(true); $table->initialbars(true); $table->column_suppress('picture'); $table->column_suppress('fullname'); $table->column_class('picture', 'picture'); $table->column_class('fullname', 'fullname'); $table->column_class('grade', 'grade'); $table->column_class('submissioncomment', 'comment'); $table->column_class('timemodified', 'timemodified'); $table->column_class('timemarked', 'timemarked'); $table->column_class('status', 'status'); $table->column_class('finalgrade', 'finalgrade'); if ($uses_outcomes) { $table->column_class('outcome', 'outcome'); } $table->set_attribute('cellspacing', '0'); $table->set_attribute('id', 'attempts'); $table->set_attribute('class', 'submissions'); $table->set_attribute('width', '100%'); //$table->set_attribute('align', 'center'); $table->no_sorting('finalgrade'); $table->no_sorting('outcome'); // Start working -- this is necessary as soon as the niceties are over $table->setup(); if (empty($users)) { echo $OUTPUT->heading(get_string('nosubmitusers','assignment')); echo '</div>'; return true; } if ($this->assignment->assignmenttype=='upload' || $this->assignment->assignmenttype=='online' || $this->assignment->assignmenttype=='uploadsingle') { //TODO: this is an ugly hack, where is the plugin spirit? (skodak) echo '<div style="text-align:right"><a href="submissions.php?id='.$this->cm->id.'&download=zip">'.get_string('downloadall', 'assignment').'</a></div>'; } /// Construct the SQL list($where, $params) = $table->get_sql_where(); if ($where) { $where .= ' AND '; } if ($filter == self::FILTER_SUBMITTED) { $where .= 's.timemodified > 0 AND '; } else if($filter == self::FILTER_REQUIRE_GRADING) { $where .= 's.timemarked < s.timemodified AND '; } if ($sort = $table->get_sql_sort()) { $sort = ' ORDER BY '.$sort; } $ufields = user_picture::fields('u'); $select = "SELECT $ufields, s.id AS submissionid, s.grade, s.submissioncomment, s.timemodified, s.timemarked, COALESCE(SIGN(SIGN(s.timemarked) + SIGN(s.timemarked - s.timemodified)), 0) AS status "; $sql = 'FROM {user} u '. 'LEFT JOIN {assignment_submissions} s ON u.id = s.userid AND s.assignment = '.$this->assignment->id.' '. 'WHERE '.$where.'u.id IN ('.implode(',',$users).') '; $ausers = $DB->get_records_sql($select.$sql.$sort, $params, $table->get_page_start(), $table->get_page_size()); $table->pagesize($perpage, count($users)); ///offset used to calculate index of student in that particular query, needed for the pop up to know who's next $offset = $page * $perpage; $strupdate = get_string('update'); $strgrade = get_string('grade'); $grademenu = make_grades_menu($this->assignment->grade); if ($ausers !== false) { $grading_info = grade_get_grades($this->course->id, 'mod', 'assignment', $this->assignment->id, array_keys($ausers)); $endposition = $offset + $perpage; $currentposition = 0; foreach ($ausers as $auser) { if ($currentposition == $offset && $offset < $endposition) { $final_grade = $grading_info->items[0]->grades[$auser->id]; $grademax = $grading_info->items[0]->grademax; $final_grade->formatted_grade = round($final_grade->grade,2) .' / ' . round($grademax,2); $locked_overridden = 'locked'; if ($final_grade->overridden) { $locked_overridden = 'overridden'; } /// Calculate user status $auser->status = ($auser->timemarked > 0) && ($auser->timemarked >= $auser->timemodified); $picture = $OUTPUT->user_picture($auser); if (empty($auser->submissionid)) { $auser->grade = -1; //no submission yet } if (!empty($auser->submissionid)) { ///Prints student answer and student modified date ///attach file or print link to student answer, depending on the type of the assignment. ///Refer to print_student_answer in inherited classes. if ($auser->timemodified > 0) { $studentmodified = '<div id="ts'.$auser->id.'">'.$this->print_student_answer($auser->id) . userdate($auser->timemodified).'</div>'; } else { $studentmodified = '<div id="ts'.$auser->id.'"> </div>'; } ///Print grade, dropdown or text if ($auser->timemarked > 0) { $teachermodified = '<div id="tt'.$auser->id.'">'.userdate($auser->timemarked).'</div>'; if ($final_grade->locked or $final_grade->overridden) { $grade = '<div id="g'.$auser->id.'" class="'. $locked_overridden .'">'.$final_grade->formatted_grade.'</div>'; } else if ($quickgrade) { $attributes = array(); $attributes['tabindex'] = $tabindex++; $menu = html_writer::select(make_grades_menu($this->assignment->grade), 'menu['.$auser->id.']', $auser->grade, array(-1=>get_string('nograde')), $attributes); $grade = '<div id="g'.$auser->id.'">'. $menu .'</div>'; } else { $grade = '<div id="g'.$auser->id.'">'.$this->display_grade($auser->grade).'</div>'; } } else { $teachermodified = '<div id="tt'.$auser->id.'"> </div>'; if ($final_grade->locked or $final_grade->overridden) { $grade = '<div id="g'.$auser->id.'" class="'. $locked_overridden .'">'.$final_grade->formatted_grade.'</div>'; } else if ($quickgrade) { $attributes = array(); $attributes['tabindex'] = $tabindex++; $menu = html_writer::select(make_grades_menu($this->assignment->grade), 'menu['.$auser->id.']', $auser->grade, array(-1=>get_string('nograde')), $attributes); $grade = '<div id="g'.$auser->id.'">'.$menu.'</div>'; } else { $grade = '<div id="g'.$auser->id.'">'.$this->display_grade($auser->grade).'</div>'; } } ///Print Comment if ($final_grade->locked or $final_grade->overridden) { $comment = '<div id="com'.$auser->id.'">'.shorten_text(strip_tags($final_grade->str_feedback),15).'</div>'; } else if ($quickgrade) { $comment = '<div id="com'.$auser->id.'">' . '<textarea tabindex="'.$tabindex++.'" name="submissioncomment['.$auser->id.']" id="submissioncomment' . $auser->id.'" rows="2" cols="20">'.($auser->submissioncomment).'</textarea></div>'; } else { $comment = '<div id="com'.$auser->id.'">'.shorten_text(strip_tags($auser->submissioncomment),15).'</div>'; } } else { $studentmodified = '<div id="ts'.$auser->id.'"> </div>'; $teachermodified = '<div id="tt'.$auser->id.'"> </div>'; $status = '<div id="st'.$auser->id.'"> </div>'; if ($final_grade->locked or $final_grade->overridden) { $grade = '<div id="g'.$auser->id.'">'.$final_grade->formatted_grade . '</div>'; } else if ($quickgrade) { // allow editing $attributes = array(); $attributes['tabindex'] = $tabindex++; $menu = html_writer::select(make_grades_menu($this->assignment->grade), 'menu['.$auser->id.']', $auser->grade, array(-1=>get_string('nograde')), $attributes); $grade = '<div id="g'.$auser->id.'">'.$menu.'</div>'; } else { $grade = '<div id="g'.$auser->id.'">-</div>'; } if ($final_grade->locked or $final_grade->overridden) { $comment = '<div id="com'.$auser->id.'">'.$final_grade->str_feedback.'</div>'; } else if ($quickgrade) { $comment = '<div id="com'.$auser->id.'">' . '<textarea tabindex="'.$tabindex++.'" name="submissioncomment['.$auser->id.']" id="submissioncomment' . $auser->id.'" rows="2" cols="20">'.($auser->submissioncomment).'</textarea></div>'; } else { $comment = '<div id="com'.$auser->id.'"> </div>'; } } if (empty($auser->status)) { /// Confirm we have exclusively 0 or 1 $auser->status = 0; } else { $auser->status = 1; } $buttontext = ($auser->status == 1) ? $strupdate : $strgrade; ///No more buttons, we use popups ;-). $popup_url = '/mod/assignment/submissions.php?id='.$this->cm->id . '&userid='.$auser->id.'&mode=single'.'&filter='.$filter.'&offset='.$offset++; $button = $OUTPUT->action_link($popup_url, $buttontext); $status = '<div id="up'.$auser->id.'" class="s'.$auser->status.'">'.$button.'</div>'; $finalgrade = '<span id="finalgrade_'.$auser->id.'">'.$final_grade->str_grade.'</span>'; $outcomes = ''; if ($uses_outcomes) { foreach($grading_info->outcomes as $n=>$outcome) { $outcomes .= '<div class="outcome"><label>'.$outcome->name.'</label>'; $options = make_grades_menu(-$outcome->scaleid); if ($outcome->grades[$auser->id]->locked or !$quickgrade) { $options[0] = get_string('nooutcome', 'grades'); $outcomes .= ': <span id="outcome_'.$n.'_'.$auser->id.'">'.$options[$outcome->grades[$auser->id]->grade].'</span>'; } else { $attributes = array(); $attributes['tabindex'] = $tabindex++; $attributes['id'] = 'outcome_'.$n.'_'.$auser->id; $outcomes .= ' '.html_writer::select($options, 'outcome_'.$n.'['.$auser->id.']', $outcome->grades[$auser->id]->grade, array(0=>get_string('nooutcome', 'grades')), $attributes); } $outcomes .= '</div>'; } } $userlink = '<a href="' . $CFG->wwwroot . '/user/view.php?id=' . $auser->id . '&course=' . $course->id . '">' . fullname($auser, has_capability('moodle/site:viewfullnames', $this->context)) . '</a>'; $row = array($picture, $userlink, $grade, $comment, $studentmodified, $teachermodified, $status, $finalgrade); if ($uses_outcomes) { $row[] = $outcomes; } $table->add_data($row); } $currentposition++; } } $table->print_html(); /// Print the whole table /// Print quickgrade form around the table if ($quickgrade && $table->started_output){ $mailinfopref = false; if (get_user_preferences('assignment_mailinfo', 1)) { $mailinfopref = true; } $emailnotification = html_writer::checkbox('mailinfo', 1, $mailinfopref, get_string('enableemailnotification','assignment')); $emailnotification .= $OUTPUT->help_icon('enableemailnotification', 'assignment'); echo html_writer::tag('div', $emailnotification, array('class'=>'emailnotification')); $savefeedback = html_writer::empty_tag('input', array('type'=>'submit', 'name'=>'fastg', 'value'=>get_string('saveallfeedback', 'assignment'))); echo html_writer::tag('div', $savefeedback, array('class'=>'fastgbutton')); echo html_writer::end_tag('form'); } else if ($quickgrade) { echo html_writer::end_tag('form'); } echo '</div>'; /// End of fast grading form /// Mini form for setting user preference $formaction = new moodle_url('/mod/assignment/submissions.php', array('id'=>$this->cm->id)); $mform = new MoodleQuickForm('optionspref', 'post', $formaction, '', array('class'=>'optionspref')); $mform->addElement('hidden', 'updatepref'); $mform->setDefault('updatepref', 1); $mform->addElement('header', 'qgprefs', get_string('optionalsettings', 'assignment')); $mform->addElement('select', 'filter', get_string('show'), $filters); $mform->setDefault('filter', $filter); $mform->addElement('text', 'perpage', get_string('pagesize', 'assignment'), array('size'=>1)); $mform->setDefault('perpage', $perpage); $mform->addElement('checkbox', 'quickgrade', get_string('quickgrade','assignment')); $mform->setDefault('quickgrade', $quickgrade); $mform->addHelpButton('quickgrade', 'quickgrade', 'assignment'); $mform->addElement('submit', 'savepreferences', get_string('savepreferences')); $mform->display(); echo $OUTPUT->footer(); }
/** * Enrol all not enrolled cohort members into course via enrol instance. * * @param stdClass $instance * @param int $cohortid * @param int $roleid optional role id * @param int $timestart 0 means unknown * @param int $timeend 0 means forever * @param int $status default to ENROL_USER_ACTIVE for new enrolments, no change by default in updates * @param bool $recovergrades restore grade history */ public function enrol_cohort(stdClass $instance, $cohortid, $roleid = null, $timestart = 0, $timeend = 0, $status = null, $recovergrades = null) { global $DB; $context = context_course::instance($instance->courseid); list($esql, $params) = get_enrolled_sql($context); $sql = "SELECT cm.userid FROM {cohort_members} cm LEFT JOIN ({$esql}) u ON u.id = cm.userid " . "WHERE cm.cohortid = :cohortid AND u.id IS NULL"; $params['cohortid'] = $cohortid; $members = $DB->get_fieldset_sql($sql, $params); foreach ($members as $userid) { $this->enrol_user($instance, $userid, $roleid, $timestart, $timeend, $status, $recovergrades); } }
public function find_users($search) { global $DB; // Get list of allowed roles. $context = get_context_instance(CONTEXT_COURSE, $this->courseid); if ($validroleids = groups_get_possible_roles($context)) { list($roleids, $roleparams) = $DB->get_in_or_equal($validroleids, SQL_PARAMS_NAMED, 'r'); } else { $roleids = " = -1"; $roleparams = array(); } // Get the search condition. list($searchcondition, $searchparams) = $this->search_sql($search, 'u'); // Build the SQL list($enrolsql, $enrolparams) = get_enrolled_sql($context); $fields = "SELECT r.id AS roleid, r.shortname AS roleshortname, r.name AS rolename, u.id AS userid,\n " . $this->required_fields_sql('u') . ",\n (SELECT count(igm.groupid)\n FROM {groups_members} igm\n JOIN {groups} ig ON igm.groupid = ig.id\n WHERE igm.userid = u.id AND ig.courseid = :courseid) AS numgroups"; $sql = " FROM {user} u\n JOIN ({$enrolsql}) e ON e.id = u.id\n LEFT JOIN {role_assignments} ra ON (ra.userid = u.id AND ra.contextid " . get_related_contexts_string($context) . " AND ra.roleid {$roleids})\n LEFT JOIN {role} r ON r.id = ra.roleid\n WHERE u.deleted = 0\n AND u.id NOT IN (SELECT userid\n FROM {groups_members}\n WHERE groupid = :groupid)\n AND {$searchcondition}"; $orderby = "ORDER BY u.lastname, u.firstname"; $params = array_merge($searchparams, $roleparams, $enrolparams); $params['courseid'] = $this->courseid; $params['groupid'] = $this->groupid; if (!$this->is_validating()) { $potentialmemberscount = $DB->count_records_sql("SELECT COUNT(DISTINCT u.id) {$sql}", $params); if ($potentialmemberscount > group_non_members_selector::MAX_USERS_PER_PAGE) { return $this->too_many_results($search, $potentialmemberscount); } } $rs = $DB->get_recordset_sql("{$fields} {$sql} {$orderby}", $params); $roles = groups_calculate_role_people($rs, $context); //don't hold onto user IDs if we're doing validation if (empty($this->validatinguserids)) { if ($roles) { foreach ($roles as $k => $v) { if ($v) { foreach ($v->users as $uid => $userobject) { $this->potentialmembersids[] = $uid; } } } } } return $this->convert_array_format($roles, $search); }
public function find_users($search) { global $DB; // Get list of allowed roles. $context = context_course::instance($this->courseid); if ($validroleids = groups_get_possible_roles($context)) { list($roleids, $roleparams) = $DB->get_in_or_equal($validroleids, SQL_PARAMS_NAMED, 'r'); } else { $roleids = " = -1"; $roleparams = array(); } // We want to query both the current context and parent contexts. list($relatedctxsql, $relatedctxparams) = $DB->get_in_or_equal($context->get_parent_context_ids(true), SQL_PARAMS_NAMED, 'relatedctx'); // Get the search condition. list($searchcondition, $searchparams) = $this->search_sql($search, 'u'); // Build the SQL list($enrolsql, $enrolparams) = get_enrolled_sql($context); $fields = "SELECT r.id AS roleid, u.id AS userid,\n " . $this->required_fields_sql('u') . ",\n (SELECT count(igm.groupid)\n FROM {groups_members} igm\n JOIN {groups} ig ON igm.groupid = ig.id\n WHERE igm.userid = u.id AND ig.courseid = :courseid) AS numgroups"; $sql = " FROM {user} u\n JOIN ({$enrolsql}) e ON e.id = u.id\n LEFT JOIN {role_assignments} ra ON (ra.userid = u.id AND ra.contextid {$relatedctxsql} AND ra.roleid {$roleids})\n LEFT JOIN {role} r ON r.id = ra.roleid\n LEFT JOIN {groups_members} gm ON (gm.userid = u.id AND gm.groupid = :groupid)\n WHERE u.deleted = 0\n AND gm.id IS NULL\n AND {$searchcondition}"; list($sort, $sortparams) = users_order_by_sql('u', $search, $this->accesscontext); $orderby = ' ORDER BY ' . $sort; $params = array_merge($searchparams, $roleparams, $enrolparams, $relatedctxparams); $params['courseid'] = $this->courseid; $params['groupid'] = $this->groupid; if (!$this->is_validating()) { $potentialmemberscount = $DB->count_records_sql("SELECT COUNT(DISTINCT u.id) {$sql}", $params); if ($potentialmemberscount > $this->maxusersperpage) { return $this->too_many_results($search, $potentialmemberscount); } } $rs = $DB->get_recordset_sql("{$fields} {$sql} {$orderby}", array_merge($params, $sortparams)); $roles = groups_calculate_role_people($rs, $context); //don't hold onto user IDs if we're doing validation if (empty($this->validatinguserids)) { if ($roles) { foreach ($roles as $k => $v) { if ($v) { foreach ($v->users as $uid => $userobject) { $this->potentialmembersids[] = $uid; } } } } } return $this->convert_array_format($roles, $search); }
/** * Initialise the iterator * * @return boolean success */ public function init() { global $CFG, $DB; $this->close(); export_verify_grades($this->course->id); $course_item = grade_item::fetch_course_item($this->course->id); if ($course_item->needsupdate) { // Can not calculate all final grades - sorry. return false; } $coursecontext = context_course::instance($this->course->id); list($relatedctxsql, $relatedctxparams) = $DB->get_in_or_equal($coursecontext->get_parent_context_ids(true), SQL_PARAMS_NAMED, 'relatedctx'); list($gradebookroles_sql, $params) = $DB->get_in_or_equal(explode(',', $CFG->gradebookroles), SQL_PARAMS_NAMED, 'grbr'); list($enrolledsql, $enrolledparams) = get_enrolled_sql($coursecontext, '', 0, $this->onlyactive); $params = array_merge($params, $enrolledparams, $relatedctxparams); if ($this->groupid) { $groupsql = "INNER JOIN {groups_members} gm ON gm.userid = u.id"; $groupwheresql = "AND gm.groupid = :groupid"; // $params contents: gradebookroles $params['groupid'] = $this->groupid; } else { $groupsql = ""; $groupwheresql = ""; } if (empty($this->sortfield1)) { // We must do some sorting even if not specified. $ofields = ", u.id AS usrt"; $order = "usrt ASC"; } else { $ofields = ", u.{$this->sortfield1} AS usrt1"; $order = "usrt1 {$this->sortorder1}"; if (!empty($this->sortfield2)) { $ofields .= ", u.{$this->sortfield2} AS usrt2"; $order .= ", usrt2 {$this->sortorder2}"; } if ($this->sortfield1 != 'id' and $this->sortfield2 != 'id') { // User order MUST be the same in both queries, // must include the only unique user->id if not already present. $ofields .= ", u.id AS usrt"; $order .= ", usrt ASC"; } } $userfields = 'u.*'; $customfieldssql = ''; if ($this->allowusercustomfields && !empty($CFG->grade_export_customprofilefields)) { $customfieldscount = 0; $customfieldsarray = grade_helper::get_user_profile_fields($this->course->id, $this->allowusercustomfields); foreach ($customfieldsarray as $field) { if (!empty($field->customid)) { $customfieldssql .= "\n LEFT JOIN (SELECT * FROM {user_info_data}\n WHERE fieldid = :cf{$customfieldscount}) cf{$customfieldscount}\n ON u.id = cf{$customfieldscount}.userid"; $userfields .= ", cf{$customfieldscount}.data AS customfield_{$field->shortname}"; $params['cf' . $customfieldscount] = $field->customid; $customfieldscount++; } } } $users_sql = "SELECT {$userfields} {$ofields}\n FROM {user} u\n JOIN ({$enrolledsql}) je ON je.id = u.id\n {$groupsql} {$customfieldssql}\n JOIN (\n SELECT DISTINCT ra.userid\n FROM {role_assignments} ra\n WHERE ra.roleid {$gradebookroles_sql}\n AND ra.contextid {$relatedctxsql}\n ) rainner ON rainner.userid = u.id\n WHERE u.deleted = 0\n {$groupwheresql}\n ORDER BY {$order}"; $this->users_rs = $DB->get_recordset_sql($users_sql, $params); if (!$this->onlyactive) { $context = context_course::instance($this->course->id); $this->suspendedusers = get_suspended_userids($context); } else { $this->suspendedusers = array(); } if (!empty($this->grade_items)) { $itemids = array_keys($this->grade_items); list($itemidsql, $grades_params) = $DB->get_in_or_equal($itemids, SQL_PARAMS_NAMED, 'items'); $params = array_merge($params, $grades_params); $grades_sql = "SELECT g.* {$ofields}\n FROM {grade_grades} g\n JOIN {user} u ON g.userid = u.id\n JOIN ({$enrolledsql}) je ON je.id = u.id\n {$groupsql}\n JOIN (\n SELECT DISTINCT ra.userid\n FROM {role_assignments} ra\n WHERE ra.roleid {$gradebookroles_sql}\n AND ra.contextid {$relatedctxsql}\n ) rainner ON rainner.userid = u.id\n WHERE u.deleted = 0\n AND g.itemid {$itemidsql}\n {$groupwheresql}\n ORDER BY {$order}, g.itemid ASC"; $this->grades_rs = $DB->get_recordset_sql($grades_sql, $params); } else { $this->grades_rs = false; } return true; }
/** * Returns a list of teachers that should be grading given submission * * @param object $user * @return array */ function get_graders($user) { global $DB; //potential graders list($enrolledsql, $params) = get_enrolled_sql($this->context, 'mod/assignment:grade', 0, true); $sql = "SELECT u.*\n FROM {user} u\n JOIN ({$enrolledsql}) je ON je.id = u.id"; $potgraders = $DB->get_records_sql($sql, $params); $graders = array(); if (groups_get_activity_groupmode($this->cm) == SEPARATEGROUPS) { // Separate groups are being used if ($groups = groups_get_all_groups($this->course->id, $user->id)) { // Try to find all groups foreach ($groups as $group) { foreach ($potgraders as $t) { if ($t->id == $user->id) { continue; // do not send self } if (groups_is_member($group->id, $t->id)) { $graders[$t->id] = $t; } } } } else { // user not in group, try to find graders without group foreach ($potgraders as $t) { if ($t->id == $user->id) { continue; // do not send self } if (!groups_get_all_groups($this->course->id, $t->id)) { //ugly hack $graders[$t->id] = $t; } } } } else { foreach ($potgraders as $t) { if ($t->id == $user->id) { continue; // do not send self } $graders[$t->id] = $t; } } return $graders; }
/** * Counts list of users enrolled into course (as per above function) * * @param context $context * @param string $withcapability * @param int $groupid 0 means ignore groups, any other value limits the result by group id * @return array of user records */ function count_enrolled_users(context $context, $withcapability = '', $groupid = 0) { global $DB; list($esql, $params) = get_enrolled_sql($context, $withcapability, $groupid); $sql = "SELECT count(u.id)\n FROM {user} u\n JOIN ({$esql}) je ON je.id = u.id\n WHERE u.deleted = 0"; return $DB->count_records_sql($sql, $params); }
/** * Obtains a list of everybody who has read this discussion (only works * if the discussion is within the 'read' period). The list is in date order * (most recent first). Each returned item has ->time (time last read) and * ->user (Moodle user object) fields. * @param int $groupid Group ID or mod_forumng::ALL_GROUPS * @return array Array of information about readers * @throws coding_exception If you try to call it in a shared forum (not supported) */ public function get_readers($groupid = mod_forumng::ALL_GROUPS) { global $DB; if ($this->get_forum()->is_shared()) { throw new coding_exception('get_readers not supported in shared forums'); } list($sql, $params) = get_enrolled_sql($this->get_forum()->get_context(), '', $groupid ? $groupid : 0, true); $now = round(time(), -2); $params['discussionid'] = $this->discussionfields->id; $result = $DB->get_records_sql($sql = "\nSELECT\n fr.id,\n " . mod_forumng_utils::select_username_fields('u', false) . ",\n fr.time,\n u.idnumber AS u_idnumber\nFROM\n {forumng_read} fr\n INNER JOIN {user} u ON u.id = fr.userid\nWHERE\n fr.userid IN ({$sql})\n AND fr.discussionid = :discussionid\nORDER BY\n fr.time DESC", $params); foreach ($result as $item) { $item->user = mod_forumng_utils::extract_subobject($item, 'u_'); } return $result; }