|
Moodle
2.2.1
http://www.collinsharper.com
|
00001 <?php 00002 // This file is part of Moodle - http://moodle.org/ 00003 // 00004 // Moodle is free software: you can redistribute it and/or modify 00005 // it under the terms of the GNU General Public License as published by 00006 // the Free Software Foundation, either version 3 of the License, or 00007 // (at your option) any later version. 00008 // 00009 // Moodle is distributed in the hope that it will be useful, 00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 // GNU General Public License for more details. 00013 // 00014 // You should have received a copy of the GNU General Public License 00015 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 00016 00024 defined('MOODLE_INTERNAL') || die(); 00025 00027 require_once($CFG->libdir.'/filelib.php'); 00028 require_once($CFG->libdir.'/eventslib.php'); 00029 require_once($CFG->dirroot.'/user/selector/lib.php'); 00030 00032 00033 define('FORUM_MODE_FLATOLDEST', 1); 00034 define('FORUM_MODE_FLATNEWEST', -1); 00035 define('FORUM_MODE_THREADED', 2); 00036 define('FORUM_MODE_NESTED', 3); 00037 00038 define('FORUM_CHOOSESUBSCRIBE', 0); 00039 define('FORUM_FORCESUBSCRIBE', 1); 00040 define('FORUM_INITIALSUBSCRIBE', 2); 00041 define('FORUM_DISALLOWSUBSCRIBE',3); 00042 00043 define('FORUM_TRACKING_OFF', 0); 00044 define('FORUM_TRACKING_OPTIONAL', 1); 00045 define('FORUM_TRACKING_ON', 2); 00046 00048 00060 function forum_add_instance($forum, $mform) { 00061 global $CFG, $DB; 00062 00063 $forum->timemodified = time(); 00064 00065 if (empty($forum->assessed)) { 00066 $forum->assessed = 0; 00067 } 00068 00069 if (empty($forum->ratingtime) or empty($forum->assessed)) { 00070 $forum->assesstimestart = 0; 00071 $forum->assesstimefinish = 0; 00072 } 00073 00074 $forum->id = $DB->insert_record('forum', $forum); 00075 $modcontext = get_context_instance(CONTEXT_MODULE, $forum->coursemodule); 00076 00077 if ($forum->type == 'single') { // Create related discussion. 00078 $discussion = new stdClass(); 00079 $discussion->course = $forum->course; 00080 $discussion->forum = $forum->id; 00081 $discussion->name = $forum->name; 00082 $discussion->assessed = $forum->assessed; 00083 $discussion->message = $forum->intro; 00084 $discussion->messageformat = $forum->introformat; 00085 $discussion->messagetrust = trusttext_trusted(get_context_instance(CONTEXT_COURSE, $forum->course)); 00086 $discussion->mailnow = false; 00087 $discussion->groupid = -1; 00088 00089 $message = ''; 00090 00091 $discussion->id = forum_add_discussion($discussion, null, $message); 00092 00093 if ($mform and $draftid = file_get_submitted_draft_itemid('introeditor')) { 00094 // ugly hack - we need to copy the files somehow 00095 $discussion = $DB->get_record('forum_discussions', array('id'=>$discussion->id), '*', MUST_EXIST); 00096 $post = $DB->get_record('forum_posts', array('id'=>$discussion->firstpost), '*', MUST_EXIST); 00097 00098 $post->message = file_save_draft_area_files($draftid, $modcontext->id, 'mod_forum', 'post', $post->id, array('subdirs'=>true), $post->message); 00099 $DB->set_field('forum_posts', 'message', $post->message, array('id'=>$post->id)); 00100 } 00101 } 00102 00103 if ($forum->forcesubscribe == FORUM_INITIALSUBSCRIBE) { 00110 $users = forum_get_potential_subscribers($modcontext, 0, 'u.id, u.email', ''); 00111 foreach ($users as $user) { 00112 forum_subscribe($user->id, $forum->id); 00113 } 00114 } 00115 00116 forum_grade_item_update($forum); 00117 00118 return $forum->id; 00119 } 00120 00121 00131 function forum_update_instance($forum, $mform) { 00132 global $DB, $OUTPUT, $USER; 00133 00134 $forum->timemodified = time(); 00135 $forum->id = $forum->instance; 00136 00137 if (empty($forum->assessed)) { 00138 $forum->assessed = 0; 00139 } 00140 00141 if (empty($forum->ratingtime) or empty($forum->assessed)) { 00142 $forum->assesstimestart = 0; 00143 $forum->assesstimefinish = 0; 00144 } 00145 00146 $oldforum = $DB->get_record('forum', array('id'=>$forum->id)); 00147 00148 // MDL-3942 - if the aggregation type or scale (i.e. max grade) changes then recalculate the grades for the entire forum 00149 // if scale changes - do we need to recheck the ratings, if ratings higher than scale how do we want to respond? 00150 // for count and sum aggregation types the grade we check to make sure they do not exceed the scale (i.e. max score) when calculating the grade 00151 if (($oldforum->assessed<>$forum->assessed) or ($oldforum->scale<>$forum->scale)) { 00152 forum_update_grades($forum); // recalculate grades for the forum 00153 } 00154 00155 if ($forum->type == 'single') { // Update related discussion and post. 00156 $discussions = $DB->get_records('forum_discussions', array('forum'=>$forum->id), 'timemodified ASC'); 00157 if (!empty($discussions)) { 00158 if (count($discussions) > 1) { 00159 echo $OUTPUT->notification(get_string('warnformorepost', 'forum')); 00160 } 00161 $discussion = array_pop($discussions); 00162 } else { 00163 // try to recover by creating initial discussion - MDL-16262 00164 $discussion = new stdClass(); 00165 $discussion->course = $forum->course; 00166 $discussion->forum = $forum->id; 00167 $discussion->name = $forum->name; 00168 $discussion->assessed = $forum->assessed; 00169 $discussion->message = $forum->intro; 00170 $discussion->messageformat = $forum->introformat; 00171 $discussion->messagetrust = true; 00172 $discussion->mailnow = false; 00173 $discussion->groupid = -1; 00174 00175 $message = ''; 00176 00177 forum_add_discussion($discussion, null, $message); 00178 00179 if (! $discussion = $DB->get_record('forum_discussions', array('forum'=>$forum->id))) { 00180 print_error('cannotadd', 'forum'); 00181 } 00182 } 00183 if (! $post = $DB->get_record('forum_posts', array('id'=>$discussion->firstpost))) { 00184 print_error('cannotfindfirstpost', 'forum'); 00185 } 00186 00187 $cm = get_coursemodule_from_instance('forum', $forum->id); 00188 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id, MUST_EXIST); 00189 00190 if ($mform and $draftid = file_get_submitted_draft_itemid('introeditor')) { 00191 // ugly hack - we need to copy the files somehow 00192 $discussion = $DB->get_record('forum_discussions', array('id'=>$discussion->id), '*', MUST_EXIST); 00193 $post = $DB->get_record('forum_posts', array('id'=>$discussion->firstpost), '*', MUST_EXIST); 00194 00195 $post->message = file_save_draft_area_files($draftid, $modcontext->id, 'mod_forum', 'post', $post->id, array('subdirs'=>true), $post->message); 00196 } 00197 00198 $post->subject = $forum->name; 00199 $post->message = $forum->intro; 00200 $post->messageformat = $forum->introformat; 00201 $post->messagetrust = trusttext_trusted($modcontext); 00202 $post->modified = $forum->timemodified; 00203 $post->userid = $USER->id; // MDL-18599, so that current teacher can take ownership of activities 00204 00205 $DB->update_record('forum_posts', $post); 00206 $discussion->name = $forum->name; 00207 $DB->update_record('forum_discussions', $discussion); 00208 } 00209 00210 $DB->update_record('forum', $forum); 00211 00212 forum_grade_item_update($forum); 00213 00214 return true; 00215 } 00216 00217 00227 function forum_delete_instance($id) { 00228 global $DB; 00229 00230 if (!$forum = $DB->get_record('forum', array('id'=>$id))) { 00231 return false; 00232 } 00233 if (!$cm = get_coursemodule_from_instance('forum', $forum->id)) { 00234 return false; 00235 } 00236 if (!$course = $DB->get_record('course', array('id'=>$cm->course))) { 00237 return false; 00238 } 00239 00240 $context = get_context_instance(CONTEXT_MODULE, $cm->id); 00241 00242 // now get rid of all files 00243 $fs = get_file_storage(); 00244 $fs->delete_area_files($context->id); 00245 00246 $result = true; 00247 00248 if ($discussions = $DB->get_records('forum_discussions', array('forum'=>$forum->id))) { 00249 foreach ($discussions as $discussion) { 00250 if (!forum_delete_discussion($discussion, true, $course, $cm, $forum)) { 00251 $result = false; 00252 } 00253 } 00254 } 00255 00256 if (!$DB->delete_records('forum_subscriptions', array('forum'=>$forum->id))) { 00257 $result = false; 00258 } 00259 00260 forum_tp_delete_read_records(-1, -1, -1, $forum->id); 00261 00262 if (!$DB->delete_records('forum', array('id'=>$forum->id))) { 00263 $result = false; 00264 } 00265 00266 forum_grade_item_delete($forum); 00267 00268 return $result; 00269 } 00270 00271 00286 function forum_supports($feature) { 00287 switch($feature) { 00288 case FEATURE_GROUPS: return true; 00289 case FEATURE_GROUPINGS: return true; 00290 case FEATURE_GROUPMEMBERSONLY: return true; 00291 case FEATURE_MOD_INTRO: return true; 00292 case FEATURE_COMPLETION_TRACKS_VIEWS: return true; 00293 case FEATURE_COMPLETION_HAS_RULES: return true; 00294 case FEATURE_GRADE_HAS_GRADE: return true; 00295 case FEATURE_GRADE_OUTCOMES: return true; 00296 case FEATURE_RATE: return true; 00297 case FEATURE_BACKUP_MOODLE2: return true; 00298 case FEATURE_SHOW_DESCRIPTION: return true; 00299 00300 default: return null; 00301 } 00302 } 00303 00304 00318 function forum_get_completion_state($course,$cm,$userid,$type) { 00319 global $CFG,$DB; 00320 00321 // Get forum details 00322 if (!($forum=$DB->get_record('forum',array('id'=>$cm->instance)))) { 00323 throw new Exception("Can't find forum {$cm->instance}"); 00324 } 00325 00326 $result=$type; // Default return value 00327 00328 $postcountparams=array('userid'=>$userid,'forumid'=>$forum->id); 00329 $postcountsql=" 00330 SELECT 00331 COUNT(1) 00332 FROM 00333 {forum_posts} fp 00334 INNER JOIN {forum_discussions} fd ON fp.discussion=fd.id 00335 WHERE 00336 fp.userid=:userid AND fd.forum=:forumid"; 00337 00338 if ($forum->completiondiscussions) { 00339 $value = $forum->completiondiscussions <= 00340 $DB->count_records('forum_discussions',array('forum'=>$forum->id,'userid'=>$userid)); 00341 if ($type == COMPLETION_AND) { 00342 $result = $result && $value; 00343 } else { 00344 $result = $result || $value; 00345 } 00346 } 00347 if ($forum->completionreplies) { 00348 $value = $forum->completionreplies <= 00349 $DB->get_field_sql( $postcountsql.' AND fp.parent<>0',$postcountparams); 00350 if ($type==COMPLETION_AND) { 00351 $result = $result && $value; 00352 } else { 00353 $result = $result || $value; 00354 } 00355 } 00356 if ($forum->completionposts) { 00357 $value = $forum->completionposts <= $DB->get_field_sql($postcountsql,$postcountparams); 00358 if ($type == COMPLETION_AND) { 00359 $result = $result && $value; 00360 } else { 00361 $result = $result || $value; 00362 } 00363 } 00364 00365 return $result; 00366 } 00367 00368 00383 function forum_cron() { 00384 global $CFG, $USER, $DB; 00385 00386 $site = get_site(); 00387 00388 // all users that are subscribed to any post that needs sending 00389 $users = array(); 00390 00391 // status arrays 00392 $mailcount = array(); 00393 $errorcount = array(); 00394 00395 // caches 00396 $discussions = array(); 00397 $forums = array(); 00398 $courses = array(); 00399 $coursemodules = array(); 00400 $subscribedusers = array(); 00401 00402 00403 // Posts older than 2 days will not be mailed. This is to avoid the problem where 00404 // cron has not been running for a long time, and then suddenly people are flooded 00405 // with mail from the past few weeks or months 00406 $timenow = time(); 00407 $endtime = $timenow - $CFG->maxeditingtime; 00408 $starttime = $endtime - 48 * 3600; // Two days earlier 00409 00410 if ($posts = forum_get_unmailed_posts($starttime, $endtime, $timenow)) { 00411 // Mark them all now as being mailed. It's unlikely but possible there 00412 // might be an error later so that a post is NOT actually mailed out, 00413 // but since mail isn't crucial, we can accept this risk. Doing it now 00414 // prevents the risk of duplicated mails, which is a worse problem. 00415 00416 if (!forum_mark_old_posts_as_mailed($endtime)) { 00417 mtrace('Errors occurred while trying to mark some posts as being mailed.'); 00418 return false; // Don't continue trying to mail them, in case we are in a cron loop 00419 } 00420 00421 // checking post validity, and adding users to loop through later 00422 foreach ($posts as $pid => $post) { 00423 00424 $discussionid = $post->discussion; 00425 if (!isset($discussions[$discussionid])) { 00426 if ($discussion = $DB->get_record('forum_discussions', array('id'=> $post->discussion))) { 00427 $discussions[$discussionid] = $discussion; 00428 } else { 00429 mtrace('Could not find discussion '.$discussionid); 00430 unset($posts[$pid]); 00431 continue; 00432 } 00433 } 00434 $forumid = $discussions[$discussionid]->forum; 00435 if (!isset($forums[$forumid])) { 00436 if ($forum = $DB->get_record('forum', array('id' => $forumid))) { 00437 $forums[$forumid] = $forum; 00438 } else { 00439 mtrace('Could not find forum '.$forumid); 00440 unset($posts[$pid]); 00441 continue; 00442 } 00443 } 00444 $courseid = $forums[$forumid]->course; 00445 if (!isset($courses[$courseid])) { 00446 if ($course = $DB->get_record('course', array('id' => $courseid))) { 00447 $courses[$courseid] = $course; 00448 } else { 00449 mtrace('Could not find course '.$courseid); 00450 unset($posts[$pid]); 00451 continue; 00452 } 00453 } 00454 if (!isset($coursemodules[$forumid])) { 00455 if ($cm = get_coursemodule_from_instance('forum', $forumid, $courseid)) { 00456 $coursemodules[$forumid] = $cm; 00457 } else { 00458 mtrace('Could not find course module for forum '.$forumid); 00459 unset($posts[$pid]); 00460 continue; 00461 } 00462 } 00463 00464 00465 // caching subscribed users of each forum 00466 if (!isset($subscribedusers[$forumid])) { 00467 $modcontext = get_context_instance(CONTEXT_MODULE, $coursemodules[$forumid]->id); 00468 if ($subusers = forum_subscribed_users($courses[$courseid], $forums[$forumid], 0, $modcontext, "u.*")) { 00469 foreach ($subusers as $postuser) { 00470 unset($postuser->description); // not necessary 00471 // this user is subscribed to this forum 00472 $subscribedusers[$forumid][$postuser->id] = $postuser->id; 00473 // this user is a user we have to process later 00474 $users[$postuser->id] = $postuser; 00475 } 00476 unset($subusers); // release memory 00477 } 00478 } 00479 00480 $mailcount[$pid] = 0; 00481 $errorcount[$pid] = 0; 00482 } 00483 } 00484 00485 if ($users && $posts) { 00486 00487 $urlinfo = parse_url($CFG->wwwroot); 00488 $hostname = $urlinfo['host']; 00489 00490 foreach ($users as $userto) { 00491 00492 @set_time_limit(120); // terminate if processing of any account takes longer than 2 minutes 00493 00494 // set this so that the capabilities are cached, and environment matches receiving user 00495 cron_setup_user($userto); 00496 00497 mtrace('Processing user '.$userto->id); 00498 00499 // init caches 00500 $userto->viewfullnames = array(); 00501 $userto->canpost = array(); 00502 $userto->markposts = array(); 00503 00504 // reset the caches 00505 foreach ($coursemodules as $forumid=>$unused) { 00506 $coursemodules[$forumid]->cache = new stdClass(); 00507 $coursemodules[$forumid]->cache->caps = array(); 00508 unset($coursemodules[$forumid]->uservisible); 00509 } 00510 00511 foreach ($posts as $pid => $post) { 00512 00513 // Set up the environment for the post, discussion, forum, course 00514 $discussion = $discussions[$post->discussion]; 00515 $forum = $forums[$discussion->forum]; 00516 $course = $courses[$forum->course]; 00517 $cm =& $coursemodules[$forum->id]; 00518 00519 // Do some checks to see if we can bail out now 00520 // Only active enrolled users are in the list of subscribers 00521 if (!isset($subscribedusers[$forum->id][$userto->id])) { 00522 continue; // user does not subscribe to this forum 00523 } 00524 00525 // Don't send email if the forum is Q&A and the user has not posted 00526 // Initial topics are still mailed 00527 if ($forum->type == 'qanda' && !forum_get_user_posted_time($discussion->id, $userto->id) && $pid != $discussion->firstpost) { 00528 mtrace('Did not email '.$userto->id.' because user has not posted in discussion'); 00529 continue; 00530 } 00531 00532 // Get info about the sending user 00533 if (array_key_exists($post->userid, $users)) { // we might know him/her already 00534 $userfrom = $users[$post->userid]; 00535 } else if ($userfrom = $DB->get_record('user', array('id' => $post->userid))) { 00536 unset($userfrom->description); // not necessary 00537 $users[$userfrom->id] = $userfrom; // fetch only once, we can add it to user list, it will be skipped anyway 00538 } else { 00539 mtrace('Could not find user '.$post->userid); 00540 continue; 00541 } 00542 00543 //if we want to check that userto and userfrom are not the same person this is probably the spot to do it 00544 00545 // setup global $COURSE properly - needed for roles and languages 00546 cron_setup_user($userto, $course); 00547 00548 // Fill caches 00549 if (!isset($userto->viewfullnames[$forum->id])) { 00550 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id); 00551 $userto->viewfullnames[$forum->id] = has_capability('moodle/site:viewfullnames', $modcontext); 00552 } 00553 if (!isset($userto->canpost[$discussion->id])) { 00554 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id); 00555 $userto->canpost[$discussion->id] = forum_user_can_post($forum, $discussion, $userto, $cm, $course, $modcontext); 00556 } 00557 if (!isset($userfrom->groups[$forum->id])) { 00558 if (!isset($userfrom->groups)) { 00559 $userfrom->groups = array(); 00560 $users[$userfrom->id]->groups = array(); 00561 } 00562 $userfrom->groups[$forum->id] = groups_get_all_groups($course->id, $userfrom->id, $cm->groupingid); 00563 $users[$userfrom->id]->groups[$forum->id] = $userfrom->groups[$forum->id]; 00564 } 00565 00566 // Make sure groups allow this user to see this email 00567 if ($discussion->groupid > 0 and $groupmode = groups_get_activity_groupmode($cm, $course)) { // Groups are being used 00568 if (!groups_group_exists($discussion->groupid)) { // Can't find group 00569 continue; // Be safe and don't send it to anyone 00570 } 00571 00572 if (!groups_is_member($discussion->groupid) and !has_capability('moodle/site:accessallgroups', $modcontext)) { 00573 // do not send posts from other groups when in SEPARATEGROUPS or VISIBLEGROUPS 00574 continue; 00575 } 00576 } 00577 00578 // Make sure we're allowed to see it... 00579 if (!forum_user_can_see_post($forum, $discussion, $post, NULL, $cm)) { 00580 mtrace('user '.$userto->id. ' can not see '.$post->id); 00581 continue; 00582 } 00583 00584 // OK so we need to send the email. 00585 00586 // Does the user want this post in a digest? If so postpone it for now. 00587 if ($userto->maildigest > 0) { 00588 // This user wants the mails to be in digest form 00589 $queue = new stdClass(); 00590 $queue->userid = $userto->id; 00591 $queue->discussionid = $discussion->id; 00592 $queue->postid = $post->id; 00593 $queue->timemodified = $post->created; 00594 $DB->insert_record('forum_queue', $queue); 00595 continue; 00596 } 00597 00598 00599 // Prepare to actually send the post now, and build up the content 00600 00601 $cleanforumname = str_replace('"', "'", strip_tags(format_string($forum->name))); 00602 00603 $userfrom->customheaders = array ( // Headers to make emails easier to track 00604 'Precedence: Bulk', 00605 'List-Id: "'.$cleanforumname.'" <moodleforum'.$forum->id.'@'.$hostname.'>', 00606 'List-Help: '.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id, 00607 'Message-ID: <moodlepost'.$post->id.'@'.$hostname.'>', 00608 'X-Course-Id: '.$course->id, 00609 'X-Course-Name: '.format_string($course->fullname, true) 00610 ); 00611 00612 if ($post->parent) { // This post is a reply, so add headers for threading (see MDL-22551) 00613 $userfrom->customheaders[] = 'In-Reply-To: <moodlepost'.$post->parent.'@'.$hostname.'>'; 00614 $userfrom->customheaders[] = 'References: <moodlepost'.$post->parent.'@'.$hostname.'>'; 00615 } 00616 00617 $shortname = format_string($course->shortname, true, array('context' => get_context_instance(CONTEXT_COURSE, $course->id))); 00618 00619 $postsubject = "$shortname: ".format_string($post->subject,true); 00620 $posttext = forum_make_mail_text($course, $cm, $forum, $discussion, $post, $userfrom, $userto); 00621 $posthtml = forum_make_mail_html($course, $cm, $forum, $discussion, $post, $userfrom, $userto); 00622 00623 // Send the post now! 00624 00625 mtrace('Sending ', ''); 00626 00627 $eventdata = new stdClass(); 00628 $eventdata->component = 'mod_forum'; 00629 $eventdata->name = 'posts'; 00630 $eventdata->userfrom = $userfrom; 00631 $eventdata->userto = $userto; 00632 $eventdata->subject = $postsubject; 00633 $eventdata->fullmessage = $posttext; 00634 $eventdata->fullmessageformat = FORMAT_PLAIN; 00635 $eventdata->fullmessagehtml = $posthtml; 00636 $eventdata->notification = 1; 00637 00638 $smallmessagestrings = new stdClass(); 00639 $smallmessagestrings->user = fullname($userfrom); 00640 $smallmessagestrings->forumname = "$shortname: ".format_string($forum->name,true).": ".$discussion->name; 00641 $smallmessagestrings->message = $post->message; 00642 //make sure strings are in message recipients language 00643 $eventdata->smallmessage = get_string_manager()->get_string('smallmessage', 'forum', $smallmessagestrings, $userto->lang); 00644 00645 $eventdata->contexturl = "{$CFG->wwwroot}/mod/forum/discuss.php?d={$discussion->id}#p{$post->id}"; 00646 $eventdata->contexturlname = $discussion->name; 00647 00648 $mailresult = message_send($eventdata); 00649 if (!$mailresult){ 00650 mtrace("Error: mod/forum/lib.php forum_cron(): Could not send out mail for id $post->id to user $userto->id". 00651 " ($userto->email) .. not trying again."); 00652 add_to_log($course->id, 'forum', 'mail error', "discuss.php?d=$discussion->id#p$post->id", 00653 substr(format_string($post->subject,true),0,30), $cm->id, $userto->id); 00654 $errorcount[$post->id]++; 00655 } else { 00656 $mailcount[$post->id]++; 00657 00658 // Mark post as read if forum_usermarksread is set off 00659 if (!$CFG->forum_usermarksread) { 00660 $userto->markposts[$post->id] = $post->id; 00661 } 00662 } 00663 00664 mtrace('post '.$post->id. ': '.$post->subject); 00665 } 00666 00667 // mark processed posts as read 00668 forum_tp_mark_posts_read($userto, $userto->markposts); 00669 } 00670 } 00671 00672 if ($posts) { 00673 foreach ($posts as $post) { 00674 mtrace($mailcount[$post->id]." users were sent post $post->id, '$post->subject'"); 00675 if ($errorcount[$post->id]) { 00676 $DB->set_field("forum_posts", "mailed", "2", array("id" => "$post->id")); 00677 } 00678 } 00679 } 00680 00681 // release some memory 00682 unset($subscribedusers); 00683 unset($mailcount); 00684 unset($errorcount); 00685 00686 cron_setup_user(); 00687 00688 $sitetimezone = $CFG->timezone; 00689 00690 // Now see if there are any digest mails waiting to be sent, and if we should send them 00691 00692 mtrace('Starting digest processing...'); 00693 00694 @set_time_limit(300); // terminate if not able to fetch all digests in 5 minutes 00695 00696 if (!isset($CFG->digestmailtimelast)) { // To catch the first time 00697 set_config('digestmailtimelast', 0); 00698 } 00699 00700 $timenow = time(); 00701 $digesttime = usergetmidnight($timenow, $sitetimezone) + ($CFG->digestmailtime * 3600); 00702 00703 // Delete any really old ones (normally there shouldn't be any) 00704 $weekago = $timenow - (7 * 24 * 3600); 00705 $DB->delete_records_select('forum_queue', "timemodified < ?", array($weekago)); 00706 mtrace ('Cleaned old digest records'); 00707 00708 if ($CFG->digestmailtimelast < $digesttime and $timenow > $digesttime) { 00709 00710 mtrace('Sending forum digests: '.userdate($timenow, '', $sitetimezone)); 00711 00712 $digestposts_rs = $DB->get_recordset_select('forum_queue', "timemodified < ?", array($digesttime)); 00713 00714 if ($digestposts_rs->valid()) { 00715 00716 // We have work to do 00717 $usermailcount = 0; 00718 00719 //caches - reuse the those filled before too 00720 $discussionposts = array(); 00721 $userdiscussions = array(); 00722 00723 foreach ($digestposts_rs as $digestpost) { 00724 if (!isset($users[$digestpost->userid])) { 00725 if ($user = $DB->get_record('user', array('id' => $digestpost->userid))) { 00726 $users[$digestpost->userid] = $user; 00727 } else { 00728 continue; 00729 } 00730 } 00731 $postuser = $users[$digestpost->userid]; 00732 00733 if (!isset($posts[$digestpost->postid])) { 00734 if ($post = $DB->get_record('forum_posts', array('id' => $digestpost->postid))) { 00735 $posts[$digestpost->postid] = $post; 00736 } else { 00737 continue; 00738 } 00739 } 00740 $discussionid = $digestpost->discussionid; 00741 if (!isset($discussions[$discussionid])) { 00742 if ($discussion = $DB->get_record('forum_discussions', array('id' => $discussionid))) { 00743 $discussions[$discussionid] = $discussion; 00744 } else { 00745 continue; 00746 } 00747 } 00748 $forumid = $discussions[$discussionid]->forum; 00749 if (!isset($forums[$forumid])) { 00750 if ($forum = $DB->get_record('forum', array('id' => $forumid))) { 00751 $forums[$forumid] = $forum; 00752 } else { 00753 continue; 00754 } 00755 } 00756 00757 $courseid = $forums[$forumid]->course; 00758 if (!isset($courses[$courseid])) { 00759 if ($course = $DB->get_record('course', array('id' => $courseid))) { 00760 $courses[$courseid] = $course; 00761 } else { 00762 continue; 00763 } 00764 } 00765 00766 if (!isset($coursemodules[$forumid])) { 00767 if ($cm = get_coursemodule_from_instance('forum', $forumid, $courseid)) { 00768 $coursemodules[$forumid] = $cm; 00769 } else { 00770 continue; 00771 } 00772 } 00773 $userdiscussions[$digestpost->userid][$digestpost->discussionid] = $digestpost->discussionid; 00774 $discussionposts[$digestpost->discussionid][$digestpost->postid] = $digestpost->postid; 00775 } 00776 $digestposts_rs->close(); 00777 00778 // Data collected, start sending out emails to each user 00779 foreach ($userdiscussions as $userid => $thesediscussions) { 00780 00781 @set_time_limit(120); // terminate if processing of any account takes longer than 2 minutes 00782 00783 cron_setup_user(); 00784 00785 mtrace(get_string('processingdigest', 'forum', $userid), '... '); 00786 00787 // First of all delete all the queue entries for this user 00788 $DB->delete_records_select('forum_queue', "userid = ? AND timemodified < ?", array($userid, $digesttime)); 00789 $userto = $users[$userid]; 00790 00791 // Override the language and timezone of the "current" user, so that 00792 // mail is customised for the receiver. 00793 cron_setup_user($userto); 00794 00795 // init caches 00796 $userto->viewfullnames = array(); 00797 $userto->canpost = array(); 00798 $userto->markposts = array(); 00799 00800 $postsubject = get_string('digestmailsubject', 'forum', format_string($site->shortname, true)); 00801 00802 $headerdata = new stdClass(); 00803 $headerdata->sitename = format_string($site->fullname, true); 00804 $headerdata->userprefs = $CFG->wwwroot.'/user/edit.php?id='.$userid.'&course='.$site->id; 00805 00806 $posttext = get_string('digestmailheader', 'forum', $headerdata)."\n\n"; 00807 $headerdata->userprefs = '<a target="_blank" href="'.$headerdata->userprefs.'">'.get_string('digestmailprefs', 'forum').'</a>'; 00808 00809 $posthtml = "<head>"; 00810 /* foreach ($CFG->stylesheets as $stylesheet) { 00811 //TODO: MDL-21120 00812 $posthtml .= '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'" />'."\n"; 00813 }*/ 00814 $posthtml .= "</head>\n<body id=\"email\">\n"; 00815 $posthtml .= '<p>'.get_string('digestmailheader', 'forum', $headerdata).'</p><br /><hr size="1" noshade="noshade" />'; 00816 00817 foreach ($thesediscussions as $discussionid) { 00818 00819 @set_time_limit(120); // to be reset for each post 00820 00821 $discussion = $discussions[$discussionid]; 00822 $forum = $forums[$discussion->forum]; 00823 $course = $courses[$forum->course]; 00824 $cm = $coursemodules[$forum->id]; 00825 00826 //override language 00827 cron_setup_user($userto, $course); 00828 00829 // Fill caches 00830 if (!isset($userto->viewfullnames[$forum->id])) { 00831 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id); 00832 $userto->viewfullnames[$forum->id] = has_capability('moodle/site:viewfullnames', $modcontext); 00833 } 00834 if (!isset($userto->canpost[$discussion->id])) { 00835 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id); 00836 $userto->canpost[$discussion->id] = forum_user_can_post($forum, $discussion, $userto, $cm, $course, $modcontext); 00837 } 00838 00839 $strforums = get_string('forums', 'forum'); 00840 $canunsubscribe = ! forum_is_forcesubscribed($forum); 00841 $canreply = $userto->canpost[$discussion->id]; 00842 $shortname = format_string($course->shortname, true, array('context' => get_context_instance(CONTEXT_COURSE, $course->id))); 00843 00844 $posttext .= "\n \n"; 00845 $posttext .= '====================================================================='; 00846 $posttext .= "\n \n"; 00847 $posttext .= "$shortname -> $strforums -> ".format_string($forum->name,true); 00848 if ($discussion->name != $forum->name) { 00849 $posttext .= " -> ".format_string($discussion->name,true); 00850 } 00851 $posttext .= "\n"; 00852 00853 $posthtml .= "<p><font face=\"sans-serif\">". 00854 "<a target=\"_blank\" href=\"$CFG->wwwroot/course/view.php?id=$course->id\">$shortname</a> -> ". 00855 "<a target=\"_blank\" href=\"$CFG->wwwroot/mod/forum/index.php?id=$course->id\">$strforums</a> -> ". 00856 "<a target=\"_blank\" href=\"$CFG->wwwroot/mod/forum/view.php?f=$forum->id\">".format_string($forum->name,true)."</a>"; 00857 if ($discussion->name == $forum->name) { 00858 $posthtml .= "</font></p>"; 00859 } else { 00860 $posthtml .= " -> <a target=\"_blank\" href=\"$CFG->wwwroot/mod/forum/discuss.php?d=$discussion->id\">".format_string($discussion->name,true)."</a></font></p>"; 00861 } 00862 $posthtml .= '<p>'; 00863 00864 $postsarray = $discussionposts[$discussionid]; 00865 sort($postsarray); 00866 00867 foreach ($postsarray as $postid) { 00868 $post = $posts[$postid]; 00869 00870 if (array_key_exists($post->userid, $users)) { // we might know him/her already 00871 $userfrom = $users[$post->userid]; 00872 } else if ($userfrom = $DB->get_record('user', array('id' => $post->userid))) { 00873 $users[$userfrom->id] = $userfrom; // fetch only once, we can add it to user list, it will be skipped anyway 00874 } else { 00875 mtrace('Could not find user '.$post->userid); 00876 continue; 00877 } 00878 00879 if (!isset($userfrom->groups[$forum->id])) { 00880 if (!isset($userfrom->groups)) { 00881 $userfrom->groups = array(); 00882 $users[$userfrom->id]->groups = array(); 00883 } 00884 $userfrom->groups[$forum->id] = groups_get_all_groups($course->id, $userfrom->id, $cm->groupingid); 00885 $users[$userfrom->id]->groups[$forum->id] = $userfrom->groups[$forum->id]; 00886 } 00887 00888 $userfrom->customheaders = array ("Precedence: Bulk"); 00889 00890 if ($userto->maildigest == 2) { 00891 // Subjects only 00892 $by = new stdClass(); 00893 $by->name = fullname($userfrom); 00894 $by->date = userdate($post->modified); 00895 $posttext .= "\n".format_string($post->subject,true).' '.get_string("bynameondate", "forum", $by); 00896 $posttext .= "\n---------------------------------------------------------------------"; 00897 00898 $by->name = "<a target=\"_blank\" href=\"$CFG->wwwroot/user/view.php?id=$userfrom->id&course=$course->id\">$by->name</a>"; 00899 $posthtml .= '<div><a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$discussion->id.'#p'.$post->id.'">'.format_string($post->subject,true).'</a> '.get_string("bynameondate", "forum", $by).'</div>'; 00900 00901 } else { 00902 // The full treatment 00903 $posttext .= forum_make_mail_text($course, $cm, $forum, $discussion, $post, $userfrom, $userto, true); 00904 $posthtml .= forum_make_mail_post($course, $cm, $forum, $discussion, $post, $userfrom, $userto, false, $canreply, true, false); 00905 00906 // Create an array of postid's for this user to mark as read. 00907 if (!$CFG->forum_usermarksread) { 00908 $userto->markposts[$post->id] = $post->id; 00909 } 00910 } 00911 } 00912 if ($canunsubscribe) { 00913 $posthtml .= "\n<div class='mdl-right'><font size=\"1\"><a href=\"$CFG->wwwroot/mod/forum/subscribe.php?id=$forum->id\">".get_string("unsubscribe", "forum")."</a></font></div>"; 00914 } else { 00915 $posthtml .= "\n<div class='mdl-right'><font size=\"1\">".get_string("everyoneissubscribed", "forum")."</font></div>"; 00916 } 00917 $posthtml .= '<hr size="1" noshade="noshade" /></p>'; 00918 } 00919 $posthtml .= '</body>'; 00920 00921 if (empty($userto->mailformat) || $userto->mailformat != 1) { 00922 // This user DOESN'T want to receive HTML 00923 $posthtml = ''; 00924 } 00925 00926 $attachment = $attachname=''; 00927 $usetrueaddress = true; 00928 //directly email forum digests rather than sending them via messaging 00929 $mailresult = email_to_user($userto, $site->shortname, $postsubject, $posttext, $posthtml, $attachment, $attachname, $usetrueaddress, $CFG->forum_replytouser); 00930 00931 if (!$mailresult) { 00932 mtrace("ERROR!"); 00933 echo "Error: mod/forum/cron.php: Could not send out digest mail to user $userto->id ($userto->email)... not trying again.\n"; 00934 add_to_log($course->id, 'forum', 'mail digest error', '', '', $cm->id, $userto->id); 00935 } else { 00936 mtrace("success."); 00937 $usermailcount++; 00938 00939 // Mark post as read if forum_usermarksread is set off 00940 forum_tp_mark_posts_read($userto, $userto->markposts); 00941 } 00942 } 00943 } 00945 set_config('digestmailtimelast', $timenow); 00946 } 00947 00948 cron_setup_user(); 00949 00950 if (!empty($usermailcount)) { 00951 mtrace(get_string('digestsentusers', 'forum', $usermailcount)); 00952 } 00953 00954 if (!empty($CFG->forum_lastreadclean)) { 00955 $timenow = time(); 00956 if ($CFG->forum_lastreadclean + (24*3600) < $timenow) { 00957 set_config('forum_lastreadclean', $timenow); 00958 mtrace('Removing old forum read tracking info...'); 00959 forum_tp_clean_read_records(); 00960 } 00961 } else { 00962 set_config('forum_lastreadclean', time()); 00963 } 00964 00965 00966 return true; 00967 } 00968 00985 function forum_make_mail_text($course, $cm, $forum, $discussion, $post, $userfrom, $userto, $bare = false) { 00986 global $CFG, $USER; 00987 00988 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id); 00989 00990 if (!isset($userto->viewfullnames[$forum->id])) { 00991 $viewfullnames = has_capability('moodle/site:viewfullnames', $modcontext, $userto->id); 00992 } else { 00993 $viewfullnames = $userto->viewfullnames[$forum->id]; 00994 } 00995 00996 if (!isset($userto->canpost[$discussion->id])) { 00997 $canreply = forum_user_can_post($forum, $discussion, $userto, $cm, $course, $modcontext); 00998 } else { 00999 $canreply = $userto->canpost[$discussion->id]; 01000 } 01001 01002 $by = New stdClass; 01003 $by->name = fullname($userfrom, $viewfullnames); 01004 $by->date = userdate($post->modified, "", $userto->timezone); 01005 01006 $strbynameondate = get_string('bynameondate', 'forum', $by); 01007 01008 $strforums = get_string('forums', 'forum'); 01009 01010 $canunsubscribe = ! forum_is_forcesubscribed($forum); 01011 01012 $posttext = ''; 01013 01014 if (!$bare) { 01015 $shortname = format_string($course->shortname, true, array('context' => get_context_instance(CONTEXT_COURSE, $course->id))); 01016 $posttext = "$shortname -> $strforums -> ".format_string($forum->name,true); 01017 01018 if ($discussion->name != $forum->name) { 01019 $posttext .= " -> ".format_string($discussion->name,true); 01020 } 01021 } 01022 01023 // add absolute file links 01024 $post->message = file_rewrite_pluginfile_urls($post->message, 'pluginfile.php', $modcontext->id, 'mod_forum', 'post', $post->id); 01025 01026 $posttext .= "\n---------------------------------------------------------------------\n"; 01027 $posttext .= format_string($post->subject,true); 01028 if ($bare) { 01029 $posttext .= " ($CFG->wwwroot/mod/forum/discuss.php?d=$discussion->id#p$post->id)"; 01030 } 01031 $posttext .= "\n".$strbynameondate."\n"; 01032 $posttext .= "---------------------------------------------------------------------\n"; 01033 $posttext .= format_text_email($post->message, $post->messageformat); 01034 $posttext .= "\n\n"; 01035 $posttext .= forum_print_attachments($post, $cm, "text"); 01036 01037 if (!$bare && $canreply) { 01038 $posttext .= "---------------------------------------------------------------------\n"; 01039 $posttext .= get_string("postmailinfo", "forum", $shortname)."\n"; 01040 $posttext .= "$CFG->wwwroot/mod/forum/post.php?reply=$post->id\n"; 01041 } 01042 if (!$bare && $canunsubscribe) { 01043 $posttext .= "\n---------------------------------------------------------------------\n"; 01044 $posttext .= get_string("unsubscribe", "forum"); 01045 $posttext .= ": $CFG->wwwroot/mod/forum/subscribe.php?id=$forum->id\n"; 01046 } 01047 01048 return $posttext; 01049 } 01050 01064 function forum_make_mail_html($course, $cm, $forum, $discussion, $post, $userfrom, $userto) { 01065 global $CFG; 01066 01067 if ($userto->mailformat != 1) { // Needs to be HTML 01068 return ''; 01069 } 01070 01071 if (!isset($userto->canpost[$discussion->id])) { 01072 $canreply = forum_user_can_post($forum, $discussion, $userto, $cm, $course); 01073 } else { 01074 $canreply = $userto->canpost[$discussion->id]; 01075 } 01076 01077 $strforums = get_string('forums', 'forum'); 01078 $canunsubscribe = ! forum_is_forcesubscribed($forum); 01079 $shortname = format_string($course->shortname, true, array('context' => get_context_instance(CONTEXT_COURSE, $course->id))); 01080 01081 $posthtml = '<head>'; 01082 /* foreach ($CFG->stylesheets as $stylesheet) { 01083 //TODO: MDL-21120 01084 $posthtml .= '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'" />'."\n"; 01085 }*/ 01086 $posthtml .= '</head>'; 01087 $posthtml .= "\n<body id=\"email\">\n\n"; 01088 01089 $posthtml .= '<div class="navbar">'. 01090 '<a target="_blank" href="'.$CFG->wwwroot.'/course/view.php?id='.$course->id.'">'.$shortname.'</a> » '. 01091 '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/index.php?id='.$course->id.'">'.$strforums.'</a> » '. 01092 '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id.'">'.format_string($forum->name,true).'</a>'; 01093 if ($discussion->name == $forum->name) { 01094 $posthtml .= '</div>'; 01095 } else { 01096 $posthtml .= ' » <a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$discussion->id.'">'. 01097 format_string($discussion->name,true).'</a></div>'; 01098 } 01099 $posthtml .= forum_make_mail_post($course, $cm, $forum, $discussion, $post, $userfrom, $userto, false, $canreply, true, false); 01100 01101 if ($canunsubscribe) { 01102 $posthtml .= '<hr /><div class="mdl-align unsubscribelink"> 01103 <a href="'.$CFG->wwwroot.'/mod/forum/subscribe.php?id='.$forum->id.'">'.get_string('unsubscribe', 'forum').'</a> 01104 <a href="'.$CFG->wwwroot.'/mod/forum/unsubscribeall.php">'.get_string('unsubscribeall', 'forum').'</a></div>'; 01105 } 01106 01107 $posthtml .= '</body>'; 01108 01109 return $posthtml; 01110 } 01111 01112 01121 function forum_user_outline($course, $user, $mod, $forum) { 01122 global $CFG; 01123 require_once("$CFG->libdir/gradelib.php"); 01124 $grades = grade_get_grades($course->id, 'mod', 'forum', $forum->id, $user->id); 01125 if (empty($grades->items[0]->grades)) { 01126 $grade = false; 01127 } else { 01128 $grade = reset($grades->items[0]->grades); 01129 } 01130 01131 $count = forum_count_user_posts($forum->id, $user->id); 01132 01133 if ($count && $count->postcount > 0) { 01134 $result = new stdClass(); 01135 $result->info = get_string("numposts", "forum", $count->postcount); 01136 $result->time = $count->lastpost; 01137 if ($grade) { 01138 $result->info .= ', ' . get_string('grade') . ': ' . $grade->str_long_grade; 01139 } 01140 return $result; 01141 } else if ($grade) { 01142 $result = new stdClass(); 01143 $result->info = get_string('grade') . ': ' . $grade->str_long_grade; 01144 01145 //datesubmitted == time created. dategraded == time modified or time overridden 01146 //if grade was last modified by the user themselves use date graded. Otherwise use date submitted 01147 //TODO: move this copied & pasted code somewhere in the grades API. See MDL-26704 01148 if ($grade->usermodified == $user->id || empty($grade->datesubmitted)) { 01149 $result->time = $grade->dategraded; 01150 } else { 01151 $result->time = $grade->datesubmitted; 01152 } 01153 01154 return $result; 01155 } 01156 return NULL; 01157 } 01158 01159 01168 function forum_user_complete($course, $user, $mod, $forum) { 01169 global $CFG,$USER, $OUTPUT; 01170 require_once("$CFG->libdir/gradelib.php"); 01171 01172 $grades = grade_get_grades($course->id, 'mod', 'forum', $forum->id, $user->id); 01173 if (!empty($grades->items[0]->grades)) { 01174 $grade = reset($grades->items[0]->grades); 01175 echo $OUTPUT->container(get_string('grade').': '.$grade->str_long_grade); 01176 if ($grade->str_feedback) { 01177 echo $OUTPUT->container(get_string('feedback').': '.$grade->str_feedback); 01178 } 01179 } 01180 01181 if ($posts = forum_get_user_posts($forum->id, $user->id)) { 01182 01183 if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $course->id)) { 01184 print_error('invalidcoursemodule'); 01185 } 01186 $discussions = forum_get_user_involved_discussions($forum->id, $user->id); 01187 01188 foreach ($posts as $post) { 01189 if (!isset($discussions[$post->discussion])) { 01190 continue; 01191 } 01192 $discussion = $discussions[$post->discussion]; 01193 01194 forum_print_post($post, $discussion, $forum, $cm, $course, false, false, false); 01195 } 01196 } else { 01197 echo "<p>".get_string("noposts", "forum")."</p>"; 01198 } 01199 } 01200 01201 01202 01203 01204 01205 01213 function forum_print_overview($courses,&$htmlarray) { 01214 global $USER, $CFG, $DB, $SESSION; 01215 01216 if (empty($courses) || !is_array($courses) || count($courses) == 0) { 01217 return array(); 01218 } 01219 01220 if (!$forums = get_all_instances_in_courses('forum',$courses)) { 01221 return; 01222 } 01223 01224 01225 // get all forum logs in ONE query (much better!) 01226 $params = array(); 01227 $sql = "SELECT instance,cmid,l.course,COUNT(l.id) as count FROM {log} l " 01228 ." JOIN {course_modules} cm ON cm.id = cmid " 01229 ." WHERE ("; 01230 foreach ($courses as $course) { 01231 $sql .= '(l.course = ? AND l.time > ?) OR '; 01232 $params[] = $course->id; 01233 $params[] = $course->lastaccess; 01234 } 01235 $sql = substr($sql,0,-3); // take off the last OR 01236 01237 $sql .= ") AND l.module = 'forum' AND action = 'add post' " 01238 ." AND userid != ? GROUP BY cmid,l.course,instance"; 01239 01240 $params[] = $USER->id; 01241 01242 if (!$new = $DB->get_records_sql($sql, $params)) { 01243 $new = array(); // avoid warnings 01244 } 01245 01246 // also get all forum tracking stuff ONCE. 01247 $trackingforums = array(); 01248 foreach ($forums as $forum) { 01249 if (forum_tp_can_track_forums($forum)) { 01250 $trackingforums[$forum->id] = $forum; 01251 } 01252 } 01253 01254 if (count($trackingforums) > 0) { 01255 $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0; 01256 $sql = 'SELECT d.forum,d.course,COUNT(p.id) AS count '. 01257 ' FROM {forum_posts} p '. 01258 ' JOIN {forum_discussions} d ON p.discussion = d.id '. 01259 ' LEFT JOIN {forum_read} r ON r.postid = p.id AND r.userid = ? WHERE ('; 01260 $params = array($USER->id); 01261 01262 foreach ($trackingforums as $track) { 01263 $sql .= '(d.forum = ? AND (d.groupid = -1 OR d.groupid = 0 OR d.groupid = ?)) OR '; 01264 $params[] = $track->id; 01265 if (isset($SESSION->currentgroup[$track->course])) { 01266 $groupid = $SESSION->currentgroup[$track->course]; 01267 } else { 01268 $groupid = groups_get_all_groups($track->course, $USER->id); 01269 if (is_array($groupid)) { 01270 $groupid = array_shift(array_keys($groupid)); 01271 $SESSION->currentgroup[$track->course] = $groupid; 01272 } else { 01273 $groupid = 0; 01274 } 01275 } 01276 $params[] = $groupid; 01277 } 01278 $sql = substr($sql,0,-3); // take off the last OR 01279 $sql .= ') AND p.modified >= ? AND r.id is NULL GROUP BY d.forum,d.course'; 01280 $params[] = $cutoffdate; 01281 01282 if (!$unread = $DB->get_records_sql($sql, $params)) { 01283 $unread = array(); 01284 } 01285 } else { 01286 $unread = array(); 01287 } 01288 01289 if (empty($unread) and empty($new)) { 01290 return; 01291 } 01292 01293 $strforum = get_string('modulename','forum'); 01294 $strnumunread = get_string('overviewnumunread','forum'); 01295 $strnumpostssince = get_string('overviewnumpostssince','forum'); 01296 01297 foreach ($forums as $forum) { 01298 $str = ''; 01299 $count = 0; 01300 $thisunread = 0; 01301 $showunread = false; 01302 // either we have something from logs, or trackposts, or nothing. 01303 if (array_key_exists($forum->id, $new) && !empty($new[$forum->id])) { 01304 $count = $new[$forum->id]->count; 01305 } 01306 if (array_key_exists($forum->id,$unread)) { 01307 $thisunread = $unread[$forum->id]->count; 01308 $showunread = true; 01309 } 01310 if ($count > 0 || $thisunread > 0) { 01311 $str .= '<div class="overview forum"><div class="name">'.$strforum.': <a title="'.$strforum.'" href="'.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id.'">'. 01312 $forum->name.'</a></div>'; 01313 $str .= '<div class="info"><span class="postsincelogin">'; 01314 $str .= $count.' '.$strnumpostssince."</span>"; 01315 if (!empty($showunread)) { 01316 $str .= '<div class="unreadposts">'.$thisunread .' '.$strnumunread.'</div>'; 01317 } 01318 $str .= '</div></div>'; 01319 } 01320 if (!empty($str)) { 01321 if (!array_key_exists($forum->course,$htmlarray)) { 01322 $htmlarray[$forum->course] = array(); 01323 } 01324 if (!array_key_exists('forum',$htmlarray[$forum->course])) { 01325 $htmlarray[$forum->course]['forum'] = ''; // initialize, avoid warnings 01326 } 01327 $htmlarray[$forum->course]['forum'] .= $str; 01328 } 01329 } 01330 } 01331 01346 function forum_print_recent_activity($course, $viewfullnames, $timestart) { 01347 global $CFG, $USER, $DB, $OUTPUT; 01348 01349 // do not use log table if possible, it may be huge and is expensive to join with other tables 01350 01351 if (!$posts = $DB->get_records_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid, 01352 d.timestart, d.timeend, d.userid AS duserid, 01353 u.firstname, u.lastname, u.email, u.picture 01354 FROM {forum_posts} p 01355 JOIN {forum_discussions} d ON d.id = p.discussion 01356 JOIN {forum} f ON f.id = d.forum 01357 JOIN {user} u ON u.id = p.userid 01358 WHERE p.created > ? AND f.course = ? 01359 ORDER BY p.id ASC", array($timestart, $course->id))) { // order by initial posting date 01360 return false; 01361 } 01362 01363 $modinfo =& get_fast_modinfo($course); 01364 01365 $groupmodes = array(); 01366 $cms = array(); 01367 01368 $strftimerecent = get_string('strftimerecent'); 01369 01370 $printposts = array(); 01371 foreach ($posts as $post) { 01372 if (!isset($modinfo->instances['forum'][$post->forum])) { 01373 // not visible 01374 continue; 01375 } 01376 $cm = $modinfo->instances['forum'][$post->forum]; 01377 if (!$cm->uservisible) { 01378 continue; 01379 } 01380 $context = get_context_instance(CONTEXT_MODULE, $cm->id); 01381 01382 if (!has_capability('mod/forum:viewdiscussion', $context)) { 01383 continue; 01384 } 01385 01386 if (!empty($CFG->forum_enabletimedposts) and $USER->id != $post->duserid 01387 and (($post->timestart > 0 and $post->timestart > time()) or ($post->timeend > 0 and $post->timeend < time()))) { 01388 if (!has_capability('mod/forum:viewhiddentimedposts', $context)) { 01389 continue; 01390 } 01391 } 01392 01393 $groupmode = groups_get_activity_groupmode($cm, $course); 01394 01395 if ($groupmode) { 01396 if ($post->groupid == -1 or $groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $context)) { 01397 // oki (Open discussions have groupid -1) 01398 } else { 01399 // separate mode 01400 if (isguestuser()) { 01401 // shortcut 01402 continue; 01403 } 01404 01405 if (is_null($modinfo->groups)) { 01406 $modinfo->groups = groups_get_user_groups($course->id); // load all my groups and cache it in modinfo 01407 } 01408 01409 if (!array_key_exists($post->groupid, $modinfo->groups[0])) { 01410 continue; 01411 } 01412 } 01413 } 01414 01415 $printposts[] = $post; 01416 } 01417 unset($posts); 01418 01419 if (!$printposts) { 01420 return false; 01421 } 01422 01423 echo $OUTPUT->heading(get_string('newforumposts', 'forum').':', 3); 01424 echo "\n<ul class='unlist'>\n"; 01425 01426 foreach ($printposts as $post) { 01427 $subjectclass = empty($post->parent) ? ' bold' : ''; 01428 01429 echo '<li><div class="head">'. 01430 '<div class="date">'.userdate($post->modified, $strftimerecent).'</div>'. 01431 '<div class="name">'.fullname($post, $viewfullnames).'</div>'. 01432 '</div>'; 01433 echo '<div class="info'.$subjectclass.'">'; 01434 if (empty($post->parent)) { 01435 echo '"<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'">'; 01436 } else { 01437 echo '"<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'&parent='.$post->parent.'#p'.$post->id.'">'; 01438 } 01439 $post->subject = break_up_long_words(format_string($post->subject, true)); 01440 echo $post->subject; 01441 echo "</a>\"</div></li>\n"; 01442 } 01443 01444 echo "</ul>\n"; 01445 01446 return true; 01447 } 01448 01458 function forum_get_user_grades($forum, $userid = 0) { 01459 global $CFG; 01460 01461 require_once($CFG->dirroot.'/rating/lib.php'); 01462 01463 $ratingoptions = new stdClass; 01464 $ratingoptions->component = 'mod_forum'; 01465 $ratingoptions->ratingarea = 'post'; 01466 01467 //need these to work backwards to get a context id. Is there a better way to get contextid from a module instance? 01468 $ratingoptions->modulename = 'forum'; 01469 $ratingoptions->moduleid = $forum->id; 01470 $ratingoptions->userid = $userid; 01471 $ratingoptions->aggregationmethod = $forum->assessed; 01472 $ratingoptions->scaleid = $forum->scale; 01473 $ratingoptions->itemtable = 'forum_posts'; 01474 $ratingoptions->itemtableusercolumn = 'userid'; 01475 01476 $rm = new rating_manager(); 01477 return $rm->get_user_grades($ratingoptions); 01478 } 01479 01490 function forum_update_grades($forum, $userid=0, $nullifnone=true) { 01491 global $CFG, $DB; 01492 require_once($CFG->libdir.'/gradelib.php'); 01493 01494 if (!$forum->assessed) { 01495 forum_grade_item_update($forum); 01496 01497 } else if ($grades = forum_get_user_grades($forum, $userid)) { 01498 forum_grade_item_update($forum, $grades); 01499 01500 } else if ($userid and $nullifnone) { 01501 $grade = new stdClass(); 01502 $grade->userid = $userid; 01503 $grade->rawgrade = NULL; 01504 forum_grade_item_update($forum, $grade); 01505 01506 } else { 01507 forum_grade_item_update($forum); 01508 } 01509 } 01510 01515 function forum_upgrade_grades() { 01516 global $DB; 01517 01518 $sql = "SELECT COUNT('x') 01519 FROM {forum} f, {course_modules} cm, {modules} m 01520 WHERE m.name='forum' AND m.id=cm.module AND cm.instance=f.id"; 01521 $count = $DB->count_records_sql($sql); 01522 01523 $sql = "SELECT f.*, cm.idnumber AS cmidnumber, f.course AS courseid 01524 FROM {forum} f, {course_modules} cm, {modules} m 01525 WHERE m.name='forum' AND m.id=cm.module AND cm.instance=f.id"; 01526 $rs = $DB->get_recordset_sql($sql); 01527 if ($rs->valid()) { 01528 $pbar = new progress_bar('forumupgradegrades', 500, true); 01529 $i=0; 01530 foreach ($rs as $forum) { 01531 $i++; 01532 upgrade_set_timeout(60*5); // set up timeout, may also abort execution 01533 forum_update_grades($forum, 0, false); 01534 $pbar->update($i, $count, "Updating Forum grades ($i/$count)."); 01535 } 01536 } 01537 $rs->close(); 01538 } 01539 01551 function forum_grade_item_update($forum, $grades=NULL) { 01552 global $CFG; 01553 if (!function_exists('grade_update')) { //workaround for buggy PHP versions 01554 require_once($CFG->libdir.'/gradelib.php'); 01555 } 01556 01557 $params = array('itemname'=>$forum->name, 'idnumber'=>$forum->cmidnumber); 01558 01559 if (!$forum->assessed or $forum->scale == 0) { 01560 $params['gradetype'] = GRADE_TYPE_NONE; 01561 01562 } else if ($forum->scale > 0) { 01563 $params['gradetype'] = GRADE_TYPE_VALUE; 01564 $params['grademax'] = $forum->scale; 01565 $params['grademin'] = 0; 01566 01567 } else if ($forum->scale < 0) { 01568 $params['gradetype'] = GRADE_TYPE_SCALE; 01569 $params['scaleid'] = -$forum->scale; 01570 } 01571 01572 if ($grades === 'reset') { 01573 $params['reset'] = true; 01574 $grades = NULL; 01575 } 01576 01577 return grade_update('mod/forum', $forum->course, 'mod', 'forum', $forum->id, 0, $grades, $params); 01578 } 01579 01587 function forum_grade_item_delete($forum) { 01588 global $CFG; 01589 require_once($CFG->libdir.'/gradelib.php'); 01590 01591 return grade_update('mod/forum', $forum->course, 'mod', 'forum', $forum->id, 0, NULL, array('deleted'=>1)); 01592 } 01593 01594 01604 function forum_get_participants($forumid) { 01605 01606 global $CFG, $DB; 01607 01608 $params = array('forumid' => $forumid); 01609 01610 //Get students from forum_subscriptions 01611 $sql = "SELECT DISTINCT u.id, u.id 01612 FROM {user} u, 01613 {forum_subscriptions} s 01614 WHERE s.forum = :forumid AND 01615 u.id = s.userid"; 01616 $st_subscriptions = $DB->get_records_sql($sql, $params); 01617 01618 //Get students from forum_posts 01619 $sql = "SELECT DISTINCT u.id, u.id 01620 FROM {user} u, 01621 {forum_discussions} d, 01622 {forum_posts} p 01623 WHERE d.forum = :forumid AND 01624 p.discussion = d.id AND 01625 u.id = p.userid"; 01626 $st_posts = $DB->get_records_sql($sql, $params); 01627 01628 //Get students from the ratings table 01629 $sql = "SELECT DISTINCT r.userid, r.userid AS id 01630 FROM {forum_discussions} d 01631 JOIN {forum_posts} p ON p.discussion = d.id 01632 JOIN {rating} r on r.itemid = p.id 01633 WHERE d.forum = :forumid AND 01634 r.component = 'mod_forum' AND 01635 r.ratingarea = 'post'"; 01636 $st_ratings = $DB->get_records_sql($sql, $params); 01637 01638 //Add st_posts to st_subscriptions 01639 if ($st_posts) { 01640 foreach ($st_posts as $st_post) { 01641 $st_subscriptions[$st_post->id] = $st_post; 01642 } 01643 } 01644 //Add st_ratings to st_subscriptions 01645 if ($st_ratings) { 01646 foreach ($st_ratings as $st_rating) { 01647 $st_subscriptions[$st_rating->id] = $st_rating; 01648 } 01649 } 01650 //Return st_subscriptions array (it contains an array of unique users) 01651 return ($st_subscriptions); 01652 } 01653 01662 function forum_scale_used ($forumid,$scaleid) { 01663 global $DB; 01664 $return = false; 01665 01666 $rec = $DB->get_record("forum",array("id" => "$forumid","scale" => "-$scaleid")); 01667 01668 if (!empty($rec) && !empty($scaleid)) { 01669 $return = true; 01670 } 01671 01672 return $return; 01673 } 01674 01684 function forum_scale_used_anywhere($scaleid) { 01685 global $DB; 01686 if ($scaleid and $DB->record_exists('forum', array('scale' => -$scaleid))) { 01687 return true; 01688 } else { 01689 return false; 01690 } 01691 } 01692 01693 // SQL FUNCTIONS /////////////////////////////////////////////////////////// 01694 01704 function forum_get_post_full($postid) { 01705 global $CFG, $DB; 01706 01707 return $DB->get_record_sql("SELECT p.*, d.forum, u.firstname, u.lastname, u.email, u.picture, u.imagealt 01708 FROM {forum_posts} p 01709 JOIN {forum_discussions} d ON p.discussion = d.id 01710 LEFT JOIN {user} u ON p.userid = u.id 01711 WHERE p.id = ?", array($postid)); 01712 } 01713 01723 function forum_get_discussion_posts($discussion, $sort, $forumid) { 01724 global $CFG, $DB; 01725 01726 return $DB->get_records_sql("SELECT p.*, $forumid AS forum, u.firstname, u.lastname, u.email, u.picture, u.imagealt 01727 FROM {forum_posts} p 01728 LEFT JOIN {user} u ON p.userid = u.id 01729 WHERE p.discussion = ? 01730 AND p.parent > 0 $sort", array($discussion)); 01731 } 01732 01744 function forum_get_all_discussion_posts($discussionid, $sort, $tracking=false) { 01745 global $CFG, $DB, $USER; 01746 01747 $tr_sel = ""; 01748 $tr_join = ""; 01749 $params = array(); 01750 01751 if ($tracking) { 01752 $now = time(); 01753 $cutoffdate = $now - ($CFG->forum_oldpostdays * 24 * 3600); 01754 $tr_sel = ", fr.id AS postread"; 01755 $tr_join = "LEFT JOIN {forum_read} fr ON (fr.postid = p.id AND fr.userid = ?)"; 01756 $params[] = $USER->id; 01757 } 01758 01759 $params[] = $discussionid; 01760 if (!$posts = $DB->get_records_sql("SELECT p.*, u.firstname, u.lastname, u.email, u.picture, u.imagealt $tr_sel 01761 FROM {forum_posts} p 01762 LEFT JOIN {user} u ON p.userid = u.id 01763 $tr_join 01764 WHERE p.discussion = ? 01765 ORDER BY $sort", $params)) { 01766 return array(); 01767 } 01768 01769 foreach ($posts as $pid=>$p) { 01770 if ($tracking) { 01771 if (forum_tp_is_post_old($p)) { 01772 $posts[$pid]->postread = true; 01773 } 01774 } 01775 if (!$p->parent) { 01776 continue; 01777 } 01778 if (!isset($posts[$p->parent])) { 01779 continue; // parent does not exist?? 01780 } 01781 if (!isset($posts[$p->parent]->children)) { 01782 $posts[$p->parent]->children = array(); 01783 } 01784 $posts[$p->parent]->children[$pid] =& $posts[$pid]; 01785 } 01786 01787 return $posts; 01788 } 01789 01801 function forum_get_child_posts($parent, $forumid) { 01802 global $CFG, $DB; 01803 01804 return $DB->get_records_sql("SELECT p.*, $forumid AS forum, u.firstname, u.lastname, u.email, u.picture, u.imagealt 01805 FROM {forum_posts} p 01806 LEFT JOIN {user} u ON p.userid = u.id 01807 WHERE p.parent = ? 01808 ORDER BY p.created ASC", array($parent)); 01809 } 01810 01824 function forum_get_readable_forums($userid, $courseid=0) { 01825 01826 global $CFG, $DB, $USER; 01827 require_once($CFG->dirroot.'/course/lib.php'); 01828 01829 if (!$forummod = $DB->get_record('modules', array('name' => 'forum'))) { 01830 print_error('notinstalled', 'forum'); 01831 } 01832 01833 if ($courseid) { 01834 $courses = $DB->get_records('course', array('id' => $courseid)); 01835 } else { 01836 // If no course is specified, then the user can see SITE + his courses. 01837 $courses1 = $DB->get_records('course', array('id' => SITEID)); 01838 $courses2 = enrol_get_users_courses($userid, true, array('modinfo')); 01839 $courses = array_merge($courses1, $courses2); 01840 } 01841 if (!$courses) { 01842 return array(); 01843 } 01844 01845 $readableforums = array(); 01846 01847 foreach ($courses as $course) { 01848 01849 $modinfo =& get_fast_modinfo($course); 01850 if (is_null($modinfo->groups)) { 01851 $modinfo->groups = groups_get_user_groups($course->id, $userid); 01852 } 01853 01854 if (empty($modinfo->instances['forum'])) { 01855 // hmm, no forums? 01856 continue; 01857 } 01858 01859 $courseforums = $DB->get_records('forum', array('course' => $course->id)); 01860 01861 foreach ($modinfo->instances['forum'] as $forumid => $cm) { 01862 if (!$cm->uservisible or !isset($courseforums[$forumid])) { 01863 continue; 01864 } 01865 $context = get_context_instance(CONTEXT_MODULE, $cm->id); 01866 $forum = $courseforums[$forumid]; 01867 $forum->context = $context; 01868 $forum->cm = $cm; 01869 01870 if (!has_capability('mod/forum:viewdiscussion', $context)) { 01871 continue; 01872 } 01873 01875 if (groups_get_activity_groupmode($cm, $course) == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) { 01876 if (is_null($modinfo->groups)) { 01877 $modinfo->groups = groups_get_user_groups($course->id, $USER->id); 01878 } 01879 if (isset($modinfo->groups[$cm->groupingid])) { 01880 $forum->onlygroups = $modinfo->groups[$cm->groupingid]; 01881 $forum->onlygroups[] = -1; 01882 } else { 01883 $forum->onlygroups = array(-1); 01884 } 01885 } 01886 01888 $forum->viewhiddentimedposts = true; 01889 if (!empty($CFG->forum_enabletimedposts)) { 01890 if (!has_capability('mod/forum:viewhiddentimedposts', $context)) { 01891 $forum->viewhiddentimedposts = false; 01892 } 01893 } 01894 01896 if ($forum->type == 'qanda' 01897 && !has_capability('mod/forum:viewqandawithoutposting', $context)) { 01898 01899 // We need to check whether the user has posted in the qanda forum. 01900 $forum->onlydiscussions = array(); // Holds discussion ids for the discussions 01901 // the user is allowed to see in this forum. 01902 if ($discussionspostedin = forum_discussions_user_has_posted_in($forum->id, $USER->id)) { 01903 foreach ($discussionspostedin as $d) { 01904 $forum->onlydiscussions[] = $d->id; 01905 } 01906 } 01907 } 01908 01909 $readableforums[$forum->id] = $forum; 01910 } 01911 01912 unset($modinfo); 01913 01914 } // End foreach $courses 01915 01916 return $readableforums; 01917 } 01918 01933 function forum_search_posts($searchterms, $courseid=0, $limitfrom=0, $limitnum=50, 01934 &$totalcount, $extrasql='') { 01935 global $CFG, $DB, $USER; 01936 require_once($CFG->libdir.'/searchlib.php'); 01937 01938 $forums = forum_get_readable_forums($USER->id, $courseid); 01939 01940 if (count($forums) == 0) { 01941 $totalcount = 0; 01942 return false; 01943 } 01944 01945 $now = round(time(), -2); // db friendly 01946 01947 $fullaccess = array(); 01948 $where = array(); 01949 $params = array(); 01950 01951 foreach ($forums as $forumid => $forum) { 01952 $select = array(); 01953 01954 if (!$forum->viewhiddentimedposts) { 01955 $select[] = "(d.userid = :userid{$forumid} OR (d.timestart < :timestart{$forumid} AND (d.timeend = 0 OR d.timeend > :timeend{$forumid})))"; 01956 $params = array_merge($params, array('userid'.$forumid=>$USER->id, 'timestart'.$forumid=>$now, 'timeend'.$forumid=>$now)); 01957 } 01958 01959 $cm = $forum->cm; 01960 $context = $forum->context; 01961 01962 if ($forum->type == 'qanda' 01963 && !has_capability('mod/forum:viewqandawithoutposting', $context)) { 01964 if (!empty($forum->onlydiscussions)) { 01965 list($discussionid_sql, $discussionid_params) = $DB->get_in_or_equal($forum->onlydiscussions, SQL_PARAMS_NAMED, 'qanda'.$forumid.'_'); 01966 $params = array_merge($params, $discussionid_params); 01967 $select[] = "(d.id $discussionid_sql OR p.parent = 0)"; 01968 } else { 01969 $select[] = "p.parent = 0"; 01970 } 01971 } 01972 01973 if (!empty($forum->onlygroups)) { 01974 list($groupid_sql, $groupid_params) = $DB->get_in_or_equal($forum->onlygroups, SQL_PARAMS_NAMED, 'grps'.$forumid.'_'); 01975 $params = array_merge($params, $groupid_params); 01976 $select[] = "d.groupid $groupid_sql"; 01977 } 01978 01979 if ($select) { 01980 $selects = implode(" AND ", $select); 01981 $where[] = "(d.forum = :forum{$forumid} AND $selects)"; 01982 $params['forum'.$forumid] = $forumid; 01983 } else { 01984 $fullaccess[] = $forumid; 01985 } 01986 } 01987 01988 if ($fullaccess) { 01989 list($fullid_sql, $fullid_params) = $DB->get_in_or_equal($fullaccess, SQL_PARAMS_NAMED, 'fula'); 01990 $params = array_merge($params, $fullid_params); 01991 $where[] = "(d.forum $fullid_sql)"; 01992 } 01993 01994 $selectdiscussion = "(".implode(" OR ", $where).")"; 01995 01996 $messagesearch = ''; 01997 $searchstring = ''; 01998 01999 // Need to concat these back together for parser to work. 02000 foreach($searchterms as $searchterm){ 02001 if ($searchstring != '') { 02002 $searchstring .= ' '; 02003 } 02004 $searchstring .= $searchterm; 02005 } 02006 02007 // We need to allow quoted strings for the search. The quotes *should* be stripped 02008 // by the parser, but this should be examined carefully for security implications. 02009 $searchstring = str_replace("\\\"","\"",$searchstring); 02010 $parser = new search_parser(); 02011 $lexer = new search_lexer($parser); 02012 02013 if ($lexer->parse($searchstring)) { 02014 $parsearray = $parser->get_parsed_array(); 02015 // Experimental feature under 1.8! MDL-8830 02016 // Use alternative text searches if defined 02017 // This feature only works under mysql until properly implemented for other DBs 02018 // Requires manual creation of text index for forum_posts before enabling it: 02019 // CREATE FULLTEXT INDEX foru_post_tix ON [prefix]forum_posts (subject, message) 02020 // Experimental feature under 1.8! MDL-8830 02021 if (!empty($CFG->forum_usetextsearches)) { 02022 list($messagesearch, $msparams) = search_generate_text_SQL($parsearray, 'p.message', 'p.subject', 02023 'p.userid', 'u.id', 'u.firstname', 02024 'u.lastname', 'p.modified', 'd.forum'); 02025 } else { 02026 list($messagesearch, $msparams) = search_generate_SQL($parsearray, 'p.message', 'p.subject', 02027 'p.userid', 'u.id', 'u.firstname', 02028 'u.lastname', 'p.modified', 'd.forum'); 02029 } 02030 $params = array_merge($params, $msparams); 02031 } 02032 02033 $fromsql = "{forum_posts} p, 02034 {forum_discussions} d, 02035 {user} u"; 02036 02037 $selectsql = " $messagesearch 02038 AND p.discussion = d.id 02039 AND p.userid = u.id 02040 AND $selectdiscussion 02041 $extrasql"; 02042 02043 $countsql = "SELECT COUNT(*) 02044 FROM $fromsql 02045 WHERE $selectsql"; 02046 02047 $searchsql = "SELECT p.*, 02048 d.forum, 02049 u.firstname, 02050 u.lastname, 02051 u.email, 02052 u.picture, 02053 u.imagealt, 02054 u.email 02055 FROM $fromsql 02056 WHERE $selectsql 02057 ORDER BY p.modified DESC"; 02058 02059 $totalcount = $DB->count_records_sql($countsql, $params); 02060 02061 return $DB->get_records_sql($searchsql, $params, $limitfrom, $limitnum); 02062 } 02063 02075 function forum_get_ratings($context, $postid, $sort = "u.firstname ASC") { 02076 $options = new stdClass; 02077 $options->context = $context; 02078 $options->component = 'mod_forum'; 02079 $options->ratingarea = 'post'; 02080 $options->itemid = $postid; 02081 $options->sort = "ORDER BY $sort"; 02082 02083 $rm = new rating_manager(); 02084 return $rm->get_all_ratings_for_item($options); 02085 } 02086 02095 function forum_get_unmailed_posts($starttime, $endtime, $now=null) { 02096 global $CFG, $DB; 02097 02098 $params = array($starttime, $endtime); 02099 if (!empty($CFG->forum_enabletimedposts)) { 02100 if (empty($now)) { 02101 $now = time(); 02102 } 02103 $timedsql = "AND (d.timestart < ? AND (d.timeend = 0 OR d.timeend > ?))"; 02104 $params[] = $now; 02105 $params[] = $now; 02106 } else { 02107 $timedsql = ""; 02108 } 02109 02110 return $DB->get_records_sql("SELECT p.*, d.course, d.forum 02111 FROM {forum_posts} p 02112 JOIN {forum_discussions} d ON d.id = p.discussion 02113 WHERE p.mailed = 0 02114 AND p.created >= ? 02115 AND (p.created < ? OR p.mailnow = 1) 02116 $timedsql 02117 ORDER BY p.modified ASC", $params); 02118 } 02119 02129 function forum_mark_old_posts_as_mailed($endtime, $now=null) { 02130 global $CFG, $DB; 02131 if (empty($now)) { 02132 $now = time(); 02133 } 02134 02135 if (empty($CFG->forum_enabletimedposts)) { 02136 return $DB->execute("UPDATE {forum_posts} 02137 SET mailed = '1' 02138 WHERE (created < ? OR mailnow = 1) 02139 AND mailed = 0", array($endtime)); 02140 02141 } else { 02142 return $DB->execute("UPDATE {forum_posts} 02143 SET mailed = '1' 02144 WHERE discussion NOT IN (SELECT d.id 02145 FROM {forum_discussions} d 02146 WHERE d.timestart > ?) 02147 AND (created < ? OR mailnow = 1) 02148 AND mailed = 0", array($now, $endtime)); 02149 } 02150 } 02151 02160 function forum_get_user_posts($forumid, $userid) { 02161 global $CFG, $DB; 02162 02163 $timedsql = ""; 02164 $params = array($forumid, $userid); 02165 02166 if (!empty($CFG->forum_enabletimedposts)) { 02167 $cm = get_coursemodule_from_instance('forum', $forumid); 02168 if (!has_capability('mod/forum:viewhiddentimedposts' , get_context_instance(CONTEXT_MODULE, $cm->id))) { 02169 $now = time(); 02170 $timedsql = "AND (d.timestart < ? AND (d.timeend = 0 OR d.timeend > ?))"; 02171 $params[] = $now; 02172 $params[] = $now; 02173 } 02174 } 02175 02176 return $DB->get_records_sql("SELECT p.*, d.forum, u.firstname, u.lastname, u.email, u.picture, u.imagealt 02177 FROM {forum} f 02178 JOIN {forum_discussions} d ON d.forum = f.id 02179 JOIN {forum_posts} p ON p.discussion = d.id 02180 JOIN {user} u ON u.id = p.userid 02181 WHERE f.id = ? 02182 AND p.userid = ? 02183 $timedsql 02184 ORDER BY p.modified ASC", $params); 02185 } 02186 02197 function forum_get_user_involved_discussions($forumid, $userid) { 02198 global $CFG, $DB; 02199 02200 $timedsql = ""; 02201 $params = array($forumid, $userid); 02202 if (!empty($CFG->forum_enabletimedposts)) { 02203 $cm = get_coursemodule_from_instance('forum', $forumid); 02204 if (!has_capability('mod/forum:viewhiddentimedposts' , get_context_instance(CONTEXT_MODULE, $cm->id))) { 02205 $now = time(); 02206 $timedsql = "AND (d.timestart < ? AND (d.timeend = 0 OR d.timeend > ?))"; 02207 $params[] = $now; 02208 $params[] = $now; 02209 } 02210 } 02211 02212 return $DB->get_records_sql("SELECT DISTINCT d.* 02213 FROM {forum} f 02214 JOIN {forum_discussions} d ON d.forum = f.id 02215 JOIN {forum_posts} p ON p.discussion = d.id 02216 WHERE f.id = ? 02217 AND p.userid = ? 02218 $timedsql", $params); 02219 } 02220 02230 function forum_count_user_posts($forumid, $userid) { 02231 global $CFG, $DB; 02232 02233 $timedsql = ""; 02234 $params = array($forumid, $userid); 02235 if (!empty($CFG->forum_enabletimedposts)) { 02236 $cm = get_coursemodule_from_instance('forum', $forumid); 02237 if (!has_capability('mod/forum:viewhiddentimedposts' , get_context_instance(CONTEXT_MODULE, $cm->id))) { 02238 $now = time(); 02239 $timedsql = "AND (d.timestart < ? AND (d.timeend = 0 OR d.timeend > ?))"; 02240 $params[] = $now; 02241 $params[] = $now; 02242 } 02243 } 02244 02245 return $DB->get_record_sql("SELECT COUNT(p.id) AS postcount, MAX(p.modified) AS lastpost 02246 FROM {forum} f 02247 JOIN {forum_discussions} d ON d.forum = f.id 02248 JOIN {forum_posts} p ON p.discussion = d.id 02249 JOIN {user} u ON u.id = p.userid 02250 WHERE f.id = ? 02251 AND p.userid = ? 02252 $timedsql", $params); 02253 } 02254 02263 function forum_get_post_from_log($log) { 02264 global $CFG, $DB; 02265 02266 if ($log->action == "add post") { 02267 02268 return $DB->get_record_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid, 02269 u.firstname, u.lastname, u.email, u.picture 02270 FROM {forum_discussions} d, 02271 {forum_posts} p, 02272 {forum} f, 02273 {user} u 02274 WHERE p.id = ? 02275 AND d.id = p.discussion 02276 AND p.userid = u.id 02277 AND u.deleted <> '1' 02278 AND f.id = d.forum", array($log->info)); 02279 02280 02281 } else if ($log->action == "add discussion") { 02282 02283 return $DB->get_record_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid, 02284 u.firstname, u.lastname, u.email, u.picture 02285 FROM {forum_discussions} d, 02286 {forum_posts} p, 02287 {forum} f, 02288 {user} u 02289 WHERE d.id = ? 02290 AND d.firstpost = p.id 02291 AND p.userid = u.id 02292 AND u.deleted <> '1' 02293 AND f.id = d.forum", array($log->info)); 02294 } 02295 return NULL; 02296 } 02297 02306 function forum_get_firstpost_from_discussion($discussionid) { 02307 global $CFG, $DB; 02308 02309 return $DB->get_record_sql("SELECT p.* 02310 FROM {forum_discussions} d, 02311 {forum_posts} p 02312 WHERE d.id = ? 02313 AND d.firstpost = p.id ", array($discussionid)); 02314 } 02315 02328 function forum_count_discussion_replies($forumid, $forumsort="", $limit=-1, $page=-1, $perpage=0) { 02329 global $CFG, $DB; 02330 02331 if ($limit > 0) { 02332 $limitfrom = 0; 02333 $limitnum = $limit; 02334 } else if ($page != -1) { 02335 $limitfrom = $page*$perpage; 02336 $limitnum = $perpage; 02337 } else { 02338 $limitfrom = 0; 02339 $limitnum = 0; 02340 } 02341 02342 if ($forumsort == "") { 02343 $orderby = ""; 02344 $groupby = ""; 02345 02346 } else { 02347 $orderby = "ORDER BY $forumsort"; 02348 $groupby = ", ".strtolower($forumsort); 02349 $groupby = str_replace('desc', '', $groupby); 02350 $groupby = str_replace('asc', '', $groupby); 02351 } 02352 02353 if (($limitfrom == 0 and $limitnum == 0) or $forumsort == "") { 02354 $sql = "SELECT p.discussion, COUNT(p.id) AS replies, MAX(p.id) AS lastpostid 02355 FROM {forum_posts} p 02356 JOIN {forum_discussions} d ON p.discussion = d.id 02357 WHERE p.parent > 0 AND d.forum = ? 02358 GROUP BY p.discussion"; 02359 return $DB->get_records_sql($sql, array($forumid)); 02360 02361 } else { 02362 $sql = "SELECT p.discussion, (COUNT(p.id) - 1) AS replies, MAX(p.id) AS lastpostid 02363 FROM {forum_posts} p 02364 JOIN {forum_discussions} d ON p.discussion = d.id 02365 WHERE d.forum = ? 02366 GROUP BY p.discussion $groupby 02367 $orderby"; 02368 return $DB->get_records_sql("SELECT * FROM ($sql) sq", array($forumid), $limitfrom, $limitnum); 02369 } 02370 } 02371 02382 function forum_count_discussions($forum, $cm, $course) { 02383 global $CFG, $DB, $USER; 02384 02385 static $cache = array(); 02386 02387 $now = round(time(), -2); // db cache friendliness 02388 02389 $params = array($course->id); 02390 02391 if (!isset($cache[$course->id])) { 02392 if (!empty($CFG->forum_enabletimedposts)) { 02393 $timedsql = "AND d.timestart < ? AND (d.timeend = 0 OR d.timeend > ?)"; 02394 $params[] = $now; 02395 $params[] = $now; 02396 } else { 02397 $timedsql = ""; 02398 } 02399 02400 $sql = "SELECT f.id, COUNT(d.id) as dcount 02401 FROM {forum} f 02402 JOIN {forum_discussions} d ON d.forum = f.id 02403 WHERE f.course = ? 02404 $timedsql 02405 GROUP BY f.id"; 02406 02407 if ($counts = $DB->get_records_sql($sql, $params)) { 02408 foreach ($counts as $count) { 02409 $counts[$count->id] = $count->dcount; 02410 } 02411 $cache[$course->id] = $counts; 02412 } else { 02413 $cache[$course->id] = array(); 02414 } 02415 } 02416 02417 if (empty($cache[$course->id][$forum->id])) { 02418 return 0; 02419 } 02420 02421 $groupmode = groups_get_activity_groupmode($cm, $course); 02422 02423 if ($groupmode != SEPARATEGROUPS) { 02424 return $cache[$course->id][$forum->id]; 02425 } 02426 02427 if (has_capability('moodle/site:accessallgroups', get_context_instance(CONTEXT_MODULE, $cm->id))) { 02428 return $cache[$course->id][$forum->id]; 02429 } 02430 02431 require_once($CFG->dirroot.'/course/lib.php'); 02432 02433 $modinfo =& get_fast_modinfo($course); 02434 if (is_null($modinfo->groups)) { 02435 $modinfo->groups = groups_get_user_groups($course->id, $USER->id); 02436 } 02437 02438 if (array_key_exists($cm->groupingid, $modinfo->groups)) { 02439 $mygroups = $modinfo->groups[$cm->groupingid]; 02440 } else { 02441 $mygroups = false; // Will be set below 02442 } 02443 02444 // add all groups posts 02445 if (empty($mygroups)) { 02446 $mygroups = array(-1=>-1); 02447 } else { 02448 $mygroups[-1] = -1; 02449 } 02450 02451 list($mygroups_sql, $params) = $DB->get_in_or_equal($mygroups); 02452 $params[] = $forum->id; 02453 02454 if (!empty($CFG->forum_enabletimedposts)) { 02455 $timedsql = "AND d.timestart < $now AND (d.timeend = 0 OR d.timeend > $now)"; 02456 $params[] = $now; 02457 $params[] = $now; 02458 } else { 02459 $timedsql = ""; 02460 } 02461 02462 $sql = "SELECT COUNT(d.id) 02463 FROM {forum_discussions} d 02464 WHERE d.groupid $mygroups_sql AND d.forum = ? 02465 $timedsql"; 02466 02467 return $DB->get_field_sql($sql, $params); 02468 } 02469 02479 function forum_count_unrated_posts($discussionid, $userid) { 02480 global $CFG, $DB; 02481 02482 $sql = "SELECT COUNT(*) as num 02483 FROM {forum_posts} 02484 WHERE parent > 0 02485 AND discussion = :discussionid 02486 AND userid <> :userid"; 02487 $params = array('discussionid' => $discussionid, 'userid' => $userid); 02488 $posts = $DB->get_record_sql($sql, $params); 02489 if ($posts) { 02490 $sql = "SELECT count(*) as num 02491 FROM {forum_posts} p, 02492 {rating} r 02493 WHERE p.discussion = :discussionid AND 02494 p.id = r.itemid AND 02495 r.userid = userid AND 02496 r.component = 'mod_forum' AND 02497 r.ratingarea = 'post'"; 02498 $rated = $DB->get_record_sql($sql, $params); 02499 if ($rated) { 02500 if ($posts->num > $rated->num) { 02501 return $posts->num - $rated->num; 02502 } else { 02503 return 0; // Just in case there was a counting error 02504 } 02505 } else { 02506 return $posts->num; 02507 } 02508 } else { 02509 return 0; 02510 } 02511 } 02512 02531 function forum_get_discussions($cm, $forumsort="d.timemodified DESC", $fullpost=true, $unused=-1, $limit=-1, $userlastmodified=false, $page=-1, $perpage=0) { 02532 global $CFG, $DB, $USER; 02533 02534 $timelimit = ''; 02535 02536 $now = round(time(), -2); 02537 $params = array($cm->instance); 02538 02539 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id); 02540 02541 if (!has_capability('mod/forum:viewdiscussion', $modcontext)) { 02542 return array(); 02543 } 02544 02545 if (!empty($CFG->forum_enabletimedposts)) { 02546 02547 if (!has_capability('mod/forum:viewhiddentimedposts', $modcontext)) { 02548 $timelimit = " AND ((d.timestart <= ? AND (d.timeend = 0 OR d.timeend > ?))"; 02549 $params[] = $now; 02550 $params[] = $now; 02551 if (isloggedin()) { 02552 $timelimit .= " OR d.userid = ?"; 02553 $params[] = $USER->id; 02554 } 02555 $timelimit .= ")"; 02556 } 02557 } 02558 02559 if ($limit > 0) { 02560 $limitfrom = 0; 02561 $limitnum = $limit; 02562 } else if ($page != -1) { 02563 $limitfrom = $page*$perpage; 02564 $limitnum = $perpage; 02565 } else { 02566 $limitfrom = 0; 02567 $limitnum = 0; 02568 } 02569 02570 $groupmode = groups_get_activity_groupmode($cm); 02571 $currentgroup = groups_get_activity_group($cm); 02572 02573 if ($groupmode) { 02574 if (empty($modcontext)) { 02575 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id); 02576 } 02577 02578 if ($groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $modcontext)) { 02579 if ($currentgroup) { 02580 $groupselect = "AND (d.groupid = ? OR d.groupid = -1)"; 02581 $params[] = $currentgroup; 02582 } else { 02583 $groupselect = ""; 02584 } 02585 02586 } else { 02587 //seprate groups without access all 02588 if ($currentgroup) { 02589 $groupselect = "AND (d.groupid = ? OR d.groupid = -1)"; 02590 $params[] = $currentgroup; 02591 } else { 02592 $groupselect = "AND d.groupid = -1"; 02593 } 02594 } 02595 } else { 02596 $groupselect = ""; 02597 } 02598 02599 02600 if (empty($forumsort)) { 02601 $forumsort = "d.timemodified DESC"; 02602 } 02603 if (empty($fullpost)) { 02604 $postdata = "p.id,p.subject,p.modified,p.discussion,p.userid"; 02605 } else { 02606 $postdata = "p.*"; 02607 } 02608 02609 if (empty($userlastmodified)) { // We don't need to know this 02610 $umfields = ""; 02611 $umtable = ""; 02612 } else { 02613 $umfields = ", um.firstname AS umfirstname, um.lastname AS umlastname"; 02614 $umtable = " LEFT JOIN {user} um ON (d.usermodified = um.id)"; 02615 } 02616 02617 $sql = "SELECT $postdata, d.name, d.timemodified, d.usermodified, d.groupid, d.timestart, d.timeend, 02618 u.firstname, u.lastname, u.email, u.picture, u.imagealt $umfields 02619 FROM {forum_discussions} d 02620 JOIN {forum_posts} p ON p.discussion = d.id 02621 JOIN {user} u ON p.userid = u.id 02622 $umtable 02623 WHERE d.forum = ? AND p.parent = 0 02624 $timelimit $groupselect 02625 ORDER BY $forumsort"; 02626 return $DB->get_records_sql($sql, $params, $limitfrom, $limitnum); 02627 } 02628 02639 function forum_get_discussions_unread($cm) { 02640 global $CFG, $DB, $USER; 02641 02642 $now = round(time(), -2); 02643 $cutoffdate = $now - ($CFG->forum_oldpostdays*24*60*60); 02644 02645 $params = array(); 02646 $groupmode = groups_get_activity_groupmode($cm); 02647 $currentgroup = groups_get_activity_group($cm); 02648 02649 if ($groupmode) { 02650 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id); 02651 02652 if ($groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $modcontext)) { 02653 if ($currentgroup) { 02654 $groupselect = "AND (d.groupid = :currentgroup OR d.groupid = -1)"; 02655 $params['currentgroup'] = $currentgroup; 02656 } else { 02657 $groupselect = ""; 02658 } 02659 02660 } else { 02661 //separate groups without access all 02662 if ($currentgroup) { 02663 $groupselect = "AND (d.groupid = :currentgroup OR d.groupid = -1)"; 02664 $params['currentgroup'] = $currentgroup; 02665 } else { 02666 $groupselect = "AND d.groupid = -1"; 02667 } 02668 } 02669 } else { 02670 $groupselect = ""; 02671 } 02672 02673 if (!empty($CFG->forum_enabletimedposts)) { 02674 $timedsql = "AND d.timestart < :now1 AND (d.timeend = 0 OR d.timeend > :now2)"; 02675 $params['now1'] = $now; 02676 $params['now2'] = $now; 02677 } else { 02678 $timedsql = ""; 02679 } 02680 02681 $sql = "SELECT d.id, COUNT(p.id) AS unread 02682 FROM {forum_discussions} d 02683 JOIN {forum_posts} p ON p.discussion = d.id 02684 LEFT JOIN {forum_read} r ON (r.postid = p.id AND r.userid = $USER->id) 02685 WHERE d.forum = {$cm->instance} 02686 AND p.modified >= :cutoffdate AND r.id is NULL 02687 $groupselect 02688 $timedsql 02689 GROUP BY d.id"; 02690 $params['cutoffdate'] = $cutoffdate; 02691 02692 if ($unreads = $DB->get_records_sql($sql, $params)) { 02693 foreach ($unreads as $unread) { 02694 $unreads[$unread->id] = $unread->unread; 02695 } 02696 return $unreads; 02697 } else { 02698 return array(); 02699 } 02700 } 02701 02711 function forum_get_discussions_count($cm) { 02712 global $CFG, $DB, $USER; 02713 02714 $now = round(time(), -2); 02715 $params = array($cm->instance); 02716 $groupmode = groups_get_activity_groupmode($cm); 02717 $currentgroup = groups_get_activity_group($cm); 02718 02719 if ($groupmode) { 02720 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id); 02721 02722 if ($groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $modcontext)) { 02723 if ($currentgroup) { 02724 $groupselect = "AND (d.groupid = ? OR d.groupid = -1)"; 02725 $params[] = $currentgroup; 02726 } else { 02727 $groupselect = ""; 02728 } 02729 02730 } else { 02731 //seprate groups without access all 02732 if ($currentgroup) { 02733 $groupselect = "AND (d.groupid = ? OR d.groupid = -1)"; 02734 $params[] = $currentgroup; 02735 } else { 02736 $groupselect = "AND d.groupid = -1"; 02737 } 02738 } 02739 } else { 02740 $groupselect = ""; 02741 } 02742 02743 $cutoffdate = $now - ($CFG->forum_oldpostdays*24*60*60); 02744 02745 $timelimit = ""; 02746 02747 if (!empty($CFG->forum_enabletimedposts)) { 02748 02749 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id); 02750 02751 if (!has_capability('mod/forum:viewhiddentimedposts', $modcontext)) { 02752 $timelimit = " AND ((d.timestart <= ? AND (d.timeend = 0 OR d.timeend > ?))"; 02753 $params[] = $now; 02754 $params[] = $now; 02755 if (isloggedin()) { 02756 $timelimit .= " OR d.userid = ?"; 02757 $params[] = $USER->id; 02758 } 02759 $timelimit .= ")"; 02760 } 02761 } 02762 02763 $sql = "SELECT COUNT(d.id) 02764 FROM {forum_discussions} d 02765 JOIN {forum_posts} p ON p.discussion = d.id 02766 WHERE d.forum = ? AND p.parent = 0 02767 $groupselect $timelimit"; 02768 02769 return $DB->get_field_sql($sql, $params); 02770 } 02771 02772 02785 function forum_get_user_discussions($courseid, $userid, $groupid=0) { 02786 global $CFG, $DB; 02787 $params = array($courseid, $userid); 02788 if ($groupid) { 02789 $groupselect = " AND d.groupid = ? "; 02790 $params[] = $groupid; 02791 } else { 02792 $groupselect = ""; 02793 } 02794 02795 return $DB->get_records_sql("SELECT p.*, d.groupid, u.firstname, u.lastname, u.email, u.picture, u.imagealt, 02796 f.type as forumtype, f.name as forumname, f.id as forumid 02797 FROM {forum_discussions} d, 02798 {forum_posts} p, 02799 {user} u, 02800 {forum} f 02801 WHERE d.course = ? 02802 AND p.discussion = d.id 02803 AND p.parent = 0 02804 AND p.userid = u.id 02805 AND u.id = ? 02806 AND d.forum = f.id $groupselect 02807 ORDER BY p.created DESC", $params); 02808 } 02809 02819 function forum_get_potential_subscribers($forumcontext, $groupid, $fields, $sort) { 02820 global $DB; 02821 02822 // only active enrolled users or everybody on the frontpage 02823 list($esql, $params) = get_enrolled_sql($forumcontext, '', $groupid, true); 02824 02825 $sql = "SELECT $fields 02826 FROM {user} u 02827 JOIN ($esql) je ON je.id = u.id"; 02828 if ($sort) { 02829 $sql = "$sql ORDER BY $sort"; 02830 } else { 02831 $sql = "$sql ORDER BY u.lastname ASC, u.firstname ASC"; 02832 } 02833 02834 return $DB->get_records_sql($sql, $params); 02835 } 02836 02849 function forum_subscribed_users($course, $forum, $groupid=0, $context = null, $fields = null) { 02850 global $CFG, $DB; 02851 02852 if (empty($fields)) { 02853 $fields ="u.id, 02854 u.username, 02855 u.firstname, 02856 u.lastname, 02857 u.maildisplay, 02858 u.mailformat, 02859 u.maildigest, 02860 u.imagealt, 02861 u.email, 02862 u.emailstop, 02863 u.city, 02864 u.country, 02865 u.lastaccess, 02866 u.lastlogin, 02867 u.picture, 02868 u.timezone, 02869 u.theme, 02870 u.lang, 02871 u.trackforums, 02872 u.mnethostid"; 02873 } 02874 02875 if (empty($context)) { 02876 $cm = get_coursemodule_from_instance('forum', $forum->id, $course->id); 02877 $context = get_context_instance(CONTEXT_MODULE, $cm->id); 02878 } 02879 02880 if (forum_is_forcesubscribed($forum)) { 02881 $results = forum_get_potential_subscribers($context, $groupid, $fields, "u.email ASC"); 02882 02883 } else { 02884 // only active enrolled users or everybody on the frontpage 02885 list($esql, $params) = get_enrolled_sql($context, '', $groupid, true); 02886 $params['forumid'] = $forum->id; 02887 $results = $DB->get_records_sql("SELECT $fields 02888 FROM {user} u 02889 JOIN ($esql) je ON je.id = u.id 02890 JOIN {forum_subscriptions} s ON s.userid = u.id 02891 WHERE s.forum = :forumid 02892 ORDER BY u.email ASC", $params); 02893 } 02894 02895 // Guest user should never be subscribed to a forum. 02896 unset($results[$CFG->siteguest]); 02897 02898 return $results; 02899 } 02900 02901 02902 02903 // OTHER FUNCTIONS /////////////////////////////////////////////////////////// 02904 02905 02912 function forum_get_course_forum($courseid, $type) { 02913 // How to set up special 1-per-course forums 02914 global $CFG, $DB, $OUTPUT; 02915 02916 if ($forums = $DB->get_records_select("forum", "course = ? AND type = ?", array($courseid, $type), "id ASC")) { 02917 // There should always only be ONE, but with the right combination of 02918 // errors there might be more. In this case, just return the oldest one (lowest ID). 02919 foreach ($forums as $forum) { 02920 return $forum; // ie the first one 02921 } 02922 } 02923 02924 // Doesn't exist, so create one now. 02925 $forum = new stdClass(); 02926 $forum->course = $courseid; 02927 $forum->type = "$type"; 02928 switch ($forum->type) { 02929 case "news": 02930 $forum->name = get_string("namenews", "forum"); 02931 $forum->intro = get_string("intronews", "forum"); 02932 $forum->forcesubscribe = FORUM_FORCESUBSCRIBE; 02933 $forum->assessed = 0; 02934 if ($courseid == SITEID) { 02935 $forum->name = get_string("sitenews"); 02936 $forum->forcesubscribe = 0; 02937 } 02938 break; 02939 case "social": 02940 $forum->name = get_string("namesocial", "forum"); 02941 $forum->intro = get_string("introsocial", "forum"); 02942 $forum->assessed = 0; 02943 $forum->forcesubscribe = 0; 02944 break; 02945 case "blog": 02946 $forum->name = get_string('blogforum', 'forum'); 02947 $forum->intro = get_string('introblog', 'forum'); 02948 $forum->assessed = 0; 02949 $forum->forcesubscribe = 0; 02950 break; 02951 default: 02952 echo $OUTPUT->notification("That forum type doesn't exist!"); 02953 return false; 02954 break; 02955 } 02956 02957 $forum->timemodified = time(); 02958 $forum->id = $DB->insert_record("forum", $forum); 02959 02960 if (! $module = $DB->get_record("modules", array("name" => "forum"))) { 02961 echo $OUTPUT->notification("Could not find forum module!!"); 02962 return false; 02963 } 02964 $mod = new stdClass(); 02965 $mod->course = $courseid; 02966 $mod->module = $module->id; 02967 $mod->instance = $forum->id; 02968 $mod->section = 0; 02969 if (! $mod->coursemodule = add_course_module($mod) ) { // assumes course/lib.php is loaded 02970 echo $OUTPUT->notification("Could not add a new course module to the course '" . $courseid . "'"); 02971 return false; 02972 } 02973 if (! $sectionid = add_mod_to_section($mod) ) { // assumes course/lib.php is loaded 02974 echo $OUTPUT->notification("Could not add the new course module to that section"); 02975 return false; 02976 } 02977 $DB->set_field("course_modules", "section", $sectionid, array("id" => $mod->coursemodule)); 02978 02979 include_once("$CFG->dirroot/course/lib.php"); 02980 rebuild_course_cache($courseid); 02981 02982 return $DB->get_record("forum", array("id" => "$forum->id")); 02983 } 02984 02985 03005 function forum_make_mail_post($course, $cm, $forum, $discussion, $post, $userfrom, $userto, 03006 $ownpost=false, $reply=false, $link=false, $rate=false, $footer="") { 03007 03008 global $CFG, $OUTPUT; 03009 03010 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id); 03011 03012 if (!isset($userto->viewfullnames[$forum->id])) { 03013 $viewfullnames = has_capability('moodle/site:viewfullnames', $modcontext, $userto->id); 03014 } else { 03015 $viewfullnames = $userto->viewfullnames[$forum->id]; 03016 } 03017 03018 // add absolute file links 03019 $post->message = file_rewrite_pluginfile_urls($post->message, 'pluginfile.php', $modcontext->id, 'mod_forum', 'post', $post->id); 03020 03021 // format the post body 03022 $options = new stdClass(); 03023 $options->para = true; 03024 $formattedtext = format_text($post->message, $post->messageformat, $options, $course->id); 03025 03026 $output = '<table border="0" cellpadding="3" cellspacing="0" class="forumpost">'; 03027 03028 $output .= '<tr class="header"><td width="35" valign="top" class="picture left">'; 03029 $output .= $OUTPUT->user_picture($userfrom, array('courseid'=>$course->id)); 03030 $output .= '</td>'; 03031 03032 if ($post->parent) { 03033 $output .= '<td class="topic">'; 03034 } else { 03035 $output .= '<td class="topic starter">'; 03036 } 03037 $output .= '<div class="subject">'.format_string($post->subject).'</div>'; 03038 03039 $fullname = fullname($userfrom, $viewfullnames); 03040 $by = new stdClass(); 03041 $by->name = '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$userfrom->id.'&course='.$course->id.'">'.$fullname.'</a>'; 03042 $by->date = userdate($post->modified, '', $userto->timezone); 03043 $output .= '<div class="author">'.get_string('bynameondate', 'forum', $by).'</div>'; 03044 03045 $output .= '</td></tr>'; 03046 03047 $output .= '<tr><td class="left side" valign="top">'; 03048 03049 if (isset($userfrom->groups)) { 03050 $groups = $userfrom->groups[$forum->id]; 03051 } else { 03052 $groups = groups_get_all_groups($course->id, $userfrom->id, $cm->groupingid); 03053 } 03054 03055 if ($groups) { 03056 $output .= print_group_picture($groups, $course->id, false, true, true); 03057 } else { 03058 $output .= ' '; 03059 } 03060 03061 $output .= '</td><td class="content">'; 03062 03063 $attachments = forum_print_attachments($post, $cm, 'html'); 03064 if ($attachments !== '') { 03065 $output .= '<div class="attachments">'; 03066 $output .= $attachments; 03067 $output .= '</div>'; 03068 } 03069 03070 $output .= $formattedtext; 03071 03072 // Commands 03073 $commands = array(); 03074 03075 if ($post->parent) { 03076 $commands[] = '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='. 03077 $post->discussion.'&parent='.$post->parent.'">'.get_string('parent', 'forum').'</a>'; 03078 } 03079 03080 if ($reply) { 03081 $commands[] = '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/post.php?reply='.$post->id.'">'. 03082 get_string('reply', 'forum').'</a>'; 03083 } 03084 03085 $output .= '<div class="commands">'; 03086 $output .= implode(' | ', $commands); 03087 $output .= '</div>'; 03088 03089 // Context link to post if required 03090 if ($link) { 03091 $output .= '<div class="link">'; 03092 $output .= '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'#p'.$post->id.'">'. 03093 get_string('postincontext', 'forum').'</a>'; 03094 $output .= '</div>'; 03095 } 03096 03097 if ($footer) { 03098 $output .= '<div class="footer">'.$footer.'</div>'; 03099 } 03100 $output .= '</td></tr></table>'."\n\n"; 03101 03102 return $output; 03103 } 03104 03136 function forum_print_post($post, $discussion, $forum, &$cm, $course, $ownpost=false, $reply=false, $link=false, 03137 $footer="", $highlight="", $postisread=null, $dummyifcantsee=true, $istracked=null, $return=false) { 03138 global $USER, $CFG, $OUTPUT; 03139 03140 require_once($CFG->libdir . '/filelib.php'); 03141 03142 // String cache 03143 static $str; 03144 03145 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id); 03146 03147 $post->course = $course->id; 03148 $post->forum = $forum->id; 03149 $post->message = file_rewrite_pluginfile_urls($post->message, 'pluginfile.php', $modcontext->id, 'mod_forum', 'post', $post->id); 03150 03151 // caching 03152 if (!isset($cm->cache)) { 03153 $cm->cache = new stdClass; 03154 } 03155 03156 if (!isset($cm->cache->caps)) { 03157 $cm->cache->caps = array(); 03158 $cm->cache->caps['mod/forum:viewdiscussion'] = has_capability('mod/forum:viewdiscussion', $modcontext); 03159 $cm->cache->caps['moodle/site:viewfullnames'] = has_capability('moodle/site:viewfullnames', $modcontext); 03160 $cm->cache->caps['mod/forum:editanypost'] = has_capability('mod/forum:editanypost', $modcontext); 03161 $cm->cache->caps['mod/forum:splitdiscussions'] = has_capability('mod/forum:splitdiscussions', $modcontext); 03162 $cm->cache->caps['mod/forum:deleteownpost'] = has_capability('mod/forum:deleteownpost', $modcontext); 03163 $cm->cache->caps['mod/forum:deleteanypost'] = has_capability('mod/forum:deleteanypost', $modcontext); 03164 $cm->cache->caps['mod/forum:viewanyrating'] = has_capability('mod/forum:viewanyrating', $modcontext); 03165 $cm->cache->caps['mod/forum:exportpost'] = has_capability('mod/forum:exportpost', $modcontext); 03166 $cm->cache->caps['mod/forum:exportownpost'] = has_capability('mod/forum:exportownpost', $modcontext); 03167 } 03168 03169 if (!isset($cm->uservisible)) { 03170 $cm->uservisible = coursemodule_visible_for_user($cm); 03171 } 03172 03173 if ($istracked && is_null($postisread)) { 03174 $postisread = forum_tp_is_post_read($USER->id, $post); 03175 } 03176 03177 if (!forum_user_can_see_post($forum, $discussion, $post, NULL, $cm)) { 03178 $output = ''; 03179 if (!$dummyifcantsee) { 03180 if ($return) { 03181 return $output; 03182 } 03183 echo $output; 03184 return; 03185 } 03186 $output .= html_writer::tag('a', '', array('id'=>'p'.$post->id)); 03187 $output .= html_writer::start_tag('div', array('class'=>'forumpost clearfix')); 03188 $output .= html_writer::start_tag('div', array('class'=>'row header')); 03189 $output .= html_writer::tag('div', '', array('class'=>'left picture')); // Picture 03190 if ($post->parent) { 03191 $output .= html_writer::start_tag('div', array('class'=>'topic')); 03192 } else { 03193 $output .= html_writer::start_tag('div', array('class'=>'topic starter')); 03194 } 03195 $output .= html_writer::tag('div', get_string('forumsubjecthidden','forum'), array('class'=>'subject')); // Subject 03196 $output .= html_writer::tag('div', get_string('forumauthorhidden','forum'), array('class'=>'author')); // author 03197 $output .= html_writer::end_tag('div'); 03198 $output .= html_writer::end_tag('div'); // row 03199 $output .= html_writer::start_tag('div', array('class'=>'row')); 03200 $output .= html_writer::tag('div', ' ', array('class'=>'left side')); // Groups 03201 $output .= html_writer::tag('div', get_string('forumbodyhidden','forum'), array('class'=>'content')); // Content 03202 $output .= html_writer::end_tag('div'); // row 03203 $output .= html_writer::end_tag('div'); // forumpost 03204 03205 if ($return) { 03206 return $output; 03207 } 03208 echo $output; 03209 return; 03210 } 03211 03212 if (empty($str)) { 03213 $str = new stdClass; 03214 $str->edit = get_string('edit', 'forum'); 03215 $str->delete = get_string('delete', 'forum'); 03216 $str->reply = get_string('reply', 'forum'); 03217 $str->parent = get_string('parent', 'forum'); 03218 $str->pruneheading = get_string('pruneheading', 'forum'); 03219 $str->prune = get_string('prune', 'forum'); 03220 $str->displaymode = get_user_preferences('forum_displaymode', $CFG->forum_displaymode); 03221 $str->markread = get_string('markread', 'forum'); 03222 $str->markunread = get_string('markunread', 'forum'); 03223 } 03224 03225 $discussionlink = new moodle_url('/mod/forum/discuss.php', array('d'=>$post->discussion)); 03226 03227 // Build an object that represents the posting user 03228 $postuser = new stdClass; 03229 $postuser->id = $post->userid; 03230 $postuser->firstname = $post->firstname; 03231 $postuser->lastname = $post->lastname; 03232 $postuser->imagealt = $post->imagealt; 03233 $postuser->picture = $post->picture; 03234 $postuser->email = $post->email; 03235 // Some handy things for later on 03236 $postuser->fullname = fullname($postuser, $cm->cache->caps['moodle/site:viewfullnames']); 03237 $postuser->profilelink = new moodle_url('/user/view.php', array('id'=>$post->userid, 'course'=>$course->id)); 03238 03239 // Prepare the groups the posting user belongs to 03240 if (isset($cm->cache->usersgroups)) { 03241 $groups = array(); 03242 if (isset($cm->cache->usersgroups[$post->userid])) { 03243 foreach ($cm->cache->usersgroups[$post->userid] as $gid) { 03244 $groups[$gid] = $cm->cache->groups[$gid]; 03245 } 03246 } 03247 } else { 03248 $groups = groups_get_all_groups($course->id, $post->userid, $cm->groupingid); 03249 } 03250 03251 // Prepare the attachements for the post, files then images 03252 list($attachments, $attachedimages) = forum_print_attachments($post, $cm, 'separateimages'); 03253 03254 // Determine if we need to shorten this post 03255 $shortenpost = ($link && (strlen(strip_tags($post->message)) > $CFG->forum_longpost)); 03256 03257 03258 // Prepare an array of commands 03259 $commands = array(); 03260 03261 // SPECIAL CASE: The front page can display a news item post to non-logged in users. 03262 // Don't display the mark read / unread controls in this case. 03263 if ($istracked && $CFG->forum_usermarksread && isloggedin()) { 03264 $url = new moodle_url($discussionlink, array('postid'=>$post->id, 'mark'=>'unread')); 03265 $text = $str->markunread; 03266 if (!$postisread) { 03267 $url->param('mark', 'read'); 03268 $text = $str->markread; 03269 } 03270 if ($str->displaymode == FORUM_MODE_THREADED) { 03271 $url->param('parent', $post->parent); 03272 } else { 03273 $url->set_anchor('p'.$post->id); 03274 } 03275 $commands[] = array('url'=>$url, 'text'=>$text); 03276 } 03277 03278 // Zoom in to the parent specifically 03279 if ($post->parent) { 03280 $url = new moodle_url($discussionlink); 03281 if ($str->displaymode == FORUM_MODE_THREADED) { 03282 $url->param('parent', $post->parent); 03283 } else { 03284 $url->set_anchor('p'.$post->parent); 03285 } 03286 $commands[] = array('url'=>$url, 'text'=>$str->parent); 03287 } 03288 03289 // Hack for allow to edit news posts those are not displayed yet until they are displayed 03290 $age = time() - $post->created; 03291 if (!$post->parent && $forum->type == 'news' && $discussion->timestart > time()) { 03292 $age = 0; 03293 } 03294 if (($ownpost && $age < $CFG->maxeditingtime) || $cm->cache->caps['mod/forum:editanypost']) { 03295 $commands[] = array('url'=>new moodle_url('/mod/forum/post.php', array('edit'=>$post->id)), 'text'=>$str->edit); 03296 } 03297 03298 if ($cm->cache->caps['mod/forum:splitdiscussions'] && $post->parent && $forum->type != 'single') { 03299 $commands[] = array('url'=>new moodle_url('/mod/forum/post.php', array('prune'=>$post->id)), 'text'=>$str->prune, 'title'=>$str->pruneheading); 03300 } 03301 03302 if (($ownpost && $age < $CFG->maxeditingtime && $cm->cache->caps['mod/forum:deleteownpost']) || $cm->cache->caps['mod/forum:deleteanypost']) { 03303 $commands[] = array('url'=>new moodle_url('/mod/forum/post.php', array('delete'=>$post->id)), 'text'=>$str->delete); 03304 } 03305 03306 if ($reply) { 03307 $commands[] = array('url'=>new moodle_url('/mod/forum/post.php', array('reply'=>$post->id)), 'text'=>$str->reply); 03308 } 03309 03310 if ($CFG->enableportfolios && ($cm->cache->caps['mod/forum:exportpost'] || ($ownpost && $cm->cache->caps['mod/forum:exportownpost']))) { 03311 $p = array('postid' => $post->id); 03312 require_once($CFG->libdir.'/portfoliolib.php'); 03313 $button = new portfolio_add_button(); 03314 $button->set_callback_options('forum_portfolio_caller', array('postid' => $post->id), '/mod/forum/locallib.php'); 03315 if (empty($attachments)) { 03316 $button->set_formats(PORTFOLIO_FORMAT_PLAINHTML); 03317 } else { 03318 $button->set_formats(PORTFOLIO_FORMAT_RICHHTML); 03319 } 03320 03321 $porfoliohtml = $button->to_html(PORTFOLIO_ADD_TEXT_LINK); 03322 if (!empty($porfoliohtml)) { 03323 $commands[] = $porfoliohtml; 03324 } 03325 } 03326 // Finished building commands 03327 03328 03329 // Begin output 03330 03331 $output = ''; 03332 03333 if ($istracked) { 03334 if ($postisread) { 03335 $forumpostclass = ' read'; 03336 } else { 03337 $forumpostclass = ' unread'; 03338 $output .= html_writer::tag('a', '', array('name'=>'unread')); 03339 } 03340 } else { 03341 // ignore trackign status if not tracked or tracked param missing 03342 $forumpostclass = ''; 03343 } 03344 03345 $topicclass = ''; 03346 if (empty($post->parent)) { 03347 $topicclass = ' firstpost starter'; 03348 } 03349 03350 $output .= html_writer::tag('a', '', array('id'=>'p'.$post->id)); 03351 $output .= html_writer::start_tag('div', array('class'=>'forumpost clearfix'.$forumpostclass.$topicclass)); 03352 $output .= html_writer::start_tag('div', array('class'=>'row header clearfix')); 03353 $output .= html_writer::start_tag('div', array('class'=>'left picture')); 03354 $output .= $OUTPUT->user_picture($postuser, array('courseid'=>$course->id)); 03355 $output .= html_writer::end_tag('div'); 03356 03357 03358 $output .= html_writer::start_tag('div', array('class'=>'topic'.$topicclass)); 03359 03360 $postsubject = $post->subject; 03361 if (empty($post->subjectnoformat)) { 03362 $postsubject = format_string($postsubject); 03363 } 03364 $output .= html_writer::tag('div', $postsubject, array('class'=>'subject')); 03365 03366 $by = new stdClass(); 03367 $by->name = html_writer::link($postuser->profilelink, $postuser->fullname); 03368 $by->date = userdate($post->modified); 03369 $output .= html_writer::tag('div', get_string('bynameondate', 'forum', $by), array('class'=>'author')); 03370 03371 $output .= html_writer::end_tag('div'); //topic 03372 $output .= html_writer::end_tag('div'); //row 03373 03374 $output .= html_writer::start_tag('div', array('class'=>'row maincontent clearfix')); 03375 $output .= html_writer::start_tag('div', array('class'=>'left')); 03376 03377 $groupoutput = ''; 03378 if ($groups) { 03379 $groupoutput = print_group_picture($groups, $course->id, false, true, true); 03380 } 03381 if (empty($groupoutput)) { 03382 $groupoutput = ' '; 03383 } 03384 $output .= html_writer::tag('div', $groupoutput, array('class'=>'grouppictures')); 03385 03386 $output .= html_writer::end_tag('div'); //left side 03387 $output .= html_writer::start_tag('div', array('class'=>'no-overflow')); 03388 $output .= html_writer::start_tag('div', array('class'=>'content')); 03389 if (!empty($attachments)) { 03390 $output .= html_writer::tag('div', $attachments, array('class'=>'attachments')); 03391 } 03392 03393 $options = new stdClass; 03394 $options->para = false; 03395 $options->trusted = $post->messagetrust; 03396 $options->context = $modcontext; 03397 if ($shortenpost) { 03398 // Prepare shortened version 03399 $postclass = 'shortenedpost'; 03400 $postcontent = format_text(forum_shorten_post($post->message), $post->messageformat, $options, $course->id); 03401 $postcontent .= html_writer::link($discussionlink, get_string('readtherest', 'forum')); 03402 $postcontent .= html_writer::tag('span', '('.get_string('numwords', 'moodle', count_words(strip_tags($post->message))).')...', array('class'=>'post-word-count')); 03403 } else { 03404 // Prepare whole post 03405 $postclass = 'fullpost'; 03406 $postcontent = format_text($post->message, $post->messageformat, $options, $course->id); 03407 if (!empty($highlight)) { 03408 $postcontent = highlight($highlight, $postcontent); 03409 } 03410 $postcontent .= html_writer::tag('div', $attachedimages, array('class'=>'attachedimages')); 03411 } 03412 // Output the post content 03413 $output .= html_writer::tag('div', $postcontent, array('class'=>'posting '.$postclass)); 03414 $output .= html_writer::end_tag('div'); // Content 03415 $output .= html_writer::end_tag('div'); // Content mask 03416 $output .= html_writer::end_tag('div'); // Row 03417 03418 $output .= html_writer::start_tag('div', array('class'=>'row side')); 03419 $output .= html_writer::tag('div',' ', array('class'=>'left')); 03420 $output .= html_writer::start_tag('div', array('class'=>'options clearfix')); 03421 03422 // Output ratings 03423 if (!empty($post->rating)) { 03424 $output .= html_writer::tag('div', $OUTPUT->render($post->rating), array('class'=>'forum-post-rating')); 03425 } 03426 03427 // Output the commands 03428 $commandhtml = array(); 03429 foreach ($commands as $command) { 03430 if (is_array($command)) { 03431 $commandhtml[] = html_writer::link($command['url'], $command['text']); 03432 } else { 03433 $commandhtml[] = $command; 03434 } 03435 } 03436 $output .= html_writer::tag('div', implode(' | ', $commandhtml), array('class'=>'commands')); 03437 03438 // Output link to post if required 03439 if ($link) { 03440 if ($post->replies == 1) { 03441 $replystring = get_string('repliesone', 'forum', $post->replies); 03442 } else { 03443 $replystring = get_string('repliesmany', 'forum', $post->replies); 03444 } 03445 03446 $output .= html_writer::start_tag('div', array('class'=>'link')); 03447 $output .= html_writer::link($discussionlink, get_string('discussthistopic', 'forum')); 03448 $output .= ' ('.$replystring.')'; 03449 $output .= html_writer::end_tag('div'); // link 03450 } 03451 03452 // Output footer if required 03453 if ($footer) { 03454 $output .= html_writer::tag('div', $footer, array('class'=>'footer')); 03455 } 03456 03457 // Close remaining open divs 03458 $output .= html_writer::end_tag('div'); // content 03459 $output .= html_writer::end_tag('div'); // row 03460 $output .= html_writer::end_tag('div'); // forumpost 03461 03462 // Mark the forum post as read if required 03463 if ($istracked && !$CFG->forum_usermarksread && !$postisread) { 03464 forum_tp_mark_post_read($USER->id, $post, $forum->id); 03465 } 03466 03467 if ($return) { 03468 return $output; 03469 } 03470 echo $output; 03471 return; 03472 } 03473 03480 function forum_rating_permissions($contextid, $component, $ratingarea) { 03481 $context = get_context_instance_by_id($contextid, MUST_EXIST); 03482 if ($component != 'mod_forum' || $ratingarea != 'post') { 03483 // We don't know about this component/ratingarea so just return null to get the 03484 // default restrictive permissions. 03485 return null; 03486 } 03487 return array( 03488 'view' => has_capability('mod/forum:viewrating', $context), 03489 'viewany' => has_capability('mod/forum:viewanyrating', $context), 03490 'viewall' => has_capability('mod/forum:viewallratings', $context), 03491 'rate' => has_capability('mod/forum:rate', $context) 03492 ); 03493 } 03494 03508 function forum_rating_validate($params) { 03509 global $DB, $USER; 03510 03511 // Check the component is mod_forum 03512 if ($params['component'] != 'mod_forum') { 03513 throw new rating_exception('invalidcomponent'); 03514 } 03515 03516 // Check the ratingarea is post (the only rating area in forum) 03517 if ($params['ratingarea'] != 'post') { 03518 throw new rating_exception('invalidratingarea'); 03519 } 03520 03521 // Check the rateduserid is not the current user .. you can't rate your own posts 03522 if ($params['rateduserid'] == $USER->id) { 03523 throw new rating_exception('nopermissiontorate'); 03524 } 03525 03526 // Fetch all the related records ... we need to do this anyway to call forum_user_can_see_post 03527 $post = $DB->get_record('forum_posts', array('id' => $params['itemid'], 'userid' => $params['rateduserid']), '*', MUST_EXIST); 03528 $discussion = $DB->get_record('forum_discussions', array('id' => $post->discussion), '*', MUST_EXIST); 03529 $forum = $DB->get_record('forum', array('id' => $discussion->forum), '*', MUST_EXIST); 03530 $course = $DB->get_record('course', array('id' => $forum->course), '*', MUST_EXIST); 03531 $cm = get_coursemodule_from_instance('forum', $forum->id, $course->id , false, MUST_EXIST); 03532 $context = get_context_instance(CONTEXT_MODULE, $cm->id); 03533 03534 // Make sure the context provided is the context of the forum 03535 if ($context->id != $params['context']->id) { 03536 throw new rating_exception('invalidcontext'); 03537 } 03538 03539 if ($forum->scale != $params['scaleid']) { 03540 //the scale being submitted doesnt match the one in the database 03541 throw new rating_exception('invalidscaleid'); 03542 } 03543 03544 // check the item we're rating was created in the assessable time window 03545 if (!empty($forum->assesstimestart) && !empty($forum->assesstimefinish)) { 03546 if ($post->created < $forum->assesstimestart || $post->created > $forum->assesstimefinish) { 03547 throw new rating_exception('notavailable'); 03548 } 03549 } 03550 03551 //check that the submitted rating is valid for the scale 03552 03553 // lower limit 03554 if ($params['rating'] < 0 && $params['rating'] != RATING_UNSET_RATING) { 03555 throw new rating_exception('invalidnum'); 03556 } 03557 03558 // upper limit 03559 if ($forum->scale < 0) { 03560 //its a custom scale 03561 $scalerecord = $DB->get_record('scale', array('id' => -$forum->scale)); 03562 if ($scalerecord) { 03563 $scalearray = explode(',', $scalerecord->scale); 03564 if ($params['rating'] > count($scalearray)) { 03565 throw new rating_exception('invalidnum'); 03566 } 03567 } else { 03568 throw new rating_exception('invalidscaleid'); 03569 } 03570 } else if ($params['rating'] > $forum->scale) { 03571 //if its numeric and submitted rating is above maximum 03572 throw new rating_exception('invalidnum'); 03573 } 03574 03575 // Make sure groups allow this user to see the item they're rating 03576 if ($discussion->groupid > 0 and $groupmode = groups_get_activity_groupmode($cm, $course)) { // Groups are being used 03577 if (!groups_group_exists($discussion->groupid)) { // Can't find group 03578 throw new rating_exception('cannotfindgroup');//something is wrong 03579 } 03580 03581 if (!groups_is_member($discussion->groupid) and !has_capability('moodle/site:accessallgroups', $context)) { 03582 // do not allow rating of posts from other groups when in SEPARATEGROUPS or VISIBLEGROUPS 03583 throw new rating_exception('notmemberofgroup'); 03584 } 03585 } 03586 03587 // perform some final capability checks 03588 if (!forum_user_can_see_post($forum, $discussion, $post, $USER, $cm)) { 03589 throw new rating_exception('nopermissiontorate'); 03590 } 03591 03592 return true; 03593 } 03594 03595 03612 function forum_print_discussion_header(&$post, $forum, $group=-1, $datestring="", 03613 $cantrack=true, $forumtracked=true, $canviewparticipants=true, $modcontext=NULL) { 03614 03615 global $USER, $CFG, $OUTPUT; 03616 03617 static $rowcount; 03618 static $strmarkalldread; 03619 03620 if (empty($modcontext)) { 03621 if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) { 03622 print_error('invalidcoursemodule'); 03623 } 03624 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id); 03625 } 03626 03627 if (!isset($rowcount)) { 03628 $rowcount = 0; 03629 $strmarkalldread = get_string('markalldread', 'forum'); 03630 } else { 03631 $rowcount = ($rowcount + 1) % 2; 03632 } 03633 03634 $post->subject = format_string($post->subject,true); 03635 03636 echo "\n\n"; 03637 echo '<tr class="discussion r'.$rowcount.'">'; 03638 03639 // Topic 03640 echo '<td class="topic starter">'; 03641 echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'">'.$post->subject.'</a>'; 03642 echo "</td>\n"; 03643 03644 // Picture 03645 $postuser = new stdClass(); 03646 $postuser->id = $post->userid; 03647 $postuser->firstname = $post->firstname; 03648 $postuser->lastname = $post->lastname; 03649 $postuser->imagealt = $post->imagealt; 03650 $postuser->picture = $post->picture; 03651 $postuser->email = $post->email; 03652 03653 echo '<td class="picture">'; 03654 echo $OUTPUT->user_picture($postuser, array('courseid'=>$forum->course)); 03655 echo "</td>\n"; 03656 03657 // User name 03658 $fullname = fullname($post, has_capability('moodle/site:viewfullnames', $modcontext)); 03659 echo '<td class="author">'; 03660 echo '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$post->userid.'&course='.$forum->course.'">'.$fullname.'</a>'; 03661 echo "</td>\n"; 03662 03663 // Group picture 03664 if ($group !== -1) { // Groups are active - group is a group data object or NULL 03665 echo '<td class="picture group">'; 03666 if (!empty($group->picture) and empty($group->hidepicture)) { 03667 print_group_picture($group, $forum->course, false, false, true); 03668 } else if (isset($group->id)) { 03669 if($canviewparticipants) { 03670 echo '<a href="'.$CFG->wwwroot.'/user/index.php?id='.$forum->course.'&group='.$group->id.'">'.$group->name.'</a>'; 03671 } else { 03672 echo $group->name; 03673 } 03674 } 03675 echo "</td>\n"; 03676 } 03677 03678 if (has_capability('mod/forum:viewdiscussion', $modcontext)) { // Show the column with replies 03679 echo '<td class="replies">'; 03680 echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'">'; 03681 echo $post->replies.'</a>'; 03682 echo "</td>\n"; 03683 03684 if ($cantrack) { 03685 echo '<td class="replies">'; 03686 if ($forumtracked) { 03687 if ($post->unread > 0) { 03688 echo '<span class="unread">'; 03689 echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'#unread">'; 03690 echo $post->unread; 03691 echo '</a>'; 03692 echo '<a title="'.$strmarkalldread.'" href="'.$CFG->wwwroot.'/mod/forum/markposts.php?f='. 03693 $forum->id.'&d='.$post->discussion.'&mark=read&returnpage=view.php">' . 03694 '<img src="'.$OUTPUT->pix_url('t/clear') . '" class="iconsmall" alt="'.$strmarkalldread.'" /></a>'; 03695 echo '</span>'; 03696 } else { 03697 echo '<span class="read">'; 03698 echo $post->unread; 03699 echo '</span>'; 03700 } 03701 } else { 03702 echo '<span class="read">'; 03703 echo '-'; 03704 echo '</span>'; 03705 } 03706 echo "</td>\n"; 03707 } 03708 } 03709 03710 echo '<td class="lastpost">'; 03711 $usedate = (empty($post->timemodified)) ? $post->modified : $post->timemodified; // Just in case 03712 $parenturl = (empty($post->lastpostid)) ? '' : '&parent='.$post->lastpostid; 03713 $usermodified = new stdClass(); 03714 $usermodified->id = $post->usermodified; 03715 $usermodified->firstname = $post->umfirstname; 03716 $usermodified->lastname = $post->umlastname; 03717 echo '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$post->usermodified.'&course='.$forum->course.'">'. 03718 fullname($usermodified).'</a><br />'; 03719 echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.$parenturl.'">'. 03720 userdate($usedate, $datestring).'</a>'; 03721 echo "</td>\n"; 03722 03723 echo "</tr>\n\n"; 03724 03725 } 03726 03727 03737 function forum_shorten_post($message) { 03738 03739 global $CFG; 03740 03741 $i = 0; 03742 $tag = false; 03743 $length = strlen($message); 03744 $count = 0; 03745 $stopzone = false; 03746 $truncate = 0; 03747 03748 for ($i=0; $i<$length; $i++) { 03749 $char = $message[$i]; 03750 03751 switch ($char) { 03752 case "<": 03753 $tag = true; 03754 break; 03755 case ">": 03756 $tag = false; 03757 break; 03758 default: 03759 if (!$tag) { 03760 if ($stopzone) { 03761 if ($char == ".") { 03762 $truncate = $i+1; 03763 break 2; 03764 } 03765 } 03766 $count++; 03767 } 03768 break; 03769 } 03770 if (!$stopzone) { 03771 if ($count > $CFG->forum_shortpost) { 03772 $stopzone = true; 03773 } 03774 } 03775 } 03776 03777 if (!$truncate) { 03778 $truncate = $i; 03779 } 03780 03781 return substr($message, 0, $truncate); 03782 } 03783 03793 function forum_print_mode_form($id, $mode, $forumtype='') { 03794 global $OUTPUT; 03795 if ($forumtype == 'single') { 03796 $select = new single_select(new moodle_url("/mod/forum/view.php", array('f'=>$id)), 'mode', forum_get_layout_modes(), $mode, null, "mode"); 03797 $select->class = "forummode"; 03798 } else { 03799 $select = new single_select(new moodle_url("/mod/forum/discuss.php", array('d'=>$id)), 'mode', forum_get_layout_modes(), $mode, null, "mode"); 03800 } 03801 echo $OUTPUT->render($select); 03802 } 03803 03810 function forum_search_form($course, $search='') { 03811 global $CFG, $OUTPUT; 03812 03813 $output = '<div class="forumsearch">'; 03814 $output .= '<form action="'.$CFG->wwwroot.'/mod/forum/search.php" style="display:inline">'; 03815 $output .= '<fieldset class="invisiblefieldset">'; 03816 $output .= $OUTPUT->help_icon('search'); 03817 $output .= '<label class="accesshide" for="search" >'.get_string('search', 'forum').'</label>'; 03818 $output .= '<input id="search" name="search" type="text" size="18" value="'.s($search, true).'" alt="search" />'; 03819 $output .= '<label class="accesshide" for="searchforums" >'.get_string('searchforums', 'forum').'</label>'; 03820 $output .= '<input id="searchforums" value="'.get_string('searchforums', 'forum').'" type="submit" />'; 03821 $output .= '<input name="id" type="hidden" value="'.$course->id.'" />'; 03822 $output .= '</fieldset>'; 03823 $output .= '</form>'; 03824 $output .= '</div>'; 03825 03826 return $output; 03827 } 03828 03829 03834 function forum_set_return() { 03835 global $CFG, $SESSION; 03836 03837 if (! isset($SESSION->fromdiscussion)) { 03838 if (!empty($_SERVER['HTTP_REFERER'])) { 03839 $referer = $_SERVER['HTTP_REFERER']; 03840 } else { 03841 $referer = ""; 03842 } 03843 // If the referer is NOT a login screen then save it. 03844 if (! strncasecmp("$CFG->wwwroot/login", $referer, 300)) { 03845 $SESSION->fromdiscussion = $_SERVER["HTTP_REFERER"]; 03846 } 03847 } 03848 } 03849 03850 03856 function forum_go_back_to($default) { 03857 global $SESSION; 03858 03859 if (!empty($SESSION->fromdiscussion)) { 03860 $returnto = $SESSION->fromdiscussion; 03861 unset($SESSION->fromdiscussion); 03862 return $returnto; 03863 } else { 03864 return $default; 03865 } 03866 } 03867 03880 function forum_move_attachments($discussion, $forumfrom, $forumto) { 03881 global $DB; 03882 03883 $fs = get_file_storage(); 03884 03885 $newcm = get_coursemodule_from_instance('forum', $forumto); 03886 $oldcm = get_coursemodule_from_instance('forum', $forumfrom); 03887 03888 $newcontext = get_context_instance(CONTEXT_MODULE, $newcm->id); 03889 $oldcontext = get_context_instance(CONTEXT_MODULE, $oldcm->id); 03890 03891 // loop through all posts, better not use attachment flag ;-) 03892 if ($posts = $DB->get_records('forum_posts', array('discussion'=>$discussion->id), '', 'id, attachment')) { 03893 foreach ($posts as $post) { 03894 $fs->move_area_files_to_new_context($oldcontext->id, 03895 $newcontext->id, 'mod_forum', 'post', $post->id); 03896 $attachmentsmoved = $fs->move_area_files_to_new_context($oldcontext->id, 03897 $newcontext->id, 'mod_forum', 'attachment', $post->id); 03898 if ($attachmentsmoved > 0 && $post->attachment != '1') { 03899 // Weird - let's fix it 03900 $post->attachment = '1'; 03901 $DB->update_record('forum_posts', $post); 03902 } else if ($attachmentsmoved == 0 && $post->attachment != '') { 03903 // Weird - let's fix it 03904 $post->attachment = ''; 03905 $DB->update_record('forum_posts', $post); 03906 } 03907 } 03908 } 03909 03910 return true; 03911 } 03912 03924 function forum_print_attachments($post, $cm, $type) { 03925 global $CFG, $DB, $USER, $OUTPUT; 03926 03927 if (empty($post->attachment)) { 03928 return $type !== 'separateimages' ? '' : array('', ''); 03929 } 03930 03931 if (!in_array($type, array('separateimages', 'html', 'text'))) { 03932 return $type !== 'separateimages' ? '' : array('', ''); 03933 } 03934 03935 if (!$context = get_context_instance(CONTEXT_MODULE, $cm->id)) { 03936 return $type !== 'separateimages' ? '' : array('', ''); 03937 } 03938 $strattachment = get_string('attachment', 'forum'); 03939 03940 $fs = get_file_storage(); 03941 03942 $imagereturn = ''; 03943 $output = ''; 03944 03945 $canexport = !empty($CFG->enableportfolios) && (has_capability('mod/forum:exportpost', $context) || ($post->userid == $USER->id && has_capability('mod/forum:exportownpost', $context))); 03946 03947 if ($canexport) { 03948 require_once($CFG->libdir.'/portfoliolib.php'); 03949 } 03950 03951 $files = $fs->get_area_files($context->id, 'mod_forum', 'attachment', $post->id, "timemodified", false); 03952 if ($files) { 03953 if ($canexport) { 03954 $button = new portfolio_add_button(); 03955 } 03956 foreach ($files as $file) { 03957 $filename = $file->get_filename(); 03958 $mimetype = $file->get_mimetype(); 03959 $iconimage = '<img src="'.$OUTPUT->pix_url(file_mimetype_icon($mimetype)).'" class="icon" alt="'.$mimetype.'" />'; 03960 $path = file_encode_url($CFG->wwwroot.'/pluginfile.php', '/'.$context->id.'/mod_forum/attachment/'.$post->id.'/'.$filename); 03961 03962 if ($type == 'html') { 03963 $output .= "<a href=\"$path\">$iconimage</a> "; 03964 $output .= "<a href=\"$path\">".s($filename)."</a>"; 03965 if ($canexport) { 03966 $button->set_callback_options('forum_portfolio_caller', array('postid' => $post->id, 'attachment' => $file->get_id()), '/mod/forum/locallib.php'); 03967 $button->set_format_by_file($file); 03968 $output .= $button->to_html(PORTFOLIO_ADD_ICON_LINK); 03969 } 03970 $output .= "<br />"; 03971 03972 } else if ($type == 'text') { 03973 $output .= "$strattachment ".s($filename).":\n$path\n"; 03974 03975 } else { //'returnimages' 03976 if (in_array($mimetype, array('image/gif', 'image/jpeg', 'image/png'))) { 03977 // Image attachments don't get printed as links 03978 $imagereturn .= "<br /><img src=\"$path\" alt=\"\" />"; 03979 if ($canexport) { 03980 $button->set_callback_options('forum_portfolio_caller', array('postid' => $post->id, 'attachment' => $file->get_id()), '/mod/forum/locallib.php'); 03981 $button->set_format_by_file($file); 03982 $imagereturn .= $button->to_html(PORTFOLIO_ADD_ICON_LINK); 03983 } 03984 } else { 03985 $output .= "<a href=\"$path\">$iconimage</a> "; 03986 $output .= format_text("<a href=\"$path\">".s($filename)."</a>", FORMAT_HTML, array('context'=>$context)); 03987 if ($canexport) { 03988 $button->set_callback_options('forum_portfolio_caller', array('postid' => $post->id, 'attachment' => $file->get_id()), '/mod/forum/locallib.php'); 03989 $button->set_format_by_file($file); 03990 $output .= $button->to_html(PORTFOLIO_ADD_ICON_LINK); 03991 } 03992 $output .= '<br />'; 03993 } 03994 } 03995 } 03996 } 03997 03998 if ($type !== 'separateimages') { 03999 return $output; 04000 04001 } else { 04002 return array($output, $imagereturn); 04003 } 04004 } 04005 04014 function forum_get_file_areas($course, $cm, $context) { 04015 $areas = array(); 04016 return $areas; 04017 } 04018 04033 function forum_get_file_info($browser, $areas, $course, $cm, $context, $filearea, $itemid, $filepath, $filename) { 04034 global $CFG, $DB; 04035 04036 if ($context->contextlevel != CONTEXT_MODULE) { 04037 return null; 04038 } 04039 04040 $fileareas = array('attachment', 'post'); 04041 if (!in_array($filearea, $fileareas)) { 04042 return null; 04043 } 04044 04045 if (!$post = $DB->get_record('forum_posts', array('id' => $itemid))) { 04046 return null; 04047 } 04048 04049 if (!$discussion = $DB->get_record('forum_discussions', array('id' => $post->discussion))) { 04050 return null; 04051 } 04052 04053 if (!$forum = $DB->get_record('forum', array('id' => $cm->instance))) { 04054 return null; 04055 } 04056 04057 $fs = get_file_storage(); 04058 $filepath = is_null($filepath) ? '/' : $filepath; 04059 $filename = is_null($filename) ? '.' : $filename; 04060 if (!($storedfile = $fs->get_file($context->id, 'mod_forum', $filearea, $itemid, $filepath, $filename))) { 04061 return null; 04062 } 04063 04064 // Make sure groups allow this user to see this file 04065 if ($discussion->groupid > 0 and $groupmode = groups_get_activity_groupmode($cm, $course)) { // Groups are being used 04066 if (!groups_group_exists($discussion->groupid)) { // Can't find group 04067 return null; // Be safe and don't send it to anyone 04068 } 04069 04070 if (!groups_is_member($discussion->groupid) and !has_capability('moodle/site:accessallgroups', $context)) { 04071 // do not send posts from other groups when in SEPARATEGROUPS or VISIBLEGROUPS 04072 return null; 04073 } 04074 } 04075 04076 // Make sure we're allowed to see it... 04077 if (!forum_user_can_see_post($forum, $discussion, $post, NULL, $cm)) { 04078 return null; 04079 } 04080 04081 $urlbase = $CFG->wwwroot.'/pluginfile.php'; 04082 return new file_info_stored($browser, $context, $storedfile, $urlbase, $filearea, $itemid, true, true, false); 04083 } 04084 04096 function forum_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload) { 04097 global $CFG, $DB; 04098 04099 if ($context->contextlevel != CONTEXT_MODULE) { 04100 return false; 04101 } 04102 04103 require_course_login($course, true, $cm); 04104 04105 $fileareas = array('attachment', 'post'); 04106 if (!in_array($filearea, $fileareas)) { 04107 return false; 04108 } 04109 04110 $postid = (int)array_shift($args); 04111 04112 if (!$post = $DB->get_record('forum_posts', array('id'=>$postid))) { 04113 return false; 04114 } 04115 04116 if (!$discussion = $DB->get_record('forum_discussions', array('id'=>$post->discussion))) { 04117 return false; 04118 } 04119 04120 if (!$forum = $DB->get_record('forum', array('id'=>$cm->instance))) { 04121 return false; 04122 } 04123 04124 $fs = get_file_storage(); 04125 $relativepath = implode('/', $args); 04126 $fullpath = "/$context->id/mod_forum/$filearea/$postid/$relativepath"; 04127 if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) { 04128 return false; 04129 } 04130 04131 // Make sure groups allow this user to see this file 04132 if ($discussion->groupid > 0 and $groupmode = groups_get_activity_groupmode($cm, $course)) { // Groups are being used 04133 if (!groups_group_exists($discussion->groupid)) { // Can't find group 04134 return false; // Be safe and don't send it to anyone 04135 } 04136 04137 if (!groups_is_member($discussion->groupid) and !has_capability('moodle/site:accessallgroups', $context)) { 04138 // do not send posts from other groups when in SEPARATEGROUPS or VISIBLEGROUPS 04139 return false; 04140 } 04141 } 04142 04143 // Make sure we're allowed to see it... 04144 if (!forum_user_can_see_post($forum, $discussion, $post, NULL, $cm)) { 04145 return false; 04146 } 04147 04148 04149 // finally send the file 04150 send_stored_file($file, 0, 0, true); // download MUST be forced - security! 04151 } 04152 04164 function forum_add_attachment($post, $forum, $cm, $mform=null, &$message=null) { 04165 global $DB; 04166 04167 if (empty($mform)) { 04168 return false; 04169 } 04170 04171 if (empty($post->attachments)) { 04172 return true; // Nothing to do 04173 } 04174 04175 $context = get_context_instance(CONTEXT_MODULE, $cm->id); 04176 04177 $info = file_get_draft_area_info($post->attachments); 04178 $present = ($info['filecount']>0) ? '1' : ''; 04179 file_save_draft_area_files($post->attachments, $context->id, 'mod_forum', 'attachment', $post->id); 04180 04181 $DB->set_field('forum_posts', 'attachment', $present, array('id'=>$post->id)); 04182 04183 return true; 04184 } 04185 04197 function forum_add_new_post($post, $mform, &$message) { 04198 global $USER, $CFG, $DB; 04199 04200 $discussion = $DB->get_record('forum_discussions', array('id' => $post->discussion)); 04201 $forum = $DB->get_record('forum', array('id' => $discussion->forum)); 04202 $cm = get_coursemodule_from_instance('forum', $forum->id); 04203 $context = get_context_instance(CONTEXT_MODULE, $cm->id); 04204 04205 $post->created = $post->modified = time(); 04206 $post->mailed = "0"; 04207 $post->userid = $USER->id; 04208 $post->attachment = ""; 04209 04210 $post->id = $DB->insert_record("forum_posts", $post); 04211 $post->message = file_save_draft_area_files($post->itemid, $context->id, 'mod_forum', 'post', $post->id, array('subdirs'=>true), $post->message); 04212 $DB->set_field('forum_posts', 'message', $post->message, array('id'=>$post->id)); 04213 forum_add_attachment($post, $forum, $cm, $mform, $message); 04214 04215 // Update discussion modified date 04216 $DB->set_field("forum_discussions", "timemodified", $post->modified, array("id" => $post->discussion)); 04217 $DB->set_field("forum_discussions", "usermodified", $post->userid, array("id" => $post->discussion)); 04218 04219 if (forum_tp_can_track_forums($forum) && forum_tp_is_tracked($forum)) { 04220 forum_tp_mark_post_read($post->userid, $post, $post->forum); 04221 } 04222 04223 return $post->id; 04224 } 04225 04237 function forum_update_post($post, $mform, &$message) { 04238 global $USER, $CFG, $DB; 04239 04240 $discussion = $DB->get_record('forum_discussions', array('id' => $post->discussion)); 04241 $forum = $DB->get_record('forum', array('id' => $discussion->forum)); 04242 $cm = get_coursemodule_from_instance('forum', $forum->id); 04243 $context = get_context_instance(CONTEXT_MODULE, $cm->id); 04244 04245 $post->modified = time(); 04246 04247 $DB->update_record('forum_posts', $post); 04248 04249 $discussion->timemodified = $post->modified; // last modified tracking 04250 $discussion->usermodified = $post->userid; // last modified tracking 04251 04252 if (!$post->parent) { // Post is a discussion starter - update discussion title and times too 04253 $discussion->name = $post->subject; 04254 $discussion->timestart = $post->timestart; 04255 $discussion->timeend = $post->timeend; 04256 } 04257 $post->message = file_save_draft_area_files($post->itemid, $context->id, 'mod_forum', 'post', $post->id, array('subdirs'=>true), $post->message); 04258 $DB->set_field('forum_posts', 'message', $post->message, array('id'=>$post->id)); 04259 04260 $DB->update_record('forum_discussions', $discussion); 04261 04262 forum_add_attachment($post, $forum, $cm, $mform, $message); 04263 04264 if (forum_tp_can_track_forums($forum) && forum_tp_is_tracked($forum)) { 04265 forum_tp_mark_post_read($post->userid, $post, $post->forum); 04266 } 04267 04268 return true; 04269 } 04270 04284 function forum_add_discussion($discussion, $mform=null, &$message=null, $userid=null) { 04285 global $USER, $CFG, $DB; 04286 04287 $timenow = time(); 04288 04289 if (is_null($userid)) { 04290 $userid = $USER->id; 04291 } 04292 04293 // The first post is stored as a real post, and linked 04294 // to from the discuss entry. 04295 04296 $forum = $DB->get_record('forum', array('id'=>$discussion->forum)); 04297 $cm = get_coursemodule_from_instance('forum', $forum->id); 04298 04299 $post = new stdClass(); 04300 $post->discussion = 0; 04301 $post->parent = 0; 04302 $post->userid = $userid; 04303 $post->created = $timenow; 04304 $post->modified = $timenow; 04305 $post->mailed = 0; 04306 $post->subject = $discussion->name; 04307 $post->message = $discussion->message; 04308 $post->messageformat = $discussion->messageformat; 04309 $post->messagetrust = $discussion->messagetrust; 04310 $post->attachments = isset($discussion->attachments) ? $discussion->attachments : null; 04311 $post->forum = $forum->id; // speedup 04312 $post->course = $forum->course; // speedup 04313 $post->mailnow = $discussion->mailnow; 04314 04315 $post->id = $DB->insert_record("forum_posts", $post); 04316 04317 // TODO: Fix the calling code so that there always is a $cm when this function is called 04318 if (!empty($cm->id) && !empty($discussion->itemid)) { // In "single simple discussions" this may not exist yet 04319 $context = get_context_instance(CONTEXT_MODULE, $cm->id); 04320 $text = file_save_draft_area_files($discussion->itemid, $context->id, 'mod_forum', 'post', $post->id, array('subdirs'=>true), $post->message); 04321 $DB->set_field('forum_posts', 'message', $text, array('id'=>$post->id)); 04322 } 04323 04324 // Now do the main entry for the discussion, linking to this first post 04325 04326 $discussion->firstpost = $post->id; 04327 $discussion->timemodified = $timenow; 04328 $discussion->usermodified = $post->userid; 04329 $discussion->userid = $userid; 04330 04331 $post->discussion = $DB->insert_record("forum_discussions", $discussion); 04332 04333 // Finally, set the pointer on the post. 04334 $DB->set_field("forum_posts", "discussion", $post->discussion, array("id"=>$post->id)); 04335 04336 if (!empty($cm->id)) { 04337 forum_add_attachment($post, $forum, $cm, $mform, $message); 04338 } 04339 04340 if (forum_tp_can_track_forums($forum) && forum_tp_is_tracked($forum)) { 04341 forum_tp_mark_post_read($post->userid, $post, $post->forum); 04342 } 04343 04344 return $post->discussion; 04345 } 04346 04347 04359 function forum_delete_discussion($discussion, $fulldelete, $course, $cm, $forum) { 04360 global $DB, $CFG; 04361 require_once($CFG->libdir.'/completionlib.php'); 04362 04363 $result = true; 04364 04365 if ($posts = $DB->get_records("forum_posts", array("discussion" => $discussion->id))) { 04366 foreach ($posts as $post) { 04367 $post->course = $discussion->course; 04368 $post->forum = $discussion->forum; 04369 if (!forum_delete_post($post, 'ignore', $course, $cm, $forum, $fulldelete)) { 04370 $result = false; 04371 } 04372 } 04373 } 04374 04375 forum_tp_delete_read_records(-1, -1, $discussion->id); 04376 04377 if (!$DB->delete_records("forum_discussions", array("id"=>$discussion->id))) { 04378 $result = false; 04379 } 04380 04381 // Update completion state if we are tracking completion based on number of posts 04382 // But don't bother when deleting whole thing 04383 if (!$fulldelete) { 04384 $completion = new completion_info($course); 04385 if ($completion->is_enabled($cm) == COMPLETION_TRACKING_AUTOMATIC && 04386 ($forum->completiondiscussions || $forum->completionreplies || $forum->completionposts)) { 04387 $completion->update_state($cm, COMPLETION_INCOMPLETE, $discussion->userid); 04388 } 04389 } 04390 04391 return $result; 04392 } 04393 04394 04412 function forum_delete_post($post, $children, $course, $cm, $forum, $skipcompletion=false) { 04413 global $DB, $CFG; 04414 require_once($CFG->libdir.'/completionlib.php'); 04415 04416 $context = get_context_instance(CONTEXT_MODULE, $cm->id); 04417 04418 if ($children != 'ignore' && ($childposts = $DB->get_records('forum_posts', array('parent'=>$post->id)))) { 04419 if ($children) { 04420 foreach ($childposts as $childpost) { 04421 forum_delete_post($childpost, true, $course, $cm, $forum, $skipcompletion); 04422 } 04423 } else { 04424 return false; 04425 } 04426 } 04427 04428 //delete ratings 04429 require_once($CFG->dirroot.'/rating/lib.php'); 04430 $delopt = new stdClass; 04431 $delopt->contextid = $context->id; 04432 $delopt->component = 'mod_forum'; 04433 $delopt->ratingarea = 'post'; 04434 $delopt->itemid = $post->id; 04435 $rm = new rating_manager(); 04436 $rm->delete_ratings($delopt); 04437 04438 //delete attachments 04439 $fs = get_file_storage(); 04440 $fs->delete_area_files($context->id, 'mod_forum', 'attachment', $post->id); 04441 $fs->delete_area_files($context->id, 'mod_forum', 'post', $post->id); 04442 04443 if ($DB->delete_records("forum_posts", array("id" => $post->id))) { 04444 04445 forum_tp_delete_read_records(-1, $post->id); 04446 04447 // Just in case we are deleting the last post 04448 forum_discussion_update_last_post($post->discussion); 04449 04450 // Update completion state if we are tracking completion based on number of posts 04451 // But don't bother when deleting whole thing 04452 04453 if (!$skipcompletion) { 04454 $completion = new completion_info($course); 04455 if ($completion->is_enabled($cm) == COMPLETION_TRACKING_AUTOMATIC && 04456 ($forum->completiondiscussions || $forum->completionreplies || $forum->completionposts)) { 04457 $completion->update_state($cm, COMPLETION_INCOMPLETE, $post->userid); 04458 } 04459 } 04460 04461 return true; 04462 } 04463 return false; 04464 } 04465 04472 function forum_count_replies($post, $children=true) { 04473 global $DB; 04474 $count = 0; 04475 04476 if ($children) { 04477 if ($childposts = $DB->get_records('forum_posts', array('parent' => $post->id))) { 04478 foreach ($childposts as $childpost) { 04479 $count ++; // For this child 04480 $count += forum_count_replies($childpost, true); 04481 } 04482 } 04483 } else { 04484 $count += $DB->count_records('forum_posts', array('parent' => $post->id)); 04485 } 04486 04487 return $count; 04488 } 04489 04490 04497 function forum_forcesubscribe($forumid, $value=1) { 04498 global $DB; 04499 return $DB->set_field("forum", "forcesubscribe", $value, array("id" => $forumid)); 04500 } 04501 04507 function forum_is_forcesubscribed($forum) { 04508 global $DB; 04509 if (isset($forum->forcesubscribe)) { // then we use that 04510 return ($forum->forcesubscribe == FORUM_FORCESUBSCRIBE); 04511 } else { // Check the database 04512 return ($DB->get_field('forum', 'forcesubscribe', array('id' => $forum)) == FORUM_FORCESUBSCRIBE); 04513 } 04514 } 04515 04516 function forum_get_forcesubscribed($forum) { 04517 global $DB; 04518 if (isset($forum->forcesubscribe)) { // then we use that 04519 return $forum->forcesubscribe; 04520 } else { // Check the database 04521 return $DB->get_field('forum', 'forcesubscribe', array('id' => $forum)); 04522 } 04523 } 04524 04531 function forum_is_subscribed($userid, $forum) { 04532 global $DB; 04533 if (is_numeric($forum)) { 04534 $forum = $DB->get_record('forum', array('id' => $forum)); 04535 } 04536 if (forum_is_forcesubscribed($forum)) { 04537 return true; 04538 } 04539 return $DB->record_exists("forum_subscriptions", array("userid" => $userid, "forum" => $forum->id)); 04540 } 04541 04542 function forum_get_subscribed_forums($course) { 04543 global $USER, $CFG, $DB; 04544 $sql = "SELECT f.id 04545 FROM {forum} f 04546 LEFT JOIN {forum_subscriptions} fs ON (fs.forum = f.id AND fs.userid = ?) 04547 WHERE f.course = ? 04548 AND f.forcesubscribe <> ".FORUM_DISALLOWSUBSCRIBE." 04549 AND (f.forcesubscribe = ".FORUM_FORCESUBSCRIBE." OR fs.id IS NOT NULL)"; 04550 if ($subscribed = $DB->get_records_sql($sql, array($USER->id, $course->id))) { 04551 foreach ($subscribed as $s) { 04552 $subscribed[$s->id] = $s->id; 04553 } 04554 return $subscribed; 04555 } else { 04556 return array(); 04557 } 04558 } 04559 04567 function forum_subscribe($userid, $forumid) { 04568 global $DB; 04569 04570 if ($DB->record_exists("forum_subscriptions", array("userid"=>$userid, "forum"=>$forumid))) { 04571 return true; 04572 } 04573 04574 $sub = new stdClass(); 04575 $sub->userid = $userid; 04576 $sub->forum = $forumid; 04577 04578 return $DB->insert_record("forum_subscriptions", $sub); 04579 } 04580 04588 function forum_unsubscribe($userid, $forumid) { 04589 global $DB; 04590 return $DB->delete_records("forum_subscriptions", array("userid"=>$userid, "forum"=>$forumid)); 04591 } 04592 04601 function forum_post_subscription($post, $forum) { 04602 04603 global $USER; 04604 04605 $action = ''; 04606 $subscribed = forum_is_subscribed($USER->id, $forum); 04607 04608 if ($forum->forcesubscribe == FORUM_FORCESUBSCRIBE) { // database ignored 04609 return ""; 04610 04611 } elseif (($forum->forcesubscribe == FORUM_DISALLOWSUBSCRIBE) 04612 && !has_capability('moodle/course:manageactivities', get_context_instance(CONTEXT_COURSE, $forum->course), $USER->id)) { 04613 if ($subscribed) { 04614 $action = 'unsubscribe'; // sanity check, following MDL-14558 04615 } else { 04616 return ""; 04617 } 04618 04619 } else { // go with the user's choice 04620 if (isset($post->subscribe)) { 04621 // no change 04622 if ((!empty($post->subscribe) && $subscribed) 04623 || (empty($post->subscribe) && !$subscribed)) { 04624 return ""; 04625 04626 } elseif (!empty($post->subscribe) && !$subscribed) { 04627 $action = 'subscribe'; 04628 04629 } elseif (empty($post->subscribe) && $subscribed) { 04630 $action = 'unsubscribe'; 04631 } 04632 } 04633 } 04634 04635 $info = new stdClass(); 04636 $info->name = fullname($USER); 04637 $info->forum = format_string($forum->name); 04638 04639 switch ($action) { 04640 case 'subscribe': 04641 forum_subscribe($USER->id, $post->forum); 04642 return "<p>".get_string("nowsubscribed", "forum", $info)."</p>"; 04643 case 'unsubscribe': 04644 forum_unsubscribe($USER->id, $post->forum); 04645 return "<p>".get_string("nownotsubscribed", "forum", $info)."</p>"; 04646 } 04647 } 04648 04664 function forum_get_subscribe_link($forum, $context, $messages = array(), $cantaccessagroup = false, $fakelink=true, $backtoindex=false, $subscribed_forums=null) { 04665 global $CFG, $USER, $PAGE, $OUTPUT; 04666 $defaultmessages = array( 04667 'subscribed' => get_string('unsubscribe', 'forum'), 04668 'unsubscribed' => get_string('subscribe', 'forum'), 04669 'cantaccessgroup' => get_string('no'), 04670 'forcesubscribed' => get_string('everyoneissubscribed', 'forum'), 04671 'cantsubscribe' => get_string('disallowsubscribe','forum') 04672 ); 04673 $messages = $messages + $defaultmessages; 04674 04675 if (forum_is_forcesubscribed($forum)) { 04676 return $messages['forcesubscribed']; 04677 } else if ($forum->forcesubscribe == FORUM_DISALLOWSUBSCRIBE && !has_capability('mod/forum:managesubscriptions', $context)) { 04678 return $messages['cantsubscribe']; 04679 } else if ($cantaccessagroup) { 04680 return $messages['cantaccessgroup']; 04681 } else { 04682 if (!is_enrolled($context, $USER, '', true)) { 04683 return ''; 04684 } 04685 if (is_null($subscribed_forums)) { 04686 $subscribed = forum_is_subscribed($USER->id, $forum); 04687 } else { 04688 $subscribed = !empty($subscribed_forums[$forum->id]); 04689 } 04690 if ($subscribed) { 04691 $linktext = $messages['subscribed']; 04692 $linktitle = get_string('subscribestop', 'forum'); 04693 } else { 04694 $linktext = $messages['unsubscribed']; 04695 $linktitle = get_string('subscribestart', 'forum'); 04696 } 04697 04698 $options = array(); 04699 if ($backtoindex) { 04700 $backtoindexlink = '&backtoindex=1'; 04701 $options['backtoindex'] = 1; 04702 } else { 04703 $backtoindexlink = ''; 04704 } 04705 $link = ''; 04706 04707 if ($fakelink) { 04708 $PAGE->requires->js('/mod/forum/forum.js'); 04709 $PAGE->requires->js_function_call('forum_produce_subscribe_link', array($forum->id, $backtoindexlink, $linktext, $linktitle)); 04710 $link = "<noscript>"; 04711 } 04712 $options['id'] = $forum->id; 04713 $options['sesskey'] = sesskey(); 04714 $url = new moodle_url('/mod/forum/subscribe.php', $options); 04715 $link .= $OUTPUT->single_button($url, $linktext, 'get', array('title'=>$linktitle)); 04716 if ($fakelink) { 04717 $link .= '</noscript>'; 04718 } 04719 04720 return $link; 04721 } 04722 } 04723 04724 04736 function forum_get_tracking_link($forum, $messages=array(), $fakelink=true) { 04737 global $CFG, $USER, $PAGE, $OUTPUT; 04738 04739 static $strnotrackforum, $strtrackforum; 04740 04741 if (isset($messages['trackforum'])) { 04742 $strtrackforum = $messages['trackforum']; 04743 } 04744 if (isset($messages['notrackforum'])) { 04745 $strnotrackforum = $messages['notrackforum']; 04746 } 04747 if (empty($strtrackforum)) { 04748 $strtrackforum = get_string('trackforum', 'forum'); 04749 } 04750 if (empty($strnotrackforum)) { 04751 $strnotrackforum = get_string('notrackforum', 'forum'); 04752 } 04753 04754 if (forum_tp_is_tracked($forum)) { 04755 $linktitle = $strnotrackforum; 04756 $linktext = $strnotrackforum; 04757 } else { 04758 $linktitle = $strtrackforum; 04759 $linktext = $strtrackforum; 04760 } 04761 04762 $link = ''; 04763 if ($fakelink) { 04764 $PAGE->requires->js('/mod/forum/forum.js'); 04765 $PAGE->requires->js_function_call('forum_produce_tracking_link', Array($forum->id, $linktext, $linktitle)); 04766 // use <noscript> to print button in case javascript is not enabled 04767 $link .= '<noscript>'; 04768 } 04769 $url = new moodle_url('/mod/forum/settracking.php', array('id'=>$forum->id)); 04770 $link .= $OUTPUT->single_button($url, $linktext, 'get', array('title'=>$linktitle)); 04771 04772 if ($fakelink) { 04773 $link .= '</noscript>'; 04774 } 04775 04776 return $link; 04777 } 04778 04779 04780 04790 function forum_user_has_posted_discussion($forumid, $userid) { 04791 global $CFG, $DB; 04792 04793 $sql = "SELECT 'x' 04794 FROM {forum_discussions} d, {forum_posts} p 04795 WHERE d.forum = ? AND p.discussion = d.id AND p.parent = 0 and p.userid = ?"; 04796 04797 return $DB->record_exists_sql($sql, array($forumid, $userid)); 04798 } 04799 04807 function forum_discussions_user_has_posted_in($forumid, $userid) { 04808 global $CFG, $DB; 04809 04810 $haspostedsql = "SELECT d.id AS id, 04811 d.* 04812 FROM {forum_posts} p, 04813 {forum_discussions} d 04814 WHERE p.discussion = d.id 04815 AND d.forum = ? 04816 AND p.userid = ?"; 04817 04818 return $DB->get_records_sql($haspostedsql, array($forumid, $userid)); 04819 } 04820 04829 function forum_user_has_posted($forumid, $did, $userid) { 04830 global $DB; 04831 04832 if (empty($did)) { 04833 // posted in any forum discussion? 04834 $sql = "SELECT 'x' 04835 FROM {forum_posts} p 04836 JOIN {forum_discussions} d ON d.id = p.discussion 04837 WHERE p.userid = :userid AND d.forum = :forumid"; 04838 return $DB->record_exists_sql($sql, array('forumid'=>$forumid,'userid'=>$userid)); 04839 } else { 04840 return $DB->record_exists('forum_posts', array('discussion'=>$did,'userid'=>$userid)); 04841 } 04842 } 04843 04851 function forum_get_user_posted_time($did, $userid) { 04852 global $DB; 04853 04854 $posttime = $DB->get_field('forum_posts', 'MIN(created)', array('userid'=>$userid, 'discussion'=>$did)); 04855 if (empty($posttime)) { 04856 return false; 04857 } 04858 return $posttime; 04859 } 04860 04870 function forum_user_can_post_discussion($forum, $currentgroup=null, $unused=-1, $cm=NULL, $context=NULL) { 04871 // $forum is an object 04872 global $USER; 04873 04874 // shortcut - guest and not-logged-in users can not post 04875 if (isguestuser() or !isloggedin()) { 04876 return false; 04877 } 04878 04879 if (!$cm) { 04880 debugging('missing cm', DEBUG_DEVELOPER); 04881 if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) { 04882 print_error('invalidcoursemodule'); 04883 } 04884 } 04885 04886 if (!$context) { 04887 $context = get_context_instance(CONTEXT_MODULE, $cm->id); 04888 } 04889 04890 if ($currentgroup === null) { 04891 $currentgroup = groups_get_activity_group($cm); 04892 } 04893 04894 $groupmode = groups_get_activity_groupmode($cm); 04895 04896 if ($forum->type == 'news') { 04897 $capname = 'mod/forum:addnews'; 04898 } else if ($forum->type == 'qanda') { 04899 $capname = 'mod/forum:addquestion'; 04900 } else { 04901 $capname = 'mod/forum:startdiscussion'; 04902 } 04903 04904 if (!has_capability($capname, $context)) { 04905 return false; 04906 } 04907 04908 if ($forum->type == 'single') { 04909 return false; 04910 } 04911 04912 if ($forum->type == 'eachuser') { 04913 if (forum_user_has_posted_discussion($forum->id, $USER->id)) { 04914 return false; 04915 } 04916 } 04917 04918 if (!$groupmode or has_capability('moodle/site:accessallgroups', $context)) { 04919 return true; 04920 } 04921 04922 if ($currentgroup) { 04923 return groups_is_member($currentgroup); 04924 } else { 04925 // no group membership and no accessallgroups means no new discussions 04926 // reverted to 1.7 behaviour in 1.9+, buggy in 1.8.0-1.9.0 04927 return false; 04928 } 04929 } 04930 04949 function forum_user_can_post($forum, $discussion, $user=NULL, $cm=NULL, $course=NULL, $context=NULL) { 04950 global $USER, $DB; 04951 if (empty($user)) { 04952 $user = $USER; 04953 } 04954 04955 // shortcut - guest and not-logged-in users can not post 04956 if (isguestuser($user) or empty($user->id)) { 04957 return false; 04958 } 04959 04960 if (!isset($discussion->groupid)) { 04961 debugging('incorrect discussion parameter', DEBUG_DEVELOPER); 04962 return false; 04963 } 04964 04965 if (!$cm) { 04966 debugging('missing cm', DEBUG_DEVELOPER); 04967 if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) { 04968 print_error('invalidcoursemodule'); 04969 } 04970 } 04971 04972 if (!$course) { 04973 debugging('missing course', DEBUG_DEVELOPER); 04974 if (!$course = $DB->get_record('course', array('id' => $forum->course))) { 04975 print_error('invalidcourseid'); 04976 } 04977 } 04978 04979 if (!$context) { 04980 $context = get_context_instance(CONTEXT_MODULE, $cm->id); 04981 } 04982 04983 // normal users with temporary guest access can not post, suspended users can not post either 04984 if (!is_viewing($context, $user->id) and !is_enrolled($context, $user->id, '', true)) { 04985 return false; 04986 } 04987 04988 if ($forum->type == 'news') { 04989 $capname = 'mod/forum:replynews'; 04990 } else { 04991 $capname = 'mod/forum:replypost'; 04992 } 04993 04994 if (!has_capability($capname, $context, $user->id)) { 04995 return false; 04996 } 04997 04998 if (!$groupmode = groups_get_activity_groupmode($cm, $course)) { 04999 return true; 05000 } 05001 05002 if (has_capability('moodle/site:accessallgroups', $context)) { 05003 return true; 05004 } 05005 05006 if ($groupmode == VISIBLEGROUPS) { 05007 if ($discussion->groupid == -1) { 05008 // allow students to reply to all participants discussions - this was not possible in Moodle <1.8 05009 return true; 05010 } 05011 return groups_is_member($discussion->groupid); 05012 05013 } else { 05014 //separate groups 05015 if ($discussion->groupid == -1) { 05016 return false; 05017 } 05018 return groups_is_member($discussion->groupid); 05019 } 05020 } 05021 05022 05037 function forum_user_can_view_post($post, $course, $cm, $forum, $discussion, $user=NULL){ 05038 05039 global $CFG, $USER; 05040 05041 if (!$user){ 05042 $user = $USER; 05043 } 05044 05045 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id); 05046 if (!has_capability('mod/forum:viewdiscussion', $modcontext)) { 05047 return false; 05048 } 05049 05050 // If it's a grouped discussion, make sure the user is a member 05051 if ($discussion->groupid > 0) { 05052 $groupmode = groups_get_activity_groupmode($cm); 05053 if ($groupmode == SEPARATEGROUPS) { 05054 return groups_is_member($discussion->groupid) || has_capability('moodle/site:accessallgroups', $modcontext); 05055 } 05056 } 05057 return true; 05058 } 05059 05060 05071 function forum_user_can_see_discussion($forum, $discussion, $context, $user=NULL) { 05072 global $USER, $DB; 05073 05074 if (empty($user) || empty($user->id)) { 05075 $user = $USER; 05076 } 05077 05078 // retrieve objects (yuk) 05079 if (is_numeric($forum)) { 05080 debugging('missing full forum', DEBUG_DEVELOPER); 05081 if (!$forum = $DB->get_record('forum',array('id'=>$forum))) { 05082 return false; 05083 } 05084 } 05085 if (is_numeric($discussion)) { 05086 debugging('missing full discussion', DEBUG_DEVELOPER); 05087 if (!$discussion = $DB->get_record('forum_discussions',array('id'=>$discussion))) { 05088 return false; 05089 } 05090 } 05091 05092 if (!has_capability('mod/forum:viewdiscussion', $context)) { 05093 return false; 05094 } 05095 05096 if ($forum->type == 'qanda' && 05097 !forum_user_has_posted($forum->id, $discussion->id, $user->id) && 05098 !has_capability('mod/forum:viewqandawithoutposting', $context)) { 05099 return false; 05100 } 05101 return true; 05102 } 05103 05104 05115 function forum_user_can_see_post($forum, $discussion, $post, $user=NULL, $cm=NULL) { 05116 global $CFG, $USER, $DB; 05117 05118 // retrieve objects (yuk) 05119 if (is_numeric($forum)) { 05120 debugging('missing full forum', DEBUG_DEVELOPER); 05121 if (!$forum = $DB->get_record('forum',array('id'=>$forum))) { 05122 return false; 05123 } 05124 } 05125 05126 if (is_numeric($discussion)) { 05127 debugging('missing full discussion', DEBUG_DEVELOPER); 05128 if (!$discussion = $DB->get_record('forum_discussions',array('id'=>$discussion))) { 05129 return false; 05130 } 05131 } 05132 if (is_numeric($post)) { 05133 debugging('missing full post', DEBUG_DEVELOPER); 05134 if (!$post = $DB->get_record('forum_posts',array('id'=>$post))) { 05135 return false; 05136 } 05137 } 05138 if (!isset($post->id) && isset($post->parent)) { 05139 $post->id = $post->parent; 05140 } 05141 05142 if (!$cm) { 05143 debugging('missing cm', DEBUG_DEVELOPER); 05144 if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) { 05145 print_error('invalidcoursemodule'); 05146 } 05147 } 05148 05149 if (empty($user) || empty($user->id)) { 05150 $user = $USER; 05151 } 05152 05153 $canviewdiscussion = !empty($cm->cache->caps['mod/forum:viewdiscussion']) || has_capability('mod/forum:viewdiscussion', get_context_instance(CONTEXT_MODULE, $cm->id), $user->id); 05154 if (!$canviewdiscussion && !has_all_capabilities(array('moodle/user:viewdetails', 'moodle/user:readuserposts'), get_context_instance(CONTEXT_USER, $post->userid))) { 05155 return false; 05156 } 05157 05158 if (isset($cm->uservisible)) { 05159 if (!$cm->uservisible) { 05160 return false; 05161 } 05162 } else { 05163 if (!coursemodule_visible_for_user($cm, $user->id)) { 05164 return false; 05165 } 05166 } 05167 05168 if ($forum->type == 'qanda') { 05169 $firstpost = forum_get_firstpost_from_discussion($discussion->id); 05170 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id); 05171 $userfirstpost = forum_get_user_posted_time($discussion->id, $user->id); 05172 05173 return (($userfirstpost !== false && (time() - $userfirstpost >= $CFG->maxeditingtime)) || 05174 $firstpost->id == $post->id || $post->userid == $user->id || $firstpost->userid == $user->id || 05175 has_capability('mod/forum:viewqandawithoutposting', $modcontext, $user->id, false)); 05176 } 05177 return true; 05178 } 05179 05180 05197 function forum_print_latest_discussions($course, $forum, $maxdiscussions=-1, $displayformat='plain', $sort='', 05198 $currentgroup=-1, $groupmode=-1, $page=-1, $perpage=100, $cm=NULL) { 05199 global $CFG, $USER, $OUTPUT; 05200 05201 if (!$cm) { 05202 if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) { 05203 print_error('invalidcoursemodule'); 05204 } 05205 } 05206 $context = get_context_instance(CONTEXT_MODULE, $cm->id); 05207 05208 if (empty($sort)) { 05209 $sort = "d.timemodified DESC"; 05210 } 05211 05212 $olddiscussionlink = false; 05213 05214 // Sort out some defaults 05215 if ($perpage <= 0) { 05216 $perpage = 0; 05217 $page = -1; 05218 } 05219 05220 if ($maxdiscussions == 0) { 05221 // all discussions - backwards compatibility 05222 $page = -1; 05223 $perpage = 0; 05224 if ($displayformat == 'plain') { 05225 $displayformat = 'header'; // Abbreviate display by default 05226 } 05227 05228 } else if ($maxdiscussions > 0) { 05229 $page = -1; 05230 $perpage = $maxdiscussions; 05231 } 05232 05233 $fullpost = false; 05234 if ($displayformat == 'plain') { 05235 $fullpost = true; 05236 } 05237 05238 05239 // Decide if current user is allowed to see ALL the current discussions or not 05240 05241 // First check the group stuff 05242 if ($currentgroup == -1 or $groupmode == -1) { 05243 $groupmode = groups_get_activity_groupmode($cm, $course); 05244 $currentgroup = groups_get_activity_group($cm); 05245 } 05246 05247 $groups = array(); //cache 05248 05249 // If the user can post discussions, then this is a good place to put the 05250 // button for it. We do not show the button if we are showing site news 05251 // and the current user is a guest. 05252 05253 $canstart = forum_user_can_post_discussion($forum, $currentgroup, $groupmode, $cm, $context); 05254 if (!$canstart and $forum->type !== 'news') { 05255 if (isguestuser() or !isloggedin()) { 05256 $canstart = true; 05257 } 05258 if (!is_enrolled($context) and !is_viewing($context)) { 05259 // allow guests and not-logged-in to see the button - they are prompted to log in after clicking the link 05260 // normal users with temporary guest access see this button too, they are asked to enrol instead 05261 // do not show the button to users with suspended enrolments here 05262 $canstart = enrol_selfenrol_available($course->id); 05263 } 05264 } 05265 05266 if ($canstart) { 05267 echo '<div class="singlebutton forumaddnew">'; 05268 echo "<form id=\"newdiscussionform\" method=\"get\" action=\"$CFG->wwwroot/mod/forum/post.php\">"; 05269 echo '<div>'; 05270 echo "<input type=\"hidden\" name=\"forum\" value=\"$forum->id\" />"; 05271 switch ($forum->type) { 05272 case 'news': 05273 case 'blog': 05274 $buttonadd = get_string('addanewtopic', 'forum'); 05275 break; 05276 case 'qanda': 05277 $buttonadd = get_string('addanewquestion', 'forum'); 05278 break; 05279 default: 05280 $buttonadd = get_string('addanewdiscussion', 'forum'); 05281 break; 05282 } 05283 echo '<input type="submit" value="'.$buttonadd.'" />'; 05284 echo '</div>'; 05285 echo '</form>'; 05286 echo "</div>\n"; 05287 05288 } else if (isguestuser() or !isloggedin() or $forum->type == 'news') { 05289 // no button and no info 05290 05291 } else if ($groupmode and has_capability('mod/forum:startdiscussion', $context)) { 05292 // inform users why they can not post new discussion 05293 if ($currentgroup) { 05294 echo $OUTPUT->notification(get_string('cannotadddiscussion', 'forum')); 05295 } else { 05296 echo $OUTPUT->notification(get_string('cannotadddiscussionall', 'forum')); 05297 } 05298 } 05299 05300 // Get all the recent discussions we're allowed to see 05301 05302 $getuserlastmodified = ($displayformat == 'header'); 05303 05304 if (! $discussions = forum_get_discussions($cm, $sort, $fullpost, null, $maxdiscussions, $getuserlastmodified, $page, $perpage) ) { 05305 echo '<div class="forumnodiscuss">'; 05306 if ($forum->type == 'news') { 05307 echo '('.get_string('nonews', 'forum').')'; 05308 } else if ($forum->type == 'qanda') { 05309 echo '('.get_string('noquestions','forum').')'; 05310 } else { 05311 echo '('.get_string('nodiscussions', 'forum').')'; 05312 } 05313 echo "</div>\n"; 05314 return; 05315 } 05316 05317 // If we want paging 05318 if ($page != -1) { 05320 $numdiscussions = forum_get_discussions_count($cm); 05321 05323 echo $OUTPUT->paging_bar($numdiscussions, $page, $perpage, "view.php?f=$forum->id"); 05324 if ($numdiscussions > 1000) { 05325 // saves some memory on sites with very large forums 05326 $replies = forum_count_discussion_replies($forum->id, $sort, $maxdiscussions, $page, $perpage); 05327 } else { 05328 $replies = forum_count_discussion_replies($forum->id); 05329 } 05330 05331 } else { 05332 $replies = forum_count_discussion_replies($forum->id); 05333 05334 if ($maxdiscussions > 0 and $maxdiscussions <= count($discussions)) { 05335 $olddiscussionlink = true; 05336 } 05337 } 05338 05339 $canviewparticipants = has_capability('moodle/course:viewparticipants',$context); 05340 05341 $strdatestring = get_string('strftimerecentfull'); 05342 05343 // Check if the forum is tracked. 05344 if ($cantrack = forum_tp_can_track_forums($forum)) { 05345 $forumtracked = forum_tp_is_tracked($forum); 05346 } else { 05347 $forumtracked = false; 05348 } 05349 05350 if ($forumtracked) { 05351 $unreads = forum_get_discussions_unread($cm); 05352 } else { 05353 $unreads = array(); 05354 } 05355 05356 if ($displayformat == 'header') { 05357 echo '<table cellspacing="0" class="forumheaderlist">'; 05358 echo '<thead>'; 05359 echo '<tr>'; 05360 echo '<th class="header topic" scope="col">'.get_string('discussion', 'forum').'</th>'; 05361 echo '<th class="header author" colspan="2" scope="col">'.get_string('startedby', 'forum').'</th>'; 05362 if ($groupmode > 0) { 05363 echo '<th class="header group" scope="col">'.get_string('group').'</th>'; 05364 } 05365 if (has_capability('mod/forum:viewdiscussion', $context)) { 05366 echo '<th class="header replies" scope="col">'.get_string('replies', 'forum').'</th>'; 05367 // If the forum can be tracked, display the unread column. 05368 if ($cantrack) { 05369 echo '<th class="header replies" scope="col">'.get_string('unread', 'forum'); 05370 if ($forumtracked) { 05371 echo ' <a title="'.get_string('markallread', 'forum'). 05372 '" href="'.$CFG->wwwroot.'/mod/forum/markposts.php?f='. 05373 $forum->id.'&mark=read&returnpage=view.php">'. 05374 '<img src="'.$OUTPUT->pix_url('t/clear') . '" class="iconsmall" alt="'.get_string('markallread', 'forum').'" /></a>'; 05375 } 05376 echo '</th>'; 05377 } 05378 } 05379 echo '<th class="header lastpost" scope="col">'.get_string('lastpost', 'forum').'</th>'; 05380 echo '</tr>'; 05381 echo '</thead>'; 05382 echo '<tbody>'; 05383 } 05384 05385 foreach ($discussions as $discussion) { 05386 if (!empty($replies[$discussion->discussion])) { 05387 $discussion->replies = $replies[$discussion->discussion]->replies; 05388 $discussion->lastpostid = $replies[$discussion->discussion]->lastpostid; 05389 } else { 05390 $discussion->replies = 0; 05391 } 05392 05393 // SPECIAL CASE: The front page can display a news item post to non-logged in users. 05394 // All posts are read in this case. 05395 if (!$forumtracked) { 05396 $discussion->unread = '-'; 05397 } else if (empty($USER)) { 05398 $discussion->unread = 0; 05399 } else { 05400 if (empty($unreads[$discussion->discussion])) { 05401 $discussion->unread = 0; 05402 } else { 05403 $discussion->unread = $unreads[$discussion->discussion]; 05404 } 05405 } 05406 05407 if (isloggedin()) { 05408 $ownpost = ($discussion->userid == $USER->id); 05409 } else { 05410 $ownpost=false; 05411 } 05412 // Use discussion name instead of subject of first post 05413 $discussion->subject = $discussion->name; 05414 05415 switch ($displayformat) { 05416 case 'header': 05417 if ($groupmode > 0) { 05418 if (isset($groups[$discussion->groupid])) { 05419 $group = $groups[$discussion->groupid]; 05420 } else { 05421 $group = $groups[$discussion->groupid] = groups_get_group($discussion->groupid); 05422 } 05423 } else { 05424 $group = -1; 05425 } 05426 forum_print_discussion_header($discussion, $forum, $group, $strdatestring, $cantrack, $forumtracked, 05427 $canviewparticipants, $context); 05428 break; 05429 default: 05430 $link = false; 05431 05432 if ($discussion->replies) { 05433 $link = true; 05434 } else { 05435 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id); 05436 $link = forum_user_can_post($forum, $discussion, $USER, $cm, $course, $modcontext); 05437 } 05438 05439 $discussion->forum = $forum->id; 05440 05441 forum_print_post($discussion, $discussion, $forum, $cm, $course, $ownpost, 0, $link, false); 05442 break; 05443 } 05444 } 05445 05446 if ($displayformat == "header") { 05447 echo '</tbody>'; 05448 echo '</table>'; 05449 } 05450 05451 if ($olddiscussionlink) { 05452 if ($forum->type == 'news') { 05453 $strolder = get_string('oldertopics', 'forum'); 05454 } else { 05455 $strolder = get_string('olderdiscussions', 'forum'); 05456 } 05457 echo '<div class="forumolddiscuss">'; 05458 echo '<a href="'.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id.'&showall=1">'; 05459 echo $strolder.'</a> ...</div>'; 05460 } 05461 05462 if ($page != -1) { 05463 echo $OUTPUT->paging_bar($numdiscussions, $page, $perpage, "view.php?f=$forum->id"); 05464 } 05465 } 05466 05467 05485 function forum_print_discussion($course, $cm, $forum, $discussion, $post, $mode, $canreply=NULL, $canrate=false) { 05486 global $USER, $CFG; 05487 05488 require_once($CFG->dirroot.'/rating/lib.php'); 05489 05490 $ownpost = (isloggedin() && $USER->id == $post->userid); 05491 05492 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id); 05493 if ($canreply === NULL) { 05494 $reply = forum_user_can_post($forum, $discussion, $USER, $cm, $course, $modcontext); 05495 } else { 05496 $reply = $canreply; 05497 } 05498 05499 // $cm holds general cache for forum functions 05500 $cm->cache = new stdClass; 05501 $cm->cache->groups = groups_get_all_groups($course->id, 0, $cm->groupingid); 05502 $cm->cache->usersgroups = array(); 05503 05504 $posters = array(); 05505 05506 // preload all posts - TODO: improve... 05507 if ($mode == FORUM_MODE_FLATNEWEST) { 05508 $sort = "p.created DESC"; 05509 } else { 05510 $sort = "p.created ASC"; 05511 } 05512 05513 $forumtracked = forum_tp_is_tracked($forum); 05514 $posts = forum_get_all_discussion_posts($discussion->id, $sort, $forumtracked); 05515 $post = $posts[$post->id]; 05516 05517 foreach ($posts as $pid=>$p) { 05518 $posters[$p->userid] = $p->userid; 05519 } 05520 05521 // preload all groups of ppl that posted in this discussion 05522 if ($postersgroups = groups_get_all_groups($course->id, $posters, $cm->groupingid, 'gm.id, gm.groupid, gm.userid')) { 05523 foreach($postersgroups as $pg) { 05524 if (!isset($cm->cache->usersgroups[$pg->userid])) { 05525 $cm->cache->usersgroups[$pg->userid] = array(); 05526 } 05527 $cm->cache->usersgroups[$pg->userid][$pg->groupid] = $pg->groupid; 05528 } 05529 unset($postersgroups); 05530 } 05531 05532 //load ratings 05533 if ($forum->assessed != RATING_AGGREGATE_NONE) { 05534 $ratingoptions = new stdClass; 05535 $ratingoptions->context = $modcontext; 05536 $ratingoptions->component = 'mod_forum'; 05537 $ratingoptions->ratingarea = 'post'; 05538 $ratingoptions->items = $posts; 05539 $ratingoptions->aggregate = $forum->assessed;//the aggregation method 05540 $ratingoptions->scaleid = $forum->scale; 05541 $ratingoptions->userid = $USER->id; 05542 if ($forum->type == 'single' or !$discussion->id) { 05543 $ratingoptions->returnurl = "$CFG->wwwroot/mod/forum/view.php?id=$cm->id"; 05544 } else { 05545 $ratingoptions->returnurl = "$CFG->wwwroot/mod/forum/discuss.php?d=$discussion->id"; 05546 } 05547 $ratingoptions->assesstimestart = $forum->assesstimestart; 05548 $ratingoptions->assesstimefinish = $forum->assesstimefinish; 05549 05550 $rm = new rating_manager(); 05551 $posts = $rm->get_ratings($ratingoptions); 05552 } 05553 05554 05555 $post->forum = $forum->id; // Add the forum id to the post object, later used by forum_print_post 05556 $post->forumtype = $forum->type; 05557 05558 $post->subject = format_string($post->subject); 05559 05560 $postread = !empty($post->postread); 05561 05562 forum_print_post($post, $discussion, $forum, $cm, $course, $ownpost, $reply, false, 05563 '', '', $postread, true, $forumtracked); 05564 05565 switch ($mode) { 05566 case FORUM_MODE_FLATOLDEST : 05567 case FORUM_MODE_FLATNEWEST : 05568 default: 05569 forum_print_posts_flat($course, $cm, $forum, $discussion, $post, $mode, $reply, $forumtracked, $posts); 05570 break; 05571 05572 case FORUM_MODE_THREADED : 05573 forum_print_posts_threaded($course, $cm, $forum, $discussion, $post, 0, $reply, $forumtracked, $posts); 05574 break; 05575 05576 case FORUM_MODE_NESTED : 05577 forum_print_posts_nested($course, $cm, $forum, $discussion, $post, $reply, $forumtracked, $posts); 05578 break; 05579 } 05580 } 05581 05582 05598 function forum_print_posts_flat($course, &$cm, $forum, $discussion, $post, $mode, $reply, $forumtracked, $posts) { 05599 global $USER, $CFG; 05600 05601 $link = false; 05602 05603 if ($mode == FORUM_MODE_FLATNEWEST) { 05604 $sort = "ORDER BY created DESC"; 05605 } else { 05606 $sort = "ORDER BY created ASC"; 05607 } 05608 05609 foreach ($posts as $post) { 05610 if (!$post->parent) { 05611 continue; 05612 } 05613 $post->subject = format_string($post->subject); 05614 $ownpost = ($USER->id == $post->userid); 05615 05616 $postread = !empty($post->postread); 05617 05618 forum_print_post($post, $discussion, $forum, $cm, $course, $ownpost, $reply, $link, 05619 '', '', $postread, true, $forumtracked); 05620 } 05621 } 05622 05631 function forum_print_posts_threaded($course, &$cm, $forum, $discussion, $parent, $depth, $reply, $forumtracked, $posts) { 05632 global $USER, $CFG; 05633 05634 $link = false; 05635 05636 if (!empty($posts[$parent->id]->children)) { 05637 $posts = $posts[$parent->id]->children; 05638 05639 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id); 05640 $canviewfullnames = has_capability('moodle/site:viewfullnames', $modcontext); 05641 05642 foreach ($posts as $post) { 05643 05644 echo '<div class="indent">'; 05645 if ($depth > 0) { 05646 $ownpost = ($USER->id == $post->userid); 05647 $post->subject = format_string($post->subject); 05648 05649 $postread = !empty($post->postread); 05650 05651 forum_print_post($post, $discussion, $forum, $cm, $course, $ownpost, $reply, $link, 05652 '', '', $postread, true, $forumtracked); 05653 } else { 05654 if (!forum_user_can_see_post($forum, $discussion, $post, NULL, $cm)) { 05655 echo "</div>\n"; 05656 continue; 05657 } 05658 $by = new stdClass(); 05659 $by->name = fullname($post, $canviewfullnames); 05660 $by->date = userdate($post->modified); 05661 05662 if ($forumtracked) { 05663 if (!empty($post->postread)) { 05664 $style = '<span class="forumthread read">'; 05665 } else { 05666 $style = '<span class="forumthread unread">'; 05667 } 05668 } else { 05669 $style = '<span class="forumthread">'; 05670 } 05671 echo $style."<a name=\"$post->id\"></a>". 05672 "<a href=\"discuss.php?d=$post->discussion&parent=$post->id\">".format_string($post->subject,true)."</a> "; 05673 print_string("bynameondate", "forum", $by); 05674 echo "</span>"; 05675 } 05676 05677 forum_print_posts_threaded($course, $cm, $forum, $discussion, $post, $depth-1, $reply, $forumtracked, $posts); 05678 echo "</div>\n"; 05679 } 05680 } 05681 } 05682 05689 function forum_print_posts_nested($course, &$cm, $forum, $discussion, $parent, $reply, $forumtracked, $posts) { 05690 global $USER, $CFG; 05691 05692 $link = false; 05693 05694 if (!empty($posts[$parent->id]->children)) { 05695 $posts = $posts[$parent->id]->children; 05696 05697 foreach ($posts as $post) { 05698 05699 echo '<div class="indent">'; 05700 if (!isloggedin()) { 05701 $ownpost = false; 05702 } else { 05703 $ownpost = ($USER->id == $post->userid); 05704 } 05705 05706 $post->subject = format_string($post->subject); 05707 $postread = !empty($post->postread); 05708 05709 forum_print_post($post, $discussion, $forum, $cm, $course, $ownpost, $reply, $link, 05710 '', '', $postread, true, $forumtracked); 05711 forum_print_posts_nested($course, $cm, $forum, $discussion, $post, $reply, $forumtracked, $posts); 05712 echo "</div>\n"; 05713 } 05714 } 05715 } 05716 05726 function forum_get_recent_mod_activity(&$activities, &$index, $timestart, $courseid, $cmid, $userid=0, $groupid=0) { 05727 global $CFG, $COURSE, $USER, $DB; 05728 05729 if ($COURSE->id == $courseid) { 05730 $course = $COURSE; 05731 } else { 05732 $course = $DB->get_record('course', array('id' => $courseid)); 05733 } 05734 05735 $modinfo =& get_fast_modinfo($course); 05736 05737 $cm = $modinfo->cms[$cmid]; 05738 $params = array($timestart, $cm->instance); 05739 05740 if ($userid) { 05741 $userselect = "AND u.id = ?"; 05742 $params[] = $userid; 05743 } else { 05744 $userselect = ""; 05745 } 05746 05747 if ($groupid) { 05748 $groupselect = "AND gm.groupid = ?"; 05749 $groupjoin = "JOIN {groups_members} gm ON gm.userid=u.id"; 05750 $params[] = $groupid; 05751 } else { 05752 $groupselect = ""; 05753 $groupjoin = ""; 05754 } 05755 05756 if (!$posts = $DB->get_records_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid, 05757 d.timestart, d.timeend, d.userid AS duserid, 05758 u.firstname, u.lastname, u.email, u.picture, u.imagealt, u.email 05759 FROM {forum_posts} p 05760 JOIN {forum_discussions} d ON d.id = p.discussion 05761 JOIN {forum} f ON f.id = d.forum 05762 JOIN {user} u ON u.id = p.userid 05763 $groupjoin 05764 WHERE p.created > ? AND f.id = ? 05765 $userselect $groupselect 05766 ORDER BY p.id ASC", $params)) { // order by initial posting date 05767 return; 05768 } 05769 05770 $groupmode = groups_get_activity_groupmode($cm, $course); 05771 $cm_context = get_context_instance(CONTEXT_MODULE, $cm->id); 05772 $viewhiddentimed = has_capability('mod/forum:viewhiddentimedposts', $cm_context); 05773 $accessallgroups = has_capability('moodle/site:accessallgroups', $cm_context); 05774 05775 if (is_null($modinfo->groups)) { 05776 $modinfo->groups = groups_get_user_groups($course->id); // load all my groups and cache it in modinfo 05777 } 05778 05779 $printposts = array(); 05780 foreach ($posts as $post) { 05781 05782 if (!empty($CFG->forum_enabletimedposts) and $USER->id != $post->duserid 05783 and (($post->timestart > 0 and $post->timestart > time()) or ($post->timeend > 0 and $post->timeend < time()))) { 05784 if (!$viewhiddentimed) { 05785 continue; 05786 } 05787 } 05788 05789 if ($groupmode) { 05790 if ($post->groupid == -1 or $groupmode == VISIBLEGROUPS or $accessallgroups) { 05791 // oki (Open discussions have groupid -1) 05792 } else { 05793 // separate mode 05794 if (isguestuser()) { 05795 // shortcut 05796 continue; 05797 } 05798 05799 if (!array_key_exists($post->groupid, $modinfo->groups[0])) { 05800 continue; 05801 } 05802 } 05803 } 05804 05805 $printposts[] = $post; 05806 } 05807 05808 if (!$printposts) { 05809 return; 05810 } 05811 05812 $aname = format_string($cm->name,true); 05813 05814 foreach ($printposts as $post) { 05815 $tmpactivity = new stdClass(); 05816 05817 $tmpactivity->type = 'forum'; 05818 $tmpactivity->cmid = $cm->id; 05819 $tmpactivity->name = $aname; 05820 $tmpactivity->sectionnum = $cm->sectionnum; 05821 $tmpactivity->timestamp = $post->modified; 05822 05823 $tmpactivity->content = new stdClass(); 05824 $tmpactivity->content->id = $post->id; 05825 $tmpactivity->content->discussion = $post->discussion; 05826 $tmpactivity->content->subject = format_string($post->subject); 05827 $tmpactivity->content->parent = $post->parent; 05828 05829 $tmpactivity->user = new stdClass(); 05830 $tmpactivity->user->id = $post->userid; 05831 $tmpactivity->user->firstname = $post->firstname; 05832 $tmpactivity->user->lastname = $post->lastname; 05833 $tmpactivity->user->picture = $post->picture; 05834 $tmpactivity->user->imagealt = $post->imagealt; 05835 $tmpactivity->user->email = $post->email; 05836 05837 $activities[$index++] = $tmpactivity; 05838 } 05839 05840 return; 05841 } 05842 05847 function forum_print_recent_mod_activity($activity, $courseid, $detail, $modnames, $viewfullnames) { 05848 global $CFG, $OUTPUT; 05849 05850 if ($activity->content->parent) { 05851 $class = 'reply'; 05852 } else { 05853 $class = 'discussion'; 05854 } 05855 05856 echo '<table border="0" cellpadding="3" cellspacing="0" class="forum-recent">'; 05857 05858 echo "<tr><td class=\"userpicture\" valign=\"top\">"; 05859 echo $OUTPUT->user_picture($activity->user, array('courseid'=>$courseid)); 05860 echo "</td><td class=\"$class\">"; 05861 05862 echo '<div class="title">'; 05863 if ($detail) { 05864 $aname = s($activity->name); 05865 echo "<img src=\"" . $OUTPUT->pix_url('icon', $activity->type) . "\" ". 05866 "class=\"icon\" alt=\"{$aname}\" />"; 05867 } 05868 echo "<a href=\"$CFG->wwwroot/mod/forum/discuss.php?d={$activity->content->discussion}" 05869 ."#p{$activity->content->id}\">{$activity->content->subject}</a>"; 05870 echo '</div>'; 05871 05872 echo '<div class="user">'; 05873 $fullname = fullname($activity->user, $viewfullnames); 05874 echo "<a href=\"$CFG->wwwroot/user/view.php?id={$activity->user->id}&course=$courseid\">" 05875 ."{$fullname}</a> - ".userdate($activity->timestamp); 05876 echo '</div>'; 05877 echo "</td></tr></table>"; 05878 05879 return; 05880 } 05881 05891 function forum_change_discussionid($postid, $discussionid) { 05892 global $DB; 05893 $DB->set_field('forum_posts', 'discussion', $discussionid, array('id' => $postid)); 05894 if ($posts = $DB->get_records('forum_posts', array('parent' => $postid))) { 05895 foreach ($posts as $post) { 05896 forum_change_discussionid($post->id, $discussionid); 05897 } 05898 } 05899 return true; 05900 } 05901 05911 function forum_update_subscriptions_button($courseid, $forumid) { 05912 global $CFG, $USER; 05913 05914 if (!empty($USER->subscriptionsediting)) { 05915 $string = get_string('turneditingoff'); 05916 $edit = "off"; 05917 } else { 05918 $string = get_string('turneditingon'); 05919 $edit = "on"; 05920 } 05921 05922 return "<form method=\"get\" action=\"$CFG->wwwroot/mod/forum/subscribers.php\">". 05923 "<input type=\"hidden\" name=\"id\" value=\"$forumid\" />". 05924 "<input type=\"hidden\" name=\"edit\" value=\"$edit\" />". 05925 "<input type=\"submit\" value=\"$string\" /></form>"; 05926 } 05927 05934 function forum_user_enrolled($cp) { 05935 global $DB; 05936 05937 // NOTE: this has to be as fast as possible - we do not want to slow down enrolments! 05938 // Originally there used to be 'mod/forum:initialsubscriptions' which was 05939 // introduced because we did not have enrolment information in earlier versions... 05940 05941 $sql = "SELECT f.id 05942 FROM {forum} f 05943 LEFT JOIN {forum_subscriptions} fs ON (fs.forum = f.id AND fs.userid = :userid) 05944 WHERE f.course = :courseid AND f.forcesubscribe = :initial AND fs.id IS NULL"; 05945 $params = array('courseid'=>$cp->courseid, 'userid'=>$cp->userid, 'initial'=>FORUM_INITIALSUBSCRIBE); 05946 05947 $forums = $DB->get_records_sql($sql, $params); 05948 foreach ($forums as $forum) { 05949 forum_subscribe($cp->userid, $forum->id); 05950 } 05951 } 05952 05959 function forum_user_unenrolled($cp) { 05960 global $DB; 05961 05962 // NOTE: this has to be as fast as possible! 05963 05964 if ($cp->lastenrol) { 05965 $params = array('userid'=>$cp->userid, 'courseid'=>$cp->courseid); 05966 $forumselect = "IN (SELECT f.id FROM {forum} f WHERE f.course = :courseid)"; 05967 05968 $DB->delete_records_select('forum_subscriptions', "userid = :userid AND forum $forumselect", $params); 05969 $DB->delete_records_select('forum_track_prefs', "userid = :userid AND forumid $forumselect", $params); 05970 $DB->delete_records_select('forum_read', "userid = :userid AND forumid $forumselect", $params); 05971 } 05972 } 05973 05974 // Functions to do with read tracking. 05975 05985 function forum_tp_mark_posts_read($user, $postids) { 05986 global $CFG, $DB; 05987 05988 if (!forum_tp_can_track_forums(false, $user)) { 05989 return true; 05990 } 05991 05992 $status = true; 05993 05994 $now = time(); 05995 $cutoffdate = $now - ($CFG->forum_oldpostdays * 24 * 3600); 05996 05997 if (empty($postids)) { 05998 return true; 05999 06000 } else if (count($postids) > 200) { 06001 while ($part = array_splice($postids, 0, 200)) { 06002 $status = forum_tp_mark_posts_read($user, $part) && $status; 06003 } 06004 return $status; 06005 } 06006 06007 list($usql, $params) = $DB->get_in_or_equal($postids); 06008 $params[] = $user->id; 06009 06010 $sql = "SELECT id 06011 FROM {forum_read} 06012 WHERE postid $usql AND userid = ?"; 06013 if ($existing = $DB->get_records_sql($sql, $params)) { 06014 $existing = array_keys($existing); 06015 } else { 06016 $existing = array(); 06017 } 06018 06019 $new = array_diff($postids, $existing); 06020 06021 if ($new) { 06022 list($usql, $new_params) = $DB->get_in_or_equal($new); 06023 $params = array($user->id, $now, $now, $user->id); 06024 $params = array_merge($params, $new_params); 06025 $params[] = $cutoffdate; 06026 06027 $sql = "INSERT INTO {forum_read} (userid, postid, discussionid, forumid, firstread, lastread) 06028 06029 SELECT ?, p.id, p.discussion, d.forum, ?, ? 06030 FROM {forum_posts} p 06031 JOIN {forum_discussions} d ON d.id = p.discussion 06032 JOIN {forum} f ON f.id = d.forum 06033 LEFT JOIN {forum_track_prefs} tf ON (tf.userid = ? AND tf.forumid = f.id) 06034 WHERE p.id $usql 06035 AND p.modified >= ? 06036 AND (f.trackingtype = ".FORUM_TRACKING_ON." 06037 OR (f.trackingtype = ".FORUM_TRACKING_OPTIONAL." AND tf.id IS NULL))"; 06038 $status = $DB->execute($sql, $params) && $status; 06039 } 06040 06041 if ($existing) { 06042 list($usql, $new_params) = $DB->get_in_or_equal($existing); 06043 $params = array($now, $user->id); 06044 $params = array_merge($params, $new_params); 06045 06046 $sql = "UPDATE {forum_read} 06047 SET lastread = ? 06048 WHERE userid = ? AND postid $usql"; 06049 $status = $DB->execute($sql, $params) && $status; 06050 } 06051 06052 return $status; 06053 } 06054 06062 function forum_tp_add_read_record($userid, $postid) { 06063 global $CFG, $DB; 06064 06065 $now = time(); 06066 $cutoffdate = $now - ($CFG->forum_oldpostdays * 24 * 3600); 06067 06068 if (!$DB->record_exists('forum_read', array('userid' => $userid, 'postid' => $postid))) { 06069 $sql = "INSERT INTO {forum_read} (userid, postid, discussionid, forumid, firstread, lastread) 06070 06071 SELECT ?, p.id, p.discussion, d.forum, ?, ? 06072 FROM {forum_posts} p 06073 JOIN {forum_discussions} d ON d.id = p.discussion 06074 WHERE p.id = ? AND p.modified >= ?"; 06075 return $DB->execute($sql, array($userid, $now, $now, $postid, $cutoffdate)); 06076 06077 } else { 06078 $sql = "UPDATE {forum_read} 06079 SET lastread = ? 06080 WHERE userid = ? AND postid = ?"; 06081 return $DB->execute($sql, array($now, $userid, $userid)); 06082 } 06083 } 06084 06096 function forum_tp_get_read_records($userid=-1, $postid=-1, $discussionid=-1, $forumid=-1) { 06097 global $DB; 06098 $select = ''; 06099 $params = array(); 06100 06101 if ($userid > -1) { 06102 if ($select != '') $select .= ' AND '; 06103 $select .= 'userid = ?'; 06104 $params[] = $userid; 06105 } 06106 if ($postid > -1) { 06107 if ($select != '') $select .= ' AND '; 06108 $select .= 'postid = ?'; 06109 $params[] = $postid; 06110 } 06111 if ($discussionid > -1) { 06112 if ($select != '') $select .= ' AND '; 06113 $select .= 'discussionid = ?'; 06114 $params[] = $discussionid; 06115 } 06116 if ($forumid > -1) { 06117 if ($select != '') $select .= ' AND '; 06118 $select .= 'forumid = ?'; 06119 $params[] = $forumid; 06120 } 06121 06122 return $DB->get_records_select('forum_read', $select, $params); 06123 } 06124 06132 function forum_tp_get_discussion_read_records($userid, $discussionid) { 06133 global $DB; 06134 $select = 'userid = ? AND discussionid = ?'; 06135 $fields = 'postid, firstread, lastread'; 06136 return $DB->get_records_select('forum_read', $select, array($userid, $discussionid), '', $fields); 06137 } 06138 06144 function forum_tp_mark_post_read($userid, $post, $forumid) { 06145 if (!forum_tp_is_post_old($post)) { 06146 return forum_tp_add_read_record($userid, $post->id); 06147 } else { 06148 return true; 06149 } 06150 } 06151 06162 function forum_tp_mark_forum_read($user, $forumid, $groupid=false) { 06163 global $CFG, $DB; 06164 06165 $cutoffdate = time() - ($CFG->forum_oldpostdays*24*60*60); 06166 06167 $groupsel = ""; 06168 $params = array($user->id, $forumid, $cutoffdate); 06169 06170 if ($groupid !== false) { 06171 $groupsel = " AND (d.groupid = ? OR d.groupid = -1)"; 06172 $params[] = $groupid; 06173 } 06174 06175 $sql = "SELECT p.id 06176 FROM {forum_posts} p 06177 LEFT JOIN {forum_discussions} d ON d.id = p.discussion 06178 LEFT JOIN {forum_read} r ON (r.postid = p.id AND r.userid = ?) 06179 WHERE d.forum = ? 06180 AND p.modified >= ? AND r.id is NULL 06181 $groupsel"; 06182 06183 if ($posts = $DB->get_records_sql($sql, $params)) { 06184 $postids = array_keys($posts); 06185 return forum_tp_mark_posts_read($user, $postids); 06186 } 06187 06188 return true; 06189 } 06190 06200 function forum_tp_mark_discussion_read($user, $discussionid) { 06201 global $CFG, $DB; 06202 06203 $cutoffdate = time() - ($CFG->forum_oldpostdays*24*60*60); 06204 06205 $sql = "SELECT p.id 06206 FROM {forum_posts} p 06207 LEFT JOIN {forum_read} r ON (r.postid = p.id AND r.userid = ?) 06208 WHERE p.discussion = ? 06209 AND p.modified >= ? AND r.id is NULL"; 06210 06211 if ($posts = $DB->get_records_sql($sql, array($user->id, $discussionid, $cutoffdate))) { 06212 $postids = array_keys($posts); 06213 return forum_tp_mark_posts_read($user, $postids); 06214 } 06215 06216 return true; 06217 } 06218 06224 function forum_tp_is_post_read($userid, $post) { 06225 global $DB; 06226 return (forum_tp_is_post_old($post) || 06227 $DB->record_exists('forum_read', array('userid' => $userid, 'postid' => $post->id))); 06228 } 06229 06235 function forum_tp_is_post_old($post, $time=null) { 06236 global $CFG; 06237 06238 if (is_null($time)) { 06239 $time = time(); 06240 } 06241 return ($post->modified < ($time - ($CFG->forum_oldpostdays * 24 * 3600))); 06242 } 06243 06253 function forum_tp_count_discussion_read_records($userid, $discussionid) { 06254 global $CFG, $DB; 06255 06256 $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0; 06257 06258 $sql = 'SELECT COUNT(DISTINCT p.id) '. 06259 'FROM {forum_discussions} d '. 06260 'LEFT JOIN {forum_read} r ON d.id = r.discussionid AND r.userid = ? '. 06261 'LEFT JOIN {forum_posts} p ON p.discussion = d.id '. 06262 'AND (p.modified < ? OR p.id = r.postid) '. 06263 'WHERE d.id = ? '; 06264 06265 return ($DB->count_records_sql($sql, array($userid, $cutoffdate, $discussionid))); 06266 } 06267 06277 function forum_tp_count_discussion_unread_posts($userid, $discussionid) { 06278 global $CFG, $DB; 06279 06280 $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0; 06281 06282 $sql = 'SELECT COUNT(p.id) '. 06283 'FROM {forum_posts} p '. 06284 'LEFT JOIN {forum_read} r ON r.postid = p.id AND r.userid = ? '. 06285 'WHERE p.discussion = ? '. 06286 'AND p.modified >= ? AND r.id is NULL'; 06287 06288 return $DB->count_records_sql($sql, array($userid, $cutoffdate, $discussionid)); 06289 } 06290 06299 function forum_tp_count_forum_posts($forumid, $groupid=false) { 06300 global $CFG, $DB; 06301 $params = array($forumid); 06302 $sql = 'SELECT COUNT(*) '. 06303 'FROM {forum_posts} fp,{forum_discussions} fd '. 06304 'WHERE fd.forum = ? AND fp.discussion = fd.id'; 06305 if ($groupid !== false) { 06306 $sql .= ' AND (fd.groupid = ? OR fd.groupid = -1)'; 06307 $params[] = $groupid; 06308 } 06309 $count = $DB->count_records_sql($sql, $params); 06310 06311 06312 return $count; 06313 } 06314 06324 function forum_tp_count_forum_read_records($userid, $forumid, $groupid=false) { 06325 global $CFG, $DB; 06326 06327 $cutoffdate = time() - ($CFG->forum_oldpostdays*24*60*60); 06328 06329 $groupsel = ''; 06330 $params = array($userid, $forumid, $cutoffdate); 06331 if ($groupid !== false) { 06332 $groupsel = "AND (d.groupid = ? OR d.groupid = -1)"; 06333 $params[] = $groupid; 06334 } 06335 06336 $sql = "SELECT COUNT(p.id) 06337 FROM {forum_posts} p 06338 JOIN {forum_discussions} d ON d.id = p.discussion 06339 LEFT JOIN {forum_read} r ON (r.postid = p.id AND r.userid= ?) 06340 WHERE d.forum = ? 06341 AND (p.modified < $cutoffdate OR (p.modified >= ? AND r.id IS NOT NULL)) 06342 $groupsel"; 06343 06344 return $DB->get_field_sql($sql, $params); 06345 } 06346 06357 function forum_tp_get_course_unread_posts($userid, $courseid) { 06358 global $CFG, $DB; 06359 06360 $now = round(time(), -2); // db cache friendliness 06361 $cutoffdate = $now - ($CFG->forum_oldpostdays*24*60*60); 06362 $params = array($userid, $userid, $courseid, $cutoffdate); 06363 06364 if (!empty($CFG->forum_enabletimedposts)) { 06365 $timedsql = "AND d.timestart < ? AND (d.timeend = 0 OR d.timeend > ?)"; 06366 $params[] = $now; 06367 $params[] = $now; 06368 } else { 06369 $timedsql = ""; 06370 } 06371 06372 $sql = "SELECT f.id, COUNT(p.id) AS unread 06373 FROM {forum_posts} p 06374 JOIN {forum_discussions} d ON d.id = p.discussion 06375 JOIN {forum} f ON f.id = d.forum 06376 JOIN {course} c ON c.id = f.course 06377 LEFT JOIN {forum_read} r ON (r.postid = p.id AND r.userid = ?) 06378 LEFT JOIN {forum_track_prefs} tf ON (tf.userid = ? AND tf.forumid = f.id) 06379 WHERE f.course = ? 06380 AND p.modified >= ? AND r.id is NULL 06381 AND (f.trackingtype = ".FORUM_TRACKING_ON." 06382 OR (f.trackingtype = ".FORUM_TRACKING_OPTIONAL." AND tf.id IS NULL)) 06383 $timedsql 06384 GROUP BY f.id"; 06385 06386 if ($return = $DB->get_records_sql($sql, $params)) { 06387 return $return; 06388 } 06389 06390 return array(); 06391 } 06392 06403 function forum_tp_count_forum_unread_posts($cm, $course) { 06404 global $CFG, $USER, $DB; 06405 06406 static $readcache = array(); 06407 06408 $forumid = $cm->instance; 06409 06410 if (!isset($readcache[$course->id])) { 06411 $readcache[$course->id] = array(); 06412 if ($counts = forum_tp_get_course_unread_posts($USER->id, $course->id)) { 06413 foreach ($counts as $count) { 06414 $readcache[$course->id][$count->id] = $count->unread; 06415 } 06416 } 06417 } 06418 06419 if (empty($readcache[$course->id][$forumid])) { 06420 // no need to check group mode ;-) 06421 return 0; 06422 } 06423 06424 $groupmode = groups_get_activity_groupmode($cm, $course); 06425 06426 if ($groupmode != SEPARATEGROUPS) { 06427 return $readcache[$course->id][$forumid]; 06428 } 06429 06430 if (has_capability('moodle/site:accessallgroups', get_context_instance(CONTEXT_MODULE, $cm->id))) { 06431 return $readcache[$course->id][$forumid]; 06432 } 06433 06434 require_once($CFG->dirroot.'/course/lib.php'); 06435 06436 $modinfo =& get_fast_modinfo($course); 06437 if (is_null($modinfo->groups)) { 06438 $modinfo->groups = groups_get_user_groups($course->id, $USER->id); 06439 } 06440 06441 $mygroups = $modinfo->groups[$cm->groupingid]; 06442 06443 // add all groups posts 06444 if (empty($mygroups)) { 06445 $mygroups = array(-1=>-1); 06446 } else { 06447 $mygroups[-1] = -1; 06448 } 06449 06450 list ($groups_sql, $groups_params) = $DB->get_in_or_equal($mygroups); 06451 06452 $now = round(time(), -2); // db cache friendliness 06453 $cutoffdate = $now - ($CFG->forum_oldpostdays*24*60*60); 06454 $params = array($USER->id, $forumid, $cutoffdate); 06455 06456 if (!empty($CFG->forum_enabletimedposts)) { 06457 $timedsql = "AND d.timestart < ? AND (d.timeend = 0 OR d.timeend > ?)"; 06458 $params[] = $now; 06459 $params[] = $now; 06460 } else { 06461 $timedsql = ""; 06462 } 06463 06464 $params = array_merge($params, $groups_params); 06465 06466 $sql = "SELECT COUNT(p.id) 06467 FROM {forum_posts} p 06468 JOIN {forum_discussions} d ON p.discussion = d.id 06469 LEFT JOIN {forum_read} r ON (r.postid = p.id AND r.userid = ?) 06470 WHERE d.forum = ? 06471 AND p.modified >= ? AND r.id is NULL 06472 $timedsql 06473 AND d.groupid $groups_sql"; 06474 06475 return $DB->get_field_sql($sql, $params); 06476 } 06477 06488 function forum_tp_delete_read_records($userid=-1, $postid=-1, $discussionid=-1, $forumid=-1) { 06489 global $DB; 06490 $params = array(); 06491 06492 $select = ''; 06493 if ($userid > -1) { 06494 if ($select != '') $select .= ' AND '; 06495 $select .= 'userid = ?'; 06496 $params[] = $userid; 06497 } 06498 if ($postid > -1) { 06499 if ($select != '') $select .= ' AND '; 06500 $select .= 'postid = ?'; 06501 $params[] = $postid; 06502 } 06503 if ($discussionid > -1) { 06504 if ($select != '') $select .= ' AND '; 06505 $select .= 'discussionid = ?'; 06506 $params[] = $discussionid; 06507 } 06508 if ($forumid > -1) { 06509 if ($select != '') $select .= ' AND '; 06510 $select .= 'forumid = ?'; 06511 $params[] = $forumid; 06512 } 06513 if ($select == '') { 06514 return false; 06515 } 06516 else { 06517 return $DB->delete_records_select('forum_read', $select, $params); 06518 } 06519 } 06529 function forum_tp_get_untracked_forums($userid, $courseid) { 06530 global $CFG, $DB; 06531 06532 $sql = "SELECT f.id 06533 FROM {forum} f 06534 LEFT JOIN {forum_track_prefs} ft ON (ft.forumid = f.id AND ft.userid = ?) 06535 WHERE f.course = ? 06536 AND (f.trackingtype = ".FORUM_TRACKING_OFF." 06537 OR (f.trackingtype = ".FORUM_TRACKING_OPTIONAL." AND ft.id IS NOT NULL))"; 06538 06539 if ($forums = $DB->get_records_sql($sql, array($userid, $courseid))) { 06540 foreach ($forums as $forum) { 06541 $forums[$forum->id] = $forum; 06542 } 06543 return $forums; 06544 06545 } else { 06546 return array(); 06547 } 06548 } 06549 06562 function forum_tp_can_track_forums($forum=false, $user=false) { 06563 global $USER, $CFG, $DB; 06564 06565 // if possible, avoid expensive 06566 // queries 06567 if (empty($CFG->forum_trackreadposts)) { 06568 return false; 06569 } 06570 06571 if ($user === false) { 06572 $user = $USER; 06573 } 06574 06575 if (isguestuser($user) or empty($user->id)) { 06576 return false; 06577 } 06578 06579 if ($forum === false) { 06580 // general abitily to track forums 06581 return (bool)$user->trackforums; 06582 } 06583 06584 06585 // Work toward always passing an object... 06586 if (is_numeric($forum)) { 06587 debugging('Better use proper forum object.', DEBUG_DEVELOPER); 06588 $forum = $DB->get_record('forum', array('id' => $forum), '', 'id,trackingtype'); 06589 } 06590 06591 $forumallows = ($forum->trackingtype == FORUM_TRACKING_OPTIONAL); 06592 $forumforced = ($forum->trackingtype == FORUM_TRACKING_ON); 06593 06594 return ($forumforced || $forumallows) && !empty($user->trackforums); 06595 } 06596 06608 function forum_tp_is_tracked($forum, $user=false) { 06609 global $USER, $CFG, $DB; 06610 06611 if ($user === false) { 06612 $user = $USER; 06613 } 06614 06615 if (isguestuser($user) or empty($user->id)) { 06616 return false; 06617 } 06618 06619 // Work toward always passing an object... 06620 if (is_numeric($forum)) { 06621 debugging('Better use proper forum object.', DEBUG_DEVELOPER); 06622 $forum = $DB->get_record('forum', array('id' => $forum)); 06623 } 06624 06625 if (!forum_tp_can_track_forums($forum, $user)) { 06626 return false; 06627 } 06628 06629 $forumallows = ($forum->trackingtype == FORUM_TRACKING_OPTIONAL); 06630 $forumforced = ($forum->trackingtype == FORUM_TRACKING_ON); 06631 06632 return $forumforced || 06633 ($forumallows && $DB->get_record('forum_track_prefs', array('userid' => $user->id, 'forumid' => $forum->id)) === false); 06634 } 06635 06642 function forum_tp_start_tracking($forumid, $userid=false) { 06643 global $USER, $DB; 06644 06645 if ($userid === false) { 06646 $userid = $USER->id; 06647 } 06648 06649 return $DB->delete_records('forum_track_prefs', array('userid' => $userid, 'forumid' => $forumid)); 06650 } 06651 06658 function forum_tp_stop_tracking($forumid, $userid=false) { 06659 global $USER, $DB; 06660 06661 if ($userid === false) { 06662 $userid = $USER->id; 06663 } 06664 06665 if (!$DB->record_exists('forum_track_prefs', array('userid' => $userid, 'forumid' => $forumid))) { 06666 $track_prefs = new stdClass(); 06667 $track_prefs->userid = $userid; 06668 $track_prefs->forumid = $forumid; 06669 $DB->insert_record('forum_track_prefs', $track_prefs); 06670 } 06671 06672 return forum_tp_delete_read_records($userid, -1, -1, $forumid); 06673 } 06674 06675 06682 function forum_tp_clean_read_records() { 06683 global $CFG, $DB; 06684 06685 if (!isset($CFG->forum_oldpostdays)) { 06686 return; 06687 } 06688 // Look for records older than the cutoffdate that are still in the forum_read table. 06689 $cutoffdate = time() - ($CFG->forum_oldpostdays*24*60*60); 06690 06691 //first get the oldest tracking present - we need tis to speedup the next delete query 06692 $sql = "SELECT MIN(fp.modified) AS first 06693 FROM {forum_posts} fp 06694 JOIN {forum_read} fr ON fr.postid=fp.id"; 06695 if (!$first = $DB->get_field_sql($sql)) { 06696 // nothing to delete; 06697 return; 06698 } 06699 06700 // now delete old tracking info 06701 $sql = "DELETE 06702 FROM {forum_read} 06703 WHERE postid IN (SELECT fp.id 06704 FROM {forum_posts} fp 06705 WHERE fp.modified >= ? AND fp.modified < ?)"; 06706 $DB->execute($sql, array($first, $cutoffdate)); 06707 } 06708 06717 function forum_discussion_update_last_post($discussionid) { 06718 global $CFG, $DB; 06719 06720 // Check the given discussion exists 06721 if (!$DB->record_exists('forum_discussions', array('id' => $discussionid))) { 06722 return false; 06723 } 06724 06725 // Use SQL to find the last post for this discussion 06726 $sql = "SELECT id, userid, modified 06727 FROM {forum_posts} 06728 WHERE discussion=? 06729 ORDER BY modified DESC"; 06730 06731 // Lets go find the last post 06732 if (($lastposts = $DB->get_records_sql($sql, array($discussionid), 0, 1))) { 06733 $lastpost = reset($lastposts); 06734 $discussionobject = new stdClass(); 06735 $discussionobject->id = $discussionid; 06736 $discussionobject->usermodified = $lastpost->userid; 06737 $discussionobject->timemodified = $lastpost->modified; 06738 $DB->update_record('forum_discussions', $discussionobject); 06739 return $lastpost->id; 06740 } 06741 06742 // To get here either we couldn't find a post for the discussion (weird) 06743 // or we couldn't update the discussion record (weird x2) 06744 return false; 06745 } 06746 06747 06751 function forum_get_view_actions() { 06752 return array('view discussion', 'search', 'forum', 'forums', 'subscribers', 'view forum'); 06753 } 06754 06758 function forum_get_post_actions() { 06759 return array('add discussion','add post','delete discussion','delete post','move discussion','prune post','update post'); 06760 } 06761 06770 function forum_get_separate_modules($courseid) { 06771 06772 global $CFG,$DB; 06773 $forummodule = $DB->get_record("modules", array("name" => "forum")); 06774 06775 $sql = 'SELECT f.id, f.id FROM {forum} f, {course_modules} cm WHERE 06776 f.id = cm.instance AND cm.module =? AND cm.visible = 1 AND cm.course = ? 06777 AND cm.groupmode ='.SEPARATEGROUPS; 06778 06779 return $DB->get_records_sql($sql, array($forummodule->id, $courseid)); 06780 06781 } 06782 06791 function forum_check_throttling($forum, $cm=null) { 06792 global $USER, $CFG, $DB, $OUTPUT; 06793 06794 if (is_numeric($forum)) { 06795 $forum = $DB->get_record('forum',array('id'=>$forum)); 06796 } 06797 if (!is_object($forum)) { 06798 return false; // this is broken. 06799 } 06800 06801 if (empty($forum->blockafter)) { 06802 return true; 06803 } 06804 06805 if (empty($forum->blockperiod)) { 06806 return true; 06807 } 06808 06809 if (!$cm) { 06810 if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) { 06811 print_error('invalidcoursemodule'); 06812 } 06813 } 06814 06815 $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id); 06816 if(has_capability('mod/forum:postwithoutthrottling', $modcontext)) { 06817 return true; 06818 } 06819 06820 // get the number of posts in the last period we care about 06821 $timenow = time(); 06822 $timeafter = $timenow - $forum->blockperiod; 06823 06824 $numposts = $DB->count_records_sql('SELECT COUNT(p.id) FROM {forum_posts} p' 06825 .' JOIN {forum_discussions} d' 06826 .' ON p.discussion = d.id WHERE d.forum = ?' 06827 .' AND p.userid = ? AND p.created > ?', array($forum->id, $USER->id, $timeafter)); 06828 06829 $a = new stdClass(); 06830 $a->blockafter = $forum->blockafter; 06831 $a->numposts = $numposts; 06832 $a->blockperiod = get_string('secondstotime'.$forum->blockperiod); 06833 06834 if ($forum->blockafter <= $numposts) { 06835 print_error('forumblockingtoomanyposts', 'error', $CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id, $a); 06836 } 06837 if ($forum->warnafter <= $numposts) { 06838 echo $OUTPUT->notification(get_string('forumblockingalmosttoomanyposts','forum',$a)); 06839 } 06840 06841 06842 } 06843 06844 06853 function forum_reset_gradebook($courseid, $type='') { 06854 global $CFG, $DB; 06855 06856 $wheresql = ''; 06857 $params = array($courseid); 06858 if ($type) { 06859 $wheresql = "AND f.type=?"; 06860 $params[] = $type; 06861 } 06862 06863 $sql = "SELECT f.*, cm.idnumber as cmidnumber, f.course as courseid 06864 FROM {forum} f, {course_modules} cm, {modules} m 06865 WHERE m.name='forum' AND m.id=cm.module AND cm.instance=f.id AND f.course=? $wheresql"; 06866 06867 if ($forums = $DB->get_records_sql($sql, $params)) { 06868 foreach ($forums as $forum) { 06869 forum_grade_item_update($forum, 'reset'); 06870 } 06871 } 06872 } 06873 06884 function forum_reset_userdata($data) { 06885 global $CFG, $DB; 06886 require_once($CFG->dirroot.'/rating/lib.php'); 06887 06888 $componentstr = get_string('modulenameplural', 'forum'); 06889 $status = array(); 06890 06891 $params = array($data->courseid); 06892 06893 $removeposts = false; 06894 $typesql = ""; 06895 if (!empty($data->reset_forum_all)) { 06896 $removeposts = true; 06897 $typesstr = get_string('resetforumsall', 'forum'); 06898 $types = array(); 06899 } else if (!empty($data->reset_forum_types)){ 06900 $removeposts = true; 06901 $typesql = ""; 06902 $types = array(); 06903 $forum_types_all = forum_get_forum_types_all(); 06904 foreach ($data->reset_forum_types as $type) { 06905 if (!array_key_exists($type, $forum_types_all)) { 06906 continue; 06907 } 06908 $typesql .= " AND f.type=?"; 06909 $types[] = $forum_types_all[$type]; 06910 $params[] = $type; 06911 } 06912 $typesstr = get_string('resetforums', 'forum').': '.implode(', ', $types); 06913 } 06914 $alldiscussionssql = "SELECT fd.id 06915 FROM {forum_discussions} fd, {forum} f 06916 WHERE f.course=? AND f.id=fd.forum"; 06917 06918 $allforumssql = "SELECT f.id 06919 FROM {forum} f 06920 WHERE f.course=?"; 06921 06922 $allpostssql = "SELECT fp.id 06923 FROM {forum_posts} fp, {forum_discussions} fd, {forum} f 06924 WHERE f.course=? AND f.id=fd.forum AND fd.id=fp.discussion"; 06925 06926 $forumssql = $forums = $rm = null; 06927 06928 if( $removeposts || !empty($data->reset_forum_ratings) ) { 06929 $forumssql = "$allforumssql $typesql"; 06930 $forums = $forums = $DB->get_records_sql($forumssql, $params); 06931 $rm = new rating_manager();; 06932 $ratingdeloptions = new stdClass; 06933 $ratingdeloptions->component = 'mod_forum'; 06934 $ratingdeloptions->ratingarea = 'post'; 06935 } 06936 06937 if ($removeposts) { 06938 $discussionssql = "$alldiscussionssql $typesql"; 06939 $postssql = "$allpostssql $typesql"; 06940 06941 // now get rid of all attachments 06942 $fs = get_file_storage(); 06943 if ($forums) { 06944 foreach ($forums as $forumid=>$unused) { 06945 if (!$cm = get_coursemodule_from_instance('forum', $forumid)) { 06946 continue; 06947 } 06948 $context = get_context_instance(CONTEXT_MODULE, $cm->id); 06949 $fs->delete_area_files($context->id, 'mod_forum', 'attachment'); 06950 $fs->delete_area_files($context->id, 'mod_forum', 'post'); 06951 06952 //remove ratings 06953 $ratingdeloptions->contextid = $context->id; 06954 $rm->delete_ratings($ratingdeloptions); 06955 } 06956 } 06957 06958 // first delete all read flags 06959 $DB->delete_records_select('forum_read', "forumid IN ($forumssql)", $params); 06960 06961 // remove tracking prefs 06962 $DB->delete_records_select('forum_track_prefs', "forumid IN ($forumssql)", $params); 06963 06964 // remove posts from queue 06965 $DB->delete_records_select('forum_queue', "discussionid IN ($discussionssql)", $params); 06966 06967 // all posts - initial posts must be kept in single simple discussion forums 06968 $DB->delete_records_select('forum_posts', "discussion IN ($discussionssql) AND parent <> 0", $params); // first all children 06969 $DB->delete_records_select('forum_posts', "discussion IN ($discussionssql AND f.type <> 'single') AND parent = 0", $params); // now the initial posts for non single simple 06970 06971 // finally all discussions except single simple forums 06972 $DB->delete_records_select('forum_discussions', "forum IN ($forumssql AND f.type <> 'single')", $params); 06973 06974 // remove all grades from gradebook 06975 if (empty($data->reset_gradebook_grades)) { 06976 if (empty($types)) { 06977 forum_reset_gradebook($data->courseid); 06978 } else { 06979 foreach ($types as $type) { 06980 forum_reset_gradebook($data->courseid, $type); 06981 } 06982 } 06983 } 06984 06985 $status[] = array('component'=>$componentstr, 'item'=>$typesstr, 'error'=>false); 06986 } 06987 06988 // remove all ratings in this course's forums 06989 if (!empty($data->reset_forum_ratings)) { 06990 if ($forums) { 06991 foreach ($forums as $forumid=>$unused) { 06992 if (!$cm = get_coursemodule_from_instance('forum', $forumid)) { 06993 continue; 06994 } 06995 $context = get_context_instance(CONTEXT_MODULE, $cm->id); 06996 06997 //remove ratings 06998 $ratingdeloptions->contextid = $context->id; 06999 $rm->delete_ratings($ratingdeloptions); 07000 } 07001 } 07002 07003 // remove all grades from gradebook 07004 if (empty($data->reset_gradebook_grades)) { 07005 forum_reset_gradebook($data->courseid); 07006 } 07007 } 07008 07009 // remove all subscriptions unconditionally - even for users still enrolled in course 07010 if (!empty($data->reset_forum_subscriptions)) { 07011 $DB->delete_records_select('forum_subscriptions', "forum IN ($allforumssql)", $params); 07012 $status[] = array('component'=>$componentstr, 'item'=>get_string('resetsubscriptions','forum'), 'error'=>false); 07013 } 07014 07015 // remove all tracking prefs unconditionally - even for users still enrolled in course 07016 if (!empty($data->reset_forum_track_prefs)) { 07017 $DB->delete_records_select('forum_track_prefs', "forumid IN ($allforumssql)", $params); 07018 $status[] = array('component'=>$componentstr, 'item'=>get_string('resettrackprefs','forum'), 'error'=>false); 07019 } 07020 07022 if ($data->timeshift) { 07023 shift_course_mod_dates('forum', array('assesstimestart', 'assesstimefinish'), $data->timeshift, $data->courseid); 07024 $status[] = array('component'=>$componentstr, 'item'=>get_string('datechanged'), 'error'=>false); 07025 } 07026 07027 return $status; 07028 } 07029 07035 function forum_reset_course_form_definition(&$mform) { 07036 $mform->addElement('header', 'forumheader', get_string('modulenameplural', 'forum')); 07037 07038 $mform->addElement('checkbox', 'reset_forum_all', get_string('resetforumsall','forum')); 07039 07040 $mform->addElement('select', 'reset_forum_types', get_string('resetforums', 'forum'), forum_get_forum_types_all(), array('multiple' => 'multiple')); 07041 $mform->setAdvanced('reset_forum_types'); 07042 $mform->disabledIf('reset_forum_types', 'reset_forum_all', 'checked'); 07043 07044 $mform->addElement('checkbox', 'reset_forum_subscriptions', get_string('resetsubscriptions','forum')); 07045 $mform->setAdvanced('reset_forum_subscriptions'); 07046 07047 $mform->addElement('checkbox', 'reset_forum_track_prefs', get_string('resettrackprefs','forum')); 07048 $mform->setAdvanced('reset_forum_track_prefs'); 07049 $mform->disabledIf('reset_forum_track_prefs', 'reset_forum_all', 'checked'); 07050 07051 $mform->addElement('checkbox', 'reset_forum_ratings', get_string('deleteallratings')); 07052 $mform->disabledIf('reset_forum_ratings', 'reset_forum_all', 'checked'); 07053 } 07054 07059 function forum_reset_course_form_defaults($course) { 07060 return array('reset_forum_all'=>1, 'reset_forum_subscriptions'=>0, 'reset_forum_track_prefs'=>0, 'reset_forum_ratings'=>1); 07061 } 07062 07077 function forum_convert_to_roles($forum, $forummodid, $teacherroles=array(), 07078 $studentroles=array(), $guestroles=array(), $cmid=NULL) { 07079 07080 global $CFG, $DB, $OUTPUT; 07081 07082 if (!isset($forum->open) && !isset($forum->assesspublic)) { 07083 // We assume that this forum has already been converted to use the 07084 // Roles System. Columns forum.open and forum.assesspublic get dropped 07085 // once the forum module has been upgraded to use Roles. 07086 return false; 07087 } 07088 07089 if ($forum->type == 'teacher') { 07090 07091 // Teacher forums should be converted to normal forums that 07092 // use the Roles System to implement the old behavior. 07093 // Note: 07094 // Seems that teacher forums were never backed up in 1.6 since they 07095 // didn't have an entry in the course_modules table. 07096 require_once($CFG->dirroot.'/course/lib.php'); 07097 07098 if ($DB->count_records('forum_discussions', array('forum' => $forum->id)) == 0) { 07099 // Delete empty teacher forums. 07100 $DB->delete_records('forum', array('id' => $forum->id)); 07101 } else { 07102 // Create a course module for the forum and assign it to 07103 // section 0 in the course. 07104 $mod = new stdClass(); 07105 $mod->course = $forum->course; 07106 $mod->module = $forummodid; 07107 $mod->instance = $forum->id; 07108 $mod->section = 0; 07109 $mod->visible = 0; // Hide the forum 07110 $mod->visibleold = 0; // Hide the forum 07111 $mod->groupmode = 0; 07112 07113 if (!$cmid = add_course_module($mod)) { 07114 print_error('cannotcreateinstanceforteacher', 'forum'); 07115 } else { 07116 $mod->coursemodule = $cmid; 07117 if (!$sectionid = add_mod_to_section($mod)) { 07118 print_error('cannotaddteacherforumto', 'forum'); 07119 } else { 07120 $DB->set_field('course_modules', 'section', $sectionid, array('id' => $cmid)); 07121 } 07122 } 07123 07124 // Change the forum type to general. 07125 $forum->type = 'general'; 07126 $DB->update_record('forum', $forum); 07127 07128 $context = get_context_instance(CONTEXT_MODULE, $cmid); 07129 07130 // Create overrides for default student and guest roles (prevent). 07131 foreach ($studentroles as $studentrole) { 07132 assign_capability('mod/forum:viewdiscussion', CAP_PREVENT, $studentrole->id, $context->id); 07133 assign_capability('mod/forum:viewhiddentimedposts', CAP_PREVENT, $studentrole->id, $context->id); 07134 assign_capability('mod/forum:startdiscussion', CAP_PREVENT, $studentrole->id, $context->id); 07135 assign_capability('mod/forum:replypost', CAP_PREVENT, $studentrole->id, $context->id); 07136 assign_capability('mod/forum:viewrating', CAP_PREVENT, $studentrole->id, $context->id); 07137 assign_capability('mod/forum:viewanyrating', CAP_PREVENT, $studentrole->id, $context->id); 07138 assign_capability('mod/forum:rate', CAP_PREVENT, $studentrole->id, $context->id); 07139 assign_capability('mod/forum:createattachment', CAP_PREVENT, $studentrole->id, $context->id); 07140 assign_capability('mod/forum:deleteownpost', CAP_PREVENT, $studentrole->id, $context->id); 07141 assign_capability('mod/forum:deleteanypost', CAP_PREVENT, $studentrole->id, $context->id); 07142 assign_capability('mod/forum:splitdiscussions', CAP_PREVENT, $studentrole->id, $context->id); 07143 assign_capability('mod/forum:movediscussions', CAP_PREVENT, $studentrole->id, $context->id); 07144 assign_capability('mod/forum:editanypost', CAP_PREVENT, $studentrole->id, $context->id); 07145 assign_capability('mod/forum:viewqandawithoutposting', CAP_PREVENT, $studentrole->id, $context->id); 07146 assign_capability('mod/forum:viewsubscribers', CAP_PREVENT, $studentrole->id, $context->id); 07147 assign_capability('mod/forum:managesubscriptions', CAP_PREVENT, $studentrole->id, $context->id); 07148 assign_capability('mod/forum:postwithoutthrottling', CAP_PREVENT, $studentrole->id, $context->id); 07149 } 07150 foreach ($guestroles as $guestrole) { 07151 assign_capability('mod/forum:viewdiscussion', CAP_PREVENT, $guestrole->id, $context->id); 07152 assign_capability('mod/forum:viewhiddentimedposts', CAP_PREVENT, $guestrole->id, $context->id); 07153 assign_capability('mod/forum:startdiscussion', CAP_PREVENT, $guestrole->id, $context->id); 07154 assign_capability('mod/forum:replypost', CAP_PREVENT, $guestrole->id, $context->id); 07155 assign_capability('mod/forum:viewrating', CAP_PREVENT, $guestrole->id, $context->id); 07156 assign_capability('mod/forum:viewanyrating', CAP_PREVENT, $guestrole->id, $context->id); 07157 assign_capability('mod/forum:rate', CAP_PREVENT, $guestrole->id, $context->id); 07158 assign_capability('mod/forum:createattachment', CAP_PREVENT, $guestrole->id, $context->id); 07159 assign_capability('mod/forum:deleteownpost', CAP_PREVENT, $guestrole->id, $context->id); 07160 assign_capability('mod/forum:deleteanypost', CAP_PREVENT, $guestrole->id, $context->id); 07161 assign_capability('mod/forum:splitdiscussions', CAP_PREVENT, $guestrole->id, $context->id); 07162 assign_capability('mod/forum:movediscussions', CAP_PREVENT, $guestrole->id, $context->id); 07163 assign_capability('mod/forum:editanypost', CAP_PREVENT, $guestrole->id, $context->id); 07164 assign_capability('mod/forum:viewqandawithoutposting', CAP_PREVENT, $guestrole->id, $context->id); 07165 assign_capability('mod/forum:viewsubscribers', CAP_PREVENT, $guestrole->id, $context->id); 07166 assign_capability('mod/forum:managesubscriptions', CAP_PREVENT, $guestrole->id, $context->id); 07167 assign_capability('mod/forum:postwithoutthrottling', CAP_PREVENT, $guestrole->id, $context->id); 07168 } 07169 } 07170 } else { 07171 // Non-teacher forum. 07172 07173 if (empty($cmid)) { 07174 // We were not given the course_module id. Try to find it. 07175 if (!$cm = get_coursemodule_from_instance('forum', $forum->id)) { 07176 echo $OUTPUT->notification('Could not get the course module for the forum'); 07177 return false; 07178 } else { 07179 $cmid = $cm->id; 07180 } 07181 } 07182 $context = get_context_instance(CONTEXT_MODULE, $cmid); 07183 07184 // $forum->open defines what students can do: 07185 // 0 = No discussions, no replies 07186 // 1 = No discussions, but replies are allowed 07187 // 2 = Discussions and replies are allowed 07188 switch ($forum->open) { 07189 case 0: 07190 foreach ($studentroles as $studentrole) { 07191 assign_capability('mod/forum:startdiscussion', CAP_PREVENT, $studentrole->id, $context->id); 07192 assign_capability('mod/forum:replypost', CAP_PREVENT, $studentrole->id, $context->id); 07193 } 07194 break; 07195 case 1: 07196 foreach ($studentroles as $studentrole) { 07197 assign_capability('mod/forum:startdiscussion', CAP_PREVENT, $studentrole->id, $context->id); 07198 assign_capability('mod/forum:replypost', CAP_ALLOW, $studentrole->id, $context->id); 07199 } 07200 break; 07201 case 2: 07202 foreach ($studentroles as $studentrole) { 07203 assign_capability('mod/forum:startdiscussion', CAP_ALLOW, $studentrole->id, $context->id); 07204 assign_capability('mod/forum:replypost', CAP_ALLOW, $studentrole->id, $context->id); 07205 } 07206 break; 07207 } 07208 07209 // $forum->assessed defines whether forum rating is turned 07210 // on (1 or 2) and who can rate posts: 07211 // 1 = Everyone can rate posts 07212 // 2 = Only teachers can rate posts 07213 switch ($forum->assessed) { 07214 case 1: 07215 foreach ($studentroles as $studentrole) { 07216 assign_capability('mod/forum:rate', CAP_ALLOW, $studentrole->id, $context->id); 07217 } 07218 foreach ($teacherroles as $teacherrole) { 07219 assign_capability('mod/forum:rate', CAP_ALLOW, $teacherrole->id, $context->id); 07220 } 07221 break; 07222 case 2: 07223 foreach ($studentroles as $studentrole) { 07224 assign_capability('mod/forum:rate', CAP_PREVENT, $studentrole->id, $context->id); 07225 } 07226 foreach ($teacherroles as $teacherrole) { 07227 assign_capability('mod/forum:rate', CAP_ALLOW, $teacherrole->id, $context->id); 07228 } 07229 break; 07230 } 07231 07232 // $forum->assesspublic defines whether students can see 07233 // everybody's ratings: 07234 // 0 = Students can only see their own ratings 07235 // 1 = Students can see everyone's ratings 07236 switch ($forum->assesspublic) { 07237 case 0: 07238 foreach ($studentroles as $studentrole) { 07239 assign_capability('mod/forum:viewanyrating', CAP_PREVENT, $studentrole->id, $context->id); 07240 } 07241 foreach ($teacherroles as $teacherrole) { 07242 assign_capability('mod/forum:viewanyrating', CAP_ALLOW, $teacherrole->id, $context->id); 07243 } 07244 break; 07245 case 1: 07246 foreach ($studentroles as $studentrole) { 07247 assign_capability('mod/forum:viewanyrating', CAP_ALLOW, $studentrole->id, $context->id); 07248 } 07249 foreach ($teacherroles as $teacherrole) { 07250 assign_capability('mod/forum:viewanyrating', CAP_ALLOW, $teacherrole->id, $context->id); 07251 } 07252 break; 07253 } 07254 07255 if (empty($cm)) { 07256 $cm = $DB->get_record('course_modules', array('id' => $cmid)); 07257 } 07258 07259 // $cm->groupmode: 07260 // 0 - No groups 07261 // 1 - Separate groups 07262 // 2 - Visible groups 07263 switch ($cm->groupmode) { 07264 case 0: 07265 break; 07266 case 1: 07267 foreach ($studentroles as $studentrole) { 07268 assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $studentrole->id, $context->id); 07269 } 07270 foreach ($teacherroles as $teacherrole) { 07271 assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $teacherrole->id, $context->id); 07272 } 07273 break; 07274 case 2: 07275 foreach ($studentroles as $studentrole) { 07276 assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $studentrole->id, $context->id); 07277 } 07278 foreach ($teacherroles as $teacherrole) { 07279 assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $teacherrole->id, $context->id); 07280 } 07281 break; 07282 } 07283 } 07284 return true; 07285 } 07286 07292 function forum_get_layout_modes() { 07293 return array (FORUM_MODE_FLATOLDEST => get_string('modeflatoldestfirst', 'forum'), 07294 FORUM_MODE_FLATNEWEST => get_string('modeflatnewestfirst', 'forum'), 07295 FORUM_MODE_THREADED => get_string('modethreaded', 'forum'), 07296 FORUM_MODE_NESTED => get_string('modenested', 'forum')); 07297 } 07298 07304 function forum_get_forum_types() { 07305 return array ('general' => get_string('generalforum', 'forum'), 07306 'eachuser' => get_string('eachuserforum', 'forum'), 07307 'single' => get_string('singleforum', 'forum'), 07308 'qanda' => get_string('qandaforum', 'forum'), 07309 'blog' => get_string('blogforum', 'forum')); 07310 } 07311 07317 function forum_get_forum_types_all() { 07318 return array ('news' => get_string('namenews','forum'), 07319 'social' => get_string('namesocial','forum'), 07320 'general' => get_string('generalforum', 'forum'), 07321 'eachuser' => get_string('eachuserforum', 'forum'), 07322 'single' => get_string('singleforum', 'forum'), 07323 'qanda' => get_string('qandaforum', 'forum'), 07324 'blog' => get_string('blogforum', 'forum')); 07325 } 07326 07332 function forum_get_open_modes() { 07333 return array ('2' => get_string('openmode2', 'forum'), 07334 '1' => get_string('openmode1', 'forum'), 07335 '0' => get_string('openmode0', 'forum') ); 07336 } 07337 07343 function forum_get_extra_capabilities() { 07344 return array('moodle/site:accessallgroups', 'moodle/site:viewfullnames', 'moodle/site:trustcontent', 'moodle/rating:view', 'moodle/rating:viewany', 'moodle/rating:viewall', 'moodle/rating:rate'); 07345 } 07346 07347 07357 /************************************************* 07358 function forum_extend_navigation($navref, $course, $module, $cm) { 07359 global $CFG, $OUTPUT, $USER; 07360 07361 $limit = 5; 07362 07363 $discussions = forum_get_discussions($cm,"d.timemodified DESC", false, -1, $limit); 07364 $discussioncount = forum_get_discussions_count($cm); 07365 if (!is_array($discussions) || count($discussions)==0) { 07366 return; 07367 } 07368 $discussionnode = $navref->add(get_string('discussions', 'forum').' ('.$discussioncount.')'); 07369 $discussionnode->mainnavonly = true; 07370 $discussionnode->display = false; // Do not display on navigation (only on navbar) 07371 07372 foreach ($discussions as $discussion) { 07373 $icon = new pix_icon('i/feedback', ''); 07374 $url = new moodle_url('/mod/forum/discuss.php', array('d'=>$discussion->discussion)); 07375 $discussionnode->add($discussion->subject, $url, navigation_node::TYPE_SETTING, null, null, $icon); 07376 } 07377 07378 if ($discussioncount > count($discussions)) { 07379 if (!empty($navref->action)) { 07380 $url = $navref->action; 07381 } else { 07382 $url = new moodle_url('/mod/forum/view.php', array('id'=>$cm->id)); 07383 } 07384 $discussionnode->add(get_string('viewalldiscussions', 'forum'), $url, navigation_node::TYPE_SETTING, null, null, $icon); 07385 } 07386 07387 $index = 0; 07388 $recentposts = array(); 07389 $lastlogin = time() - COURSE_MAX_RECENT_PERIOD; 07390 if (!isguestuser() and !empty($USER->lastcourseaccess[$course->id])) { 07391 if ($USER->lastcourseaccess[$course->id] > $lastlogin) { 07392 $lastlogin = $USER->lastcourseaccess[$course->id]; 07393 } 07394 } 07395 forum_get_recent_mod_activity($recentposts, $index, $lastlogin, $course->id, $cm->id); 07396 07397 if (is_array($recentposts) && count($recentposts)>0) { 07398 $recentnode = $navref->add(get_string('recentactivity').' ('.count($recentposts).')'); 07399 $recentnode->mainnavonly = true; 07400 $recentnode->display = false; 07401 foreach ($recentposts as $post) { 07402 $icon = new pix_icon('i/feedback', ''); 07403 $url = new moodle_url('/mod/forum/discuss.php', array('d'=>$post->content->discussion)); 07404 $title = $post->content->subject."\n".userdate($post->timestamp, get_string('strftimerecent', 'langconfig'))."\n".$post->user->firstname.' '.$post->user->lastname; 07405 $recentnode->add($title, $url, navigation_node::TYPE_SETTING, null, null, $icon); 07406 } 07407 } 07408 } 07409 *************************/ 07410 07417 function forum_extend_settings_navigation(settings_navigation $settingsnav, navigation_node $forumnode) { 07418 global $USER, $PAGE, $CFG, $DB, $OUTPUT; 07419 07420 $forumobject = $DB->get_record("forum", array("id" => $PAGE->cm->instance)); 07421 if (empty($PAGE->cm->context)) { 07422 $PAGE->cm->context = get_context_instance(CONTEXT_MODULE, $PAGE->cm->instance); 07423 } 07424 07425 // for some actions you need to be enrolled, beiing admin is not enough sometimes here 07426 $enrolled = is_enrolled($PAGE->cm->context, $USER, '', false); 07427 $activeenrolled = is_enrolled($PAGE->cm->context, $USER, '', true); 07428 07429 $canmanage = has_capability('mod/forum:managesubscriptions', $PAGE->cm->context); 07430 $subscriptionmode = forum_get_forcesubscribed($forumobject); 07431 $cansubscribe = ($activeenrolled && $subscriptionmode != FORUM_FORCESUBSCRIBE && ($subscriptionmode != FORUM_DISALLOWSUBSCRIBE || $canmanage)); 07432 07433 if ($canmanage) { 07434 $mode = $forumnode->add(get_string('subscriptionmode', 'forum'), null, navigation_node::TYPE_CONTAINER); 07435 07436 $allowchoice = $mode->add(get_string('subscriptionoptional', 'forum'), new moodle_url('/mod/forum/subscribe.php', array('id'=>$forumobject->id, 'mode'=>FORUM_CHOOSESUBSCRIBE, 'sesskey'=>sesskey())), navigation_node::TYPE_SETTING); 07437 $forceforever = $mode->add(get_string("subscriptionforced", "forum"), new moodle_url('/mod/forum/subscribe.php', array('id'=>$forumobject->id, 'mode'=>FORUM_FORCESUBSCRIBE, 'sesskey'=>sesskey())), navigation_node::TYPE_SETTING); 07438 $forceinitially = $mode->add(get_string("subscriptionauto", "forum"), new moodle_url('/mod/forum/subscribe.php', array('id'=>$forumobject->id, 'mode'=>FORUM_INITIALSUBSCRIBE, 'sesskey'=>sesskey())), navigation_node::TYPE_SETTING); 07439 $disallowchoice = $mode->add(get_string('subscriptiondisabled', 'forum'), new moodle_url('/mod/forum/subscribe.php', array('id'=>$forumobject->id, 'mode'=>FORUM_DISALLOWSUBSCRIBE, 'sesskey'=>sesskey())), navigation_node::TYPE_SETTING); 07440 07441 switch ($subscriptionmode) { 07442 case FORUM_CHOOSESUBSCRIBE : // 0 07443 $allowchoice->action = null; 07444 $allowchoice->add_class('activesetting'); 07445 break; 07446 case FORUM_FORCESUBSCRIBE : // 1 07447 $forceforever->action = null; 07448 $forceforever->add_class('activesetting'); 07449 break; 07450 case FORUM_INITIALSUBSCRIBE : // 2 07451 $forceinitially->action = null; 07452 $forceinitially->add_class('activesetting'); 07453 break; 07454 case FORUM_DISALLOWSUBSCRIBE : // 3 07455 $disallowchoice->action = null; 07456 $disallowchoice->add_class('activesetting'); 07457 break; 07458 } 07459 07460 } else if ($activeenrolled) { 07461 07462 switch ($subscriptionmode) { 07463 case FORUM_CHOOSESUBSCRIBE : // 0 07464 $notenode = $forumnode->add(get_string('subscriptionoptional', 'forum')); 07465 break; 07466 case FORUM_FORCESUBSCRIBE : // 1 07467 $notenode = $forumnode->add(get_string('subscriptionforced', 'forum')); 07468 break; 07469 case FORUM_INITIALSUBSCRIBE : // 2 07470 $notenode = $forumnode->add(get_string('subscriptionauto', 'forum')); 07471 break; 07472 case FORUM_DISALLOWSUBSCRIBE : // 3 07473 $notenode = $forumnode->add(get_string('subscriptiondisabled', 'forum')); 07474 break; 07475 } 07476 } 07477 07478 if ($cansubscribe) { 07479 if (forum_is_subscribed($USER->id, $forumobject)) { 07480 $linktext = get_string('unsubscribe', 'forum'); 07481 } else { 07482 $linktext = get_string('subscribe', 'forum'); 07483 } 07484 $url = new moodle_url('/mod/forum/subscribe.php', array('id'=>$forumobject->id, 'sesskey'=>sesskey())); 07485 $forumnode->add($linktext, $url, navigation_node::TYPE_SETTING); 07486 } 07487 07488 if (has_capability('mod/forum:viewsubscribers', $PAGE->cm->context)){ 07489 $url = new moodle_url('/mod/forum/subscribers.php', array('id'=>$forumobject->id)); 07490 $forumnode->add(get_string('showsubscribers', 'forum'), $url, navigation_node::TYPE_SETTING); 07491 } 07492 07493 if ($enrolled && forum_tp_can_track_forums($forumobject)) { // keep tracking info for users with suspended enrolments 07494 if ($forumobject->trackingtype != FORUM_TRACKING_OPTIONAL) { 07495 //tracking forced on or off in forum settings so dont provide a link here to change it 07496 //could add unclickable text like for forced subscription but not sure this justifies adding another menu item 07497 } else { 07498 if (forum_tp_is_tracked($forumobject)) { 07499 $linktext = get_string('notrackforum', 'forum'); 07500 } else { 07501 $linktext = get_string('trackforum', 'forum'); 07502 } 07503 $url = new moodle_url('/mod/forum/settracking.php', array('id'=>$forumobject->id)); 07504 $forumnode->add($linktext, $url, navigation_node::TYPE_SETTING); 07505 } 07506 } 07507 07508 if (!isloggedin() && $PAGE->course->id == SITEID) { 07509 $userid = guest_user()->id; 07510 } else { 07511 $userid = $USER->id; 07512 } 07513 07514 $hascourseaccess = ($PAGE->course->id == SITEID) || can_access_course($PAGE->course, $userid); 07515 $enablerssfeeds = !empty($CFG->enablerssfeeds) && !empty($CFG->forum_enablerssfeeds); 07516 07517 if ($enablerssfeeds && $forumobject->rsstype && $forumobject->rssarticles && $hascourseaccess) { 07518 07519 if (!function_exists('rss_get_url')) { 07520 require_once("$CFG->libdir/rsslib.php"); 07521 } 07522 07523 if ($forumobject->rsstype == 1) { 07524 $string = get_string('rsssubscriberssdiscussions','forum'); 07525 } else { 07526 $string = get_string('rsssubscriberssposts','forum'); 07527 } 07528 07529 $url = new moodle_url(rss_get_url($PAGE->cm->context->id, $userid, "mod_forum", $forumobject->id)); 07530 $forumnode->add($string, $url, settings_navigation::TYPE_SETTING, null, null, new pix_icon('i/rss', '')); 07531 } 07532 } 07533 07540 abstract class forum_subscriber_selector_base extends user_selector_base { 07541 07546 protected $forumid = null; 07551 protected $context = null; 07556 protected $currentgroup = null; 07557 07563 public function __construct($name, $options) { 07564 $options['accesscontext'] = $options['context']; 07565 parent::__construct($name, $options); 07566 if (isset($options['context'])) { 07567 $this->context = $options['context']; 07568 } 07569 if (isset($options['currentgroup'])) { 07570 $this->currentgroup = $options['currentgroup']; 07571 } 07572 if (isset($options['forumid'])) { 07573 $this->forumid = $options['forumid']; 07574 } 07575 } 07576 07582 protected function get_options() { 07583 global $CFG; 07584 $options = parent::get_options(); 07585 $options['file'] = substr(__FILE__, strlen($CFG->dirroot.'/')); 07586 $options['context'] = $this->context; 07587 $options['currentgroup'] = $this->currentgroup; 07588 $options['forumid'] = $this->forumid; 07589 return $options; 07590 } 07591 07592 } 07593 07600 class forum_potential_subscriber_selector extends forum_subscriber_selector_base { 07601 07606 protected $forcesubscribed = false; 07611 protected $existingsubscribers = array(); 07612 07618 public function __construct($name, $options) { 07619 parent::__construct($name, $options); 07620 if (isset($options['forcesubscribed'])) { 07621 $this->forcesubscribed=true; 07622 } 07623 } 07624 07629 protected function get_options() { 07630 $options = parent::get_options(); 07631 if ($this->forcesubscribed===true) { 07632 $options['forcesubscribed']=1; 07633 } 07634 return $options; 07635 } 07636 07646 public function find_users($search) { 07647 global $DB; 07648 07649 $availableusers = forum_get_potential_subscribers($this->context, $this->currentgroup, $this->required_fields_sql('u'), 'u.firstname ASC, u.lastname ASC'); 07650 07651 if (empty($availableusers)) { 07652 $availableusers = array(); 07653 } else if ($search) { 07654 $search = strtolower($search); 07655 foreach ($availableusers as $key=>$user) { 07656 if (stripos($user->firstname, $search) === false && stripos($user->lastname, $search) === false) { 07657 unset($availableusers[$key]); 07658 } 07659 } 07660 } 07661 07662 // Unset any existing subscribers 07663 if (count($this->existingsubscribers)>0 && !$this->forcesubscribed) { 07664 foreach ($this->existingsubscribers as $group) { 07665 foreach ($group as $user) { 07666 if (array_key_exists($user->id, $availableusers)) { 07667 unset($availableusers[$user->id]); 07668 } 07669 } 07670 } 07671 } 07672 07673 if ($this->forcesubscribed) { 07674 return array(get_string("existingsubscribers", 'forum') => $availableusers); 07675 } else { 07676 return array(get_string("potentialsubscribers", 'forum') => $availableusers); 07677 } 07678 } 07679 07684 public function set_existing_subscribers(array $users) { 07685 $this->existingsubscribers = $users; 07686 } 07687 07691 public function set_force_subscribed($setting=true) { 07692 $this->forcesubscribed = true; 07693 } 07694 } 07695 07702 class forum_existing_subscriber_selector extends forum_subscriber_selector_base { 07703 07710 public function find_users($search) { 07711 global $DB; 07712 list($wherecondition, $params) = $this->search_sql($search, 'u'); 07713 $params['forumid'] = $this->forumid; 07714 07715 // only active enrolled or everybody on the frontpage 07716 list($esql, $eparams) = get_enrolled_sql($this->context, '', $this->currentgroup, true); 07717 $params = array_merge($params, $eparams); 07718 07719 $fields = $this->required_fields_sql('u'); 07720 07721 $subscribers = $DB->get_records_sql("SELECT $fields 07722 FROM {user} u 07723 JOIN ($esql) je ON je.id = u.id 07724 JOIN {forum_subscriptions} s ON s.userid = u.id 07725 WHERE $wherecondition AND s.forum = :forumid 07726 ORDER BY u.lastname ASC, u.firstname ASC", $params); 07727 07728 return array(get_string("existingsubscribers", 'forum') => $subscribers); 07729 } 07730 07731 } 07732 07738 function forum_cm_info_view(cm_info $cm) { 07739 global $CFG; 07740 07741 // Get tracking status (once per request) 07742 static $initialised; 07743 static $usetracking, $strunreadpostsone; 07744 if (!isset($initialised)) { 07745 if ($usetracking = forum_tp_can_track_forums()) { 07746 $strunreadpostsone = get_string('unreadpostsone', 'forum'); 07747 } 07748 $initialised = true; 07749 } 07750 07751 if ($usetracking) { 07752 if ($unread = forum_tp_count_forum_unread_posts($cm, $cm->get_course())) { 07753 $out = '<span class="unread"> <a href="' . $cm->get_url() . '">'; 07754 if ($unread == 1) { 07755 $out .= $strunreadpostsone; 07756 } else { 07757 $out .= get_string('unreadpostsnumber', 'forum', $unread); 07758 } 07759 $out .= '</a></span>'; 07760 $cm->set_after_link($out); 07761 } 07762 } 07763 } 07764 07771 function forum_page_type_list($pagetype, $parentcontext, $currentcontext) { 07772 $forum_pagetype = array( 07773 'mod-forum-*'=>get_string('page-mod-forum-x', 'forum'), 07774 'mod-forum-view'=>get_string('page-mod-forum-view', 'forum'), 07775 'mod-forum-discuss'=>get_string('page-mod-forum-discuss', 'forum') 07776 ); 07777 return $forum_pagetype; 07778 } 07779 07791 function forum_get_courses_user_posted_in($user, $discussionsonly = false, $includecontexts = true, $limitfrom = null, $limitnum = null) { 07792 global $DB; 07793 07794 // If we are only after discussions we need only look at the forum_discussions 07795 // table and join to the userid there. If we are looking for posts then we need 07796 // to join to the forum_posts table. 07797 if (!$discussionsonly) { 07798 $joinsql = 'JOIN {forum_discussions} fd ON fd.course = c.id 07799 JOIN {forum_posts} fp ON fp.discussion = fd.id'; 07800 $wheresql = 'fp.userid = :userid'; 07801 $params = array('userid' => $user->id); 07802 } else { 07803 $joinsql = 'JOIN {forum_discussions} fd ON fd.course = c.id'; 07804 $wheresql = 'fd.userid = :userid'; 07805 $params = array('userid' => $user->id); 07806 } 07807 07808 // Join to the context table so that we can preload contexts if required. 07809 if ($includecontexts) { 07810 list($ctxselect, $ctxjoin) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx'); 07811 } else { 07812 $ctxselect = ''; 07813 $ctxjoin = ''; 07814 } 07815 07816 // Now we need to get all of the courses to search. 07817 // All courses where the user has posted within a forum will be returned. 07818 $sql = "SELECT DISTINCT c.* $ctxselect 07819 FROM {course} c 07820 $joinsql 07821 $ctxjoin 07822 WHERE $wheresql"; 07823 $courses = $DB->get_records_sql($sql, $params, $limitfrom, $limitnum); 07824 if ($includecontexts) { 07825 array_map('context_instance_preload', $courses); 07826 } 07827 return $courses; 07828 } 07829 07843 function forum_get_forums_user_posted_in($user, array $courseids = null, $discussionsonly = false, $limitfrom = null, $limitnum = null) { 07844 global $DB; 07845 07846 $where = array("m.name = 'forum'"); 07847 $params = array(); 07848 if (!is_null($courseids)) { 07849 list($coursewhere, $params) = $DB->get_in_or_equal($courseids, SQL_PARAMS_NAMED, 'courseid'); 07850 $where[] = 'f.course '.$coursewhere; 07851 } 07852 if (!$discussionsonly) { 07853 $joinsql = 'JOIN {forum_discussions} fd ON fd.forum = f.id 07854 JOIN {forum_posts} fp ON fp.discussion = fd.id'; 07855 $where[] = 'fp.userid = :userid'; 07856 } else { 07857 $joinsql = 'JOIN {forum_discussions} fd ON fd.forum = f.id'; 07858 $where[] = 'fd.userid = :userid'; 07859 } 07860 $params['userid'] = $user->id; 07861 $wheresql = join(' AND ', $where); 07862 07863 $sql = "SELECT DISTINCT f.*, cm.id AS cmid 07864 FROM {forum} f 07865 JOIN {course_modules} cm ON cm.instance = f.id 07866 JOIN {modules} m ON m.id = cm.module 07867 $joinsql 07868 WHERE $wheresql"; 07869 $courseforums = $DB->get_records_sql($sql, $params, $limitfrom, $limitnum); 07870 return $courseforums; 07871 } 07872 07901 function forum_get_posts_by_user($user, array $courses, $musthaveaccess = false, $discussionsonly = false, $limitfrom = 0, $limitnum = 50) { 07902 global $DB, $USER, $CFG; 07903 07904 $return = new stdClass; 07905 $return->totalcount = 0; // The total number of posts that the current user is able to view 07906 $return->courses = array(); // The courses the current user can access 07907 $return->forums = array(); // The forums that the current user can access that contain posts 07908 $return->posts = array(); // The posts to display 07909 07910 // First up a small sanity check. If there are no courses to check we can 07911 // return immediately, there is obviously nothing to search. 07912 if (empty($courses)) { 07913 return $return; 07914 } 07915 07916 // A couple of quick setups 07917 $isloggedin = isloggedin(); 07918 $isguestuser = $isloggedin && isguestuser(); 07919 $iscurrentuser = $isloggedin && $USER->id == $user->id; 07920 07921 // Checkout whether or not the current user has capabilities over the requested 07922 // user and if so they have the capabilities required to view the requested 07923 // users content. 07924 $usercontext = get_context_instance(CONTEXT_USER, $user->id, MUST_EXIST); 07925 $hascapsonuser = !$iscurrentuser && $DB->record_exists('role_assignments', array('userid' => $USER->id, 'contextid' => $usercontext->id)); 07926 $hascapsonuser = $hascapsonuser && has_all_capabilities(array('moodle/user:viewdetails', 'moodle/user:readuserposts'), $usercontext); 07927 07928 // Before we actually search each course we need to check the user's access to the 07929 // course. If the user doesn't have the appropraite access then we either throw an 07930 // error if a particular course was requested or we just skip over the course. 07931 foreach ($courses as $course) { 07932 $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id, MUST_EXIST); 07933 if ($iscurrentuser || $hascapsonuser) { 07934 // If it is the current user, or the current user has capabilities to the 07935 // requested user then all we need to do is check the requested users 07936 // current access to the course. 07937 // Note: There is no need to check group access or anything of the like 07938 // as either the current user is the requested user, or has granted 07939 // capabilities on the requested user. Either way they can see what the 07940 // requested user posted, although its VERY unlikely in the `parent` situation 07941 // that the current user will be able to view the posts in context. 07942 if (!is_viewing($coursecontext, $user) && !is_enrolled($coursecontext, $user)) { 07943 // Need to have full access to a course to see the rest of own info 07944 if ($musthaveaccess) { 07945 print_error('errorenrolmentrequired', 'forum'); 07946 } 07947 continue; 07948 } 07949 } else { 07950 // Check whether the current user is enrolled or has access to view the course 07951 // if they don't we immediately have a problem. 07952 if (!can_access_course($course)) { 07953 if ($musthaveaccess) { 07954 print_error('errorenrolmentrequired', 'forum'); 07955 } 07956 continue; 07957 } 07958 07959 // Check whether the requested user is enrolled or has access to view the course 07960 // if they don't we immediately have a problem. 07961 if (!can_access_course($course, $user)) { 07962 if ($musthaveaccess) { 07963 print_error('notenrolled', 'forum'); 07964 } 07965 continue; 07966 } 07967 07968 // If groups are in use and enforced throughout the course then make sure 07969 // we can meet in at least one course level group. 07970 // Note that we check if either the current user or the requested user have 07971 // the capability to access all groups. This is because with that capability 07972 // a user in group A could post in the group B forum. Grrrr. 07973 if (groups_get_course_groupmode($course) == SEPARATEGROUPS && $course->groupmodeforce 07974 && !has_capability('moodle/site:accessallgroups', $coursecontext) && !has_capability('moodle/site:accessallgroups', $coursecontext, $user->id)) { 07975 // If its the guest user to bad... the guest user cannot access groups 07976 if (!$isloggedin or $isguestuser) { 07977 // do not use require_login() here because we might have already used require_login($course) 07978 if ($musthaveaccess) { 07979 redirect(get_login_url()); 07980 } 07981 continue; 07982 } 07983 // Get the groups of the current user 07984 $mygroups = array_keys(groups_get_all_groups($course->id, $USER->id, $course->defaultgroupingid, 'g.id, g.name')); 07985 // Get the groups the requested user is a member of 07986 $usergroups = array_keys(groups_get_all_groups($course->id, $user->id, $course->defaultgroupingid, 'g.id, g.name')); 07987 // Check whether they are members of the same group. If they are great. 07988 $intersect = array_intersect($mygroups, $usergroups); 07989 if (empty($intersect)) { 07990 // But they're not... if it was a specific course throw an error otherwise 07991 // just skip this course so that it is not searched. 07992 if ($musthaveaccess) { 07993 print_error("groupnotamember", '', $CFG->wwwroot."/course/view.php?id=$course->id"); 07994 } 07995 continue; 07996 } 07997 } 07998 } 07999 // Woo hoo we got this far which means the current user can search this 08000 // this course for the requested user. Although this is only the course accessibility 08001 // handling that is complete, the forum accessibility tests are yet to come. 08002 $return->courses[$course->id] = $course; 08003 } 08004 // No longer beed $courses array - lose it not it may be big 08005 unset($courses); 08006 08007 // Make sure that we have some courses to search 08008 if (empty($return->courses)) { 08009 // If we don't have any courses to search then the reality is that the current 08010 // user doesn't have access to any courses is which the requested user has posted. 08011 // Although we do know at this point that the requested user has posts. 08012 if ($musthaveaccess) { 08013 print_error('permissiondenied'); 08014 } else { 08015 return $return; 08016 } 08017 } 08018 08019 // Next step: Collect all of the forums that we will want to search. 08020 // It is important to note that this step isn't actually about searching, it is 08021 // about determining which forums we can search by testing accessibility. 08022 $forums = forum_get_forums_user_posted_in($user, array_keys($return->courses), $discussionsonly); 08023 08024 // Will be used to build the where conditions for the search 08025 $forumsearchwhere = array(); 08026 // Will be used to store the where condition params for the search 08027 $forumsearchparams = array(); 08028 // Will record forums where the user can freely access everything 08029 $forumsearchfullaccess = array(); 08030 // DB caching friendly 08031 $now = round(time(), -2); 08032 // For each course to search we want to find the forums the user has posted in 08033 // and providing the current user can access the forum create a search condition 08034 // for the forum to get the requested users posts. 08035 foreach ($return->courses as $course) { 08036 // Now we need to get the forums 08037 $modinfo = get_fast_modinfo($course); 08038 if (empty($modinfo->instances['forum'])) { 08039 // hmmm, no forums? well at least its easy... skip! 08040 continue; 08041 } 08042 // Iterate 08043 foreach ($modinfo->get_instances_of('forum') as $forumid => $cm) { 08044 if (!$cm->uservisible or !isset($forums[$forumid])) { 08045 continue; 08046 } 08047 // Get the forum in question 08048 $forum = $forums[$forumid]; 08049 // This is needed for functionality later on in the forum code.... 08050 $forum->cm = $cm; 08051 08052 // Check that either the current user can view the forum, or that the 08053 // current user has capabilities over the requested user and the requested 08054 // user can view the discussion 08055 if (!has_capability('mod/forum:viewdiscussion', $cm->context) && !($hascapsonuser && has_capability('mod/forum:viewdiscussion', $cm->context, $user->id))) { 08056 continue; 08057 } 08058 08059 // This will contain forum specific where clauses 08060 $forumsearchselect = array(); 08061 if (!$iscurrentuser && !$hascapsonuser) { 08062 // Make sure we check group access 08063 if (groups_get_activity_groupmode($cm, $course) == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $cm->context)) { 08064 $groups = $modinfo->get_groups($cm->groupingid); 08065 $groups[] = -1; 08066 list($groupid_sql, $groupid_params) = $DB->get_in_or_equal($groups, SQL_PARAMS_NAMED, 'grps'.$forumid.'_'); 08067 $forumsearchparams = array_merge($forumsearchparams, $groupid_params); 08068 $forumsearchselect[] = "d.groupid $groupid_sql"; 08069 } 08070 08071 // hidden timed discussions 08072 if (!empty($CFG->forum_enabletimedposts) && !has_capability('mod/forum:viewhiddentimedposts', $cm->context)) { 08073 $forumsearchselect[] = "(d.userid = :userid{$forumid} OR (d.timestart < :timestart{$forumid} AND (d.timeend = 0 OR d.timeend > :timeend{$forumid})))"; 08074 $forumsearchparams['userid'.$forumid] = $user->id; 08075 $forumsearchparams['timestart'.$forumid] = $now; 08076 $forumsearchparams['timeend'.$forumid] = $now; 08077 } 08078 08079 // qanda access 08080 if ($forum->type == 'qanda' && !has_capability('mod/forum:viewqandawithoutposting', $cm->context)) { 08081 // We need to check whether the user has posted in the qanda forum. 08082 $discussionspostedin = forum_discussions_user_has_posted_in($forum->id, $user->id); 08083 if (!empty($discussionspostedin)) { 08084 $forumonlydiscussions = array(); // Holds discussion ids for the discussions the user is allowed to see in this forum. 08085 foreach ($discussionspostedin as $d) { 08086 $forumonlydiscussions[] = $d->id; 08087 } 08088 list($discussionid_sql, $discussionid_params) = $DB->get_in_or_equal($forumonlydiscussions, SQL_PARAMS_NAMED, 'qanda'.$forumid.'_'); 08089 $forumsearchparams = array_merge($forumsearchparams, $discussionid_params); 08090 $forumsearchselect[] = "(d.id $discussionid_sql OR p.parent = 0)"; 08091 } else { 08092 $forumsearchselect[] = "p.parent = 0"; 08093 } 08094 08095 } 08096 08097 if (count($forumsearchselect) > 0) { 08098 $forumsearchwhere[] = "(d.forum = :forum{$forumid} AND ".implode(" AND ", $forumsearchselect).")"; 08099 $forumsearchparams['forum'.$forumid] = $forumid; 08100 } else { 08101 $forumsearchfullaccess[] = $forumid; 08102 } 08103 } else { 08104 // The current user/parent can see all of their own posts 08105 $forumsearchfullaccess[] = $forumid; 08106 } 08107 } 08108 } 08109 08110 // If we dont have any search conditions, and we don't have any forums where 08111 // the user has full access then we just return the default. 08112 if (empty($forumsearchwhere) && empty($forumsearchfullaccess)) { 08113 return $return; 08114 } 08115 08116 // Prepare a where condition for the full access forums. 08117 if (count($forumsearchfullaccess) > 0) { 08118 list($fullidsql, $fullidparams) = $DB->get_in_or_equal($forumsearchfullaccess, SQL_PARAMS_NAMED, 'fula'); 08119 $forumsearchparams = array_merge($forumsearchparams, $fullidparams); 08120 $forumsearchwhere[] = "(d.forum $fullidsql)"; 08121 } 08122 08123 // Prepare SQL to both count and search 08124 $userfields = user_picture::fields('u', null, 'userid'); 08125 $countsql = 'SELECT COUNT(*) '; 08126 $selectsql = 'SELECT p.*, d.forum, d.name AS discussionname, '.$userfields.' '; 08127 $wheresql = implode(" OR ", $forumsearchwhere); 08128 08129 if ($discussionsonly) { 08130 if ($wheresql == '') { 08131 $wheresql = 'p.parent = 0'; 08132 } else { 08133 $wheresql = 'p.parent = 0 AND ('.$wheresql.')'; 08134 } 08135 } 08136 08137 $sql = "FROM {forum_posts} p 08138 JOIN {forum_discussions} d ON d.id = p.discussion 08139 JOIN {user} u ON u.id = p.userid 08140 WHERE ($wheresql) 08141 AND p.userid = :userid "; 08142 $orderby = "ORDER BY p.modified DESC"; 08143 $forumsearchparams['userid'] = $user->id; 08144 08145 // Set the total number posts made by the requested user that the current user can see 08146 $return->totalcount = $DB->count_records_sql($countsql.$sql, $forumsearchparams); 08147 // Set the collection of posts that has been requested 08148 $return->posts = $DB->get_records_sql($selectsql.$sql.$orderby, $forumsearchparams, $limitfrom, $limitnum); 08149 08150 // We need to build an array of forums for which posts will be displayed. 08151 // We do this here to save the caller needing to retrieve them themselves before 08152 // printing these forums posts. Given we have the forums already there is 08153 // practically no overhead here. 08154 foreach ($return->posts as $post) { 08155 if (!array_key_exists($post->forum, $return->forums)) { 08156 $return->forums[$post->forum] = $forums[$post->forum]; 08157 } 08158 } 08159 08160 return $return; 08161 }