/**
  * This is the main method for markasread controller when using APIs.
  * @uses Xml template generic_xml_buttons sub template
  */
 public function action_index_api()
 {
     global $context, $txt, $user_info, $scripturl;
     loadTemplate('Xml');
     Template_Layers::getInstance()->removeAll();
     $context['sub_template'] = 'generic_xml_buttons';
     // Guests can't mark things.
     if ($user_info['is_guest']) {
         loadLanguage('Errors');
         $context['xml_data'] = array('error' => 1, 'text' => $txt['not_guests']);
         return;
     }
     if (checkSession('get', '', false)) {
         // Again, this is a special case, someone will deal with the others later :P
         if (isset($_REQUEST['sa']) && $_REQUEST['sa'] == 'all') {
             loadLanguage('Errors');
             $context['xml_data'] = array('error' => 1, 'url' => $scripturl . '?action=markasread;sa=all;' . $context['session_var'] . '=' . $context['session_id']);
             return;
         } else {
             obExit(false);
         }
     }
     $this->_dispatch();
     // For the time being this is a special case, but in BoardIndex no, we don't want it
     if (isset($_REQUEST['sa']) && ($_REQUEST['sa'] == 'all' || $_REQUEST['sa'] == 'board') && !isset($_REQUEST['bi'])) {
         $context['xml_data'] = array('text' => $txt['topic_alert_none'], 'body' => str_replace('{unread_all_url}', $scripturl . '?action=unread;all' . sprintf($this->_querystring_board_limits, 0) . $this->_querystring_sort_limits, $txt['unread_topics_visit_none']));
     } else {
         obExit(false);
     }
 }
 /**
  * Handling function for generating reports.
  *  - Requires the admin_forum permission.
  *  - Loads the Reports template and language files.
  *  - Decides which type of report to generate, if this isn't passed
  *    through the querystring it will set the report_type sub-template to
  *    force the user to choose which type.
  * - When generating a report chooses which sub_template to use.
  * - Depends on the cal_enabled setting, and many of the other cal_ settings.
  * - Will call the relevant report generation function.
  * - If generating report will call finishTables before returning.
  *
  * Accessed through ?action=admin;area=reports.
  *
  * @see Action_Controller::action_index()
  */
 public function action_index()
 {
     global $txt, $context, $scripturl;
     // Only admins, only EVER admins!
     isAllowedTo('admin_forum');
     // Let's get our things running...
     loadTemplate('Reports');
     loadLanguage('Reports');
     $context['page_title'] = $txt['generate_reports'];
     // These are the types of reports which exist - and the functions to generate them.
     $context['report_types'] = array('boards' => 'action_boards', 'board_perms' => 'action_board_perms', 'member_groups' => 'action_member_groups', 'group_perms' => 'action_group_perms', 'staff' => 'action_staff');
     call_integration_hook('integrate_report_types');
     // Load up all the tabs...
     $context[$context['admin_menu_name']]['tab_data'] = array('title' => $txt['generate_reports'], 'help' => '', 'description' => $txt['generate_reports_desc']);
     $is_first = 0;
     foreach ($context['report_types'] as $k => $temp) {
         $context['report_types'][$k] = array('id' => $k, 'title' => isset($txt['gr_type_' . $k]) ? $txt['gr_type_' . $k] : $k, 'description' => isset($txt['gr_type_desc_' . $k]) ? $txt['gr_type_desc_' . $k] : null, 'function' => $temp, 'is_first' => $is_first++ == 0);
     }
     // If they haven't choosen a report type which is valid, send them off to the report type chooser!
     if (empty($_REQUEST['rt']) || !isset($context['report_types'][$_REQUEST['rt']])) {
         $context['sub_template'] = 'report_type';
         return;
     }
     $context['report_type'] = $_REQUEST['rt'];
     $context['sub_template'] = 'generate_report';
     // What are valid templates for showing reports?
     $reportTemplates = array('main' => array('layers' => null), 'print' => array('layers' => array('print')));
     // Specific template? Use that instead of main!
     if (isset($_REQUEST['st']) && isset($reportTemplates[$_REQUEST['st']])) {
         $context['sub_template'] = $_REQUEST['st'];
         // Are we disabling the other layers - print friendly for example?
         if ($reportTemplates[$_REQUEST['st']]['layers'] !== null) {
             $template_layers = Template_Layers::getInstance();
             $template_layers->removeAll();
             foreach ($reportTemplates[$_REQUEST['st']]['layers'] as $layer) {
                 $template_layers->add($layer);
             }
         }
     }
     // Make the page title more descriptive.
     $context['page_title'] .= ' - ' . (isset($txt['gr_type_' . $context['report_type']]) ? $txt['gr_type_' . $context['report_type']] : $context['report_type']);
     // Build the reports button array.
     $context['report_buttons'] = array('generate_reports' => array('text' => 'generate_reports', 'image' => 'print.png', 'lang' => true, 'url' => $scripturl . '?action=admin;area=reports', 'active' => true), 'print' => array('text' => 'print', 'image' => 'print.png', 'lang' => true, 'url' => $scripturl . '?action=admin;area=reports;rt=' . $context['report_type'] . ';st=print', 'custom' => 'target="_blank"'));
     // Allow mods to add additional buttons here
     call_integration_hook('integrate_report_buttons');
     // Now generate the data.
     $this->{$context['report_types'][$context['report_type']]['function']}();
     // Finish the tables before exiting - this is to help the templates a little more.
     finishTables();
 }
 protected static function buildTemplate($notices)
 {
     global $context, $user_info;
     loadTemplate('DismissibleNotices');
     loadLanguage('DismissibleNotices');
     Template_Layers::getInstance()->addAfter('notices', 'body');
     loadJavascriptFile('notify.js', array('defer' => true));
     loadCSSFile('notify.css');
     foreach ($notices as $key => $notice) {
         $notice['body'] = str_replace('{username}', $user_info['name'], $notice['body']);
         $context['notices'][$key] = $notice;
     }
     $context['notices'] = $notices;
 }
 /**
  * Handy shortcut to prepare the "system"
  * @param string $language_file
  */
 private function _prepareLocale($language_file)
 {
     global $modSettings;
     if (!empty($language_file)) {
         loadLanguage($language_file);
     }
     Template_Layers::getInstance()->removeAll();
     // Lets make sure we aren't going to output anything nasty.
     @ob_end_clean();
     if (!empty($modSettings['enableCompressedOutput'])) {
         ob_start('ob_gzhandler');
     } else {
         ob_start();
     }
 }
 public function action_index_api()
 {
     Template_Layers::getInstance()->removeAll();
     loadTemplate('DismissibleNotices');
     loadLanguage('DismissibleNotices');
     if (isset($_GET['edit'])) {
         return $this->action_edit();
     } elseif (isset($_GET['save'])) {
         return $this->action_save();
     } elseif (isset($_GET['new'])) {
         return $this->action_new();
     } elseif (isset($_GET['reset'])) {
         return $this->action_reset();
     }
 }
