/**
  * Gets the names of all attachments (if any)
  * @return array Array of attachment names (may be empty). Names only,
  *   not including path to attachment folder
  */
 public function get_attachment_names()
 {
     $result = array();
     if (!$this->has_attachments()) {
         return $result;
     }
     $folder = $this->get_attachment_folder();
     $handle = forum_utils::opendir($folder);
     while (false !== ($name = readdir($handle))) {
         if ($name != '.' && $name != '..') {
             if (!is_dir("{$folder}/{$name}")) {
                 $result[] = $name;
             }
         }
     }
     closedir($handle);
     sort($result);
     return $result;
 }
 /**
  * Given a parent folder, deletes all folders within it (and their
  * contents) if they are older than 24 hours.
  * @param float $start Start time
  * @param string $folder Folder path
  */
 private static function clear_old_folders($start, $folder)
 {
     // Threshold is 24 hours ago
     $threshold = time() - 24 * 3600;
     // Loop through all files in folder
     $killed = 0;
     $spared = 0;
     $handle = forum_utils::opendir($folder);
     while (($file = readdir($handle)) !== false) {
         if ($file == '.' || $file == '..') {
             continue;
         }
         // Check it's a folder
         $target = "{$folder}/{$file}";
         if (!is_dir($target)) {
             continue;
         }
         // Check time
         if (filemtime($target) < $threshold) {
             // Older than threshold - delete
             if (!remove_dir($target)) {
                 mtrace("\nError deleting folder: {$target}");
             }
             $killed++;
         } else {
             $spared++;
         }
     }
     closedir($handle);
     mtrace("({$killed} deleted, {$spared} left): " . round(microtime(true) - $start, 1));
 }
 /**
  * Lists all files in a playspace.
  * @param string $playspaceid ID of playspace (two comma-separated numbers)
  * @param bool $duplicates If true, actually lists duplicate files
  * @return array Array of full names including paths
  */
 static function get_attachment_playspace_files($playspaceid, $duplicates)
 {
     $folder = self::get_attachment_playspace_folder($playspaceid);
     if ($duplicates) {
         self::delete_attachment_playspace($playspaceid, true);
         forum_utils::mkdir("{$folder}/" . self::DUPLICATESFOLDER);
     }
     $handle = forum_utils::opendir($folder);
     $result = array();
     while (false !== ($name = readdir($handle))) {
         if ($name != '.' && $name != '..' && $name != self::DUPLICATESFOLDER) {
             $path = "{$folder}/{$name}";
             if ($duplicates) {
                 $duplicate = "{$folder}/" . self::DUPLICATESFOLDER . "/{$name}";
                 forum_utils::copy($path, $duplicate);
                 $result[] = $duplicate;
             } else {
                 $result[] = $path;
             }
         }
     }
     closedir($handle);
     return $result;
 }
 /**
  * Merges the contents of this discussion into another discussion.
  * @param forum_discussion $targetdiscussion Target discussion
  * @param int $userid User ID (0 = current)
  * @param bool $log True to log this action
  */
 public function merge_into($targetdiscussion, $userid = 0, $log = true)
 {
     global $CFG;
     forum_utils::start_transaction();
     // Update parent post id of root post
     $record = new stdClass();
     $record->id = $this->discussionfields->postid;
     $record->parentpostid = $targetdiscussion->discussionfields->postid;
     forum_utils::update_record('forumng_posts', $record);
     // Move all posts into new discussion
     forum_utils::execute_sql("UPDATE {$CFG->prefix}forumng_posts SET " . "discussionid=" . $targetdiscussion->get_id() . " WHERE discussionid=" . $this->get_id());
     // Delete this discussion
     forum_utils::delete_records('forumng_discussions', 'id', $this->discussionfields->id);
     // Merge attachments (if any)
     $oldfolder = $this->get_attachment_folder();
     $newfolder = $targetdiscussion->get_attachment_folder();
     if (is_dir($oldfolder)) {
         $handle = forum_utils::opendir($oldfolder);
         $madenewfolder = false;
         while (false !== ($name = readdir($handle))) {
             if ($name != '.' && $name != '..') {
                 if (!$madenewfolder) {
                     check_dir_exists($newfolder, true, true);
                     $madenewfolder = true;
                 }
                 $oldname = $oldfolder . '/' . $name;
                 $newname = $newfolder . '/' . $name;
                 forum_utils::rename($oldname, $newname);
             }
         }
         closedir($handle);
     }
     // Merging the discussion into another might cause completion changes
     // (if there was a requirement for discussions and this is no longer
     // a discussion in its own right).
     $this->update_completion(false);
     if ($log) {
         $this->log('merge discussion d' . $targetdiscussion->get_id());
     }
     forum_utils::finish_transaction();
     $this->uncache();
     $targetdiscussion->uncache();
 }
 /**
  * Edits an existing message. The previous version of the message is
  * retained for admins to view if needed.
  * @param string $subject Subject
  * @param string $message Message
  * @param int $format Moodle format ID
  * @param mixed $deleteattachments Array of names (only) of existing 
  *   attachments to delete, or 'true' to delete all
  * @param array $newattachments Additional attachments to add (if any)
  * @param bool $setimportant If true, highlight the post
  * @param bool $mailnow New value of mailnow flag (ignored if message was already mailed)
  * @param int $userid Userid doing the editing (0 = current)
  */
 function edit($subject, $message, $format, $deleteattachments = array(), $newattachments = array(), $setimportant = false, $mailnow = false, $userid = 0, $log = true)
 {
     $now = time();
     // Create copy of existing entry ('old version')
     $copy = clone $this->postfields;
     $copy->subject = is_null($copy->subject) ? null : addslashes($copy->subject);
     $copy->message = addslashes($copy->message);
     // Copy has oldversion set to 1 and parentpost set to id of real post
     $copy->oldversion = 1;
     $copy->parentpostid = $copy->id;
     unset($copy->id);
     // OK, add copy
     forum_utils::start_transaction();
     $copyid = forum_utils::insert_record('forumng_posts', $copy);
     // If there are attachments...
     $attachments = $this->get_attachment_names();
     $copiedattachments = 0;
     if ($attachments) {
         // NOTE: The names are confusing. $newfolder is the EXISTING folder
         // but it relates to the attachments for the NEW version of the
         // post. $oldfolder is a newly-created folder but it relates to
         // attachments for the OLD version of the posdt
         $newfolder = $this->get_attachment_folder();
         $oldfolder = $this->get_attachment_folder($copyid);
         // Copy the attachments folder to an 'old' historic one, then
         // delete files from the current attachment folder. This used to be
         // done with rename but we experienced failures when creating the
         // folder with the same name as the one we just renamed away.
         forum_utils::mkdir($oldfolder);
         $handle = forum_utils::opendir($newfolder);
         $keepfolder = false;
         while (false !== ($name = readdir($handle))) {
             if ($name != '.' && $name != '..') {
                 $source = "{$newfolder}/{$name}";
                 // Copy file to the 'old' version folder
                 forum_utils::copy($source, "{$oldfolder}/{$name}");
                 // Consider whether it should be deleted from the 'new'
                 // version folder
                 if ($deleteattachments === true || $deleteattachments && in_array($name, $deleteattachments)) {
                     forum_utils::unlink($source);
                 } else {
                     $keepfolder = true;
                     $copiedattachments++;
                 }
             }
         }
         closedir($handle);
         if (!$keepfolder) {
             forum_utils::rmdir($newfolder);
         }
     }
     // Update existing entry with new data where it changed
     $update = new StdClass();
     $gotsubject = false;
     if ($subject !== $this->postfields->subject) {
         $update->subject = strlen(trim($subject)) == 0 ? null : addslashes($subject);
         $gotsubject = true;
     }
     if ($message !== $this->postfields->message) {
         $update->message = addslashes($message);
     }
     if ($format != $this->postfields->format) {
         $update->format = $format;
     }
     if ($copiedattachments == 0 && count($newattachments) == 0 && $this->postfields->attachments) {
         $update->attachments = 0;
     } else {
         if (count($newattachments) > 0 && !$this->postfields->attachments) {
             $update->attachments = 1;
         }
     }
     if ($setimportant) {
         $update->important = 1;
     } else {
         $update->important = 0;
     }
     if ($this->postfields->mailstate == forum::MAILSTATE_NOT_MAILED && $mailnow) {
         $update->mailstate = forum::MAILSTATE_NOW_NOT_MAILED;
     } else {
         if ($this->postfields->mailstate == forum::MAILSTATE_NOW_NOT_MAILED && !$mailnow) {
             $update->mailstate = forum::MAILSTATE_NOT_MAILED;
         }
     }
     $update->modified = $now;
     $update->edituserid = forum_utils::get_real_userid($userid);
     if (count((array) $update) > 0) {
         $update->id = $this->postfields->id;
         forum_utils::update_record('forumng_posts', $update);
     }
     // Add new attachments
     foreach ($newattachments as $path) {
         $this->add_attachment($path);
     }
     if ($log) {
         $this->log('edit post');
     }
     // Update in-memory representation
     foreach ((array) $update as $name => $value) {
         $this->postfields->{$name} = $value === null ? null : stripslashes($value);
     }
     // If this is the root post, then changing its subject affects
     // the discussion subhject
     if ($this->is_root_post() && $gotsubject) {
         $this->discussion->hack_subject($this->postfields->subject);
     }
     // Uncache before updating search (want to make sure that the recursive
     // update gets latest data)
     $this->get_discussion()->uncache();
     // Update search index
     if (isset($update->message) || $gotsubject) {
         // Update for this post
         $this->search_update();
         // If changing the subject of a root post, update all posts in the
         // discussion (ugh)
         if ($this->is_root_post() && $gotsubject) {
             $this->search_update_children();
         }
     }
     forum_utils::finish_transaction();
 }