/** * Obtains a search document given the ousearch parameters. * @param object $document Object containing fields from the ousearch documents table * @return mixed False if object can't be found, otherwise object containing the following * fields: ->content, ->title, ->url, ->activityname, ->activityurl, * and optionally ->extrastrings array, ->data, ->hide */ public static function search_get_page($document) { global $DB, $CFG, $USER; // Implemented directly in SQL for performance, rather than using the // objects themselves $result = $DB->get_record_sql("\nSELECT\n fp.message AS content, fp.subject, firstpost.subject AS firstpostsubject,\n firstpost.id AS firstpostid, fd.id AS discussionid,\n f.name AS activityname, cm.id AS cmid, fd.timestart, fd.timeend,\n f.shared AS shared, f.type AS forumtype\nFROM\n {forumng_posts} fp\n INNER JOIN {forumng_discussions} fd ON fd.id = fp.discussionid\n INNER JOIN {forumng_posts} firstpost ON fd.postid = firstpost.id\n INNER JOIN {forumng} f ON fd.forumngid = f.id\n INNER JOIN {course_modules} cm ON cm.instance = f.id AND cm.course = f.course\n INNER JOIN {modules} m ON cm.module = m.id\nWHERE\n fp.id = ? AND m.name = 'forumng'", array($document->intref1), '*', IGNORE_MISSING); if (!$result) { return false; } // Title is either the post subject or Re: plus the discussion subject // if the post subject is blank $result->title = $result->subject; if (is_null($result->title)) { $result->title = get_string('re', 'forumng', $result->firstpostsubject); } // Link is to value in url if present, otherwise to original forum $cloneparam = $result->cmid; if ($result->shared) { global $FORUMNG_CLONE_MAP; if (!empty($FORUMNG_CLONE_MAP)) { $cloneparam = $FORUMNG_CLONE_MAP[$result->cmid]->id; $clonebit = '&clone=' . $cloneparam; } else { $clonebit = '&clone=' . ($cloneparam = optional_param('clone', $result->cmid, PARAM_INT)); } } else { $clonebit = ''; } // Work out URL to post $result->url = $CFG->wwwroot . '/mod/forumng/discuss.php?d=' . $result->discussionid . $clonebit . '#p' . $document->intref1; // Activity URL $result->activityurl = $CFG->wwwroot . '/mod/forumng/view.php?id=' . $result->cmid . $clonebit; // Hide results outside their time range (unless current user can see) $now = time(); if ($now < $result->timestart || $result->timeend && $now >= $result->timeend && !has_capability('mod/forumng:viewallposts', context_module::instance($result->cmid))) { $result->hide = true; } // Handle annoying forum types that hide discussions $type = forumngtype::get_new($result->forumtype); if ($type->has_unread_restriction()) { // TODO The name of the _unread_restriction should be _discussion_restriction. // This is going to be slow, we need to load the discussion $discussion = mod_forumng_discussion::get_from_id($result->discussionid, $cloneparam); if (!$type->can_view_discussion($discussion, $USER->id)) { $result->hide = true; } } return $result; }
/** * Internal method. Queries for a number of forums, including additional * data about unread posts etc. Returns the database result. * @param array $cmids If specified, array of course-module IDs of desired * forums * @param object $course If specified, course object * @param int $userid User ID, 0 = current user * @param int $unread Type of unread data to obtain (UNREAD_xx constant). * @param array $groups Array of group IDs to which the given user belongs * (may be null if unread data not required) * @param array $aagforums Array of forums in which the user has * 'access all groups' (may be null if unread data not required) * @param array $viewhiddenforums Array of forums in which the user has * 'view hidden discussions' (may be null if unread data not required) * @return array Array of row objects */ private static function query_forums($cmids = array(), $course = null, $userid, $unread, $groups, $aagforums, $viewhiddenforums) { global $DB, $CFG, $USER; if (!count($cmids) && !$course) { throw new coding_exception("mod_forumng::query_forums requires course id or cmids"); } if (count($cmids)) { list($in, $conditionsparams) = mod_forumng_utils::get_in_array_sql('cm.id', $cmids); $conditions = $in; } else { $conditions = "f.course = ?"; $conditionsparams = array($course->id); } $singleforum = count($cmids) == 1 ? reset($cmids) : false; list($inviewhiddenforums, $inviewhiddenforumsparams) = mod_forumng_utils::get_in_array_sql('fd.forumngid', $viewhiddenforums); list($cfdinviewhiddenforums, $inviewhiddenforumsparams) = mod_forumng_utils::get_in_array_sql('cfd.forumngid', $viewhiddenforums); // This array of additional results is used later if combining // standard results with single-forum calls. $plusresult = array(); // For read tracking, we get a count of total number of posts in // forum, and total number of read posts in the forum (this // is so we can display the number of UNread posts, but the query // works that way around because it will return 0 if no read // information is stored). if ($unread != self::UNREAD_NONE && self::enabled_read_tracking()) { // Work out when unread status ends $endtime = time() - $CFG->forumng_readafterdays * 24 * 3600; if (!$userid) { $userid = $USER->id; } list($ingroups, $ingroupsparams) = mod_forumng_utils::get_in_array_sql('fd.groupid', $groups); list($inaagforums, $inaagforumsparams) = mod_forumng_utils::get_in_array_sql('fd.forumngid', $aagforums); $restrictionsql = ''; $restrictionparams = array(); if ($singleforum) { // If it is for a single forum, get the restriction from the // forum type $forum = self::get_from_cmid($singleforum, self::CLONE_DIRECT); $type = $forum->get_type(); if ($type->has_unread_restriction()) { list($value, $restrictionparams) = $type->get_unread_restriction_sql($forum); if ($value) { $restrictionsql = 'AND ' . $value; } } } else { // When it is not for a single forum, we can only group together // results for types that do not place restrictions on the // unread count. $modinfo = get_fast_modinfo($course); $okayids = array(); if (array_key_exists('forumng', $modinfo->instances)) { foreach ($modinfo->instances['forumng'] as $info) { if (count($cmids) && !in_array($info->id, $cmids)) { continue; } $type = self::get_type_from_modinfo_info($info); if (forumngtype::get_new($type)->has_unread_restriction()) { // This one's a problem! Do it individually $problemresults = self::query_forums(array($info->id), null, $userid, $unread, $groups, $aagforums, $viewhiddenforums); foreach ($problemresults as $problemresult) { $plusresult[$problemresult->f_id] = $problemresult; } } else { $okayids[] = $info->id; } } } if (count($okayids) == 0) { // There are no 'normal' forums, so return result so far // after sorting it uasort($plusresult, 'mod_forumng::sort_mod_forumng_result'); return $plusresult; } else { // Fall through to normal calculation, but change conditions // to include only the 'normal' forums list($in, $inparams) = mod_forumng_utils::get_in_array_sql('cm.id', $okayids); $conditions .= " AND " . $in; $conditionsparams = array_merge($conditionsparams, $inparams); } } $indreadpart = ''; $indreadparms = array(); $indreadwhere = ''; // Get individual posts unread if manual read marking (on unread discussions only). if (!mod_forumng::mark_read_automatically($userid)) { $indreadpart = "INNER JOIN {forumng_posts} fp ON fp.discussionid = discussions.id\n LEFT JOIN {forumng_read_posts} frp ON frp.postid = fp.id AND frp.userid = ?"; $indreadwhere = "AND frp.id IS NULL\n AND ((fp.edituserid IS NOT NULL AND fp.edituserid <> ?)\n OR (fp.edituserid IS NULL AND fp.userid <> ?))\n AND fp.deleted = ?\n AND fp.oldversion = ?\n AND fp.modified > ?\n AND (discussions.time IS NULL OR fp.modified > discussions.time)"; $indreadparms = array($userid, $userid, $userid, 0, 0, $endtime); } // NOTE fpfirst is used only by forum types, not here $now = time(); $sharedquerypart = "\n FROM\n (SELECT fd.id, fr.time, fd.forumngid\n FROM {forumng_discussions} fd\n INNER JOIN {forumng_posts} fplast ON fd.lastpostid = fplast.id\n INNER JOIN {forumng_posts} fpfirst ON fd.postid = fpfirst.id\n LEFT JOIN {forumng_read} fr ON fd.id = fr.discussionid AND fr.userid = ?\n INNER JOIN {course_modules} cm2 ON cm2.instance = fd.forumngid\n AND cm2.module = (SELECT id FROM {modules} WHERE name = 'forumng')\n WHERE fplast.modified > ?\n AND (\n (fd.groupid IS NULL)\n OR ({$ingroups})\n OR cm2.groupmode = " . VISIBLEGROUPS . "\n OR ({$inaagforums})\n )\n AND fd.deleted = 0\n AND (\n ((fd.timestart = 0 OR fd.timestart <= ?)\n AND (fd.timeend = 0 OR fd.timeend > ? OR ({$inviewhiddenforums})))\n )\n AND ((fplast.edituserid IS NOT NULL AND fplast.edituserid <> ?)\n OR fplast.userid <> ?)\n AND (fr.time IS NULL OR fplast.modified > fr.time)\n {$restrictionsql}\n ) discussions\n {$indreadpart}\n WHERE discussions.forumngid = f.id\n {$indreadwhere}"; $sharedqueryparams = array_merge(array($userid, $endtime), $ingroupsparams, $inaagforumsparams, array($now, $now), $inviewhiddenforumsparams, array($userid, $userid), $restrictionparams, $indreadparms); // Note: There is an unusual case in which this number can // be inaccurate. It is to do with ignoring messages the user // posted. We consider a discussion as 'not unread' if the last // message is by current user. In actual fact, a discussion could // contain unread messages if messages were posted by other users // after this user viewed the forum last, but before they posted // their reply. Since this should be an infrequent occurrence I // believe this behaviour is acceptable. if ($unread == self::UNREAD_BINARY) { // Query to get 0/1 unread discussions count $readtracking = self::select_exists("SELECT 1 {$sharedquerypart}") . "AS f_hasunreaddiscussions"; $readtrackingparams = $sharedqueryparams; } else { // Query to get full unread discussions count $readtracking = "\n(SELECT\n COUNT(DISTINCT discussions.id)\n{$sharedquerypart}\n) AS f_numunreaddiscussions"; $readtrackingparams = $sharedqueryparams; } } else { $readtracking = "NULL AS numreadposts, NULL AS timeread"; $readtrackingparams = array(); } $now = time(); $orderby = "LOWER(f.name)"; // Main query. This retrieves: // - Full forum fields // - Basic course-module and course data (not whole tables) // - Discussion count // - Unread data, if enabled // - User subscription data $result = $DB->get_records_sql($sql = "\nSELECT\n " . mod_forumng_utils::select_mod_forumng_fields('f') . ",\n " . mod_forumng_utils::select_course_module_fields('cm') . ",\n " . mod_forumng_utils::select_course_fields('c') . ",\n (SELECT COUNT(1)\n FROM {forumng_discussions} cfd\n WHERE cfd.forumngid = f.id AND cfd.deleted = 0\n AND (\n ((cfd.timestart = 0 OR cfd.timestart <= ?)\n AND (cfd.timeend = 0 OR cfd.timeend > ?))\n OR ({$cfdinviewhiddenforums})\n )\n ) AS f_numdiscussions,\n {$readtracking}\nFROM\n {forumng} f\n INNER JOIN {course_modules} cm ON cm.instance = f.id\n AND cm.module = (SELECT id from {modules} WHERE name = 'forumng')\n INNER JOIN {course} c ON c.id = f.course\nWHERE\n {$conditions}\nORDER BY\n {$orderby}", array_merge(array($now, $now), $inviewhiddenforumsparams, $readtrackingparams, $conditionsparams)); if (count($plusresult) > 0) { foreach ($plusresult as $key => $value) { $result[$key] = $value; } uasort($result, 'mod_forumng::sort_mod_forumng_result'); } return $result; }
public function definition() { global $CFG, $COURSE, $DB; $mform =& $this->_form; $coursecontext = context_course::instance($COURSE->id); $forumng = $this->_instance ? $DB->get_record('forumng', array('id' => $this->_instance)) : null; $this->clone = $forumng ? $forumng->originalcmid : 0; // If this is a clone, don't show the normal form if ($this->clone) { $mform->addElement('hidden', 'name', $forumng->name); if (!empty($CFG->formatstringstriptags)) { $mform->setType('name', PARAM_TEXT); } else { $mform->setType('name', PARAM_NOTAGS); } $mform->addElement('static', 'sharedthing', '', get_string('sharedinfo', 'forumng', $CFG->wwwroot . '/course/modedit.php?update=' . $this->clone . '&return=1')); $this->shared_definition_part($coursecontext); return; } $mform->addElement('header', 'general', get_string('general', 'form')); // Forum name $mform->addElement('text', 'name', get_string('forumname', 'forumng'), array('size' => '64')); if (!empty($CFG->formatstringstriptags)) { $mform->setType('name', PARAM_TEXT); } else { $mform->setType('name', PARAM_NOTAGS); } $mform->addRule('name', null, 'required', null, 'client'); $mform->addRule('name', get_string('maximumchars', '', 255), 'maxlength', 255, 'client'); // Forum types $types = forumngtype::get_all(); $options = array(); foreach ($types as $type) { if ($type->is_user_selectable()) { $options[$type->get_id()] = $type->get_name(); } } $mform->addElement('select', 'type', get_string('forumtype', 'forumng'), $options); $mform->addHelpButton('type', 'forumtype', 'forumng'); $mform->setDefault('type', 'general'); $this->standard_intro_elements(get_string('forumintro', 'forumng')); // Subscription option displays only if enabled at site level if ($CFG->forumng_subscription == -1) { $options = mod_forumng::get_subscription_options(); $mform->addElement('select', 'subscription', get_string('subscription', 'forumng'), $options); $mform->setDefault('subscription', mod_forumng::SUBSCRIPTION_PERMITTED); $mform->addHelpButton('subscription', 'subscription', 'forumng'); } else { // Hidden element contains default value (not used anyhow) $mform->addElement('hidden', 'subscription', mod_forumng::SUBSCRIPTION_PERMITTED); $mform->setType('subscription', PARAM_INT); } // Max size of attachments $choices = get_max_upload_sizes($CFG->maxbytes, $COURSE->maxbytes); $choices[-1] = get_string('uploadnotallowed'); $mform->addElement('select', 'attachmentmaxbytes', get_string('attachmentmaxbytes', 'forumng'), $choices); $mform->addHelpButton('attachmentmaxbytes', 'attachmentmaxbytes', 'forumng'); $mform->setDefault('attachmentmaxbytes', $CFG->forumng_attachmentmaxbytes); // Email address for reporting unacceptable post for this forum, default is blank. $mform->addElement('text', 'reportingemail', get_string('reportingemail', 'forumng'), array('size' => 64)); $mform->setType('reportingemail', PARAM_NOTAGS); $mform->addRule('reportingemail', get_string('maximumchars', '', 255), 'maxlength', 255, 'client'); $mform->addHelpButton('reportingemail', 'reportingemail', 'forumng'); $mform->addElement('checkbox', 'canpostanon', get_string('canpostanon', 'forumng')); $mform->addHelpButton('canpostanon', 'canpostanon', 'forumng'); // Atom/RSS feed on/off/discussions-only if ($CFG->enablerssfeeds && !empty($CFG->forumng_enablerssfeeds)) { if ($CFG->forumng_feedtype == -1 || $CFG->forumng_feeditems == -1) { $mform->addElement('header', '', get_string('feeds', 'forumng')); } if ($CFG->forumng_feedtype == -1) { $mform->addElement('select', 'feedtype', get_string('feedtype', 'forumng'), mod_forumng::get_feedtype_options()); $mform->setDefault('feedtype', mod_forumng::FEEDTYPE_ALL_POSTS); $mform->addHelpButton('feedtype', 'feedtype', 'forumng'); } // Atom/RSS feed item count if ($CFG->forumng_feeditems == -1) { $mform->addElement('select', 'feeditems', get_string('feeditems', 'forumng'), mod_forumng::get_feeditems_options()); $mform->setDefault('feeditems', 20); $mform->addHelpButton('feeditems', 'feeditems', 'forumng'); } } // Add tagging to discussions. if ($CFG->usetags) { $mform->addElement('header', '', get_string('tagging', 'forumng')); // Enable tagging. $mform->addElement('checkbox', 'tags', get_string('enabletagging', 'forumng')); $mform->addHelpButton('tags', 'tagging', 'forumng'); // Add 'Set' forumng wide named tags to discussion tagging dropdowns. $settags = null; if ($forumng) { $settags = mod_forumng::get_set_tags($this->_instance); // Create 'Set' forumng wide tags. $mform->addElement('tags', 'settags', get_string('setforumtags', 'forumng'), array('display' => 'noofficial')); $mform->disabledIf('settags', 'tags', 'notchecked'); $mform->setType('settags', PARAM_TAGLIST); $mform->setDefault('settags', $settags); $mform->addHelpButton('settags', 'settags', 'forumng'); } } // Ratings header /*///////////////*/ $mform->addElement('header', '', get_string('ratings', 'forumng')); $options = array(mod_forumng::FORUMNG_NO_RATING => get_string('noratings', 'forumng'), mod_forumng::FORUMNG_RATING_OBSOLETE => get_string('forumngratingsobsolete', 'forumng'), mod_forumng::FORUMNG_STANDARD_RATING => get_string('standardratings', 'forumng')); $mform->addElement('select', 'enableratings', get_string('enableratings', 'forumng'), $options); $mform->addHelpButton('enableratings', 'enableratings', 'forumng'); // Scale $mform->addElement('modgrade', 'ratingscale', get_string('scale'), null, true); $mform->disabledIf('ratingscale', 'enableratings', 'eq', 0); $mform->setDefault('ratingscale', 5); // From/until times $mform->addElement('date_time_selector', 'ratingfrom', get_string('ratingfrom', 'forumng'), array('optional' => true)); $mform->disabledIf('ratingfrom', 'enableratings', 'eq', 0); $mform->addElement('date_time_selector', 'ratinguntil', get_string('ratinguntil', 'forumng'), array('optional' => true)); $mform->disabledIf('ratinguntil', 'enableratings', 'eq', 0); $mform->addElement('text', 'ratingthreshold', get_string('ratingthreshold', 'forumng')); $mform->setType('ratingthreshold', PARAM_INT); $mform->setDefault('ratingthreshold', 1); $mform->addRule('ratingthreshold', get_string('error_ratingthreshold', 'forumng'), 'regex', '/[1-9][0-9]*/', 'client'); $mform->addHelpButton('ratingthreshold', 'ratingthreshold', 'forumng'); $mform->disabledIf('ratingthreshold', 'enableratings', 'neq', mod_forumng::FORUMNG_RATING_OBSOLETE); // Grading. $mform->addElement('header', '', get_string('grading', 'forumng')); $mform->addElement('select', 'grading', get_string('grade'), mod_forumng::get_grading_options()); $mform->setDefault('grading', mod_forumng::GRADING_NONE); $mform->addHelpButton('grading', 'grading', 'forumng'); $mform->addElement('modgrade', 'gradingscale', get_string('gradingscale', 'forumng')); $mform->disabledIf('gradingscale', 'grading', 'ne', mod_forumng::GRADING_MANUAL); $mform->setDefault('gradingscale', 5); // Blocking header /*////////////////*/ $mform->addElement('header', '', get_string('limitposts', 'forumng')); // Post dates $mform->addElement('date_time_selector', 'postingfrom', get_string('postingfrom', 'forumng'), array('optional' => true)); $mform->addElement('date_time_selector', 'postinguntil', get_string('postinguntil', 'forumng'), array('optional' => true)); // User limits $limitgroup = array(); $limitgroup[] = $mform->createElement('checkbox', 'enablelimit', ''); $options = mod_forumng::get_max_posts_period_options(); $limitgroup[] = $mform->createElement('text', 'maxpostsblock', '', array('size' => 3)); $limitgroup[] = $mform->createElement('static', 'staticthing', '', ' ' . get_string('postsper', 'forumng') . ' '); $limitgroup[] = $mform->createElement('select', 'maxpostsperiod', '', $options); $mform->addGroup($limitgroup, 'limitgroup', get_string('enablelimit', 'forumng')); $mform->disabledIf('limitgroup[maxpostsblock]', 'limitgroup[enablelimit]'); $mform->disabledIf('limitgroup[maxpostsperiod]', 'limitgroup[enablelimit]'); $mform->addHelpButton('limitgroup', 'enablelimit', 'forumng'); $mform->setType('limitgroup[maxpostsblock]', PARAM_INT); $mform->setDefault('limitgroup[maxpostsblock]', '10'); // Remove old discussion $options = array(); $options[0] = get_string('removeolddiscussionsdefault', 'forumng'); for ($i = 1; $i <= 36; $i++) { $options[$i * 2592000] = $i > 1 ? get_string('nummonths', 'moodle', $i) : get_string('onemonth', 'forumng'); } $mform->addElement('header', '', get_string('removeolddiscussions', 'forumng')); $mform->addElement('select', 'removeafter', get_string('removeolddiscussionsafter', 'forumng'), $options); $mform->addHelpButton('removeafter', 'removeolddiscussions', 'forumng'); $options = array(); $options[0] = get_string('deletepermanently', 'forumng'); $options[-1] = get_string('automaticallylock', 'forumng'); $modinfo = get_fast_modinfo($COURSE); $targetforumngid = $this->_instance ? $this->_instance : 0; // Add all instances to drop down if the user can access them and // it's not the same as the current forum if (array_key_exists('forumng', $modinfo->instances)) { foreach ($modinfo->instances['forumng'] as $info) { if ($info->uservisible && $targetforumngid != $info->instance) { $options[$info->instance] = $info->name; } } } $mform->addElement('select', 'removeto', get_string('withremoveddiscussions', 'forumng'), $options); $mform->disabledIf('removeto', 'removeafter', 'eq', 0); $mform->addHelpButton('removeto', 'withremoveddiscussions', 'forumng'); // Sharing options are advanced and for administrators only if ($CFG->forumng_enableadvanced && has_capability('mod/forumng:addinstance', context_system::instance())) { $mform->addElement('header', '', get_string('sharing', 'forumng')); $mform->addElement('advcheckbox', 'shared', get_string('shared', 'forumng')); $mform->addHelpButton('shared', 'shared', 'forumng'); // Only when creating a forum, you can choose to make it a clone if (!$this->_instance) { $sharegroup = array(); $sharegroup[] = $mform->createElement('checkbox', 'useshared', ''); $sharegroup[] = $mform->createElement('text', 'originalcmidnumber', ''); $mform->setType('usesharedgroup[originalcmidnumber]', PARAM_RAW); $mform->addGroup($sharegroup, 'usesharedgroup', get_string('useshared', 'forumng')); $mform->disabledIf('usesharedgroup[originalcmidnumber]', 'usesharedgroup[useshared]', 'notchecked'); $mform->addHelpButton('usesharedgroup', 'useshared', 'forumng'); } } // Do definition that is shared with clone version of form $this->shared_definition_part($coursecontext); if (count(mod_forumng_utils::get_convertible_forums($COURSE)) > 0 && !$this->_instance) { $mform->addElement('static', '', '', '<div class="forumng-convertoffer">' . get_string('offerconvert', 'forumng', $CFG->wwwroot . '/mod/forumng/convert.php?course=' . $COURSE->id) . '</div>'); } }