Exemple #6
0
 /**
  * Turn off/on unread replies subscription for a topic
  * Intended for use in XML or JSON calls
  */
 public function action_unwatchtopic_api()
 {
     global $user_info, $topic, $modSettings, $txt, $context, $scripturl;
     loadTemplate('Xml');
     Template_Layers::getInstance()->removeAll();
     $context['sub_template'] = 'generic_xml_buttons';
     if ($user_info['is_guest']) {
         loadLanguage('Errors');
         $context['xml_data'] = array('error' => 1, 'text' => $txt['not_guests']);
         return;
     }
     // Let's do something only if the function is enabled
     if (empty($modSettings['enable_unwatch'])) {
         loadLanguage('Errors');
         $context['xml_data'] = array('error' => 1, 'text' => $txt['feature_disabled']);
         return;
     }
     if (checkSession('get', '', false)) {
         loadLanguage('Errors');
         $context['xml_data'] = array('error' => 1, 'url' => $scripturl . '?action=unwatchtopic;sa=' . ($_GET['sa'] == 'on' ? 'on' : 'off') . ';topic=' . $topic . '.' . $_REQUEST['start'] . ';' . $context['session_var'] . '=' . $context['session_id']);
         return;
     }
     // our topic functions are here
     require_once SUBSDIR . '/Topic.subs.php';
     setTopicWatch($user_info['id'], $topic, $_GET['sa'] == 'on');
     $context['xml_data'] = array('text' => $_GET['sa'] == 'on' ? $txt['watch'] : $txt['unwatch'], 'url' => $scripturl . '?action=unwatchtopic;topic=' . $context['current_topic'] . '.' . $_REQUEST['start'] . ';sa=' . ($_GET['sa'] == 'on' ? 'off' : 'on') . ';' . $context['session_var'] . '=' . $context['session_id'] . ';api' . (isset($_REQUEST['json']) ? ';json' : ''));
 }
 /**
  * This function shows the board index.
  *
  * What it does:
  * - It uses the BoardIndex template, and main sub template.
  * - It updates the most online statistics.
  * - It is accessed by ?action=boardindex.
  */
 public function action_boardindex()
 {
     global $txt, $user_info, $modSettings, $context, $settings, $scripturl;
     loadTemplate('BoardIndex');
     // Set a canonical URL for this page.
     $context['canonical_url'] = $scripturl;
     Template_Layers::getInstance()->add('boardindex_outer');
     // Do not let search engines index anything if there is a random thing in $_GET.
     if (!empty($_GET)) {
         $context['robot_no_index'] = true;
     }
     // Retrieve the categories and boards.
     require_once SUBSDIR . '/BoardsList.class.php';
     $boardIndexOptions = array('include_categories' => true, 'base_level' => 0, 'parent_id' => 0, 'set_latest_post' => true, 'countChildPosts' => !empty($modSettings['countChildPosts']));
     $boardlist = new Boards_List($boardIndexOptions);
     $context['categories'] = $boardlist->getBoards();
     $context['latest_post'] = $boardlist->getLatestPost();
     // Get the user online list.
     require_once SUBSDIR . '/MembersOnline.subs.php';
     $membersOnlineOptions = array('show_hidden' => allowedTo('moderate_forum'), 'sort' => 'log_time', 'reverse_sort' => true);
     $context += getMembersOnlineStats($membersOnlineOptions);
     $context['show_buddies'] = !empty($user_info['buddies']);
     // Are we showing all membergroups on the board index?
     if (!empty($settings['show_group_key'])) {
         $context['membergroups'] = cache_quick_get('membergroup_list', 'subs/Membergroups.subs.php', 'cache_getMembergroupList', array());
     }
     // Track most online statistics? (subs/Members.subs.phpOnline.php)
     if (!empty($modSettings['trackStats'])) {
         trackStatsUsersOnline($context['num_guests'] + $context['num_users_online']);
     }
     // Retrieve the latest posts if the theme settings require it.
     if (isset($settings['number_recent_posts']) && $settings['number_recent_posts'] > 1) {
         $latestPostOptions = array('number_posts' => $settings['number_recent_posts']);
         $context['latest_posts'] = cache_quick_get('boardindex-latest_posts:' . md5($user_info['query_wanna_see_board'] . $user_info['language']), 'subs/Recent.subs.php', 'cache_getLastPosts', array($latestPostOptions));
     }
     // Let the template know what the members can do if the theme enables these options
     $context['show_stats'] = allowedTo('view_stats') && !empty($modSettings['trackStats']);
     $context['show_member_list'] = allowedTo('view_mlist');
     $context['show_who'] = allowedTo('who_view') && !empty($modSettings['who_enabled']);
     // Load the calendar?
     if (!empty($modSettings['cal_enabled']) && allowedTo('calendar_view')) {
         // Retrieve the calendar data (events, birthdays, holidays).
         $eventOptions = array('include_holidays' => $modSettings['cal_showholidays'] > 1, 'include_birthdays' => $modSettings['cal_showbdays'] > 1, 'include_events' => $modSettings['cal_showevents'] > 1, 'num_days_shown' => empty($modSettings['cal_days_for_index']) || $modSettings['cal_days_for_index'] < 1 ? 1 : $modSettings['cal_days_for_index']);
         $context += cache_quick_get('calendar_index_offset_' . ($user_info['time_offset'] + $modSettings['time_offset']), 'subs/Calendar.subs.php', 'cache_getRecentEvents', array($eventOptions));
         // Whether one or multiple days are shown on the board index.
         $context['calendar_only_today'] = $modSettings['cal_days_for_index'] == 1;
         // This is used to show the "how-do-I-edit" help.
         $context['calendar_can_edit'] = allowedTo('calendar_edit_any');
         $show_calendar = true;
     } else {
         $show_calendar = false;
     }
     $context['page_title'] = sprintf($txt['forum_index'], $context['forum_name']);
     $context['sub_template'] = 'boards_list';
     // Mark read button
     $context['mark_read_button'] = array('markread' => array('text' => 'mark_as_read', 'image' => 'markread.png', 'lang' => true, 'custom' => 'onclick="return markallreadButton(this);"', 'url' => $scripturl . '?action=markasread;sa=all;bi;' . $context['session_var'] . '=' . $context['session_id']));
     $context['info_center_callbacks'] = array();
     if (!empty($settings['number_recent_posts']) && (!empty($context['latest_posts']) || !empty($context['latest_post']))) {
         $context['info_center_callbacks'][] = 'recent_posts';
     }
     if ($show_calendar) {
         $context['info_center_callbacks'][] = 'show_events';
     }
     if (!empty($settings['show_stats_index'])) {
         $context['info_center_callbacks'][] = 'show_stats';
     }
     $context['info_center_callbacks'][] = 'show_users';
     // Allow mods to add additional buttons here
     call_integration_hook('integrate_mark_read_button');
 }
 /**
  * Shows an interface to set and test censored words.
  *
  * - It uses the censor_vulgar, censor_proper, censorWholeWord, and
  * censorIgnoreCase settings.
  * - Requires the admin_forum permission.
  * - Accessed from ?action=admin;area=postsettings;sa=censor.
  *
  * @uses the Admin template and the edit_censored sub template.
  */
 public function action_censor()
 {
     global $txt, $modSettings, $context;
     if (!empty($_POST['save_censor'])) {
         // Make sure censoring is something they can do.
         checkSession();
         validateToken('admin-censor');
         $censored_vulgar = array();
         $censored_proper = array();
         // Rip it apart, then split it into two arrays.
         if (isset($_POST['censortext'])) {
             $_POST['censortext'] = explode("\n", strtr($_POST['censortext'], array("\r" => '')));
             foreach ($_POST['censortext'] as $c) {
                 list($censored_vulgar[], $censored_proper[]) = array_pad(explode('=', trim($c)), 2, '');
             }
         } elseif (isset($_POST['censor_vulgar'], $_POST['censor_proper'])) {
             if (is_array($_POST['censor_vulgar'])) {
                 foreach ($_POST['censor_vulgar'] as $i => $value) {
                     if (trim(strtr($value, '*', ' ')) == '') {
                         unset($_POST['censor_vulgar'][$i], $_POST['censor_proper'][$i]);
                     }
                 }
                 $censored_vulgar = $_POST['censor_vulgar'];
                 $censored_proper = $_POST['censor_proper'];
             } else {
                 $censored_vulgar = explode("\n", strtr($_POST['censor_vulgar'], array("\r" => '')));
                 $censored_proper = explode("\n", strtr($_POST['censor_proper'], array("\r" => '')));
             }
         }
         // Set the new arrays and settings in the database.
         $updates = array('censor_vulgar' => implode("\n", $censored_vulgar), 'censor_proper' => implode("\n", $censored_proper), 'censorWholeWord' => empty($_POST['censorWholeWord']) ? '0' : '1', 'censorIgnoreCase' => empty($_POST['censorIgnoreCase']) ? '0' : '1');
         call_integration_hook('integrate_save_censors', array(&$updates));
         updateSettings($updates);
     }
     // Testing a word to see how it will be censored?
     if (isset($_POST['censortest'])) {
         require_once SUBSDIR . '/Post.subs.php';
         $censorText = htmlspecialchars($_POST['censortest'], ENT_QUOTES, 'UTF-8');
         preparsecode($censorText);
         $pre_censor = $censorText;
         $context['censor_test'] = strtr(censorText($censorText), array('"' => '&quot;'));
     }
     // Set everything up for the template to do its thang.
     $censor_vulgar = explode("\n", $modSettings['censor_vulgar']);
     $censor_proper = explode("\n", $modSettings['censor_proper']);
     $context['censored_words'] = array();
     for ($i = 0, $n = count($censor_vulgar); $i < $n; $i++) {
         if (empty($censor_vulgar[$i])) {
             continue;
         }
         // Skip it, it's either spaces or stars only.
         if (trim(strtr($censor_vulgar[$i], '*', ' ')) == '') {
             continue;
         }
         $context['censored_words'][htmlspecialchars(trim($censor_vulgar[$i]))] = isset($censor_proper[$i]) ? htmlspecialchars($censor_proper[$i], ENT_COMPAT, 'UTF-8') : '';
     }
     call_integration_hook('integrate_censors');
     createToken('admin-censor');
     // Using ajax?
     if (isset($_REQUEST['xml'], $_POST['censortest'])) {
         // Clear the templates
         $template_layers = Template_Layers::getInstance();
         $template_layers->removeAll();
         // Send back a response
         loadTemplate('Json');
         $context['sub_template'] = 'send_json';
         $context['json_data'] = array('result' => true, 'censor' => $pre_censor . ' <i class="fa fa-arrow-circle-right"></i> ' . $context['censor_test'], 'token_val' => $context['admin-censor_token_var'], 'token' => $context['admin-censor_token']);
     } else {
         $context['sub_template'] = 'edit_censored';
         $context['page_title'] = $txt['admin_censored_words'];
     }
 }
 /**
  * Allow the change or view of profiles.
  * Loads the profile menu.
  *
  * @see Action_Controller::action_index()
  */
 public function action_index()
 {
     global $txt, $scripturl, $user_info, $context, $user_profile, $cur_profile;
     global $modSettings, $memberContext, $profile_vars, $post_errors, $user_settings;
     // Don't reload this as we may have processed error strings.
     if (empty($post_errors)) {
         loadLanguage('Profile+Drafts');
     }
     loadTemplate('Profile');
     require_once SUBSDIR . '/Menu.subs.php';
     require_once SUBSDIR . '/Profile.subs.php';
     $memID = currentMemberID();
     $context['id_member'] = $memID;
     $cur_profile = $user_profile[$memID];
     // Let's have some information about this member ready, too.
     loadMemberContext($memID);
     $context['member'] = $memberContext[$memID];
     // Is this the profile of the user himself or herself?
     $context['user']['is_owner'] = $memID == $user_info['id'];
     /**
      * Define all the sections within the profile area!
      * We start by defining the permission required - then we take this and turn
      * it into the relevant context ;)
      *
      * Possible fields:
      *   For Section:
      *    - string $title: Section title.
      *    - array $areas:  Array of areas within this section.
      *
      *   For Areas:
      *    - string $label:      Text string that will be used to show the area in the menu.
      *    - string $file:       Optional text string that may contain a file name that's needed for inclusion in order to display the area properly.
      *    - string $custom_url: Optional href for area.
      *    - string $function:   Function to execute for this section.
      *    - bool $enabled:      Should area be shown?
      *    - string $sc:         Session check validation to do on save - note without this save will get unset - if set.
      *    - bool $hidden:       Does this not actually appear on the menu?
      *    - bool $password:     Whether to require the user's password in order to save the data in the area.
      *    - array $subsections: Array of subsections, in order of appearance.
      *    - array $permission:  Array of permissions to determine who can access this area. Should contain arrays $own and $any.
      */
     $profile_areas = array('info' => array('title' => $txt['profileInfo'], 'areas' => array('summary' => array('label' => $txt['summary'], 'file' => 'ProfileInfo.controller.php', 'controller' => 'ProfileInfo_Controller', 'function' => 'action_summary', 'token' => 'profile-aa%u', 'token_type' => 'get', 'permission' => array('own' => 'profile_view_own', 'any' => 'profile_view_any')), 'statistics' => array('label' => $txt['statPanel'], 'file' => 'ProfileInfo.controller.php', 'controller' => 'ProfileInfo_Controller', 'function' => 'action_statPanel', 'permission' => array('own' => 'profile_view_own', 'any' => 'profile_view_any')), 'showposts' => array('label' => $txt['showPosts'], 'file' => 'ProfileInfo.controller.php', 'controller' => 'ProfileInfo_Controller', 'function' => 'action_showPosts', 'subsections' => array('messages' => array($txt['showMessages'], array('profile_view_own', 'profile_view_any')), 'topics' => array($txt['showTopics'], array('profile_view_own', 'profile_view_any')), 'unwatchedtopics' => array($txt['showUnwatched'], array('profile_view_own', 'profile_view_any'), 'enabled' => $modSettings['enable_unwatch'] && $context['user']['is_owner']), 'attach' => array($txt['showAttachments'], array('profile_view_own', 'profile_view_any'))), 'permission' => array('own' => 'profile_view_own', 'any' => 'profile_view_any')), 'showdrafts' => array('label' => $txt['drafts_show'], 'file' => 'Draft.controller.php', 'controller' => 'Draft_Controller', 'function' => 'action_showProfileDrafts', 'enabled' => !empty($modSettings['drafts_enabled']) && $context['user']['is_owner'], 'permission' => array('own' => 'profile_view_own', 'any' => array())), 'showlikes' => array('label' => $txt['likes_show'], 'file' => 'Likes.controller.php', 'controller' => 'Likes_Controller', 'function' => 'action_showProfileLikes', 'enabled' => !empty($modSettings['likes_enabled']) && $context['user']['is_owner'], 'subsections' => array('given' => array($txt['likes_given'], array('profile_view_own')), 'received' => array($txt['likes_received'], array('profile_view_own'))), 'permission' => array('own' => 'profile_view_own', 'any' => array())), 'permissions' => array('label' => $txt['showPermissions'], 'file' => 'ProfileInfo.controller.php', 'controller' => 'ProfileInfo_Controller', 'function' => 'action_showPermissions', 'permission' => array('own' => 'manage_permissions', 'any' => 'manage_permissions')), 'history' => array('label' => $txt['history'], 'file' => 'ProfileHistory.controller.php', 'controller' => 'ProfileHistory_Controller', 'function' => 'action_index', 'subsections' => array('activity' => array($txt['trackActivity'], 'moderate_forum'), 'ip' => array($txt['trackIP'], 'moderate_forum'), 'edits' => array($txt['trackEdits'], 'moderate_forum'), 'logins' => array($txt['trackLogins'], array('profile_view_own', 'moderate_forum'))), 'permission' => array('own' => 'moderate_forum', 'any' => 'moderate_forum')), 'viewwarning' => array('label' => $txt['profile_view_warnings'], 'enabled' => in_array('w', $context['admin_features']) && !empty($modSettings['warning_enable']) && $cur_profile['warning'] && (!empty($modSettings['warning_show']) && ($context['user']['is_owner'] || $modSettings['warning_show'] == 2)), 'file' => 'ProfileInfo.controller.php', 'controller' => 'ProfileInfo_Controller', 'function' => 'action_viewWarning', 'permission' => array('own' => 'profile_view_own', 'any' => 'issue_warning')))), 'edit_profile' => array('title' => $txt['profileEdit'], 'areas' => array('account' => array('label' => $txt['account'], 'file' => 'ProfileOptions.controller.php', 'controller' => 'ProfileOptions_Controller', 'function' => 'action_account', 'enabled' => $context['user']['is_admin'] || $cur_profile['id_group'] != 1 && !in_array(1, explode(',', $cur_profile['additional_groups'])), 'sc' => 'post', 'token' => 'profile-ac%u', 'password' => true, 'permission' => array('own' => array('profile_identity_any', 'profile_identity_own', 'manage_membergroups'), 'any' => array('profile_identity_any', 'manage_membergroups'))), 'forumprofile' => array('label' => $txt['forumprofile'], 'file' => 'ProfileOptions.controller.php', 'controller' => 'ProfileOptions_Controller', 'function' => 'action_forumProfile', 'sc' => 'post', 'token' => 'profile-fp%u', 'permission' => array('own' => array('profile_extra_any', 'profile_extra_own', 'profile_title_own', 'profile_title_any'), 'any' => array('profile_extra_any', 'profile_title_any'))), 'theme' => array('label' => $txt['theme'], 'file' => 'ProfileOptions.controller.php', 'controller' => 'ProfileOptions_Controller', 'function' => 'action_themepick', 'sc' => 'post', 'token' => 'profile-th%u', 'permission' => array('own' => array('profile_extra_any', 'profile_extra_own'), 'any' => array('profile_extra_any'))), 'authentication' => array('label' => $txt['authentication'], 'file' => 'ProfileOptions.controller.php', 'controller' => 'ProfileOptions_Controller', 'function' => 'action_authentication', 'enabled' => !empty($modSettings['enableOpenID']) || !empty($cur_profile['openid_uri']), 'sc' => 'post', 'token' => 'profile-au%u', 'hidden' => empty($modSettings['enableOpenID']) && empty($cur_profile['openid_uri']), 'password' => true, 'permission' => array('own' => array('profile_identity_any', 'profile_identity_own'), 'any' => array('profile_identity_any'))), 'notification' => array('label' => $txt['notifications'], 'file' => 'ProfileOptions.controller.php', 'controller' => 'ProfileOptions_Controller', 'function' => 'action_notification', 'sc' => 'post', 'token' => 'profile-nt%u', 'permission' => array('own' => array('profile_extra_any', 'profile_extra_own'), 'any' => array('profile_extra_any'))), 'contactprefs' => array('label' => $txt['contactprefs'], 'file' => 'ProfileOptions.controller.php', 'controller' => 'ProfileOptions_Controller', 'function' => 'action_pmprefs', 'enabled' => allowedTo(array('profile_extra_own', 'profile_extra_any')), 'sc' => 'post', 'token' => 'profile-pm%u', 'permission' => array('own' => array('pm_read'), 'any' => array('profile_extra_any'))), 'ignoreboards' => array('label' => $txt['ignoreboards'], 'file' => 'ProfileOptions.controller.php', 'controller' => 'ProfileOptions_Controller', 'function' => 'action_ignoreboards', 'enabled' => !empty($modSettings['allow_ignore_boards']), 'sc' => 'post', 'token' => 'profile-ib%u', 'permission' => array('own' => array('profile_extra_any', 'profile_extra_own'), 'any' => array('profile_extra_any'))), 'lists' => array('label' => $txt['editBuddyIgnoreLists'], 'file' => 'ProfileOptions.controller.php', 'controller' => 'ProfileOptions_Controller', 'function' => 'action_editBuddyIgnoreLists', 'enabled' => !empty($modSettings['enable_buddylist']) && $context['user']['is_owner'], 'sc' => 'post', 'token' => 'profile-bl%u', 'subsections' => array('buddies' => array($txt['editBuddies']), 'ignore' => array($txt['editIgnoreList'])), 'permission' => array('own' => array('profile_extra_any', 'profile_extra_own'), 'any' => array())), 'groupmembership' => array('label' => $txt['groupmembership'], 'file' => 'ProfileOptions.controller.php', 'controller' => 'ProfileOptions_Controller', 'function' => 'action_groupMembership', 'enabled' => !empty($modSettings['show_group_membership']) && $context['user']['is_owner'], 'sc' => 'request', 'token' => 'profile-gm%u', 'token_type' => 'request', 'permission' => array('own' => array('profile_view_own'), 'any' => array('manage_membergroups'))))), 'profile_action' => array('title' => $txt['profileAction'], 'areas' => array('sendpm' => array('label' => $txt['profileSendIm'], 'custom_url' => $scripturl . '?action=pm;sa=send', 'permission' => array('own' => array(), 'any' => array('pm_send'))), 'issuewarning' => array('label' => $txt['profile_issue_warning'], 'enabled' => in_array('w', $context['admin_features']) && !empty($modSettings['warning_enable']) && (!$context['user']['is_owner'] || $context['user']['is_admin']), 'file' => 'ProfileAccount.controller.php', 'controller' => 'ProfileAccount_Controller', 'function' => 'action_issuewarning', 'token' => 'profile-iw%u', 'permission' => array('own' => array(), 'any' => array('issue_warning'))), 'banuser' => array('label' => $txt['profileBanUser'], 'custom_url' => $scripturl . '?action=admin;area=ban;sa=add', 'enabled' => $cur_profile['id_group'] != 1 && !in_array(1, explode(',', $cur_profile['additional_groups'])), 'permission' => array('own' => array(), 'any' => array('manage_bans'))), 'subscriptions' => array('label' => $txt['subscriptions'], 'file' => 'ProfileSubscriptions.controller.php', 'controller' => 'ProfileSubscriptions_Controller', 'function' => 'action_subscriptions', 'enabled' => !empty($modSettings['paid_enabled']), 'permission' => array('own' => array('profile_view_own'), 'any' => array('moderate_forum'))), 'deleteaccount' => array('label' => $txt['deleteAccount'], 'file' => 'ProfileAccount.controller.php', 'controller' => 'ProfileAccount_Controller', 'function' => 'action_deleteaccount', 'sc' => 'post', 'token' => 'profile-da%u', 'password' => true, 'permission' => array('own' => array('profile_remove_any', 'profile_remove_own'), 'any' => array('profile_remove_any'))), 'activateaccount' => array('file' => 'ProfileAccount.controller.php', 'controller' => 'ProfileAccount_Controller', 'function' => 'action_activateaccount', 'sc' => 'get', 'token' => 'profile-aa%u', 'permission' => array('own' => array(), 'any' => array('moderate_forum'))))));
     // Is there an updated message to show?
     if (isset($_GET['updated'])) {
         $context['profile_updated'] = $txt['profile_updated_own'];
     }
     // Set a few options for the menu.
     $menuOptions = array('disable_url_session_check' => true, 'hook' => 'profile', 'extra_url_parameters' => array('u' => $context['id_member']), 'default_include_dir' => CONTROLLERDIR);
     // Actually create the menu!
     $profile_include_data = createMenu($profile_areas, $menuOptions);
     unset($profile_areas);
     // If it said no permissions that meant it wasn't valid!
     if ($profile_include_data && empty($profile_include_data['permission'])) {
         $profile_include_data['enabled'] = false;
     }
     // No menu and guest? A warm welcome to register
     if (!$profile_include_data && $user_info['is_guest']) {
         is_not_guest();
     }
     // No menu means no access.
     if (!$profile_include_data || isset($profile_include_data['enabled']) && $profile_include_data['enabled'] === false) {
         fatal_lang_error('no_access', false);
     }
     // Make a note of the Unique ID for this menu.
     $context['profile_menu_id'] = $context['max_menu_id'];
     $context['profile_menu_name'] = 'menu_data_' . $context['profile_menu_id'];
     // Set the selected item - now it's been validated.
     $current_area = $profile_include_data['current_area'];
     $context['menu_item_selected'] = $current_area;
     // Before we go any further, let's work on the area we've said is valid.
     // Note this is done here just in case we ever compromise the menu function in error!
     $this->_completed_save = false;
     $context['do_preview'] = isset($_REQUEST['preview_signature']);
     // Are we saving data in a valid area?
     if (isset($profile_include_data['sc']) && (isset($_REQUEST['save']) || $context['do_preview'])) {
         checkSession($profile_include_data['sc']);
         $this->_completed_save = true;
     }
     // Does this require session validating?
     if (!empty($area['validate']) || isset($_REQUEST['save']) && !$context['user']['is_owner']) {
         validateSession();
     }
     // Do we need to perform a token check?
     if (!empty($profile_include_data['token'])) {
         if ($profile_include_data['token'] !== true) {
             $token_name = str_replace('%u', $context['id_member'], $profile_include_data['token']);
         } else {
             $token_name = 'profile-u' . $context['id_member'];
         }
         if (isset($profile_include_data['token_type']) && in_array($profile_include_data['token_type'], array('request', 'post', 'get'))) {
             $token_type = $profile_include_data['token_type'];
         } else {
             $token_type = 'post';
         }
         if (isset($_REQUEST['save'])) {
             validateToken($token_name, $token_type);
         }
     }
     // Permissions for good measure.
     if (!empty($profile_include_data['permission'])) {
         isAllowedTo($profile_include_data['permission'][$context['user']['is_owner'] ? 'own' : 'any']);
     }
     // Create a token if needed.
     if (!empty($profile_include_data['token'])) {
         createToken($token_name, $token_type);
         $context['token_check'] = $token_name;
     }
     // Build the link tree.
     $context['linktree'][] = array('url' => $scripturl . '?action=profile' . ($memID != $user_info['id'] ? ';u=' . $memID : ''), 'name' => sprintf($txt['profile_of_username'], $context['member']['name']));
     if (!empty($profile_include_data['label'])) {
         $context['linktree'][] = array('url' => $scripturl . '?action=profile' . ($memID != $user_info['id'] ? ';u=' . $memID : '') . ';area=' . $profile_include_data['current_area'], 'name' => $profile_include_data['label']);
     }
     if (!empty($profile_include_data['current_subsection']) && $profile_include_data['subsections'][$profile_include_data['current_subsection']][0] != $profile_include_data['label']) {
         $context['linktree'][] = array('url' => $scripturl . '?action=profile' . ($memID != $user_info['id'] ? ';u=' . $memID : '') . ';area=' . $profile_include_data['current_area'] . ';sa=' . $profile_include_data['current_subsection'], 'name' => $profile_include_data['subsections'][$profile_include_data['current_subsection']][0]);
     }
     // Set the template for this area... if you still can :P
     // and add the profile layer.
     $context['sub_template'] = $profile_include_data['function'];
     Template_Layers::getInstance()->add('profile');
     loadJavascriptFile('profile.js');
     // All the subactions that require a user password in order to validate.
     $check_password = $context['user']['is_owner'] && !empty($profile_include_data['password']);
     $context['require_password'] = $check_password && empty($user_settings['openid_uri']);
     // These will get populated soon!
     $post_errors = array();
     $profile_vars = array();
     // Right - are we saving - if so let's save the old data first.
     if ($this->_completed_save) {
         // Clean up the POST variables.
         $_POST = htmltrim__recursive($_POST);
         $_POST = htmlspecialchars__recursive($_POST);
         if ($check_password) {
             // If we're using OpenID try to revalidate.
             if (!empty($user_settings['openid_uri'])) {
                 require_once SUBSDIR . '/OpenID.subs.php';
                 $openID = new OpenID();
                 $openID->revalidate();
             } else {
                 // You didn't even enter a password!
                 if (trim($_POST['oldpasswrd']) == '') {
                     $post_errors[] = 'no_password';
                 }
                 // Since the password got modified due to all the $_POST cleaning, lets undo it so we can get the correct password
                 $_POST['oldpasswrd'] = un_htmlspecialchars($_POST['oldpasswrd']);
                 // Does the integration want to check passwords?
                 $good_password = in_array(true, call_integration_hook('integrate_verify_password', array($cur_profile['member_name'], $_POST['oldpasswrd'], false)), true);
                 // Start up the password checker, we have work to do
                 require_once SUBSDIR . '/Auth.subs.php';
                 // Bad password!!!
                 if (!$good_password && !validateLoginPassword($_POST['oldpasswrd'], $user_info['passwd'], $user_profile[$memID]['member_name'])) {
                     $post_errors[] = 'bad_password';
                 }
                 // Warn other elements not to jump the gun and do custom changes!
                 if (in_array('bad_password', $post_errors)) {
                     $context['password_auth_failed'] = true;
                 }
             }
         }
         // Change the IP address in the database.
         if ($context['user']['is_owner']) {
             $profile_vars['member_ip'] = $user_info['ip'];
         }
         // Now call the sub-action function...
         if ($current_area == 'activateaccount') {
             if (empty($post_errors)) {
                 require_once CONTROLLERDIR . '/ProfileAccount.controller.php';
                 $controller = new ProfileAccount_Controller();
                 $controller->action_activateaccount();
             }
         } elseif ($current_area == 'deleteaccount') {
             if (empty($post_errors)) {
                 require_once CONTROLLERDIR . '/ProfileAccount.controller.php';
                 $controller = new ProfileAccount_Controller();
                 $controller->action_deleteaccount2();
                 redirectexit();
             }
         } elseif ($current_area == 'groupmembership' && empty($post_errors)) {
             require_once CONTROLLERDIR . '/ProfileOptions.controller.php';
             $controller = new Profileoptions_Controller();
             $msg = $controller->action_groupMembership2();
             // Whatever we've done, we have nothing else to do here...
             redirectexit('action=profile' . ($context['user']['is_owner'] ? '' : ';u=' . $memID) . ';area=groupmembership' . (!empty($msg) ? ';msg=' . $msg : ''));
         } elseif ($current_area == 'authentication') {
             require_once CONTROLLERDIR . '/ProfileOptions.controller.php';
             $controller = new ProfileOptions_Controller();
             $controller->action_authentication(true);
         } elseif (in_array($current_area, array('account', 'forumprofile', 'theme', 'contactprefs'))) {
             saveProfileFields();
         } else {
             $force_redirect = true;
             saveProfileChanges($profile_vars, $memID);
         }
         call_integration_hook('integrate_profile_save', array(&$profile_vars, &$post_errors, $memID));
         // There was a problem, let them try to re-enter.
         if (!empty($post_errors)) {
             // Load the language file so we can give a nice explanation of the errors.
             loadLanguage('Errors');
             $context['post_errors'] = $post_errors;
         } elseif (!empty($profile_vars)) {
             // If we've changed the password, notify any integration that may be listening in.
             if (isset($profile_vars['passwd'])) {
                 call_integration_hook('integrate_reset_pass', array($cur_profile['member_name'], $cur_profile['member_name'], $_POST['passwrd2']));
             }
             updateMemberData($memID, $profile_vars);
             // What if this is the newest member?
             if ($modSettings['latestMember'] == $memID) {
                 updateStats('member');
             } elseif (isset($profile_vars['real_name'])) {
                 updateSettings(array('memberlist_updated' => time()));
             }
             // If the member changed his/her birthdate, update calendar statistics.
             if (isset($profile_vars['birthdate']) || isset($profile_vars['real_name'])) {
                 updateSettings(array('calendar_updated' => time()));
             }
             // Anything worth logging?
             if (!empty($context['log_changes']) && !empty($modSettings['modlog_enabled'])) {
                 $log_changes = array();
                 foreach ($context['log_changes'] as $k => $v) {
                     $log_changes[] = array('action' => $k, 'log_type' => 'user', 'extra' => array_merge($v, array('applicator' => $user_info['id'], 'member_affected' => $memID)));
                 }
                 logActions($log_changes);
             }
             // Have we got any post save functions to execute?
             if (!empty($context['profile_execute_on_save'])) {
                 foreach ($context['profile_execute_on_save'] as $saveFunc) {
                     $saveFunc();
                 }
             }
             // Let them know it worked!
             $context['profile_updated'] = $context['user']['is_owner'] ? $txt['profile_updated_own'] : sprintf($txt['profile_updated_else'], $cur_profile['member_name']);
             // Invalidate any cached data.
             cache_put_data('member_data-profile-' . $memID, null, 0);
         }
     }
     // Have some errors for some reason?
     if (!empty($post_errors)) {
         // Set all the errors so the template knows what went wrong.
         foreach ($post_errors as $error_type) {
             $context['modify_error'][$error_type] = true;
         }
     } elseif (!empty($profile_vars) && $context['user']['is_owner'] && !$context['do_preview']) {
         redirectexit('action=profile;area=' . $current_area . ';updated');
     } elseif (!empty($force_redirect)) {
         redirectexit('action=profile' . ($context['user']['is_owner'] ? '' : ';u=' . $memID) . ';area=' . $current_area);
     }
     // Let go to the right place
     if (isset($profile_include_data['file'])) {
         require_once $profile_include_data['file'];
     }
     callMenu($profile_include_data);
     // Set the page title if it's not already set...
     if (!isset($context['page_title'])) {
         $context['page_title'] = $txt['profile'] . (isset($txt[$current_area]) ? ' - ' . $txt[$current_area] : '');
     }
 }
 /**
  * Show the verification code or let it hear.
  * Accessed by ?action=verificationcode
  */
 public function action_verificationcode()
 {
     global $context, $scripturl;
     $verification_id = isset($_GET['vid']) ? $_GET['vid'] : '';
     $code = $verification_id && isset($_SESSION[$verification_id . '_vv']) ? $_SESSION[$verification_id . '_vv']['code'] : (isset($_SESSION['visual_verification_code']) ? $_SESSION['visual_verification_code'] : '');
     // Somehow no code was generated or the session was lost.
     if (empty($code)) {
         header('Content-Type: image/gif');
         die("GIF89a€!ù,D;");
     } elseif (isset($_REQUEST['sound'])) {
         loadLanguage('Login');
         loadTemplate('Register');
         $context['verification_sound_href'] = $scripturl . '?action=verificationcode;rand=' . md5(mt_rand()) . ($verification_id ? ';vid=' . $verification_id : '') . ';format=.wav';
         $context['sub_template'] = 'verification_sound';
         Template_Layers::getInstance()->removeAll();
         obExit();
     } elseif (empty($_REQUEST['format'])) {
         require_once SUBSDIR . '/Graphics.subs.php';
         if (in_array('gd', get_loaded_extensions()) && !showCodeImage($code)) {
             header('HTTP/1.1 400 Bad Request');
         } elseif (isset($_REQUEST['letter'])) {
             $_REQUEST['letter'] = (int) $_REQUEST['letter'];
             if ($_REQUEST['letter'] > 0 && $_REQUEST['letter'] <= strlen($code) && !showLetterImage(strtolower($code[$_REQUEST['letter'] - 1]))) {
                 header('Content-Type: image/gif');
                 die("GIF89a€!ù,D;");
             }
         } else {
             header('Content-Type: image/gif');
             die("GIF89a€!ù,D;");
         }
     } elseif ($_REQUEST['format'] === '.wav') {
         require_once SUBSDIR . '/Sound.subs.php';
         if (!createWaveFile($code)) {
             header('HTTP/1.1 400 Bad Request');
         }
     }
     // We all die one day...
     die;
 }
 /**
  * Find and return Template_Layers instance if it exists,
  * or create a new instance if it didn't already exist.
  *
  * @param boolean $error_safe if error mode is on or off
  * @return Template_Layers instance of the class
  */
 public static function getInstance($error_safe = false)
 {
     if (self::$_instance === null) {
         self::$_instance = new Template_Layers();
     }
     self::$_error_safe = $error_safe;
     return self::$_instance;
 }
