Merge branch 'w21_MDL-32379_m23_forumcron' of git://github.com/skodak/moodle
authorDan Poltawski <dan@moodle.com>
Tue, 22 May 2012 05:05:28 +0000 (13:05 +0800)
committerDan Poltawski <dan@moodle.com>
Tue, 22 May 2012 05:05:28 +0000 (13:05 +0800)
1  2 
mod/forum/lib.php

diff --combined mod/forum/lib.php
@@@ -44,6 -44,11 +44,11 @@@ define('FORUM_TRACKING_OFF', 0)
  define('FORUM_TRACKING_OPTIONAL', 1);
  define('FORUM_TRACKING_ON', 2);
  
+ if (!defined('FORUM_CRON_USER_CACHE')) {
+     /** Defines how many full user records are cached in forum cron. */
+     define('FORUM_CRON_USER_CACHE', 5000);
+ }
  /// STANDARD FUNCTIONS ///////////////////////////////////////////////////////////
  
  /**
@@@ -378,6 -383,28 +383,28 @@@ function forum_get_email_message_id($po
      return '<'.hash('sha256',$postid.'to'.$usertoid.'@'.$hostname).'>';
  }
  
+ /**
+  * Removes properties from user record that are not necessary
+  * for sending post notifications.
+  * @param stdClass $user
+  * @return void, $user parameter is modified
+  */
+ function forum_cron_minimise_user_record(stdClass $user) {
+     // We store large amount of users in one huge array,
+     // make sure we do not store info there we do not actually need
+     // in mail generation code or messaging.
+     unset($user->institution);
+     unset($user->department);
+     unset($user->address);
+     unset($user->city);
+     unset($user->url);
+     unset($user->currentlogin);
+     unset($user->description);
+     unset($user->descriptionformat);
+ }
  /**
   * Function to be run periodically according to the moodle cron
   * Finds all posts that have yet to be mailed out, and mails them
@@@ -397,8 -424,11 +424,11 @@@ function forum_cron() 
  
      $site = get_site();
  
-     // all users that are subscribed to any post that needs sending
+     // All users that are subscribed to any post that needs sending,
+     // please increase $CFG->extramemorylimit on large sites that
+     // send notifications to a large number of users.
      $users = array();
+     $userscount = 0; // Cached user counter - count($users) in PHP is horribly slow!!!
  
      // status arrays
      $mailcount  = array();
                  $modcontext = get_context_instance(CONTEXT_MODULE, $coursemodules[$forumid]->id);
                  if ($subusers = forum_subscribed_users($courses[$courseid], $forums[$forumid], 0, $modcontext, "u.*")) {
                      foreach ($subusers as $postuser) {
-                         unset($postuser->description); // not necessary
                          // this user is subscribed to this forum
                          $subscribedusers[$forumid][$postuser->id] = $postuser->id;
-                         // this user is a user we have to process later
-                         $users[$postuser->id] = $postuser;
+                         $userscount++;
+                         if ($userscount > FORUM_CRON_USER_CACHE) {
+                             // Store minimal user info.
+                             $minuser = new stdClass();
+                             $minuser->id = $postuser->id;
+                             $users[$postuser->id] = $minuser;
+                         } else {
+                             // Cache full user record.
+                             forum_cron_minimise_user_record($postuser);
+                             $users[$postuser->id] = $postuser;
+                         }
                      }
-                     unset($subusers); // release memory
+                     // Release memory.
+                     unset($subusers);
+                     unset($postuser);
                  }
              }
  
  
              @set_time_limit(120); // terminate if processing of any account takes longer than 2 minutes
  
-             // set this so that the capabilities are cached, and environment matches receiving user
-             cron_setup_user($userto);
              mtrace('Processing user '.$userto->id);
  
-             // init caches
+             // Init user caches - we keep the cache for one cycle only,
+             // otherwise it could consume too much memory.
+             if (isset($userto->username)) {
+                 $userto = clone($userto);
+             } else {
+                 $userto = $DB->get_record('user', array('id' => $userto->id));
+                 forum_cron_minimise_user_record($userto);
+             }
              $userto->viewfullnames = array();
              $userto->canpost       = array();
              $userto->markposts     = array();
  
+             // set this so that the capabilities are cached, and environment matches receiving user
+             cron_setup_user($userto);
              // reset the caches
              foreach ($coursemodules as $forumid=>$unused) {
                  $coursemodules[$forumid]->cache       = new stdClass();
                  // Get info about the sending user
                  if (array_key_exists($post->userid, $users)) { // we might know him/her already
                      $userfrom = $users[$post->userid];
+                     if (!isset($userfrom->idnumber)) {
+                         // Minimalised user info, fetch full record.
+                         $userfrom = $DB->get_record('user', array('id' => $userfrom->id));
+                         forum_cron_minimise_user_record($userfrom);
+                     }
                  } else if ($userfrom = $DB->get_record('user', array('id' => $post->userid))) {
-                     unset($userfrom->description); // not necessary
-                     $users[$userfrom->id] = $userfrom; // fetch only once, we can add it to user list, it will be skipped anyway
+                     forum_cron_minimise_user_record($userfrom);
+                     // Fetch only once if possible, we can add it to user list, it will be skipped anyway.
+                     if ($userscount <= FORUM_CRON_USER_CACHE) {
+                         $userscount++;
+                         $users[$userfrom->id] = $userfrom;
+                     }
                  } else {
                      mtrace('Could not find user '.$post->userid);
                      continue;
                  if (!isset($userfrom->groups[$forum->id])) {
                      if (!isset($userfrom->groups)) {
                          $userfrom->groups = array();
-                         $users[$userfrom->id]->groups = array();
+                         if (isset($users[$userfrom->id])) {
+                             $users[$userfrom->id]->groups = array();
+                         }
                      }
                      $userfrom->groups[$forum->id] = groups_get_all_groups($course->id, $userfrom->id, $cm->groupingid);
-                     $users[$userfrom->id]->groups[$forum->id] = $userfrom->groups[$forum->id];
+                     if (isset($users[$userfrom->id])) {
+                         $users[$userfrom->id]->groups[$forum->id] = $userfrom->groups[$forum->id];
+                     }
                  }
  
                  // Make sure groups allow this user to see this email
  
              // mark processed posts as read
              forum_tp_mark_posts_read($userto, $userto->markposts);
+             unset($userto);
          }
      }
  
              $userdiscussions = array();
  
              foreach ($digestposts_rs as $digestpost) {
-                 if (!isset($users[$digestpost->userid])) {
-                     if ($user = $DB->get_record('user', array('id' => $digestpost->userid))) {
-                         $users[$digestpost->userid] = $user;
-                     } else {
-                         continue;
-                     }
-                 }
-                 $postuser = $users[$digestpost->userid];
                  if (!isset($posts[$digestpost->postid])) {
                      if ($post = $DB->get_record('forum_posts', array('id' => $digestpost->postid))) {
                          $posts[$digestpost->postid] = $post;
  
                  // First of all delete all the queue entries for this user
                  $DB->delete_records_select('forum_queue', "userid = ? AND timemodified < ?", array($userid, $digesttime));
-                 $userto = $users[$userid];
  
-                 // Override the language and timezone of the "current" user, so that
-                 // mail is customised for the receiver.
-                 cron_setup_user($userto);
-                 // init caches
+                 // Init user caches - we keep the cache for one cycle only,
+                 // otherwise it would unnecessarily consume memory.
+                 if (array_key_exists($userid, $users) and isset($users[$userid]->username)) {
+                     $userto = clone($users[$userid]);
+                 } else {
+                     $userto = $DB->get_record('user', array('id' => $userid));
+                     forum_cron_minimise_user_record($userto);
+                 }
                  $userto->viewfullnames = array();
                  $userto->canpost       = array();
                  $userto->markposts     = array();
  
+                 // Override the language and timezone of the "current" user, so that
+                 // mail is customised for the receiver.
+                 cron_setup_user($userto);
                  $postsubject = get_string('digestmailsubject', 'forum', format_string($site->shortname, true));
  
                  $headerdata = new stdClass();
  
                          if (array_key_exists($post->userid, $users)) { // we might know him/her already
                              $userfrom = $users[$post->userid];
+                             if (!isset($userfrom->idnumber)) {
+                                 $userfrom = $DB->get_record('user', array('id' => $userfrom->id));
+                                 forum_cron_minimise_user_record($userfrom);
+                             }
                          } else if ($userfrom = $DB->get_record('user', array('id' => $post->userid))) {
-                             $users[$userfrom->id] = $userfrom; // fetch only once, we can add it to user list, it will be skipped anyway
+                             forum_cron_minimise_user_record($userfrom);
+                             if ($userscount <= FORUM_CRON_USER_CACHE) {
+                                 $userscount++;
+                                 $users[$userfrom->id] = $userfrom;
+                             }
                          } else {
                              mtrace('Could not find user '.$post->userid);
                              continue;
                          if (!isset($userfrom->groups[$forum->id])) {
                              if (!isset($userfrom->groups)) {
                                  $userfrom->groups = array();
-                                 $users[$userfrom->id]->groups = array();
+                                 if (isset($users[$userfrom->id])) {
+                                     $users[$userfrom->id]->groups = array();
+                                 }
                              }
                              $userfrom->groups[$forum->id] = groups_get_all_groups($course->id, $userfrom->id, $cm->groupingid);
-                             $users[$userfrom->id]->groups[$forum->id] = $userfrom->groups[$forum->id];
+                             if (isset($users[$userfrom->id])) {
+                                 $users[$userfrom->id]->groups[$forum->id] = $userfrom->groups[$forum->id];
+                             }
                          }
  
                          $userfrom->customheaders = array ("Precedence: Bulk");
@@@ -3911,7 -3985,7 +3985,7 @@@ function forum_print_attachments($post
          foreach ($files as $file) {
              $filename = $file->get_filename();
              $mimetype = $file->get_mimetype();
 -            $iconimage = '<img src="'.$OUTPUT->pix_url(file_mimetype_icon($mimetype)).'" class="icon" alt="'.$mimetype.'" />';
 +            $iconimage = $OUTPUT->pix_icon(file_file_icon($file), get_mimetype_description($file), 'moodle', array('class' => 'icon'));
              $path = file_encode_url($CFG->wwwroot.'/pluginfile.php', '/'.$context->id.'/mod_forum/attachment/'.$post->id.'/'.$filename);
  
              if ($type == 'html') {