/** * Display a post. This method is used for: * - The normal HTML display of a post * - HTML email of a post * - Text-only email of a post * These are all combined in one method since ordinarily they change at * the same time (i.e. if adding/hiding information it is usually added to * or hidden from all views). * * $options is an associative array from a forum_post::OPTION_xx constant. * All available options are always set - if they were not set by * the user, they will have been set to false before this call happens, * so there is no need to use empty() or isset(). * * Options are as follows. These are available in email mode: * * OPTION_TIME_ZONE (int) - Moodle time zone * OPTION_VIEW_FULL_NAMES (bool) - If user is allowed to see full names * OPTION_EMAIL (bool) - True if this is an email (false = standard view) * OPTION_DIGEST (bool) - True if this is part of an email digest * OPTION_COMMAND_REPLY (bool) - True if 'Reply' link should be displayed * (available in email too) * * These options only apply in non-email usage: * * OPTION_SUMMARY (bool) - True if the entire post should not be displayed, * only a short summary * OPTION_NO_COMMANDS (bool) - True if this post is being printed on its own * OPTION_COMMAND_EDIT (bool) - Display 'Edit' command * OPTION_COMMAND_DELETE (bool) - Display 'Edit' command * OPTION_COMMAND_SPLIT (bool) - Display 'Split' command * OPTION_RATINGS_VIEW (bool) - True to display current ratings * OPTION_RATINGS_EDIT (bool) - True to display ratings edit combo * OPTION_LEAVE_DIV_OPEN (bool) - True to not close post div (means that * child posts can be added within). * OPTION_EXPANDED (bool) - True to show full post, otherwise abbreviate * OPTION_DISCUSSION_SUBJECT (bool) - If true, and only IF post is a * discussion root, includes subject (HTML, shortened as it would be for * header display) as a hidden field. * * @param forum_post $post Post object * @param bool $html True if using HTML, false to output in plain text * @param array $options Associative array of name=>option, as above * @return string HTML or text of post */ public function display_post($post, $html, $options) { global $CFG, $USER, $THEME; $discussion = $post->get_discussion(); $expanded = $options[forum_post::OPTION_EXPANDED]; $export = $options[forum_post::OPTION_EXPORT]; $email = $options[forum_post::OPTION_EMAIL]; // When posts are deleted we hide a lot of info - except when the person // viewing it has the ability to view deleted posts. $deletedhide = $post->get_deleted() && !$options[forum_post::OPTION_VIEW_DELETED_INFO]; // Hide deleted messages if they have no replies if ($deletedhide && !$email && !$post->has_children()) { // note: !email check is to deal with posts that are deleted // between when the mail list finds them, and when it sends out // mail. It would be confusing to send out a blank email so let's // not do that. Also, ->has_children() is not safe to call during // email processing because it doesn't load the whole discussion. return ''; } // Save some bandwidth by not sending link full paths except in emails if ($options[forum_post::OPTION_FULL_ADDRESSES]) { $linkprefix = $CFG->wwwroot . '/mod/forumng/'; } else { $linkprefix = ''; } $postnumber = ($options[forum_post::OPTION_NO_COMMANDS] || $email) && !$options[forum_post::OPTION_VISIBLE_POST_NUMBERS] ? '' : $post->get_number(); $lf = "\n"; // Initialise result $out = ''; if ($html) { if ($export) { $out .= '<hr />'; } // Basic intro $classes = $expanded ? ' forumng-full' : ' forumng-short'; $classes .= $post->is_important() ? ' forumng-important' : ''; $classes .= !$email && !$options[forum_post::OPTION_UNREAD_NOT_HIGHLIGHTED] && $post->is_unread() ? ' forumng-unread' : ' forumng-read'; $classes .= $post->get_deleted() ? ' forumng-deleted' : ''; $classes .= ' forumng-p' . $postnumber; $out .= $lf . '<div class="forumng-post' . $classes . '"><a id="p' . $post->get_id() . '"></a>'; if ($options[forum_post::OPTION_FIRST_UNREAD]) { $out .= '<a id="firstunread"></a>'; } // Theme hooks if (!empty($THEME->forumng_post_hooks)) { for ($i = 1; $i <= $THEME->forumng_post_hooks; $i++) { $out .= '<div class="forumng-' . $i . '"></div>'; } } } if ($html || $options[forum_post::OPTION_VISIBLE_POST_NUMBERS]) { // Accessible text giving post a number so we can make links unique // etc. if ($postnumber) { $data = new stdClass(); $data->num = $postnumber; if ($post->get_parent()) { if ($html) { $data->parent = '<a class="forumng-parentlink" href="#p' . $post->get_parent()->get_id() . '">' . $post->get_parent()->get_number() . '</a>'; } else { $data->parent = $post->get_parent()->get_number(); } $data->info = ''; if ($post->is_unread()) { $data->info = get_string('postinfo_unread', 'forumng'); } if (!$expanded) { $data->info .= ' ' . get_string('postinfo_short', 'forumng'); } if ($post->get_deleted()) { $data->info .= ' ' . get_string('postinfo_deleted', 'forumng'); } $data->info = trim($data->info); if ($data->info) { $data->info = ' (' . $data->info . ')'; } $info = get_string('postnumreply', 'forumng', $data); } else { $info = get_string('postnum', 'forumng', $data); } if ($options[forum_post::OPTION_VISIBLE_POST_NUMBERS]) { if (!$html) { $out .= "## " . $info . "\n"; } } } } // Discussion subject (root only) if ($options[forum_post::OPTION_DISCUSSION_SUBJECT] && $post->is_root_post()) { $out .= '<input type="hidden" name="discussion_subject" value="' . shorten_text(htmlspecialchars($post->get_subject())) . '" />'; } // Pictures (HTML version only) if ($html && !$export && $options[forum_post::OPTION_USER_IMAGE]) { $out .= $lf . '<div class="forumng-pic">'; // User picture $out .= $deletedhide ? '' : $post->display_user_picture(); // Group pictures if any - only for expanded version if ($expanded) { $grouppics = $post->display_group_pictures(); if ($grouppics) { $out .= '<div class="forumng-grouppicss">' . $grouppics . '</div>'; } } $out .= '</div>'; } // Link used to expand post $expandlink = ''; if (!$expanded && !$deletedhide) { $expandlink = ' [<a class="forumng-expandlink" ' . 'href="' . $linkprefix . 'discuss.php?' . $discussion->get_link_params(forum::PARAM_HTML) . '&expand=1#p' . $post->get_id() . '">' . get_string('expandall', 'forumng') . '</a>] <img src="' . $CFG->pixpath . '/spacer.gif" width="16" height="16" alt="" />'; } // Byline $by = new stdClass(); $by->name = $deletedhide ? '' : fullname($post->get_user(), $options[forum_post::OPTION_VIEW_FULL_NAMES]); $by->date = $deletedhide ? '' : userdate($post->get_created(), get_string('strftimedatetime', 'langconfig'), $options[forum_post::OPTION_TIME_ZONE]); if ($html) { $out .= $lf . '<div class="forumng-info"><h2 class="forumng-author">'; $out .= $post->is_important() ? '<img src="' . $CFG->modpixpath . '/forumng/exclamation_mark.gif" alt="' . get_string('important', 'forumng') . '" ' . 'title = "' . get_string('important', 'forumng') . '"/>' : ''; if ($export) { $out .= $by->name; } else { $out .= '<a href="' . $CFG->wwwroot . '/user/view.php?id=' . $post->get_user()->id . ($post->get_forum()->is_shared() ? '' : '&course=' . $post->get_forum()->get_course_id()) . '">' . $by->name . '</a>'; } if ($postnumber) { if ($options[forum_post::OPTION_VISIBLE_POST_NUMBERS]) { $out .= '<span class="accesshide" style="position:static"> ' . $info . ' </span>'; } else { $out .= '<span class="accesshide"> ' . $info . ' </span>'; } } $out .= $deletedhide ? '' : '</h2> <span class="forumng-separator">•</span> '; $out .= '<span class="forumng-date">' . $by->date . '</span>'; if ($edituser = $post->get_edit_user()) { $out .= ' <span class="forumng-separator">•</span> ' . '<span class="forumng-edit">'; $edit = new stdClass(); $edit->date = userdate($post->get_modified(), get_string('strftimedatetime', 'langconfig'), $options[forum_post::OPTION_TIME_ZONE]); $edit->name = fullname($edituser, $options[forum_post::OPTION_VIEW_FULL_NAMES]); if ($edituser->id == $post->get_user()->id) { $out .= get_string('editbyself', 'forumng', $edit->date); } else { $out .= get_string('editbyother', 'forumng', $edit); } if ($options[forum_post::OPTION_COMMAND_HISTORY]) { $out .= ' (<a href="history.php?' . $post->get_link_params(forum::PARAM_HTML) . '">' . get_string('history', 'forumng') . '</a>)'; } $out .= '</span>'; } if ($options[forum_post::OPTION_SELECTABLE]) { $out .= ' • <input type="checkbox" name="selectp' . $post->get_id() . '" id="id_selectp' . $post->get_id() . '" /><label class="accesshide" for="id_selectp' . $post->get_id() . '">' . get_string('selectlabel', 'forumng', $postnumber) . '</label>'; } if ($options[forum_post::OPTION_FLAG_CONTROL]) { $out .= '<div class="forumng-flag">' . '<input type="image" title="' . get_string($post->is_flagged() ? 'clearflag' : 'setflag', 'forumng') . '" src="' . $CFG->modpixpath . '/forumng/flag.' . ($post->is_flagged() ? 'on' : 'off') . '.png" alt="' . get_string($post->is_flagged() ? 'flagon' : 'flagoff', 'forumng') . '" name="action.flag.p_' . $post->get_id() . '.timeread_' . $options[forum_post::OPTION_READ_TIME] . '.flag_' . ($post->is_flagged() ? 0 : 1) . '"/></div>'; } $out .= '</div>'; } else { $out .= $by->name . ' - ' . $by->date . $lf; $out .= forum_cron::EMAIL_DIVIDER; } if ($post->get_deleted()) { $out .= '<p class="forumng-deleted-info"><strong>' . get_string('deletedpost', 'forumng') . '</strong> '; if ($deletedhide) { $out .= get_string($post->get_delete_user()->id == $post->get_user()->id ? 'deletedbyauthor' : 'deletedbymoderator', 'forumng', userdate($post->get_deleted())); } else { $a = new stdClass(); $a->date = userdate($post->get_deleted()); $a->user = '******' . $CFG->wwwroot . '/user/view.php?id=' . $post->get_delete_user()->id . '&course=' . $post->get_forum()->get_course_id() . '">' . fullname($post->get_delete_user(), $options[forum_post::OPTION_VIEW_FULL_NAMES]) . '</a>'; $out .= get_string('deletedbyuser', 'forumng', $a); } $out .= '</p>'; } // Get subject. This may make a db query when showing a single post // (which includes parent subject). if ($options[forum_post::OPTION_EMAIL] || $options[forum_post::OPTION_NO_COMMANDS]) { $subject = $post->get_effective_subject(true); } else { $subject = $post->get_subject(); } // Otherwise, subject is only displayed if it has changed if ($subject !== null && $expanded && !$deletedhide) { if ($html) { $out .= $lf . '<h3 class="forumng-subject">'; if ($options[forum_post::OPTION_DIGEST]) { // Digest contains link to original post $out .= '<a href="' . $linkprefix . 'discuss.php?' . $discussion->get_link_params(forum::PARAM_HTML) . '#p' . $post->get_id() . '">' . format_string($subject) . '</a>'; } else { $out .= format_string($subject); } $out .= '</h3>'; } else { $out .= format_string($subject, true); if ($options[forum_post::OPTION_DIGEST]) { // Link to original post $out .= " <{$linkprefix}discuss.php?" . $discussion->get_link_params(forum::PARAM_HTML) . $discussion->get_id() . '#p' . $post->get_id() . '>'; } $out .= $lf; } } // Get content of actual message in HTML if ($html) { $textoptions = new stdClass(); // Don't put a <p> tag round post $textoptions->para = false; // Does not indicate that we trust the text, only that the // TRUSTTEXT marker is supported. $textoptions->trusttext = true; $message = format_text($post->get_message(), $post->get_format(), $textoptions, $post->get_forum()->get_course_id()); if (!$expanded && !$deletedhide) { // When not expanded and no subject, we include a summary of the // message $stripped = strip_tags(preg_replace('~<script.*?</script>~s', '', $message)); $messagetosummarise = $subject !== null ? '<h3>' . $subject . '</h3> ' . $stripped : $stripped; $summary = self::nice_shorten_text($messagetosummarise, 50); $out .= $lf . '<div class="forumng-summary"><div class="forumng-text">' . $summary . '</div> ' . $expandlink . '</div>'; } } // Start of post main section if ($expanded && !$deletedhide) { if ($html) { $out .= '<div class="forumng-postmain">'; } // Attachments $attachments = $post->get_attachment_names(); if (count($attachments)) { if ($html) { $out .= $lf . '<ul class="forumng-attachments">'; } if (count($attachments) == 1) { $attachmentlabel = get_string('attachment', 'forumng'); } else { $attachmentlabel = get_string('attachments', 'forumng'); } $out .= '<span class="accesshide">' . $attachmentlabel . '</span>'; foreach ($attachments as $attachment) { if ($html) { require_once $CFG->libdir . '/filelib.php'; $iconsrc = $CFG->pixpath . '/f/' . mimeinfo('icon', $attachment); $alt = get_mimetype_description(mimeinfo('type', $attachment)); $out .= '<li><a href="' . $linkprefix . 'attachment.php?' . $post->get_link_params(forum::PARAM_HTML) . '&file=' . $attachment . '">' . '<img src="' . $iconsrc . '" alt="' . $alt . '" /> <span>' . htmlspecialchars($attachment) . '</span></a></li>'; } else { // Right-align the entry to 70 characters $padding = 70 - strlen($attachment); if ($padding > 0) { $out .= str_repeat(' ', $padding); } // Add filename $out .= $attachment . $lf; } } if ($html) { $out .= '</ul>' . $lf; } else { $out .= $lf; // Extra line break after attachments } } // Display actual content if ($html) { if ($options[forum_post::OPTION_PRINTABLE_VERSION]) { $message = preg_replace('~<a[^>]*\\shref\\s*=\\s*[\'"](http:.*?)[\'"][^>]*>' . '(?!(http:|www\\.)).*?</a>~', "\$0 [\$1]", $message); } $out .= $lf . '<div class="forumng-message">' . $message . '</div>'; } else { $out .= format_text_email(trusttext_strip($post->get_message()), $post->get_format()); $out .= "\n\n"; } if ($html) { $out .= $lf . '<div class="forumng-postfooter">'; } // Ratings $ratings = ''; $ratingclasses = ''; if ($options[forum_post::OPTION_RATINGS_VIEW]) { $ratingclasses .= ' forumng-canview'; if ($post->get_num_ratings() >= $post->get_forum()->get_rating_threshold()) { if ($html) { $ratings .= '<div class="forumng-rating">'; $a = new stdClass(); $a->avg = '<strong id="rating_for_' . $post->get_id() . '">' . $post->get_average_rating(true) . '</strong>'; $a->num = '<span class="forumng-count">' . $post->get_num_ratings() . '</span>'; $ratings .= get_string('averagerating', 'forumng', $a); $ratings .= '</div>'; } else { $ratings .= strip_tags($post->get_average_rating(true)); } } } if ($options[forum_post::OPTION_RATINGS_EDIT] && $html) { $ratingclasses .= ' forumng-canedit'; $ratings .= '<div class="forumng-editrating">' . get_string('yourrating', 'forumng') . ' '; $ratings .= choose_from_menu($post->get_forum()->get_rating_options(), 'rating' . $post->get_id(), $post->get_own_rating(), '-', '', forum_post::NO_RATING, true); $ratings .= '</div>'; } if ($ratings) { $out .= '<div class="forumng-ratings' . $ratingclasses . '">' . $ratings . '</div>'; } // Commands at bottom of mail if (class_exists('ouflags') && ou_get_is_mobile_from_cookies()) { $mobileclass = ' class="forumng-mobilepost-link"'; } else { $mobileclass = ''; } if ($html) { $commands = ''; $expires = $post->can_ignore_edit_time_limit() ? '' : '&expires=' . ($post->get_edit_time_limit() - time()); // Jump box if ($options[forum_post::OPTION_JUMP_PREVIOUS] || $options[forum_post::OPTION_JUMP_NEXT] || $options[forum_post::OPTION_JUMP_PARENT]) { $commands .= '<li class="forumng-jumpto">' . get_string('jumpto', 'forumng'); if ($nextid = $options[forum_post::OPTION_JUMP_NEXT]) { $commands .= ' <a href="#p' . $nextid . '" class="forumng-next">' . get_string('jumpnext', 'forumng') . '</a>'; } if ($pid = $options[forum_post::OPTION_JUMP_PREVIOUS]) { if ($nextid) { $commands .= ' (<a href="#p' . $pid . '" class="forumng-prev">' . get_string('jumppreviousboth', 'forumng') . '</a>)'; } else { $commands .= ' <a href="#p' . $pid . '" class="forumng-prev">' . get_string('jumpprevious', 'forumng') . '</a>'; } } if ($parentid = $options[forum_post::OPTION_JUMP_PARENT]) { $commands .= ' <a href="#p' . $parentid . '" class="forumng-parent">' . get_string('jumpparent', 'forumng') . '</a>'; } $commands .= '</li>'; } //Direct link if ($options[forum_post::OPTION_COMMAND_DIRECTLINK]) { $commands .= '<li class="forumng-permalink"><a href="discuss.php?' . $discussion->get_link_params(forum::PARAM_HTML) . '#p' . $post->get_id() . '" title="' . get_string('directlinktitle', 'forumng') . '">' . get_string('directlink', 'forumng', $postnumber) . '</a></li>'; } // Alert link if ($options[forum_post::OPTION_COMMAND_REPORT]) { $commands .= '<li><a href="' . $linkprefix . 'alert.php?' . $post->get_link_params(forum::PARAM_HTML) . '" title="' . get_string('alert_linktitle', 'forumng') . '">' . get_string('alert_link', 'forumng', $postnumber) . '</a></li>'; } // Split link if ($options[forum_post::OPTION_COMMAND_SPLIT]) { $commands .= '<li class="forumng-split"><a href="' . $linkprefix . 'splitpost.php?' . $post->get_link_params(forum::PARAM_HTML) . '">' . get_string('split', 'forumng', $postnumber) . '</a></li>'; } // Delete link if ($options[forum_post::OPTION_COMMAND_DELETE]) { $commands .= '<li><a' . $mobileclass . ' href="' . $linkprefix . 'deletepost.php?' . $post->get_link_params(forum::PARAM_HTML) . $expires . '">' . get_string('delete', 'forumng', $postnumber) . '</a></li>'; } // Undelete link if ($options[forum_post::OPTION_COMMAND_UNDELETE]) { $commands .= '<li><a href="' . $linkprefix . 'deletepost.php?' . $post->get_link_params(forum::PARAM_HTML) . '&delete=0">' . get_string('undelete', 'forumng', $postnumber) . '</a></li>'; } // Edit link if ($options[forum_post::OPTION_COMMAND_EDIT]) { $commands .= '<li><a' . $mobileclass . ' href="' . $linkprefix . 'editpost.php?' . $post->get_link_params(forum::PARAM_HTML) . $expires . '">' . get_string('edit', 'forumng', $postnumber) . '</a></li>'; } // Reply link if ($options[forum_post::OPTION_COMMAND_REPLY]) { $commands .= '<li class="forumng-replylink"><a' . $mobileclass . ' href="' . $linkprefix . 'editpost.php?replyto=' . $post->get_id() . $post->get_forum()->get_clone_param(forum::PARAM_HTML) . '">' . get_string('reply', 'forumng', $postnumber) . '</a></li>'; } if ($commands) { $out .= $lf . '<ul class="forumng-commands">' . $commands . '</ul>'; } } else { // Reply link if ($options[forum_post::OPTION_COMMAND_REPLY]) { $out .= forum_cron::EMAIL_DIVIDER; if ($options[forum_post::OPTION_EMAIL]) { $course = $post->get_forum()->get_course(); $out .= get_string("postmailinfo", "forumng", $course->shortname) . $lf; } $out .= "{$linkprefix}editpost.php?replyto=" . $post->get_id() . $post->get_forum()->get_clone_param(forum::PARAM_PLAIN) . $lf; } // Only the reply command is available in text mode } // End of post footer and main section if ($html) { $out .= '</div></div>'; } } // End of post div if ($html) { $out .= '<div class="forumng-endpost"></div></div>'; if ($export) { $out .= '<br /><br />'; } } return $out; }
/** * Used by forum_post when creating a reply. Do not call directly. * @param forum_post $parentpost Parent post object (NULL when creating root post) * @param string $subject Subject * @param string $message Message * @param int $format Moodle format used for message * @param array $attachments Array of paths to temporary files of * attachments in post. [Note that these should have already been checked * and renamed by the Moodle upload manager. They will be moved or * deleted by the time this method returns.] * @param bool $setimportant If true, highlight the post * @param bool $mailnow If true, sends mail ASAP * @param int $userid User ID (0 = current) * * @return int ID of newly-created post */ function create_reply($parentpost, $subject, $message, $format, $attachments = array(), $setimportant = false, $mailnow = false, $userid = 0) { if ($userid == 0) { global $USER; $userid = $USER->id; if (!$userid) { throw new forum_exception('Cannot determine user ID'); } } // Prepare post object $postobj = new StdClass(); $postobj->discussionid = $this->discussionfields->id; $postobj->parentpostid = $parentpost ? $parentpost->get_id() : null; $postobj->userid = $userid; $postobj->created = time(); $postobj->modified = $postobj->created; $postobj->deleted = 0; $postobj->mailstate = $mailnow ? forum::MAILSTATE_NOW_NOT_MAILED : forum::MAILSTATE_NOT_MAILED; $postobj->important = $setimportant ? 1 : 0; $postobj->oldversion = 0; $postobj->edituserid = null; $postobj->subject = strlen(trim($subject)) == 0 ? null : addslashes($subject); $postobj->message = addslashes($message); $postobj->format = $format; $postobj->attachments = count($attachments) > 0; forum_utils::start_transaction(); try { // Create post $postobj->id = forum_utils::insert_record('forumng_posts', $postobj); $post = new forum_post($this, $postobj); // For replies, update last post id if ($parentpost) { $discussionchange = new stdClass(); $discussionchange->id = $parentpost->get_discussion()->get_id(); $discussionchange->lastpostid = $postobj->id; forum_utils::update_record('forumng_discussions', $discussionchange); } // Place attachments foreach ($attachments as $path) { $post->add_attachment($path); } // Update search index (replies only) if (forum::search_installed() && $parentpost) { $post->search_update(); } // Update completion state $post->update_completion(true); // Outside the catch so we don't commit transaction if something // fails forum_utils::finish_transaction(); return $post->get_id(); } catch (Exception $e) { // Erase attachments from temp storage if error occurs foreach ($attachments as $path) { unlink($path); } throw $e; } }