Exemple #12
0
/**
 * Do some important security checks:
 *
 * What it does:
 * - checks the existence of critical files e.g. install.php
 * - checks for an active admin session.
 * - checks cache directory is writable.
 * - calls secureDirectory to protect attachments & cache.
 * - checks if the forum is in maintance mode.
 */
function doSecurityChecks()
{
    global $modSettings, $context, $maintenance, $user_info, $txt, $scripturl, $user_settings, $options;
    $show_warnings = false;
    if (allowedTo('admin_forum') && !$user_info['is_guest']) {
        // If agreement is enabled, at least the english version shall exists
        if ($modSettings['requireAgreement'] && !file_exists(BOARDDIR . '/agreement.txt')) {
            $context['security_controls_files']['title'] = $txt['generic_warning'];
            $context['security_controls_files']['errors']['agreement'] = $txt['agreement_missing'];
            $show_warnings = true;
        }
        // Cache directory writeable?
        if (!empty($modSettings['cache_enable']) && !is_writable(CACHEDIR)) {
            $context['security_controls_files']['title'] = $txt['generic_warning'];
            $context['security_controls_files']['errors']['cache'] = $txt['cache_writable'];
            $show_warnings = true;
        }
        // @todo add a hook here
        $securityFiles = array('install.php', 'upgrade.php', 'convert.php', 'repair_paths.php', 'repair_settings.php', 'Settings.php~', 'Settings_bak.php~');
        foreach ($securityFiles as $securityFile) {
            if (file_exists(BOARDDIR . '/' . $securityFile)) {
                $context['security_controls_files']['title'] = $txt['security_risk'];
                $context['security_controls_files']['errors'][$securityFile] = sprintf($txt['not_removed'], $securityFile);
                $show_warnings = true;
                if ($securityFile == 'Settings.php~' || $securityFile == 'Settings_bak.php~') {
                    $context['security_controls_files']['errors'][$securityFile] .= '<span class="smalltext">' . sprintf($txt['not_removed_extra'], $securityFile, substr($securityFile, 0, -1)) . '</span>';
                }
            }
        }
        // We are already checking so many files...just few more doesn't make any difference! :P
        require_once SUBSDIR . '/Attachments.subs.php';
        $path = getAttachmentPath();
        secureDirectory($path, true);
        secureDirectory(CACHEDIR);
        // Active admin session?
        if (empty($modSettings['securityDisable']) && (isset($_SESSION['admin_time']) && $_SESSION['admin_time'] + $modSettings['admin_session_lifetime'] * 60 > time())) {
            $context['warning_controls']['admin_session'] = sprintf($txt['admin_session_active'], $scripturl . '?action=admin;area=adminlogoff;redir;' . $context['session_var'] . '=' . $context['session_id']);
        }
        // Maintenance mode enabled?
        if (!empty($maintenance)) {
            $context['warning_controls']['maintenance'] = sprintf($txt['admin_maintenance_active'], $scripturl . '?action=admin;area=serversettings;' . $context['session_var'] . '=' . $context['session_id']);
        }
        // New updates
        if (defined('FORUM_VERSION')) {
            $index = 'new_in_' . str_replace(array('ElkArte ', '.'), array('', '_'), FORUM_VERSION);
            if (!empty($modSettings[$index]) && empty($options['dismissed_' . $index])) {
                $show_warnings = true;
                $context['new_version_updates'] = array('title' => $txt['new_version_updates'], 'errors' => array(replaceBasicActionUrl($txt['new_version_updates_text'])));
            }
        }
    }
    // Check for database errors.
    if (!empty($_SESSION['query_command_denied'])) {
        if ($user_info['is_admin']) {
            $context['security_controls_query']['title'] = $txt['query_command_denied'];
            $show_warnings = true;
            foreach ($_SESSION['query_command_denied'] as $command => $error) {
                $context['security_controls_query']['errors'][$command] = '<pre>' . Util::htmlspecialchars($error) . '</pre>';
            }
        } else {
            $context['security_controls_query']['title'] = $txt['query_command_denied_guests'];
            foreach ($_SESSION['query_command_denied'] as $command => $error) {
                $context['security_controls_query']['errors'][$command] = '<pre>' . sprintf($txt['query_command_denied_guests_msg'], Util::htmlspecialchars($command)) . '</pre>';
            }
        }
    }
    // Are there any members waiting for approval?
    if (allowedTo('moderate_forum') && (!empty($modSettings['registration_method']) && $modSettings['registration_method'] == 2 || !empty($modSettings['approveAccountDeletion'])) && !empty($modSettings['unapprovedMembers'])) {
        $context['warning_controls']['unapproved_members'] = sprintf($txt[$modSettings['unapprovedMembers'] == 1 ? 'approve_one_member_waiting' : 'approve_many_members_waiting'], $scripturl . '?action=admin;area=viewmembers;sa=browse;type=approve', $modSettings['unapprovedMembers']);
    }
    if (!empty($context['open_mod_reports']) && (empty($user_settings['mod_prefs']) || $user_settings['mod_prefs'][0] == 1)) {
        $context['warning_controls']['open_mod_reports'] = '<a href="' . $scripturl . '?action=moderate;area=reports">' . sprintf($txt['mod_reports_waiting'], $context['open_mod_reports']) . '</a>';
    }
    if (isset($_SESSION['ban']['cannot_post'])) {
        // An admin cannot be banned (technically he could), and if it is better he knows.
        $context['security_controls_ban']['title'] = sprintf($txt['you_are_post_banned'], $user_info['is_guest'] ? $txt['guest_title'] : $user_info['name']);
        $show_warnings = true;
        $context['security_controls_ban']['errors']['reason'] = '';
        if (!empty($_SESSION['ban']['cannot_post']['reason'])) {
            $context['security_controls_ban']['errors']['reason'] = $_SESSION['ban']['cannot_post']['reason'];
        }
        if (!empty($_SESSION['ban']['expire_time'])) {
            $context['security_controls_ban']['errors']['reason'] .= '<span class="smalltext">' . sprintf($txt['your_ban_expires'], standardTime($_SESSION['ban']['expire_time'], false)) . '</span>';
        } else {
            $context['security_controls_ban']['errors']['reason'] .= '<span class="smalltext">' . $txt['your_ban_expires_never'] . '</span>';
        }
    }
    // Finally, let's show the layer.
    if ($show_warnings || !empty($context['warning_controls'])) {
        Template_Layers::getInstance()->addAfter('admin_warning', 'body');
    }
}
Exemple #13
0
    header('Content-Type: text/html; charset=UTF-8');
}
// Take care of any banning that needs to be done.
if (isset($_REQUEST['ssi_ban']) || isset($ssi_ban) && $ssi_ban === true) {
    is_not_banned();
}
// Do we allow guests in here?
if (empty($ssi_guest_access) && empty($modSettings['allow_guestAccess']) && $user_info['is_guest'] && basename($_SERVER['PHP_SELF']) != 'SSI.php') {
    require_once CONTROLLERDIR . '/Auth.controller.php';
    $controller = new Auth_Controller();
    $controller->action_kickguest();
    obExit(null, true);
}
// Load the stuff like the menu bar, etc.
if (isset($ssi_layers)) {
    $template_layers = Template_Layers::getInstance();
    $template_layers->removeAll();
    foreach ($ssi_layers as $layer) {
        $template_layers->addBegin($layer);
    }
    template_header();
} else {
    setupThemeContext();
}
// We need to set up user agent, and make more checks on the request
$req = request();
// Make sure they didn't muss around with the settings... but only if it's not cli.
if (isset($_SERVER['REMOTE_ADDR']) && session_id() == '') {
    trigger_error($txt['ssi_session_broken'], E_USER_NOTICE);
}
// Without visiting the forum this session variable might not be set on submit.
 /**
  * The Shoutbox ... allows for the adding, editing, deleting and viewing of shouts
  */
 public function action_sportal_shoutbox()
 {
     global $context, $scripturl, $user_info;
     // ID of the shoutbox we are working on and timestamp
     $shoutbox_id = !empty($_REQUEST['shoutbox_id']) ? (int) $_REQUEST['shoutbox_id'] : 0;
     $request_time = !empty($_REQUEST['time']) ? (int) $_REQUEST['time'] : 0;
     // We need to know which shoutbox this is for/from
     $context['SPortal']['shoutbox'] = sportal_get_shoutbox($shoutbox_id, true, true);
     if (empty($context['SPortal']['shoutbox'])) {
         if (isset($_REQUEST['xml'])) {
             obExit(false, false);
         } else {
             fatal_lang_error('error_sp_shoutbox_not_exist', false);
         }
     }
     // Any warning title for the shoutbox, like Not For Support ;P
     $context['SPortal']['shoutbox']['warning'] = parse_bbc($context['SPortal']['shoutbox']['warning']);
     $can_moderate = allowedTo('sp_admin') || allowedTo('sp_manage_shoutbox');
     if (!$can_moderate && !empty($context['SPortal']['shoutbox']['moderator_groups'])) {
         $can_moderate = count(array_intersect($user_info['groups'], $context['SPortal']['shoutbox']['moderator_groups'])) > 0;
     }
     // Adding a shout
     if (!empty($_REQUEST['shout'])) {
         // Pretty basic
         is_not_guest();
         checkSession('request');
         // If you are not flooding the system, add the shout to the box
         if (!($flood = sp_prevent_flood('spsbp', false))) {
             require_once SUBSDIR . '/Post.subs.php';
             $_REQUEST['shout'] = Util::htmlspecialchars(trim($_REQUEST['shout']));
             preparsecode($_REQUEST['shout']);
             if (!empty($_REQUEST['shout'])) {
                 sportal_create_shout($context['SPortal']['shoutbox'], $_REQUEST['shout']);
             }
         } else {
             $context['SPortal']['shoutbox']['warning'] = $flood;
         }
     }
     // Removing a shout, regret saying that do you :P
     if (!empty($_REQUEST['delete'])) {
         checkSession('request');
         if (!$can_moderate) {
             fatal_lang_error('error_sp_cannot_shoutbox_moderate', false);
         }
         $delete = (int) $_REQUEST['delete'];
         if (!empty($delete)) {
             sportal_delete_shout($shoutbox_id, $delete);
         }
     }
     // Responding to an ajax request
     if (isset($_REQUEST['xml'])) {
         $shout_parameters = array('limit' => $context['SPortal']['shoutbox']['num_show'], 'bbc' => $context['SPortal']['shoutbox']['allowed_bbc'], 'reverse' => $context['SPortal']['shoutbox']['reverse'], 'cache' => $context['SPortal']['shoutbox']['caching'], 'can_moderate' => $can_moderate);
         // Get all the shouts for this box
         $context['SPortal']['shouts'] = sportal_get_shouts($shoutbox_id, $shout_parameters);
         // Return a clean xml response
         Template_Layers::getInstance()->removeAll();
         $context['sub_template'] = 'shoutbox_xml';
         $context['SPortal']['updated'] = empty($context['SPortal']['shoutbox']['last_update']) || $context['SPortal']['shoutbox']['last_update'] > $request_time;
         return;
     }
     // Show all the shouts in this box
     $total_shouts = sportal_get_shoutbox_count($shoutbox_id);
     $context['per_page'] = $context['SPortal']['shoutbox']['num_show'];
     $context['start'] = !empty($_REQUEST['start']) ? (int) $_REQUEST['start'] : 0;
     $context['page_index'] = constructPageIndex($scripturl . '?action=shoutbox;shoutbox_id=' . $shoutbox_id, $context['start'], $total_shouts, $context['per_page']);
     $shout_parameters = array('start' => $context['start'], 'limit' => $context['per_page'], 'bbc' => $context['SPortal']['shoutbox']['allowed_bbc'], 'cache' => $context['SPortal']['shoutbox']['caching'], 'can_moderate' => $can_moderate);
     $context['SPortal']['shouts_history'] = sportal_get_shouts($shoutbox_id, $shout_parameters);
     $context['SPortal']['shoutbox_id'] = $shoutbox_id;
     $context['sub_template'] = 'shoutbox_all';
     $context['page_title'] = $context['SPortal']['shoutbox']['name'];
 }
