/** * An array of forum objects that the user is allowed to read/search through. * * @global object * @global object * @global object * @param int $userid * @param int $courseid if 0, we look for forums throughout the whole site. * @return array of forum objects, or false if no matches * Forum objects have the following attributes: * id, type, course, cmid, cmvisible, cmgroupmode, accessallgroups, * viewhiddentimedposts */ function forum_get_readable_forums($userid, $courseid=0) { global $CFG, $DB, $USER; require_once($CFG->dirroot.'/course/lib.php'); if (!$forummod = $DB->get_record('modules', array('name' => 'forum'))) { print_error('notinstalled', 'forum'); } if ($courseid) { $courses = $DB->get_records('course', array('id' => $courseid)); } else { // If no course is specified, then the user can see SITE + his courses. $courses1 = $DB->get_records('course', array('id' => SITEID)); $courses2 = enrol_get_users_courses($userid, true, array('modinfo')); $courses = array_merge($courses1, $courses2); } if (!$courses) { return array(); } $readableforums = array(); foreach ($courses as $course) { $modinfo =& get_fast_modinfo($course); if (is_null($modinfo->groups)) { $modinfo->groups = groups_get_user_groups($course->id, $userid); } if (empty($modinfo->instances['forum'])) { // hmm, no forums? continue; } $courseforums = $DB->get_records('forum', array('course' => $course->id)); foreach ($modinfo->instances['forum'] as $forumid => $cm) { if (!$cm->uservisible or !isset($courseforums[$forumid])) { continue; } $context = get_context_instance(CONTEXT_MODULE, $cm->id); $forum = $courseforums[$forumid]; $forum->context = $context; $forum->cm = $cm; if (!has_capability('mod/forum:viewdiscussion', $context)) { continue; } /// group access if (groups_get_activity_groupmode($cm, $course) == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) { if (is_null($modinfo->groups)) { $modinfo->groups = groups_get_user_groups($course->id, $USER->id); } if (isset($modinfo->groups[$cm->groupingid])) { $forum->onlygroups = $modinfo->groups[$cm->groupingid]; $forum->onlygroups[] = -1; } else { $forum->onlygroups = array(-1); } } /// hidden timed discussions $forum->viewhiddentimedposts = true; if (!empty($CFG->forum_enabletimedposts)) { if (!has_capability('mod/forum:viewhiddentimedposts', $context)) { $forum->viewhiddentimedposts = false; } } /// qanda access if ($forum->type == 'qanda' && !has_capability('mod/forum:viewqandawithoutposting', $context)) { // We need to check whether the user has posted in the qanda forum. $forum->onlydiscussions = array(); // Holds discussion ids for the discussions // the user is allowed to see in this forum. if ($discussionspostedin = forum_discussions_user_has_posted_in($forum->id, $USER->id)) { foreach ($discussionspostedin as $d) { $forum->onlydiscussions[] = $d->id; } } } $readableforums[$forum->id] = $forum; } unset($modinfo); } // End foreach $courses return $readableforums; }
/** * Returns posts made by the selected user in the requested courses. * * This method can be used to return all of the posts made by the requested user * within the given courses. * For each course the access of the current user and requested user is checked * and then for each post access to the post and forum is checked as well. * * This function is safe to use with usercapabilities. * * @global moodle_database $DB * @param stdClass $user The user whose posts we want to get * @param array $courses The courses to search * @param bool $musthaveaccess If set to true errors will be thrown if the user * cannot access one or more of the courses to search * @param bool $discussionsonly If set to true only discussion starting posts * will be returned. * @param int $limitfrom The offset of records to return * @param int $limitnum The number of records to return * @return stdClass An object the following properties * ->totalcount: the total number of posts made by the requested user * that the current user can see. * ->courses: An array of courses the current user can see that the * requested user has posted in. * ->forums: An array of forums relating to the posts returned in the * property below. * ->posts: An array containing the posts to show for this request. */ function forum_get_posts_by_user($user, array $courses, $musthaveaccess = false, $discussionsonly = false, $limitfrom = 0, $limitnum = 50) { global $DB, $USER, $CFG; $return = new stdClass; $return->totalcount = 0; // The total number of posts that the current user is able to view $return->courses = array(); // The courses the current user can access $return->forums = array(); // The forums that the current user can access that contain posts $return->posts = array(); // The posts to display // First up a small sanity check. If there are no courses to check we can // return immediately, there is obviously nothing to search. if (empty($courses)) { return $return; } // A couple of quick setups $isloggedin = isloggedin(); $isguestuser = $isloggedin && isguestuser(); $iscurrentuser = $isloggedin && $USER->id == $user->id; // Checkout whether or not the current user has capabilities over the requested // user and if so they have the capabilities required to view the requested // users content. $usercontext = context_user::instance($user->id, MUST_EXIST); $hascapsonuser = !$iscurrentuser && $DB->record_exists('role_assignments', array('userid' => $USER->id, 'contextid' => $usercontext->id)); $hascapsonuser = $hascapsonuser && has_all_capabilities(array('moodle/user:viewdetails', 'moodle/user:readuserposts'), $usercontext); // Before we actually search each course we need to check the user's access to the // course. If the user doesn't have the appropraite access then we either throw an // error if a particular course was requested or we just skip over the course. foreach ($courses as $course) { $coursecontext = context_course::instance($course->id, MUST_EXIST); if ($iscurrentuser || $hascapsonuser) { // If it is the current user, or the current user has capabilities to the // requested user then all we need to do is check the requested users // current access to the course. // Note: There is no need to check group access or anything of the like // as either the current user is the requested user, or has granted // capabilities on the requested user. Either way they can see what the // requested user posted, although its VERY unlikely in the `parent` situation // that the current user will be able to view the posts in context. if (!is_viewing($coursecontext, $user) && !is_enrolled($coursecontext, $user)) { // Need to have full access to a course to see the rest of own info if ($musthaveaccess) { print_error('errorenrolmentrequired', 'forum'); } continue; } } else { // Check whether the current user is enrolled or has access to view the course // if they don't we immediately have a problem. if (!can_access_course($course)) { if ($musthaveaccess) { print_error('errorenrolmentrequired', 'forum'); } continue; } // Check whether the requested user is enrolled or has access to view the course // if they don't we immediately have a problem. if (!can_access_course($course, $user)) { if ($musthaveaccess) { print_error('notenrolled', 'forum'); } continue; } // If groups are in use and enforced throughout the course then make sure // we can meet in at least one course level group. // Note that we check if either the current user or the requested user have // the capability to access all groups. This is because with that capability // a user in group A could post in the group B forum. Grrrr. if (groups_get_course_groupmode($course) == SEPARATEGROUPS && $course->groupmodeforce && !has_capability('moodle/site:accessallgroups', $coursecontext) && !has_capability('moodle/site:accessallgroups', $coursecontext, $user->id)) { // If its the guest user to bad... the guest user cannot access groups if (!$isloggedin or $isguestuser) { // do not use require_login() here because we might have already used require_login($course) if ($musthaveaccess) { redirect(get_login_url()); } continue; } // Get the groups of the current user $mygroups = array_keys(groups_get_all_groups($course->id, $USER->id, $course->defaultgroupingid, 'g.id, g.name')); // Get the groups the requested user is a member of $usergroups = array_keys(groups_get_all_groups($course->id, $user->id, $course->defaultgroupingid, 'g.id, g.name')); // Check whether they are members of the same group. If they are great. $intersect = array_intersect($mygroups, $usergroups); if (empty($intersect)) { // But they're not... if it was a specific course throw an error otherwise // just skip this course so that it is not searched. if ($musthaveaccess) { print_error("groupnotamember", '', $CFG->wwwroot."/course/view.php?id=$course->id"); } continue; } } } // Woo hoo we got this far which means the current user can search this // this course for the requested user. Although this is only the course accessibility // handling that is complete, the forum accessibility tests are yet to come. $return->courses[$course->id] = $course; } // No longer beed $courses array - lose it not it may be big unset($courses); // Make sure that we have some courses to search if (empty($return->courses)) { // If we don't have any courses to search then the reality is that the current // user doesn't have access to any courses is which the requested user has posted. // Although we do know at this point that the requested user has posts. if ($musthaveaccess) { print_error('permissiondenied'); } else { return $return; } } // Next step: Collect all of the forums that we will want to search. // It is important to note that this step isn't actually about searching, it is // about determining which forums we can search by testing accessibility. $forums = forum_get_forums_user_posted_in($user, array_keys($return->courses), $discussionsonly); // Will be used to build the where conditions for the search $forumsearchwhere = array(); // Will be used to store the where condition params for the search $forumsearchparams = array(); // Will record forums where the user can freely access everything $forumsearchfullaccess = array(); // DB caching friendly $now = round(time(), -2); // For each course to search we want to find the forums the user has posted in // and providing the current user can access the forum create a search condition // for the forum to get the requested users posts. foreach ($return->courses as $course) { // Now we need to get the forums $modinfo = get_fast_modinfo($course); if (empty($modinfo->instances['forum'])) { // hmmm, no forums? well at least its easy... skip! continue; } // Iterate foreach ($modinfo->get_instances_of('forum') as $forumid => $cm) { if (!$cm->uservisible or !isset($forums[$forumid])) { continue; } // Get the forum in question $forum = $forums[$forumid]; // This is needed for functionality later on in the forum code.... $forum->cm = $cm; // Check that either the current user can view the forum, or that the // current user has capabilities over the requested user and the requested // user can view the discussion if (!has_capability('mod/forum:viewdiscussion', $cm->context) && !($hascapsonuser && has_capability('mod/forum:viewdiscussion', $cm->context, $user->id))) { continue; } // This will contain forum specific where clauses $forumsearchselect = array(); if (!$iscurrentuser && !$hascapsonuser) { // Make sure we check group access if (groups_get_activity_groupmode($cm, $course) == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $cm->context)) { $groups = $modinfo->get_groups($cm->groupingid); $groups[] = -1; list($groupid_sql, $groupid_params) = $DB->get_in_or_equal($groups, SQL_PARAMS_NAMED, 'grps'.$forumid.'_'); $forumsearchparams = array_merge($forumsearchparams, $groupid_params); $forumsearchselect[] = "d.groupid $groupid_sql"; } // hidden timed discussions if (!empty($CFG->forum_enabletimedposts) && !has_capability('mod/forum:viewhiddentimedposts', $cm->context)) { $forumsearchselect[] = "(d.userid = :userid{$forumid} OR (d.timestart < :timestart{$forumid} AND (d.timeend = 0 OR d.timeend > :timeend{$forumid})))"; $forumsearchparams['userid'.$forumid] = $user->id; $forumsearchparams['timestart'.$forumid] = $now; $forumsearchparams['timeend'.$forumid] = $now; } // qanda access if ($forum->type == 'qanda' && !has_capability('mod/forum:viewqandawithoutposting', $cm->context)) { // We need to check whether the user has posted in the qanda forum. $discussionspostedin = forum_discussions_user_has_posted_in($forum->id, $user->id); if (!empty($discussionspostedin)) { $forumonlydiscussions = array(); // Holds discussion ids for the discussions the user is allowed to see in this forum. foreach ($discussionspostedin as $d) { $forumonlydiscussions[] = $d->id; } list($discussionid_sql, $discussionid_params) = $DB->get_in_or_equal($forumonlydiscussions, SQL_PARAMS_NAMED, 'qanda'.$forumid.'_'); $forumsearchparams = array_merge($forumsearchparams, $discussionid_params); $forumsearchselect[] = "(d.id $discussionid_sql OR p.parent = 0)"; } else { $forumsearchselect[] = "p.parent = 0"; } } if (count($forumsearchselect) > 0) { $forumsearchwhere[] = "(d.forum = :forum{$forumid} AND ".implode(" AND ", $forumsearchselect).")"; $forumsearchparams['forum'.$forumid] = $forumid; } else { $forumsearchfullaccess[] = $forumid; } } else { // The current user/parent can see all of their own posts $forumsearchfullaccess[] = $forumid; } } } // If we dont have any search conditions, and we don't have any forums where // the user has full access then we just return the default. if (empty($forumsearchwhere) && empty($forumsearchfullaccess)) { return $return; } // Prepare a where condition for the full access forums. if (count($forumsearchfullaccess) > 0) { list($fullidsql, $fullidparams) = $DB->get_in_or_equal($forumsearchfullaccess, SQL_PARAMS_NAMED, 'fula'); $forumsearchparams = array_merge($forumsearchparams, $fullidparams); $forumsearchwhere[] = "(d.forum $fullidsql)"; } // Prepare SQL to both count and search. // We alias user.id to useridx because we forum_posts already has a userid field and not aliasing this would break // oracle and mssql. $userfields = user_picture::fields('u', null, 'useridx'); $countsql = 'SELECT COUNT(*) '; $selectsql = 'SELECT p.*, d.forum, d.name AS discussionname, '.$userfields.' '; $wheresql = implode(" OR ", $forumsearchwhere); if ($discussionsonly) { if ($wheresql == '') { $wheresql = 'p.parent = 0'; } else { $wheresql = 'p.parent = 0 AND ('.$wheresql.')'; } } $sql = "FROM {forum_posts} p JOIN {forum_discussions} d ON d.id = p.discussion JOIN {user} u ON u.id = p.userid WHERE ($wheresql) AND p.userid = :userid "; $orderby = "ORDER BY p.modified DESC"; $forumsearchparams['userid'] = $user->id; // Set the total number posts made by the requested user that the current user can see $return->totalcount = $DB->count_records_sql($countsql.$sql, $forumsearchparams); // Set the collection of posts that has been requested $return->posts = $DB->get_records_sql($selectsql.$sql.$orderby, $forumsearchparams, $limitfrom, $limitnum); // We need to build an array of forums for which posts will be displayed. // We do this here to save the caller needing to retrieve them themselves before // printing these forums posts. Given we have the forums already there is // practically no overhead here. foreach ($return->posts as $post) { if (!array_key_exists($post->forum, $return->forums)) { $return->forums[$post->forum] = $forums[$post->forum]; } } return $return; }
/** * An array of forum objects that the user is allowed to read/search through. * @param $userid * @param $courseid - if 0, we look for forums throughout the whole site. * @return array of forum objects, or false if no matches * Forum objects have the following attributes: * id, type, course, cmid, cmvisible, cmgroupmode, accessallgroups, * viewhiddentimedposts */ function forum_get_readable_forums($userid, $courseid = 0) { global $CFG, $USER; if (!($forummod = get_record('modules', 'name', 'forum'))) { error('The forum module is not installed'); } if ($courseid) { $courses = get_records('course', 'id', $courseid); } else { // If no course is specified, then the user can see SITE + his courses. // And admins can see all courses, so pass the $doanything flag enabled $courses1 = get_records('course', 'id', SITEID); $courses2 = get_my_courses($userid, null, null, true); $courses = array_merge($courses1, $courses2); } if (!$courses) { return false; } $readableforums = array(); foreach ($courses as $course) { $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id); if (!has_capability('moodle/course:viewhiddenactivities', $coursecontext)) { $selecthidden = ' AND cm.visible = 1'; } else { $selecthidden = ''; } $selectforums = "SELECT f.id AS id,\n f.name AS name,\n f.type AS type,\n f.course AS course,\n cm.id AS cmid,\n cm.visible AS cmvisible,\n cm.groupmode AS cmgroupmode,\n cm.groupingid AS cmgroupingid,\n cm.groupmembersonly AS cmgroupmembersonly\n FROM {$CFG->prefix}course_modules cm,\n {$CFG->prefix}forum f\n WHERE cm.instance = f.id\n AND cm.course = {$course->id}\n AND cm.module = {$forummod->id}\n {$selecthidden}\n ORDER BY f.name ASC"; if ($forums = get_records_sql($selectforums)) { $groups = array(); if ($group = groups_get_all_groups($course->id, $userid)) { foreach ($group as $grp) { if (isset($grp->id)) { $groups[] = $grp->id; } } } foreach ($forums as $forum) { $forumcontext = get_context_instance(CONTEXT_MODULE, $forum->cmid); if (has_capability('mod/forum:viewdiscussion', $forumcontext)) { // Evaluate groupmode. $cm = new object(); $cm->id = $forum->cmid; $cm->groupmode = $forum->cmgroupmode; $cm->groupingid = $forum->cmgroupingid; $cm->groupmembersonly = $forum->cmgroupmembersonly; $cm->course = $forum->course; $forum->cmgroupmode = groups_get_activity_groupmode($cm); if (!groups_course_module_visible($cm)) { continue; } if ($forum->cmgroupmode == SEPARATEGROUPS && !has_capability('moodle/site:accessallgroups', $forumcontext)) { $forum->accessallgroups = false; $forum->accessgroup = $groups; // The user can only access // discussions for this group. } else { $forum->accessallgroups = true; } $forum->viewhiddentimedposts = has_capability('mod/forum:viewhiddentimedposts', $forumcontext); if ($forum->type == 'qanda' && !has_capability('mod/forum:viewqandawithoutposting', $forumcontext)) { // We need to check whether the user has posted in the qanda forum. $forum->onlydiscussions = array(); // Holds discussion ids for the discussions // the user is allowed to see in this forum. if ($discussionspostedin = forum_discussions_user_has_posted_in($forum->id, $USER->id)) { foreach ($discussionspostedin as $d) { array_push($forum->onlydiscussions, $d->id); } } } array_push($readableforums, $forum); } } } } // End foreach $courses //print_object($courses); //print_object($readableforums); return $readableforums; }