/** * This simple function adds the passed user from the current users buddy list. * * Called by ?action=buddy;u=x;session_id=y. * Redirects to ?action=profile;u=x. */ public function action_addbuddy() { global $user_info, $modSettings; checkSession('get'); is_not_guest(); // You have to give a user if (empty($_REQUEST['u'])) { fatal_lang_error('no_access', false); } // Always an int $user = (int) $_REQUEST['u']; call_integration_hook('integrate_add_buddies', array($user_info['id'], &$user)); // Add if it's not there (and not you). if (!in_array($user, $user_info['buddies']) && $user_info['id'] != $user) { $user_info['buddies'][] = $user; // Do we want a mention for our newly added buddy? if (!empty($modSettings['mentions_enabled']) && !empty($modSettings['mentions_buddy'])) { require_once CONTROLLERDIR . '/Mentions.controller.php'; $mentions = new Mentions_Controller(); // Set a mention for our buddy. $mentions->setData(array('id_member' => $user, 'type' => 'buddy', 'id_msg' => 0)); $mentions->action_add(); } } // Update the settings. updateMemberData($user_info['id'], array('buddy_list' => implode(',', $user_info['buddies']))); // Redirect back to the profile redirectexit('action=profile;u=' . $user); }
/** * The central part of the board - topic display. * * What it does: * - This function loads the posts in a topic up so they can be displayed. * - It uses the main sub template of the Display template. * - It requires a topic, and can go to the previous or next topic from it. * - It jumps to the correct post depending on a number/time/IS_MSG passed. * - It depends on the messages_per_page, defaultMaxMessages and enableAllMessages settings. * - It is accessed by ?topic=id_topic.START. */ public function action_display() { global $scripturl, $txt, $modSettings, $context, $settings; global $options, $user_info, $board_info, $topic, $board; global $attachments, $messages_request; // What are you gonna display if these are empty?! if (empty($topic)) { fatal_lang_error('no_board', false); } // Load the template loadTemplate('Display'); $context['sub_template'] = 'messages'; // And the topic functions require_once SUBSDIR . '/Topic.subs.php'; require_once SUBSDIR . '/Messages.subs.php'; // Not only does a prefetch make things slower for the server, but it makes it impossible to know if they read it. if (isset($_SERVER['HTTP_X_MOZ']) && $_SERVER['HTTP_X_MOZ'] == 'prefetch') { @ob_end_clean(); header('HTTP/1.1 403 Prefetch Forbidden'); die; } // How much are we sticking on each page? $context['messages_per_page'] = empty($modSettings['disableCustomPerPage']) && !empty($options['messages_per_page']) ? $options['messages_per_page'] : $modSettings['defaultMaxMessages']; $template_layers = Template_Layers::getInstance(); $template_layers->addEnd('messages_informations'); $includeUnapproved = !$modSettings['postmod_active'] || allowedTo('approve_posts'); // Let's do some work on what to search index. if (count($_GET) > 2) { foreach ($_GET as $k => $v) { if (!in_array($k, array('topic', 'board', 'start', session_name()))) { $context['robot_no_index'] = true; } } } if (!empty($_REQUEST['start']) && (!is_numeric($_REQUEST['start']) || $_REQUEST['start'] % $context['messages_per_page'] != 0)) { $context['robot_no_index'] = true; } // Find the previous or next topic. Make a fuss if there are no more. if (isset($_REQUEST['prev_next']) && ($_REQUEST['prev_next'] == 'prev' || $_REQUEST['prev_next'] == 'next')) { // No use in calculating the next topic if there's only one. if ($board_info['num_topics'] > 1) { $includeStickies = !empty($modSettings['enableStickyTopics']); $topic = $_REQUEST['prev_next'] === 'prev' ? previousTopic($topic, $board, $user_info['id'], $includeUnapproved, $includeStickies) : nextTopic($topic, $board, $user_info['id'], $includeUnapproved, $includeStickies); $context['current_topic'] = $topic; } // Go to the newest message on this topic. $_REQUEST['start'] = 'new'; } // Add 1 to the number of views of this topic (except for robots). if (!$user_info['possibly_robot'] && (empty($_SESSION['last_read_topic']) || $_SESSION['last_read_topic'] != $topic)) { increaseViewCounter($topic); $_SESSION['last_read_topic'] = $topic; } $topic_selects = array(); $topic_tables = array(); $topic_parameters = array('topic' => $topic, 'member' => $user_info['id'], 'board' => (int) $board); // Allow addons to add additional details to the topic query call_integration_hook('integrate_topic_query', array(&$topic_selects, &$topic_tables, &$topic_parameters)); // Load the topic details $topicinfo = getTopicInfo($topic_parameters, 'all', $topic_selects, $topic_tables); if (empty($topicinfo)) { fatal_lang_error('not_a_topic', false); } // Is this a moved topic that we are redirecting to? if (!empty($topicinfo['id_redirect_topic']) && !isset($_GET['noredir'])) { markTopicsRead(array($user_info['id'], $topic, $topicinfo['id_last_msg'], 0), $topicinfo['new_from'] !== 0); redirectexit('topic=' . $topicinfo['id_redirect_topic'] . '.0;redirfrom=' . $topicinfo['id_topic']); } $context['real_num_replies'] = $context['num_replies'] = $topicinfo['num_replies']; $context['topic_first_message'] = $topicinfo['id_first_msg']; $context['topic_last_message'] = $topicinfo['id_last_msg']; $context['topic_unwatched'] = isset($topicinfo['unwatched']) ? $topicinfo['unwatched'] : 0; if (isset($_GET['redirfrom'])) { $redir_topics = topicsList(array((int) $_GET['redirfrom'])); if (!empty($redir_topics[(int) $_GET['redirfrom']])) { $context['topic_redirected_from'] = $redir_topics[(int) $_GET['redirfrom']]; $context['topic_redirected_from']['redir_href'] = $scripturl . '?topic=' . $context['topic_redirected_from']['id_topic'] . '.0;noredir'; } } // Add up unapproved replies to get real number of replies... if ($modSettings['postmod_active'] && allowedTo('approve_posts')) { $context['real_num_replies'] += $topicinfo['unapproved_posts'] - ($topicinfo['approved'] ? 0 : 1); } // If this topic was derived from another, set the followup details if (!empty($topicinfo['derived_from'])) { require_once SUBSDIR . '/FollowUps.subs.php'; $context['topic_derived_from'] = topicStartedHere($topic, $includeUnapproved); } // If this topic has unapproved posts, we need to work out how many posts the user can see, for page indexing. if (!$includeUnapproved && $topicinfo['unapproved_posts'] && !$user_info['is_guest']) { $myUnapprovedPosts = unapprovedPosts($topic, $user_info['id']); $context['total_visible_posts'] = $context['num_replies'] + $myUnapprovedPosts + ($topicinfo['approved'] ? 1 : 0); } elseif ($user_info['is_guest']) { $context['total_visible_posts'] = $context['num_replies'] + ($topicinfo['approved'] ? 1 : 0); } else { $context['total_visible_posts'] = $context['num_replies'] + $topicinfo['unapproved_posts'] + ($topicinfo['approved'] ? 1 : 0); } // When was the last time this topic was replied to? Should we warn them about it? if (!empty($modSettings['oldTopicDays'])) { $mgsOptions = basicMessageInfo($topicinfo['id_last_msg'], true); $context['oldTopicError'] = $mgsOptions['poster_time'] + $modSettings['oldTopicDays'] * 86400 < time() && empty($topicinfo['is_sticky']); } else { $context['oldTopicError'] = false; } // The start isn't a number; it's information about what to do, where to go. if (!is_numeric($_REQUEST['start'])) { // Redirect to the page and post with new messages, originally by Omar Bazavilvazo. if ($_REQUEST['start'] == 'new') { // Guests automatically go to the last post. if ($user_info['is_guest']) { $context['start_from'] = $context['total_visible_posts'] - 1; $_REQUEST['start'] = $context['start_from']; } else { // Fall through to the next if statement. $_REQUEST['start'] = 'msg' . $topicinfo['new_from']; } } // Start from a certain time index, not a message. if (substr($_REQUEST['start'], 0, 4) == 'from') { $timestamp = (int) substr($_REQUEST['start'], 4); if ($timestamp === 0) { $_REQUEST['start'] = 0; } else { // Find the number of messages posted before said time... $context['start_from'] = countNewPosts($topic, $topicinfo, $timestamp); $_REQUEST['start'] = $context['start_from']; } } elseif (substr($_REQUEST['start'], 0, 3) == 'msg') { $virtual_msg = (int) substr($_REQUEST['start'], 3); if (!$topicinfo['unapproved_posts'] && $virtual_msg >= $topicinfo['id_last_msg']) { $context['start_from'] = $context['total_visible_posts'] - 1; } elseif (!$topicinfo['unapproved_posts'] && $virtual_msg <= $topicinfo['id_first_msg']) { $context['start_from'] = 0; } else { $only_approved = $modSettings['postmod_active'] && $topicinfo['unapproved_posts'] && !allowedTo('approve_posts'); $context['start_from'] = countMessagesBefore($topic, $virtual_msg, false, $only_approved, !$user_info['is_guest']); } // We need to reverse the start as well in this case. $_REQUEST['start'] = $context['start_from']; } } // Mark the mention as read if requested if (isset($_REQUEST['mentionread']) && !empty($virtual_msg)) { require_once CONTROLLERDIR . '/Mentions.controller.php'; $mentions = new Mentions_Controller(); $mentions->setData(array('id_mention' => $_REQUEST['item'], 'mark' => $_REQUEST['mark'])); $mentions->action_markread(); } // Create a previous next string if the selected theme has it as a selected option. if ($modSettings['enablePreviousNext']) { $context['links'] += array('go_prev' => $scripturl . '?topic=' . $topic . '.0;prev_next=prev#new', 'go_next' => $scripturl . '?topic=' . $topic . '.0;prev_next=next#new'); } // Derived from, set the link back if (!empty($context['topic_derived_from'])) { $context['links']['derived_from'] = $scripturl . '?msg=' . $context['topic_derived_from']['derived_from']; } // Check if spellchecking is both enabled and actually working. (for quick reply.) $context['show_spellchecking'] = !empty($modSettings['enableSpellChecking']) && function_exists('pspell_new'); if ($context['show_spellchecking']) { loadJavascriptFile('spellcheck.js', array('defer' => true)); } // Do we need to show the visual verification image? $context['require_verification'] = !$user_info['is_mod'] && !$user_info['is_admin'] && !empty($modSettings['posts_require_captcha']) && ($user_info['posts'] < $modSettings['posts_require_captcha'] || $user_info['is_guest'] && $modSettings['posts_require_captcha'] == -1); if ($context['require_verification']) { require_once SUBSDIR . '/VerificationControls.class.php'; $verificationOptions = array('id' => 'post'); $context['require_verification'] = create_control_verification($verificationOptions); $context['visual_verification_id'] = $verificationOptions['id']; } // Are we showing signatures - or disabled fields? $context['signature_enabled'] = substr($modSettings['signature_settings'], 0, 1) == 1; $context['disabled_fields'] = isset($modSettings['disabled_profile_fields']) ? array_flip(explode(',', $modSettings['disabled_profile_fields'])) : array(); // Censor the title... censorText($topicinfo['subject']); $context['page_title'] = $topicinfo['subject']; // Is this topic sticky, or can it even be? $topicinfo['is_sticky'] = empty($modSettings['enableStickyTopics']) ? '0' : $topicinfo['is_sticky']; // Allow addons access to the topicinfo array call_integration_hook('integrate_display_topic', array($topicinfo)); // Default this topic to not marked for notifications... of course... $context['is_marked_notify'] = false; // Did we report a post to a moderator just now? $context['report_sent'] = isset($_GET['reportsent']); if ($context['report_sent']) { $template_layers->add('report_sent'); } // Let's get nosey, who is viewing this topic? if (!empty($settings['display_who_viewing'])) { require_once SUBSDIR . '/Who.subs.php'; formatViewers($topic, 'topic'); } // If all is set, but not allowed... just unset it. $can_show_all = !empty($modSettings['enableAllMessages']) && $context['total_visible_posts'] > $context['messages_per_page'] && $context['total_visible_posts'] < $modSettings['enableAllMessages']; if (isset($_REQUEST['all']) && !$can_show_all) { unset($_REQUEST['all']); } elseif (isset($_REQUEST['all'])) { $_REQUEST['start'] = -1; } // Construct the page index, allowing for the .START method... $context['page_index'] = constructPageIndex($scripturl . '?topic=' . $topic . '.%1$d', $_REQUEST['start'], $context['total_visible_posts'], $context['messages_per_page'], true, array('all' => $can_show_all, 'all_selected' => isset($_REQUEST['all']))); $context['start'] = $_REQUEST['start']; // This is information about which page is current, and which page we're on - in case you don't like the constructed page index. (again, wireles..) $context['page_info'] = array('current_page' => $_REQUEST['start'] / $context['messages_per_page'] + 1, 'num_pages' => floor(($context['total_visible_posts'] - 1) / $context['messages_per_page']) + 1); // Figure out all the link to the next/prev $context['links'] += array('prev' => $_REQUEST['start'] >= $context['messages_per_page'] ? $scripturl . '?topic=' . $topic . '.' . ($_REQUEST['start'] - $context['messages_per_page']) : '', 'next' => $_REQUEST['start'] + $context['messages_per_page'] < $context['total_visible_posts'] ? $scripturl . '?topic=' . $topic . '.' . ($_REQUEST['start'] + $context['messages_per_page']) : ''); // If they are viewing all the posts, show all the posts, otherwise limit the number. if ($can_show_all && isset($_REQUEST['all'])) { // No limit! (actually, there is a limit, but...) $context['messages_per_page'] = -1; // Set start back to 0... $_REQUEST['start'] = 0; } // Build the link tree. $context['linktree'][] = array('url' => $scripturl . '?topic=' . $topic . '.0', 'name' => $topicinfo['subject']); // Build a list of this board's moderators. $context['moderators'] =& $board_info['moderators']; $context['link_moderators'] = array(); // Information about the current topic... $context['is_locked'] = $topicinfo['locked']; $context['is_sticky'] = $topicinfo['is_sticky']; $context['is_very_hot'] = $topicinfo['num_replies'] >= $modSettings['hotTopicVeryPosts']; $context['is_hot'] = $topicinfo['num_replies'] >= $modSettings['hotTopicPosts']; $context['is_approved'] = $topicinfo['approved']; $context['is_poll'] = $topicinfo['id_poll'] > 0 && !empty($modSettings['pollMode']) && allowedTo('poll_view'); determineTopicClass($context); // Did this user start the topic or not? $context['user']['started'] = $user_info['id'] == $topicinfo['id_member_started'] && !$user_info['is_guest']; $context['topic_starter_id'] = $topicinfo['id_member_started']; // Set the topic's information for the template. $context['subject'] = $topicinfo['subject']; $context['num_views'] = $topicinfo['num_views']; $context['num_views_text'] = $context['num_views'] == 1 ? $txt['read_one_time'] : sprintf($txt['read_many_times'], $context['num_views']); $context['mark_unread_time'] = !empty($virtual_msg) ? $virtual_msg : $topicinfo['new_from']; // Set a canonical URL for this page. $context['canonical_url'] = $scripturl . '?topic=' . $topic . '.' . $context['start']; // For quick reply we need a response prefix in the default forum language. $context['response_prefix'] = response_prefix(); // If we want to show event information in the topic, prepare the data. if (allowedTo('calendar_view') && !empty($modSettings['cal_showInTopic']) && !empty($modSettings['cal_enabled'])) { // We need events details and all that jazz require_once SUBSDIR . '/Calendar.subs.php'; // First, try create a better time format, ignoring the "time" elements. if (preg_match('~%[AaBbCcDdeGghjmuYy](?:[^%]*%[AaBbCcDdeGghjmuYy])*~', $user_info['time_format'], $matches) == 0 || empty($matches[0])) { $date_string = $user_info['time_format']; } else { $date_string = $matches[0]; } // Get event information for this topic. $events = eventInfoForTopic($topic); $context['linked_calendar_events'] = array(); foreach ($events as $event) { // Prepare the dates for being formatted. $start_date = sscanf($event['start_date'], '%04d-%02d-%02d'); $start_date = mktime(12, 0, 0, $start_date[1], $start_date[2], $start_date[0]); $end_date = sscanf($event['end_date'], '%04d-%02d-%02d'); $end_date = mktime(12, 0, 0, $end_date[1], $end_date[2], $end_date[0]); $context['linked_calendar_events'][] = array('id' => $event['id_event'], 'title' => $event['title'], 'can_edit' => allowedTo('calendar_edit_any') || $event['id_member'] == $user_info['id'] && allowedTo('calendar_edit_own'), 'modify_href' => $scripturl . '?action=post;msg=' . $topicinfo['id_first_msg'] . ';topic=' . $topic . '.0;calendar;eventid=' . $event['id_event'] . ';' . $context['session_var'] . '=' . $context['session_id'], 'can_export' => allowedTo('calendar_edit_any') || $event['id_member'] == $user_info['id'] && allowedTo('calendar_edit_own'), 'export_href' => $scripturl . '?action=calendar;sa=ical;eventid=' . $event['id_event'] . ';' . $context['session_var'] . '=' . $context['session_id'], 'start_date' => standardTime($start_date, $date_string, 'none'), 'start_timestamp' => $start_date, 'end_date' => standardTime($end_date, $date_string, 'none'), 'end_timestamp' => $end_date, 'is_last' => false); } if (!empty($context['linked_calendar_events'])) { $context['linked_calendar_events'][count($context['linked_calendar_events']) - 1]['is_last'] = true; $template_layers->add('display_calendar'); } } // Create the poll info if it exists. if ($context['is_poll']) { $template_layers->add('display_poll'); require_once SUBSDIR . '/Poll.subs.php'; loadPollContext($topicinfo['id_poll']); // Build the poll moderation button array. $context['poll_buttons'] = array('vote' => array('test' => 'allow_return_vote', 'text' => 'poll_return_vote', 'image' => 'poll_options.png', 'lang' => true, 'url' => $scripturl . '?topic=' . $context['current_topic'] . '.' . $context['start']), 'results' => array('test' => 'allow_poll_view', 'text' => 'poll_results', 'image' => 'poll_results.png', 'lang' => true, 'url' => $scripturl . '?topic=' . $context['current_topic'] . '.' . $context['start'] . ';viewresults'), 'change_vote' => array('test' => 'allow_change_vote', 'text' => 'poll_change_vote', 'image' => 'poll_change_vote.png', 'lang' => true, 'url' => $scripturl . '?action=poll;sa=vote;topic=' . $context['current_topic'] . '.' . $context['start'] . ';poll=' . $context['poll']['id'] . ';' . $context['session_var'] . '=' . $context['session_id']), 'lock' => array('test' => 'allow_lock_poll', 'text' => !$context['poll']['is_locked'] ? 'poll_lock' : 'poll_unlock', 'image' => 'poll_lock.png', 'lang' => true, 'url' => $scripturl . '?action=lockvoting;topic=' . $context['current_topic'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']), 'edit' => array('test' => 'allow_edit_poll', 'text' => 'poll_edit', 'image' => 'poll_edit.png', 'lang' => true, 'url' => $scripturl . '?action=editpoll;topic=' . $context['current_topic'] . '.' . $context['start']), 'remove_poll' => array('test' => 'can_remove_poll', 'text' => 'poll_remove', 'image' => 'admin_remove_poll.png', 'lang' => true, 'custom' => 'onclick="return confirm(\'' . $txt['poll_remove_warn'] . '\');"', 'url' => $scripturl . '?action=poll;sa=remove;topic=' . $context['current_topic'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id'])); // Allow mods to add additional buttons here call_integration_hook('integrate_poll_buttons'); } // Calculate the fastest way to get the messages! $ascending = true; $start = $_REQUEST['start']; $limit = $context['messages_per_page']; $firstIndex = 0; if ($start >= $context['total_visible_posts'] / 2 && $context['messages_per_page'] != -1) { $ascending = !$ascending; $limit = $context['total_visible_posts'] <= $start + $limit ? $context['total_visible_posts'] - $start : $limit; $start = $context['total_visible_posts'] <= $start + $limit ? 0 : $context['total_visible_posts'] - $start - $limit; $firstIndex = $limit - 1; } // Taking care of member specific settings $limit_settings = array('messages_per_page' => $context['messages_per_page'], 'start' => $start, 'offset' => $limit); // Get each post and poster in this topic. $topic_details = getTopicsPostsAndPoster($topic, $limit_settings, $ascending); $messages = $topic_details['messages']; $posters = array_unique($topic_details['all_posters']); $all_posters = $topic_details['all_posters']; unset($topic_details); call_integration_hook('integrate_display_message_list', array(&$messages, &$posters)); // Guests can't mark topics read or for notifications, just can't sorry. if (!$user_info['is_guest'] && !empty($messages)) { $mark_at_msg = max($messages); if ($mark_at_msg >= $topicinfo['id_last_msg']) { $mark_at_msg = $modSettings['maxMsgID']; } if ($mark_at_msg >= $topicinfo['new_from']) { markTopicsRead(array($user_info['id'], $topic, $mark_at_msg, $topicinfo['unwatched']), $topicinfo['new_from'] !== 0); } updateReadNotificationsFor($topic, $board); // Have we recently cached the number of new topics in this board, and it's still a lot? if (isset($_REQUEST['topicseen']) && isset($_SESSION['topicseen_cache'][$board]) && $_SESSION['topicseen_cache'][$board] > 5) { $_SESSION['topicseen_cache'][$board]--; } elseif (isset($_REQUEST['topicseen'])) { // Use the mark read tables... and the last visit to figure out if this should be read or not. $numNewTopics = getUnreadCountSince($board, empty($_SESSION['id_msg_last_visit']) ? 0 : $_SESSION['id_msg_last_visit']); // If there're no real new topics in this board, mark the board as seen. if (empty($numNewTopics)) { $_REQUEST['boardseen'] = true; } else { $_SESSION['topicseen_cache'][$board] = $numNewTopics; } } elseif (isset($_SESSION['topicseen_cache'][$board])) { $_SESSION['topicseen_cache'][$board]--; } // Mark board as seen if we came using last post link from BoardIndex. (or other places...) if (isset($_REQUEST['boardseen'])) { require_once SUBSDIR . '/Boards.subs.php'; markBoardsRead($board, false, false); } } $attachments = array(); // If there _are_ messages here... (probably an error otherwise :!) if (!empty($messages)) { require_once SUBSDIR . '/Attachments.subs.php'; // Fetch attachments. $includeUnapproved = !$modSettings['postmod_active'] || allowedTo('approve_posts'); if (!empty($modSettings['attachmentEnable']) && allowedTo('view_attachments')) { $attachments = getAttachments($messages, $includeUnapproved, 'filter_accessible_attachment', $all_posters); } $msg_parameters = array('message_list' => $messages, 'new_from' => $topicinfo['new_from']); $msg_selects = array(); $msg_tables = array(); call_integration_hook('integrate_message_query', array(&$msg_selects, &$msg_tables, &$msg_parameters)); // What? It's not like it *couldn't* be only guests in this topic... if (!empty($posters)) { loadMemberData($posters); } // Load in the likes for this group of messages if (!empty($modSettings['likes_enabled'])) { require_once SUBSDIR . '/Likes.subs.php'; $context['likes'] = loadLikes($messages, true); // ajax controller for likes loadJavascriptFile('like_posts.js', array('defer' => true)); loadLanguage('Errors'); // Initiate likes and the tooltips for likes addInlineJavascript(' $(document).ready(function () { var likePostInstance = likePosts.prototype.init({ oTxt: ({ btnText : ' . JavaScriptEscape($txt['ok_uppercase']) . ', likeHeadingError : ' . JavaScriptEscape($txt['like_heading_error']) . ', error_occurred : ' . JavaScriptEscape($txt['error_occurred']) . ' }), }); $(".like_button, .unlike_button").SiteTooltip({ hoverIntent: { sensitivity: 10, interval: 150, timeout: 50 } }); });', true); } $messages_request = loadMessageRequest($msg_selects, $msg_tables, $msg_parameters); if (!empty($modSettings['enableFollowup'])) { require_once SUBSDIR . '/FollowUps.subs.php'; $context['follow_ups'] = followupTopics($messages, $includeUnapproved); } // Go to the last message if the given time is beyond the time of the last message. if (isset($context['start_from']) && $context['start_from'] >= $topicinfo['num_replies']) { $context['start_from'] = $topicinfo['num_replies']; } // Since the anchor information is needed on the top of the page we load these variables beforehand. $context['first_message'] = isset($messages[$firstIndex]) ? $messages[$firstIndex] : $messages[0]; $context['first_new_message'] = isset($context['start_from']) && $_REQUEST['start'] == $context['start_from']; } else { $messages_request = false; $context['first_message'] = 0; $context['first_new_message'] = false; } $context['jump_to'] = array('label' => addslashes(un_htmlspecialchars($txt['jump_to'])), 'board_name' => htmlspecialchars(strtr(strip_tags($board_info['name']), array('&' => '&')), ENT_COMPAT, 'UTF-8'), 'child_level' => $board_info['child_level']); // Set the callback. (do you REALIZE how much memory all the messages would take?!?) // This will be called from the template. $context['get_message'] = array($this, 'prepareDisplayContext_callback'); // Now set all the wonderful, wonderful permissions... like moderation ones... $common_permissions = array('can_approve' => 'approve_posts', 'can_ban' => 'manage_bans', 'can_sticky' => 'make_sticky', 'can_merge' => 'merge_any', 'can_split' => 'split_any', 'calendar_post' => 'calendar_post', 'can_mark_notify' => 'mark_any_notify', 'can_send_topic' => 'send_topic', 'can_send_pm' => 'pm_send', 'can_send_email' => 'send_email_to_members', 'can_report_moderator' => 'report_any', 'can_moderate_forum' => 'moderate_forum', 'can_issue_warning' => 'issue_warning', 'can_restore_topic' => 'move_any', 'can_restore_msg' => 'move_any'); foreach ($common_permissions as $contextual => $perm) { $context[$contextual] = allowedTo($perm); } // Permissions with _any/_own versions. $context[YYY] => ZZZ_any/_own. $anyown_permissions = array('can_move' => 'move', 'can_lock' => 'lock', 'can_delete' => 'remove', 'can_add_poll' => 'poll_add', 'can_remove_poll' => 'poll_remove', 'can_reply' => 'post_reply', 'can_reply_unapproved' => 'post_unapproved_replies'); foreach ($anyown_permissions as $contextual => $perm) { $context[$contextual] = allowedTo($perm . '_any') || $context['user']['started'] && allowedTo($perm . '_own'); } // Cleanup all the permissions with extra stuff... $context['can_mark_notify'] &= !$context['user']['is_guest']; $context['can_sticky'] &= !empty($modSettings['enableStickyTopics']); $context['calendar_post'] &= !empty($modSettings['cal_enabled']) && (allowedTo('modify_any') || $context['user']['started'] && allowedTo('modify_own')); $context['can_add_poll'] &= !empty($modSettings['pollMode']) && $topicinfo['id_poll'] <= 0; $context['can_remove_poll'] &= !empty($modSettings['pollMode']) && $topicinfo['id_poll'] > 0; $context['can_reply'] &= empty($topicinfo['locked']) || allowedTo('moderate_board'); $context['can_reply_unapproved'] &= $modSettings['postmod_active'] && (empty($topicinfo['locked']) || allowedTo('moderate_board')); $context['can_issue_warning'] &= in_array('w', $context['admin_features']) && !empty($modSettings['warning_enable']); // Handle approval flags... $context['can_reply_approved'] = $context['can_reply']; $context['can_reply'] |= $context['can_reply_unapproved']; $context['can_quote'] = $context['can_reply'] && (empty($modSettings['disabledBBC']) || !in_array('quote', explode(',', $modSettings['disabledBBC']))); $context['can_mark_unread'] = !$user_info['is_guest'] && $settings['show_mark_read']; $context['can_unwatch'] = !$user_info['is_guest'] && $modSettings['enable_unwatch']; $context['can_send_topic'] = (!$modSettings['postmod_active'] || $topicinfo['approved']) && allowedTo('send_topic'); $context['can_print'] = empty($modSettings['disable_print_topic']); // Start this off for quick moderation - it will be or'd for each post. $context['can_remove_post'] = allowedTo('delete_any') || allowedTo('delete_replies') && $context['user']['started']; // Can restore topic? That's if the topic is in the recycle board and has a previous restore state. $context['can_restore_topic'] &= !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] == $board && !empty($topicinfo['id_previous_board']); $context['can_restore_msg'] &= !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] == $board && !empty($topicinfo['id_previous_topic']); $context['can_follow_up'] = !empty($modSettings['enableFollowup']) && boardsallowedto('post_new') !== array(); // Check if the draft functions are enabled and that they have permission to use them (for quick reply.) $context['drafts_save'] = !empty($modSettings['drafts_enabled']) && !empty($modSettings['drafts_post_enabled']) && allowedTo('post_draft') && $context['can_reply']; $context['drafts_autosave'] = !empty($context['drafts_save']) && !empty($modSettings['drafts_autosave_enabled']) && allowedTo('post_autosave_draft'); if (!empty($context['drafts_save'])) { loadLanguage('Drafts'); } if (!empty($context['drafts_autosave']) && empty($options['use_editor_quick_reply'])) { loadJavascriptFile('drafts.js'); } if (!empty($modSettings['mentions_enabled'])) { $context['mentions_enabled'] = true; // Just using the plain text quick reply and not the editor if (empty($options['use_editor_quick_reply'])) { loadJavascriptFile(array('jquery.atwho.js', 'jquery.caret.min.js', 'mentioning.js')); } loadCSSFile('jquery.atwho.css'); addInlineJavascript(' $(document).ready(function () { for (var i = 0, count = all_elk_mentions.length; i < count; i++) all_elk_mentions[i].oMention = new elk_mentions(all_elk_mentions[i].oOptions); });'); } // Load up the Quick ModifyTopic and Quick Reply scripts loadJavascriptFile('topic.js'); // Auto video embeding enabled? if (!empty($modSettings['enableVideoEmbeding'])) { addInlineJavascript(' $(document).ready(function() { $().linkifyvideo(oEmbedtext); });'); } // Load up the "double post" sequencing magic. if (!empty($options['display_quick_reply'])) { checkSubmitOnce('register'); $context['name'] = isset($_SESSION['guest_name']) ? $_SESSION['guest_name'] : ''; $context['email'] = isset($_SESSION['guest_email']) ? $_SESSION['guest_email'] : ''; if (!empty($options['use_editor_quick_reply']) && $context['can_reply']) { // Needed for the editor and message icons. require_once SUBSDIR . '/Editor.subs.php'; // Now create the editor. $editorOptions = array('id' => 'message', 'value' => '', 'labels' => array('post_button' => $txt['post']), 'height' => '250px', 'width' => '100%', 'preview_type' => 0); create_control_richedit($editorOptions); $context['attached'] = ''; $context['make_poll'] = isset($_REQUEST['poll']); // Message icons - customized icons are off? $context['icons'] = getMessageIcons($board); if (!empty($context['icons'])) { $context['icons'][count($context['icons']) - 1]['is_last'] = true; } } } addJavascriptVar(array('notification_topic_notice' => $context['is_marked_notify'] ? $txt['notification_disable_topic'] : $txt['notification_enable_topic']), true); if ($context['can_send_topic']) { addJavascriptVar(array('sendtopic_cancel' => $txt['modify_cancel'], 'sendtopic_back' => $txt['back'], 'sendtopic_close' => $txt['find_close'], 'sendtopic_error' => $txt['send_error_occurred'], 'required_field' => $txt['require_field']), true); } // Build the normal button array. $context['normal_buttons'] = array('reply' => array('test' => 'can_reply', 'text' => 'reply', 'image' => 'reply.png', 'lang' => true, 'url' => $scripturl . '?action=post;topic=' . $context['current_topic'] . '.' . $context['start'] . ';last_msg=' . $context['topic_last_message'], 'active' => true), 'notify' => array('test' => 'can_mark_notify', 'text' => $context['is_marked_notify'] ? 'unnotify' : 'notify', 'image' => ($context['is_marked_notify'] ? 'un' : '') . 'notify.png', 'lang' => true, 'custom' => 'onclick="return notifyButton(this);"', 'url' => $scripturl . '?action=notify;sa=' . ($context['is_marked_notify'] ? 'off' : 'on') . ';topic=' . $context['current_topic'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']), 'mark_unread' => array('test' => 'can_mark_unread', 'text' => 'mark_unread', 'image' => 'markunread.png', 'lang' => true, 'url' => $scripturl . '?action=markasread;sa=topic;t=' . $context['mark_unread_time'] . ';topic=' . $context['current_topic'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']), 'unwatch' => array('test' => 'can_unwatch', 'text' => ($context['topic_unwatched'] ? '' : 'un') . 'watch', 'image' => ($context['topic_unwatched'] ? '' : 'un') . 'watched.png', 'lang' => true, 'custom' => 'onclick="return unwatchButton(this);"', 'url' => $scripturl . '?action=unwatchtopic;topic=' . $context['current_topic'] . '.' . $context['start'] . ';sa=' . ($context['topic_unwatched'] ? 'off' : 'on') . ';' . $context['session_var'] . '=' . $context['session_id']), 'send' => array('test' => 'can_send_topic', 'text' => 'send_topic', 'image' => 'sendtopic.png', 'lang' => true, 'url' => $scripturl . '?action=emailuser;sa=sendtopic;topic=' . $context['current_topic'] . '.0', 'custom' => 'onclick="return sendtopicOverlayDiv(this.href, \'' . $txt['send_topic'] . '\');"'), 'print' => array('test' => 'can_print', 'text' => 'print', 'image' => 'print.png', 'lang' => true, 'custom' => 'rel="nofollow"', 'class' => 'new_win', 'url' => $scripturl . '?action=topic;sa=printpage;topic=' . $context['current_topic'] . '.0')); // Build the mod button array $context['mod_buttons'] = array('move' => array('test' => 'can_move', 'text' => 'move_topic', 'image' => 'admin_move.png', 'lang' => true, 'url' => $scripturl . '?action=movetopic;current_board=' . $context['current_board'] . ';topic=' . $context['current_topic'] . '.0'), 'delete' => array('test' => 'can_delete', 'text' => 'remove_topic', 'image' => 'admin_rem.png', 'lang' => true, 'custom' => 'onclick="return confirm(\'' . $txt['are_sure_remove_topic'] . '\');"', 'url' => $scripturl . '?action=removetopic2;topic=' . $context['current_topic'] . '.0;' . $context['session_var'] . '=' . $context['session_id']), 'lock' => array('test' => 'can_lock', 'text' => empty($context['is_locked']) ? 'set_lock' : 'set_unlock', 'image' => 'admin_lock.png', 'lang' => true, 'url' => $scripturl . '?action=topic;sa=lock;topic=' . $context['current_topic'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']), 'sticky' => array('test' => 'can_sticky', 'text' => empty($context['is_sticky']) ? 'set_sticky' : 'set_nonsticky', 'image' => 'admin_sticky.png', 'lang' => true, 'url' => $scripturl . '?action=topic;sa=sticky;topic=' . $context['current_topic'] . '.' . $context['start'] . ';' . $context['session_var'] . '=' . $context['session_id']), 'merge' => array('test' => 'can_merge', 'text' => 'merge', 'image' => 'merge.png', 'lang' => true, 'url' => $scripturl . '?action=mergetopics;board=' . $context['current_board'] . '.0;from=' . $context['current_topic']), 'calendar' => array('test' => 'calendar_post', 'text' => 'calendar_link', 'image' => 'linktocal.png', 'lang' => true, 'url' => $scripturl . '?action=post;calendar;msg=' . $context['topic_first_message'] . ';topic=' . $context['current_topic'] . '.0')); // Restore topic. eh? No monkey business. if ($context['can_restore_topic']) { $context['mod_buttons'][] = array('text' => 'restore_topic', 'image' => '', 'lang' => true, 'url' => $scripturl . '?action=restoretopic;topics=' . $context['current_topic'] . ';' . $context['session_var'] . '=' . $context['session_id']); } if ($context['can_reply'] && !empty($options['display_quick_reply'])) { $template_layers->add('quickreply'); } $template_layers->add('pages_and_buttons'); // Allow adding new buttons easily. call_integration_hook('integrate_display_buttons'); call_integration_hook('integrate_mod_buttons'); }
/** * Unlikes a post that you previously liked ... no negatives though, hurts feelings :'( * It redirects back to the referrer afterward. * It is accessed via ?action=like,sa=unlikepost. */ public function action_unlikepost() { global $user_info, $topic, $txt, $modSettings; $this->_id_liked = !empty($_REQUEST['msg']) ? (int) $_REQUEST['msg'] : 0; // We used to like these require_once SUBSDIR . '/Likes.subs.php'; require_once SUBSDIR . '/Messages.subs.php'; // Have to be able to access it to unlike it now if ($this->prepare_like() && canAccessMessage($this->_id_liked)) { $liked_message = basicMessageInfo($this->_id_liked, true, true); if ($liked_message) { $likeResult = likePost($user_info['id'], $liked_message, '-'); if ($likeResult === true) { // Oh noes, taking the like back, let them know so they can complain if (!empty($modSettings['mentions_enabled'])) { require_once CONTROLLERDIR . '/Mentions.controller.php'; $mentions = new Mentions_Controller(); $mentions->setData(array('id_member' => $liked_message['id_member'], 'type' => 'rlike', 'id_msg' => $this->_id_liked)); // Notifying that likes were removed ? if (!empty($modSettings['mentions_dont_notify_rlike'])) { $mentions->action_rlike(); } else { $mentions->action_add(); } } } elseif ($this->_api) { $this->_likes_response = array('result' => false, 'data' => $likeResult); } } elseif ($this->_api) { loadLanguage('Errors'); $this->_likes_response = array('result' => false, 'data' => $txt['like_unlike_error']); } } // Back we go if ($this->_api) { $this->likeResponse(); } elseif (!isset($_REQUEST['profile'])) { redirectexit('topic=' . $topic . '.msg' . $this->_id_liked . '#msg' . $this->_id_liked); } else { redirectexit('action=profile;area=showlikes;sa=given;u=' . $user_info['id']); } }
/** * Posts or saves the message composed with Post(). * * requires various permissions depending on the action. * handles attachment, post, and calendar saving. * sends off notifications, and allows for announcements and moderation. * accessed from ?action=post2. */ public function action_post2() { global $board, $topic, $txt, $modSettings, $context, $user_settings; global $user_info, $board_info, $options, $ignore_temp; // Sneaking off, are we? if (empty($_POST) && empty($topic)) { if (empty($_SERVER['CONTENT_LENGTH'])) { redirectexit('action=post;board=' . $board . '.0'); } else { fatal_lang_error('post_upload_error', false); } } elseif (empty($_POST) && !empty($topic)) { redirectexit('action=post;topic=' . $topic . '.0'); } // No need! $context['robot_no_index'] = true; // We are now in post2 action $context['current_action'] = 'post2'; require_once SOURCEDIR . '/AttachmentErrorContext.class.php'; // No errors as yet. $post_errors = Error_Context::context('post', 1); $attach_errors = Attachment_Error_Context::context(); // If the session has timed out, let the user re-submit their form. if (checkSession('post', '', false) != '') { $post_errors->addError('session_timeout'); // Disable the preview so that any potentially malicious code is not executed $_REQUEST['preview'] = false; return $this->action_post(); } // Wrong verification code? if (!$user_info['is_admin'] && !$user_info['is_mod'] && !empty($modSettings['posts_require_captcha']) && ($user_info['posts'] < $modSettings['posts_require_captcha'] || $user_info['is_guest'] && $modSettings['posts_require_captcha'] == -1)) { require_once SUBSDIR . '/VerificationControls.class.php'; $verificationOptions = array('id' => 'post'); $context['require_verification'] = create_control_verification($verificationOptions, true); if (is_array($context['require_verification'])) { foreach ($context['require_verification'] as $verification_error) { $post_errors->addError($verification_error); } } } require_once SUBSDIR . '/Boards.subs.php'; require_once SUBSDIR . '/Post.subs.php'; loadLanguage('Post'); // Drafts enabled and needed? if (!empty($modSettings['drafts_enabled']) && (isset($_POST['save_draft']) || isset($_POST['id_draft']))) { require_once SUBSDIR . '/Drafts.subs.php'; } // First check to see if they are trying to delete any current attachments. if (isset($_POST['attach_del'])) { $keep_temp = array(); $keep_ids = array(); foreach ($_POST['attach_del'] as $dummy) { if (strpos($dummy, 'post_tmp_' . $user_info['id']) !== false) { $keep_temp[] = $dummy; } else { $keep_ids[] = (int) $dummy; } } if (isset($_SESSION['temp_attachments'])) { foreach ($_SESSION['temp_attachments'] as $attachID => $attachment) { if (isset($_SESSION['temp_attachments']['post']['files'], $attachment['name']) && in_array($attachment['name'], $_SESSION['temp_attachments']['post']['files']) || in_array($attachID, $keep_temp) || strpos($attachID, 'post_tmp_' . $user_info['id']) === false) { continue; } unset($_SESSION['temp_attachments'][$attachID]); @unlink($attachment['tmp_name']); } } if (!empty($_REQUEST['msg'])) { require_once SUBSDIR . '/ManageAttachments.subs.php'; $attachmentQuery = array('attachment_type' => 0, 'id_msg' => (int) $_REQUEST['msg'], 'not_id_attach' => $keep_ids); removeAttachments($attachmentQuery); } } // Then try to upload any attachments. $context['attachments']['can']['post'] = !empty($modSettings['attachmentEnable']) && $modSettings['attachmentEnable'] == 1 && (allowedTo('post_attachment') || $modSettings['postmod_active'] && allowedTo('post_unapproved_attachments')); if ($context['attachments']['can']['post'] && empty($_POST['from_qr'])) { require_once SUBSDIR . '/Attachments.subs.php'; if (isset($_REQUEST['msg'])) { processAttachments((int) $_REQUEST['msg']); } else { processAttachments(); } } // Previewing? Go back to start. if (isset($_REQUEST['preview'])) { return $this->action_post(); } // Prevent double submission of this form. checkSubmitOnce('check'); // If this isn't a new topic load the topic info that we need. if (!empty($topic)) { require_once SUBSDIR . '/Topic.subs.php'; $topic_info = getTopicInfo($topic); // Though the topic should be there, it might have vanished. if (empty($topic_info)) { fatal_lang_error('topic_doesnt_exist'); } // Did this topic suddenly move? Just checking... if ($topic_info['id_board'] != $board) { fatal_lang_error('not_a_topic'); } } // Replying to a topic? if (!empty($topic) && !isset($_REQUEST['msg'])) { // Don't allow a post if it's locked. if ($topic_info['locked'] != 0 && !allowedTo('moderate_board')) { fatal_lang_error('topic_locked', false); } // Sorry, multiple polls aren't allowed... yet. You should stop giving me ideas :P. if (isset($_REQUEST['poll']) && $topic_info['id_poll'] > 0) { unset($_REQUEST['poll']); } // Do the permissions and approval stuff... $becomesApproved = true; if ($topic_info['id_member_started'] != $user_info['id']) { if ($modSettings['postmod_active'] && allowedTo('post_unapproved_replies_any') && !allowedTo('post_reply_any')) { $becomesApproved = false; } else { isAllowedTo('post_reply_any'); } } elseif (!allowedTo('post_reply_any')) { if ($modSettings['postmod_active']) { if (allowedTo('post_unapproved_replies_own') && !allowedTo('post_reply_own')) { $becomesApproved = false; } elseif ($user_info['is_guest'] && allowedTo('post_unapproved_replies_any')) { $becomesApproved = false; } else { isAllowedTo('post_reply_own'); } } } if (isset($_POST['lock'])) { // Nothing is changed to the lock. if (empty($topic_info['locked']) && empty($_POST['lock']) || !empty($_POST['lock']) && !empty($topic_info['locked'])) { unset($_POST['lock']); } elseif (!allowedTo(array('lock_any', 'lock_own')) || !allowedTo('lock_any') && $user_info['id'] != $topic_info['id_member_started']) { unset($_POST['lock']); } elseif (!allowedTo('lock_any')) { // You cannot override a moderator lock. if ($topic_info['locked'] == 1) { unset($_POST['lock']); } else { $_POST['lock'] = empty($_POST['lock']) ? 0 : 2; } } else { $_POST['lock'] = empty($_POST['lock']) ? 0 : 1; } } // So you wanna (un)sticky this...let's see. if (isset($_POST['sticky']) && (empty($modSettings['enableStickyTopics']) || $_POST['sticky'] == $topic_info['is_sticky'] || !allowedTo('make_sticky'))) { unset($_POST['sticky']); } // If drafts are enabled, then pass this off if (!empty($modSettings['drafts_enabled']) && isset($_POST['save_draft'])) { saveDraft(); return $this->action_post(); } // If the number of replies has changed, if the setting is enabled, go back to action_post() - which handles the error. if (empty($options['no_new_reply_warning']) && isset($_POST['last_msg']) && $topic_info['id_last_msg'] > $_POST['last_msg']) { addInlineJavascript(' $(document).ready(function () { $("html,body").scrollTop($(\'.category_header:visible:first\').offset().top); });'); return $this->action_post(); } $posterIsGuest = $user_info['is_guest']; } elseif (empty($topic)) { // Now don't be silly, new topics will get their own id_msg soon enough. unset($_REQUEST['msg'], $_POST['msg'], $_GET['msg']); // Do like, the permissions, for safety and stuff... $becomesApproved = true; if ($modSettings['postmod_active'] && !allowedTo('post_new') && allowedTo('post_unapproved_topics')) { $becomesApproved = false; } else { isAllowedTo('post_new'); } if (isset($_POST['lock'])) { // New topics are by default not locked. if (empty($_POST['lock'])) { unset($_POST['lock']); } elseif (!allowedTo(array('lock_any', 'lock_own'))) { unset($_POST['lock']); } else { $_POST['lock'] = allowedTo('lock_any') ? 1 : 2; } } if (isset($_POST['sticky']) && (empty($modSettings['enableStickyTopics']) || empty($_POST['sticky']) || !allowedTo('make_sticky'))) { unset($_POST['sticky']); } // Saving your new topic as a draft first? if (!empty($modSettings['drafts_enabled']) && isset($_POST['save_draft'])) { saveDraft(); return $this->action_post(); } $posterIsGuest = $user_info['is_guest']; } elseif (isset($_REQUEST['msg']) && !empty($topic)) { $_REQUEST['msg'] = (int) $_REQUEST['msg']; require_once SUBSDIR . '/Messages.subs.php'; $msgInfo = basicMessageInfo($_REQUEST['msg'], true); if (empty($msgInfo)) { fatal_lang_error('cant_find_messages', false); } if (!empty($topic_info['locked']) && !allowedTo('moderate_board')) { fatal_lang_error('topic_locked', false); } if (isset($_POST['lock'])) { // Nothing changes to the lock status. if (empty($_POST['lock']) && empty($topic_info['locked']) || !empty($_POST['lock']) && !empty($topic_info['locked'])) { unset($_POST['lock']); } elseif (!allowedTo(array('lock_any', 'lock_own')) || !allowedTo('lock_any') && $user_info['id'] != $topic_info['id_member_started']) { unset($_POST['lock']); } elseif (!allowedTo('lock_any')) { // You're not allowed to break a moderator's lock. if ($topic_info['locked'] == 1) { unset($_POST['lock']); } else { $_POST['lock'] = empty($_POST['lock']) ? 0 : 2; } } else { $_POST['lock'] = empty($_POST['lock']) ? 0 : 1; } } // Change the sticky status of this topic? if (isset($_POST['sticky']) && (!allowedTo('make_sticky') || $_POST['sticky'] == $topic_info['is_sticky'])) { unset($_POST['sticky']); } if ($msgInfo['id_member'] == $user_info['id'] && !allowedTo('modify_any')) { if ((!$modSettings['postmod_active'] || $msgInfo['approved']) && !empty($modSettings['edit_disable_time']) && $msgInfo['poster_time'] + ($modSettings['edit_disable_time'] + 5) * 60 < time()) { fatal_lang_error('modify_post_time_passed', false); } elseif ($topic_info['id_member_started'] == $user_info['id'] && !allowedTo('modify_own')) { isAllowedTo('modify_replies'); } else { isAllowedTo('modify_own'); } } elseif ($topic_info['id_member_started'] == $user_info['id'] && !allowedTo('modify_any')) { isAllowedTo('modify_replies'); // If you're modifying a reply, I say it better be logged... $moderationAction = true; } else { isAllowedTo('modify_any'); // Log it, assuming you're not modifying your own post. if ($msgInfo['id_member'] != $user_info['id']) { $moderationAction = true; } } // If drafts are enabled, then lets send this off to save if (!empty($modSettings['drafts_enabled']) && isset($_POST['save_draft'])) { saveDraft(); return $this->action_post(); } $posterIsGuest = empty($msgInfo['id_member']); // Can they approve it? $can_approve = allowedTo('approve_posts'); $becomesApproved = $modSettings['postmod_active'] ? $can_approve && !$msgInfo['approved'] ? !empty($_REQUEST['approve']) ? 1 : 0 : $msgInfo['approved'] : 1; $approve_has_changed = $msgInfo['approved'] != $becomesApproved; if (!allowedTo('moderate_forum') || !$posterIsGuest) { $_POST['guestname'] = $msgInfo['poster_name']; $_POST['email'] = $msgInfo['poster_email']; } } // In case we want to override if (allowedTo('approve_posts')) { $becomesApproved = !isset($_REQUEST['approve']) || !empty($_REQUEST['approve']) ? 1 : 0; $approve_has_changed = isset($msgInfo['approved']) ? $msgInfo['approved'] != $becomesApproved : false; } // If the poster is a guest evaluate the legality of name and email. if ($posterIsGuest) { $_POST['guestname'] = !isset($_POST['guestname']) ? '' : Util::htmlspecialchars(trim($_POST['guestname'])); $_POST['email'] = !isset($_POST['email']) ? '' : Util::htmlspecialchars(trim($_POST['email'])); if ($_POST['guestname'] == '' || $_POST['guestname'] == '_') { $post_errors->addError('no_name'); } if (Util::strlen($_POST['guestname']) > 25) { $post_errors->addError('long_name'); } if (empty($modSettings['guest_post_no_email'])) { // Only check if they changed it! if (!isset($msgInfo) || $msgInfo['poster_email'] != $_POST['email']) { require_once SUBSDIR . '/DataValidator.class.php'; if (!allowedTo('moderate_forum') && !Data_Validator::is_valid($_POST, array('email' => 'valid_email|required'), array('email' => 'trim'))) { empty($_POST['email']) ? $post_errors->addError('no_email') : $post_errors->addError('bad_email'); } } // Now make sure this email address is not banned from posting. isBannedEmail($_POST['email'], 'cannot_post', sprintf($txt['you_are_post_banned'], $txt['guest_title'])); } // In case they are making multiple posts this visit, help them along by storing their name. if (!$post_errors->hasErrors()) { $_SESSION['guest_name'] = $_POST['guestname']; $_SESSION['guest_email'] = $_POST['email']; } } // Check the subject and message. if (!isset($_POST['subject']) || Util::htmltrim(Util::htmlspecialchars($_POST['subject'])) === '') { $post_errors->addError('no_subject'); } if (!isset($_POST['message']) || Util::htmltrim(Util::htmlspecialchars($_POST['message'], ENT_QUOTES)) === '') { $post_errors->addError('no_message'); } elseif (!empty($modSettings['max_messageLength']) && Util::strlen($_POST['message']) > $modSettings['max_messageLength']) { $post_errors->addError(array('long_message', array($modSettings['max_messageLength']))); } else { // Prepare the message a bit for some additional testing. $_POST['message'] = Util::htmlspecialchars($_POST['message'], ENT_QUOTES); // Preparse code. (Zef) if ($user_info['is_guest']) { $user_info['name'] = $_POST['guestname']; } preparsecode($_POST['message']); // Let's see if there's still some content left without the tags. if (Util::htmltrim(strip_tags(parse_bbc($_POST['message'], false), '<img>')) === '' && (!allowedTo('admin_forum') || strpos($_POST['message'], '[html]') === false)) { $post_errors->addError('no_message'); } } if (isset($_POST['calendar']) && !isset($_REQUEST['deleteevent']) && Util::htmltrim($_POST['evtitle']) === '') { $post_errors->addError('no_event'); } // Validate the poll... if (isset($_REQUEST['poll']) && !empty($modSettings['pollMode'])) { if (!empty($topic) && !isset($_REQUEST['msg'])) { fatal_lang_error('no_access', false); } // This is a new topic... so it's a new poll. if (empty($topic)) { isAllowedTo('poll_post'); } elseif ($user_info['id'] == $topic_info['id_member_started'] && !allowedTo('poll_add_any')) { isAllowedTo('poll_add_own'); } else { isAllowedTo('poll_add_any'); } if (!isset($_POST['question']) || trim($_POST['question']) == '') { $post_errors->addError('no_question'); } $_POST['options'] = empty($_POST['options']) ? array() : htmltrim__recursive($_POST['options']); // Get rid of empty ones. foreach ($_POST['options'] as $k => $option) { if ($option == '') { unset($_POST['options'][$k], $_POST['options'][$k]); } } // What are you going to vote between with one choice?!? if (count($_POST['options']) < 2) { $post_errors->addError('poll_few'); } elseif (count($_POST['options']) > 256) { $post_errors->addError('poll_many'); } } if ($posterIsGuest) { // If user is a guest, make sure the chosen name isn't taken. require_once SUBSDIR . '/Members.subs.php'; if (isReservedName($_POST['guestname'], 0, true, false) && (!isset($msgInfo['poster_name']) || $_POST['guestname'] != $msgInfo['poster_name'])) { $post_errors->addError('bad_name'); } } elseif (!isset($_REQUEST['msg'])) { $_POST['guestname'] = $user_info['username']; $_POST['email'] = $user_info['email']; } // Posting somewhere else? Are we sure you can? if (!empty($_REQUEST['post_in_board'])) { $new_board = (int) $_REQUEST['post_in_board']; if (!allowedTo('post_new', $new_board)) { $post_in_board = boardInfo($new_board); if (!empty($post_in_board)) { $post_errors->addError(array('post_new_board', array($post_in_board['name']))); } else { $post_errors->addError('post_new'); } } } // Any mistakes? if ($post_errors->hasErrors() || $attach_errors->hasErrors()) { addInlineJavascript(' $(document).ready(function () { $("html,body").scrollTop($(\'.category_header:visible:first\').offset().top); });'); return $this->action_post(); } // Make sure the user isn't spamming the board. if (!isset($_REQUEST['msg'])) { spamProtection('post'); } // At about this point, we're posting and that's that. ignore_user_abort(true); @set_time_limit(300); // Add special html entities to the subject, name, and email. $_POST['subject'] = strtr(Util::htmlspecialchars($_POST['subject']), array("\r" => '', "\n" => '', "\t" => '')); $_POST['guestname'] = htmlspecialchars($_POST['guestname'], ENT_COMPAT, 'UTF-8'); $_POST['email'] = htmlspecialchars($_POST['email'], ENT_COMPAT, 'UTF-8'); // At this point, we want to make sure the subject isn't too long. if (Util::strlen($_POST['subject']) > 100) { $_POST['subject'] = Util::substr($_POST['subject'], 0, 100); } if (!empty($modSettings['mentions_enabled']) && !empty($_REQUEST['uid'])) { $query_params = array(); $query_params['member_ids'] = array_unique(array_map('intval', $_REQUEST['uid'])); require_once SUBSDIR . '/Members.subs.php'; $mentioned_members = membersBy('member_ids', $query_params, true); $replacements = 0; $actually_mentioned = array(); foreach ($mentioned_members as $member) { $_POST['message'] = str_replace('@' . $member['real_name'], '[member=' . $member['id_member'] . ']' . $member['real_name'] . '[/member]', $_POST['message'], $replacements); if ($replacements > 0) { $actually_mentioned[] = $member['id_member']; } } } // Make the poll... if (isset($_REQUEST['poll'])) { // Make sure that the user has not entered a ridiculous number of options.. if (empty($_POST['poll_max_votes']) || $_POST['poll_max_votes'] <= 0) { $_POST['poll_max_votes'] = 1; } elseif ($_POST['poll_max_votes'] > count($_POST['options'])) { $_POST['poll_max_votes'] = count($_POST['options']); } else { $_POST['poll_max_votes'] = (int) $_POST['poll_max_votes']; } $_POST['poll_expire'] = (int) $_POST['poll_expire']; $_POST['poll_expire'] = $_POST['poll_expire'] > 9999 ? 9999 : ($_POST['poll_expire'] < 0 ? 0 : $_POST['poll_expire']); // Just set it to zero if it's not there.. if (!isset($_POST['poll_hide'])) { $_POST['poll_hide'] = 0; } else { $_POST['poll_hide'] = (int) $_POST['poll_hide']; } $_POST['poll_change_vote'] = isset($_POST['poll_change_vote']) ? 1 : 0; $_POST['poll_guest_vote'] = isset($_POST['poll_guest_vote']) ? 1 : 0; // Make sure guests are actually allowed to vote generally. if ($_POST['poll_guest_vote']) { require_once SUBSDIR . '/Members.subs.php'; $allowedVoteGroups = groupsAllowedTo('poll_vote', $board); if (!in_array(-1, $allowedVoteGroups['allowed'])) { $_POST['poll_guest_vote'] = 0; } } // If the user tries to set the poll too far in advance, don't let them. if (!empty($_POST['poll_expire']) && $_POST['poll_expire'] < 1) { fatal_lang_error('poll_range_error', false); } elseif (empty($_POST['poll_expire']) && $_POST['poll_hide'] == 2) { $_POST['poll_hide'] = 1; } // Clean up the question and answers. $_POST['question'] = htmlspecialchars($_POST['question'], ENT_COMPAT, 'UTF-8'); $_POST['question'] = Util::substr($_POST['question'], 0, 255); $_POST['question'] = preg_replace('~&#(\\d{4,5}|[2-9]\\d{2,4}|1[2-9]\\d);~', '&#$1;', $_POST['question']); $_POST['options'] = htmlspecialchars__recursive($_POST['options']); // Finally, make the poll. require_once SUBSDIR . '/Poll.subs.php'; $id_poll = createPoll($_POST['question'], $user_info['id'], $_POST['guestname'], $_POST['poll_max_votes'], $_POST['poll_hide'], $_POST['poll_expire'], $_POST['poll_change_vote'], $_POST['poll_guest_vote'], $_POST['options']); } else { $id_poll = 0; } // ...or attach a new file... if (empty($ignore_temp) && $context['attachments']['can']['post'] && !empty($_SESSION['temp_attachments']) && empty($_POST['from_qr'])) { $attachIDs = array(); foreach ($_SESSION['temp_attachments'] as $attachID => $attachment) { if ($attachID != 'initial_error' && strpos($attachID, 'post_tmp_' . $user_info['id']) === false) { continue; } // If there was an initial error just show that message. if ($attachID == 'initial_error') { unset($_SESSION['temp_attachments']); break; } // No errors, then try to create the attachment if (empty($attachment['errors'])) { // Load the attachmentOptions array with the data needed to create an attachment $attachmentOptions = array('post' => isset($_REQUEST['msg']) ? $_REQUEST['msg'] : 0, 'poster' => $user_info['id'], 'name' => $attachment['name'], 'tmp_name' => $attachment['tmp_name'], 'size' => isset($attachment['size']) ? $attachment['size'] : 0, 'mime_type' => isset($attachment['type']) ? $attachment['type'] : '', 'id_folder' => isset($attachment['id_folder']) ? $attachment['id_folder'] : 0, 'approved' => !$modSettings['postmod_active'] || allowedTo('post_attachment'), 'errors' => array()); if (createAttachment($attachmentOptions)) { $attachIDs[] = $attachmentOptions['id']; if (!empty($attachmentOptions['thumb'])) { $attachIDs[] = $attachmentOptions['thumb']; } } } else { @unlink($attachment['tmp_name']); } } unset($_SESSION['temp_attachments']); } // Creating a new topic? $newTopic = empty($_REQUEST['msg']) && empty($topic); $_POST['icon'] = !empty($attachIDs) && $_POST['icon'] == 'xx' ? 'clip' : $_POST['icon']; // Collect all parameters for the creation or modification of a post. $msgOptions = array('id' => empty($_REQUEST['msg']) ? 0 : (int) $_REQUEST['msg'], 'subject' => $_POST['subject'], 'body' => $_POST['message'], 'icon' => preg_replace('~[\\./\\\\*:"\'<>]~', '', $_POST['icon']), 'smileys_enabled' => !isset($_POST['ns']), 'attachments' => empty($attachIDs) ? array() : $attachIDs, 'approved' => $becomesApproved); $topicOptions = array('id' => empty($topic) ? 0 : $topic, 'board' => $board, 'poll' => isset($_REQUEST['poll']) ? $id_poll : null, 'lock_mode' => isset($_POST['lock']) ? (int) $_POST['lock'] : null, 'sticky_mode' => isset($_POST['sticky']) && !empty($modSettings['enableStickyTopics']) ? (int) $_POST['sticky'] : null, 'mark_as_read' => true, 'is_approved' => !$modSettings['postmod_active'] || empty($topic) || !empty($board_info['cur_topic_approved'])); $posterOptions = array('id' => $user_info['id'], 'name' => $_POST['guestname'], 'email' => $_POST['email'], 'update_post_count' => !$user_info['is_guest'] && !isset($_REQUEST['msg']) && $board_info['posts_count']); // This is an already existing message. Edit it. if (!empty($_REQUEST['msg'])) { // Have admins allowed people to hide their screwups? if (time() - $msgInfo['poster_time'] > $modSettings['edit_wait_time'] || $user_info['id'] != $msgInfo['id_member']) { $msgOptions['modify_time'] = time(); $msgOptions['modify_name'] = $user_info['name']; } // This will save some time... if (empty($approve_has_changed)) { unset($msgOptions['approved']); } modifyPost($msgOptions, $topicOptions, $posterOptions); } else { if (!empty($modSettings['enableFollowup']) && !empty($_REQUEST['followup'])) { $original_post = (int) $_REQUEST['followup']; } // We also have to fake the board: // if it's valid and it's not the current, let's forget about the "current" and load the new one if (!empty($new_board) && $board !== $new_board) { $board = $new_board; loadBoard(); // Some details changed $topicOptions['board'] = $board; $topicOptions['is_approved'] = !$modSettings['postmod_active'] || empty($topic) || !empty($board_info['cur_topic_approved']); $posterOptions['update_post_count'] = !$user_info['is_guest'] && !isset($_REQUEST['msg']) && $board_info['posts_count']; } createPost($msgOptions, $topicOptions, $posterOptions); if (isset($topicOptions['id'])) { $topic = $topicOptions['id']; } if (!empty($modSettings['enableFollowup'])) { require_once SUBSDIR . '/FollowUps.subs.php'; require_once SUBSDIR . '/Messages.subs.php'; // Time to update the original message with a pointer to the new one if (!empty($original_post) && canAccessMessage($original_post)) { linkMessages($original_post, $topic); } } } // If we had a draft for this, its time to remove it since it was just posted if (!empty($modSettings['drafts_enabled']) && !empty($_POST['id_draft'])) { deleteDrafts($_POST['id_draft'], $user_info['id']); } // Editing or posting an event? if (isset($_POST['calendar']) && (!isset($_REQUEST['eventid']) || $_REQUEST['eventid'] == -1)) { require_once SUBSDIR . '/Calendar.subs.php'; // Make sure they can link an event to this post. canLinkEvent(); // Insert the event. $eventOptions = array('id_board' => $board, 'id_topic' => $topic, 'title' => $_POST['evtitle'], 'member' => $user_info['id'], 'start_date' => sprintf('%04d-%02d-%02d', $_POST['year'], $_POST['month'], $_POST['day']), 'span' => isset($_POST['span']) && $_POST['span'] > 0 ? min((int) $modSettings['cal_maxspan'], (int) $_POST['span'] - 1) : 0); insertEvent($eventOptions); } elseif (isset($_POST['calendar'])) { $_REQUEST['eventid'] = (int) $_REQUEST['eventid']; // Validate the post... require_once SUBSDIR . '/Calendar.subs.php'; validateEventPost(); // If you're not allowed to edit any events, you have to be the poster. if (!allowedTo('calendar_edit_any')) { $event_poster = getEventPoster($_REQUEST['eventid']); // Silly hacker, Trix are for kids. ...probably trademarked somewhere, this is FAIR USE! (parody...) isAllowedTo('calendar_edit_' . ($event_poster == $user_info['id'] ? 'own' : 'any')); } // Delete it? if (isset($_REQUEST['deleteevent'])) { removeEvent($_REQUEST['eventid']); } else { $span = !empty($modSettings['cal_allowspan']) && !empty($_REQUEST['span']) ? min((int) $modSettings['cal_maxspan'], (int) $_REQUEST['span'] - 1) : 0; $start_time = mktime(0, 0, 0, (int) $_REQUEST['month'], (int) $_REQUEST['day'], (int) $_REQUEST['year']); $eventOptions = array('start_date' => strftime('%Y-%m-%d', $start_time), 'end_date' => strftime('%Y-%m-%d', $start_time + $span * 86400), 'title' => $_REQUEST['evtitle']); modifyEvent($_REQUEST['eventid'], $eventOptions); } } // Marking boards as read. // (You just posted and they will be unread.) if (!$user_info['is_guest']) { $board_list = !empty($board_info['parent_boards']) ? array_keys($board_info['parent_boards']) : array(); // Returning to the topic? if (!empty($_REQUEST['goback'])) { $board_list[] = $board; } if (!empty($board_list)) { markBoardsRead($board_list, false, false); } } // Turn notification on or off. if (!empty($_POST['notify']) && allowedTo('mark_any_notify')) { setTopicNotification($user_info['id'], $topic, true); } elseif (!$newTopic) { setTopicNotification($user_info['id'], $topic, false); } // Log an act of moderation - modifying. if (!empty($moderationAction)) { logAction('modify', array('topic' => $topic, 'message' => (int) $_REQUEST['msg'], 'member' => $msgInfo['id_member'], 'board' => $board)); } if (isset($_POST['lock']) && $_POST['lock'] != 2) { logAction(empty($_POST['lock']) ? 'unlock' : 'lock', array('topic' => $topicOptions['id'], 'board' => $topicOptions['board'])); } if (isset($_POST['sticky']) && !empty($modSettings['enableStickyTopics'])) { logAction(empty($_POST['sticky']) ? 'unsticky' : 'sticky', array('topic' => $topicOptions['id'], 'board' => $topicOptions['board'])); } // Notify any members who have notification turned on for this topic/board - only do this if it's going to be approved(!) if ($becomesApproved) { require_once SUBSDIR . '/Notification.subs.php'; if ($newTopic) { $notifyData = array('body' => $_POST['message'], 'subject' => $_POST['subject'], 'name' => $user_info['name'], 'poster' => $user_info['id'], 'msg' => $msgOptions['id'], 'board' => $board, 'topic' => $topic, 'signature' => isset($user_settings['signature']) ? $user_settings['signature'] : ''); sendBoardNotifications($notifyData); } elseif (empty($_REQUEST['msg'])) { // Only send it to everyone if the topic is approved, otherwise just to the topic starter if they want it. if ($topic_info['approved']) { sendNotifications($topic, 'reply'); } else { sendNotifications($topic, 'reply', array(), $topic_info['id_member_started']); } } } if (!empty($modSettings['mentions_enabled']) && !empty($actually_mentioned)) { require_once CONTROLLERDIR . '/Mentions.controller.php'; $mentions = new Mentions_Controller(); $mentions->setData(array('id_member' => $actually_mentioned, 'type' => 'men', 'id_msg' => $msgOptions['id'], 'status' => $becomesApproved ? 'new' : 'unapproved')); $mentions->action_add(); } if ($board_info['num_topics'] == 0) { cache_put_data('board-' . $board, null, 120); } if (!empty($_POST['announce_topic'])) { redirectexit('action=announce;sa=selectgroup;topic=' . $topic . (!empty($_POST['move']) && allowedTo('move_any') ? ';move' : '') . (empty($_REQUEST['goback']) ? '' : ';goback')); } if (!empty($_POST['move']) && allowedTo('move_any')) { redirectexit('action=movetopic;topic=' . $topic . '.0' . (empty($_REQUEST['goback']) ? '' : ';goback')); } // Return to post if the mod is on. if (isset($_REQUEST['msg']) && !empty($_REQUEST['goback'])) { redirectexit('topic=' . $topic . '.msg' . $_REQUEST['msg'] . '#msg' . $_REQUEST['msg'], isBrowser('ie')); } elseif (!empty($_REQUEST['goback'])) { redirectexit('topic=' . $topic . '.new#new', isBrowser('ie')); } else { redirectexit('board=' . $board . '.0'); } }
/** * Show all the users buddies, as well as a add/delete interface. * * @param int $memID id_member */ public function action_editBuddies($memID) { global $context, $user_profile, $memberContext, $modSettings; $db = database(); loadTemplate('ProfileOptions'); // We want to view what we're doing :P $context['sub_template'] = 'editBuddies'; loadJavascriptFile('suggest.js', array('defer' => true)); // For making changes! $buddiesArray = explode(',', $user_profile[$memID]['buddy_list']); foreach ($buddiesArray as $k => $dummy) { if ($dummy == '') { unset($buddiesArray[$k]); } } // Removing a buddy? if (isset($_GET['remove'])) { checkSession('get'); call_integration_hook('integrate_remove_buddy', array($memID)); // Heh, I'm lazy, do it the easy way... foreach ($buddiesArray as $key => $buddy) { if ($buddy == (int) $_GET['remove']) { unset($buddiesArray[$key]); } } // Make the changes. $user_profile[$memID]['buddy_list'] = implode(',', $buddiesArray); updateMemberData($memID, array('buddy_list' => $user_profile[$memID]['buddy_list'])); // Redirect off the page because we don't like all this ugly query stuff to stick in the history. redirectexit('action=profile;area=lists;sa=buddies;u=' . $memID); } elseif (isset($_POST['new_buddy'])) { checkSession(); // Prepare the string for extraction... $_POST['new_buddy'] = strtr(Util::htmlspecialchars($_POST['new_buddy'], ENT_QUOTES), array('"' => '"')); preg_match_all('~"([^"]+)"~', $_POST['new_buddy'], $matches); $new_buddies = array_unique(array_merge($matches[1], explode(',', preg_replace('~"[^"]+"~', '', $_POST['new_buddy'])))); foreach ($new_buddies as $k => $dummy) { $new_buddies[$k] = strtr(trim($new_buddies[$k]), array('\'' => ''')); if (strlen($new_buddies[$k]) == 0 || in_array($new_buddies[$k], array($user_profile[$memID]['member_name'], $user_profile[$memID]['real_name']))) { unset($new_buddies[$k]); } } call_integration_hook('integrate_add_buddies', array($memID, &$new_buddies)); if (!empty($new_buddies)) { // Now find out the id_member of the buddy. $request = $db->query('', ' SELECT id_member FROM {db_prefix}members WHERE member_name IN ({array_string:new_buddies}) OR real_name IN ({array_string:new_buddies}) LIMIT {int:count_new_buddies}', array('new_buddies' => $new_buddies, 'count_new_buddies' => count($new_buddies))); // Let them know who's their buddy. if (!empty($modSettings['mentions_enabled']) && !empty($modSettings['mentions_buddy'])) { require_once CONTROLLERDIR . '/Mentions.controller.php'; $mentions = new Mentions_Controller(); } // Add the new member to the buddies array. while ($row = $db->fetch_assoc($request)) { $buddiesArray[] = (int) $row['id_member']; if (!empty($modSettings['mentions_enabled']) && !empty($modSettings['mentions_buddy'])) { // Set a mentions for our buddy. $mentions->setData(array('id_member' => $row['id_member'], 'type' => 'buddy', 'id_msg' => 0)); $mentions->action_add(); } } $db->free_result($request); // Now update the current users buddy list. $user_profile[$memID]['buddy_list'] = implode(',', $buddiesArray); updateMemberData($memID, array('buddy_list' => $user_profile[$memID]['buddy_list'])); } // Back to the buddy list! redirectexit('action=profile;area=lists;sa=buddies;u=' . $memID); } // Get all the users "buddies"... $buddies = array(); if (!empty($buddiesArray)) { require_once SUBSDIR . '/Members.subs.php'; $result = getBasicMemberData($buddiesArray, array('sort' => 'real_name', 'limit' => substr_count($user_profile[$memID]['buddy_list'], ',') + 1)); foreach ($result as $row) { $buddies[] = $row['id_member']; } } $context['buddy_count'] = count($buddies); // Load all the members up. loadMemberData($buddies, false, 'profile'); // Setup the context for each buddy. $context['buddies'] = array(); foreach ($buddies as $buddy) { loadMemberContext($buddy, true); $context['buddies'][$buddy] = $memberContext[$buddy]; } call_integration_hook('integrate_view_buddies', array($memID)); }