Exemple #15
0
 /**
  * Format a topic to be printer friendly.
  * Must be called with a topic specified.
  * Accessed via ?action=topic;sa=printpage.
  *
  * @uses Printpage template, main sub-template.
  * @uses print_above/print_below later without the main layer.
  */
 public function action_printpage()
 {
     global $topic, $scripturl, $context, $user_info, $board_info, $modSettings;
     // Redirect to the boardindex if no valid topic id is provided.
     if (empty($topic)) {
         redirectexit();
     }
     $template_layers = Template_Layers::getInstance();
     $template_layers->removeAll();
     if (!empty($modSettings['disable_print_topic'])) {
         unset($_REQUEST['action']);
         $context['theme_loaded'] = false;
         fatal_lang_error('feature_disabled', false);
     }
     require_once SUBSDIR . '/Topic.subs.php';
     // Get the topic starter information.
     $topicinfo = getTopicInfo($topic, 'starter');
     $context['user']['started'] = $user_info['id'] == $topicinfo['id_member'] && !$user_info['is_guest'];
     // Whatever happens don't index this.
     $context['robot_no_index'] = true;
     // Redirect to the boardindex if no valid topic id is provided.
     if (empty($topicinfo)) {
         redirectexit();
     }
     // @todo this code is almost the same as the one in Display.controller.php
     if ($topicinfo['id_poll'] > 0 && !empty($modSettings['pollMode']) && allowedTo('poll_view')) {
         loadLanguage('Post');
         require_once SUBSDIR . '/Poll.subs.php';
         loadPollContext($topicinfo['id_poll']);
         $template_layers->addAfter('print_poll', 'print');
     }
     // Lets "output" all that info.
     loadTemplate('Printpage');
     $template_layers->add('print');
     $context['sub_template'] = 'print_page';
     $context['board_name'] = $board_info['name'];
     $context['category_name'] = $board_info['cat']['name'];
     $context['poster_name'] = $topicinfo['poster_name'];
     $context['post_time'] = standardTime($topicinfo['poster_time'], false);
     $context['parent_boards'] = array();
     foreach ($board_info['parent_boards'] as $parent) {
         $context['parent_boards'][] = $parent['name'];
     }
     // Split the topics up so we can print them.
     $context['posts'] = topicMessages($topic);
     $posts_id = array_keys($context['posts']);
     if (!isset($context['topic_subject'])) {
         $context['topic_subject'] = $context['posts'][min($posts_id)]['subject'];
     }
     // Fetch attachments so we can print them if asked, enabled and allowed
     if (isset($_REQUEST['images']) && !empty($modSettings['attachmentEnable']) && allowedTo('view_attachments')) {
         require_once SUBSDIR . '/Topic.subs.php';
         $context['printattach'] = messagesAttachments(array_keys($context['posts']));
         $context['viewing_attach'] = true;
     }
     // Set a canonical URL for this page.
     $context['canonical_url'] = $scripturl . '?topic=' . $topic . '.0';
     $context['view_attach_mode'] = array('text' => $scripturl . '?action=topic;sa=printpage;topic=' . $topic . '.0', 'images' => $scripturl . '?action=topic;sa=printpage;topic=' . $topic . '.0;images');
 }
