PageRenderTime 102ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 2ms

/mod/forum/lib.php

https://bitbucket.org/ceu/moodle_demo
PHP | 6976 lines | 5155 code | 992 blank | 829 comment | 1034 complexity | c8d63c1982e7b59eb17a68b8404fb675 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.0, LGPL-2.1
  1. <?php // $Id$
  2. require_once($CFG->libdir.'/filelib.php');
  3. /// CONSTANTS ///////////////////////////////////////////////////////////
  4. define('FORUM_MODE_FLATOLDEST', 1);
  5. define('FORUM_MODE_FLATNEWEST', -1);
  6. define('FORUM_MODE_THREADED', 2);
  7. define('FORUM_MODE_NESTED', 3);
  8. define('FORUM_FORCESUBSCRIBE', 1);
  9. define('FORUM_INITIALSUBSCRIBE', 2);
  10. define('FORUM_DISALLOWSUBSCRIBE',3);
  11. define('FORUM_TRACKING_OFF', 0);
  12. define('FORUM_TRACKING_OPTIONAL', 1);
  13. define('FORUM_TRACKING_ON', 2);
  14. define('FORUM_UNSET_POST_RATING', -999);
  15. define ('FORUM_AGGREGATE_NONE', 0); //no ratings
  16. define ('FORUM_AGGREGATE_AVG', 1);
  17. define ('FORUM_AGGREGATE_COUNT', 2);
  18. define ('FORUM_AGGREGATE_MAX', 3);
  19. define ('FORUM_AGGREGATE_MIN', 4);
  20. define ('FORUM_AGGREGATE_SUM', 5);
  21. /// STANDARD FUNCTIONS ///////////////////////////////////////////////////////////
  22. /**
  23. * Given an object containing all the necessary data,
  24. * (defined by the form in mod.html) this function
  25. * will create a new instance and return the id number
  26. * of the new instance.
  27. * @param object $forum add forum instance (with magic quotes)
  28. * @return int intance id
  29. */
  30. function forum_add_instance($forum) {
  31. global $CFG;
  32. $forum->timemodified = time();
  33. if (empty($forum->assessed)) {
  34. $forum->assessed = 0;
  35. }
  36. if (empty($forum->ratingtime) or empty($forum->assessed)) {
  37. $forum->assesstimestart = 0;
  38. $forum->assesstimefinish = 0;
  39. }
  40. if (!$forum->id = insert_record('forum', $forum)) {
  41. return false;
  42. }
  43. if ($forum->type == 'single') { // Create related discussion.
  44. $discussion = new object();
  45. $discussion->course = $forum->course;
  46. $discussion->forum = $forum->id;
  47. $discussion->name = $forum->name;
  48. $discussion->intro = $forum->intro;
  49. $discussion->assessed = $forum->assessed;
  50. $discussion->format = $forum->type;
  51. $discussion->mailnow = false;
  52. $discussion->groupid = -1;
  53. if (! forum_add_discussion($discussion, $discussion->intro)) {
  54. error('Could not add the discussion for this forum');
  55. }
  56. }
  57. if ($forum->forcesubscribe == FORUM_INITIALSUBSCRIBE) {
  58. /// all users should be subscribed initially
  59. /// Note: forum_get_potential_subscribers should take the forum context,
  60. /// but that does not exist yet, becuase the forum is only half build at this
  61. /// stage. However, because the forum is brand new, we know that there are
  62. /// no role assignments or overrides in the forum context, so using the
  63. /// course context gives the same list of users.
  64. $users = forum_get_potential_subscribers(get_context_instance(CONTEXT_COURSE, $forum->course), 0, 'u.id, u.email', '');
  65. foreach ($users as $user) {
  66. forum_subscribe($user->id, $forum->id);
  67. }
  68. }
  69. $forum = stripslashes_recursive($forum);
  70. forum_grade_item_update($forum);
  71. return $forum->id;
  72. }
  73. /**
  74. * Given an object containing all the necessary data,
  75. * (defined by the form in mod.html) this function
  76. * will update an existing instance with new data.
  77. * @param object $forum forum instance (with magic quotes)
  78. * @return bool success
  79. */
  80. function forum_update_instance($forum) {
  81. global $USER;
  82. $forum->timemodified = time();
  83. $forum->id = $forum->instance;
  84. if (empty($forum->assessed)) {
  85. $forum->assessed = 0;
  86. }
  87. if (empty($forum->ratingtime) or empty($forum->assessed)) {
  88. $forum->assesstimestart = 0;
  89. $forum->assesstimefinish = 0;
  90. }
  91. $oldforum = get_record('forum', 'id', $forum->id);
  92. // MDL-3942 - if the aggregation type or scale (i.e. max grade) changes then recalculate the grades for the entire forum
  93. // if scale changes - do we need to recheck the ratings, if ratings higher than scale how do we want to respond?
  94. // 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
  95. if (($oldforum->assessed<>$forum->assessed) or ($oldforum->scale<>$forum->scale)) {
  96. forum_update_grades($forum); // recalculate grades for the forum
  97. }
  98. if ($forum->type == 'single') { // Update related discussion and post.
  99. if (! $discussion = get_record('forum_discussions', 'forum', $forum->id)) {
  100. if ($discussions = get_records('forum_discussions', 'forum', $forum->id, 'timemodified ASC')) {
  101. notify('Warning! There is more than one discussion in this forum - using the most recent');
  102. $discussion = array_pop($discussions);
  103. } else {
  104. // try to recover by creating initial discussion - MDL-16262
  105. $discussion = new object();
  106. $discussion->course = $forum->course;
  107. $discussion->forum = $forum->id;
  108. $discussion->name = $forum->name;
  109. $discussion->intro = $forum->intro;
  110. $discussion->assessed = $forum->assessed;
  111. $discussion->format = $forum->type;
  112. $discussion->mailnow = false;
  113. $discussion->groupid = -1;
  114. forum_add_discussion($discussion, $discussion->intro);
  115. if (! $discussion = get_record('forum_discussions', 'forum', $forum->id)) {
  116. error('Could not add the discussion for this forum');
  117. }
  118. }
  119. }
  120. if (! $post = get_record('forum_posts', 'id', $discussion->firstpost)) {
  121. error('Could not find the first post in this forum discussion');
  122. }
  123. $post->subject = $forum->name;
  124. $post->message = $forum->intro;
  125. $post->modified = $forum->timemodified;
  126. $post->userid = $USER->id; // MDL-18599, so that current teacher can take ownership of activities
  127. if (! update_record('forum_posts', ($post))) {
  128. error('Could not update the first post');
  129. }
  130. $discussion->name = $forum->name;
  131. if (! update_record('forum_discussions', ($discussion))) {
  132. error('Could not update the discussion');
  133. }
  134. }
  135. if (!update_record('forum', $forum)) {
  136. error('Can not update forum');
  137. }
  138. $forum = stripslashes_recursive($forum);
  139. forum_grade_item_update($forum);
  140. return true;
  141. }
  142. /**
  143. * Given an ID of an instance of this module,
  144. * this function will permanently delete the instance
  145. * and any data that depends on it.
  146. * @param int forum instance id
  147. * @return bool success
  148. */
  149. function forum_delete_instance($id) {
  150. if (!$forum = get_record('forum', 'id', $id)) {
  151. return false;
  152. }
  153. $result = true;
  154. if ($discussions = get_records('forum_discussions', 'forum', $forum->id)) {
  155. foreach ($discussions as $discussion) {
  156. if (!forum_delete_discussion($discussion, true)) {
  157. $result = false;
  158. }
  159. }
  160. }
  161. if (!delete_records('forum_subscriptions', 'forum', $forum->id)) {
  162. $result = false;
  163. }
  164. forum_tp_delete_read_records(-1, -1, -1, $forum->id);
  165. if (!delete_records('forum', 'id', $forum->id)) {
  166. $result = false;
  167. }
  168. forum_grade_item_delete($forum);
  169. return $result;
  170. }
  171. /**
  172. * Function to be run periodically according to the moodle cron
  173. * Finds all posts that have yet to be mailed out, and mails them
  174. * out to all subscribers
  175. * @return void
  176. */
  177. function forum_cron() {
  178. global $CFG, $USER;
  179. $cronuser = clone($USER);
  180. $site = get_site();
  181. // all users that are subscribed to any post that needs sending
  182. $users = array();
  183. // status arrays
  184. $mailcount = array();
  185. $errorcount = array();
  186. // caches
  187. $discussions = array();
  188. $forums = array();
  189. $courses = array();
  190. $coursemodules = array();
  191. $subscribedusers = array();
  192. // Posts older than 2 days will not be mailed. This is to avoid the problem where
  193. // cron has not been running for a long time, and then suddenly people are flooded
  194. // with mail from the past few weeks or months
  195. $timenow = time();
  196. $endtime = $timenow - $CFG->maxeditingtime;
  197. $starttime = $endtime - 48 * 3600; // Two days earlier
  198. if ($posts = forum_get_unmailed_posts($starttime, $endtime, $timenow)) {
  199. // Mark them all now as being mailed. It's unlikely but possible there
  200. // might be an error later so that a post is NOT actually mailed out,
  201. // but since mail isn't crucial, we can accept this risk. Doing it now
  202. // prevents the risk of duplicated mails, which is a worse problem.
  203. if (!forum_mark_old_posts_as_mailed($endtime)) {
  204. mtrace('Errors occurred while trying to mark some posts as being mailed.');
  205. return false; // Don't continue trying to mail them, in case we are in a cron loop
  206. }
  207. // checking post validity, and adding users to loop through later
  208. foreach ($posts as $pid => $post) {
  209. $discussionid = $post->discussion;
  210. if (!isset($discussions[$discussionid])) {
  211. if ($discussion = get_record('forum_discussions', 'id', $post->discussion)) {
  212. $discussions[$discussionid] = $discussion;
  213. } else {
  214. mtrace('Could not find discussion '.$discussionid);
  215. unset($posts[$pid]);
  216. continue;
  217. }
  218. }
  219. $forumid = $discussions[$discussionid]->forum;
  220. if (!isset($forums[$forumid])) {
  221. if ($forum = get_record('forum', 'id', $forumid)) {
  222. $forums[$forumid] = $forum;
  223. } else {
  224. mtrace('Could not find forum '.$forumid);
  225. unset($posts[$pid]);
  226. continue;
  227. }
  228. }
  229. $courseid = $forums[$forumid]->course;
  230. if (!isset($courses[$courseid])) {
  231. if ($course = get_record('course', 'id', $courseid)) {
  232. $courses[$courseid] = $course;
  233. } else {
  234. mtrace('Could not find course '.$courseid);
  235. unset($posts[$pid]);
  236. continue;
  237. }
  238. }
  239. if (!isset($coursemodules[$forumid])) {
  240. if ($cm = get_coursemodule_from_instance('forum', $forumid, $courseid)) {
  241. $coursemodules[$forumid] = $cm;
  242. } else {
  243. mtrace('Could not course module for forum '.$forumid);
  244. unset($posts[$pid]);
  245. continue;
  246. }
  247. }
  248. // caching subscribed users of each forum
  249. if (!isset($subscribedusers[$forumid])) {
  250. $modcontext = get_context_instance(CONTEXT_MODULE, $coursemodules[$forumid]->id);
  251. if ($subusers = forum_subscribed_users($courses[$courseid], $forums[$forumid], 0, $modcontext)) {
  252. foreach ($subusers as $postuser) {
  253. // do not try to mail users with stopped email
  254. if ($postuser->emailstop) {
  255. if (!empty($CFG->forum_logblocked)) {
  256. add_to_log(SITEID, 'forum', 'mail blocked', '', '', 0, $postuser->id);
  257. }
  258. continue;
  259. }
  260. // this user is subscribed to this forum
  261. $subscribedusers[$forumid][$postuser->id] = $postuser->id;
  262. // this user is a user we have to process later
  263. $users[$postuser->id] = $postuser;
  264. }
  265. unset($subusers); // release memory
  266. }
  267. }
  268. $mailcount[$pid] = 0;
  269. $errorcount[$pid] = 0;
  270. }
  271. }
  272. if ($users && $posts) {
  273. $urlinfo = parse_url($CFG->wwwroot);
  274. $hostname = $urlinfo['host'];
  275. foreach ($users as $userto) {
  276. @set_time_limit(120); // terminate if processing of any account takes longer than 2 minutes
  277. // set this so that the capabilities are cached, and environment matches receiving user
  278. $USER = $userto;
  279. mtrace('Processing user '.$userto->id);
  280. // init caches
  281. $userto->viewfullnames = array();
  282. $userto->canpost = array();
  283. $userto->markposts = array();
  284. $userto->enrolledin = array();
  285. // reset the caches
  286. foreach ($coursemodules as $forumid=>$unused) {
  287. $coursemodules[$forumid]->cache = new object();
  288. $coursemodules[$forumid]->cache->caps = array();
  289. unset($coursemodules[$forumid]->uservisible);
  290. }
  291. foreach ($posts as $pid => $post) {
  292. // Set up the environment for the post, discussion, forum, course
  293. $discussion = $discussions[$post->discussion];
  294. $forum = $forums[$discussion->forum];
  295. $course = $courses[$forum->course];
  296. $cm =& $coursemodules[$forum->id];
  297. // Do some checks to see if we can bail out now
  298. if (!isset($subscribedusers[$forum->id][$userto->id])) {
  299. continue; // user does not subscribe to this forum
  300. }
  301. // Verify user is enrollend in course - if not do not send any email
  302. if (!isset($userto->enrolledin[$course->id])) {
  303. $userto->enrolledin[$course->id] = has_capability('moodle/course:view', get_context_instance(CONTEXT_COURSE, $course->id));
  304. }
  305. if (!$userto->enrolledin[$course->id]) {
  306. // oops - this user should not receive anything from this course
  307. continue;
  308. }
  309. // Don't send email if the forum is Q&A and the user has not posted
  310. if ($forum->type == 'qanda' && !forum_get_user_posted_time($discussion->id, $userto->id)) {
  311. mtrace('Did not email '.$userto->id.' because user has not posted in discussion');
  312. continue;
  313. }
  314. // Get info about the sending user
  315. if (array_key_exists($post->userid, $users)) { // we might know him/her already
  316. $userfrom = $users[$post->userid];
  317. } else if ($userfrom = get_record('user', 'id', $post->userid)) {
  318. $users[$userfrom->id] = $userfrom; // fetch only once, we can add it to user list, it will be skipped anyway
  319. } else {
  320. mtrace('Could not find user '.$post->userid);
  321. continue;
  322. }
  323. // setup global $COURSE properly - needed for roles and languages
  324. course_setup($course); // More environment
  325. // Fill caches
  326. if (!isset($userto->viewfullnames[$forum->id])) {
  327. $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
  328. $userto->viewfullnames[$forum->id] = has_capability('moodle/site:viewfullnames', $modcontext);
  329. }
  330. if (!isset($userto->canpost[$discussion->id])) {
  331. $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
  332. $userto->canpost[$discussion->id] = forum_user_can_post($forum, $discussion, $userto, $cm, $course, $modcontext);
  333. }
  334. if (!isset($userfrom->groups[$forum->id])) {
  335. if (!isset($userfrom->groups)) {
  336. $userfrom->groups = array();
  337. $users[$userfrom->id]->groups = array();
  338. }
  339. $userfrom->groups[$forum->id] = groups_get_all_groups($course->id, $userfrom->id, $cm->groupingid);
  340. $users[$userfrom->id]->groups[$forum->id] = $userfrom->groups[$forum->id];
  341. }
  342. // Make sure groups allow this user to see this email
  343. if ($discussion->groupid > 0 and $groupmode = groups_get_activity_groupmode($cm, $course)) { // Groups are being used
  344. if (!groups_group_exists($discussion->groupid)) { // Can't find group
  345. continue; // Be safe and don't send it to anyone
  346. }
  347. if (!groups_is_member($discussion->groupid) and !has_capability('moodle/site:accessallgroups', $modcontext)) {
  348. // do not send posts from other groups when in SEPARATEGROUPS or VISIBLEGROUPS
  349. continue;
  350. }
  351. }
  352. // Make sure we're allowed to see it...
  353. if (!forum_user_can_see_post($forum, $discussion, $post, NULL, $cm)) {
  354. mtrace('user '.$userto->id. ' can not see '.$post->id);
  355. continue;
  356. }
  357. // OK so we need to send the email.
  358. // Does the user want this post in a digest? If so postpone it for now.
  359. if ($userto->maildigest > 0) {
  360. // This user wants the mails to be in digest form
  361. $queue = new object();
  362. $queue->userid = $userto->id;
  363. $queue->discussionid = $discussion->id;
  364. $queue->postid = $post->id;
  365. $queue->timemodified = $post->created;
  366. if (!insert_record('forum_queue', $queue)) {
  367. mtrace("Error: mod/forum/cron.php: Could not queue for digest mail for id $post->id to user $userto->id ($userto->email) .. not trying again.");
  368. }
  369. continue;
  370. }
  371. // Prepare to actually send the post now, and build up the content
  372. $cleanforumname = str_replace('"', "'", strip_tags(format_string($forum->name)));
  373. $userfrom->customheaders = array ( // Headers to make emails easier to track
  374. 'Precedence: Bulk',
  375. 'List-Id: "'.$cleanforumname.'" <moodleforum'.$forum->id.'@'.$hostname.'>',
  376. 'List-Help: '.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id,
  377. 'Message-ID: <moodlepost'.$post->id.'@'.$hostname.'>',
  378. 'X-Course-Id: '.$course->id,
  379. 'X-Course-Name: '.format_string($course->fullname, true)
  380. );
  381. if ($post->parent) { // This post is a reply, so add headers for threading (see MDL-22551)
  382. $userfrom->customheaders[] = 'In-Reply-To: <moodlepost'.$post->parent.'@'.$hostname.'>';
  383. $userfrom->customheaders[] = 'References: <moodlepost'.$post->parent.'@'.$hostname.'>';
  384. }
  385. $postsubject = "$course->shortname: ".format_string($post->subject,true);
  386. $posttext = forum_make_mail_text($course, $forum, $discussion, $post, $userfrom, $userto);
  387. $posthtml = forum_make_mail_html($course, $forum, $discussion, $post, $userfrom, $userto);
  388. // Send the post now!
  389. mtrace('Sending ', '');
  390. if (!$mailresult = email_to_user($userto, $userfrom, $postsubject, $posttext,
  391. $posthtml, '', '', $CFG->forum_replytouser)) {
  392. mtrace("Error: mod/forum/cron.php: Could not send out mail for id $post->id to user $userto->id".
  393. " ($userto->email) .. not trying again.");
  394. add_to_log($course->id, 'forum', 'mail error', "discuss.php?d=$discussion->id#p$post->id",
  395. substr(format_string($post->subject,true),0,30), $cm->id, $userto->id);
  396. $errorcount[$post->id]++;
  397. } else if ($mailresult === 'emailstop') {
  398. // should not be reached anymore - see check above
  399. } else {
  400. $mailcount[$post->id]++;
  401. // Mark post as read if forum_usermarksread is set off
  402. if (!$CFG->forum_usermarksread) {
  403. $userto->markposts[$post->id] = $post->id;
  404. }
  405. }
  406. mtrace('post '.$post->id. ': '.$post->subject);
  407. }
  408. // mark processed posts as read
  409. forum_tp_mark_posts_read($userto, $userto->markposts);
  410. }
  411. }
  412. if ($posts) {
  413. foreach ($posts as $post) {
  414. mtrace($mailcount[$post->id]." users were sent post $post->id, '$post->subject'");
  415. if ($errorcount[$post->id]) {
  416. set_field("forum_posts", "mailed", "2", "id", "$post->id");
  417. }
  418. }
  419. }
  420. // release some memory
  421. unset($subscribedusers);
  422. unset($mailcount);
  423. unset($errorcount);
  424. $USER = clone($cronuser);
  425. course_setup(SITEID);
  426. $sitetimezone = $CFG->timezone;
  427. // Now see if there are any digest mails waiting to be sent, and if we should send them
  428. mtrace('Starting digest processing...');
  429. @set_time_limit(300); // terminate if not able to fetch all digests in 5 minutes
  430. if (!isset($CFG->digestmailtimelast)) { // To catch the first time
  431. set_config('digestmailtimelast', 0);
  432. }
  433. $timenow = time();
  434. $digesttime = usergetmidnight($timenow, $sitetimezone) + ($CFG->digestmailtime * 3600);
  435. // Delete any really old ones (normally there shouldn't be any)
  436. $weekago = $timenow - (7 * 24 * 3600);
  437. delete_records_select('forum_queue', "timemodified < $weekago");
  438. mtrace ('Cleaned old digest records');
  439. if ($CFG->digestmailtimelast < $digesttime and $timenow > $digesttime) {
  440. mtrace('Sending forum digests: '.userdate($timenow, '', $sitetimezone));
  441. $digestposts_rs = get_recordset_select('forum_queue', "timemodified < $digesttime");
  442. if (!rs_EOF($digestposts_rs)) {
  443. // We have work to do
  444. $usermailcount = 0;
  445. //caches - reuse the those filled before too
  446. $discussionposts = array();
  447. $userdiscussions = array();
  448. while ($digestpost = rs_fetch_next_record($digestposts_rs)) {
  449. if (!isset($users[$digestpost->userid])) {
  450. if ($user = get_record('user', 'id', $digestpost->userid)) {
  451. $users[$digestpost->userid] = $user;
  452. } else {
  453. continue;
  454. }
  455. }
  456. $postuser = $users[$digestpost->userid];
  457. if ($postuser->emailstop) {
  458. if (!empty($CFG->forum_logblocked)) {
  459. add_to_log(SITEID, 'forum', 'mail blocked', '', '', 0, $postuser->id);
  460. }
  461. continue;
  462. }
  463. if (!isset($posts[$digestpost->postid])) {
  464. if ($post = get_record('forum_posts', 'id', $digestpost->postid)) {
  465. $posts[$digestpost->postid] = $post;
  466. } else {
  467. continue;
  468. }
  469. }
  470. $discussionid = $digestpost->discussionid;
  471. if (!isset($discussions[$discussionid])) {
  472. if ($discussion = get_record('forum_discussions', 'id', $discussionid)) {
  473. $discussions[$discussionid] = $discussion;
  474. } else {
  475. continue;
  476. }
  477. }
  478. $forumid = $discussions[$discussionid]->forum;
  479. if (!isset($forums[$forumid])) {
  480. if ($forum = get_record('forum', 'id', $forumid)) {
  481. $forums[$forumid] = $forum;
  482. } else {
  483. continue;
  484. }
  485. }
  486. $courseid = $forums[$forumid]->course;
  487. if (!isset($courses[$courseid])) {
  488. if ($course = get_record('course', 'id', $courseid)) {
  489. $courses[$courseid] = $course;
  490. } else {
  491. continue;
  492. }
  493. }
  494. if (!isset($coursemodules[$forumid])) {
  495. if ($cm = get_coursemodule_from_instance('forum', $forumid, $courseid)) {
  496. $coursemodules[$forumid] = $cm;
  497. } else {
  498. continue;
  499. }
  500. }
  501. $userdiscussions[$digestpost->userid][$digestpost->discussionid] = $digestpost->discussionid;
  502. $discussionposts[$digestpost->discussionid][$digestpost->postid] = $digestpost->postid;
  503. }
  504. rs_close($digestposts_rs); /// Finished iteration, let's close the resultset
  505. // Data collected, start sending out emails to each user
  506. foreach ($userdiscussions as $userid => $thesediscussions) {
  507. @set_time_limit(120); // terminate if processing of any account takes longer than 2 minutes
  508. $USER = $cronuser;
  509. course_setup(SITEID); // reset cron user language, theme and timezone settings
  510. mtrace(get_string('processingdigest', 'forum', $userid), '... ');
  511. // First of all delete all the queue entries for this user
  512. delete_records_select('forum_queue', "userid = $userid AND timemodified < $digesttime");
  513. $userto = $users[$userid];
  514. // Override the language and timezone of the "current" user, so that
  515. // mail is customised for the receiver.
  516. $USER = $userto;
  517. course_setup(SITEID);
  518. // init caches
  519. $userto->viewfullnames = array();
  520. $userto->canpost = array();
  521. $userto->markposts = array();
  522. $postsubject = get_string('digestmailsubject', 'forum', format_string($site->shortname, true));
  523. $headerdata = new object();
  524. $headerdata->sitename = format_string($site->fullname, true);
  525. $headerdata->userprefs = $CFG->wwwroot.'/user/edit.php?id='.$userid.'&amp;course='.$site->id;
  526. $posttext = get_string('digestmailheader', 'forum', $headerdata)."\n\n";
  527. $headerdata->userprefs = '<a target="_blank" href="'.$headerdata->userprefs.'">'.get_string('digestmailprefs', 'forum').'</a>';
  528. $posthtml = "<head>";
  529. foreach ($CFG->stylesheets as $stylesheet) {
  530. $posthtml .= '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'" />'."\n";
  531. }
  532. $posthtml .= "</head>\n<body id=\"email\">\n";
  533. $posthtml .= '<p>'.get_string('digestmailheader', 'forum', $headerdata).'</p><br /><hr size="1" noshade="noshade" />';
  534. foreach ($thesediscussions as $discussionid) {
  535. @set_time_limit(120); // to be reset for each post
  536. $discussion = $discussions[$discussionid];
  537. $forum = $forums[$discussion->forum];
  538. $course = $courses[$forum->course];
  539. $cm = $coursemodules[$forum->id];
  540. //override language
  541. course_setup($course);
  542. // Fill caches
  543. if (!isset($userto->viewfullnames[$forum->id])) {
  544. $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
  545. $userto->viewfullnames[$forum->id] = has_capability('moodle/site:viewfullnames', $modcontext);
  546. }
  547. if (!isset($userto->canpost[$discussion->id])) {
  548. $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
  549. $userto->canpost[$discussion->id] = forum_user_can_post($forum, $discussion, $userto, $cm, $course, $modcontext);
  550. }
  551. $strforums = get_string('forums', 'forum');
  552. $canunsubscribe = ! forum_is_forcesubscribed($forum);
  553. $canreply = $userto->canpost[$discussion->id];
  554. $posttext .= "\n \n";
  555. $posttext .= '=====================================================================';
  556. $posttext .= "\n \n";
  557. $posttext .= "$course->shortname -> $strforums -> ".format_string($forum->name,true);
  558. if ($discussion->name != $forum->name) {
  559. $posttext .= " -> ".format_string($discussion->name,true);
  560. }
  561. $posttext .= "\n";
  562. $posthtml .= "<p><font face=\"sans-serif\">".
  563. "<a target=\"_blank\" href=\"$CFG->wwwroot/course/view.php?id=$course->id\">$course->shortname</a> -> ".
  564. "<a target=\"_blank\" href=\"$CFG->wwwroot/mod/forum/index.php?id=$course->id\">$strforums</a> -> ".
  565. "<a target=\"_blank\" href=\"$CFG->wwwroot/mod/forum/view.php?f=$forum->id\">".format_string($forum->name,true)."</a>";
  566. if ($discussion->name == $forum->name) {
  567. $posthtml .= "</font></p>";
  568. } else {
  569. $posthtml .= " -> <a target=\"_blank\" href=\"$CFG->wwwroot/mod/forum/discuss.php?d=$discussion->id\">".format_string($discussion->name,true)."</a></font></p>";
  570. }
  571. $posthtml .= '<p>';
  572. $postsarray = $discussionposts[$discussionid];
  573. sort($postsarray);
  574. foreach ($postsarray as $postid) {
  575. $post = $posts[$postid];
  576. if (array_key_exists($post->userid, $users)) { // we might know him/her already
  577. $userfrom = $users[$post->userid];
  578. } else if ($userfrom = get_record('user', 'id', $post->userid)) {
  579. $users[$userfrom->id] = $userfrom; // fetch only once, we can add it to user list, it will be skipped anyway
  580. } else {
  581. mtrace('Could not find user '.$post->userid);
  582. continue;
  583. }
  584. if (!isset($userfrom->groups[$forum->id])) {
  585. if (!isset($userfrom->groups)) {
  586. $userfrom->groups = array();
  587. $users[$userfrom->id]->groups = array();
  588. }
  589. $userfrom->groups[$forum->id] = groups_get_all_groups($course->id, $userfrom->id, $cm->groupingid);
  590. $users[$userfrom->id]->groups[$forum->id] = $userfrom->groups[$forum->id];
  591. }
  592. $userfrom->customheaders = array ("Precedence: Bulk");
  593. if ($userto->maildigest == 2) {
  594. // Subjects only
  595. $by = new object();
  596. $by->name = fullname($userfrom);
  597. $by->date = userdate($post->modified);
  598. $posttext .= "\n".format_string($post->subject,true).' '.get_string("bynameondate", "forum", $by);
  599. $posttext .= "\n---------------------------------------------------------------------";
  600. $by->name = "<a target=\"_blank\" href=\"$CFG->wwwroot/user/view.php?id=$userfrom->id&amp;course=$course->id\">$by->name</a>";
  601. $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>';
  602. } else {
  603. // The full treatment
  604. $posttext .= forum_make_mail_text($course, $forum, $discussion, $post, $userfrom, $userto, true);
  605. $posthtml .= forum_make_mail_post($course, $forum, $discussion, $post, $userfrom, $userto, false, $canreply, true, false);
  606. // Create an array of postid's for this user to mark as read.
  607. if (!$CFG->forum_usermarksread) {
  608. $userto->markposts[$post->id] = $post->id;
  609. }
  610. }
  611. }
  612. if ($canunsubscribe) {
  613. $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>";
  614. } else {
  615. $posthtml .= "\n<div class='mdl-right'><font size=\"1\">".get_string("everyoneissubscribed", "forum")."</font></div>";
  616. }
  617. $posthtml .= '<hr size="1" noshade="noshade" /></p>';
  618. }
  619. $posthtml .= '</body>';
  620. if ($userto->mailformat != 1) {
  621. // This user DOESN'T want to receive HTML
  622. $posthtml = '';
  623. }
  624. if (!$mailresult = email_to_user($userto, $site->shortname, $postsubject, $posttext, $posthtml,
  625. '', '', $CFG->forum_replytouser)) {
  626. mtrace("ERROR!");
  627. echo "Error: mod/forum/cron.php: Could not send out digest mail to user $userto->id ($userto->email)... not trying again.\n";
  628. add_to_log($course->id, 'forum', 'mail digest error', '', '', $cm->id, $userto->id);
  629. } else if ($mailresult === 'emailstop') {
  630. // should not happen anymore - see check above
  631. } else {
  632. mtrace("success.");
  633. $usermailcount++;
  634. // Mark post as read if forum_usermarksread is set off
  635. forum_tp_mark_posts_read($userto, $userto->markposts);
  636. }
  637. }
  638. }
  639. /// We have finishied all digest emails, update $CFG->digestmailtimelast
  640. set_config('digestmailtimelast', $timenow);
  641. }
  642. $USER = $cronuser;
  643. course_setup(SITEID); // reset cron user language, theme and timezone settings
  644. if (!empty($usermailcount)) {
  645. mtrace(get_string('digestsentusers', 'forum', $usermailcount));
  646. }
  647. if (!empty($CFG->forum_lastreadclean)) {
  648. $timenow = time();
  649. if ($CFG->forum_lastreadclean + (24*3600) < $timenow) {
  650. set_config('forum_lastreadclean', $timenow);
  651. mtrace('Removing old forum read tracking info...');
  652. forum_tp_clean_read_records();
  653. }
  654. } else {
  655. set_config('forum_lastreadclean', time());
  656. }
  657. return true;
  658. }
  659. /**
  660. * Builds and returns the body of the email notification in plain text.
  661. *
  662. * @param object $course
  663. * @param object $forum
  664. * @param object $discussion
  665. * @param object $post
  666. * @param object $userfrom
  667. * @param object $userto
  668. * @param boolean $bare
  669. * @return string The email body in plain text format.
  670. */
  671. function forum_make_mail_text($course, $forum, $discussion, $post, $userfrom, $userto, $bare = false) {
  672. global $CFG, $USER;
  673. if (!isset($userto->viewfullnames[$forum->id])) {
  674. if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $course->id)) {
  675. error('Course Module ID was incorrect');
  676. }
  677. $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
  678. $viewfullnames = has_capability('moodle/site:viewfullnames', $modcontext, $userto->id);
  679. } else {
  680. $viewfullnames = $userto->viewfullnames[$forum->id];
  681. }
  682. if (!isset($userto->canpost[$discussion->id])) {
  683. $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
  684. $canreply = forum_user_can_post($forum, $discussion, $userto, $cm, $course, $modcontext);
  685. } else {
  686. $canreply = $userto->canpost[$discussion->id];
  687. }
  688. $by = New stdClass;
  689. $by->name = fullname($userfrom, $viewfullnames);
  690. $by->date = userdate($post->modified, "", $userto->timezone);
  691. $strbynameondate = get_string('bynameondate', 'forum', $by);
  692. $strforums = get_string('forums', 'forum');
  693. $canunsubscribe = ! forum_is_forcesubscribed($forum);
  694. $posttext = '';
  695. if (!$bare) {
  696. $posttext = "$course->shortname -> $strforums -> ".format_string($forum->name,true);
  697. if ($discussion->name != $forum->name) {
  698. $posttext .= " -> ".format_string($discussion->name,true);
  699. }
  700. }
  701. $posttext .= "\n---------------------------------------------------------------------\n";
  702. $posttext .= format_string($post->subject,true);
  703. if ($bare) {
  704. $posttext .= " ($CFG->wwwroot/mod/forum/discuss.php?d=$discussion->id#p$post->id)";
  705. }
  706. $posttext .= "\n".$strbynameondate."\n";
  707. $posttext .= "---------------------------------------------------------------------\n";
  708. $posttext .= format_text_email(trusttext_strip($post->message), $post->format);
  709. $posttext .= "\n\n";
  710. if ($post->attachment) {
  711. $post->course = $course->id;
  712. $post->forum = $forum->id;
  713. $posttext .= forum_print_attachments($post, "text");
  714. }
  715. if (!$bare && $canreply) {
  716. $posttext .= "---------------------------------------------------------------------\n";
  717. $posttext .= get_string("postmailinfo", "forum", $course->shortname)."\n";
  718. $posttext .= "$CFG->wwwroot/mod/forum/post.php?reply=$post->id\n";
  719. }
  720. if (!$bare && $canunsubscribe) {
  721. $posttext .= "\n---------------------------------------------------------------------\n";
  722. $posttext .= get_string("unsubscribe", "forum");
  723. $posttext .= ": $CFG->wwwroot/mod/forum/subscribe.php?id=$forum->id\n";
  724. }
  725. return $posttext;
  726. }
  727. /**
  728. * Builds and returns the body of the email notification in html format.
  729. *
  730. * @param object $course
  731. * @param object $forum
  732. * @param object $discussion
  733. * @param object $post
  734. * @param object $userfrom
  735. * @param object $userto
  736. * @return string The email text in HTML format
  737. */
  738. function forum_make_mail_html($course, $forum, $discussion, $post, $userfrom, $userto) {
  739. global $CFG;
  740. if ($userto->mailformat != 1) { // Needs to be HTML
  741. return '';
  742. }
  743. if (!isset($userto->canpost[$discussion->id])) {
  744. $canreply = forum_user_can_post($forum, $discussion, $userto);
  745. } else {
  746. $canreply = $userto->canpost[$discussion->id];
  747. }
  748. $strforums = get_string('forums', 'forum');
  749. $canunsubscribe = ! forum_is_forcesubscribed($forum);
  750. $posthtml = '<head>';
  751. foreach ($CFG->stylesheets as $stylesheet) {
  752. $posthtml .= '<link rel="stylesheet" type="text/css" href="'.$stylesheet.'" />'."\n";
  753. }
  754. $posthtml .= '</head>';
  755. $posthtml .= "\n<body id=\"email\">\n\n";
  756. $posthtml .= '<div class="navbar">'.
  757. '<a target="_blank" href="'.$CFG->wwwroot.'/course/view.php?id='.$course->id.'">'.$course->shortname.'</a> &raquo; '.
  758. '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/index.php?id='.$course->id.'">'.$strforums.'</a> &raquo; '.
  759. '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id.'">'.format_string($forum->name,true).'</a>';
  760. if ($discussion->name == $forum->name) {
  761. $posthtml .= '</div>';
  762. } else {
  763. $posthtml .= ' &raquo; <a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$discussion->id.'">'.
  764. format_string($discussion->name,true).'</a></div>';
  765. }
  766. $posthtml .= forum_make_mail_post($course, $forum, $discussion, $post, $userfrom, $userto, false, $canreply, true, false);
  767. if ($canunsubscribe) {
  768. $posthtml .= '<hr /><div class="mdl-align unsubscribelink">
  769. <a href="'.$CFG->wwwroot.'/mod/forum/subscribe.php?id='.$forum->id.'">'.get_string('unsubscribe', 'forum').'</a>&nbsp;
  770. <a href="'.$CFG->wwwroot.'/mod/forum/unsubscribeall.php">'.get_string('unsubscribeall', 'forum').'</a></div>';
  771. }
  772. $posthtml .= '</body>';
  773. return $posthtml;
  774. }
  775. /**
  776. *
  777. * @param object $course
  778. * @param object $user
  779. * @param object $mod TODO this is not used in this function, refactor
  780. * @param object $forum
  781. * @return object A standard object with 2 variables: info (number of posts for this user) and time (last modified)
  782. */
  783. function forum_user_outline($course, $user, $mod, $forum) {
  784. global $CFG;
  785. require_once("$CFG->libdir/gradelib.php");
  786. $grades = grade_get_grades($course->id, 'mod', 'forum', $forum->id, $user->id);
  787. if (empty($grades->items[0]->grades)) {
  788. $grade = false;
  789. } else {
  790. $grade = reset($grades->items[0]->grades);
  791. }
  792. $count = forum_count_user_posts($forum->id, $user->id);
  793. if ($count && $count->postcount > 0) {
  794. $result = new object();
  795. $result->info = get_string("numposts", "forum", $count->postcount);
  796. $result->time = $count->lastpost;
  797. if ($grade) {
  798. $result->info .= ', ' . get_string('grade') . ': ' . $grade->str_long_grade;
  799. }
  800. return $result;
  801. } else if ($grade) {
  802. $result = new object();
  803. $result->info = get_string('grade') . ': ' . $grade->str_long_grade;
  804. //datesubmitted == time created. dategraded == time modified or time overridden
  805. //if grade was last modified by the user themselves use date graded. Otherwise use date submitted
  806. if ($grade->usermodified == $user->id || empty($grade->datesubmitted)) {
  807. $result->time = $grade->dategraded;
  808. } else {
  809. $result->time = $grade->datesubmitted;
  810. }
  811. return $result;
  812. }
  813. return NULL;
  814. }
  815. /**
  816. *
  817. */
  818. function forum_user_complete($course, $user, $mod, $forum) {
  819. global $CFG,$USER;
  820. require_once("$CFG->libdir/gradelib.php");
  821. $grades = grade_get_grades($course->id, 'mod', 'forum', $forum->id, $user->id);
  822. if (!empty($grades->items[0]->grades)) {
  823. $grade = reset($grades->items[0]->grades);
  824. echo '<p>'.get_string('grade').': '.$grade->str_long_grade.'</p>';
  825. if ($grade->str_feedback) {
  826. echo '<p>'.get_string('feedback').': '.$grade->str_feedback.'</p>';
  827. }
  828. }
  829. if ($posts = forum_get_user_posts($forum->id, $user->id)) {
  830. if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $course->id)) {
  831. error('Course Module ID was incorrect');
  832. }
  833. $discussions = forum_get_user_involved_discussions($forum->id, $user->id);
  834. // preload all user ratings for these discussions - one query only and minimal memory
  835. $cm->cache->ratings = array();
  836. $cm->cache->myratings = array();
  837. if ($postratings = forum_get_all_user_ratings($user->id, $discussions)) {
  838. foreach ($postratings as $pr) {
  839. if (!isset($cm->cache->ratings[$pr->postid])) {
  840. $cm->cache->ratings[$pr->postid] = array();
  841. }
  842. $cm->cache->ratings[$pr->postid][$pr->id] = $pr->rating;
  843. if ($pr->userid == $USER->id) {
  844. $cm->cache->myratings[$pr->postid] = $pr->rating;
  845. }
  846. }
  847. unset($postratings);
  848. }
  849. foreach ($posts as $post) {
  850. if (!isset($discussions[$post->discussion])) {
  851. continue;
  852. }
  853. $discussion = $discussions[$post->discussion];
  854. $ratings = null;
  855. if ($forum->assessed) {
  856. if ($scale = make_grades_menu($forum->scale)) {
  857. $ratings =new object();
  858. $ratings->scale = $scale;
  859. $ratings->assesstimestart = $forum->assesstimestart;
  860. $ratings->assesstimefinish = $forum->assesstimefinish;
  861. $ratings->allow = false;
  862. }
  863. }
  864. forum_print_post($post, $discussion, $forum, $cm, $course, false, false, false, $ratings);
  865. }
  866. } else {
  867. echo "<p>".get_string("noposts", "forum")."</p>";
  868. }
  869. }
  870. /**
  871. *
  872. */
  873. function forum_print_overview($courses,&$htmlarray) {
  874. global $USER, $CFG;
  875. //$LIKE = sql_ilike();//no longer using like in queries. MDL-20578
  876. if (empty($courses) || !is_array($courses) || count($courses) == 0) {
  877. return array();
  878. }
  879. if (!$forums = get_all_instances_in_courses('forum',$courses)) {
  880. return;
  881. }
  882. // get all forum logs in ONE query (much better!)
  883. $sql = "SELECT instance,cmid,l.course,COUNT(l.id) as count FROM {$CFG->prefix}log l "
  884. ." JOIN {$CFG->prefix}course_modules cm ON cm.id = cmid "
  885. ." WHERE (";
  886. foreach ($courses as $course) {
  887. $sql .= '(l.course = '.$course->id.' AND l.time > '.$course->lastaccess.') OR ';
  888. }
  889. $sql = substr($sql,0,-3); // take off the last OR
  890. $sql .= ") AND l.module = 'forum' AND action = 'add post' "
  891. ." AND userid != ".$USER->id." GROUP BY cmid,l.course,instance";
  892. if (!$new = get_records_sql($sql)) {
  893. $new = array(); // avoid warnings
  894. }
  895. // also get all forum tracking stuff ONCE.
  896. $trackingforums = array();
  897. foreach ($forums as $forum) {
  898. if (forum_tp_can_track_forums($forum)) {
  899. $trackingforums[$forum->id] = $forum;
  900. }
  901. }
  902. if (count($trackingforums) > 0) {
  903. $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0;
  904. $sql = 'SELECT d.forum,d.course,COUNT(p.id) AS count '.
  905. ' FROM '.$CFG->prefix.'forum_posts p '.
  906. ' JOIN '.$CFG->prefix.'forum_discussions d ON p.discussion = d.id '.
  907. ' LEFT JOIN '.$CFG->prefix.'forum_read r ON r.postid = p.id AND r.userid = '.$USER->id.' WHERE (';
  908. foreach ($trackingforums as $track) {
  909. $sql .= '(d.forum = '.$track->id.' AND (d.groupid = -1 OR d.groupid = 0 OR d.groupid = '.get_current_group($track->course).')) OR ';
  910. }
  911. $sql = substr($sql,0,-3); // take off the last OR
  912. $sql .= ') AND p.modified >= '.$cutoffdate.' AND r.id is NULL GROUP BY d.forum,d.course';
  913. if (!$unread = get_records_sql($sql)) {
  914. $unread = array();
  915. }
  916. } else {
  917. $unread = array();
  918. }
  919. if (empty($unread) and empty($new)) {
  920. return;
  921. }
  922. $strforum = get_string('modulename','forum');
  923. $strnumunread = get_string('overviewnumunread','forum');
  924. $strnumpostssince = get_string('overviewnumpostssince','forum');
  925. foreach ($forums as $forum) {
  926. $str = '';
  927. $count = 0;
  928. $thisunread = 0;
  929. $showunread = false;
  930. // either we have something from logs, or trackposts, or nothing.
  931. if (array_key_exists($forum->id, $new) && !empty($new[$forum->id])) {
  932. $count = $new[$forum->id]->count;
  933. }
  934. if (array_key_exists($forum->id,$unread)) {
  935. $thisunread = $unread[$forum->id]->count;
  936. $showunread = true;
  937. }
  938. if ($count > 0 || $thisunread > 0) {
  939. $str .= '<div class="overview forum"><div class="name">'.$strforum.': <a title="'.$strforum.'" href="'.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id.'">'.
  940. $forum->name.'</a></div>';
  941. $str .= '<div class="info">';
  942. $str .= $count.' '.$strnumpostssince;
  943. if (!empty($showunread)) {
  944. $str .= '<br />'.$thisunread .' '.$strnumunread;
  945. }
  946. $str .= '</div></div>';
  947. }
  948. if (!empty($str)) {
  949. if (!array_key_exists($forum->course,$htmlarray)) {
  950. $htmlarray[$forum->course] = array();
  951. }
  952. if (!array_key_exists('forum',$htmlarray[$forum->course])) {
  953. $htmlarray[$forum->course]['forum'] = ''; // initialize, avoid warnings
  954. }
  955. $htmlarray[$forum->course]['forum'] .= $str;
  956. }
  957. }
  958. }
  959. /**
  960. * Given a course and a date, prints a summary of all the new
  961. * messages posted in the course since that date
  962. * @param object $course
  963. * @param bool $viewfullnames capability
  964. * @param int $timestart
  965. * @return bool success
  966. */
  967. function forum_print_recent_activity($course, $viewfullnames, $timestart) {
  968. global $CFG, $USER;
  969. // do not use log table if possible, it may be huge and is expensive to join with other tables
  970. if (!$posts = get_records_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid,
  971. d.timestart, d.timeend, d.userid AS duserid,
  972. u.firstname, u.lastname, u.email, u.picture
  973. FROM {$CFG->prefix}forum_posts p
  974. JOIN {$CFG->prefix}forum_discussions d ON d.id = p.discussion
  975. JOIN {$CFG->prefix}forum f ON f.id = d.forum
  976. JOIN {$CFG->prefix}user u ON u.id = p.userid
  977. WHERE p.created > $timestart AND f.course = {$course->id}
  978. ORDER BY p.id ASC")) { // order by initial posting date
  979. return false;
  980. }
  981. $modinfo =& get_fast_modinfo($course);
  982. $groupmodes = array();
  983. $cms = array();
  984. $strftimerecent = get_string('strftimerecent');
  985. $printposts = array();
  986. foreach ($posts as $post) {
  987. if (!isset($modinfo->instances['forum'][$post->forum])) {
  988. // not visible
  989. continue;
  990. }
  991. $cm = $modinfo->instances['forum'][$post->forum];
  992. if (!$cm->uservisible) {
  993. continue;
  994. }
  995. $context = get_context_instance(CONTEXT_MODULE, $cm->id);
  996. if (!has_capability('mod/forum:viewdiscussion', $context)) {
  997. continue;
  998. }
  999. if (!empty($CFG->forum_enabletimedposts) and $USER->id != $post->duserid
  1000. and (($post->timestart > 0 and $post->timestart > time()) or ($post->timeend > 0 and $post->timeend < time()))) {
  1001. if (!has_capability('mod/forum:viewhiddentimedposts', $context)) {
  1002. continue;
  1003. }
  1004. }
  1005. $groupmode = groups_get_activity_groupmode($cm, $course);
  1006. if ($groupmode) {
  1007. if ($post->groupid == -1 or $groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $context)) {
  1008. // oki (Open discussions have groupid -1)
  1009. } else {
  1010. // separate mode
  1011. if (isguestuser()) {
  1012. // shortcut
  1013. continue;
  1014. }
  1015. if (is_null($modinfo->groups)) {
  1016. $modinfo->groups = groups_get_user_groups($course->id); // load all my groups and cache it in modinfo
  1017. }
  1018. if (!array_key_exists($post->groupid, $modinfo->groups[0])) {
  1019. continue;
  1020. }
  1021. }
  1022. }
  1023. $printposts[] = $post;
  1024. }
  1025. unset($posts);
  1026. if (!$printposts) {
  1027. return false;
  1028. }
  1029. print_headline(get_string('newforumposts', 'forum').':', 3);
  1030. echo "\n<ul class='unlist'>\n";
  1031. foreach ($printposts as $post) {
  1032. $subjectclass = empty($post->parent) ? ' bold' : '';
  1033. echo '<li><div class="head">'.
  1034. '<div class="date">'.userdate($post->modified, $strftimerecent).'</div>'.
  1035. '<div class="name">'.fullname($post, $viewfullnames).'</div>'.
  1036. '</div>';
  1037. echo '<div class="info'.$subjectclass.'">';
  1038. if (empty($post->parent)) {
  1039. echo '"<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'">';
  1040. } else {
  1041. echo '"<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'&amp;parent='.$post->parent.'#p'.$post->id.'">';
  1042. }
  1043. $post->subject = break_up_long_words(format_string($post->subject, true));
  1044. echo $post->subject;
  1045. echo "</a>\"</div></li>\n";
  1046. }
  1047. echo "</ul>\n";
  1048. return true;
  1049. }
  1050. /**
  1051. * Return grade for given user or all users.
  1052. *
  1053. * @param int $forumid id of forum
  1054. * @param int $userid optional user id, 0 means all users
  1055. * @return array array of grades, false if none
  1056. */
  1057. function forum_get_user_grades($forum, $userid=0) {
  1058. global $CFG;
  1059. $user = $userid ? "AND u.id = $userid" : "";
  1060. $aggtype = $forum->assessed;
  1061. switch ($aggtype) {
  1062. case FORUM_AGGREGATE_COUNT :
  1063. $sql = "SELECT u.id, u.id AS userid, COUNT(fr.rating) AS rawgrade
  1064. FROM {$CFG->prefix}user u, {$CFG->prefix}forum_posts fp,
  1065. {$CFG->prefix}forum_ratings fr, {$CFG->prefix}forum_discussions fd
  1066. WHERE u.id = fp.userid AND fp.discussion = fd.id AND fr.post = fp.id
  1067. AND fr.userid != u.id AND fd.forum = $forum->id
  1068. $user
  1069. GROUP BY u.id";
  1070. break;
  1071. case FORUM_AGGREGATE_MAX :
  1072. $sql = "SELECT u.id, u.id AS userid, MAX(fr.rating) AS rawgrade
  1073. FROM {$CFG->prefix}user u, {$CFG->prefix}forum_posts fp,
  1074. {$CFG->prefix}forum_ratings fr, {$CFG->prefix}forum_discussions fd
  1075. WHERE u.id = fp.userid AND fp.discussion = fd.id AND fr.post = fp.id
  1076. AND fr.userid != u.id AND fd.forum = $forum->id
  1077. $user
  1078. GROUP BY u.id";
  1079. break;
  1080. case FORUM_AGGREGATE_MIN :
  1081. $sql = "SELECT u.id, u.id AS userid, MIN(fr.rating) AS rawgrade
  1082. FROM {$CFG->prefix}user u, {$CFG->prefix}forum_posts fp,
  1083. {$CFG->prefix}forum_ratings fr, {$CFG->prefix}forum_discussions fd
  1084. WHERE u.id = fp.userid AND fp.discussion = fd.id AND fr.post = fp.id
  1085. AND fr.userid != u.id AND fd.forum = $forum->id
  1086. $user
  1087. GROUP BY u.id";
  1088. break;
  1089. case FORUM_AGGREGATE_SUM :
  1090. $sql = "SELECT u.id, u.id AS userid, SUM(fr.rating) AS rawgrade
  1091. FROM {$CFG->prefix}user u, {$CFG->prefix}forum_posts fp,
  1092. {$CFG->prefix}forum_ratings fr, {$CFG->prefix}forum_discussions fd
  1093. WHERE u.id = fp.userid AND fp.discussion = fd.id AND fr.post = fp.id
  1094. AND fr.userid != u.id AND fd.forum = $forum->id
  1095. $user
  1096. GROUP BY u.id";
  1097. break;
  1098. default : //avg
  1099. $sql = "SELECT u.id, u.id AS userid, AVG(fr.rating) AS rawgrade
  1100. FROM {$CFG->prefix}user u, {$CFG->prefix}forum_posts fp,
  1101. {$CFG->prefix}forum_ratings fr, {$CFG->prefix}forum_discussions fd
  1102. WHERE u.id = fp.userid AND fp.discussion = fd.id AND fr.post = fp.id
  1103. AND fr.userid != u.id AND fd.forum = $forum->id
  1104. $user
  1105. GROUP BY u.id";
  1106. break;
  1107. }
  1108. if ($results = get_records_sql($sql)) {
  1109. // it could throw off the grading if count and sum returned a rawgrade higher than scale
  1110. // so to prevent it we review the results and ensure that rawgrade does not exceed the scale, if it does we set rawgrade = scale (i.e. full credit)
  1111. foreach ($results as $rid=>$result) {
  1112. if ($forum->scale >= 0) {
  1113. //numeric
  1114. if ($result->rawgrade > $forum->scale) {
  1115. $results[$rid]->rawgrade = $forum->scale;
  1116. }
  1117. } else {
  1118. //scales
  1119. if ($scale = get_record('scale', 'id', -$forum->scale)) {
  1120. $scale = explode(',', $scale->scale);
  1121. $max = count($scale);
  1122. if ($result->rawgrade > $max) {
  1123. $results[$rid]->rawgrade = $max;
  1124. }
  1125. }
  1126. }
  1127. }
  1128. }
  1129. return $results;
  1130. }
  1131. /**
  1132. * Update grades by firing grade_updated event
  1133. *
  1134. * @param object $forum null means all forums
  1135. * @param int $userid specific user only, 0 mean all
  1136. * @param boolean $nullifnone return null if grade does not exist
  1137. * @return void
  1138. */
  1139. function forum_update_grades($forum=null, $userid=0, $nullifnone=true) {
  1140. global $CFG;
  1141. if ($forum != null) {
  1142. require_once($CFG->libdir.'/gradelib.php');
  1143. if ($grades = forum_get_user_grades($forum, $userid)) {
  1144. forum_grade_item_update($forum, $grades);
  1145. } else if ($userid and $nullifnone) {
  1146. $grade = new object();
  1147. $grade->userid = $userid;
  1148. $grade->rawgrade = NULL;
  1149. forum_grade_item_update($forum, $grade);
  1150. } else {
  1151. forum_grade_item_update($forum);
  1152. }
  1153. } else {
  1154. $sql = "SELECT f.*, cm.idnumber as cmidnumber
  1155. FROM {$CFG->prefix}forum f, {$CFG->prefix}course_modules cm, {$CFG->prefix}modules m
  1156. WHERE m.name='forum' AND m.id=cm.module AND cm.instance=f.id";
  1157. if ($rs = get_recordset_sql($sql)) {
  1158. while ($forum = rs_fetch_next_record($rs)) {
  1159. if ($forum->assessed) {
  1160. forum_update_grades($forum, 0, false);
  1161. } else {
  1162. forum_grade_item_update($forum);
  1163. }
  1164. }
  1165. rs_close($rs);
  1166. }
  1167. }
  1168. }
  1169. /**
  1170. * Create/update grade item for given forum
  1171. *
  1172. * @param object $forum object with extra cmidnumber
  1173. * @param mixed optional array/object of grade(s); 'reset' means reset grades in gradebook
  1174. * @return int 0 if ok
  1175. */
  1176. function forum_grade_item_update($forum, $grades=NULL) {
  1177. global $CFG;
  1178. if (!function_exists('grade_update')) { //workaround for buggy PHP versions
  1179. require_once($CFG->libdir.'/gradelib.php');
  1180. }
  1181. $params = array('itemname'=>$forum->name, 'idnumber'=>$forum->cmidnumber);
  1182. if (!$forum->assessed or $forum->scale == 0) {
  1183. $params['gradetype'] = GRADE_TYPE_NONE;
  1184. } else if ($forum->scale > 0) {
  1185. $params['gradetype'] = GRADE_TYPE_VALUE;
  1186. $params['grademax'] = $forum->scale;
  1187. $params['grademin'] = 0;
  1188. } else if ($forum->scale < 0) {
  1189. $params['gradetype'] = GRADE_TYPE_SCALE;
  1190. $params['scaleid'] = -$forum->scale;
  1191. }
  1192. if ($grades === 'reset') {
  1193. $params['reset'] = true;
  1194. $grades = NULL;
  1195. }
  1196. return grade_update('mod/forum', $forum->course, 'mod', 'forum', $forum->id, 0, $grades, $params);
  1197. }
  1198. /**
  1199. * Delete grade item for given forum
  1200. *
  1201. * @param object $forum object
  1202. * @return object grade_item
  1203. */
  1204. function forum_grade_item_delete($forum) {
  1205. global $CFG;
  1206. require_once($CFG->libdir.'/gradelib.php');
  1207. return grade_update('mod/forum', $forum->course, 'mod', 'forum', $forum->id, 0, NULL, array('deleted'=>1));
  1208. }
  1209. /**
  1210. * Returns the users with data in one forum
  1211. * (users with records in forum_subscriptions, forum_posts and forum_ratings, students)
  1212. * @param int $forumid
  1213. * @return mixed array or false if none
  1214. */
  1215. function forum_get_participants($forumid) {
  1216. global $CFG;
  1217. //Get students from forum_subscriptions
  1218. $st_subscriptions = get_records_sql("SELECT DISTINCT u.id, u.id
  1219. FROM {$CFG->prefix}user u,
  1220. {$CFG->prefix}forum_subscriptions s
  1221. WHERE s.forum = '$forumid' and
  1222. u.id = s.userid");
  1223. //Get students from forum_posts
  1224. $st_posts = get_records_sql("SELECT DISTINCT u.id, u.id
  1225. FROM {$CFG->prefix}user u,
  1226. {$CFG->prefix}forum_discussions d,
  1227. {$CFG->prefix}forum_posts p
  1228. WHERE d.forum = '$forumid' and
  1229. p.discussion = d.id and
  1230. u.id = p.userid");
  1231. //Get students from forum_ratings
  1232. $st_ratings = get_records_sql("SELECT DISTINCT u.id, u.id
  1233. FROM {$CFG->prefix}user u,
  1234. {$CFG->prefix}forum_discussions d,
  1235. {$CFG->prefix}forum_posts p,
  1236. {$CFG->prefix}forum_ratings r
  1237. WHERE d.forum = '$forumid' and
  1238. p.discussion = d.id and
  1239. r.post = p.id and
  1240. u.id = r.userid");
  1241. //Add st_posts to st_subscriptions
  1242. if ($st_posts) {
  1243. foreach ($st_posts as $st_post) {
  1244. $st_subscriptions[$st_post->id] = $st_post;
  1245. }
  1246. }
  1247. //Add st_ratings to st_subscriptions
  1248. if ($st_ratings) {
  1249. foreach ($st_ratings as $st_rating) {
  1250. $st_subscriptions[$st_rating->id] = $st_rating;
  1251. }
  1252. }
  1253. //Return st_subscriptions array (it contains an array of unique users)
  1254. return ($st_subscriptions);
  1255. }
  1256. /**
  1257. * This function returns if a scale is being used by one forum
  1258. * @param int $forumid
  1259. * @param int $scaleid negative number
  1260. * @return bool
  1261. */
  1262. function forum_scale_used ($forumid,$scaleid) {
  1263. $return = false;
  1264. $rec = get_record("forum","id","$forumid","scale","-$scaleid");
  1265. if (!empty($rec) && !empty($scaleid)) {
  1266. $return = true;
  1267. }
  1268. return $return;
  1269. }
  1270. /**
  1271. * Checks if scale is being used by any instance of forum
  1272. *
  1273. * This is used to find out if scale used anywhere
  1274. * @param $scaleid int
  1275. * @return boolean True if the scale is used by any forum
  1276. */
  1277. function forum_scale_used_anywhere($scaleid) {
  1278. if ($scaleid and record_exists('forum', 'scale', -$scaleid)) {
  1279. return true;
  1280. } else {
  1281. return false;
  1282. }
  1283. }
  1284. // SQL FUNCTIONS ///////////////////////////////////////////////////////////
  1285. /**
  1286. * Gets a post with all info ready for forum_print_post
  1287. * Most of these joins are just to get the forum id
  1288. * @param int $postid
  1289. * @return mixed array of posts or false
  1290. */
  1291. function forum_get_post_full($postid) {
  1292. global $CFG;
  1293. return get_record_sql("SELECT p.*, d.forum, u.firstname, u.lastname, u.email, u.picture, u.imagealt
  1294. FROM {$CFG->prefix}forum_posts p
  1295. JOIN {$CFG->prefix}forum_discussions d ON p.discussion = d.id
  1296. LEFT JOIN {$CFG->prefix}user u ON p.userid = u.id
  1297. WHERE p.id = '$postid'");
  1298. }
  1299. /**
  1300. * Gets posts with all info ready for forum_print_post
  1301. * We pass forumid in because we always know it so no need to make a
  1302. * complicated join to find it out.
  1303. * @return mixed array of posts or false
  1304. */
  1305. function forum_get_discussion_posts($discussion, $sort, $forumid) {
  1306. global $CFG;
  1307. return get_records_sql("SELECT p.*, $forumid AS forum, u.firstname, u.lastname, u.email, u.picture, u.imagealt
  1308. FROM {$CFG->prefix}forum_posts p
  1309. LEFT JOIN {$CFG->prefix}user u ON p.userid = u.id
  1310. WHERE p.discussion = $discussion
  1311. AND p.parent > 0 $sort");
  1312. }
  1313. /**
  1314. * Gets all posts in discussion including top parent.
  1315. * @param int $discussionid
  1316. * @param string $sort
  1317. * @param bool $tracking does user track the forum?
  1318. * @return array of posts
  1319. */
  1320. function forum_get_all_discussion_posts($discussionid, $sort, $tracking=false) {
  1321. global $CFG, $USER;
  1322. $tr_sel = "";
  1323. $tr_join = "";
  1324. if ($tracking) {
  1325. $now = time();
  1326. $cutoffdate = $now - ($CFG->forum_oldpostdays * 24 * 3600);
  1327. $tr_sel = ", fr.id AS postread";
  1328. $tr_join = "LEFT JOIN {$CFG->prefix}forum_read fr ON (fr.postid = p.id AND fr.userid = $USER->id)";
  1329. }
  1330. if (!$posts = get_records_sql("SELECT p.*, u.firstname, u.lastname, u.email, u.picture, u.imagealt $tr_sel
  1331. FROM {$CFG->prefix}forum_posts p
  1332. LEFT JOIN {$CFG->prefix}user u ON p.userid = u.id
  1333. $tr_join
  1334. WHERE p.discussion = $discussionid
  1335. ORDER BY $sort")) {
  1336. return array();
  1337. }
  1338. foreach ($posts as $pid=>$p) {
  1339. if ($tracking) {
  1340. if (forum_tp_is_post_old($p)) {
  1341. $posts[$pid]->postread = true;
  1342. }
  1343. }
  1344. if (!$p->parent) {
  1345. continue;
  1346. }
  1347. if (!isset($posts[$p->parent])) {
  1348. continue; // parent does not exist??
  1349. }
  1350. if (!isset($posts[$p->parent]->children)) {
  1351. $posts[$p->parent]->children = array();
  1352. }
  1353. $posts[$p->parent]->children[$pid] =& $posts[$pid];
  1354. }
  1355. return $posts;
  1356. }
  1357. /**
  1358. * Gets posts with all info ready for forum_print_post
  1359. * We pass forumid in because we always know it so no need to make a
  1360. * complicated join to find it out.
  1361. */
  1362. function forum_get_child_posts($parent, $forumid) {
  1363. global $CFG;
  1364. return get_records_sql("SELECT p.*, $forumid AS forum, u.firstname, u.lastname, u.email, u.picture, u.imagealt
  1365. FROM {$CFG->prefix}forum_posts p
  1366. LEFT JOIN {$CFG->prefix}user u ON p.userid = u.id
  1367. WHERE p.parent = '$parent'
  1368. ORDER BY p.created ASC");
  1369. }
  1370. /**
  1371. * An array of forum objects that the user is allowed to read/search through.
  1372. * @param $userid
  1373. * @param $courseid - if 0, we look for forums throughout the whole site.
  1374. * @return array of forum objects, or false if no matches
  1375. * Forum objects have the following attributes:
  1376. * id, type, course, cmid, cmvisible, cmgroupmode, accessallgroups,
  1377. * viewhiddentimedposts
  1378. */
  1379. function forum_get_readable_forums($userid, $courseid=0) {
  1380. global $CFG, $USER;
  1381. require_once($CFG->dirroot.'/course/lib.php');
  1382. if (!$forummod = get_record('modules', 'name', 'forum')) {
  1383. error('The forum module is not installed');
  1384. }
  1385. if ($courseid) {
  1386. $courses = get_records('course', 'id', $courseid);
  1387. } else {
  1388. // If no course is specified, then the user can see SITE + his courses.
  1389. // And admins can see all courses, so pass the $doanything flag enabled
  1390. $courses1 = get_records('course', 'id', SITEID);
  1391. $courses2 = get_my_courses($userid, null, null, true);
  1392. $courses = array_merge($courses1, $courses2);
  1393. }
  1394. if (!$courses) {
  1395. return array();
  1396. }
  1397. $readableforums = array();
  1398. foreach ($courses as $course) {
  1399. $modinfo =& get_fast_modinfo($course);
  1400. if (is_null($modinfo->groups)) {
  1401. $modinfo->groups = groups_get_user_groups($course->id, $userid);
  1402. }
  1403. if (empty($modinfo->instances['forum'])) {
  1404. // hmm, no forums?
  1405. continue;
  1406. }
  1407. $courseforums = get_records('forum', 'course', $course->id);
  1408. foreach ($modinfo->instances['forum'] as $forumid => $cm) {
  1409. if (!$cm->uservisible or !isset($courseforums[$forumid])) {
  1410. continue;
  1411. }
  1412. $context = get_context_instance(CONTEXT_MODULE, $cm->id);
  1413. $forum = $courseforums[$forumid];
  1414. $forum->context = $context;
  1415. $forum->cm = $cm;
  1416. if (!has_capability('mod/forum:viewdiscussion', $context)) {
  1417. continue;
  1418. }
  1419. /// group access
  1420. if (groups_get_activity_groupmode($cm, $course) == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) {
  1421. if (is_null($modinfo->groups)) {
  1422. $modinfo->groups = groups_get_user_groups($course->id, $USER->id);
  1423. }
  1424. if (empty($CFG->enablegroupings)) {
  1425. $forum->onlygroups = $modinfo->groups[0];
  1426. $forum->onlygroups[] = -1;
  1427. } else if (isset($modinfo->groups[$cm->groupingid])) {
  1428. $forum->onlygroups = $modinfo->groups[$cm->groupingid];
  1429. $forum->onlygroups[] = -1;
  1430. } else {
  1431. $forum->onlygroups = array(-1);
  1432. }
  1433. }
  1434. /// hidden timed discussions
  1435. $forum->viewhiddentimedposts = true;
  1436. if (!empty($CFG->forum_enabletimedposts)) {
  1437. if (!has_capability('mod/forum:viewhiddentimedposts', $context)) {
  1438. $forum->viewhiddentimedposts = false;
  1439. }
  1440. }
  1441. /// qanda access
  1442. if ($forum->type == 'qanda'
  1443. && !has_capability('mod/forum:viewqandawithoutposting', $context)) {
  1444. // We need to check whether the user has posted in the qanda forum.
  1445. $forum->onlydiscussions = array(); // Holds discussion ids for the discussions
  1446. // the user is allowed to see in this forum.
  1447. if ($discussionspostedin = forum_discussions_user_has_posted_in($forum->id, $USER->id)) {
  1448. foreach ($discussionspostedin as $d) {
  1449. $forum->onlydiscussions[] = $d->id;
  1450. }
  1451. }
  1452. }
  1453. $readableforums[$forum->id] = $forum;
  1454. }
  1455. unset($modinfo);
  1456. } // End foreach $courses
  1457. //print_object($courses);
  1458. //print_object($readableforums);
  1459. return $readableforums;
  1460. }
  1461. /**
  1462. * Returns a list of posts found using an array of search terms.
  1463. * @param $searchterms - array of search terms, e.g. word +word -word
  1464. * @param $courseid - if 0, we search through the whole site
  1465. * @param $page
  1466. * @param $recordsperpage=50
  1467. * @param &$totalcount
  1468. * @param $extrasql
  1469. * @return array of posts found
  1470. */
  1471. function forum_search_posts($searchterms, $courseid=0, $limitfrom=0, $limitnum=50,
  1472. &$totalcount, $extrasql='') {
  1473. global $CFG, $USER;
  1474. require_once($CFG->libdir.'/searchlib.php');
  1475. $forums = forum_get_readable_forums($USER->id, $courseid);
  1476. if (count($forums) == 0) {
  1477. $totalcount = 0;
  1478. return false;
  1479. }
  1480. $now = round(time(), -2); // db friendly
  1481. $fullaccess = array();
  1482. $where = array();
  1483. foreach ($forums as $forumid => $forum) {
  1484. $select = array();
  1485. if (!$forum->viewhiddentimedposts) {
  1486. $select[] = "(d.userid = {$USER->id} OR (d.timestart < $now AND (d.timeend = 0 OR d.timeend > $now)))";
  1487. }
  1488. $cm = $forum->cm;
  1489. $context = $forum->context;
  1490. if ($forum->type == 'qanda'
  1491. && !has_capability('mod/forum:viewqandawithoutposting', $context)) {
  1492. if (!empty($forum->onlydiscussions)) {
  1493. $discussionsids = implode(',', $forum->onlydiscussions);
  1494. $select[] = "(d.id IN ($discussionsids) OR p.parent = 0)";
  1495. } else {
  1496. $select[] = "p.parent = 0";
  1497. }
  1498. }
  1499. if (!empty($forum->onlygroups)) {
  1500. $groupids = implode(',', $forum->onlygroups);
  1501. $select[] = "d.groupid IN ($groupids)";
  1502. }
  1503. if ($select) {
  1504. $selects = implode(" AND ", $select);
  1505. $where[] = "(d.forum = $forumid AND $selects)";
  1506. } else {
  1507. $fullaccess[] = $forumid;
  1508. }
  1509. }
  1510. if ($fullaccess) {
  1511. $fullids = implode(',', $fullaccess);
  1512. $where[] = "(d.forum IN ($fullids))";
  1513. }
  1514. $selectdiscussion = "(".implode(" OR ", $where).")";
  1515. // Some differences SQL
  1516. $LIKE = sql_ilike();
  1517. $NOTLIKE = 'NOT ' . $LIKE;
  1518. if ($CFG->dbfamily == 'postgres') {
  1519. $REGEXP = '~*';
  1520. $NOTREGEXP = '!~*';
  1521. } else {
  1522. $REGEXP = 'REGEXP';
  1523. $NOTREGEXP = 'NOT REGEXP';
  1524. }
  1525. $messagesearch = '';
  1526. $searchstring = '';
  1527. // Need to concat these back together for parser to work.
  1528. foreach($searchterms as $searchterm){
  1529. if ($searchstring != '') {
  1530. $searchstring .= ' ';
  1531. }
  1532. $searchstring .= $searchterm;
  1533. }
  1534. // We need to allow quoted strings for the search. The quotes *should* be stripped
  1535. // by the parser, but this should be examined carefully for security implications.
  1536. $searchstring = str_replace("\\\"","\"",$searchstring);
  1537. $parser = new search_parser();
  1538. $lexer = new search_lexer($parser);
  1539. if ($lexer->parse($searchstring)) {
  1540. $parsearray = $parser->get_parsed_array();
  1541. // Experimental feature under 1.8! MDL-8830
  1542. // Use alternative text searches if defined
  1543. // This feature only works under mysql until properly implemented for other DBs
  1544. // Requires manual creation of text index for forum_posts before enabling it:
  1545. // CREATE FULLTEXT INDEX foru_post_tix ON [prefix]forum_posts (subject, message)
  1546. // Experimental feature under 1.8! MDL-8830
  1547. if (!empty($CFG->forum_usetextsearches)) {
  1548. $messagesearch = search_generate_text_SQL($parsearray, 'p.message', 'p.subject',
  1549. 'p.userid', 'u.id', 'u.firstname',
  1550. 'u.lastname', 'p.modified', 'd.forum');
  1551. } else {
  1552. $messagesearch = search_generate_SQL($parsearray, 'p.message', 'p.subject',
  1553. 'p.userid', 'u.id', 'u.firstname',
  1554. 'u.lastname', 'p.modified', 'd.forum');
  1555. }
  1556. }
  1557. $fromsql = "{$CFG->prefix}forum_posts p,
  1558. {$CFG->prefix}forum_discussions d,
  1559. {$CFG->prefix}user u";
  1560. $selectsql = " $messagesearch
  1561. AND p.discussion = d.id
  1562. AND p.userid = u.id
  1563. AND $selectdiscussion
  1564. $extrasql";
  1565. $countsql = "SELECT COUNT(*)
  1566. FROM $fromsql
  1567. WHERE $selectsql";
  1568. $searchsql = "SELECT p.*,
  1569. d.forum,
  1570. u.firstname,
  1571. u.lastname,
  1572. u.email,
  1573. u.picture,
  1574. u.imagealt
  1575. FROM $fromsql
  1576. WHERE $selectsql
  1577. ORDER BY p.modified DESC";
  1578. $totalcount = count_records_sql($countsql);
  1579. return get_records_sql($searchsql, $limitfrom, $limitnum);
  1580. }
  1581. /**
  1582. * Returns a list of ratings for all posts in discussion
  1583. * @param object $discussion
  1584. * @return array of ratings or false
  1585. */
  1586. function forum_get_all_discussion_ratings($discussion) {
  1587. global $CFG;
  1588. return get_records_sql("SELECT r.id, r.userid, p.id AS postid, r.rating
  1589. FROM {$CFG->prefix}forum_ratings r,
  1590. {$CFG->prefix}forum_posts p
  1591. WHERE r.post = p.id AND p.discussion = $discussion->id
  1592. ORDER BY p.id ASC");
  1593. }
  1594. /**
  1595. * Returns a list of ratings for one specific user for all posts in discussion
  1596. * @global object $CFG
  1597. * @param object $discussions the discussions for which we return all ratings
  1598. * @param int $userid the user for who we return all ratings
  1599. * @return object
  1600. */
  1601. function forum_get_all_user_ratings($userid, $discussions) {
  1602. global $CFG;
  1603. foreach ($discussions as $discussion) {
  1604. if (!isset($discussionsid)){
  1605. $discussionsid = $discussion->id;
  1606. }
  1607. else {
  1608. $discussionsid .= ",".$discussion->id;
  1609. }
  1610. }
  1611. $sql = "SELECT r.id, r.userid, p.id AS postid, r.rating
  1612. FROM {$CFG->prefix}forum_ratings r,
  1613. {$CFG->prefix}forum_posts p
  1614. WHERE r.post = p.id AND p.userid = $userid";
  1615. //postgres compability
  1616. if (!isset($discussionsid)) {
  1617. $sql .=" AND p.discussion IN (".$discussionsid.")";
  1618. }
  1619. $sql .=" ORDER BY p.id ASC";
  1620. return get_records_sql($sql);
  1621. }
  1622. /**
  1623. * Returns a list of ratings for a particular post - sorted.
  1624. * @param int $postid
  1625. * @param string $sort
  1626. * @return array of ratings or false
  1627. */
  1628. function forum_get_ratings($postid, $sort="u.firstname ASC") {
  1629. global $CFG;
  1630. return get_records_sql("SELECT u.*, r.rating, r.time
  1631. FROM {$CFG->prefix}forum_ratings r,
  1632. {$CFG->prefix}user u
  1633. WHERE r.post = '$postid'
  1634. AND r.userid = u.id
  1635. ORDER BY $sort");
  1636. }
  1637. /**
  1638. * Returns a list of all new posts that have not been mailed yet
  1639. * @param int $starttime - posts created after this time
  1640. * @param int $endtime - posts created before this
  1641. * @param int $now - used for timed discussions only
  1642. */
  1643. function forum_get_unmailed_posts($starttime, $endtime, $now=null) {
  1644. global $CFG;
  1645. if (!empty($CFG->forum_enabletimedposts)) {
  1646. if (empty($now)) {
  1647. $now = time();
  1648. }
  1649. $timedsql = "AND (d.timestart < $now AND (d.timeend = 0 OR d.timeend > $now))";
  1650. } else {
  1651. $timedsql = "";
  1652. }
  1653. return get_records_sql("SELECT p.*, d.course, d.forum
  1654. FROM {$CFG->prefix}forum_posts p
  1655. JOIN {$CFG->prefix}forum_discussions d ON d.id = p.discussion
  1656. WHERE p.mailed = 0
  1657. AND p.created >= $starttime
  1658. AND (p.created < $endtime OR p.mailnow = 1)
  1659. $timedsql
  1660. ORDER BY p.modified ASC");
  1661. }
  1662. /**
  1663. * Marks posts before a certain time as being mailed already
  1664. */
  1665. function forum_mark_old_posts_as_mailed($endtime, $now=null) {
  1666. global $CFG;
  1667. if (empty($now)) {
  1668. $now = time();
  1669. }
  1670. if (empty($CFG->forum_enabletimedposts)) {
  1671. return execute_sql("UPDATE {$CFG->prefix}forum_posts
  1672. SET mailed = '1'
  1673. WHERE (created < $endtime OR mailnow = 1)
  1674. AND mailed = 0", false);
  1675. } else {
  1676. return execute_sql("UPDATE {$CFG->prefix}forum_posts
  1677. SET mailed = '1'
  1678. WHERE discussion NOT IN (SELECT d.id
  1679. FROM {$CFG->prefix}forum_discussions d
  1680. WHERE d.timestart > $now)
  1681. AND (created < $endtime OR mailnow = 1)
  1682. AND mailed = 0", false);
  1683. }
  1684. }
  1685. /**
  1686. * Get all the posts for a user in a forum suitable for forum_print_post
  1687. */
  1688. function forum_get_user_posts($forumid, $userid) {
  1689. global $CFG;
  1690. $timedsql = "";
  1691. if (!empty($CFG->forum_enabletimedposts)) {
  1692. $cm = get_coursemodule_from_instance('forum', $forumid);
  1693. if (!has_capability('mod/forum:viewhiddentimedposts' , get_context_instance(CONTEXT_MODULE, $cm->id))) {
  1694. $now = time();
  1695. $timedsql = "AND (d.timestart < $now AND (d.timeend = 0 OR d.timeend > $now))";
  1696. }
  1697. }
  1698. return get_records_sql("SELECT p.*, d.forum, u.firstname, u.lastname, u.email, u.picture, u.imagealt
  1699. FROM {$CFG->prefix}forum f
  1700. JOIN {$CFG->prefix}forum_discussions d ON d.forum = f.id
  1701. JOIN {$CFG->prefix}forum_posts p ON p.discussion = d.id
  1702. JOIN {$CFG->prefix}user u ON u.id = p.userid
  1703. WHERE f.id = $forumid
  1704. AND p.userid = $userid
  1705. $timedsql
  1706. ORDER BY p.modified ASC");
  1707. }
  1708. /**
  1709. * Get all the discussions user participated in
  1710. * @param int $forumid
  1711. * @param int $userid
  1712. * @return array or false
  1713. */
  1714. function forum_get_user_involved_discussions($forumid, $userid) {
  1715. global $CFG;
  1716. $timedsql = "";
  1717. if (!empty($CFG->forum_enabletimedposts)) {
  1718. $cm = get_coursemodule_from_instance('forum', $forumid);
  1719. if (!has_capability('mod/forum:viewhiddentimedposts' , get_context_instance(CONTEXT_MODULE, $cm->id))) {
  1720. $now = time();
  1721. $timedsql = "AND (d.timestart < $now AND (d.timeend = 0 OR d.timeend > $now))";
  1722. }
  1723. }
  1724. return get_records_sql("SELECT DISTINCT d.*
  1725. FROM {$CFG->prefix}forum f
  1726. JOIN {$CFG->prefix}forum_discussions d ON d.forum = f.id
  1727. JOIN {$CFG->prefix}forum_posts p ON p.discussion = d.id
  1728. WHERE f.id = $forumid
  1729. AND p.userid = $userid
  1730. $timedsql");
  1731. }
  1732. /**
  1733. * Get all the posts for a user in a forum suitable for forum_print_post
  1734. * @param int $forumid
  1735. * @param int $userid
  1736. * @return array of counts or false
  1737. */
  1738. function forum_count_user_posts($forumid, $userid) {
  1739. global $CFG;
  1740. $timedsql = "";
  1741. if (!empty($CFG->forum_enabletimedposts)) {
  1742. $cm = get_coursemodule_from_instance('forum', $forumid);
  1743. if (!has_capability('mod/forum:viewhiddentimedposts' , get_context_instance(CONTEXT_MODULE, $cm->id))) {
  1744. $now = time();
  1745. $timedsql = "AND (d.timestart < $now AND (d.timeend = 0 OR d.timeend > $now))";
  1746. }
  1747. }
  1748. return get_record_sql("SELECT COUNT(p.id) AS postcount, MAX(p.modified) AS lastpost
  1749. FROM {$CFG->prefix}forum f
  1750. JOIN {$CFG->prefix}forum_discussions d ON d.forum = f.id
  1751. JOIN {$CFG->prefix}forum_posts p ON p.discussion = d.id
  1752. JOIN {$CFG->prefix}user u ON u.id = p.userid
  1753. WHERE f.id = $forumid
  1754. AND p.userid = $userid
  1755. $timedsql");
  1756. }
  1757. /**
  1758. * Given a log entry, return the forum post details for it.
  1759. */
  1760. function forum_get_post_from_log($log) {
  1761. global $CFG;
  1762. if ($log->action == "add post") {
  1763. return get_record_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid,
  1764. u.firstname, u.lastname, u.email, u.picture
  1765. FROM {$CFG->prefix}forum_discussions d,
  1766. {$CFG->prefix}forum_posts p,
  1767. {$CFG->prefix}forum f,
  1768. {$CFG->prefix}user u
  1769. WHERE p.id = '$log->info'
  1770. AND d.id = p.discussion
  1771. AND p.userid = u.id
  1772. AND u.deleted <> '1'
  1773. AND f.id = d.forum");
  1774. } else if ($log->action == "add discussion") {
  1775. return get_record_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid,
  1776. u.firstname, u.lastname, u.email, u.picture
  1777. FROM {$CFG->prefix}forum_discussions d,
  1778. {$CFG->prefix}forum_posts p,
  1779. {$CFG->prefix}forum f,
  1780. {$CFG->prefix}user u
  1781. WHERE d.id = '$log->info'
  1782. AND d.firstpost = p.id
  1783. AND p.userid = u.id
  1784. AND u.deleted <> '1'
  1785. AND f.id = d.forum");
  1786. }
  1787. return NULL;
  1788. }
  1789. /**
  1790. * Given a discussion id, return the first post from the discussion
  1791. */
  1792. function forum_get_firstpost_from_discussion($discussionid) {
  1793. global $CFG;
  1794. return get_record_sql("SELECT p.*
  1795. FROM {$CFG->prefix}forum_discussions d,
  1796. {$CFG->prefix}forum_posts p
  1797. WHERE d.id = '$discussionid'
  1798. AND d.firstpost = p.id ");
  1799. }
  1800. /**
  1801. * Returns an array of counts of replies to each discussion
  1802. */
  1803. function forum_count_discussion_replies($forumid, $forumsort="", $limit=-1, $page=-1, $perpage=0) {
  1804. global $CFG;
  1805. if ($limit > 0) {
  1806. $limitfrom = 0;
  1807. $limitnum = $limit;
  1808. } else if ($page != -1) {
  1809. $limitfrom = $page*$perpage;
  1810. $limitnum = $perpage;
  1811. } else {
  1812. $limitfrom = 0;
  1813. $limitnum = 0;
  1814. }
  1815. if ($forumsort == "") {
  1816. $orderby = "";
  1817. $groupby = "";
  1818. } else {
  1819. $orderby = "ORDER BY $forumsort";
  1820. $groupby = ", ".strtolower($forumsort);
  1821. $groupby = str_replace('desc', '', $groupby);
  1822. $groupby = str_replace('asc', '', $groupby);
  1823. }
  1824. if (($limitfrom == 0 and $limitnum == 0) or $forumsort == "") {
  1825. $sql = "SELECT p.discussion, COUNT(p.id) AS replies, MAX(p.id) AS lastpostid
  1826. FROM {$CFG->prefix}forum_posts p
  1827. JOIN {$CFG->prefix}forum_discussions d ON p.discussion = d.id
  1828. WHERE p.parent > 0 AND d.forum = $forumid
  1829. GROUP BY p.discussion";
  1830. return get_records_sql($sql);
  1831. } else {
  1832. $sql = "SELECT p.discussion, (COUNT(p.id) - 1) AS replies, MAX(p.id) AS lastpostid
  1833. FROM {$CFG->prefix}forum_posts p
  1834. JOIN {$CFG->prefix}forum_discussions d ON p.discussion = d.id
  1835. WHERE d.forum = $forumid
  1836. GROUP BY p.discussion $groupby
  1837. $orderby";
  1838. return get_records_sql("SELECT * FROM ($sql) sq", $limitfrom, $limitnum);
  1839. }
  1840. }
  1841. function forum_count_discussions($forum, $cm, $course) {
  1842. global $CFG, $USER;
  1843. static $cache = array();
  1844. $now = round(time(), -2); // db cache friendliness
  1845. if (!isset($cache[$course->id])) {
  1846. if (!empty($CFG->forum_enabletimedposts)) {
  1847. $timedsql = "AND d.timestart < $now AND (d.timeend = 0 OR d.timeend > $now)";
  1848. } else {
  1849. $timedsql = "";
  1850. }
  1851. $sql = "SELECT f.id, COUNT(d.id) as dcount
  1852. FROM {$CFG->prefix}forum f
  1853. JOIN {$CFG->prefix}forum_discussions d ON d.forum = f.id
  1854. WHERE f.course = $course->id
  1855. $timedsql
  1856. GROUP BY f.id";
  1857. if ($counts = get_records_sql($sql)) {
  1858. foreach ($counts as $count) {
  1859. $counts[$count->id] = $count->dcount;
  1860. }
  1861. $cache[$course->id] = $counts;
  1862. } else {
  1863. $cache[$course->id] = array();
  1864. }
  1865. }
  1866. if (empty($cache[$course->id][$forum->id])) {
  1867. return 0;
  1868. }
  1869. $groupmode = groups_get_activity_groupmode($cm, $course);
  1870. if ($groupmode != SEPARATEGROUPS) {
  1871. return $cache[$course->id][$forum->id];
  1872. }
  1873. if (has_capability('moodle/site:accessallgroups', get_context_instance(CONTEXT_MODULE, $cm->id))) {
  1874. return $cache[$course->id][$forum->id];
  1875. }
  1876. require_once($CFG->dirroot.'/course/lib.php');
  1877. $modinfo =& get_fast_modinfo($course);
  1878. if (is_null($modinfo->groups)) {
  1879. $modinfo->groups = groups_get_user_groups($course->id, $USER->id);
  1880. }
  1881. if (empty($CFG->enablegroupings)) {
  1882. $mygroups = $modinfo->groups[0];
  1883. } else {
  1884. $mygroups = $modinfo->groups[$cm->groupingid];
  1885. }
  1886. // add all groups posts
  1887. if (empty($mygroups)) {
  1888. $mygroups = array(-1=>-1);
  1889. } else {
  1890. $mygroups[-1] = -1;
  1891. }
  1892. $mygroups = implode(',', $mygroups);
  1893. if (!empty($CFG->forum_enabletimedposts)) {
  1894. $timedsql = "AND d.timestart < $now AND (d.timeend = 0 OR d.timeend > $now)";
  1895. } else {
  1896. $timedsql = "";
  1897. }
  1898. $sql = "SELECT COUNT(d.id)
  1899. FROM {$CFG->prefix}forum_discussions d
  1900. WHERE d.forum = $forum->id AND d.groupid IN ($mygroups)
  1901. $timedsql";
  1902. return get_field_sql($sql);
  1903. }
  1904. /**
  1905. * How many unrated posts are in the given discussion for a given user?
  1906. */
  1907. function forum_count_unrated_posts($discussionid, $userid) {
  1908. global $CFG;
  1909. if ($posts = get_record_sql("SELECT count(*) as num
  1910. FROM {$CFG->prefix}forum_posts
  1911. WHERE parent > 0
  1912. AND discussion = '$discussionid'
  1913. AND userid <> '$userid' ")) {
  1914. if ($rated = get_record_sql("SELECT count(*) as num
  1915. FROM {$CFG->prefix}forum_posts p,
  1916. {$CFG->prefix}forum_ratings r
  1917. WHERE p.discussion = '$discussionid'
  1918. AND p.id = r.post
  1919. AND r.userid = '$userid'")) {
  1920. $difference = $posts->num - $rated->num;
  1921. if ($difference > 0) {
  1922. return $difference;
  1923. } else {
  1924. return 0; // Just in case there was a counting error
  1925. }
  1926. } else {
  1927. return $posts->num;
  1928. }
  1929. } else {
  1930. return 0;
  1931. }
  1932. }
  1933. /**
  1934. * Get all discussions in a forum
  1935. */
  1936. function forum_get_discussions($cm, $forumsort="d.timemodified DESC", $fullpost=true, $unused=-1, $limit=-1, $userlastmodified=false, $page=-1, $perpage=0) {
  1937. global $CFG, $USER;
  1938. $timelimit = '';
  1939. $modcontext = null;
  1940. $now = round(time(), -2);
  1941. $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
  1942. if (!has_capability('mod/forum:viewdiscussion', $modcontext)) { /// User must have perms to view discussions
  1943. return array();
  1944. }
  1945. if (!empty($CFG->forum_enabletimedposts)) { /// Users must fulfill timed posts
  1946. if (!has_capability('mod/forum:viewhiddentimedposts', $modcontext)) {
  1947. $timelimit = " AND ((d.timestart <= $now AND (d.timeend = 0 OR d.timeend > $now))";
  1948. if (isloggedin()) {
  1949. $timelimit .= " OR d.userid = $USER->id";
  1950. }
  1951. $timelimit .= ")";
  1952. }
  1953. }
  1954. if ($limit > 0) {
  1955. $limitfrom = 0;
  1956. $limitnum = $limit;
  1957. } else if ($page != -1) {
  1958. $limitfrom = $page*$perpage;
  1959. $limitnum = $perpage;
  1960. } else {
  1961. $limitfrom = 0;
  1962. $limitnum = 0;
  1963. }
  1964. $groupmode = groups_get_activity_groupmode($cm);
  1965. $currentgroup = groups_get_activity_group($cm);
  1966. if ($groupmode) {
  1967. if (empty($modcontext)) {
  1968. $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
  1969. }
  1970. if ($groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $modcontext)) {
  1971. if ($currentgroup) {
  1972. $groupselect = "AND (d.groupid = $currentgroup OR d.groupid = -1)";
  1973. } else {
  1974. $groupselect = "";
  1975. }
  1976. } else {
  1977. //seprate groups without access all
  1978. if ($currentgroup) {
  1979. $groupselect = "AND (d.groupid = $currentgroup OR d.groupid = -1)";
  1980. } else {
  1981. $groupselect = "AND d.groupid = -1";
  1982. }
  1983. }
  1984. } else {
  1985. $groupselect = "";
  1986. }
  1987. if (empty($forumsort)) {
  1988. $forumsort = "d.timemodified DESC";
  1989. }
  1990. if (empty($fullpost)) {
  1991. $postdata = "p.id,p.subject,p.modified,p.discussion,p.userid";
  1992. } else {
  1993. $postdata = "p.*";
  1994. }
  1995. if (empty($userlastmodified)) { // We don't need to know this
  1996. $umfields = "";
  1997. $umtable = "";
  1998. } else {
  1999. $umfields = ", um.firstname AS umfirstname, um.lastname AS umlastname";
  2000. $umtable = " LEFT JOIN {$CFG->prefix}user um ON (d.usermodified = um.id)";
  2001. }
  2002. $sql = "SELECT $postdata, d.name, d.timemodified, d.usermodified, d.groupid, d.timestart, d.timeend,
  2003. u.firstname, u.lastname, u.email, u.picture, u.imagealt $umfields
  2004. FROM {$CFG->prefix}forum_discussions d
  2005. JOIN {$CFG->prefix}forum_posts p ON p.discussion = d.id
  2006. JOIN {$CFG->prefix}user u ON p.userid = u.id
  2007. $umtable
  2008. WHERE d.forum = {$cm->instance} AND p.parent = 0
  2009. $timelimit $groupselect
  2010. ORDER BY $forumsort";
  2011. return get_records_sql($sql, $limitfrom, $limitnum);
  2012. }
  2013. function forum_get_discussions_unread($cm) {
  2014. global $CFG, $USER;
  2015. $now = round(time(), -2);
  2016. $groupmode = groups_get_activity_groupmode($cm);
  2017. $currentgroup = groups_get_activity_group($cm);
  2018. if ($groupmode) {
  2019. $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
  2020. if ($groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $modcontext)) {
  2021. if ($currentgroup) {
  2022. $groupselect = "AND (d.groupid = $currentgroup OR d.groupid = -1)";
  2023. } else {
  2024. $groupselect = "";
  2025. }
  2026. } else {
  2027. //seprate groups without access all
  2028. if ($currentgroup) {
  2029. $groupselect = "AND (d.groupid = $currentgroup OR d.groupid = -1)";
  2030. } else {
  2031. $groupselect = "AND d.groupid = -1";
  2032. }
  2033. }
  2034. } else {
  2035. $groupselect = "";
  2036. }
  2037. $cutoffdate = $now - ($CFG->forum_oldpostdays*24*60*60);
  2038. if (!empty($CFG->forum_enabletimedposts)) {
  2039. $timedsql = "AND d.timestart < $now AND (d.timeend = 0 OR d.timeend > $now)";
  2040. } else {
  2041. $timedsql = "";
  2042. }
  2043. $sql = "SELECT d.id, COUNT(p.id) AS unread
  2044. FROM {$CFG->prefix}forum_discussions d
  2045. JOIN {$CFG->prefix}forum_posts p ON p.discussion = d.id
  2046. LEFT JOIN {$CFG->prefix}forum_read r ON (r.postid = p.id AND r.userid = $USER->id)
  2047. WHERE d.forum = {$cm->instance}
  2048. AND p.modified >= $cutoffdate AND r.id is NULL
  2049. $timedsql
  2050. $groupselect
  2051. GROUP BY d.id";
  2052. if ($unreads = get_records_sql($sql)) {
  2053. foreach ($unreads as $unread) {
  2054. $unreads[$unread->id] = $unread->unread;
  2055. }
  2056. return $unreads;
  2057. } else {
  2058. return array();
  2059. }
  2060. }
  2061. function forum_get_discussions_count($cm) {
  2062. global $CFG, $USER;
  2063. $now = round(time(), -2);
  2064. $groupmode = groups_get_activity_groupmode($cm);
  2065. $currentgroup = groups_get_activity_group($cm);
  2066. if ($groupmode) {
  2067. $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
  2068. if ($groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $modcontext)) {
  2069. if ($currentgroup) {
  2070. $groupselect = "AND (d.groupid = $currentgroup OR d.groupid = -1)";
  2071. } else {
  2072. $groupselect = "";
  2073. }
  2074. } else {
  2075. //seprate groups without access all
  2076. if ($currentgroup) {
  2077. $groupselect = "AND (d.groupid = $currentgroup OR d.groupid = -1)";
  2078. } else {
  2079. $groupselect = "AND d.groupid = -1";
  2080. }
  2081. }
  2082. } else {
  2083. $groupselect = "";
  2084. }
  2085. $cutoffdate = $now - ($CFG->forum_oldpostdays*24*60*60);
  2086. $timelimit = "";
  2087. if (!empty($CFG->forum_enabletimedposts)) {
  2088. $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
  2089. if (!has_capability('mod/forum:viewhiddentimedposts', $modcontext)) {
  2090. $timelimit = " AND ((d.timestart <= $now AND (d.timeend = 0 OR d.timeend > $now))";
  2091. if (isloggedin()) {
  2092. $timelimit .= " OR d.userid = $USER->id";
  2093. }
  2094. $timelimit .= ")";
  2095. }
  2096. }
  2097. $sql = "SELECT COUNT(d.id)
  2098. FROM {$CFG->prefix}forum_discussions d
  2099. JOIN {$CFG->prefix}forum_posts p ON p.discussion = d.id
  2100. WHERE d.forum = {$cm->instance} AND p.parent = 0
  2101. $timelimit $groupselect";
  2102. return get_field_sql($sql);
  2103. }
  2104. /**
  2105. * Get all discussions started by a particular user in a course (or group)
  2106. * This function no longer used ...
  2107. */
  2108. function forum_get_user_discussions($courseid, $userid, $groupid=0) {
  2109. global $CFG;
  2110. if ($groupid) {
  2111. $groupselect = " AND d.groupid = '$groupid' ";
  2112. } else {
  2113. $groupselect = "";
  2114. }
  2115. return get_records_sql("SELECT p.*, d.groupid, u.firstname, u.lastname, u.email, u.picture, u.imagealt,
  2116. f.type as forumtype, f.name as forumname, f.id as forumid
  2117. FROM {$CFG->prefix}forum_discussions d,
  2118. {$CFG->prefix}forum_posts p,
  2119. {$CFG->prefix}user u,
  2120. {$CFG->prefix}forum f
  2121. WHERE d.course = '$courseid'
  2122. AND p.discussion = d.id
  2123. AND p.parent = 0
  2124. AND p.userid = u.id
  2125. AND u.id = '$userid'
  2126. AND d.forum = f.id $groupselect
  2127. ORDER BY p.created DESC");
  2128. }
  2129. /**
  2130. * Get the list of potential subscribers to a forum.
  2131. *
  2132. * @param object $forumcontext the forum context.
  2133. * @param integer $groupid the id of a group, or 0 for all groups.
  2134. * @param string $fields the list of fields to return for each user. As for get_users_by_capability.
  2135. * @param string $sort sort order. As for get_users_by_capability.
  2136. * @return array list of users.
  2137. */
  2138. function forum_get_potential_subscribers($forumcontext, $groupid, $fields, $sort) {
  2139. return get_users_by_capability($forumcontext, 'mod/forum:initialsubscriptions', $fields, $sort, '', '', $groupid, '', false, true);
  2140. }
  2141. /**
  2142. * Returns list of user objects that are subscribed to this forum
  2143. *
  2144. * @param object $course the course
  2145. * @param forum $forum the forum
  2146. * @param integer $groupid group id, or 0 for all.
  2147. * @param object $context the forum context, to save re-fetching it where possible.
  2148. * @return array list of users.
  2149. */
  2150. function forum_subscribed_users($course, $forum, $groupid=0, $context = NULL) {
  2151. global $CFG;
  2152. if ($groupid) {
  2153. $grouptables = ", {$CFG->prefix}groups_members gm ";
  2154. $groupselect = "AND gm.groupid = $groupid AND u.id = gm.userid";
  2155. } else {
  2156. $grouptables = '';
  2157. $groupselect = '';
  2158. }
  2159. if (forum_is_forcesubscribed($forum)) {
  2160. if (empty($context)) {
  2161. $cm = get_coursemodule_from_instance('forum', $forum->id, $course->id);
  2162. $context = get_context_instance(CONTEXT_MODULE, $cm->id);
  2163. }
  2164. $sort = "u.email ASC";
  2165. $fields ="u.id, u.username, u.firstname, u.lastname, u.maildisplay, u.mailformat, u.maildigest, u.emailstop, u.imagealt,
  2166. u.email, u.city, u.country, u.lastaccess, u.lastlogin, u.picture, u.timezone, u.theme, u.lang, u.trackforums, u.mnethostid";
  2167. $results = forum_get_potential_subscribers($context, $groupid, $fields, $sort);
  2168. } else {
  2169. $results = get_records_sql("SELECT u.id, u.username, u.firstname, u.lastname, u.maildisplay, u.mailformat, u.maildigest, u.emailstop, u.imagealt,
  2170. u.email, u.city, u.country, u.lastaccess, u.lastlogin, u.picture, u.timezone, u.theme, u.lang, u.trackforums, u.mnethostid
  2171. FROM {$CFG->prefix}user u,
  2172. {$CFG->prefix}forum_subscriptions s $grouptables
  2173. WHERE s.forum = '$forum->id'
  2174. AND s.userid = u.id
  2175. AND u.deleted = 0 $groupselect
  2176. ORDER BY u.email ASC");
  2177. }
  2178. static $guestid = null;
  2179. if (is_null($guestid)) {
  2180. if ($guest = guest_user()) {
  2181. $guestid = $guest->id;
  2182. } else {
  2183. $guestid = 0;
  2184. }
  2185. }
  2186. // Guest user should never be subscribed to a forum.
  2187. unset($results[$guestid]);
  2188. return $results;
  2189. }
  2190. // OTHER FUNCTIONS ///////////////////////////////////////////////////////////
  2191. function forum_get_course_forum($courseid, $type) {
  2192. // How to set up special 1-per-course forums
  2193. global $CFG;
  2194. if ($forums = get_records_select("forum", "course = '$courseid' AND type = '$type'", "id ASC")) {
  2195. // There should always only be ONE, but with the right combination of
  2196. // errors there might be more. In this case, just return the oldest one (lowest ID).
  2197. foreach ($forums as $forum) {
  2198. return $forum; // ie the first one
  2199. }
  2200. }
  2201. // Doesn't exist, so create one now.
  2202. $forum->course = $courseid;
  2203. $forum->type = "$type";
  2204. switch ($forum->type) {
  2205. case "news":
  2206. $forum->name = addslashes(get_string("namenews", "forum"));
  2207. $forum->intro = addslashes(get_string("intronews", "forum"));
  2208. $forum->forcesubscribe = FORUM_FORCESUBSCRIBE;
  2209. $forum->assessed = 0;
  2210. if ($courseid == SITEID) {
  2211. $forum->name = get_string("sitenews");
  2212. $forum->forcesubscribe = 0;
  2213. }
  2214. break;
  2215. case "social":
  2216. $forum->name = addslashes(get_string("namesocial", "forum"));
  2217. $forum->intro = addslashes(get_string("introsocial", "forum"));
  2218. $forum->assessed = 0;
  2219. $forum->forcesubscribe = 0;
  2220. break;
  2221. default:
  2222. notify("That forum type doesn't exist!");
  2223. return false;
  2224. break;
  2225. }
  2226. $forum->timemodified = time();
  2227. $forum->id = insert_record("forum", $forum);
  2228. if (! $module = get_record("modules", "name", "forum")) {
  2229. notify("Could not find forum module!!");
  2230. return false;
  2231. }
  2232. $mod = new object();
  2233. $mod->course = $courseid;
  2234. $mod->module = $module->id;
  2235. $mod->instance = $forum->id;
  2236. $mod->section = 0;
  2237. if (! $mod->coursemodule = add_course_module($mod) ) { // assumes course/lib.php is loaded
  2238. notify("Could not add a new course module to the course '" . format_string($course->fullname) . "'");
  2239. return false;
  2240. }
  2241. if (! $sectionid = add_mod_to_section($mod) ) { // assumes course/lib.php is loaded
  2242. notify("Could not add the new course module to that section");
  2243. return false;
  2244. }
  2245. if (! set_field("course_modules", "section", $sectionid, "id", $mod->coursemodule)) {
  2246. notify("Could not update the course module with the correct section");
  2247. return false;
  2248. }
  2249. include_once("$CFG->dirroot/course/lib.php");
  2250. rebuild_course_cache($courseid);
  2251. return get_record("forum", "id", "$forum->id");
  2252. }
  2253. /**
  2254. * Given the data about a posting, builds up the HTML to display it and
  2255. * returns the HTML in a string. This is designed for sending via HTML email.
  2256. */
  2257. function forum_make_mail_post($course, $forum, $discussion, $post, $userfrom, $userto,
  2258. $ownpost=false, $reply=false, $link=false, $rate=false, $footer="") {
  2259. global $CFG;
  2260. if (!isset($userto->viewfullnames[$forum->id])) {
  2261. if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $course->id)) {
  2262. error('Course Module ID was incorrect');
  2263. }
  2264. $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
  2265. $viewfullnames = has_capability('moodle/site:viewfullnames', $modcontext, $userto->id);
  2266. } else {
  2267. $viewfullnames = $userto->viewfullnames[$forum->id];
  2268. }
  2269. // format the post body
  2270. $options = new object();
  2271. $options->para = true;
  2272. $formattedtext = format_text(trusttext_strip($post->message), $post->format, $options, $course->id);
  2273. $output = '<table border="0" cellpadding="3" cellspacing="0" class="forumpost">';
  2274. $output .= '<tr class="header"><td width="35" valign="top" class="picture left">';
  2275. $output .= print_user_picture($userfrom, $course->id, $userfrom->picture, false, true);
  2276. $output .= '</td>';
  2277. if ($post->parent) {
  2278. $output .= '<td class="topic">';
  2279. } else {
  2280. $output .= '<td class="topic starter">';
  2281. }
  2282. $output .= '<div class="subject">'.format_string($post->subject).'</div>';
  2283. $fullname = fullname($userfrom, $viewfullnames);
  2284. $by = new object();
  2285. $by->name = '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$userfrom->id.'&amp;course='.$course->id.'">'.$fullname.'</a>';
  2286. $by->date = userdate($post->modified, '', $userto->timezone);
  2287. $output .= '<div class="author">'.get_string('bynameondate', 'forum', $by).'</div>';
  2288. $output .= '</td></tr>';
  2289. $output .= '<tr><td class="left side" valign="top">';
  2290. if (isset($userfrom->groups)) {
  2291. $groups = $userfrom->groups[$forum->id];
  2292. } else {
  2293. if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $course->id)) {
  2294. error('Course Module ID was incorrect');
  2295. }
  2296. $group = groups_get_all_groups($course->id, $userfrom->id, $cm->groupingid);
  2297. }
  2298. if ($groups) {
  2299. $output .= print_group_picture($groups, $course->id, false, true, true);
  2300. } else {
  2301. $output .= '&nbsp;';
  2302. }
  2303. $output .= '</td><td class="content">';
  2304. if ($post->attachment) {
  2305. $post->course = $course->id;
  2306. $output .= '<div class="attachments">';
  2307. $output .= forum_print_attachments($post, 'html');
  2308. $output .= "</div>";
  2309. }
  2310. $output .= $formattedtext;
  2311. // Commands
  2312. $commands = array();
  2313. if ($post->parent) {
  2314. $commands[] = '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.
  2315. $post->discussion.'&amp;parent='.$post->parent.'">'.get_string('parent', 'forum').'</a>';
  2316. }
  2317. if ($reply) {
  2318. $commands[] = '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/post.php?reply='.$post->id.'">'.
  2319. get_string('reply', 'forum').'</a>';
  2320. }
  2321. $output .= '<div class="commands">';
  2322. $output .= implode(' | ', $commands);
  2323. $output .= '</div>';
  2324. // Context link to post if required
  2325. if ($link) {
  2326. $output .= '<div class="link">';
  2327. $output .= '<a target="_blank" href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'#p'.$post->id.'">'.
  2328. get_string('postincontext', 'forum').'</a>';
  2329. $output .= '</div>';
  2330. }
  2331. if ($footer) {
  2332. $output .= '<div class="footer">'.$footer.'</div>';
  2333. }
  2334. $output .= '</td></tr></table>'."\n\n";
  2335. return $output;
  2336. }
  2337. /**
  2338. * Print a forum post
  2339. *
  2340. * @param object $post The post to print.
  2341. * @param integer $courseid The course this post belongs to.
  2342. * @param boolean $ownpost Whether this post belongs to the current user.
  2343. * @param boolean $reply Whether to print a 'reply' link at the bottom of the message.
  2344. * @param boolean $link Just print a shortened version of the post as a link to the full post.
  2345. * @param object $ratings -- I don't really know --
  2346. * @param string $footer Extra stuff to print after the message.
  2347. * @param string $highlight Space-separated list of terms to highlight.
  2348. * @param int $post_read true, false or -99. If we already know whether this user
  2349. * has read this post, pass that in, otherwise, pass in -99, and this
  2350. * function will work it out.
  2351. * @param boolean $dummyifcantsee When forum_user_can_see_post says that
  2352. * the current user can't see this post, if this argument is true
  2353. * (the default) then print a dummy 'you can't see this post' post.
  2354. * If false, don't output anything at all.
  2355. */
  2356. function forum_print_post($post, $discussion, $forum, &$cm, $course, $ownpost=false, $reply=false, $link=false,
  2357. $ratings=NULL, $footer="", $highlight="", $post_read=null, $dummyifcantsee=true, $istracked=null) {
  2358. global $USER, $CFG;
  2359. static $stredit, $strdelete, $strreply, $strparent, $strprune;
  2360. static $strpruneheading, $displaymode;
  2361. static $strmarkread, $strmarkunread;
  2362. $post->course = $course->id;
  2363. $post->forum = $forum->id;
  2364. // caching
  2365. if (!isset($cm->cache)) {
  2366. $cm->cache = new object();
  2367. }
  2368. if (!isset($cm->cache->caps)) {
  2369. $cm->cache->caps = array();
  2370. $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
  2371. $cm->cache->caps['mod/forum:viewdiscussion'] = has_capability('mod/forum:viewdiscussion', $modcontext);
  2372. $cm->cache->caps['moodle/site:viewfullnames'] = has_capability('moodle/site:viewfullnames', $modcontext);
  2373. $cm->cache->caps['mod/forum:editanypost'] = has_capability('mod/forum:editanypost', $modcontext);
  2374. $cm->cache->caps['mod/forum:splitdiscussions'] = has_capability('mod/forum:splitdiscussions', $modcontext);
  2375. $cm->cache->caps['mod/forum:deleteownpost'] = has_capability('mod/forum:deleteownpost', $modcontext);
  2376. $cm->cache->caps['mod/forum:deleteanypost'] = has_capability('mod/forum:deleteanypost', $modcontext);
  2377. $cm->cache->caps['mod/forum:viewanyrating'] = has_capability('mod/forum:viewanyrating', $modcontext);
  2378. }
  2379. if (!isset($cm->uservisible)) {
  2380. $cm->uservisible = coursemodule_visible_for_user($cm);
  2381. }
  2382. if (!forum_user_can_see_post($forum, $discussion, $post, NULL, $cm)) {
  2383. if (!$dummyifcantsee) {
  2384. return;
  2385. }
  2386. echo '<a id="p'.$post->id.'"></a>';
  2387. echo '<table cellspacing="0" class="forumpost">';
  2388. echo '<tr class="header"><td class="picture left">';
  2389. // print_user_picture($post->userid, $courseid, $post->picture);
  2390. echo '</td>';
  2391. if ($post->parent) {
  2392. echo '<td class="topic">';
  2393. } else {
  2394. echo '<td class="topic starter">';
  2395. }
  2396. echo '<div class="subject">'.get_string('forumsubjecthidden','forum').'</div>';
  2397. echo '<div class="author">';
  2398. print_string('forumauthorhidden','forum');
  2399. echo '</div></td></tr>';
  2400. echo '<tr><td class="left side">';
  2401. echo '&nbsp;';
  2402. // Actual content
  2403. echo '</td><td class="content">'."\n";
  2404. echo get_string('forumbodyhidden','forum');
  2405. echo '</td></tr></table>';
  2406. return;
  2407. }
  2408. if (empty($stredit)) {
  2409. $stredit = get_string('edit', 'forum');
  2410. $strdelete = get_string('delete', 'forum');
  2411. $strreply = get_string('reply', 'forum');
  2412. $strparent = get_string('parent', 'forum');
  2413. $strpruneheading = get_string('pruneheading', 'forum');
  2414. $strprune = get_string('prune', 'forum');
  2415. $displaymode = get_user_preferences('forum_displaymode', $CFG->forum_displaymode);
  2416. $strmarkread = get_string('markread', 'forum');
  2417. $strmarkunread = get_string('markunread', 'forum');
  2418. }
  2419. $read_style = '';
  2420. // ignore trackign status if not tracked or tracked param missing
  2421. if ($istracked) {
  2422. if (is_null($post_read)) {
  2423. debugging('fetching post_read info');
  2424. $post_read = forum_tp_is_post_read($USER->id, $post);
  2425. }
  2426. if ($post_read) {
  2427. $read_style = ' read';
  2428. } else {
  2429. $read_style = ' unread';
  2430. echo '<a name="unread"></a>';
  2431. }
  2432. }
  2433. echo '<a id="p'.$post->id.'"></a>';
  2434. echo '<table cellspacing="0" class="forumpost'.$read_style.'">';
  2435. // Picture
  2436. $postuser = new object();
  2437. $postuser->id = $post->userid;
  2438. $postuser->firstname = $post->firstname;
  2439. $postuser->lastname = $post->lastname;
  2440. $postuser->imagealt = $post->imagealt;
  2441. $postuser->picture = $post->picture;
  2442. echo '<tr class="header"><td class="picture left">';
  2443. print_user_picture($postuser, $course->id);
  2444. echo '</td>';
  2445. if ($post->parent) {
  2446. echo '<td class="topic">';
  2447. } else {
  2448. echo '<td class="topic starter">';
  2449. }
  2450. if (!empty($post->subjectnoformat)) {
  2451. echo '<div class="subject">'.$post->subject.'</div>';
  2452. } else {
  2453. echo '<div class="subject">'.format_string($post->subject).'</div>';
  2454. }
  2455. echo '<div class="author">';
  2456. $fullname = fullname($postuser, $cm->cache->caps['moodle/site:viewfullnames']);
  2457. $by = new object();
  2458. $by->name = '<a href="'.$CFG->wwwroot.'/user/view.php?id='.
  2459. $post->userid.'&amp;course='.$course->id.'">'.$fullname.'</a>';
  2460. $by->date = userdate($post->modified);
  2461. print_string('bynameondate', 'forum', $by);
  2462. echo '</div></td></tr>';
  2463. echo '<tr><td class="left side">';
  2464. if (isset($cm->cache->usersgroups)) {
  2465. $groups = array();
  2466. if (isset($cm->cache->usersgroups[$post->userid])) {
  2467. foreach ($cm->cache->usersgroups[$post->userid] as $gid) {
  2468. $groups[$gid] = $cm->cache->groups[$gid];
  2469. }
  2470. }
  2471. } else {
  2472. $groups = groups_get_all_groups($course->id, $post->userid, $cm->groupingid);
  2473. }
  2474. if ($groups) {
  2475. print_group_picture($groups, $course->id, false, false, true);
  2476. } else {
  2477. echo '&nbsp;';
  2478. }
  2479. // Actual content
  2480. echo '</td><td class="content">'."\n";
  2481. if ($post->attachment) {
  2482. echo '<div class="attachments">';
  2483. $attachedimages = forum_print_attachments($post);
  2484. echo '</div>';
  2485. } else {
  2486. $attachedimages = '';
  2487. }
  2488. $options = new object();
  2489. $options->para = false;
  2490. $options->trusttext = true;
  2491. if ($link and (strlen(strip_tags($post->message)) > $CFG->forum_longpost)) {
  2492. // Print shortened version
  2493. echo format_text(forum_shorten_post($post->message), $post->format, $options, $course->id);
  2494. $numwords = count_words(strip_tags($post->message));
  2495. echo '<div class="posting"><a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'">';
  2496. echo get_string('readtherest', 'forum');
  2497. echo '</a> ('.get_string('numwords', '', $numwords).')...</div>';
  2498. } else {
  2499. // Print whole message
  2500. echo '<div class="posting">';
  2501. if ($highlight) {
  2502. echo highlight($highlight, format_text($post->message, $post->format, $options, $course->id));
  2503. } else {
  2504. echo format_text($post->message, $post->format, $options, $course->id);
  2505. }
  2506. echo '</div>';
  2507. echo $attachedimages;
  2508. }
  2509. // Commands
  2510. $commands = array();
  2511. if ($istracked) {
  2512. // SPECIAL CASE: The front page can display a news item post to non-logged in users.
  2513. // Don't display the mark read / unread controls in this case.
  2514. if ($CFG->forum_usermarksread and isloggedin()) {
  2515. if ($post_read) {
  2516. $mcmd = '&amp;mark=unread&amp;postid='.$post->id;
  2517. $mtxt = $strmarkunread;
  2518. } else {
  2519. $mcmd = '&amp;mark=read&amp;postid='.$post->id;
  2520. $mtxt = $strmarkread;
  2521. }
  2522. if ($displaymode == FORUM_MODE_THREADED) {
  2523. $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.
  2524. $post->discussion.'&amp;parent='.$post->id.$mcmd.'">'.$mtxt.'</a>';
  2525. } else {
  2526. $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.
  2527. $post->discussion.$mcmd.'#p'.$post->id.'">'.$mtxt.'</a>';
  2528. }
  2529. }
  2530. }
  2531. if ($post->parent) { // Zoom in to the parent specifically
  2532. if ($displaymode == FORUM_MODE_THREADED) {
  2533. $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.
  2534. $post->discussion.'&amp;parent='.$post->parent.'">'.$strparent.'</a>';
  2535. } else {
  2536. $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.
  2537. $post->discussion.'#p'.$post->parent.'">'.$strparent.'</a>';
  2538. }
  2539. }
  2540. $age = time() - $post->created;
  2541. // Hack for allow to edit news posts those are not displayed yet until they are displayed
  2542. if (!$post->parent and $forum->type == 'news' and $discussion->timestart > time()) {
  2543. $age = 0;
  2544. }
  2545. $editanypost = $cm->cache->caps['mod/forum:editanypost'];
  2546. if ($ownpost or $editanypost) {
  2547. if (($age < $CFG->maxeditingtime) or $editanypost) {
  2548. $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/post.php?edit='.$post->id.'">'.$stredit.'</a>';
  2549. }
  2550. }
  2551. if ($cm->cache->caps['mod/forum:splitdiscussions']
  2552. && $post->parent && $forum->type != 'single') {
  2553. $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/post.php?prune='.$post->id.
  2554. '" title="'.$strpruneheading.'">'.$strprune.'</a>';
  2555. }
  2556. if (($ownpost and $age < $CFG->maxeditingtime
  2557. and $cm->cache->caps['mod/forum:deleteownpost'])
  2558. or $cm->cache->caps['mod/forum:deleteanypost']) {
  2559. $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/post.php?delete='.$post->id.'">'.$strdelete.'</a>';
  2560. }
  2561. if ($reply) {
  2562. $commands[] = '<a href="'.$CFG->wwwroot.'/mod/forum/post.php?reply='.$post->id.'">'.$strreply.'</a>';
  2563. }
  2564. echo '<div class="commands">';
  2565. echo implode(' | ', $commands);
  2566. echo '</div>';
  2567. // Ratings
  2568. $ratingsmenuused = false;
  2569. if (!empty($ratings) and isloggedin()) {
  2570. echo '<div class="ratings">';
  2571. $useratings = true;
  2572. if ($ratings->assesstimestart and $ratings->assesstimefinish) {
  2573. if ($post->created < $ratings->assesstimestart or $post->created > $ratings->assesstimefinish) {
  2574. $useratings = false;
  2575. }
  2576. }
  2577. if ($useratings) {
  2578. $mypost = ($USER->id == $post->userid);
  2579. $canviewallratings = $cm->cache->caps['mod/forum:viewanyrating'];
  2580. if (isset($cm->cache->ratings)) {
  2581. if (isset($cm->cache->ratings[$post->id])) {
  2582. $allratings = $cm->cache->ratings[$post->id];
  2583. } else {
  2584. $allratings = array(); // no reatings present yet
  2585. }
  2586. } else {
  2587. $allratings = NULL; // not preloaded
  2588. }
  2589. if (isset($cm->cache->myratings)) {
  2590. if (isset($cm->cache->myratings[$post->id])) {
  2591. $myrating = $cm->cache->myratings[$post->id];
  2592. } else {
  2593. $myrating = FORUM_UNSET_POST_RATING; // no reatings present yet
  2594. }
  2595. } else {
  2596. $myrating = NULL; // not preloaded
  2597. }
  2598. if ($canviewallratings and !$mypost) {
  2599. echo '<span class="forumpostratingtext">' .
  2600. forum_print_ratings($post->id, $ratings->scale, $forum->assessed, $canviewallratings, $allratings, true) .
  2601. '</span>';
  2602. if (!empty($ratings->allow)) {
  2603. echo '&nbsp;';
  2604. forum_print_rating_menu($post->id, $USER->id, $ratings->scale, $myrating);
  2605. $ratingsmenuused = true;
  2606. }
  2607. } else if ($mypost) {
  2608. echo '<span class="forumpostratingtext">' .
  2609. forum_print_ratings($post->id, $ratings->scale, $forum->assessed, true, $allratings, true) .
  2610. '</span>';
  2611. } else if (!empty($ratings->allow) ) {
  2612. forum_print_rating_menu($post->id, $USER->id, $ratings->scale, $myrating);
  2613. $ratingsmenuused = true;
  2614. }
  2615. }
  2616. echo '</div>';
  2617. }
  2618. // Link to post if required
  2619. if ($link) {
  2620. echo '<div class="link">';
  2621. if ($post->replies == 1) {
  2622. $replystring = get_string('repliesone', 'forum', $post->replies);
  2623. } else {
  2624. $replystring = get_string('repliesmany', 'forum', $post->replies);
  2625. }
  2626. echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'">'.
  2627. get_string('discussthistopic', 'forum').'</a>&nbsp;('.$replystring.')';
  2628. echo '</div>';
  2629. }
  2630. if ($footer) {
  2631. echo '<div class="footer">'.$footer.'</div>';
  2632. }
  2633. echo '</td></tr></table>'."\n\n";
  2634. if ($istracked && !$CFG->forum_usermarksread && !$post_read) {
  2635. forum_tp_mark_post_read($USER->id, $post, $forum->id);
  2636. }
  2637. return $ratingsmenuused;
  2638. }
  2639. /**
  2640. * This function prints the overview of a discussion in the forum listing.
  2641. * It needs some discussion information and some post information, these
  2642. * happen to be combined for efficiency in the $post parameter by the function
  2643. * that calls this one: forum_print_latest_discussions()
  2644. *
  2645. * @param object $post The post object (passed by reference for speed).
  2646. * @param object $forum The forum object.
  2647. * @param int $group Current group.
  2648. * @param string $datestring Format to use for the dates.
  2649. * @param boolean $cantrack Is tracking enabled for this forum.
  2650. * @param boolean $forumtracked Is the user tracking this forum.
  2651. * @param boolean $canviewparticipants True if user has the viewparticipants permission for this course
  2652. */
  2653. function forum_print_discussion_header(&$post, $forum, $group=-1, $datestring="",
  2654. $cantrack=true, $forumtracked=true, $canviewparticipants=true, $modcontext=NULL) {
  2655. global $USER, $CFG;
  2656. static $rowcount;
  2657. static $strmarkalldread;
  2658. if (empty($modcontext)) {
  2659. if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
  2660. error('Course Module ID was incorrect');
  2661. }
  2662. $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
  2663. }
  2664. if (!isset($rowcount)) {
  2665. $rowcount = 0;
  2666. $strmarkalldread = get_string('markalldread', 'forum');
  2667. } else {
  2668. $rowcount = ($rowcount + 1) % 2;
  2669. }
  2670. $post->subject = format_string($post->subject,true);
  2671. echo "\n\n";
  2672. echo '<tr class="discussion r'.$rowcount.'">';
  2673. // Topic
  2674. echo '<td class="topic starter">';
  2675. echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'">'.$post->subject.'</a>';
  2676. echo "</td>\n";
  2677. // Picture
  2678. $postuser = new object;
  2679. $postuser->id = $post->userid;
  2680. $postuser->firstname = $post->firstname;
  2681. $postuser->lastname = $post->lastname;
  2682. $postuser->imagealt = $post->imagealt;
  2683. $postuser->picture = $post->picture;
  2684. echo '<td class="picture">';
  2685. print_user_picture($postuser, $forum->course);
  2686. echo "</td>\n";
  2687. // User name
  2688. $fullname = fullname($post, has_capability('moodle/site:viewfullnames', $modcontext));
  2689. echo '<td class="author">';
  2690. echo '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$post->userid.'&amp;course='.$forum->course.'">'.$fullname.'</a>';
  2691. echo "</td>\n";
  2692. // Group picture
  2693. if ($group !== -1) { // Groups are active - group is a group data object or NULL
  2694. echo '<td class="picture group">';
  2695. if (!empty($group->picture) and empty($group->hidepicture)) {
  2696. print_group_picture($group, $forum->course, false, false, true);
  2697. } else if (isset($group->id)) {
  2698. if($canviewparticipants) {
  2699. echo '<a href="'.$CFG->wwwroot.'/user/index.php?id='.$forum->course.'&amp;group='.$group->id.'">'.$group->name.'</a>';
  2700. } else {
  2701. echo $group->name;
  2702. }
  2703. }
  2704. echo "</td>\n";
  2705. }
  2706. if (has_capability('mod/forum:viewdiscussion', $modcontext)) { // Show the column with replies
  2707. echo '<td class="replies">';
  2708. echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'">';
  2709. echo $post->replies.'</a>';
  2710. echo "</td>\n";
  2711. if ($cantrack) {
  2712. echo '<td class="replies">';
  2713. if ($forumtracked) {
  2714. if ($post->unread > 0) {
  2715. echo '<span class="unread">';
  2716. echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.'#unread">';
  2717. echo $post->unread;
  2718. echo '</a>';
  2719. echo '<a title="'.$strmarkalldread.'" href="'.$CFG->wwwroot.'/mod/forum/markposts.php?f='.
  2720. $forum->id.'&amp;d='.$post->discussion.'&amp;mark=read&amp;returnpage=view.php">' .
  2721. '<img src="'.$CFG->pixpath.'/t/clear.gif" class="iconsmall" alt="'.$strmarkalldread.'" /></a>';
  2722. echo '</span>';
  2723. } else {
  2724. echo '<span class="read">';
  2725. echo $post->unread;
  2726. echo '</span>';
  2727. }
  2728. } else {
  2729. echo '<span class="read">';
  2730. echo '-';
  2731. echo '</span>';
  2732. }
  2733. echo "</td>\n";
  2734. }
  2735. }
  2736. echo '<td class="lastpost">';
  2737. $usedate = (empty($post->timemodified)) ? $post->modified : $post->timemodified; // Just in case
  2738. $parenturl = (empty($post->lastpostid)) ? '' : '&amp;parent='.$post->lastpostid;
  2739. $usermodified = new object();
  2740. $usermodified->id = $post->usermodified;
  2741. $usermodified->firstname = $post->umfirstname;
  2742. $usermodified->lastname = $post->umlastname;
  2743. echo '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$post->usermodified.'&amp;course='.$forum->course.'">'.
  2744. fullname($usermodified).'</a><br />';
  2745. echo '<a href="'.$CFG->wwwroot.'/mod/forum/discuss.php?d='.$post->discussion.$parenturl.'">'.
  2746. userdate($usedate, $datestring).'</a>';
  2747. echo "</td>\n";
  2748. echo "</tr>\n\n";
  2749. }
  2750. /**
  2751. * Given a post object that we already know has a long message
  2752. * this function truncates the message nicely to the first
  2753. * sane place between $CFG->forum_longpost and $CFG->forum_shortpost
  2754. */
  2755. function forum_shorten_post($message) {
  2756. global $CFG;
  2757. $i = 0;
  2758. $tag = false;
  2759. $length = strlen($message);
  2760. $count = 0;
  2761. $stopzone = false;
  2762. $truncate = 0;
  2763. for ($i=0; $i<$length; $i++) {
  2764. $char = $message[$i];
  2765. switch ($char) {
  2766. case "<":
  2767. $tag = true;
  2768. break;
  2769. case ">":
  2770. $tag = false;
  2771. break;
  2772. default:
  2773. if (!$tag) {
  2774. if ($stopzone) {
  2775. if ($char == ".") {
  2776. $truncate = $i+1;
  2777. break 2;
  2778. }
  2779. }
  2780. $count++;
  2781. }
  2782. break;
  2783. }
  2784. if (!$stopzone) {
  2785. if ($count > $CFG->forum_shortpost) {
  2786. $stopzone = true;
  2787. }
  2788. }
  2789. }
  2790. if (!$truncate) {
  2791. $truncate = $i;
  2792. }
  2793. return substr($message, 0, $truncate);
  2794. }
  2795. /**
  2796. * Print the multiple ratings on a post given to the current user by others.
  2797. * Forumid prevents the double lookup of the forumid in discussion to determine the aggregate type
  2798. * Scale is an array of ratings
  2799. */
  2800. function forum_print_ratings($postid, $scale, $aggregatetype, $link=true, $ratings=null, $return=false) {
  2801. $strratings = '';
  2802. switch ($aggregatetype) {
  2803. case FORUM_AGGREGATE_AVG :
  2804. $agg = forum_get_ratings_mean($postid, $scale, $ratings);
  2805. $strratings = get_string("aggregateavg", "forum");
  2806. break;
  2807. case FORUM_AGGREGATE_COUNT :
  2808. $agg = forum_get_ratings_count($postid, $scale, $ratings);
  2809. $strratings = get_string("aggregatecount", "forum");
  2810. break;
  2811. case FORUM_AGGREGATE_MAX :
  2812. $agg = forum_get_ratings_max($postid, $scale, $ratings);
  2813. $strratings = get_string("aggregatemax", "forum");
  2814. break;
  2815. case FORUM_AGGREGATE_MIN :
  2816. $agg = forum_get_ratings_min($postid, $scale, $ratings);
  2817. $strratings = get_string("aggregatemin", "forum");
  2818. break;
  2819. case FORUM_AGGREGATE_SUM :
  2820. $agg = forum_get_ratings_sum($postid, $scale, $ratings);
  2821. $strratings = get_string("aggregatesum", "forum");
  2822. break;
  2823. }
  2824. if ($agg !== "") {
  2825. if (empty($strratings)) {
  2826. $strratings = get_string("ratings", "forum");
  2827. }
  2828. $strratings .= ': ';
  2829. if ($link) {
  2830. $strratings .= link_to_popup_window ("/mod/forum/report.php?id=$postid", "ratings", $agg, 400, 600, null, null, true);
  2831. } else {
  2832. $strratings .= "$agg ";
  2833. }
  2834. if ($return) {
  2835. return $strratings;
  2836. } else {
  2837. echo $strratings;
  2838. }
  2839. }
  2840. }
  2841. /**
  2842. * Return the mean rating of a post given to the current user by others.
  2843. * Scale is an array of possible ratings in the scale
  2844. * Ratings is an optional simple array of actual ratings (just integers)
  2845. * Forumid is the forum id field needed - passing it avoids a double query of lookup up the discusion and then the forum id to get the aggregate type
  2846. */
  2847. function forum_get_ratings_mean($postid, $scale, $ratings=NULL) {
  2848. if (is_null($ratings)) {
  2849. $ratings = array();
  2850. if ($rates = get_records("forum_ratings", "post", $postid)) {
  2851. foreach ($rates as $rate) {
  2852. $ratings[] = $rate->rating;
  2853. }
  2854. }
  2855. }
  2856. $count = count($ratings);
  2857. if ($count == 0 ) {
  2858. return "";
  2859. } else if ($count == 1) {
  2860. $rating = reset($ratings);
  2861. return $scale[$rating];
  2862. } else {
  2863. $total = 0;
  2864. foreach ($ratings as $rating) {
  2865. $total += $rating;
  2866. }
  2867. $mean = round( ((float)$total/(float)$count) + 0.001); // Little fudge factor so that 0.5 goes UP
  2868. if (isset($scale[$mean])) {
  2869. return $scale[$mean]." ($count)";
  2870. } else {
  2871. return "$mean ($count)"; // Should never happen, hopefully
  2872. }
  2873. }
  2874. }
  2875. /**
  2876. * Return the count of the ratings of a post given to the current user by others.
  2877. *
  2878. * For numerical grades, the scale index is the same as the real grade value from interval {0..n}
  2879. * and $scale looks like Array( 0 => '0/n', 1 => '1/n', ..., n => 'n/n' )
  2880. *
  2881. * For scales, the index is the order of the scale item {1..n}
  2882. * and $scale looks like Array( 1 => 'poor', 2 => 'weak', 3 => 'good' )
  2883. * In case of no ratings done yet, we have nothing to display.
  2884. *
  2885. * @param int $postid
  2886. * @param array $scale Possible ratings in the scale - the end of the scale is the highest or max grade
  2887. * @param array $ratings An optional simple array of actual ratings (just integers)
  2888. */
  2889. function forum_get_ratings_count($postid, $scale, $ratings=NULL) {
  2890. if (is_null($ratings)) {
  2891. $ratings = array();
  2892. if ($rates = get_records("forum_ratings", "post", $postid)) {
  2893. foreach ($rates as $rate) {
  2894. $ratings[] = $rate->rating;
  2895. }
  2896. }
  2897. }
  2898. $count = count($ratings);
  2899. if (! array_key_exists(0, $scale)) {
  2900. $scaleused = true;
  2901. } else {
  2902. $scaleused = false;
  2903. }
  2904. if ($count == 0) {
  2905. if ($scaleused) { // If no rating given yet and we use a scale
  2906. return get_string('noratinggiven', 'forum');
  2907. } else {
  2908. return '';
  2909. }
  2910. }
  2911. $maxgradeidx = max(array_keys($scale)); // For numerical grades, the index is the same as the real grade value {0..n}
  2912. // and $scale looks like Array( 0 => '0/n', 1 => '1/n', ..., n => 'n/n' )
  2913. // For scales, the index is the order of the scale item {1..n}
  2914. // and $scale looks like Array( 1 => 'poor', 2 => 'weak', 3 => 'good' )
  2915. if ($count > $maxgradeidx) { // The count exceeds the max grade
  2916. $a = new stdClass();
  2917. $a->count = $count;
  2918. $a->grade = $scale[$maxgradeidx];
  2919. return get_string('aggregatecountformat', 'forum', $a);
  2920. } else { // Display the count and the aggregated grade for this post
  2921. $a = new stdClass();
  2922. $a->count = $count;
  2923. $a->grade = $scale[$count];
  2924. return get_string('aggregatecountformat', 'forum', $a);
  2925. }
  2926. }
  2927. /**
  2928. * Return the max rating of a post given to the current user by others.
  2929. * Scale is an array of possible ratings in the scale
  2930. * Ratings is an optional simple array of actual ratings (just integers)
  2931. */
  2932. function forum_get_ratings_max($postid, $scale, $ratings=NULL) {
  2933. if (is_null($ratings)) {
  2934. $ratings = array();
  2935. if ($rates = get_records("forum_ratings", "post", $postid)) {
  2936. foreach ($rates as $rate) {
  2937. $ratings[] = $rate->rating;
  2938. }
  2939. }
  2940. }
  2941. $count = count($ratings);
  2942. if ($count == 0 ) {
  2943. return "";
  2944. } else if ($count == 1) { //this works for max
  2945. $rating = reset($ratings);
  2946. return $scale[$rating];
  2947. } else {
  2948. $max = max($ratings);
  2949. if (isset($scale[$max])) {
  2950. return $scale[$max]." ($count)";
  2951. } else {
  2952. return "$max ($count)"; // Should never happen, hopefully
  2953. }
  2954. }
  2955. }
  2956. /**
  2957. * Return the min rating of a post given to the current user by others.
  2958. * Scale is an array of possible ratings in the scale
  2959. * Ratings is an optional simple array of actual ratings (just integers)
  2960. */
  2961. function forum_get_ratings_min($postid, $scale, $ratings=NULL) {
  2962. if (is_null($ratings)) {
  2963. $ratings = array();
  2964. if ($rates = get_records("forum_ratings", "post", $postid)) {
  2965. foreach ($rates as $rate) {
  2966. $ratings[] = $rate->rating;
  2967. }
  2968. }
  2969. }
  2970. $count = count($ratings);
  2971. if ($count == 0 ) {
  2972. return "";
  2973. } else if ($count == 1) {
  2974. $rating = reset($ratings);
  2975. return $scale[$rating]; //this works for min
  2976. } else {
  2977. $min = min($ratings);
  2978. if (isset($scale[$min])) {
  2979. return $scale[$min]." ($count)";
  2980. } else {
  2981. return "$min ($count)"; // Should never happen, hopefully
  2982. }
  2983. }
  2984. }
  2985. /**
  2986. * Return the sum or total of ratings of a post given to the current user by others.
  2987. * Scale is an array of possible ratings in the scale
  2988. * Ratings is an optional simple array of actual ratings (just integers)
  2989. */
  2990. function forum_get_ratings_sum($postid, $scale, $ratings=NULL) {
  2991. if (is_null($ratings)) {
  2992. $ratings = array();
  2993. if ($rates = get_records("forum_ratings", "post", $postid)) {
  2994. foreach ($rates as $rate) {
  2995. $ratings[] = $rate->rating;
  2996. }
  2997. }
  2998. }
  2999. $count = count($ratings);
  3000. $scalecount = count($scale)-1; //this should give us the last element of the scale aka the max grade with $scale[$scalecount]
  3001. if ($count == 0 ) {
  3002. return "";
  3003. } else if ($count == 1) { //this works for max.
  3004. $rating = reset($ratings);
  3005. return $scale[$rating];
  3006. } else {
  3007. $total = 0;
  3008. foreach ($ratings as $rating) {
  3009. $total += $rating;
  3010. }
  3011. if ($total > $scale[$scalecount]) { //if the total exceeds the max grade then set it to the max grade
  3012. $total = $scale[$scalecount];
  3013. }
  3014. if (isset($scale[$total])) {
  3015. return $scale[$total]." ($count)";
  3016. } else {
  3017. return "$total ($count)"; // Should never happen, hopefully
  3018. }
  3019. }
  3020. }
  3021. /**
  3022. * Return a summary of post ratings given to the current user by others.
  3023. * Scale is an array of possible ratings in the scale
  3024. * Ratings is an optional simple array of actual ratings (just integers)
  3025. */
  3026. function forum_get_ratings_summary($postid, $scale, $ratings=NULL) {
  3027. if (is_null($ratings)) {
  3028. $ratings = array();
  3029. if ($rates = get_records("forum_ratings", "post", $postid)) {
  3030. foreach ($rates as $rate) {
  3031. $rating[] = $rate->rating;
  3032. }
  3033. }
  3034. }
  3035. if (!$count = count($ratings)) {
  3036. return "";
  3037. }
  3038. foreach ($scale as $key => $scaleitem) {
  3039. $sumrating[$key] = 0;
  3040. }
  3041. foreach ($ratings as $rating) {
  3042. $sumrating[$rating]++;
  3043. }
  3044. $summary = "";
  3045. foreach ($scale as $key => $scaleitem) {
  3046. $summary = $sumrating[$key].$summary;
  3047. if ($key > 1) {
  3048. $summary = "/$summary";
  3049. }
  3050. }
  3051. return $summary;
  3052. }
  3053. /**
  3054. * Print the menu of ratings as part of a larger form.
  3055. * If the post has already been - set that value.
  3056. * Scale is an array of ratings
  3057. */
  3058. function forum_print_rating_menu($postid, $userid, $scale, $myrating=NULL) {
  3059. static $strrate;
  3060. if (is_null($myrating)) {
  3061. if (!$rating = get_record("forum_ratings", "userid", $userid, "post", $postid)) {
  3062. $myrating = FORUM_UNSET_POST_RATING;
  3063. } else {
  3064. $myrating = $rating->rating;
  3065. }
  3066. }
  3067. if (empty($strrate)) {
  3068. $strrate = get_string("rate", "forum");
  3069. }
  3070. $scale = array(FORUM_UNSET_POST_RATING => $strrate.'...') + $scale;
  3071. choose_from_menu($scale, $postid, $myrating, '', '', '0', false, false, 0, '', false, false, 'forumpostratingmenu');
  3072. }
  3073. /**
  3074. * Print the drop down that allows the user to select how they want to have
  3075. * the discussion displayed.
  3076. * @param $id - forum id if $forumtype is 'single',
  3077. * discussion id for any other forum type
  3078. * @param $mode - forum layout mode
  3079. * @param $forumtype - optional
  3080. */
  3081. function forum_print_mode_form($id, $mode, $forumtype='') {
  3082. if ($forumtype == 'single') {
  3083. echo '<div class="forummode">';
  3084. popup_form("view.php?f=$id&amp;mode=", forum_get_layout_modes(), "mode", $mode, "");
  3085. echo '</div>';
  3086. } else {
  3087. popup_form("discuss.php?d=$id&amp;mode=", forum_get_layout_modes(), "mode", $mode, "");
  3088. }
  3089. }
  3090. /**
  3091. *
  3092. */
  3093. function forum_search_form($course, $search='') {
  3094. global $CFG;
  3095. $output = '<div class="forumsearch">';
  3096. $output .= '<form action="'.$CFG->wwwroot.'/mod/forum/search.php" style="display:inline">';
  3097. $output .= '<fieldset class="invisiblefieldset">';
  3098. $output .= helpbutton('search', get_string('search'), 'moodle', true, false, '', true);
  3099. $output .= '<input name="search" type="text" size="18" value="'.s($search, true).'" alt="search" />';
  3100. $output .= '<input value="'.get_string('searchforums', 'forum').'" type="submit" />';
  3101. $output .= '<input name="id" type="hidden" value="'.$course->id.'" />';
  3102. $output .= '</fieldset>';
  3103. $output .= '</form>';
  3104. $output .= '</div>';
  3105. return $output;
  3106. }
  3107. /**
  3108. *
  3109. */
  3110. function forum_set_return() {
  3111. global $CFG, $SESSION;
  3112. if (! isset($SESSION->fromdiscussion)) {
  3113. if (!empty($_SERVER['HTTP_REFERER'])) {
  3114. $referer = $_SERVER['HTTP_REFERER'];
  3115. } else {
  3116. $referer = "";
  3117. }
  3118. // If the referer is NOT a login screen then save it.
  3119. if (! strncasecmp("$CFG->wwwroot/login", $referer, 300)) {
  3120. $SESSION->fromdiscussion = $_SERVER["HTTP_REFERER"];
  3121. }
  3122. }
  3123. }
  3124. /**
  3125. *
  3126. */
  3127. function forum_go_back_to($default) {
  3128. global $SESSION;
  3129. if (!empty($SESSION->fromdiscussion)) {
  3130. $returnto = $SESSION->fromdiscussion;
  3131. unset($SESSION->fromdiscussion);
  3132. return $returnto;
  3133. } else {
  3134. return $default;
  3135. }
  3136. }
  3137. /**
  3138. * Creates a directory file name, suitable for make_upload_directory()
  3139. */
  3140. function forum_file_area_name($post) {
  3141. global $CFG;
  3142. if (!isset($post->forum) or !isset($post->course)) {
  3143. debugging('missing forum or course', DEBUG_DEVELOPER);
  3144. if (!$discussion = get_record('forum_discussions', 'id', $post->discussion)) {
  3145. return false;
  3146. }
  3147. if (!$forum = get_record('forum', 'id', $discussion->forum)) {
  3148. return false;
  3149. }
  3150. $forumid = $forum->id;
  3151. $courseid = $forum->course;
  3152. } else {
  3153. $forumid = $post->forum;
  3154. $courseid = $post->course;
  3155. }
  3156. return "$courseid/$CFG->moddata/forum/$forumid/$post->id";
  3157. }
  3158. /**
  3159. *
  3160. */
  3161. function forum_file_area($post) {
  3162. $path = forum_file_area_name($post);
  3163. if ($path) {
  3164. return make_upload_directory($path);
  3165. } else {
  3166. return false;
  3167. }
  3168. }
  3169. /**
  3170. *
  3171. */
  3172. function forum_delete_old_attachments($post, $exception="") {
  3173. /**
  3174. * Deletes all the user files in the attachments area for a post
  3175. * EXCEPT for any file named $exception
  3176. */
  3177. if ($basedir = forum_file_area($post)) {
  3178. if ($files = get_directory_list($basedir)) {
  3179. foreach ($files as $file) {
  3180. if ($file != $exception) {
  3181. unlink("$basedir/$file");
  3182. notify("Existing file '$file' has been deleted!");
  3183. }
  3184. }
  3185. }
  3186. if (!$exception) { // Delete directory as well, if empty
  3187. rmdir("$basedir");
  3188. }
  3189. }
  3190. }
  3191. /**
  3192. * Given a discussion object that is being moved to forumid,
  3193. * this function checks all posts in that discussion
  3194. * for attachments, and if any are found, these are
  3195. * moved to the new forum directory.
  3196. */
  3197. function forum_move_attachments($discussion, $forumid) {
  3198. global $CFG;
  3199. require_once($CFG->dirroot.'/lib/uploadlib.php');
  3200. $return = true;
  3201. if ($posts = get_records_select("forum_posts", "discussion = '$discussion->id' AND attachment <> ''")) {
  3202. foreach ($posts as $oldpost) {
  3203. $oldpost->course = $discussion->course;
  3204. $oldpost->forum = $discussion->forum;
  3205. $oldpostdir = "$CFG->dataroot/".forum_file_area_name($oldpost);
  3206. if (is_dir($oldpostdir)) {
  3207. $newpost = $oldpost;
  3208. $newpost->forum = $forumid;
  3209. $newpostdir = forum_file_area_name($newpost);
  3210. // take off the last directory because otherwise we're renaming to a directory that already exists
  3211. // and this is unhappy in certain situations, eg over an nfs mount and potentially on windows too.
  3212. make_upload_directory(substr($newpostdir,0,strrpos($newpostdir,'/')));
  3213. $newpostdir = $CFG->dataroot.'/'.forum_file_area_name($newpost);
  3214. $files = get_directory_list($oldpostdir); // get it before we rename it.
  3215. if (! @rename($oldpostdir, $newpostdir)) {
  3216. $return = false;
  3217. }
  3218. foreach ($files as $file) {
  3219. clam_change_log($oldpostdir.'/'.$file,$newpostdir.'/'.$file);
  3220. }
  3221. }
  3222. }
  3223. }
  3224. return $return;
  3225. }
  3226. /**
  3227. * if return=html, then return a html string.
  3228. * if return=text, then return a text-only string.
  3229. * otherwise, print HTML for non-images, and return image HTML
  3230. */
  3231. function forum_print_attachments($post, $return=NULL) {
  3232. global $CFG;
  3233. $filearea = forum_file_area_name($post);
  3234. $imagereturn = "";
  3235. $output = "";
  3236. if ($basedir = forum_file_area($post)) {
  3237. if ($files = get_directory_list($basedir)) {
  3238. $strattachment = get_string("attachment", "forum");
  3239. foreach ($files as $file) {
  3240. $icon = mimeinfo("icon", $file);
  3241. $type = mimeinfo("type", $file);
  3242. $ffurl = get_file_url("$filearea/$file");
  3243. $image = "<img src=\"$CFG->pixpath/f/$icon\" class=\"icon\" alt=\"\" />";
  3244. if ($return == "html") {
  3245. $output .= "<a href=\"$ffurl\">$image</a> ";
  3246. $output .= "<a href=\"$ffurl\">$file</a><br />";
  3247. } else if ($return == "text") {
  3248. $output .= "$strattachment $file:\n$ffurl\n";
  3249. } else {
  3250. if (in_array($type, array('image/gif', 'image/jpeg', 'image/png'))) { // Image attachments don't get printed as links
  3251. $imagereturn .= "<br /><img src=\"$ffurl\" alt=\"\" />";
  3252. } else {
  3253. echo "<a href=\"$ffurl\">$image</a> ";
  3254. echo filter_text("<a href=\"$ffurl\">$file</a><br />");
  3255. }
  3256. }
  3257. }
  3258. }
  3259. }
  3260. if ($return) {
  3261. return $output;
  3262. }
  3263. return $imagereturn;
  3264. }
  3265. /**
  3266. * If successful, this function returns the name of the file
  3267. * @param $post is a full post record, including course and forum
  3268. * @param $newfile is a full upload array from $_FILES
  3269. * @param $message is a string to hold the messages.
  3270. */
  3271. /**
  3272. *
  3273. */
  3274. function forum_add_attachment($post, $inputname,&$message) {
  3275. global $CFG;
  3276. if (!$forum = get_record("forum", "id", $post->forum)) {
  3277. return "";
  3278. }
  3279. if (!$course = get_record("course", "id", $forum->course)) {
  3280. return "";
  3281. }
  3282. require_once($CFG->dirroot.'/lib/uploadlib.php');
  3283. $um = new upload_manager($inputname,true,false,$course,false,$forum->maxbytes,true,true);
  3284. $dir = forum_file_area_name($post);
  3285. if ($um->process_file_uploads($dir)) {
  3286. $message .= $um->get_errors();
  3287. return $um->get_new_filename();
  3288. }
  3289. $message .= $um->get_errors();
  3290. return null;
  3291. }
  3292. /**
  3293. *
  3294. */
  3295. function forum_add_new_post($post,&$message) {
  3296. global $USER, $CFG;
  3297. $discussion = get_record('forum_discussions', 'id', $post->discussion);
  3298. $forum = get_record('forum', 'id', $discussion->forum);
  3299. $post->created = $post->modified = time();
  3300. $post->mailed = "0";
  3301. $post->userid = $USER->id;
  3302. $post->attachment = "";
  3303. $post->forum = $forum->id; // speedup
  3304. $post->course = $forum->course; // speedup
  3305. if (! $post->id = insert_record("forum_posts", $post)) {
  3306. return false;
  3307. }
  3308. if ($post->attachment = forum_add_attachment($post, 'attachment',$message)) {
  3309. set_field("forum_posts", "attachment", $post->attachment, "id", $post->id);
  3310. }
  3311. // Update discussion modified date
  3312. set_field("forum_discussions", "timemodified", $post->modified, "id", $post->discussion);
  3313. set_field("forum_discussions", "usermodified", $post->userid, "id", $post->discussion);
  3314. if (forum_tp_can_track_forums($forum) && forum_tp_is_tracked($forum)) {
  3315. forum_tp_mark_post_read($post->userid, $post, $post->forum);
  3316. }
  3317. return $post->id;
  3318. }
  3319. /**
  3320. *
  3321. */
  3322. function forum_update_post($post,&$message) {
  3323. global $USER, $CFG;
  3324. $forum = get_record('forum', 'id', $post->forum);
  3325. $post->modified = time();
  3326. $updatediscussion = new object();
  3327. $updatediscussion->id = $post->discussion;
  3328. $updatediscussion->timemodified = $post->modified; // last modified tracking
  3329. $updatediscussion->usermodified = $post->userid; // last modified tracking
  3330. if (!$post->parent) { // Post is a discussion starter - update discussion title and times too
  3331. $updatediscussion->name = $post->subject;
  3332. $updatediscussion->timestart = $post->timestart;
  3333. $updatediscussion->timeend = $post->timeend;
  3334. }
  3335. if (!update_record('forum_discussions', $updatediscussion)) {
  3336. return false;
  3337. }
  3338. if ($newfilename = forum_add_attachment($post, 'attachment',$message)) {
  3339. $post->attachment = $newfilename;
  3340. } else {
  3341. unset($post->attachment);
  3342. }
  3343. if (forum_tp_can_track_forums($forum) && forum_tp_is_tracked($forum)) {
  3344. forum_tp_mark_post_read($post->userid, $post, $post->forum);
  3345. }
  3346. return update_record('forum_posts', $post);
  3347. }
  3348. /**
  3349. * Given an object containing all the necessary data,
  3350. * create a new discussion and return the id
  3351. */
  3352. function forum_add_discussion($discussion,&$message) {
  3353. global $USER, $CFG;
  3354. $timenow = time();
  3355. // The first post is stored as a real post, and linked
  3356. // to from the discuss entry.
  3357. $forum = get_record('forum', 'id', $discussion->forum);
  3358. $post = new object();
  3359. $post->discussion = 0;
  3360. $post->parent = 0;
  3361. $post->userid = $USER->id;
  3362. $post->created = $timenow;
  3363. $post->modified = $timenow;
  3364. $post->mailed = 0;
  3365. $post->subject = $discussion->name;
  3366. $post->message = $discussion->intro;
  3367. $post->attachment = "";
  3368. $post->forum = $forum->id; // speedup
  3369. $post->course = $forum->course; // speedup
  3370. $post->format = $discussion->format;
  3371. $post->mailnow = $discussion->mailnow;
  3372. if (! $post->id = insert_record("forum_posts", $post) ) {
  3373. return 0;
  3374. }
  3375. if ($post->attachment = forum_add_attachment($post, 'attachment',$message)) {
  3376. set_field("forum_posts", "attachment", $post->attachment, "id", $post->id); //ignore errors
  3377. }
  3378. // Now do the main entry for the discussion,
  3379. // linking to this first post
  3380. $discussion->firstpost = $post->id;
  3381. $discussion->timemodified = $timenow;
  3382. $discussion->usermodified = $post->userid;
  3383. $discussion->userid = $USER->id;
  3384. if (! $post->discussion = insert_record("forum_discussions", $discussion) ) {
  3385. delete_records("forum_posts", "id", $post->id);
  3386. return 0;
  3387. }
  3388. // Finally, set the pointer on the post.
  3389. if (! set_field("forum_posts", "discussion", $post->discussion, "id", $post->id)) {
  3390. delete_records("forum_posts", "id", $post->id);
  3391. delete_records("forum_discussions", "id", $post->discussion);
  3392. return 0;
  3393. }
  3394. if (forum_tp_can_track_forums($forum) && forum_tp_is_tracked($forum)) {
  3395. forum_tp_mark_post_read($post->userid, $post, $post->forum);
  3396. }
  3397. return $post->discussion;
  3398. }
  3399. /**
  3400. *
  3401. */
  3402. function forum_delete_discussion($discussion, $fulldelete=false) {
  3403. // $discussion is a discussion record object
  3404. $result = true;
  3405. if ($posts = get_records("forum_posts", "discussion", $discussion->id)) {
  3406. foreach ($posts as $post) {
  3407. $post->course = $discussion->course;
  3408. $post->forum = $discussion->forum;
  3409. if (! delete_records("forum_ratings", "post", "$post->id")) {
  3410. $result = false;
  3411. }
  3412. if (! forum_delete_post($post, $fulldelete)) {
  3413. $result = false;
  3414. }
  3415. }
  3416. }
  3417. forum_tp_delete_read_records(-1, -1, $discussion->id);
  3418. if (! delete_records("forum_discussions", "id", "$discussion->id")) {
  3419. $result = false;
  3420. }
  3421. return $result;
  3422. }
  3423. /**
  3424. *
  3425. */
  3426. function forum_delete_post($post, $children=false) {
  3427. if ($childposts = get_records('forum_posts', 'parent', $post->id)) {
  3428. if ($children) {
  3429. foreach ($childposts as $childpost) {
  3430. forum_delete_post($childpost, true);
  3431. }
  3432. } else {
  3433. return false;
  3434. }
  3435. }
  3436. if (delete_records("forum_posts", "id", $post->id)) {
  3437. delete_records("forum_ratings", "post", $post->id); // Just in case
  3438. forum_tp_delete_read_records(-1, $post->id);
  3439. if ($post->attachment) {
  3440. $discussion = get_record("forum_discussions", "id", $post->discussion);
  3441. $post->course = $discussion->course;
  3442. $post->forum = $discussion->forum;
  3443. forum_delete_old_attachments($post);
  3444. }
  3445. // Just in case we are deleting the last post
  3446. forum_discussion_update_last_post($post->discussion);
  3447. return true;
  3448. }
  3449. return false;
  3450. }
  3451. /**
  3452. *
  3453. */
  3454. function forum_count_replies($post, $children=true) {
  3455. $count = 0;
  3456. if ($children) {
  3457. if ($childposts = get_records('forum_posts', 'parent', $post->id)) {
  3458. foreach ($childposts as $childpost) {
  3459. $count ++; // For this child
  3460. $count += forum_count_replies($childpost, true);
  3461. }
  3462. }
  3463. } else {
  3464. $count += count_records('forum_posts', 'parent', $post->id);
  3465. }
  3466. return $count;
  3467. }
  3468. /**
  3469. *
  3470. */
  3471. function forum_forcesubscribe($forumid, $value=1) {
  3472. return set_field("forum", "forcesubscribe", $value, "id", $forumid);
  3473. }
  3474. /**
  3475. *
  3476. */
  3477. function forum_is_forcesubscribed($forum) {
  3478. if (isset($forum->forcesubscribe)) { // then we use that
  3479. return ($forum->forcesubscribe == FORUM_FORCESUBSCRIBE);
  3480. } else { // Check the database
  3481. return (get_field('forum', 'forcesubscribe', 'id', $forum) == FORUM_FORCESUBSCRIBE);
  3482. }
  3483. }
  3484. /**
  3485. *
  3486. */
  3487. function forum_is_subscribed($userid, $forum) {
  3488. if (is_numeric($forum)) {
  3489. $forum = get_record('forum', 'id', $forum);
  3490. }
  3491. if (forum_is_forcesubscribed($forum)) {
  3492. return true;
  3493. }
  3494. return record_exists("forum_subscriptions", "userid", $userid, "forum", $forum->id);
  3495. }
  3496. function forum_get_subscribed_forums($course) {
  3497. global $USER, $CFG;
  3498. $sql = "SELECT f.id
  3499. FROM {$CFG->prefix}forum f
  3500. LEFT JOIN {$CFG->prefix}forum_subscriptions fs ON (fs.forum = f.id AND fs.userid = $USER->id)
  3501. WHERE f.forcesubscribe <> ".FORUM_DISALLOWSUBSCRIBE."
  3502. AND (f.forcesubscribe = ".FORUM_FORCESUBSCRIBE." OR fs.id IS NOT NULL)";
  3503. if ($subscribed = get_records_sql($sql)) {
  3504. foreach ($subscribed as $s) {
  3505. $subscribed[$s->id] = $s->id;
  3506. }
  3507. return $subscribed;
  3508. } else {
  3509. return array();
  3510. }
  3511. }
  3512. /**
  3513. * Adds user to the subscriber list
  3514. */
  3515. function forum_subscribe($userid, $forumid) {
  3516. if (record_exists("forum_subscriptions", "userid", $userid, "forum", $forumid)) {
  3517. return true;
  3518. }
  3519. $sub = new object();
  3520. $sub->userid = $userid;
  3521. $sub->forum = $forumid;
  3522. return insert_record("forum_subscriptions", $sub);
  3523. }
  3524. /**
  3525. * Removes user from the subscriber list
  3526. */
  3527. function forum_unsubscribe($userid, $forumid) {
  3528. return delete_records("forum_subscriptions", "userid", $userid, "forum", $forumid);
  3529. }
  3530. /**
  3531. * Given a new post, subscribes or unsubscribes as appropriate.
  3532. * Returns some text which describes what happened.
  3533. */
  3534. function forum_post_subscription($post, $forum) {
  3535. global $USER;
  3536. $action = '';
  3537. $subscribed = forum_is_subscribed($USER->id, $forum);
  3538. if ($forum->forcesubscribe == FORUM_FORCESUBSCRIBE) { // database ignored
  3539. return "";
  3540. } elseif (($forum->forcesubscribe == FORUM_DISALLOWSUBSCRIBE)
  3541. && !has_capability('moodle/course:manageactivities', get_context_instance(CONTEXT_COURSE, $forum->course), $USER->id)) {
  3542. if ($subscribed) {
  3543. $action = 'unsubscribe'; // sanity check, following MDL-14558
  3544. } else {
  3545. return "";
  3546. }
  3547. } else { // go with the user's choice
  3548. if (isset($post->subscribe)) {
  3549. // no change
  3550. if ((!empty($post->subscribe) && $subscribed)
  3551. || (empty($post->subscribe) && !$subscribed)) {
  3552. return "";
  3553. } elseif (!empty($post->subscribe) && !$subscribed) {
  3554. $action = 'subscribe';
  3555. } elseif (empty($post->subscribe) && $subscribed) {
  3556. $action = 'unsubscribe';
  3557. }
  3558. }
  3559. }
  3560. $info = new object();
  3561. $info->name = fullname($USER);
  3562. $info->forum = format_string($forum->name);
  3563. switch ($action) {
  3564. case 'subscribe':
  3565. forum_subscribe($USER->id, $post->forum);
  3566. return "<p>".get_string("nowsubscribed", "forum", $info)."</p>";
  3567. case 'unsubscribe':
  3568. forum_unsubscribe($USER->id, $post->forum);
  3569. return "<p>".get_string("nownotsubscribed", "forum", $info)."</p>";
  3570. }
  3571. }
  3572. /**
  3573. * Generate and return the subscribe or unsubscribe link for a forum.
  3574. * @param object $forum the forum. Fields used are $forum->id and $forum->forcesubscribe.
  3575. * @param object $context the context object for this forum.
  3576. * @param array $messages text used for the link in its various states
  3577. * (subscribed, unsubscribed, forcesubscribed or cantsubscribe).
  3578. * Any strings not passed in are taken from the $defaultmessages array
  3579. * at the top of the function.
  3580. * @param
  3581. */
  3582. function forum_get_subscribe_link($forum, $context, $messages = array(), $cantaccessagroup = false, $fakelink=true, $backtoindex=false, $subscribed_forums=null) {
  3583. global $CFG, $USER;
  3584. $defaultmessages = array(
  3585. 'subscribed' => get_string('unsubscribe', 'forum'),
  3586. 'unsubscribed' => get_string('subscribe', 'forum'),
  3587. 'cantaccessgroup' => get_string('no'),
  3588. 'forcesubscribed' => get_string('everyoneissubscribed', 'forum'),
  3589. 'cantsubscribe' => get_string('disallowsubscribe','forum')
  3590. );
  3591. $messages = $messages + $defaultmessages;
  3592. if (forum_is_forcesubscribed($forum)) {
  3593. return $messages['forcesubscribed'];
  3594. } else if ($forum->forcesubscribe == FORUM_DISALLOWSUBSCRIBE && !has_capability('mod/forum:managesubscriptions', $context)) {
  3595. return $messages['cantsubscribe'];
  3596. } else if ($cantaccessagroup) {
  3597. return $messages['cantaccessgroup'];
  3598. } else {
  3599. if (is_null($subscribed_forums)) {
  3600. $subscribed = forum_is_subscribed($USER->id, $forum);
  3601. } else {
  3602. $subscribed = !empty($subscribed_forums[$forum->id]);
  3603. }
  3604. if ($subscribed) {
  3605. $linktext = $messages['subscribed'];
  3606. $linktitle = get_string('subscribestop', 'forum');
  3607. } else {
  3608. $linktext = $messages['unsubscribed'];
  3609. $linktitle = get_string('subscribestart', 'forum');
  3610. }
  3611. $options = array();
  3612. if ($backtoindex) {
  3613. $backtoindexlink = '&amp;backtoindex=1';
  3614. $options['backtoindex'] = 1;
  3615. } else {
  3616. $backtoindexlink = '';
  3617. }
  3618. $link = '';
  3619. $sesskeylink = '&amp;sesskey='.sesskey();
  3620. if ($fakelink) {
  3621. $link .= <<<EOD
  3622. <script type="text/javascript">
  3623. //<![CDATA[
  3624. var subs_link = document.getElementById("subscriptionlink");
  3625. if(subs_link){
  3626. subs_link.innerHTML = "<a title=\"$linktitle\" href='$CFG->wwwroot/mod/forum/subscribe.php?id={$forum->id}{$backtoindexlink}{$sesskeylink}'>$linktext<\/a>";
  3627. }
  3628. //]]>
  3629. </script>
  3630. <noscript>
  3631. EOD;
  3632. }
  3633. $options['id'] = $forum->id;
  3634. $options['sesskey'] = sesskey();
  3635. $link .= print_single_button($CFG->wwwroot . '/mod/forum/subscribe.php',
  3636. $options, $linktext, 'post', '_self', true, $linktitle);
  3637. if ($fakelink) {
  3638. $link .= '</noscript>';
  3639. }
  3640. return $link;
  3641. }
  3642. }
  3643. /**
  3644. * Generate and return the track or no track link for a forum.
  3645. * @param object $forum the forum. Fields used are $forum->id and $forum->forcesubscribe.
  3646. */
  3647. function forum_get_tracking_link($forum, $messages=array(), $fakelink=true) {
  3648. global $CFG, $USER;
  3649. static $strnotrackforum, $strtrackforum;
  3650. if (isset($messages['trackforum'])) {
  3651. $strtrackforum = $messages['trackforum'];
  3652. }
  3653. if (isset($messages['notrackforum'])) {
  3654. $strnotrackforum = $messages['notrackforum'];
  3655. }
  3656. if (empty($strtrackforum)) {
  3657. $strtrackforum = get_string('trackforum', 'forum');
  3658. }
  3659. if (empty($strnotrackforum)) {
  3660. $strnotrackforum = get_string('notrackforum', 'forum');
  3661. }
  3662. if (forum_tp_is_tracked($forum)) {
  3663. $linktitle = $strnotrackforum;
  3664. $linktext = $strnotrackforum;
  3665. } else {
  3666. $linktitle = $strtrackforum;
  3667. $linktext = $strtrackforum;
  3668. }
  3669. $link = '';
  3670. if ($fakelink) {
  3671. $link .= '<script type="text/javascript">';
  3672. $link .= '//<![CDATA['."\n";
  3673. $link .= 'document.getElementById("trackinglink").innerHTML = "<a title=\"' . $linktitle . '\" href=\"' . $CFG->wwwroot .
  3674. '/mod/forum/settracking.php?id=' . $forum->id . '\">' . $linktext . '<\/a>";'."\n";
  3675. $link .= '//]]>'."\n";
  3676. $link .= '</script>';
  3677. // use <noscript> to print button in case javascript is not enabled
  3678. $link .= '<noscript>';
  3679. }
  3680. $link .= print_single_button($CFG->wwwroot . '/mod/forum/settracking.php?id=' . $forum->id,
  3681. '', $linktext, 'post', '_self', true, $linktitle);
  3682. if ($fakelink) {
  3683. $link .= '</noscript>';
  3684. }
  3685. return $link;
  3686. }
  3687. /**
  3688. * Returns true if user created new discussion already
  3689. * @param int $forumid
  3690. * @param int $userid
  3691. * @return bool
  3692. */
  3693. function forum_user_has_posted_discussion($forumid, $userid) {
  3694. global $CFG;
  3695. $sql = "SELECT 'x'
  3696. FROM {$CFG->prefix}forum_discussions d, {$CFG->prefix}forum_posts p
  3697. WHERE d.forum = $forumid AND p.discussion = d.id AND p.parent = 0 and p.userid = $userid";
  3698. return record_exists_sql($sql);
  3699. }
  3700. /**
  3701. *
  3702. */
  3703. function forum_discussions_user_has_posted_in($forumid, $userid) {
  3704. global $CFG;
  3705. $haspostedsql = "SELECT d.id AS id,
  3706. d.*
  3707. FROM {$CFG->prefix}forum_posts p,
  3708. {$CFG->prefix}forum_discussions d
  3709. WHERE p.discussion = d.id
  3710. AND d.forum = $forumid
  3711. AND p.userid = $userid";
  3712. return get_records_sql($haspostedsql);
  3713. }
  3714. /**
  3715. *
  3716. */
  3717. function forum_user_has_posted($forumid, $did, $userid) {
  3718. global $CFG;
  3719. if (empty($did)) {
  3720. // posted in any forum discussion?
  3721. $sql = "SELECT 'x'
  3722. FROM {$CFG->prefix}forum_posts p
  3723. JOIN {$CFG->prefix}forum_discussions d ON d.id = p.discussion
  3724. WHERE p.userid = $userid AND d.forum = $forumid";
  3725. return record_exists_sql($sql);
  3726. } else {
  3727. // started discussion?
  3728. return record_exists('forum_posts','discussion',$did,'userid',$userid);
  3729. }
  3730. }
  3731. /**
  3732. *
  3733. */
  3734. function forum_user_can_post_discussion($forum, $currentgroup=null, $unused=-1, $cm=NULL, $context=NULL) {
  3735. // $forum is an object
  3736. global $USER;
  3737. // shortcut - guest and not-logged-in users can not post
  3738. if (isguestuser() or !isloggedin()) {
  3739. return false;
  3740. }
  3741. if (!$cm) {
  3742. debugging('missing cm', DEBUG_DEVELOPER);
  3743. if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
  3744. error('Course Module ID was incorrect');
  3745. }
  3746. }
  3747. if (!$context) {
  3748. $context = get_context_instance(CONTEXT_MODULE, $cm->id);
  3749. }
  3750. if ($currentgroup === null) {
  3751. $currentgroup = groups_get_activity_group($cm);
  3752. }
  3753. $groupmode = groups_get_activity_groupmode($cm);
  3754. if ($forum->type == 'news') {
  3755. $capname = 'mod/forum:addnews';
  3756. } else {
  3757. $capname = 'mod/forum:startdiscussion';
  3758. }
  3759. if (!has_capability($capname, $context)) {
  3760. return false;
  3761. }
  3762. if ($forum->type == 'single') {
  3763. return false;
  3764. }
  3765. if ($forum->type == 'eachuser') {
  3766. if (forum_user_has_posted_discussion($forum->id, $USER->id)) {
  3767. return false;
  3768. }
  3769. }
  3770. if (!$groupmode or has_capability('moodle/site:accessallgroups', $context)) {
  3771. return true;
  3772. }
  3773. if ($currentgroup) {
  3774. return groups_is_member($currentgroup);
  3775. } else {
  3776. // no group membership and no accessallgroups means no new discussions
  3777. // reverted to 1.7 behaviour in 1.9+, buggy in 1.8.0-1.9.0
  3778. return false;
  3779. }
  3780. }
  3781. /**
  3782. * This function checks whether the user can reply to posts in a forum
  3783. * discussion. Use forum_user_can_post_discussion() to check whether the user
  3784. * can start dicussions.
  3785. * @param $forum - forum object
  3786. * @param $user - user object
  3787. */
  3788. function forum_user_can_post($forum, $discussion, $user=NULL, $cm=NULL, $course=NULL, $context=NULL) {
  3789. global $USER;
  3790. if (empty($user)) {
  3791. $user = $USER;
  3792. }
  3793. // shortcut - guest and not-logged-in users can not post
  3794. if (isguestuser($user) or empty($user->id)) {
  3795. return false;
  3796. }
  3797. if (!isset($discussion->groupid)) {
  3798. debugging('incorrect discussion parameter', DEBUG_DEVELOPER);
  3799. return false;
  3800. }
  3801. if (!$cm) {
  3802. debugging('missing cm', DEBUG_DEVELOPER);
  3803. if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
  3804. error('Course Module ID was incorrect');
  3805. }
  3806. }
  3807. if (!$course) {
  3808. debugging('missing course', DEBUG_DEVELOPER);
  3809. if (!$course = get_record('course', 'id', $forum->course)) {
  3810. error('Incorrect course id');
  3811. }
  3812. }
  3813. if (!$context) {
  3814. $context = get_context_instance(CONTEXT_MODULE, $cm->id);
  3815. }
  3816. // normal users with temporary guest access can not post
  3817. if (has_capability('moodle/legacy:guest', $context, $user->id, false)) {
  3818. return false;
  3819. }
  3820. if ($forum->type == 'news') {
  3821. $capname = 'mod/forum:replynews';
  3822. } else {
  3823. $capname = 'mod/forum:replypost';
  3824. }
  3825. if (!has_capability($capname, $context, $user->id, false)) {
  3826. return false;
  3827. }
  3828. if (!$groupmode = groups_get_activity_groupmode($cm, $course)) {
  3829. return true;
  3830. }
  3831. if (has_capability('moodle/site:accessallgroups', $context)) {
  3832. return true;
  3833. }
  3834. if ($groupmode == VISIBLEGROUPS) {
  3835. if ($discussion->groupid == -1) {
  3836. // allow students to reply to all participants discussions - this was not possible in Moodle <1.8
  3837. return true;
  3838. }
  3839. return groups_is_member($discussion->groupid);
  3840. } else {
  3841. //separate groups
  3842. if ($discussion->groupid == -1) {
  3843. return false;
  3844. }
  3845. return groups_is_member($discussion->groupid);
  3846. }
  3847. }
  3848. //checks to see if a user can view a particular post
  3849. function forum_user_can_view_post($post, $course, $cm, $forum, $discussion, $user=NULL){
  3850. global $CFG, $USER;
  3851. if (!$user){
  3852. $user = $USER;
  3853. }
  3854. $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
  3855. if (!has_capability('mod/forum:viewdiscussion', $modcontext)) {
  3856. return false;
  3857. }
  3858. // If it's a grouped discussion, make sure the user is a member
  3859. if ($discussion->groupid > 0) {
  3860. $groupmode = groups_get_activity_groupmode($cm);
  3861. if ($groupmode == SEPARATEGROUPS) {
  3862. return groups_is_member($discussion->groupid) || has_capability('moodle/site:accessallgroups', $modcontext);
  3863. }
  3864. }
  3865. return true;
  3866. }
  3867. /**
  3868. *
  3869. */
  3870. function forum_user_can_see_discussion($forum, $discussion, $context, $user=NULL) {
  3871. global $USER;
  3872. if (empty($user) || empty($user->id)) {
  3873. $user = $USER;
  3874. }
  3875. // retrieve objects (yuk)
  3876. if (is_numeric($forum)) {
  3877. debugging('missing full forum', DEBUG_DEVELOPER);
  3878. if (!$forum = get_record('forum','id',$forum)) {
  3879. return false;
  3880. }
  3881. }
  3882. if (is_numeric($discussion)) {
  3883. debugging('missing full discussion', DEBUG_DEVELOPER);
  3884. if (!$discussion = get_record('forum_discussions','id',$discussion)) {
  3885. return false;
  3886. }
  3887. }
  3888. if (!has_capability('mod/forum:viewdiscussion', $context)) {
  3889. return false;
  3890. }
  3891. if ($forum->type == 'qanda' &&
  3892. !forum_user_has_posted($forum->id, $discussion->id, $user->id) &&
  3893. !has_capability('mod/forum:viewqandawithoutposting', $context)) {
  3894. return false;
  3895. }
  3896. return true;
  3897. }
  3898. /**
  3899. *
  3900. */
  3901. function forum_user_can_see_post($forum, $discussion, $post, $user=NULL, $cm=NULL) {
  3902. global $CFG, $USER;
  3903. // retrieve objects (yuk)
  3904. if (is_numeric($forum)) {
  3905. debugging('missing full forum', DEBUG_DEVELOPER);
  3906. if (!$forum = get_record('forum','id',$forum)) {
  3907. return false;
  3908. }
  3909. }
  3910. if (is_numeric($discussion)) {
  3911. debugging('missing full discussion', DEBUG_DEVELOPER);
  3912. if (!$discussion = get_record('forum_discussions','id',$discussion)) {
  3913. return false;
  3914. }
  3915. }
  3916. if (is_numeric($post)) {
  3917. debugging('missing full post', DEBUG_DEVELOPER);
  3918. if (!$post = get_record('forum_posts','id',$post)) {
  3919. return false;
  3920. }
  3921. }
  3922. if (!isset($post->id) && isset($post->parent)) {
  3923. $post->id = $post->parent;
  3924. }
  3925. if (!$cm) {
  3926. debugging('missing cm', DEBUG_DEVELOPER);
  3927. if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
  3928. error('Course Module ID was incorrect');
  3929. }
  3930. }
  3931. if (empty($user) || empty($user->id)) {
  3932. $user = $USER;
  3933. }
  3934. if (isset($cm->cache->caps['mod/forum:viewdiscussion'])) {
  3935. if (!$cm->cache->caps['mod/forum:viewdiscussion']) {
  3936. return false;
  3937. }
  3938. } else {
  3939. $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
  3940. if (!has_capability('mod/forum:viewdiscussion', $modcontext, $user->id)) {
  3941. return false;
  3942. }
  3943. }
  3944. if (isset($cm->uservisible)) {
  3945. if (!$cm->uservisible) {
  3946. return false;
  3947. }
  3948. } else {
  3949. if (!coursemodule_visible_for_user($cm, $user->id)) {
  3950. return false;
  3951. }
  3952. }
  3953. if ($forum->type == 'qanda') {
  3954. $firstpost = forum_get_firstpost_from_discussion($discussion->id);
  3955. $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
  3956. $userfirstpost = forum_get_user_posted_time($discussion->id, $user->id);
  3957. return (($userfirstpost !== false && (time() - $userfirstpost >= $CFG->maxeditingtime)) ||
  3958. $firstpost->id == $post->id || $post->userid == $user->id || $firstpost->userid == $user->id ||
  3959. has_capability('mod/forum:viewqandawithoutposting', $modcontext, $user->id, false));
  3960. }
  3961. return true;
  3962. }
  3963. /**
  3964. * Prints the discussion view screen for a forum.
  3965. *
  3966. * @param object $course The current course object.
  3967. * @param object $forum Forum to be printed.
  3968. * @param int $maxdiscussions .
  3969. * @param string $displayformat The display format to use (optional).
  3970. * @param string $sort Sort arguments for database query (optional).
  3971. * @param int $groupmode Group mode of the forum (optional).
  3972. * @param void $unused (originally current group)
  3973. * @param int $page Page mode, page to display (optional).
  3974. * @param int perpage The maximum number of discussions per page(optional)
  3975. *
  3976. */
  3977. function forum_print_latest_discussions($course, $forum, $maxdiscussions=-1, $displayformat='plain', $sort='',
  3978. $currentgroup=-1, $groupmode=-1, $page=-1, $perpage=100, $cm=NULL) {
  3979. global $CFG, $USER;
  3980. if (!$cm) {
  3981. if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
  3982. error('Course Module ID was incorrect');
  3983. }
  3984. }
  3985. $context = get_context_instance(CONTEXT_MODULE, $cm->id);
  3986. if (empty($sort)) {
  3987. $sort = "d.timemodified DESC";
  3988. }
  3989. $olddiscussionlink = false;
  3990. // Sort out some defaults
  3991. if ($perpage <= 0) {
  3992. $perpage = 0;
  3993. $page = -1;
  3994. }
  3995. if ($maxdiscussions == 0) {
  3996. // all discussions - backwards compatibility
  3997. $page = -1;
  3998. $perpage = 0;
  3999. if ($displayformat == 'plain') {
  4000. $displayformat = 'header'; // Abbreviate display by default
  4001. }
  4002. } else if ($maxdiscussions > 0) {
  4003. $page = -1;
  4004. $perpage = $maxdiscussions;
  4005. }
  4006. $fullpost = false;
  4007. if ($displayformat == 'plain') {
  4008. $fullpost = true;
  4009. }
  4010. // Decide if current user is allowed to see ALL the current discussions or not
  4011. // First check the group stuff
  4012. if ($currentgroup == -1 or $groupmode == -1) {
  4013. $groupmode = groups_get_activity_groupmode($cm, $course);
  4014. $currentgroup = groups_get_activity_group($cm);
  4015. }
  4016. // If the user can post discussions, then this is a good place to put the
  4017. // button for it. We do not show the button if we are showing site news
  4018. // and the current user is a guest.
  4019. if (forum_user_can_post_discussion($forum, $currentgroup, $groupmode, $cm, $context) ||
  4020. ($forum->type != 'news'
  4021. and (isguestuser() or !isloggedin() or has_capability('moodle/legacy:guest', $context, NULL, false))) ) {
  4022. echo '<div class="singlebutton forumaddnew">';
  4023. echo "<form id=\"newdiscussionform\" method=\"get\" action=\"$CFG->wwwroot/mod/forum/post.php\">";
  4024. echo '<div>';
  4025. echo "<input type=\"hidden\" name=\"forum\" value=\"$forum->id\" />";
  4026. echo '<input type="submit" value="';
  4027. echo ($forum->type == 'news') ? get_string('addanewtopic', 'forum')
  4028. : (($forum->type == 'qanda')
  4029. ? get_string('addanewquestion','forum')
  4030. : get_string('addanewdiscussion', 'forum'));
  4031. echo '" />';
  4032. echo '</div>';
  4033. echo '</form>';
  4034. echo "</div>\n";
  4035. } else if (isguestuser() or !isloggedin() or $forum->type == 'news') {
  4036. // no button and no info
  4037. } else if ($groupmode and has_capability('mod/forum:startdiscussion', $context)) {
  4038. // inform users why they can not post new discussion
  4039. if ($currentgroup) {
  4040. notify(get_string('cannotadddiscussion', 'forum'));
  4041. } else {
  4042. notify(get_string('cannotadddiscussionall', 'forum'));
  4043. }
  4044. }
  4045. // Get all the recent discussions we're allowed to see
  4046. $getuserlastmodified = ($displayformat == 'header');
  4047. if (! $discussions = forum_get_discussions($cm, $sort, $fullpost, null, $maxdiscussions, $getuserlastmodified, $page, $perpage) ) {
  4048. echo '<div class="forumnodiscuss">';
  4049. if ($forum->type == 'news') {
  4050. echo '('.get_string('nonews', 'forum').')';
  4051. } else if ($forum->type == 'qanda') {
  4052. echo '('.get_string('noquestions','forum').')';
  4053. } else {
  4054. echo '('.get_string('nodiscussions', 'forum').')';
  4055. }
  4056. echo "</div>\n";
  4057. return;
  4058. }
  4059. // If we want paging
  4060. if ($page != -1) {
  4061. ///Get the number of discussions found
  4062. $numdiscussions = forum_get_discussions_count($cm);
  4063. ///Show the paging bar
  4064. print_paging_bar($numdiscussions, $page, $perpage, "view.php?f=$forum->id&amp;");
  4065. if ($numdiscussions > 1000) {
  4066. // saves some memory on sites with very large forums
  4067. $replies = forum_count_discussion_replies($forum->id, $sort, $maxdiscussions, $page, $perpage);
  4068. } else {
  4069. $replies = forum_count_discussion_replies($forum->id);
  4070. }
  4071. } else {
  4072. $replies = forum_count_discussion_replies($forum->id);
  4073. if ($maxdiscussions > 0 and $maxdiscussions <= count($discussions)) {
  4074. $olddiscussionlink = true;
  4075. }
  4076. }
  4077. $canviewparticipants = has_capability('moodle/course:viewparticipants',$context);
  4078. $strdatestring = get_string('strftimerecentfull');
  4079. // Check if the forum is tracked.
  4080. if ($cantrack = forum_tp_can_track_forums($forum)) {
  4081. $forumtracked = forum_tp_is_tracked($forum);
  4082. } else {
  4083. $forumtracked = false;
  4084. }
  4085. if ($forumtracked) {
  4086. $unreads = forum_get_discussions_unread($cm);
  4087. } else {
  4088. $unreads = array();
  4089. }
  4090. if ($displayformat == 'header') {
  4091. echo '<table cellspacing="0" class="forumheaderlist">';
  4092. echo '<thead>';
  4093. echo '<tr>';
  4094. echo '<th class="header topic" scope="col">'.get_string('discussion', 'forum').'</th>';
  4095. echo '<th class="header author" colspan="2" scope="col">'.get_string('startedby', 'forum').'</th>';
  4096. if ($groupmode > 0) {
  4097. echo '<th class="header group" scope="col">'.get_string('group').'</th>';
  4098. }
  4099. if (has_capability('mod/forum:viewdiscussion', $context)) {
  4100. echo '<th class="header replies" scope="col">'.get_string('replies', 'forum').'</th>';
  4101. // If the forum can be tracked, display the unread column.
  4102. if ($cantrack) {
  4103. echo '<th class="header replies" scope="col">'.get_string('unread', 'forum');
  4104. if ($forumtracked) {
  4105. echo '&nbsp;<a title="'.get_string('markallread', 'forum').
  4106. '" href="'.$CFG->wwwroot.'/mod/forum/markposts.php?f='.
  4107. $forum->id.'&amp;mark=read&amp;returnpage=view.php">'.
  4108. '<img src="'.$CFG->pixpath.'/t/clear.gif" class="iconsmall" alt="'.get_string('markallread', 'forum').'" /></a>';
  4109. }
  4110. echo '</th>';
  4111. }
  4112. }
  4113. echo '<th class="header lastpost" scope="col">'.get_string('lastpost', 'forum').'</th>';
  4114. echo '</tr>';
  4115. echo '</thead>';
  4116. echo '<tbody>';
  4117. }
  4118. foreach ($discussions as $discussion) {
  4119. if (!empty($replies[$discussion->discussion])) {
  4120. $discussion->replies = $replies[$discussion->discussion]->replies;
  4121. $discussion->lastpostid = $replies[$discussion->discussion]->lastpostid;
  4122. } else {
  4123. $discussion->replies = 0;
  4124. }
  4125. // SPECIAL CASE: The front page can display a news item post to non-logged in users.
  4126. // All posts are read in this case.
  4127. if (!$forumtracked) {
  4128. $discussion->unread = '-';
  4129. } else if (empty($USER)) {
  4130. $discussion->unread = 0;
  4131. } else {
  4132. if (empty($unreads[$discussion->discussion])) {
  4133. $discussion->unread = 0;
  4134. } else {
  4135. $discussion->unread = $unreads[$discussion->discussion];
  4136. }
  4137. }
  4138. if (!empty($USER->id)) {
  4139. $ownpost = ($discussion->userid == $USER->id);
  4140. } else {
  4141. $ownpost=false;
  4142. }
  4143. // Use discussion name instead of subject of first post
  4144. $discussion->subject = $discussion->name;
  4145. switch ($displayformat) {
  4146. case 'header':
  4147. if ($groupmode > 0) {
  4148. if (isset($groups[$discussion->groupid])) {
  4149. $group = $groups[$discussion->groupid];
  4150. } else {
  4151. $group = $groups[$discussion->groupid] = groups_get_group($discussion->groupid);
  4152. }
  4153. } else {
  4154. $group = -1;
  4155. }
  4156. forum_print_discussion_header($discussion, $forum, $group, $strdatestring, $cantrack, $forumtracked,
  4157. $canviewparticipants, $context);
  4158. break;
  4159. default:
  4160. $link = false;
  4161. if ($discussion->replies) {
  4162. $link = true;
  4163. } else {
  4164. $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
  4165. $link = forum_user_can_post($forum, $discussion, $USER, $cm, $course, $modcontext);
  4166. }
  4167. $discussion->forum = $forum->id;
  4168. forum_print_post($discussion, $discussion, $forum, $cm, $course, $ownpost, 0, $link, false);
  4169. break;
  4170. }
  4171. }
  4172. if ($displayformat == "header") {
  4173. echo '</tbody>';
  4174. echo '</table>';
  4175. }
  4176. if ($olddiscussionlink) {
  4177. if ($forum->type == 'news') {
  4178. $strolder = get_string('oldertopics', 'forum');
  4179. } else {
  4180. $strolder = get_string('olderdiscussions', 'forum');
  4181. }
  4182. echo '<div class="forumolddiscuss">';
  4183. echo '<a href="'.$CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id.'&amp;showall=1">';
  4184. echo $strolder.'</a> ...</div>';
  4185. }
  4186. if ($page != -1) { ///Show the paging bar
  4187. print_paging_bar($numdiscussions, $page, $perpage, "view.php?f=$forum->id&amp;");
  4188. }
  4189. }
  4190. /**
  4191. *
  4192. */
  4193. function forum_print_discussion($course, $cm, $forum, $discussion, $post, $mode, $canreply=NULL, $canrate=false) {
  4194. global $USER, $CFG;
  4195. if (!empty($USER->id)) {
  4196. $ownpost = ($USER->id == $post->userid);
  4197. } else {
  4198. $ownpost = false;
  4199. }
  4200. if ($canreply === NULL) {
  4201. $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
  4202. $reply = forum_user_can_post($forum, $discussion, $USER, $cm, $course, $modcontext);
  4203. } else {
  4204. $reply = $canreply;
  4205. }
  4206. // $cm holds general cache for forum functions
  4207. $cm->cache = new object();
  4208. $cm->cache->groups = groups_get_all_groups($course->id, 0, $cm->groupingid);
  4209. $cm->cache->usersgroups = array();
  4210. $posters = array();
  4211. // preload all posts - TODO: improve...
  4212. if ($mode == FORUM_MODE_FLATNEWEST) {
  4213. $sort = "p.created DESC";
  4214. } else {
  4215. $sort = "p.created ASC";
  4216. }
  4217. $forumtracked = forum_tp_is_tracked($forum);
  4218. $posts = forum_get_all_discussion_posts($discussion->id, $sort, $forumtracked);
  4219. $post = $posts[$post->id];
  4220. foreach ($posts as $pid=>$p) {
  4221. $posters[$p->userid] = $p->userid;
  4222. }
  4223. // preload all groups of ppl that posted in this discussion
  4224. if ($postersgroups = groups_get_all_groups($course->id, $posters, $cm->groupingid, 'gm.id, gm.groupid, gm.userid')) {
  4225. foreach($postersgroups as $pg) {
  4226. if (!isset($cm->cache->usersgroups[$pg->userid])) {
  4227. $cm->cache->usersgroups[$pg->userid] = array();
  4228. }
  4229. $cm->cache->usersgroups[$pg->userid][$pg->groupid] = $pg->groupid;
  4230. }
  4231. unset($postersgroups);
  4232. }
  4233. $ratings = NULL;
  4234. $ratingsmenuused = false;
  4235. $ratingsformused = false;
  4236. if ($forum->assessed and isloggedin()) {
  4237. if ($scale = make_grades_menu($forum->scale)) {
  4238. $ratings =new object();
  4239. $ratings->scale = $scale;
  4240. $ratings->assesstimestart = $forum->assesstimestart;
  4241. $ratings->assesstimefinish = $forum->assesstimefinish;
  4242. $ratings->allow = $canrate;
  4243. if ($ratings->allow) {
  4244. echo '<form id="form" method="post" action="rate.php">';
  4245. echo '<div class="ratingform">';
  4246. echo '<input type="hidden" name="forumid" value="'.$forum->id.'" />';
  4247. echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
  4248. $ratingsformused = true;
  4249. }
  4250. // preload all ratings - one query only and minimal memory
  4251. $cm->cache->ratings = array();
  4252. $cm->cache->myratings = array();
  4253. if ($postratings = forum_get_all_discussion_ratings($discussion)) {
  4254. foreach ($postratings as $pr) {
  4255. if (!isset($cm->cache->ratings[$pr->postid])) {
  4256. $cm->cache->ratings[$pr->postid] = array();
  4257. }
  4258. $cm->cache->ratings[$pr->postid][$pr->id] = $pr->rating;
  4259. if ($pr->userid == $USER->id) {
  4260. $cm->cache->myratings[$pr->postid] = $pr->rating;
  4261. }
  4262. }
  4263. unset($postratings);
  4264. }
  4265. }
  4266. }
  4267. $post->forum = $forum->id; // Add the forum id to the post object, later used by forum_print_post
  4268. $post->forumtype = $forum->type;
  4269. $post->subject = format_string($post->subject);
  4270. $postread = !empty($post->postread);
  4271. if (forum_print_post($post, $discussion, $forum, $cm, $course, $ownpost, $reply, false, $ratings,
  4272. '', '', $postread, true, $forumtracked)) {
  4273. $ratingsmenuused = true;
  4274. }
  4275. switch ($mode) {
  4276. case FORUM_MODE_FLATOLDEST :
  4277. case FORUM_MODE_FLATNEWEST :
  4278. default:
  4279. if (forum_print_posts_flat($course, $cm, $forum, $discussion, $post, $mode, $ratings, $reply, $forumtracked, $posts)) {
  4280. $ratingsmenuused = true;
  4281. }
  4282. break;
  4283. case FORUM_MODE_THREADED :
  4284. if (forum_print_posts_threaded($course, $cm, $forum, $discussion, $post, 0, $ratings, $reply, $forumtracked, $posts)) {
  4285. $ratingsmenuused = true;
  4286. }
  4287. break;
  4288. case FORUM_MODE_NESTED :
  4289. if (forum_print_posts_nested($course, $cm, $forum, $discussion, $post, $ratings, $reply, $forumtracked, $posts)) {
  4290. $ratingsmenuused = true;
  4291. }
  4292. break;
  4293. }
  4294. if ($ratingsformused) {
  4295. if ($ratingsmenuused) {
  4296. echo '<div class="ratingsubmit">';
  4297. echo '<input type="submit" id="forumpostratingsubmit" value="'.get_string('sendinratings', 'forum').'" />';
  4298. if (ajaxenabled() && !empty($CFG->forum_ajaxrating)) { /// AJAX enabled, standard submission form
  4299. $rate_ajax_config_settings = array("pixpath"=>$CFG->pixpath, "wwwroot"=>$CFG->wwwroot, "sesskey"=>sesskey());
  4300. echo "<script type=\"text/javascript\">//<![CDATA[\n".
  4301. "var rate_ajax_config = " . json_encode($rate_ajax_config_settings) . ";\n".
  4302. "init_rate_ajax();\n".
  4303. "//]]></script>\n";
  4304. }
  4305. if ($forum->scale < 0) {
  4306. if ($scale = get_record("scale", "id", abs($forum->scale))) {
  4307. print_scale_menu_helpbutton($course->id, $scale );
  4308. }
  4309. }
  4310. echo '</div>';
  4311. }
  4312. echo '</div>';
  4313. echo '</form>';
  4314. }
  4315. }
  4316. /**
  4317. *
  4318. */
  4319. function forum_print_posts_flat($course, &$cm, $forum, $discussion, $post, $mode, $ratings, $reply, $forumtracked, $posts) {
  4320. global $USER, $CFG;
  4321. $link = false;
  4322. $ratingsmenuused = false;
  4323. if ($mode == FORUM_MODE_FLATNEWEST) {
  4324. $sort = "ORDER BY created DESC";
  4325. } else {
  4326. $sort = "ORDER BY created ASC";
  4327. }
  4328. foreach ($posts as $post) {
  4329. if (!$post->parent) {
  4330. continue;
  4331. }
  4332. $post->subject = format_string($post->subject);
  4333. $ownpost = ($USER->id == $post->userid);
  4334. $postread = !empty($post->postread);
  4335. if (forum_print_post($post, $discussion, $forum, $cm, $course, $ownpost, $reply, $link, $ratings,
  4336. '', '', $postread, true, $forumtracked)) {
  4337. $ratingsmenuused = true;
  4338. }
  4339. }
  4340. return $ratingsmenuused;
  4341. }
  4342. /**
  4343. * TODO document
  4344. */
  4345. function forum_print_posts_threaded($course, &$cm, $forum, $discussion, $parent, $depth, $ratings, $reply, $forumtracked, $posts) {
  4346. global $USER, $CFG;
  4347. $link = false;
  4348. $ratingsmenuused = false;
  4349. if (!empty($posts[$parent->id]->children)) {
  4350. $posts = $posts[$parent->id]->children;
  4351. $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
  4352. $canviewfullnames = has_capability('moodle/site:viewfullnames', $modcontext);
  4353. foreach ($posts as $post) {
  4354. echo '<div class="indent">';
  4355. if ($depth > 0) {
  4356. $ownpost = ($USER->id == $post->userid);
  4357. $post->subject = format_string($post->subject);
  4358. $postread = !empty($post->postread);
  4359. if (forum_print_post($post, $discussion, $forum, $cm, $course, $ownpost, $reply, $link, $ratings,
  4360. '', '', $postread, true, $forumtracked)) {
  4361. $ratingsmenuused = true;
  4362. }
  4363. } else {
  4364. if (!forum_user_can_see_post($forum, $discussion, $post, NULL, $cm)) {
  4365. echo "</div>\n";
  4366. continue;
  4367. }
  4368. $by = new object();
  4369. $by->name = fullname($post, $canviewfullnames);
  4370. $by->date = userdate($post->modified);
  4371. if ($forumtracked) {
  4372. if (!empty($post->postread)) {
  4373. $style = '<span class="forumthread read">';
  4374. } else {
  4375. $style = '<span class="forumthread unread">';
  4376. }
  4377. } else {
  4378. $style = '<span class="forumthread">';
  4379. }
  4380. echo $style."<a name=\"$post->id\"></a>".
  4381. "<a href=\"discuss.php?d=$post->discussion&amp;parent=$post->id\">".format_string($post->subject,true)."</a> ";
  4382. print_string("bynameondate", "forum", $by);
  4383. echo "</span>";
  4384. }
  4385. if (forum_print_posts_threaded($course, $cm, $forum, $discussion, $post, $depth-1, $ratings, $reply, $forumtracked, $posts)) {
  4386. $ratingsmenuused = true;
  4387. }
  4388. echo "</div>\n";
  4389. }
  4390. }
  4391. return $ratingsmenuused;
  4392. }
  4393. /**
  4394. *
  4395. */
  4396. function forum_print_posts_nested($course, &$cm, $forum, $discussion, $parent, $ratings, $reply, $forumtracked, $posts) {
  4397. global $USER, $CFG;
  4398. $link = false;
  4399. $ratingsmenuused = false;
  4400. if (!empty($posts[$parent->id]->children)) {
  4401. $posts = $posts[$parent->id]->children;
  4402. foreach ($posts as $post) {
  4403. echo '<div class="indent">';
  4404. if (empty($USER->id)) {
  4405. $ownpost = false;
  4406. } else {
  4407. $ownpost = ($USER->id == $post->userid);
  4408. }
  4409. $post->subject = format_string($post->subject);
  4410. $postread = !empty($post->postread);
  4411. if (forum_print_post($post, $discussion, $forum, $cm, $course, $ownpost, $reply, $link, $ratings,
  4412. '', '', $postread, true, $forumtracked)) {
  4413. $ratingsmenuused = true;
  4414. }
  4415. if (forum_print_posts_nested($course, $cm, $forum, $discussion, $post, $ratings, $reply, $forumtracked, $posts)) {
  4416. $ratingsmenuused = true;
  4417. }
  4418. echo "</div>\n";
  4419. }
  4420. }
  4421. return $ratingsmenuused;
  4422. }
  4423. /**
  4424. * Returns all forum posts since a given time in specified forum.
  4425. */
  4426. function forum_get_recent_mod_activity(&$activities, &$index, $timestart, $courseid, $cmid, $userid=0, $groupid=0) {
  4427. global $CFG, $COURSE, $USER;
  4428. if ($COURSE->id == $courseid) {
  4429. $course = $COURSE;
  4430. } else {
  4431. $course = get_record('course', 'id', $courseid);
  4432. }
  4433. $modinfo =& get_fast_modinfo($course);
  4434. $cm = $modinfo->cms[$cmid];
  4435. if ($userid) {
  4436. $userselect = "AND u.id = $userid";
  4437. } else {
  4438. $userselect = "";
  4439. }
  4440. if ($groupid) {
  4441. $groupselect = "AND gm.groupid = $groupid";
  4442. $groupjoin = "JOIN {$CFG->prefix}groups_members gm ON gm.userid=u.id";
  4443. } else {
  4444. $groupselect = "";
  4445. $groupjoin = "";
  4446. }
  4447. if (!$posts = get_records_sql("SELECT p.*, f.type AS forumtype, d.forum, d.groupid,
  4448. d.timestart, d.timeend, d.userid AS duserid,
  4449. u.firstname, u.lastname, u.email, u.picture, u.imagealt
  4450. FROM {$CFG->prefix}forum_posts p
  4451. JOIN {$CFG->prefix}forum_discussions d ON d.id = p.discussion
  4452. JOIN {$CFG->prefix}forum f ON f.id = d.forum
  4453. JOIN {$CFG->prefix}user u ON u.id = p.userid
  4454. $groupjoin
  4455. WHERE p.created > $timestart AND f.id = $cm->instance
  4456. $userselect $groupselect
  4457. ORDER BY p.id ASC")) { // order by initial posting date
  4458. return;
  4459. }
  4460. $groupmode = groups_get_activity_groupmode($cm, $course);
  4461. $cm_context = get_context_instance(CONTEXT_MODULE, $cm->id);
  4462. $viewhiddentimed = has_capability('mod/forum:viewhiddentimedposts', $cm_context);
  4463. $accessallgroups = has_capability('moodle/site:accessallgroups', $cm_context);
  4464. if (is_null($modinfo->groups)) {
  4465. $modinfo->groups = groups_get_user_groups($course->id); // load all my groups and cache it in modinfo
  4466. }
  4467. $printposts = array();
  4468. foreach ($posts as $post) {
  4469. if (!empty($CFG->forum_enabletimedposts) and $USER->id != $post->duserid
  4470. and (($post->timestart > 0 and $post->timestart > time()) or ($post->timeend > 0 and $post->timeend < time()))) {
  4471. if (!$viewhiddentimed) {
  4472. continue;
  4473. }
  4474. }
  4475. if ($groupmode) {
  4476. if ($post->groupid == -1 or $groupmode == VISIBLEGROUPS or $accessallgroups) {
  4477. // oki (Open discussions have groupid -1)
  4478. } else {
  4479. // separate mode
  4480. if (isguestuser()) {
  4481. // shortcut
  4482. continue;
  4483. }
  4484. if (!array_key_exists($post->groupid, $modinfo->groups[0])) {
  4485. continue;
  4486. }
  4487. }
  4488. }
  4489. $printposts[] = $post;
  4490. }
  4491. if (!$printposts) {
  4492. return;
  4493. }
  4494. $aname = format_string($cm->name,true);
  4495. foreach ($printposts as $post) {
  4496. $tmpactivity = new object();
  4497. $tmpactivity->type = 'forum';
  4498. $tmpactivity->cmid = $cm->id;
  4499. $tmpactivity->name = $aname;
  4500. $tmpactivity->sectionnum = $cm->sectionnum;
  4501. $tmpactivity->timestamp = $post->modified;
  4502. $tmpactivity->content = new object();
  4503. $tmpactivity->content->id = $post->id;
  4504. $tmpactivity->content->discussion = $post->discussion;
  4505. $tmpactivity->content->subject = format_string($post->subject);
  4506. $tmpactivity->content->parent = $post->parent;
  4507. $tmpactivity->user = new object();
  4508. $tmpactivity->user->id = $post->userid;
  4509. $tmpactivity->user->firstname = $post->firstname;
  4510. $tmpactivity->user->lastname = $post->lastname;
  4511. $tmpactivity->user->picture = $post->picture;
  4512. $tmpactivity->user->imagealt = $post->imagealt;
  4513. $tmpactivity->user->email = $post->email;
  4514. $activities[$index++] = $tmpactivity;
  4515. }
  4516. return;
  4517. }
  4518. /**
  4519. *
  4520. */
  4521. function forum_print_recent_mod_activity($activity, $courseid, $detail, $modnames, $viewfullnames) {
  4522. global $CFG;
  4523. if ($activity->content->parent) {
  4524. $class = 'reply';
  4525. } else {
  4526. $class = 'discussion';
  4527. }
  4528. echo '<table border="0" cellpadding="3" cellspacing="0" class="forum-recent">';
  4529. echo "<tr><td class=\"userpicture\" valign=\"top\">";
  4530. print_user_picture($activity->user, $courseid);
  4531. echo "</td><td class=\"$class\">";
  4532. echo '<div class="title">';
  4533. if ($detail) {
  4534. $aname = s($activity->name);
  4535. echo "<img src=\"$CFG->modpixpath/$activity->type/icon.gif\" ".
  4536. "class=\"icon\" alt=\"{$aname}\" />";
  4537. }
  4538. echo "<a href=\"$CFG->wwwroot/mod/forum/discuss.php?d={$activity->content->discussion}"
  4539. ."#p{$activity->content->id}\">{$activity->content->subject}</a>";
  4540. echo '</div>';
  4541. echo '<div class="user">';
  4542. $fullname = fullname($activity->user, $viewfullnames);
  4543. echo "<a href=\"$CFG->wwwroot/user/view.php?id={$activity->user->id}&amp;course=$courseid\">"
  4544. ."{$fullname}</a> - ".userdate($activity->timestamp);
  4545. echo '</div>';
  4546. echo "</td></tr></table>";
  4547. return;
  4548. }
  4549. /**
  4550. * recursively sets the discussion field to $discussionid on $postid and all its children
  4551. * used when pruning a post
  4552. */
  4553. function forum_change_discussionid($postid, $discussionid) {
  4554. set_field('forum_posts', 'discussion', $discussionid, 'id', $postid);
  4555. if ($posts = get_records('forum_posts', 'parent', $postid)) {
  4556. foreach ($posts as $post) {
  4557. forum_change_discussionid($post->id, $discussionid);
  4558. }
  4559. }
  4560. return true;
  4561. }
  4562. /**
  4563. * Prints the editing button on subscribers page
  4564. */
  4565. function forum_update_subscriptions_button($courseid, $forumid) {
  4566. global $CFG, $USER;
  4567. if (!empty($USER->subscriptionsediting)) {
  4568. $string = get_string('turneditingoff');
  4569. $edit = "off";
  4570. } else {
  4571. $string = get_string('turneditingon');
  4572. $edit = "on";
  4573. }
  4574. return "<form $CFG->frametarget method=\"get\" action=\"$CFG->wwwroot/mod/forum/subscribers.php\">".
  4575. "<input type=\"hidden\" name=\"id\" value=\"$forumid\" />".
  4576. "<input type=\"hidden\" name=\"edit\" value=\"$edit\" />".
  4577. "<input type=\"submit\" value=\"$string\" /></form>";
  4578. }
  4579. /*
  4580. * This function gets run whenever a role is assigned to a user in a context
  4581. *
  4582. * @param integer $userid
  4583. * @param object $context
  4584. * @return bool
  4585. */
  4586. function forum_role_assign($userid, $context, $roleid) {
  4587. // check to see if this role comes with mod/forum:initialsubscriptions
  4588. $cap = role_context_capabilities($roleid, $context, 'mod/forum:initialsubscriptions');
  4589. $cap1 = role_context_capabilities($roleid, $context, 'moodle/course:view');
  4590. // we are checking the role because has_capability() will pull this capability out
  4591. // from other roles this user might have and resolve them, which is no good
  4592. // the role needs course view to
  4593. if (isset($cap['mod/forum:initialsubscriptions']) && $cap['mod/forum:initialsubscriptions'] == CAP_ALLOW &&
  4594. isset($cap1['moodle/course:view']) && $cap1['moodle/course:view'] == CAP_ALLOW) {
  4595. return forum_add_user_default_subscriptions($userid, $context);
  4596. } else {
  4597. // MDL-8981, do not subscribe to forum
  4598. return true;
  4599. }
  4600. }
  4601. /**
  4602. * This function gets run whenever a role is assigned to a user in a context
  4603. *
  4604. * @param integer $userid
  4605. * @param object $context
  4606. * @return bool
  4607. */
  4608. function forum_role_unassign($userid, $context) {
  4609. if (empty($context->contextlevel)) {
  4610. return false;
  4611. }
  4612. forum_remove_user_subscriptions($userid, $context);
  4613. forum_remove_user_tracking($userid, $context);
  4614. return true;
  4615. }
  4616. /**
  4617. * Add subscriptions for new users
  4618. */
  4619. function forum_add_user_default_subscriptions($userid, $context) {
  4620. if (empty($context->contextlevel)) {
  4621. return false;
  4622. }
  4623. switch ($context->contextlevel) {
  4624. case CONTEXT_SYSTEM: // For the whole site
  4625. $rs = get_recordset('course', '', '', '', 'id');
  4626. while ($course = rs_fetch_next_record($rs)) {
  4627. $subcontext = get_context_instance(CONTEXT_COURSE, $course->id);
  4628. forum_add_user_default_subscriptions($userid, $subcontext);
  4629. }
  4630. rs_close($rs);
  4631. break;
  4632. case CONTEXT_COURSECAT: // For a whole category
  4633. $rs = get_recordset('course', 'category', $context->instanceid, '', 'id');
  4634. while ($course = rs_fetch_next_record($rs)) {
  4635. $subcontext = get_context_instance(CONTEXT_COURSE, $course->id);
  4636. forum_add_user_default_subscriptions($userid, $subcontext);
  4637. }
  4638. rs_close($rs);
  4639. if ($categories = get_records('course_categories', 'parent', $context->instanceid)) {
  4640. foreach ($categories as $category) {
  4641. $subcontext = get_context_instance(CONTEXT_COURSECAT, $category->id);
  4642. forum_add_user_default_subscriptions($userid, $subcontext);
  4643. }
  4644. }
  4645. break;
  4646. case CONTEXT_COURSE: // For a whole course
  4647. if ($course = get_record('course', 'id', $context->instanceid)) {
  4648. if ($forums = get_all_instances_in_course('forum', $course, $userid, false)) {
  4649. foreach ($forums as $forum) {
  4650. if ($forum->forcesubscribe != FORUM_INITIALSUBSCRIBE) {
  4651. continue;
  4652. }
  4653. if ($modcontext = get_context_instance(CONTEXT_MODULE, $forum->coursemodule)) {
  4654. if (has_capability('mod/forum:viewdiscussion', $modcontext, $userid)) {
  4655. forum_subscribe($userid, $forum->id);
  4656. }
  4657. }
  4658. }
  4659. }
  4660. }
  4661. break;
  4662. case CONTEXT_MODULE: // Just one forum
  4663. if ($cm = get_coursemodule_from_id('forum', $context->instanceid)) {
  4664. if ($forum = get_record('forum', 'id', $cm->instance)) {
  4665. if ($forum->forcesubscribe != FORUM_INITIALSUBSCRIBE) {
  4666. continue;
  4667. }
  4668. if (has_capability('mod/forum:viewdiscussion', $context, $userid)) {
  4669. forum_subscribe($userid, $forum->id);
  4670. }
  4671. }
  4672. }
  4673. break;
  4674. }
  4675. return true;
  4676. }
  4677. /**
  4678. * Remove subscriptions for a user in a context
  4679. */
  4680. function forum_remove_user_subscriptions($userid, $context) {
  4681. global $CFG;
  4682. if (empty($context->contextlevel)) {
  4683. return false;
  4684. }
  4685. switch ($context->contextlevel) {
  4686. case CONTEXT_SYSTEM: // For the whole site
  4687. //if ($courses = get_my_courses($userid)) {
  4688. // find all courses in which this user has a forum subscription
  4689. if ($courses = get_records_sql("SELECT c.id
  4690. FROM {$CFG->prefix}course c,
  4691. {$CFG->prefix}forum_subscriptions fs,
  4692. {$CFG->prefix}forum f
  4693. WHERE c.id = f.course AND f.id = fs.forum AND fs.userid = $userid
  4694. GROUP BY c.id")) {
  4695. foreach ($courses as $course) {
  4696. $subcontext = get_context_instance(CONTEXT_COURSE, $course->id);
  4697. forum_remove_user_subscriptions($userid, $subcontext);
  4698. }
  4699. }
  4700. break;
  4701. case CONTEXT_COURSECAT: // For a whole category
  4702. if ($courses = get_records('course', 'category', $context->instanceid, '', 'id')) {
  4703. foreach ($courses as $course) {
  4704. $subcontext = get_context_instance(CONTEXT_COURSE, $course->id);
  4705. forum_remove_user_subscriptions($userid, $subcontext);
  4706. }
  4707. }
  4708. if ($categories = get_records('course_categories', 'parent', $context->instanceid, '', 'id')) {
  4709. foreach ($categories as $category) {
  4710. $subcontext = get_context_instance(CONTEXT_COURSECAT, $category->id);
  4711. forum_remove_user_subscriptions($userid, $subcontext);
  4712. }
  4713. }
  4714. break;
  4715. case CONTEXT_COURSE: // For a whole course
  4716. if ($course = get_record('course', 'id', $context->instanceid, '', '', '', '', 'id')) {
  4717. // find all forums in which this user has a subscription, and its coursemodule id
  4718. if ($forums = get_records_sql("SELECT f.id, cm.id as coursemodule
  4719. FROM {$CFG->prefix}forum f,
  4720. {$CFG->prefix}modules m,
  4721. {$CFG->prefix}course_modules cm,
  4722. {$CFG->prefix}forum_subscriptions fs
  4723. WHERE fs.userid = $userid AND f.course = $context->instanceid
  4724. AND fs.forum = f.id AND cm.instance = f.id
  4725. AND cm.module = m.id AND m.name = 'forum'")) {
  4726. foreach ($forums as $forum) {
  4727. if ($modcontext = get_context_instance(CONTEXT_MODULE, $forum->coursemodule)) {
  4728. if (!has_capability('mod/forum:viewdiscussion', $modcontext, $userid)) {
  4729. forum_unsubscribe($userid, $forum->id);
  4730. }
  4731. }
  4732. }
  4733. }
  4734. }
  4735. break;
  4736. case CONTEXT_MODULE: // Just one forum
  4737. if ($cm = get_coursemodule_from_id('forum', $context->instanceid)) {
  4738. if ($forum = get_record('forum', 'id', $cm->instance)) {
  4739. if (!has_capability('mod/forum:viewdiscussion', $context, $userid)) {
  4740. forum_unsubscribe($userid, $forum->id);
  4741. }
  4742. }
  4743. }
  4744. break;
  4745. }
  4746. return true;
  4747. }
  4748. // Functions to do with read tracking.
  4749. /**
  4750. * Remove post tracking for a user in a context
  4751. */
  4752. function forum_remove_user_tracking($userid, $context) {
  4753. global $CFG;
  4754. if (empty($context->contextlevel)) {
  4755. return false;
  4756. }
  4757. switch ($context->contextlevel) {
  4758. case CONTEXT_SYSTEM: // For the whole site
  4759. // find all courses in which this user has tracking info
  4760. $allcourses = array();
  4761. if ($courses = get_records_sql("SELECT c.id
  4762. FROM {$CFG->prefix}course c,
  4763. {$CFG->prefix}forum_read fr,
  4764. {$CFG->prefix}forum f
  4765. WHERE c.id = f.course AND f.id = fr.forumid AND fr.userid = $userid
  4766. GROUP BY c.id")) {
  4767. $allcourses = $allcourses + $courses;
  4768. }
  4769. if ($courses = get_records_sql("SELECT c.id
  4770. FROM {$CFG->prefix}course c,
  4771. {$CFG->prefix}forum_track_prefs ft,
  4772. {$CFG->prefix}forum f
  4773. WHERE c.id = f.course AND f.id = ft.forumid AND ft.userid = $userid")) {
  4774. $allcourses = $allcourses + $courses;
  4775. }
  4776. foreach ($allcourses as $course) {
  4777. $subcontext = get_context_instance(CONTEXT_COURSE, $course->id);
  4778. forum_remove_user_tracking($userid, $subcontext);
  4779. }
  4780. break;
  4781. case CONTEXT_COURSECAT: // For a whole category
  4782. if ($courses = get_records('course', 'category', $context->instanceid, '', 'id')) {
  4783. foreach ($courses as $course) {
  4784. $subcontext = get_context_instance(CONTEXT_COURSE, $course->id);
  4785. forum_remove_user_tracking($userid, $subcontext);
  4786. }
  4787. }
  4788. if ($categories = get_records('course_categories', 'parent', $context->instanceid, '', 'id')) {
  4789. foreach ($categories as $category) {
  4790. $subcontext = get_context_instance(CONTEXT_COURSECAT, $category->id);
  4791. forum_remove_user_tracking($userid, $subcontext);
  4792. }
  4793. }
  4794. break;
  4795. case CONTEXT_COURSE: // For a whole course
  4796. if ($course = get_record('course', 'id', $context->instanceid, '', '', '', '', 'id')) {
  4797. // find all forums in which this user has reading tracked
  4798. if ($forums = get_records_sql("SELECT f.id, cm.id as coursemodule
  4799. FROM {$CFG->prefix}forum f,
  4800. {$CFG->prefix}modules m,
  4801. {$CFG->prefix}course_modules cm,
  4802. {$CFG->prefix}forum_read fr
  4803. WHERE fr.userid = $userid AND f.course = $context->instanceid
  4804. AND fr.forumid = f.id AND cm.instance = f.id
  4805. AND cm.module = m.id AND m.name = 'forum'")) {
  4806. foreach ($forums as $forum) {
  4807. if ($modcontext = get_context_instance(CONTEXT_MODULE, $forum->coursemodule)) {
  4808. if (!has_capability('mod/forum:viewdiscussion', $modcontext, $userid)) {
  4809. forum_tp_delete_read_records($userid, -1, -1, $forum->id);
  4810. }
  4811. }
  4812. }
  4813. }
  4814. // find all forums in which this user has a disabled tracking
  4815. if ($forums = get_records_sql("SELECT f.id, cm.id as coursemodule
  4816. FROM {$CFG->prefix}forum f,
  4817. {$CFG->prefix}modules m,
  4818. {$CFG->prefix}course_modules cm,
  4819. {$CFG->prefix}forum_track_prefs ft
  4820. WHERE ft.userid = $userid AND f.course = $context->instanceid
  4821. AND ft.forumid = f.id AND cm.instance = f.id
  4822. AND cm.module = m.id AND m.name = 'forum'")) {
  4823. foreach ($forums as $forum) {
  4824. if ($modcontext = get_context_instance(CONTEXT_MODULE, $forum->coursemodule)) {
  4825. if (!has_capability('mod/forum:viewdiscussion', $modcontext, $userid)) {
  4826. delete_records('forum_track_prefs', 'userid', $userid, 'forumid', $forum->id);
  4827. }
  4828. }
  4829. }
  4830. }
  4831. }
  4832. break;
  4833. case CONTEXT_MODULE: // Just one forum
  4834. if ($cm = get_coursemodule_from_id('forum', $context->instanceid)) {
  4835. if ($forum = get_record('forum', 'id', $cm->instance)) {
  4836. if (!has_capability('mod/forum:viewdiscussion', $context, $userid)) {
  4837. delete_records('forum_track_prefs', 'userid', $userid, 'forumid', $forum->id);
  4838. forum_tp_delete_read_records($userid, -1, -1, $forum->id);
  4839. }
  4840. }
  4841. }
  4842. break;
  4843. }
  4844. return true;
  4845. }
  4846. /**
  4847. * Mark posts as read.
  4848. * @param object $user object
  4849. * @param array $postids array of post ids
  4850. * @return boolean success
  4851. */
  4852. function forum_tp_mark_posts_read($user, $postids) {
  4853. global $CFG;
  4854. if (!forum_tp_can_track_forums(false, $user)) {
  4855. return true;
  4856. }
  4857. $status = true;
  4858. $now = time();
  4859. $cutoffdate = $now - ($CFG->forum_oldpostdays * 24 * 3600);
  4860. if (empty($postids)) {
  4861. return true;
  4862. } else if (count($postids) > 200) {
  4863. while ($part = array_splice($postids, 0, 200)) {
  4864. $status = forum_tp_mark_posts_read($user, $part) && $status;
  4865. }
  4866. return $status;
  4867. }
  4868. $sql = "SELECT id
  4869. FROM {$CFG->prefix}forum_read
  4870. WHERE userid = $user->id AND postid IN (".implode(',', $postids).")";
  4871. if ($existing = get_records_sql($sql)) {
  4872. $existing = array_keys($existing);
  4873. } else {
  4874. $existing = array();
  4875. }
  4876. $new = array_diff($postids, $existing);
  4877. if ($new) {
  4878. $sql = "INSERT INTO {$CFG->prefix}forum_read (userid, postid, discussionid, forumid, firstread, lastread)
  4879. SELECT $user->id, p.id, p.discussion, d.forum, $now, $now
  4880. FROM {$CFG->prefix}forum_posts p
  4881. JOIN {$CFG->prefix}forum_discussions d ON d.id = p.discussion
  4882. JOIN {$CFG->prefix}forum f ON f.id = d.forum
  4883. LEFT JOIN {$CFG->prefix}forum_track_prefs tf ON (tf.userid = $user->id AND tf.forumid = f.id)
  4884. WHERE p.id IN (".implode(',', $new).")
  4885. AND p.modified >= $cutoffdate
  4886. AND (f.trackingtype = ".FORUM_TRACKING_ON."
  4887. OR (f.trackingtype = ".FORUM_TRACKING_OPTIONAL." AND tf.id IS NULL))";
  4888. $status = execute_sql($sql, false) && $status;
  4889. }
  4890. if ($existing) {
  4891. $sql = "UPDATE {$CFG->prefix}forum_read
  4892. SET lastread = $now
  4893. WHERE userid = $user->id AND postid IN (".implode(',', $existing).")";
  4894. $status = execute_sql($sql, false) && $status;
  4895. }
  4896. return $status;
  4897. }
  4898. /**
  4899. * Mark post as read.
  4900. */
  4901. function forum_tp_add_read_record($userid, $postid) {
  4902. global $CFG;
  4903. $now = time();
  4904. $cutoffdate = $now - ($CFG->forum_oldpostdays * 24 * 3600);
  4905. if (!record_exists('forum_read', 'userid', $userid, 'postid', $postid)) {
  4906. $sql = "INSERT INTO {$CFG->prefix}forum_read (userid, postid, discussionid, forumid, firstread, lastread)
  4907. SELECT $userid, p.id, p.discussion, d.forum, $now, $now
  4908. FROM {$CFG->prefix}forum_posts p
  4909. JOIN {$CFG->prefix}forum_discussions d ON d.id = p.discussion
  4910. WHERE p.id = $postid AND p.modified >= $cutoffdate";
  4911. return execute_sql($sql, false);
  4912. } else {
  4913. $sql = "UPDATE {$CFG->prefix}forum_read
  4914. SET lastread = $now
  4915. WHERE userid = $userid AND postid = $userid";
  4916. return execute_sql($sql, false);
  4917. }
  4918. }
  4919. /**
  4920. * Returns all records in the 'forum_read' table matching the passed keys, indexed
  4921. * by userid.
  4922. */
  4923. function forum_tp_get_read_records($userid=-1, $postid=-1, $discussionid=-1, $forumid=-1) {
  4924. $select = '';
  4925. if ($userid > -1) {
  4926. if ($select != '') $select .= ' AND ';
  4927. $select .= 'userid = \''.$userid.'\'';
  4928. }
  4929. if ($postid > -1) {
  4930. if ($select != '') $select .= ' AND ';
  4931. $select .= 'postid = \''.$postid.'\'';
  4932. }
  4933. if ($discussionid > -1) {
  4934. if ($select != '') $select .= ' AND ';
  4935. $select .= 'discussionid = \''.$discussionid.'\'';
  4936. }
  4937. if ($forumid > -1) {
  4938. if ($select != '') $select .= ' AND ';
  4939. $select .= 'forumid = \''.$forumid.'\'';
  4940. }
  4941. return get_records_select('forum_read', $select);
  4942. }
  4943. /**
  4944. * Returns all read records for the provided user and discussion, indexed by postid.
  4945. */
  4946. function forum_tp_get_discussion_read_records($userid, $discussionid) {
  4947. $select = 'userid = \''.$userid.'\' AND discussionid = \''.$discussionid.'\'';
  4948. $fields = 'postid, firstread, lastread';
  4949. return get_records_select('forum_read', $select, '', $fields);
  4950. }
  4951. /**
  4952. * If its an old post, do nothing. If the record exists, the maintenance will clear it up later.
  4953. */
  4954. function forum_tp_mark_post_read($userid, $post, $forumid) {
  4955. if (!forum_tp_is_post_old($post)) {
  4956. return forum_tp_add_read_record($userid, $post->id);
  4957. } else {
  4958. return true;
  4959. }
  4960. }
  4961. /**
  4962. * Marks a whole forum as read, for a given user
  4963. */
  4964. function forum_tp_mark_forum_read($user, $forumid, $groupid=false) {
  4965. global $CFG;
  4966. $cutoffdate = time() - ($CFG->forum_oldpostdays*24*60*60);
  4967. $groupsel = "";
  4968. if ($groupid !== false) {
  4969. $groupsel = " AND (d.groupid = $groupid OR d.groupid = -1)";
  4970. }
  4971. $sql = "SELECT p.id
  4972. FROM {$CFG->prefix}forum_posts p
  4973. LEFT JOIN {$CFG->prefix}forum_discussions d ON d.id = p.discussion
  4974. LEFT JOIN {$CFG->prefix}forum_read r ON (r.postid = p.id AND r.userid = $user->id)
  4975. WHERE d.forum = $forumid
  4976. AND p.modified >= $cutoffdate AND r.id is NULL
  4977. $groupsel";
  4978. if ($posts = get_records_sql($sql)) {
  4979. $postids = array_keys($posts);
  4980. return forum_tp_mark_posts_read($user, $postids);
  4981. }
  4982. return true;
  4983. }
  4984. /**
  4985. * Marks a whole discussion as read, for a given user
  4986. */
  4987. function forum_tp_mark_discussion_read($user, $discussionid) {
  4988. global $CFG;
  4989. $cutoffdate = time() - ($CFG->forum_oldpostdays*24*60*60);
  4990. $sql = "SELECT p.id
  4991. FROM {$CFG->prefix}forum_posts p
  4992. LEFT JOIN {$CFG->prefix}forum_read r ON (r.postid = p.id AND r.userid = $user->id)
  4993. WHERE p.discussion = $discussionid
  4994. AND p.modified >= $cutoffdate AND r.id is NULL";
  4995. if ($posts = get_records_sql($sql)) {
  4996. $postids = array_keys($posts);
  4997. return forum_tp_mark_posts_read($user, $postids);
  4998. }
  4999. return true;
  5000. }
  5001. /**
  5002. *
  5003. */
  5004. function forum_tp_is_post_read($userid, $post) {
  5005. return (forum_tp_is_post_old($post) ||
  5006. record_exists('forum_read', 'userid', $userid, 'postid', $post->id));
  5007. }
  5008. /**
  5009. *
  5010. */
  5011. function forum_tp_is_post_old($post, $time=null) {
  5012. global $CFG;
  5013. if (is_null($time)) {
  5014. $time = time();
  5015. }
  5016. return ($post->modified < ($time - ($CFG->forum_oldpostdays * 24 * 3600)));
  5017. }
  5018. /**
  5019. * Returns the count of records for the provided user and discussion.
  5020. */
  5021. function forum_tp_count_discussion_read_records($userid, $discussionid) {
  5022. global $CFG;
  5023. $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0;
  5024. $sql = 'SELECT COUNT(DISTINCT p.id) '.
  5025. 'FROM '.$CFG->prefix.'forum_discussions d '.
  5026. 'LEFT JOIN '.$CFG->prefix.'forum_read r ON d.id = r.discussionid AND r.userid = '.$userid.' '.
  5027. 'LEFT JOIN '.$CFG->prefix.'forum_posts p ON p.discussion = d.id '.
  5028. 'AND (p.modified < '.$cutoffdate.' OR p.id = r.postid) '.
  5029. 'WHERE d.id = '.$discussionid;
  5030. return (count_records_sql($sql));
  5031. }
  5032. /**
  5033. * Returns the count of records for the provided user and discussion.
  5034. */
  5035. function forum_tp_count_discussion_unread_posts($userid, $discussionid) {
  5036. global $CFG;
  5037. $cutoffdate = isset($CFG->forum_oldpostdays) ? (time() - ($CFG->forum_oldpostdays*24*60*60)) : 0;
  5038. $sql = 'SELECT COUNT(p.id) '.
  5039. 'FROM '.$CFG->prefix.'forum_posts p '.
  5040. 'LEFT JOIN '.$CFG->prefix.'forum_read r ON r.postid = p.id AND r.userid = '.$userid.' '.
  5041. 'WHERE p.discussion = '.$discussionid.' '.
  5042. 'AND p.modified >= '.$cutoffdate.' AND r.id is NULL';
  5043. return (count_records_sql($sql));
  5044. }
  5045. /**
  5046. * Returns the count of posts for the provided forum and [optionally] group.
  5047. */
  5048. function forum_tp_count_forum_posts($forumid, $groupid=false) {
  5049. global $CFG;
  5050. $sql = 'SELECT COUNT(*) '.
  5051. 'FROM '.$CFG->prefix.'forum_posts fp,'.$CFG->prefix.'forum_discussions fd '.
  5052. 'WHERE fd.forum = '.$forumid.' AND fp.discussion = fd.id';
  5053. if ($groupid !== false) {
  5054. $sql .= ' AND (fd.groupid = '.$groupid.' OR fd.groupid = -1)';
  5055. }
  5056. $count = count_records_sql($sql);
  5057. return $count;
  5058. }
  5059. /**
  5060. * Returns the count of records for the provided user and forum and [optionally] group.
  5061. */
  5062. function forum_tp_count_forum_read_records($userid, $forumid, $groupid=false) {
  5063. global $CFG;
  5064. $cutoffdate = time() - ($CFG->forum_oldpostdays*24*60*60);
  5065. $groupsel = '';
  5066. if ($groupid !== false) {
  5067. $groupsel = "AND (d.groupid = $groupid OR d.groupid = -1)";
  5068. }
  5069. $sql = "SELECT COUNT(p.id)
  5070. FROM {$CFG->prefix}forum_posts p
  5071. JOIN {$CFG->prefix}forum_discussions d ON d.id = p.discussion
  5072. LEFT JOIN {$CFG->prefix}forum_read r ON (r.postid = p.id AND r.userid= $userid)
  5073. WHERE d.forum = $forumid
  5074. AND (p.modified < $cutoffdate OR (p.modified >= $cutoffdate AND r.id IS NOT NULL))
  5075. $groupsel";
  5076. return get_field_sql($sql);
  5077. }
  5078. /**
  5079. * Returns the count of records for the provided user and course.
  5080. * Please note that group access is ignored!
  5081. */
  5082. function forum_tp_get_course_unread_posts($userid, $courseid) {
  5083. global $CFG;
  5084. $now = round(time(), -2); // db cache friendliness
  5085. $cutoffdate = $now - ($CFG->forum_oldpostdays*24*60*60);
  5086. if (!empty($CFG->forum_enabletimedposts)) {
  5087. $timedsql = "AND d.timestart < $now AND (d.timeend = 0 OR d.timeend > $now)";
  5088. } else {
  5089. $timedsql = "";
  5090. }
  5091. $sql = "SELECT f.id, COUNT(p.id) AS unread
  5092. FROM {$CFG->prefix}forum_posts p
  5093. JOIN {$CFG->prefix}forum_discussions d ON d.id = p.discussion
  5094. JOIN {$CFG->prefix}forum f ON f.id = d.forum
  5095. JOIN {$CFG->prefix}course c ON c.id = f.course
  5096. LEFT JOIN {$CFG->prefix}forum_read r ON (r.postid = p.id AND r.userid = $userid)
  5097. LEFT JOIN {$CFG->prefix}forum_track_prefs tf ON (tf.userid = $userid AND tf.forumid = f.id)
  5098. WHERE f.course = $courseid
  5099. AND p.modified >= $cutoffdate AND r.id is NULL
  5100. AND (f.trackingtype = ".FORUM_TRACKING_ON."
  5101. OR (f.trackingtype = ".FORUM_TRACKING_OPTIONAL." AND tf.id IS NULL))
  5102. $timedsql
  5103. GROUP BY f.id";
  5104. if ($return = get_records_sql($sql)) {
  5105. return $return;
  5106. }
  5107. return array();
  5108. }
  5109. /**
  5110. * Returns the count of records for the provided user and forum and [optionally] group.
  5111. */
  5112. function forum_tp_count_forum_unread_posts($cm, $course) {
  5113. global $CFG, $USER;
  5114. static $readcache = array();
  5115. $forumid = $cm->instance;
  5116. if (!isset($readcache[$course->id])) {
  5117. $readcache[$course->id] = array();
  5118. if ($counts = forum_tp_get_course_unread_posts($USER->id, $course->id)) {
  5119. foreach ($counts as $count) {
  5120. $readcache[$course->id][$count->id] = $count->unread;
  5121. }
  5122. }
  5123. }
  5124. if (empty($readcache[$course->id][$forumid])) {
  5125. // no need to check group mode ;-)
  5126. return 0;
  5127. }
  5128. $groupmode = groups_get_activity_groupmode($cm, $course);
  5129. if ($groupmode != SEPARATEGROUPS) {
  5130. return $readcache[$course->id][$forumid];
  5131. }
  5132. if (has_capability('moodle/site:accessallgroups', get_context_instance(CONTEXT_MODULE, $cm->id))) {
  5133. return $readcache[$course->id][$forumid];
  5134. }
  5135. require_once($CFG->dirroot.'/course/lib.php');
  5136. $modinfo =& get_fast_modinfo($course);
  5137. if (is_null($modinfo->groups)) {
  5138. $modinfo->groups = groups_get_user_groups($course->id, $USER->id);
  5139. }
  5140. if (empty($CFG->enablegroupings)) {
  5141. $mygroups = $modinfo->groups[0];
  5142. } else {
  5143. if (array_key_exists($cm->groupingid, $modinfo->groups)) {
  5144. $mygroups = $modinfo->groups[$cm->groupingid];
  5145. } else {
  5146. $mygroups = false; // Will be set below
  5147. }
  5148. }
  5149. // add all groups posts
  5150. if (empty($mygroups)) {
  5151. $mygroups = array(-1=>-1);
  5152. } else {
  5153. $mygroups[-1] = -1;
  5154. }
  5155. $mygroups = implode(',', $mygroups);
  5156. $now = round(time(), -2); // db cache friendliness
  5157. $cutoffdate = $now - ($CFG->forum_oldpostdays*24*60*60);
  5158. if (!empty($CFG->forum_enabletimedposts)) {
  5159. $timedsql = "AND d.timestart < $now AND (d.timeend = 0 OR d.timeend > $now)";
  5160. } else {
  5161. $timedsql = "";
  5162. }
  5163. $sql = "SELECT COUNT(p.id)
  5164. FROM {$CFG->prefix}forum_posts p
  5165. JOIN {$CFG->prefix}forum_discussions d ON p.discussion = d.id
  5166. LEFT JOIN {$CFG->prefix}forum_read r ON (r.postid = p.id AND r.userid = $USER->id)
  5167. WHERE d.forum = $forumid
  5168. AND p.modified >= $cutoffdate AND r.id is NULL
  5169. $timedsql
  5170. AND d.groupid IN ($mygroups)";
  5171. return get_field_sql($sql);
  5172. }
  5173. /**
  5174. * Deletes read records for the specified index. At least one parameter must be specified.
  5175. */
  5176. function forum_tp_delete_read_records($userid=-1, $postid=-1, $discussionid=-1, $forumid=-1) {
  5177. $select = '';
  5178. if ($userid > -1) {
  5179. if ($select != '') $select .= ' AND ';
  5180. $select .= 'userid = \''.$userid.'\'';
  5181. }
  5182. if ($postid > -1) {
  5183. if ($select != '') $select .= ' AND ';
  5184. $select .= 'postid = \''.$postid.'\'';
  5185. }
  5186. if ($discussionid > -1) {
  5187. if ($select != '') $select .= ' AND ';
  5188. $select .= 'discussionid = \''.$discussionid.'\'';
  5189. }
  5190. if ($forumid > -1) {
  5191. if ($select != '') $select .= ' AND ';
  5192. $select .= 'forumid = \''.$forumid.'\'';
  5193. }
  5194. if ($select == '') {
  5195. return false;
  5196. }
  5197. else {
  5198. return delete_records_select('forum_read', $select);
  5199. }
  5200. }
  5201. /**
  5202. * Get a list of forums not tracked by the user.
  5203. *
  5204. * @param int $userid The id of the user to use.
  5205. * @param int $courseid The id of the course being checked.
  5206. * @return mixed An array indexed by forum id, or false.
  5207. */
  5208. function forum_tp_get_untracked_forums($userid, $courseid) {
  5209. global $CFG;
  5210. $sql = "SELECT f.id
  5211. FROM {$CFG->prefix}forum f
  5212. LEFT JOIN {$CFG->prefix}forum_track_prefs ft ON (ft.forumid = f.id AND ft.userid = $userid)
  5213. WHERE f.course = $courseid
  5214. AND (f.trackingtype = ".FORUM_TRACKING_OFF."
  5215. OR (f.trackingtype = ".FORUM_TRACKING_OPTIONAL." AND ft.id IS NOT NULL))";
  5216. if ($forums = get_records_sql($sql)) {
  5217. foreach ($forums as $forum) {
  5218. $forums[$forum->id] = $forum;
  5219. }
  5220. return $forums;
  5221. } else {
  5222. return array();
  5223. }
  5224. }
  5225. /**
  5226. * Determine if a user can track forums and optionally a particular forum.
  5227. * Checks the site settings, the user settings and the forum settings (if
  5228. * requested).
  5229. *
  5230. * @param mixed $forum The forum object to test, or the int id (optional).
  5231. * @param mixed $userid The user object to check for (optional).
  5232. * @return boolean
  5233. */
  5234. function forum_tp_can_track_forums($forum=false, $user=false) {
  5235. global $USER, $CFG;
  5236. // if possible, avoid expensive
  5237. // queries
  5238. if (empty($CFG->forum_trackreadposts)) {
  5239. return false;
  5240. }
  5241. if ($user === false) {
  5242. $user = $USER;
  5243. }
  5244. if (isguestuser($user) or empty($user->id)) {
  5245. return false;
  5246. }
  5247. if ($forum === false) {
  5248. // general abitily to track forums
  5249. return (bool)$user->trackforums;
  5250. }
  5251. // Work toward always passing an object...
  5252. if (is_numeric($forum)) {
  5253. debugging('Better use proper forum object.', DEBUG_DEVELOPER);
  5254. $forum = get_record('forum', 'id', $forum, '','','','', 'id,trackingtype');
  5255. }
  5256. $forumallows = ($forum->trackingtype == FORUM_TRACKING_OPTIONAL);
  5257. $forumforced = ($forum->trackingtype == FORUM_TRACKING_ON);
  5258. return ($forumforced || $forumallows) && !empty($user->trackforums);
  5259. }
  5260. /**
  5261. * Tells whether a specific forum is tracked by the user. A user can optionally
  5262. * be specified. If not specified, the current user is assumed.
  5263. *
  5264. * @param mixed $forum If int, the id of the forum being checked; if object, the forum object
  5265. * @param int $userid The id of the user being checked (optional).
  5266. * @return boolean
  5267. */
  5268. function forum_tp_is_tracked($forum, $user=false) {
  5269. global $USER, $CFG;
  5270. if ($user === false) {
  5271. $user = $USER;
  5272. }
  5273. if (isguestuser($user) or empty($user->id)) {
  5274. return false;
  5275. }
  5276. // Work toward always passing an object...
  5277. if (is_numeric($forum)) {
  5278. debugging('Better use proper forum object.', DEBUG_DEVELOPER);
  5279. $forum = get_record('forum', 'id', $forum);
  5280. }
  5281. if (!forum_tp_can_track_forums($forum, $user)) {
  5282. return false;
  5283. }
  5284. $forumallows = ($forum->trackingtype == FORUM_TRACKING_OPTIONAL);
  5285. $forumforced = ($forum->trackingtype == FORUM_TRACKING_ON);
  5286. return $forumforced ||
  5287. ($forumallows && get_record('forum_track_prefs', 'userid', $user->id, 'forumid', $forum->id) === false);
  5288. }
  5289. /**
  5290. *
  5291. */
  5292. function forum_tp_start_tracking($forumid, $userid=false) {
  5293. global $USER;
  5294. if ($userid === false) {
  5295. $userid = $USER->id;
  5296. }
  5297. return delete_records('forum_track_prefs', 'userid', $userid, 'forumid', $forumid);
  5298. }
  5299. /**
  5300. *
  5301. */
  5302. function forum_tp_stop_tracking($forumid, $userid=false) {
  5303. global $USER;
  5304. if ($userid === false) {
  5305. $userid = $USER->id;
  5306. }
  5307. if (!record_exists('forum_track_prefs', 'userid', $userid, 'forumid', $forumid)) {
  5308. $track_prefs = new object();
  5309. $track_prefs->userid = $userid;
  5310. $track_prefs->forumid = $forumid;
  5311. insert_record('forum_track_prefs', $track_prefs);
  5312. }
  5313. return forum_tp_delete_read_records($userid, -1, -1, $forumid);
  5314. }
  5315. /**
  5316. * Clean old records from the forum_read table.
  5317. */
  5318. function forum_tp_clean_read_records() {
  5319. global $CFG;
  5320. if (!isset($CFG->forum_oldpostdays)) {
  5321. return;
  5322. }
  5323. // Look for records older than the cutoffdate that are still in the forum_read table.
  5324. $cutoffdate = time() - ($CFG->forum_oldpostdays*24*60*60);
  5325. //first get the oldest tracking present - we need tis to speedup the next delete query
  5326. $sql = "SELECT MIN(fp.modified) AS first
  5327. FROM {$CFG->prefix}forum_posts fp
  5328. JOIN {$CFG->prefix}forum_read fr ON fr.postid=fp.id";
  5329. if (!$first = get_field_sql($sql)) {
  5330. // nothing to delete;
  5331. return;
  5332. }
  5333. // now delete old tracking info
  5334. $sql = "DELETE
  5335. FROM {$CFG->prefix}forum_read
  5336. WHERE postid IN (SELECT fp.id
  5337. FROM {$CFG->prefix}forum_posts fp
  5338. WHERE fp.modified >= $first AND fp.modified < $cutoffdate)";
  5339. execute_sql($sql, false);
  5340. }
  5341. /**
  5342. * Sets the last post for a given discussion
  5343. **/
  5344. function forum_discussion_update_last_post($discussionid) {
  5345. global $CFG, $db;
  5346. // Check the given discussion exists
  5347. if (!record_exists('forum_discussions', 'id', $discussionid)) {
  5348. return false;
  5349. }
  5350. // Use SQL to find the last post for this discussion
  5351. $sql = 'SELECT id, userid, modified '.
  5352. 'FROM '.$CFG->prefix.'forum_posts '.
  5353. 'WHERE discussion='.$discussionid.' '.
  5354. 'ORDER BY modified DESC ';
  5355. // Lets go find the last post
  5356. if (($lastpost = get_record_sql($sql, true))) {
  5357. $discussionobject = new Object;
  5358. $discussionobject->id = $discussionid;
  5359. $discussionobject->usermodified = $lastpost->userid;
  5360. $discussionobject->timemodified = $lastpost->modified;
  5361. if (update_record('forum_discussions', $discussionobject)) {
  5362. return $lastpost->id;
  5363. }
  5364. }
  5365. // To get here either we couldn't find a post for the discussion (weird)
  5366. // or we couldn't update the discussion record (weird x2)
  5367. return false;
  5368. }
  5369. /**
  5370. *
  5371. */
  5372. function forum_get_view_actions() {
  5373. return array('view discussion','search','forum','forums','subscribers');
  5374. }
  5375. /**
  5376. *
  5377. */
  5378. function forum_get_post_actions() {
  5379. return array('add discussion','add post','delete discussion','delete post','move discussion','prune post','update post');
  5380. }
  5381. /**
  5382. * this function returns all the separate forum ids, given a courseid
  5383. * @param int $courseid
  5384. * @return array
  5385. */
  5386. function forum_get_separate_modules($courseid) {
  5387. global $CFG,$db;
  5388. $forummodule = get_record("modules", "name", "forum");
  5389. $sql = 'SELECT f.id, f.id FROM '.$CFG->prefix.'forum f, '.$CFG->prefix.'course_modules cm WHERE
  5390. f.id = cm.instance AND cm.module ='.$forummodule->id.' AND cm.visible = 1 AND cm.course = '.$courseid.'
  5391. AND cm.groupmode ='.SEPARATEGROUPS;
  5392. return get_records_sql($sql);
  5393. }
  5394. /**
  5395. *
  5396. */
  5397. function forum_check_throttling($forum, $cm=null) {
  5398. global $USER, $CFG;
  5399. if (is_numeric($forum)) {
  5400. $forum = get_record('forum','id',$forum);
  5401. }
  5402. if (!is_object($forum)) {
  5403. return false; // this is broken.
  5404. }
  5405. if (empty($forum->blockafter)) {
  5406. return true;
  5407. }
  5408. if (empty($forum->blockperiod)) {
  5409. return true;
  5410. }
  5411. if (!$cm) {
  5412. if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
  5413. error('Course Module ID was incorrect');
  5414. }
  5415. }
  5416. $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
  5417. if(!has_capability('mod/forum:throttlingapplies', $modcontext)) {
  5418. return true;
  5419. }
  5420. // get the number of posts in the last period we care about
  5421. $timenow = time();
  5422. $timeafter = $timenow - $forum->blockperiod;
  5423. $numposts = count_records_sql('SELECT COUNT(p.id) FROM '.$CFG->prefix.'forum_posts p'
  5424. .' JOIN '.$CFG->prefix.'forum_discussions d'
  5425. .' ON p.discussion = d.id WHERE d.forum = '.$forum->id
  5426. .' AND p.userid = '.$USER->id.' AND p.created > '.$timeafter);
  5427. $a = new object();
  5428. $a->blockafter = $forum->blockafter;
  5429. $a->numposts = $numposts;
  5430. $a->blockperiod = get_string('secondstotime'.$forum->blockperiod);
  5431. if ($forum->blockafter <= $numposts) {
  5432. print_error('forumblockingtoomanyposts', 'error', $CFG->wwwroot.'/mod/forum/view.php?f='.$forum->id, $a);
  5433. }
  5434. if ($forum->warnafter <= $numposts) {
  5435. notify(get_string('forumblockingalmosttoomanyposts','forum',$a));
  5436. }
  5437. }
  5438. /**
  5439. * Removes all grades from gradebook
  5440. * @param int $courseid
  5441. * @param string optional type
  5442. */
  5443. function forum_reset_gradebook($courseid, $type='') {
  5444. global $CFG;
  5445. $type = $type ? "AND f.type='$type'" : '';
  5446. $sql = "SELECT f.*, cm.idnumber as cmidnumber, f.course as courseid
  5447. FROM {$CFG->prefix}forum f, {$CFG->prefix}course_modules cm, {$CFG->prefix}modules m
  5448. WHERE m.name='forum' AND m.id=cm.module AND cm.instance=f.id AND f.course=$courseid $type";
  5449. if ($forums = get_records_sql($sql)) {
  5450. foreach ($forums as $forum) {
  5451. forum_grade_item_update($forum, 'reset');
  5452. }
  5453. }
  5454. }
  5455. /**
  5456. * This function is used by the reset_course_userdata function in moodlelib.
  5457. * This function will remove all posts from the specified forum
  5458. * and clean up any related data.
  5459. * @param $data the data submitted from the reset course.
  5460. * @return array status array
  5461. */
  5462. function forum_reset_userdata($data) {
  5463. global $CFG;
  5464. require_once($CFG->libdir.'/filelib.php');
  5465. $componentstr = get_string('modulenameplural', 'forum');
  5466. $status = array();
  5467. $removeposts = false;
  5468. if (!empty($data->reset_forum_all)) {
  5469. $removeposts = true;
  5470. $typesql = "";
  5471. $typesstr = get_string('resetforumsall', 'forum');
  5472. $types = array();
  5473. } else if (!empty($data->reset_forum_types)){
  5474. $removeposts = true;
  5475. $typesql = "";
  5476. $types = array();
  5477. $forum_types_all = forum_get_forum_types_all();
  5478. foreach ($data->reset_forum_types as $type) {
  5479. if (!array_key_exists($type, $forum_types_all)) {
  5480. continue;
  5481. }
  5482. $typesql .= " AND f.type='$type'";
  5483. $types[] = $forum_types_all[$type];
  5484. }
  5485. $typesstr = get_string('resetforums', 'forum').': '.implode(', ', $types);
  5486. }
  5487. $alldiscussionssql = "SELECT fd.id
  5488. FROM {$CFG->prefix}forum_discussions fd, {$CFG->prefix}forum f
  5489. WHERE f.course={$data->courseid} AND f.id=fd.forum";
  5490. $allforumssql = "SELECT f.id
  5491. FROM {$CFG->prefix}forum f
  5492. WHERE f.course={$data->courseid}";
  5493. $allpostssql = "SELECT fp.id
  5494. FROM {$CFG->prefix}forum_posts fp, {$CFG->prefix}forum_discussions fd, {$CFG->prefix}forum f
  5495. WHERE f.course={$data->courseid} AND f.id=fd.forum AND fd.id=fp.discussion";
  5496. if ($removeposts) {
  5497. $discussionssql = "$alldiscussionssql $typesql";
  5498. $forumssql = "$allforumssql $typesql";
  5499. $postssql = "$allpostssql $typesql";
  5500. // first delete all read flags
  5501. delete_records_select('forum_read', "forumid IN ($forumssql)");
  5502. // remove tracking prefs
  5503. delete_records_select('forum_track_prefs', "forumid IN ($forumssql)");
  5504. // remove posts from queue
  5505. delete_records_select('forum_queue', "discussionid IN ($discussionssql)");
  5506. // remove ratings
  5507. delete_records_select('forum_ratings', "post IN ($postssql)");
  5508. // all posts - initial posts must be kept in single simple discussion forums
  5509. delete_records_select('forum_posts', "discussion IN ($discussionssql) AND parent <> 0"); // first all children
  5510. delete_records_select('forum_posts', "discussion IN ($discussionssql AND f.type <> 'single') AND parent = 0"); // now the initial posts for non single simple
  5511. // finally all discussions except single simple forums
  5512. delete_records_select('forum_discussions', "forum IN ($forumssql AND f.type <> 'single')");
  5513. // now get rid of all attachments
  5514. if ($forums = get_records_sql($forumssql)) {
  5515. foreach ($forums as $forumid=>$unused) {
  5516. fulldelete($CFG->dataroot.'/'.$data->courseid.'/moddata/forum/'.$forumid);
  5517. }
  5518. }
  5519. // remove all grades from gradebook
  5520. if (empty($data->reset_gradebook_grades)) {
  5521. if (empty($types)) {
  5522. forum_reset_gradebook($data->courseid);
  5523. } else {
  5524. foreach ($types as $type) {
  5525. forum_reset_gradebook($data->courseid, $type);
  5526. }
  5527. }
  5528. }
  5529. $status[] = array('component'=>$componentstr, 'item'=>$typesstr, 'error'=>false);
  5530. }
  5531. // remove all ratings
  5532. if (!empty($data->reset_forum_ratings)) {
  5533. delete_records_select('forum_ratings', "post IN ($allpostssql)");
  5534. // remove all grades from gradebook
  5535. if (empty($data->reset_gradebook_grades)) {
  5536. forum_reset_gradebook($data->courseid);
  5537. }
  5538. }
  5539. // remove all subscriptions unconditionally - even for users still enrolled in course
  5540. if (!empty($data->reset_forum_subscriptions)) {
  5541. delete_records_select('forum_subscriptions', "forum IN ($allforumssql)");
  5542. $status[] = array('component'=>$componentstr, 'item'=>get_string('resetsubscriptions','forum'), 'error'=>false);
  5543. }
  5544. // remove all tracking prefs unconditionally - even for users still enrolled in course
  5545. if (!empty($data->reset_forum_track_prefs)) {
  5546. delete_records_select('forum_track_prefs', "forumid IN ($allforumssql)");
  5547. $status[] = array('component'=>$componentstr, 'item'=>get_string('resettrackprefs','forum'), 'error'=>false);
  5548. }
  5549. /// updating dates - shift may be negative too
  5550. if ($data->timeshift) {
  5551. shift_course_mod_dates('forum', array('assesstimestart', 'assesstimefinish'), $data->timeshift, $data->courseid);
  5552. $status[] = array('component'=>$componentstr, 'item'=>get_string('datechanged'), 'error'=>false);
  5553. }
  5554. return $status;
  5555. }
  5556. /**
  5557. * Called by course/reset.php
  5558. * @param $mform form passed by reference
  5559. */
  5560. function forum_reset_course_form_definition(&$mform) {
  5561. $mform->addElement('header', 'forumheader', get_string('modulenameplural', 'forum'));
  5562. $mform->addElement('checkbox', 'reset_forum_all', get_string('resetforumsall','forum'));
  5563. $mform->addElement('select', 'reset_forum_types', get_string('resetforums', 'forum'), forum_get_forum_types_all(), array('multiple' => 'multiple'));
  5564. $mform->setAdvanced('reset_forum_types');
  5565. $mform->disabledIf('reset_forum_types', 'reset_forum_all', 'checked');
  5566. $mform->addElement('checkbox', 'reset_forum_subscriptions', get_string('resetsubscriptions','forum'));
  5567. $mform->setAdvanced('reset_forum_subscriptions');
  5568. $mform->addElement('checkbox', 'reset_forum_track_prefs', get_string('resettrackprefs','forum'));
  5569. $mform->setAdvanced('reset_forum_track_prefs');
  5570. $mform->disabledIf('reset_forum_track_prefs', 'reset_forum_all', 'checked');
  5571. $mform->addElement('checkbox', 'reset_forum_ratings', get_string('deleteallratings'));
  5572. $mform->disabledIf('reset_forum_ratings', 'reset_forum_all', 'checked');
  5573. }
  5574. /**
  5575. * Course reset form defaults.
  5576. */
  5577. function forum_reset_course_form_defaults($course) {
  5578. return array('reset_forum_all'=>1, 'reset_forum_subscriptions'=>0, 'reset_forum_track_prefs'=>0, 'reset_forum_ratings'=>1);
  5579. }
  5580. /**
  5581. * Converts a forum to use the Roles System
  5582. * @param $forum - a forum object with the same attributes as a record
  5583. * from the forum database table
  5584. * @param $forummodid - the id of the forum module, from the modules table
  5585. * @param $teacherroles - array of roles that have moodle/legacy:teacher
  5586. * @param $studentroles - array of roles that have moodle/legacy:student
  5587. * @param $guestroles - array of roles that have moodle/legacy:guest
  5588. * @param $cmid - the course_module id for this forum instance
  5589. * @return boolean - forum was converted or not
  5590. */
  5591. function forum_convert_to_roles($forum, $forummodid, $teacherroles=array(),
  5592. $studentroles=array(), $guestroles=array(), $cmid=NULL) {
  5593. global $CFG;
  5594. if (!isset($forum->open) && !isset($forum->assesspublic)) {
  5595. // We assume that this forum has already been converted to use the
  5596. // Roles System. Columns forum.open and forum.assesspublic get dropped
  5597. // once the forum module has been upgraded to use Roles.
  5598. return false;
  5599. }
  5600. if ($forum->type == 'teacher') {
  5601. // Teacher forums should be converted to normal forums that
  5602. // use the Roles System to implement the old behavior.
  5603. // Note:
  5604. // Seems that teacher forums were never backed up in 1.6 since they
  5605. // didn't have an entry in the course_modules table.
  5606. require_once($CFG->dirroot.'/course/lib.php');
  5607. if (count_records('forum_discussions', 'forum', $forum->id) == 0) {
  5608. // Delete empty teacher forums.
  5609. delete_records('forum', 'id', $forum->id);
  5610. } else {
  5611. // Create a course module for the forum and assign it to
  5612. // section 0 in the course.
  5613. $mod = new object;
  5614. $mod->course = $forum->course;
  5615. $mod->module = $forummodid;
  5616. $mod->instance = $forum->id;
  5617. $mod->section = 0;
  5618. $mod->visible = 0; // Hide the forum
  5619. $mod->visibleold = 0; // Hide the forum
  5620. $mod->groupmode = 0;
  5621. if (!$cmid = add_course_module($mod)) {
  5622. error('Could not create new course module instance for the teacher forum');
  5623. } else {
  5624. $mod->coursemodule = $cmid;
  5625. if (!$sectionid = add_mod_to_section($mod)) {
  5626. error('Could not add converted teacher forum instance to section 0 in the course');
  5627. } else {
  5628. if (!set_field('course_modules', 'section', $sectionid, 'id', $cmid)) {
  5629. error('Could not update course module with section id');
  5630. }
  5631. }
  5632. }
  5633. // Change the forum type to general.
  5634. $forum->type = 'general';
  5635. if (!update_record('forum', $forum)) {
  5636. error('Could not change forum from type teacher to type general');
  5637. }
  5638. $context = get_context_instance(CONTEXT_MODULE, $cmid);
  5639. // Create overrides for default student and guest roles (prevent).
  5640. foreach ($studentroles as $studentrole) {
  5641. assign_capability('mod/forum:viewdiscussion', CAP_PREVENT, $studentrole->id, $context->id);
  5642. assign_capability('mod/forum:viewhiddentimedposts', CAP_PREVENT, $studentrole->id, $context->id);
  5643. assign_capability('mod/forum:startdiscussion', CAP_PREVENT, $studentrole->id, $context->id);
  5644. assign_capability('mod/forum:replypost', CAP_PREVENT, $studentrole->id, $context->id);
  5645. assign_capability('mod/forum:viewrating', CAP_PREVENT, $studentrole->id, $context->id);
  5646. assign_capability('mod/forum:viewanyrating', CAP_PREVENT, $studentrole->id, $context->id);
  5647. assign_capability('mod/forum:rate', CAP_PREVENT, $studentrole->id, $context->id);
  5648. assign_capability('mod/forum:createattachment', CAP_PREVENT, $studentrole->id, $context->id);
  5649. assign_capability('mod/forum:deleteownpost', CAP_PREVENT, $studentrole->id, $context->id);
  5650. assign_capability('mod/forum:deleteanypost', CAP_PREVENT, $studentrole->id, $context->id);
  5651. assign_capability('mod/forum:splitdiscussions', CAP_PREVENT, $studentrole->id, $context->id);
  5652. assign_capability('mod/forum:movediscussions', CAP_PREVENT, $studentrole->id, $context->id);
  5653. assign_capability('mod/forum:editanypost', CAP_PREVENT, $studentrole->id, $context->id);
  5654. assign_capability('mod/forum:viewqandawithoutposting', CAP_PREVENT, $studentrole->id, $context->id);
  5655. assign_capability('mod/forum:viewsubscribers', CAP_PREVENT, $studentrole->id, $context->id);
  5656. assign_capability('mod/forum:managesubscriptions', CAP_PREVENT, $studentrole->id, $context->id);
  5657. assign_capability('mod/forum:throttlingapplies', CAP_PREVENT, $studentrole->id, $context->id);
  5658. }
  5659. foreach ($guestroles as $guestrole) {
  5660. assign_capability('mod/forum:viewdiscussion', CAP_PREVENT, $guestrole->id, $context->id);
  5661. assign_capability('mod/forum:viewhiddentimedposts', CAP_PREVENT, $guestrole->id, $context->id);
  5662. assign_capability('mod/forum:startdiscussion', CAP_PREVENT, $guestrole->id, $context->id);
  5663. assign_capability('mod/forum:replypost', CAP_PREVENT, $guestrole->id, $context->id);
  5664. assign_capability('mod/forum:viewrating', CAP_PREVENT, $guestrole->id, $context->id);
  5665. assign_capability('mod/forum:viewanyrating', CAP_PREVENT, $guestrole->id, $context->id);
  5666. assign_capability('mod/forum:rate', CAP_PREVENT, $guestrole->id, $context->id);
  5667. assign_capability('mod/forum:createattachment', CAP_PREVENT, $guestrole->id, $context->id);
  5668. assign_capability('mod/forum:deleteownpost', CAP_PREVENT, $guestrole->id, $context->id);
  5669. assign_capability('mod/forum:deleteanypost', CAP_PREVENT, $guestrole->id, $context->id);
  5670. assign_capability('mod/forum:splitdiscussions', CAP_PREVENT, $guestrole->id, $context->id);
  5671. assign_capability('mod/forum:movediscussions', CAP_PREVENT, $guestrole->id, $context->id);
  5672. assign_capability('mod/forum:editanypost', CAP_PREVENT, $guestrole->id, $context->id);
  5673. assign_capability('mod/forum:viewqandawithoutposting', CAP_PREVENT, $guestrole->id, $context->id);
  5674. assign_capability('mod/forum:viewsubscribers', CAP_PREVENT, $guestrole->id, $context->id);
  5675. assign_capability('mod/forum:managesubscriptions', CAP_PREVENT, $guestrole->id, $context->id);
  5676. assign_capability('mod/forum:throttlingapplies', CAP_PREVENT, $guestrole->id, $context->id);
  5677. }
  5678. }
  5679. } else {
  5680. // Non-teacher forum.
  5681. if (empty($cmid)) {
  5682. // We were not given the course_module id. Try to find it.
  5683. if (!$cm = get_coursemodule_from_instance('forum', $forum->id)) {
  5684. notify('Could not get the course module for the forum');
  5685. return false;
  5686. } else {
  5687. $cmid = $cm->id;
  5688. }
  5689. }
  5690. $context = get_context_instance(CONTEXT_MODULE, $cmid);
  5691. // $forum->open defines what students can do:
  5692. // 0 = No discussions, no replies
  5693. // 1 = No discussions, but replies are allowed
  5694. // 2 = Discussions and replies are allowed
  5695. switch ($forum->open) {
  5696. case 0:
  5697. foreach ($studentroles as $studentrole) {
  5698. assign_capability('mod/forum:startdiscussion', CAP_PREVENT, $studentrole->id, $context->id);
  5699. assign_capability('mod/forum:replypost', CAP_PREVENT, $studentrole->id, $context->id);
  5700. }
  5701. break;
  5702. case 1:
  5703. foreach ($studentroles as $studentrole) {
  5704. assign_capability('mod/forum:startdiscussion', CAP_PREVENT, $studentrole->id, $context->id);
  5705. assign_capability('mod/forum:replypost', CAP_ALLOW, $studentrole->id, $context->id);
  5706. }
  5707. break;
  5708. case 2:
  5709. foreach ($studentroles as $studentrole) {
  5710. assign_capability('mod/forum:startdiscussion', CAP_ALLOW, $studentrole->id, $context->id);
  5711. assign_capability('mod/forum:replypost', CAP_ALLOW, $studentrole->id, $context->id);
  5712. }
  5713. break;
  5714. }
  5715. // $forum->assessed defines whether forum rating is turned
  5716. // on (1 or 2) and who can rate posts:
  5717. // 1 = Everyone can rate posts
  5718. // 2 = Only teachers can rate posts
  5719. switch ($forum->assessed) {
  5720. case 1:
  5721. foreach ($studentroles as $studentrole) {
  5722. assign_capability('mod/forum:rate', CAP_ALLOW, $studentrole->id, $context->id);
  5723. }
  5724. foreach ($teacherroles as $teacherrole) {
  5725. assign_capability('mod/forum:rate', CAP_ALLOW, $teacherrole->id, $context->id);
  5726. }
  5727. break;
  5728. case 2:
  5729. foreach ($studentroles as $studentrole) {
  5730. assign_capability('mod/forum:rate', CAP_PREVENT, $studentrole->id, $context->id);
  5731. }
  5732. foreach ($teacherroles as $teacherrole) {
  5733. assign_capability('mod/forum:rate', CAP_ALLOW, $teacherrole->id, $context->id);
  5734. }
  5735. break;
  5736. }
  5737. // $forum->assesspublic defines whether students can see
  5738. // everybody's ratings:
  5739. // 0 = Students can only see their own ratings
  5740. // 1 = Students can see everyone's ratings
  5741. switch ($forum->assesspublic) {
  5742. case 0:
  5743. foreach ($studentroles as $studentrole) {
  5744. assign_capability('mod/forum:viewanyrating', CAP_PREVENT, $studentrole->id, $context->id);
  5745. }
  5746. foreach ($teacherroles as $teacherrole) {
  5747. assign_capability('mod/forum:viewanyrating', CAP_ALLOW, $teacherrole->id, $context->id);
  5748. }
  5749. break;
  5750. case 1:
  5751. foreach ($studentroles as $studentrole) {
  5752. assign_capability('mod/forum:viewanyrating', CAP_ALLOW, $studentrole->id, $context->id);
  5753. }
  5754. foreach ($teacherroles as $teacherrole) {
  5755. assign_capability('mod/forum:viewanyrating', CAP_ALLOW, $teacherrole->id, $context->id);
  5756. }
  5757. break;
  5758. }
  5759. if (empty($cm)) {
  5760. $cm = get_record('course_modules', 'id', $cmid);
  5761. }
  5762. // $cm->groupmode:
  5763. // 0 - No groups
  5764. // 1 - Separate groups
  5765. // 2 - Visible groups
  5766. switch ($cm->groupmode) {
  5767. case 0:
  5768. break;
  5769. case 1:
  5770. foreach ($studentroles as $studentrole) {
  5771. assign_capability('moodle/site:accessallgroups', CAP_PREVENT, $studentrole->id, $context->id);
  5772. }
  5773. foreach ($teacherroles as $teacherrole) {
  5774. assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $teacherrole->id, $context->id);
  5775. }
  5776. break;
  5777. case 2:
  5778. foreach ($studentroles as $studentrole) {
  5779. assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $studentrole->id, $context->id);
  5780. }
  5781. foreach ($teacherroles as $teacherrole) {
  5782. assign_capability('moodle/site:accessallgroups', CAP_ALLOW, $teacherrole->id, $context->id);
  5783. }
  5784. break;
  5785. }
  5786. }
  5787. return true;
  5788. }
  5789. /**
  5790. * Returns array of forum aggregate types
  5791. */
  5792. function forum_get_aggregate_types() {
  5793. return array (FORUM_AGGREGATE_NONE => get_string('aggregatenone', 'forum'),
  5794. FORUM_AGGREGATE_AVG => get_string('aggregateavg', 'forum'),
  5795. FORUM_AGGREGATE_COUNT => get_string('aggregatecount', 'forum'),
  5796. FORUM_AGGREGATE_MAX => get_string('aggregatemax', 'forum'),
  5797. FORUM_AGGREGATE_MIN => get_string('aggregatemin', 'forum'),
  5798. FORUM_AGGREGATE_SUM => get_string('aggregatesum', 'forum'));
  5799. }
  5800. /**
  5801. * Returns array of forum layout modes
  5802. */
  5803. function forum_get_layout_modes() {
  5804. return array (FORUM_MODE_FLATOLDEST => get_string('modeflatoldestfirst', 'forum'),
  5805. FORUM_MODE_FLATNEWEST => get_string('modeflatnewestfirst', 'forum'),
  5806. FORUM_MODE_THREADED => get_string('modethreaded', 'forum'),
  5807. FORUM_MODE_NESTED => get_string('modenested', 'forum'));
  5808. }
  5809. /**
  5810. * Returns array of forum types
  5811. */
  5812. function forum_get_forum_types() {
  5813. return array ('general' => get_string('generalforum', 'forum'),
  5814. 'eachuser' => get_string('eachuserforum', 'forum'),
  5815. 'single' => get_string('singleforum', 'forum'),
  5816. 'qanda' => get_string('qandaforum', 'forum'));
  5817. }
  5818. /**
  5819. * Returns array of all forum layout modes
  5820. */
  5821. function forum_get_forum_types_all() {
  5822. return array ('news' => get_string('namenews','forum'),
  5823. 'social' => get_string('namesocial','forum'),
  5824. 'general' => get_string('generalforum', 'forum'),
  5825. 'eachuser' => get_string('eachuserforum', 'forum'),
  5826. 'single' => get_string('singleforum', 'forum'),
  5827. 'qanda' => get_string('qandaforum', 'forum'));
  5828. }
  5829. /**
  5830. * Returns array of forum open modes
  5831. */
  5832. function forum_get_open_modes() {
  5833. return array ('2' => get_string('openmode2', 'forum'),
  5834. '1' => get_string('openmode1', 'forum'),
  5835. '0' => get_string('openmode0', 'forum') );
  5836. }
  5837. /**
  5838. * Returns all other caps used in module
  5839. */
  5840. function forum_get_extra_capabilities() {
  5841. return array('moodle/site:accessallgroups', 'moodle/site:viewfullnames', 'moodle/site:trustcontent');
  5842. }
  5843. /**
  5844. * Returns creation time of the first user's post in given discussion
  5845. * @global object $DB
  5846. * @param int $did Discussion id
  5847. * @param int $userid User id
  5848. * @return int|bool post creation time stamp or return false
  5849. */
  5850. function forum_get_user_posted_time($did, $userid) {
  5851. global $CFG;
  5852. $posttime = get_field('forum_posts', 'MIN(created)', 'userid', $userid, 'discussion', $did);
  5853. if (empty($posttime)) {
  5854. return false;
  5855. }
  5856. return $posttime;
  5857. }
  5858. ?>