Exemple #16
0
 /**
  * The only reason of this function is to build the poll UI and send it back in an XML form
  */
 public function action_interface()
 {
     global $context, $board, $db_show_debug;
     loadTemplate('Poll');
     loadLanguage('Post');
     Template_Layers::getInstance()->removeAll();
     if (isset($db_show_debug)) {
         $db_show_debug = false;
     }
     $context['sub_template'] = 'poll_edit';
     require_once SUBSDIR . '/Members.subs.php';
     $allowedVoteGroups = groupsAllowedTo('poll_vote', $board);
     // Set up the poll options.
     $context['poll'] = array('max_votes' => 1, 'hide_results' => 0, 'expiration' => '', 'change_vote' => false, 'guest_vote' => false, 'guest_vote_allowed' => in_array(-1, $allowedVoteGroups['allowed']));
     $context['can_moderate_poll'] = true;
     // Make all five poll choices empty.
     $context['choices'] = array(array('id' => 0, 'number' => 1, 'label' => '', 'is_last' => false), array('id' => 1, 'number' => 2, 'label' => '', 'is_last' => false), array('id' => 2, 'number' => 3, 'label' => '', 'is_last' => false), array('id' => 3, 'number' => 4, 'label' => '', 'is_last' => false), array('id' => 4, 'number' => 5, 'label' => '', 'is_last' => true));
     $context['last_choice_id'] = 4;
 }
 /**
  * Sets up the context for showing a listing of registered members.
  * For the handlers in this file, it requires the view_mlist permission.
  * Accessed by ?action_memberlist.
  * @uses Memberlist template, main sub-template.
  *
  * @see Action_Controller::action_index()
  */
 public function action_index()
 {
     global $scripturl, $txt, $modSettings, $context;
     // Make sure they can view the memberlist.
     isAllowedTo('view_mlist');
     loadTemplate('Memberlist');
     $context['sub_template'] = 'memberlist';
     Template_Layers::getInstance()->add('mlsearch');
     $context['listing_by'] = !empty($_GET['sa']) ? $_GET['sa'] : 'all';
     // $subActions array format:
     // 'subaction' => array('label', 'function', 'is_selected')
     $subActions = array('all' => array($txt['view_all_members'], 'action_mlall', $context['listing_by'] == 'all'), 'search' => array($txt['mlist_search'], 'action_mlsearch', $context['listing_by'] == 'search'));
     // Set up the sort links.
     $context['sort_links'] = array();
     foreach ($subActions as $act => $text) {
         $context['sort_links'][] = array('label' => $text[0], 'action' => $act, 'selected' => $text[2]);
     }
     $context['num_members'] = $modSettings['totalMembers'];
     // Set up the standard columns...
     $context['columns'] = array('online' => array('label' => $txt['status'], 'class' => 'status', 'sort' => array('down' => allowedTo('moderate_forum') ? 'IFNULL(lo.log_time, 1) ASC, real_name ASC' : 'CASE WHEN mem.show_online THEN IFNULL(lo.log_time, 1) ELSE 1 END ASC, real_name ASC', 'up' => allowedTo('moderate_forum') ? 'IFNULL(lo.log_time, 1) DESC, real_name DESC' : 'CASE WHEN mem.show_online THEN IFNULL(lo.log_time, 1) ELSE 1 END DESC, real_name DESC')), 'real_name' => array('label' => $txt['username'], 'class' => 'username', 'sort' => array('down' => 'mem.real_name DESC', 'up' => 'mem.real_name ASC')), 'email_address' => array('label' => $txt['email'], 'class' => 'email', 'sort' => array('down' => allowedTo('moderate_forum') ? 'mem.email_address DESC' : 'mem.hide_email DESC, mem.email_address DESC', 'up' => allowedTo('moderate_forum') ? 'mem.email_address ASC' : 'mem.hide_email ASC, mem.email_address ASC')), 'website_url' => array('label' => $txt['website'], 'class' => 'website', 'link_with' => 'website', 'sort' => array('down' => 'LENGTH(mem.website_url) > 0 ASC, IFNULL(mem.website_url, 1=1) DESC, mem.website_url DESC', 'up' => 'LENGTH(mem.website_url) > 0 DESC, IFNULL(mem.website_url, 1=1) ASC, mem.website_url ASC')), 'id_group' => array('label' => $txt['position'], 'class' => 'group', 'sort' => array('down' => 'IFNULL(mg.group_name, 1=1) DESC, mg.group_name DESC', 'up' => 'IFNULL(mg.group_name, 1=1) ASC, mg.group_name ASC')), 'date_registered' => array('label' => $txt['date_registered'], 'class' => 'date_registered', 'sort' => array('down' => 'mem.date_registered DESC', 'up' => 'mem.date_registered ASC')), 'posts' => array('label' => $txt['posts'], 'class' => 'posts', 'default_sort_rev' => true, 'sort' => array('down' => 'mem.posts DESC', 'up' => 'mem.posts ASC')));
     // Add in any custom profile columns
     require_once SUBSDIR . '/Memberlist.subs.php';
     if (ml_CustomProfile()) {
         $context['columns'] += $context['custom_profile_fields']['columns'];
     }
     // The template may appreciate how many columns it needs to display
     $context['colspan'] = 0;
     $context['disabled_fields'] = isset($modSettings['disabled_profile_fields']) ? array_flip(explode(',', $modSettings['disabled_profile_fields'])) : array();
     foreach ($context['columns'] as $key => $column) {
         if (isset($context['disabled_fields'][$key]) || isset($column['link_with']) && isset($context['disabled_fields'][$column['link_with']])) {
             unset($context['columns'][$key]);
             continue;
         }
         $context['colspan'] += isset($column['colspan']) ? $column['colspan'] : 1;
     }
     $context['linktree'][] = array('url' => $scripturl . '?action=memberlist', 'name' => $txt['members_list']);
     $context['can_send_pm'] = allowedTo('pm_send');
     // Build the memberlist button array.
     $context['memberlist_buttons'] = array('view_all_members' => array('text' => 'view_all_members', 'image' => 'mlist.png', 'lang' => true, 'url' => $scripturl . '?action=memberlist;sa=all', 'active' => true));
     // Are there custom fields they can search?
     ml_findSearchableCustomFields();
     // These are all the possible fields.
     $context['search_fields'] = array('name' => $txt['mlist_search_name'], 'email' => $txt['mlist_search_email'], 'website' => $txt['mlist_search_website'], 'group' => $txt['mlist_search_group']);
     foreach ($context['custom_search_fields'] as $field) {
         $context['search_fields']['cust_' . $field['colname']] = sprintf($txt['mlist_search_by'], $field['name']);
     }
     // What do we search for by default?
     $context['search_defaults'] = array('name', 'email');
     // Allow mods to add additional buttons here
     call_integration_hook('integrate_memberlist_buttons');
     if (!allowedTo('send_email_to_members')) {
         unset($context['columns']['email_address']);
     }
     if (isset($context['disabled_fields']['website'])) {
         unset($context['columns']['website']);
     }
     if (isset($context['disabled_fields']['posts'])) {
         unset($context['columns']['posts']);
     }
     // Jump to the sub action.
     if (isset($subActions[$context['listing_by']])) {
         $this->{$subActions[$context['listing_by']][1]}();
     } else {
         $this->{$subActions['all'][1]}();
     }
 }
Exemple #18
0
 /**
  * Called by index.php?action=findmember.
  * This function result is used as a popup for searching members.
  *
  * @deprecated since 1.0
  * @uses sub template find_members of the Members template.
  */
 public function action_findmember()
 {
     global $context, $scripturl, $user_info, $settings;
     checkSession('get');
     // Load members template
     loadTemplate('Members');
     loadTemplate('index');
     Template_Layers::getInstance()->removeAll();
     $context['sub_template'] = 'find_members';
     if (isset($_REQUEST['search'])) {
         $context['last_search'] = Util::htmlspecialchars($_REQUEST['search'], ENT_QUOTES);
     } else {
         $_REQUEST['start'] = 0;
     }
     // Allow the user to pass the input to be added to to the box.
     $context['input_box_name'] = isset($_REQUEST['input']) && preg_match('~^[\\w-]+$~', $_REQUEST['input']) === 1 ? $_REQUEST['input'] : 'to';
     // Take the delimiter over GET in case it's \n or something.
     $context['delimiter'] = isset($_REQUEST['delim']) ? $_REQUEST['delim'] == 'LB' ? "\n" : $_REQUEST['delim'] : ', ';
     $context['quote_results'] = !empty($_REQUEST['quote']);
     // List all the results.
     $context['results'] = array();
     // Some buddy related settings ;)
     $context['show_buddies'] = !empty($user_info['buddies']);
     $context['buddy_search'] = isset($_REQUEST['buddies']);
     // If the user has done a search, well - search.
     if (isset($_REQUEST['search'])) {
         require_once SUBSDIR . '/Auth.subs.php';
         $_REQUEST['search'] = Util::htmlspecialchars($_REQUEST['search'], ENT_QUOTES);
         $context['results'] = findMembers(array($_REQUEST['search']), true, $context['buddy_search']);
         $total_results = count($context['results']);
         $_REQUEST['start'] = (int) $_REQUEST['start'];
         // This is a bit hacky, but its defined in index template, and this is a popup
         $settings['page_index_template'] = array('base_link' => '<li class="linavPages"><a class="navPages" href="{base_link}" role="menuitem">%2$s</a></li>', 'previous_page' => '<span class="previous_page" role="menuitem">{prev_txt}</span>', 'current_page' => '<li class="linavPages"><strong class="current_page" role="menuitem">%1$s</strong></li>', 'next_page' => '<span class="next_page" role="menuitem">{next_txt}</span>', 'expand_pages' => '<li class="linavPages expand_pages" role="menuitem" {custom}> <a href="#">...</a> </li>', 'all' => '<li class="linavPages all_pages" role="menuitem">{all_txt}</li>');
         $context['page_index'] = constructPageIndex($scripturl . '?action=findmember;search=' . $context['last_search'] . ';' . $context['session_var'] . '=' . $context['session_id'] . ';input=' . $context['input_box_name'] . ($context['quote_results'] ? ';quote=1' : '') . ($context['buddy_search'] ? ';buddies' : ''), $_REQUEST['start'], $total_results, 7);
         // Determine the navigation context
         $base_url = $scripturl . '?action=findmember;search=' . urlencode($context['last_search']) . (empty($_REQUEST['u']) ? '' : ';u=' . $_REQUEST['u']) . ';' . $context['session_var'] . '=' . $context['session_id'];
         $context['links'] += array('prev' => $_REQUEST['start'] >= 7 ? $base_url . ';start=' . ($_REQUEST['start'] - 7) : '', 'next' => $_REQUEST['start'] + 7 < $total_results ? $base_url . ';start=' . ($_REQUEST['start'] + 7) : '');
         $context['page_info'] = array('current_page' => $_REQUEST['start'] / 7 + 1, 'num_pages' => floor(($total_results - 1) / 7) + 1);
         $context['results'] = array_slice($context['results'], $_REQUEST['start'], 7);
     }
 }
Exemple #19
0
/**
 * Shows a fatal error with a message stored in the language file.
 *
 * What it does:
 * - This function stops execution and displays an error message by key.
 * - uses the string with the error_message_key key.
 * - logs the error in the forum's default language while displaying the error
 * message in the user's language.
 * - uses Errors language file and applies the $sprintf information if specified.
 * - the information is logged if log is specified.
 *
 * @param string $error
 * @param string|boolean $log defaults to 'general' false will skip logging, true will use general
 * @param string[] $sprintf defaults to empty array()
 */
function fatal_lang_error($error, $log = 'general', $sprintf = array())
{
    global $txt, $language, $modSettings, $user_info, $context;
    static $fatal_error_called = false;
    // Try to load a theme if we don't have one.
    if (empty($context['theme_loaded']) && empty($fatal_error_called)) {
        $fatal_error_called = true;
        loadTheme();
    }
    // If we have no theme stuff we can't have the language file...
    if (empty($context['theme_loaded'])) {
        die($error);
    }
    if (class_exists('Template_Layers')) {
        Template_Layers::getInstance()->isError();
    }
    $reload_lang_file = true;
    // Log the error in the forum's language, but don't waste the time if we aren't logging
    if ($log || !empty($modSettings['enableErrorLogging']) && $modSettings['enableErrorLogging'] == 2) {
        loadLanguage('Errors', $language);
        $reload_lang_file = $language != $user_info['language'];
        $error_message = empty($sprintf) ? $txt[$error] : vsprintf($txt[$error], $sprintf);
        log_error($error_message, $log);
    }
    // Load the language file, only if it needs to be reloaded
    if ($reload_lang_file) {
        loadLanguage('Errors');
        $error_message = empty($sprintf) ? $txt[$error] : vsprintf($txt[$error], $sprintf);
    }
    setup_fatal_error_context($error_message, $error);
}
 /**
  * Responds to an ajax button request, currently only for remove
  *
  * @uses generic_xml_buttons sub template
  */
 public function action_index_api()
 {
     global $txt, $context, $user_info;
     loadTemplate('Xml');
     // Remove any template layers that may have been created, this is XML!
     Template_Layers::getInstance()->removeAll();
     $context['sub_template'] = 'generic_xml_buttons';
     // No guests in here.
     if ($user_info['is_guest']) {
         loadLanguage('Errors');
         $context['xml_data'] = array('error' => 1, 'text' => $txt['not_guests']);
         return;
     }
     // Theme administration, removal, choice, or installation...
     // Of all the actions we currently know only this
     $subActions = array('remove' => 'action_remove_api');
     // Follow the sa or just go to administration.
     if (isset($_GET['sa']) && !empty($subActions[$_GET['sa']])) {
         $this->{$subActions[$_GET['sa']]}();
     } else {
         loadLanguage('Errors');
         $context['xml_data'] = array('error' => 1, 'text' => $txt['error_sa_not_set']);
         return;
     }
 }
    /**
     * Get admin information from the database.
     * Accessed by ?action=viewadminfile.
     */
    public function action_viewadminfile()
    {
        global $modSettings;
        require_once SUBSDIR . '/AdminDebug.subs.php';
        // Don't allow non-administrators.
        isAllowedTo('admin_forum');
        setMemoryLimit('128M');
        if (empty($_REQUEST['filename']) || !is_string($_REQUEST['filename'])) {
            fatal_lang_error('no_access', false);
        }
        $file = adminInfoFile($_REQUEST['filename']);
        // @todo Temp
        // Figure out if sesc is still being used.
        if (strpos($file['file_data'], ';sesc=') !== false) {
            $file['file_data'] = '
if (!(\'elkForum_sessionvar\' in window))
	window.elkForum_sessionvar = \'sesc\';
' . strtr($file['file_data'], array(';sesc=' => ';\' + window.elkForum_sessionvar + \'='));
        }
        Template_Layers::getInstance()->removeAll();
        // Lets make sure we aren't going to output anything nasty.
        @ob_end_clean();
        if (!empty($modSettings['enableCompressedOutput'])) {
            ob_start('ob_gzhandler');
        } else {
            ob_start();
        }
        // Make sure they know what type of file we are.
        header('Content-Type: ' . $file['filetype']);
        echo $file['file_data'];
        obExit(false);
    }
Exemple #22
0
 /**
  * Who's online, and what are they doing?
  * This function prepares the who's online data for the Who template.
  * It requires the who_view permission.
  * It is enabled with the who_enabled setting.
  * It is accessed via ?action=who.
  *
  * @uses Who template, main sub-template
  * @uses Who language file.
  */
 public function action_who()
 {
     global $context, $scripturl, $txt, $modSettings, $memberContext;
     // Permissions, permissions, permissions.
     isAllowedTo('who_view');
     // You can't do anything if this is off.
     if (empty($modSettings['who_enabled'])) {
         fatal_lang_error('who_off', false);
     }
     // Load the 'Who' template.
     loadTemplate('Who');
     loadLanguage('Who');
     // Sort out... the column sorting.
     $sort_methods = array('user' => 'mem.real_name', 'time' => 'lo.log_time');
     $show_methods = array('members' => '(lo.id_member != 0)', 'guests' => '(lo.id_member = 0)', 'all' => '1=1');
     // Store the sort methods and the show types for use in the template.
     $context['sort_methods'] = array('user' => $txt['who_user'], 'time' => $txt['who_time']);
     $context['show_methods'] = array('all' => $txt['who_show_all'], 'members' => $txt['who_show_members_only'], 'guests' => $txt['who_show_guests_only']);
     // Can they see spiders too?
     if (!empty($modSettings['show_spider_online']) && ($modSettings['show_spider_online'] == 2 || allowedTo('admin_forum')) && !empty($modSettings['spider_name_cache'])) {
         $show_methods['spiders'] = '(lo.id_member = 0 AND lo.id_spider > 0)';
         $show_methods['guests'] = '(lo.id_member = 0 AND lo.id_spider = 0)';
         $context['show_methods']['spiders'] = $txt['who_show_spiders_only'];
     } elseif (empty($modSettings['show_spider_online']) && isset($_SESSION['who_online_filter']) && $_SESSION['who_online_filter'] == 'spiders') {
         unset($_SESSION['who_online_filter']);
     }
     // Does the user prefer a different sort direction?
     if (isset($_REQUEST['sort']) && isset($sort_methods[$_REQUEST['sort']])) {
         $context['sort_by'] = $_SESSION['who_online_sort_by'] = $_REQUEST['sort'];
         $sort_method = $sort_methods[$_REQUEST['sort']];
     } elseif (isset($_SESSION['who_online_sort_by'])) {
         $context['sort_by'] = $_SESSION['who_online_sort_by'];
         $sort_method = $sort_methods[$_SESSION['who_online_sort_by']];
     } else {
         $context['sort_by'] = $_SESSION['who_online_sort_by'] = 'time';
         $sort_method = 'lo.log_time';
     }
     $context['sort_direction'] = isset($_REQUEST['asc']) || isset($_REQUEST['sort_dir']) && $_REQUEST['sort_dir'] == 'asc' ? 'up' : 'down';
     $conditions = array();
     if (!allowedTo('moderate_forum')) {
         $conditions[] = '(IFNULL(mem.show_online, 1) = 1)';
     }
     // Fallback to top filter?
     if (isset($_REQUEST['submit_top']) && isset($_REQUEST['show_top'])) {
         $_REQUEST['show'] = $_REQUEST['show_top'];
     }
     // Does the user wish to apply a filter?
     if (isset($_REQUEST['show']) && isset($show_methods[$_REQUEST['show']])) {
         $context['show_by'] = $_SESSION['who_online_filter'] = $_REQUEST['show'];
         $conditions[] = $show_methods[$_REQUEST['show']];
     } elseif (isset($_SESSION['who_online_filter'])) {
         $context['show_by'] = $_SESSION['who_online_filter'];
         $conditions[] = $show_methods[$_SESSION['who_online_filter']];
     } else {
         $context['show_by'] = $_SESSION['who_online_filter'] = 'all';
     }
     require_once SUBSDIR . '/Members.subs.php';
     $totalMembers = countMembersOnline($conditions);
     // Prepare some page index variables.
     $context['page_index'] = constructPageIndex($scripturl . '?action=who;sort=' . $context['sort_by'] . ($context['sort_direction'] == 'up' ? ';asc' : '') . ';show=' . $context['show_by'], $_REQUEST['start'], $totalMembers, $modSettings['defaultMaxMembers']);
     $context['start'] = $_REQUEST['start'];
     $context['sub_template'] = 'whos_online';
     Template_Layers::getInstance()->add('whos_selection');
     // Look for people online, provided they don't mind if you see they are.
     $members = onlineMembers($conditions, $sort_method, $context['sort_direction'], $context['start']);
     $context['members'] = array();
     $member_ids = array();
     $url_data = array();
     foreach ($members as $row) {
         $actions = @unserialize($row['url']);
         if ($actions === false) {
             continue;
         }
         // Send the information to the template.
         $context['members'][$row['session']] = array('id' => $row['id_member'], 'ip' => allowedTo('moderate_forum') ? $row['ip'] : '', 'time' => standardTime($row['log_time'], true), 'html_time' => htmlTime($row['log_time']), 'timestamp' => forum_time(true, $row['log_time']), 'query' => $actions, 'is_hidden' => $row['show_online'] == 0, 'id_spider' => $row['id_spider'], 'color' => empty($row['online_color']) ? '' : $row['online_color']);
         $url_data[$row['session']] = array($row['url'], $row['id_member']);
         $member_ids[] = $row['id_member'];
     }
     // Load the user data for these members.
     loadMemberData($member_ids);
     // Load up the guest user.
     $memberContext[0] = array('id' => 0, 'name' => $txt['guest_title'], 'group' => $txt['guest_title'], 'href' => '', 'link' => $txt['guest_title'], 'email' => $txt['guest_title'], 'is_guest' => true);
     // Are we showing spiders?
     $spiderContext = array();
     if (!empty($modSettings['show_spider_online']) && ($modSettings['show_spider_online'] == 2 || allowedTo('admin_forum')) && !empty($modSettings['spider_name_cache'])) {
         foreach (unserialize($modSettings['spider_name_cache']) as $id => $name) {
             $spiderContext[$id] = array('id' => 0, 'name' => $name, 'group' => $txt['spiders'], 'href' => '', 'link' => $name, 'email' => $name, 'is_guest' => true);
         }
     }
     require_once SUBSDIR . '/Who.subs.php';
     $url_data = determineActions($url_data);
     // Setup the linktree and page title (do it down here because the language files are now loaded..)
     $context['page_title'] = $txt['who_title'];
     $context['linktree'][] = array('url' => $scripturl . '?action=who', 'name' => $txt['who_title']);
     // Put it in the context variables.
     foreach ($context['members'] as $i => $member) {
         if ($member['id'] != 0) {
             $member['id'] = loadMemberContext($member['id']) ? $member['id'] : 0;
         }
         // Keep the IP that came from the database.
         $memberContext[$member['id']]['ip'] = $member['ip'];
         $context['members'][$i]['action'] = isset($url_data[$i]) ? $url_data[$i] : $txt['who_hidden'];
         if ($member['id'] == 0 && isset($spiderContext[$member['id_spider']])) {
             $context['members'][$i] += $spiderContext[$member['id_spider']];
         } else {
             $context['members'][$i] += $memberContext[$member['id']];
         }
     }
     // Some people can't send personal messages...
     $context['can_send_pm'] = allowedTo('pm_send');
     $context['can_send_email'] = allowedTo('send_email_to_members');
     // Any profile fields disabled?
     $context['disabled_fields'] = isset($modSettings['disabled_profile_fields']) ? array_flip(explode(',', $modSettings['disabled_profile_fields'])) : array();
 }
    /**
     * 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('&amp;' => '&')), 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');
    }
 /**
  * Like action_sendtopic, but done via ajax from an API request
  * @uses Xml Template generic_xml_buttons sub template
  */
 public function action_sendtopic_api()
 {
     global $topic, $modSettings, $txt, $context, $scripturl;
     loadTemplate('Xml');
     Template_Layers::getInstance()->removeAll();
     $context['sub_template'] = 'generic_xml_buttons';
     if (empty($_POST['send'])) {
         die;
     }
     // We need at least a topic... go away if you don't have one.
     // Guests can't mark things.
     if (empty($topic)) {
         loadLanguage('Errors');
         $context['xml_data'] = array('error' => 1, 'text' => $txt['not_a_topic']);
         return;
     }
     // Is the session valid?
     if (checkSession('post', '', false)) {
         loadLanguage('Errors');
         $context['xml_data'] = array('error' => 1, 'url' => $scripturl . '?action=emailuser;sa=sendtopic;topic=' . $topic . '.0');
         return;
     }
     require_once SUBSDIR . '/Topic.subs.php';
     $row = getTopicInfo($topic, 'message');
     if (empty($row)) {
         loadLanguage('Errors');
         $context['xml_data'] = array('error' => 1, 'text' => $txt['not_a_topic']);
         return;
     }
     // Can't send topic if its unapproved and using post moderation.
     if ($modSettings['postmod_active'] && !$row['approved']) {
         loadLanguage('Errors');
         $context['xml_data'] = array('error' => 1, 'text' => $txt['not_approved_topic']);
         return;
     }
     $is_spam = spamProtection('sendtopic', false);
     if ($is_spam !== false) {
         loadLanguage('Errors');
         $context['xml_data'] = array('error' => 1, 'text' => sprintf($txt['sendtopic_WaitTime_broken'], $is_spam));
         return;
     }
     // Censor the subject....
     censorText($row['subject']);
     $result = $this->_sendTopic($row);
     if ($result !== true) {
         loadLanguage('Errors');
         $context['xml_data'] = $result;
         return;
     }
     $context['xml_data'] = array('text' => $txt['topic_sent']);
 }
 /**
  * This action handler method displays and allows to change avatar settings.
  *
  * - Called by index.php?action=admin;area=manageattachments;sa=avatars.
  *
  * @uses 'avatars' sub-template.
  */
 public function action_avatarSettings_display()
 {
     global $txt, $context, $scripturl;
     // Initialize the form
     $this->_initAvatarSettingsForm();
     $config_vars = $this->_avatarSettings->settings();
     // Saving avatar settings?
     if (isset($_GET['save'])) {
         checkSession();
         call_integration_hook('integrate_save_avatar_settings');
         // Disable if invalid values would result
         if (isset($_POST['custom_avatar_enabled']) && $_POST['custom_avatar_enabled'] == 1 && (empty($_POST['custom_avatar_dir']) || empty($_POST['custom_avatar_url']))) {
             $_POST['custom_avatar_enabled'] = 0;
         }
         Settings_Form::save_db($config_vars);
         redirectexit('action=admin;area=manageattachments;sa=avatars');
     }
     // Attempt to figure out if the admin is trying to break things.
     $context['settings_save_onclick'] = 'return document.getElementById(\'custom_avatar_enabled\').value == 1 && (document.getElementById(\'custom_avatar_dir\').value == \'\' || document.getElementById(\'custom_avatar_url\').value == \'\') ? confirm(\'' . $txt['custom_avatar_check_empty'] . '\') : true;';
     // We need this for the in-line permissions
     createToken('admin-mp');
     // Prepare the context.
     $context['post_url'] = $scripturl . '?action=admin;area=manageattachments;save;sa=avatars';
     Settings_Form::prepare_db($config_vars);
     // Add a layer for the javascript.
     Template_Layers::getInstance()->add('avatar_settings');
     $context['sub_template'] = 'show_settings';
 }
 /**
  * Show a warning notice sent to a user.
  */
 public function action_showNotice()
 {
     global $txt, $context;
     // What notice have they asked to view
     $id_notice = isset($_GET['nid']) ? (int) $_GET['nid'] : 0;
     $notice = moderatorNotice($id_notice);
     // legit?
     if (empty($notice) || !$context['can_moderate_boards']) {
         fatal_lang_error('no_access', false);
     }
     list($context['notice_body'], $context['notice_subject']) = $notice;
     $context['notice_body'] = parse_bbc($context['notice_body'], false);
     $context['page_title'] = $txt['show_notice'];
     $context['sub_template'] = 'show_notice';
     Template_Layers::getInstance()->removeAll();
     loadTemplate('ModerationCenter');
 }
 /**
  * Allow the admin to reset permissions on files.
  */
 public function action_perms()
 {
     global $context, $txt, $modSettings, $package_ftp;
     // Let's try and be good, yes?
     checkSession('get');
     // If we're restoring permissions this is just a pass through really.
     if (isset($_GET['restore'])) {
         create_chmod_control(array(), array(), true);
         fatal_lang_error('no_access', false);
     }
     // This is a time and memory eating ...
     setMemoryLimit('128M');
     @set_time_limit(600);
     // Load up some FTP stuff.
     create_chmod_control();
     if (empty($package_ftp) && !isset($_POST['skip_ftp'])) {
         require_once SUBSDIR . '/FtpConnection.class.php';
         $ftp = new Ftp_Connection(null);
         list($username, $detect_path, $found_path) = $ftp->detect_path(BOARDDIR);
         $context['package_ftp'] = array('server' => isset($modSettings['package_server']) ? $modSettings['package_server'] : 'localhost', 'port' => isset($modSettings['package_port']) ? $modSettings['package_port'] : '21', 'username' => empty($username) ? isset($modSettings['package_username']) ? $modSettings['package_username'] : '' : $username, 'path' => $detect_path, 'form_elements_only' => true);
     } else {
         $context['ftp_connected'] = true;
     }
     // Define the template.
     $context['page_title'] = $txt['package_file_perms'];
     $context['sub_template'] = 'file_permissions';
     // Define what files we're interested in, as a tree.
     $context['file_tree'] = array(strtr(BOARDDIR, array('\\' => '/')) => array('type' => 'dir', 'contents' => array('agreement.txt' => array('type' => 'file', 'writable_on' => 'standard'), 'Settings.php' => array('type' => 'file', 'writable_on' => 'restrictive'), 'Settings_bak.php' => array('type' => 'file', 'writable_on' => 'restrictive'), 'attachments' => array('type' => 'dir', 'writable_on' => 'restrictive'), 'avatars' => array('type' => 'dir', 'writable_on' => 'standard'), 'cache' => array('type' => 'dir', 'writable_on' => 'restrictive'), 'custom_avatar_dir' => array('type' => 'dir', 'writable_on' => 'restrictive'), 'smileys' => array('type' => 'dir_recursive', 'writable_on' => 'standard'), 'sources' => array('type' => 'dir', 'list_contents' => true, 'writable_on' => 'standard'), 'themes' => array('type' => 'dir_recursive', 'writable_on' => 'standard', 'contents' => array('default' => array('type' => 'dir_recursive', 'list_contents' => true, 'contents' => array('languages' => array('type' => 'dir', 'list_contents' => true))))), 'packages' => array('type' => 'dir', 'writable_on' => 'standard', 'contents' => array('temp' => array('type' => 'dir'), 'backup' => array('type' => 'dir'), 'installed.list' => array('type' => 'file', 'writable_on' => 'standard'))))));
     // Directories that can move.
     if (substr(SOURCEDIR, 0, strlen(BOARDDIR)) != BOARDDIR) {
         unset($context['file_tree'][strtr(BOARDDIR, array('\\' => '/'))]['contents']['sources']);
         $context['file_tree'][strtr(SOURCEDIR, array('\\' => '/'))] = array('type' => 'dir', 'list_contents' => true, 'writable_on' => 'standard');
     }
     // Moved the cache?
     if (substr(CACHEDIR, 0, strlen(BOARDDIR)) != BOARDDIR) {
         unset($context['file_tree'][strtr(BOARDDIR, array('\\' => '/'))]['contents']['cache']);
         $context['file_tree'][strtr(CACHEDIR, array('\\' => '/'))] = array('type' => 'dir', 'list_contents' => false, 'writable_on' => 'restrictive');
     }
     // Are we using multiple attachment directories?
     if (!empty($modSettings['currentAttachmentUploadDir'])) {
         unset($context['file_tree'][strtr(BOARDDIR, array('\\' => '/'))]['contents']['attachments']);
         if (!is_array($modSettings['attachmentUploadDir'])) {
             $modSettings['attachmentUploadDir'] = unserialize($modSettings['attachmentUploadDir']);
         }
         // @todo Should we suggest non-current directories be read only?
         foreach ($modSettings['attachmentUploadDir'] as $dir) {
             $context['file_tree'][strtr($dir, array('\\' => '/'))] = array('type' => 'dir', 'writable_on' => 'restrictive');
         }
     } elseif (substr($modSettings['attachmentUploadDir'], 0, strlen(BOARDDIR)) != BOARDDIR) {
         unset($context['file_tree'][strtr(BOARDDIR, array('\\' => '/'))]['contents']['attachments']);
         $context['file_tree'][strtr($modSettings['attachmentUploadDir'], array('\\' => '/'))] = array('type' => 'dir', 'writable_on' => 'restrictive');
     }
     if (substr($modSettings['smileys_dir'], 0, strlen(BOARDDIR)) != BOARDDIR) {
         unset($context['file_tree'][strtr(BOARDDIR, array('\\' => '/'))]['contents']['smileys']);
         $context['file_tree'][strtr($modSettings['smileys_dir'], array('\\' => '/'))] = array('type' => 'dir_recursive', 'writable_on' => 'standard');
     }
     if (substr($modSettings['avatar_directory'], 0, strlen(BOARDDIR)) != BOARDDIR) {
         unset($context['file_tree'][strtr(BOARDDIR, array('\\' => '/'))]['contents']['avatars']);
         $context['file_tree'][strtr($modSettings['avatar_directory'], array('\\' => '/'))] = array('type' => 'dir', 'writable_on' => 'standard');
     }
     if (isset($modSettings['custom_avatar_dir']) && substr($modSettings['custom_avatar_dir'], 0, strlen(BOARDDIR)) != BOARDDIR) {
         unset($context['file_tree'][strtr(BOARDDIR, array('\\' => '/'))]['contents']['custom_avatar_dir']);
         $context['file_tree'][strtr($modSettings['custom_avatar_dir'], array('\\' => '/'))] = array('type' => 'dir', 'writable_on' => 'restrictive');
     }
     // Load up any custom themes.
     require_once SUBSDIR . '/Themes.subs.php';
     $themes = getCustomThemes();
     foreach ($themes as $id => $theme) {
         // Skip the default
         if ($id == 1) {
             continue;
         }
         if (substr(strtolower(strtr($theme['theme_dir'], array('\\' => '/'))), 0, strlen(BOARDDIR) + 7) === strtolower(strtr(BOARDDIR, array('\\' => '/')) . '/themes')) {
             $context['file_tree'][strtr(BOARDDIR, array('\\' => '/'))]['contents']['themes']['contents'][substr($theme['theme_dir'], strlen(BOARDDIR) + 8)] = array('type' => 'dir_recursive', 'list_contents' => true, 'contents' => array('languages' => array('type' => 'dir', 'list_contents' => true)));
         } else {
             $context['file_tree'][strtr($theme['theme_dir'], array('\\' => '/'))] = array('type' => 'dir_recursive', 'list_contents' => true, 'contents' => array('languages' => array('type' => 'dir', 'list_contents' => true)));
         }
     }
     // If we're submitting then let's move on to another function to keep things cleaner..
     if (isset($_POST['action_changes'])) {
         return $this->action_perms_save();
     }
     $context['look_for'] = array();
     // Are we looking for a particular tree - normally an expansion?
     if (!empty($_REQUEST['find'])) {
         $context['look_for'][] = base64_decode($_REQUEST['find']);
     }
     // Only that tree?
     $context['only_find'] = isset($_GET['xml']) && !empty($_REQUEST['onlyfind']) ? $_REQUEST['onlyfind'] : '';
     if ($context['only_find']) {
         $context['look_for'][] = $context['only_find'];
     }
     // Have we got a load of back-catalogue trees to expand from a submit etc?
     if (!empty($_GET['back_look'])) {
         $potententialTrees = unserialize(base64_decode($_GET['back_look']));
         foreach ($potententialTrees as $tree) {
             $context['look_for'][] = $tree;
         }
     }
     // ... maybe posted?
     if (!empty($_POST['back_look'])) {
         $context['only_find'] = array_merge($context['only_find'], $_POST['back_look']);
     }
     $context['back_look_data'] = base64_encode(serialize(array_slice($context['look_for'], 0, 15)));
     // Are we finding more files than first thought?
     $context['file_offset'] = !empty($_REQUEST['fileoffset']) ? (int) $_REQUEST['fileoffset'] : 0;
     // Don't list more than this many files in a directory.
     $context['file_limit'] = 150;
     // How many levels shall we show?
     $context['default_level'] = empty($context['only_find']) ? 2 : 25;
     // This will be used if we end up catching XML data.
     $context['xml_data'] = array('roots' => array('identifier' => 'root', 'children' => array(array('value' => preg_replace('~[^A-Za-z0-9_\\-=:]~', ':-:', $context['only_find'])))), 'folders' => array('identifier' => 'folder', 'children' => array()));
     foreach ($context['file_tree'] as $path => $data) {
         // Run this directory.
         if (file_exists($path) && (empty($context['only_find']) || substr($context['only_find'], 0, strlen($path)) == $path)) {
             // Get the first level down only.
             fetchPerms__recursive($path, $context['file_tree'][$path], 1);
             $context['file_tree'][$path]['perms'] = array('chmod' => @is_writable($path), 'perms' => @fileperms($path));
         } else {
             unset($context['file_tree'][$path]);
         }
     }
     // Is this actually xml?
     if (isset($_GET['xml'])) {
         loadTemplate('Xml');
         $context['sub_template'] = 'generic_xml';
         Template_Layers::getInstance()->removeAll();
     }
 }
/**
 * Initializes the portal, outputs all the blocks as needed
 *
 * @param boolean $standalone
 */
function sportal_init($standalone = false)
{
    global $context, $scripturl, $modSettings, $settings, $maintenance, $sportal_version;
    $sportal_version = '1.0.0 Beta 1';
    if (isset($_REQUEST['action']) && $_REQUEST['action'] == 'dlattach') {
        return;
    }
    if ((isset($_REQUEST['xml']) || isset($_REQUEST['api'])) && (isset($_REQUEST['action']) && $_REQUEST['action'] !== 'shoutbox')) {
        return;
    }
    // Not running standalone then we need to load in some template information
    if (!$standalone) {
        // Load the portal css and the default css if its not yet loaded.
        loadCSSFile('portal.css');
        // rtl css as well?
        if (!empty($context['right_to_left'])) {
            loadCSSFile('portal_rtl.css');
        }
        if (!empty($_REQUEST['action']) && in_array($_REQUEST['action'], array('admin'))) {
            loadLanguage('SPortalAdmin', sp_languageSelect('SPortalAdmin'));
        }
        if (!isset($settings['sp_images_url'])) {
            if (file_exists($settings['theme_dir'] . '/images/sp')) {
                $settings['sp_images_url'] = $settings['theme_url'] . '/images/sp';
            } else {
                $settings['sp_images_url'] = $settings['default_theme_url'] . '/images/sp';
            }
        }
    }
    // Portal not enabled, or mobile, or debug, or maintenance, or .... then bow out now
    if (!empty($modSettings['sp_disableMobile']) && empty($_GET['page']) && empty($_GET['article']) || !empty($settings['disable_sp']) || empty($modSettings['sp_portal_mode']) || (!empty($modSettings['sp_maintenance']) || !empty($maintenance)) && !allowedTo('admin_forum') || isset($_GET['debug']) || empty($modSettings['allow_guestAccess']) && $context['user']['is_guest']) {
        $context['disable_sp'] = true;
        if ($standalone) {
            $get_string = '';
            foreach ($_GET as $get_var => $get_value) {
                $get_string .= $get_var . (!empty($get_value) ? '=' . $get_value : '') . ';';
            }
            redirectexit(substr($get_string, 0, -1));
        }
        return;
    }
    // Not standalone means we need to load some portal specific information in to context
    if (!$standalone) {
        require_once SUBSDIR . '/PortalBlocks.subs.php';
        // Not running via ssi then we need to get SSI for its functions
        if (ELK !== 'SSI') {
            require_once BOARDDIR . '/SSI.php';
        }
        // Portal specific templates and language
        loadTemplate('Portal');
        loadLanguage('SPortal', sp_languageSelect('SPortal'));
        if (!empty($modSettings['sp_maintenance']) && !allowedTo('sp_admin')) {
            $modSettings['sp_portal_mode'] = 0;
        }
        if (empty($modSettings['sp_standalone_url'])) {
            $modSettings['sp_standalone_url'] = '';
        }
        if ($modSettings['sp_portal_mode'] == 3) {
            $context += array('portal_url' => $modSettings['sp_standalone_url'], 'page_title' => $context['forum_name']);
        } else {
            $context += array('portal_url' => $scripturl);
        }
        if ($modSettings['sp_portal_mode'] == 1) {
            $context['linktree'][0] = array('url' => $scripturl . '?action=forum', 'name' => $context['forum_name']);
        }
        if (!empty($context['linktree']) && $modSettings['sp_portal_mode'] == 1) {
            foreach ($context['linktree'] as $key => $tree) {
                if (strpos($tree['url'], '#c') !== false && strpos($tree['url'], 'action=forum#c') === false) {
                    $context['linktree'][$key]['url'] = str_replace('#c', '?action=forum#c', $tree['url']);
                }
            }
        }
    } else {
        $_GET['action'] = 'portal';
    }
    // Load the headers if necessary.
    sportal_init_headers();
    // Load permissions
    sportal_load_permissions();
    // Play with our blocks
    $context['standalone'] = $standalone;
    sportal_load_blocks();
    $context['SPortal']['on_portal'] = getShowInfo(0, 'portal', '');
    if (!Template_Layers::getInstance()->hasLayers(true) && !in_array('portal', Template_Layers::getInstance()->getLayers())) {
        Template_Layers::getInstance()->add('portal');
    }
}
 /**
  * When checking via ajax
  *
  * - Clears the templates
  * - Returns a json response to the page
  */
 private function EntropyResponse()
 {
     global $context;
     // Clear the templates
     $template_layers = Template_Layers::getInstance();
     $template_layers->removeAll();
     // Make room for ajax
     loadTemplate('Json');
     $context['sub_template'] = 'send_json';
     // Provide the response
     $context['json_data'] = $this->_pwentropy_response;
 }
/**
 * Integration hook integrate_buffer, called from ob_exit via call_integration_buffer
 * Used to modify the output buffer before its sent, here we add in our copyright
 *
 * @param string $tourniquet
 */
function sp_integrate_buffer($tourniquet)
{
    global $sportal_version, $context, $modSettings, $forum_version;
    $fix = str_replace('{version}', $sportal_version, '<a href="http://www.simpleportal.net/" target="_blank" class="new_win">SimplePortal {version} &copy; 2008-2014</a>');
    if (ELK == 'SSI' && empty($context['standalone']) || !Template_Layers::getInstance()->hasLayers() || empty($modSettings['sp_portal_mode']) || strpos($tourniquet, $fix) !== false) {
        return $tourniquet;
    }
    // Don't display copyright for things like SSI.
    if (!isset($forum_version)) {
        return '';
    }
    // Append our cp notice at the end of the line
    $finds = array(sprintf('powered by %1$s</a> | ', $forum_version));
    $replaces = array(sprintf('powered by %1$s</a> | ', $forum_version) . $fix . ' | ');
    $tourniquet = str_replace($finds, $replaces, $tourniquet);
    // Can't find it for some reason so we add it at the end
    if (strpos($tourniquet, $fix) === false) {
        $fix = '<div style="text-align: center; width: 100%; font-size: x-small; margin-bottom: 5px;">' . $fix . '</div></body></html>';
        $tourniquet = preg_replace('~</body>\\s*</html>~', $fix, $tourniquet);
    }
    return $tourniquet;
}